STM32 ——使用多任务框架RT-thread Nano完成modbus接口的温湿度Slave设备


实验环境

FreeMODBUS
ModbusPoll
STM32F103C8T6
杜邦线
DHT20温湿度模块

Modbus原理以及简单应用:使用c语言完成modbus协议,从云端服务器读取温湿度信息

FreeModbus 的详细介绍可以看 FreeMosbus 官网:FreeMODBUS

STM32移植RT_Thread:STM32 —— RT-Thread Nano 移植

AHT20驱动代码文件:https://pan.baidu.com/s/1hDdyDftfJs6PshESHmHgFQ?pwd=8888
提取码:8888

一、FreeModbus 协议简介

目前,FreeModbus最新版本是V1.6,可以通过官网下载,链接如下:
https://www.embedded-solutions.at/en/freemodbus-downloads/

FreeMODBUS 是一个奥地利人写的 Modbus 协议,它是一个针对嵌入式应用的一个免费(自由)的通用 MODBUS 协议的移植。Modbus 是一个工业制造环境中应用的一个通用协议。Modbus 通信协议栈包括两层:Modbus 应用层协议,该层定义了数据模式和功能;另外一层是网络层

FreeMODBUS 提供了 RTU/ASCII 传输模式及 TCP 协议支持。FreeModbus 遵循 BSD 许可证,这意味着用户可以将 FreeModbus 应用于商业环境中。下面给出命令表:

指令代码 描述 是否支持 备注
Master 主机
Slave 从机
MB_RTU RTU模式
MB_ASCII ASCII模式
MB_TCP TCP模式
0x01 读线圈
0x02 读离散输入
0x03 读保持寄存器
0x04 读输入寄存器
0x05 写单个线圈
0x06 写单个寄存器
0x07 读异常状态
0x08 诊断
0x0B 获取事件计数器
0x0C 获取事件记录
0x0F 写多个线圈
0x10 写多个寄存器
0x11 报告从机ID 协议与文档不一致
0x14 读文件记录
0x15 写文件记录
0x16 屏蔽写寄存器
0x17 读/写多个寄存器
0x18 写FIFO
0x2B 封装接口传输
0x2B/0x0D CANopen参考请求于应答
0x2B/0x0E 读设备身份表示

二、使用HAL库移植FreeModbus 驱动

(1)FreeModbus 驱动移植准备阶段

首先要下载 FreeModbus 协议,我们这里要安装的是从机程序,下载连接会放在文末,然后将我们下载好的压缩包解压出来,解压后文件夹内容如下:
在这里插入图片描述
然后我们进入到 demo 目录下,新建一个STM32MB 用于存储适用于我们的最小系统开发板的 FreeMosbus 驱动文件

将 demo/BARE 目录下的所有文件复制到我们刚才新建的文件夹下,然后再将 demo 目录下的 modbus 文件加复制到我们刚创建的目录下,这样我们就已经准备好了 Modbus 驱动环境:
在这里插入图片描述
在这里插入图片描述

(2)CubeMX 基本项目配置

  1. RCC 配置
    在这里插入图片描述

  2. USART1 配置
    在这里插入图片描述

  3. TIM3 配置
    在这里插入图片描述

  4. I2C 配置
    在这里插入图片描述

  5. SYS 配置
    在这里插入图片描述

(3)RT-Thread 配置

  1. 点击 Softwares Packages->Select Components,进入组件配置界面
    在这里插入图片描述
  2. 选择 RealThread, 然后根3.1.5版本的,然后点击 OK 按钮
    在这里插入图片描述
  3. 这时会新增Software Packs展开就可以看见添加的RealThread.RT_Thread,勾选相应内容
    在这里插入图片描述
  4. NVIC 配置
    在这里插入图片描述
  5. 时钟配置
    在这里插入图片描述
  6. 引脚配置
    在这里插入图片描述
  7. 创建项目
    在这里插入图片描述
    在这里插入图片描述

三、引入驱动代码文件并添加头文件路径

  1. 首先我们需要将对应的所有驱动文件复制到我们的项目目录下:
    在这里插入图片描述
    这两个文件都在上述提到过并给出,请仔细阅读

  2. 创建MD,MD_Port以及AHT20文件目录,引入对应文件
    这里 MD中添加我们 STM32MB/modbus 中的所有文件,Modbus_Port 中添加我们 STM32MB/port 中的所有文件以及demo.c文件
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  3. 最后添加头文件目录:
    在这里插入图片描述

四、修改工程源码

(1)修改 FreeModbus 代码

  1. 修改 portserial.c 的代码
    引入相关头文件
#include "usart.h"

修改 vMBPortSerialEnable 函数,该函数用于串口的接收和发送功能的打开或关闭

修改后代码如下:

void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )	第一个参数为串口的接收功能的打开与关闭,第二个参数为串口的发送功能的打开与关闭
{
    
    
    /* If xRXEnable enable serial receive interrupts. If xTxENable enable
     * transmitter empty interrupts.
     */
	
	if (xRxEnable)	//将串口收发中断和modbus联系起来,下面的串口改为自己使能的串口
	{
    
    
		__HAL_UART_ENABLE_IT(&huart1,UART_IT_RXNE);	//我用的是串口1,故为&huart1
	}
	else
	{
    
    
		__HAL_UART_DISABLE_IT(&huart1,UART_IT_RXNE);
	}
	if (xTxEnable)
	{
    
    
		__HAL_UART_ENABLE_IT(&huart1,UART_IT_TXE);
	}
	else
	{
    
    
		__HAL_UART_DISABLE_IT(&huart1,UART_IT_TXE);
	}
}

修改 xMBPortSerialInit 函数,该函数对 UART 串口进行初始化,由 eMBRTUInit 函数进行调用,这里改为默认返回 TRUE

修改后代码如下:

BOOL xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
    
    
    return TRUE;
}

修改 xMBPortSerialPutByte 和 xMBPortSerialGetByte 函数,串口发送和接收函数,将STM32串口发送函数进行封装,供协议栈使用

修改后代码如下:

BOOL xMBPortSerialPutByte( CHAR ucByte )
{
    
    
    /* Put a byte in the UARTs transmit buffer. This function is called
     * by the protocol stack if pxMBFrameCBTransmitterEmpty( ) has been
     * called. */
	if(HAL_UART_Transmit(&huart1,(uint8_t *)&ucByte,1,0x01) != HAL_OK){
    
    
			return FALSE;
	}
	else {
    
    
		return TRUE;
	}
}

BOOL xMBPortSerialGetByte( CHAR * pucByte )
{
    
    
    /* Return the byte in the UARTs receive buffer. This function is called
     * by the protocol stack after pxMBFrameCBByteReceived( ) has been called.
     */
	if(HAL_UART_Receive (&huart1 ,(uint8_t *)pucByte,1,0x01) != HAL_OK )//添加接收一位代码
		return FALSE ;
	else {
    
    
		return TRUE;
	}
}

修改 prvvUARTTxReadyISR 和 prvvUARTRxISR 函数,总线状态改为发送后,会在发送缓冲时,自动调用 prvvUARTTxReadyISR() 中断服务程序。prvvUARTTxReadyISR() 只调用了一个函数,就是 pxMBFrameCBTransmitterEmpty ()

数据链路层是最基本的打包部分,将数据打包成帧,送到应用层。在数据链路层协议中,使用中断方式来接受。那么每次接收到字符就自动调用接收字符的 ISR 程序。按照规定,应该将中断服务程序安装给 prvvUARTRxISR(void) 函数。实际上这个函数只调用了一个函数:

pxMBFrameCBByteReceived(),这个指针调用了 xMBRTUReceiveFSM 函数。

将两个函数前的 static 去掉即可,修改后代码如下:

void prvvUARTTxReadyISR( void )
{
    
    
    pxMBFrameCBTransmitterEmpty(  );
}

void prvvUARTRxISR( void )
{
    
    
    pxMBFrameCBByteReceived(  );
}
  1. 完整 portserial.c 代码如下:
/*
 * FreeModbus Libary: BARE Port
 * Copyright (C) 2006 Christian Walter <[email protected]>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * File: $Id$
 */

#include "port.h"

/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
#include "usart.h"

/* ----------------------- static functions ---------------------------------*/
void prvvUARTTxReadyISR( void );
void prvvUARTRxISR( void );

/* ----------------------- Start implementation -----------------------------*/
void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
    
    
    /* If xRXEnable enable serial receive interrupts. If xTxENable enable
     * transmitter empty interrupts.
     */
	
	if (xRxEnable)															//将串口收发中断和modbus联系起来,下面的串口改为自己使能的串口
	{
    
    
		__HAL_UART_ENABLE_IT(&huart1,UART_IT_RXNE);	//我用的是串口1,故为&huart1
	}
	else
	{
    
    
		__HAL_UART_DISABLE_IT(&huart1,UART_IT_RXNE);
	}
	if (xTxEnable)
	{
    
    
		__HAL_UART_ENABLE_IT(&huart1,UART_IT_TXE);
	}
	else
	{
    
    
		__HAL_UART_DISABLE_IT(&huart1,UART_IT_TXE);
	}
}

BOOL xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
    
    
    return TRUE;
}

BOOL xMBPortSerialPutByte( CHAR ucByte )
{
    
    
    /* Put a byte in the UARTs transmit buffer. This function is called
     * by the protocol stack if pxMBFrameCBTransmitterEmpty( ) has been
     * called. */
	if(HAL_UART_Transmit(&huart1,(uint8_t *)&ucByte,1,0x01) != HAL_OK){
    
    
			return FALSE;
	}
	else {
    
    
		return TRUE;
	}
}

BOOL xMBPortSerialGetByte( CHAR * pucByte )
{
    
    
    /* Return the byte in the UARTs receive buffer. This function is called
     * by the protocol stack after pxMBFrameCBByteReceived( ) has been called.
     */
	if(HAL_UART_Receive (&huart1 ,(uint8_t *)pucByte,1,0x01) != HAL_OK )//添加接收一位代码
		return FALSE ;
	else {
    
    
		return TRUE;
	}
}

/* Create an interrupt handler for the transmit buffer empty interrupt
 * (or an equivalent) for your target processor. This function should then
 * call pxMBFrameCBTransmitterEmpty( ) which tells the protocol stack that
 * a new character can be sent. The protocol stack will then call 
 * xMBPortSerialPutByte( ) to send the character.
 */
void prvvUARTTxReadyISR( void )
{
    
    
    pxMBFrameCBTransmitterEmpty(  );
}

/* Create an interrupt handler for the receive interrupt for your target
 * processor. This function should then call pxMBFrameCBByteReceived( ). The
 * protocol stack will then call xMBPortSerialGetByte( ) to retrieve the
 * character.
 */
void prvvUARTRxISR( void )
{
    
    
    pxMBFrameCBByteReceived(  );
}

  1. 修改 porttimer.c 的代码
/*
 * FreeModbus Libary: BARE Port
 * Copyright (C) 2006 Christian Walter <[email protected]>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * File: $Id$
 */

/* ----------------------- Platform includes --------------------------------*/
#include "port.h"

/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"

#include "stm32f1xx_hal.h"
#include "tim.h"

/* ----------------------- static functions ---------------------------------*/
void prvvTIMERExpiredISR( void );

/* ----------------------- Start implementation -----------------------------*/
BOOL xMBPortTimersInit( USHORT usTim1Timerout50us )
{
    
    
    return TRUE;
}


inline void vMBPortTimersEnable(  )
{
    
    
    /* Enable the timer with the timeout passed to xMBPortTimersInit( ) */
	__HAL_TIM_CLEAR_IT(&htim3,TIM_IT_UPDATE);
	__HAL_TIM_ENABLE_IT(&htim3,TIM_IT_UPDATE);
	__HAL_TIM_SET_COUNTER(&htim3,0);
	__HAL_TIM_ENABLE(&htim3);
}

inline void vMBPortTimersDisable(  )	//取消定时器中断
{
    
    
    /* Disable any pending timers. */
	__HAL_TIM_DISABLE(&htim3);
	__HAL_TIM_SET_COUNTER(&htim3,0);
	__HAL_TIM_DISABLE_IT(&htim3,TIM_IT_UPDATE);
	__HAL_TIM_CLEAR_IT(&htim3,TIM_IT_UPDATE);
}

/* Create an ISR which is called whenever the timer has expired. This function
 * must then call pxMBPortCBTimerExpired( ) to notify the protocol stack that
 * the timer has expired.
 */
void prvvTIMERExpiredISR( void )
{
    
    
    ( void )pxMBPortCBTimerExpired(  );
}


  1. 修改 port.h 的代码
/*
 * FreeModbus Libary: BARE Port
 * Copyright (C) 2006 Christian Walter <[email protected]>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * File: $Id$
 */

#ifndef _PORT_H
#define _PORT_H

#include <assert.h>
#include <inttypes.h>
#include "stm32f1xx_hal.h"

#define	INLINE                      inline
#define PR_BEGIN_EXTERN_C           extern "C" {
      
      
#define	PR_END_EXTERN_C             }

#define ENTER_CRITICAL_SECTION( )		__set_PRIMASK(1)	// 关闭总中断
#define EXIT_CRITICAL_SECTION( )		__set_PRIMASK(0)	// 开启总中断
typedef uint8_t BOOL;

typedef unsigned char UCHAR;
typedef char CHAR;

typedef uint16_t USHORT;
typedef int16_t SHORT;

typedef uint32_t ULONG;
typedef int32_t LONG;

#ifndef TRUE
#define TRUE            1
#endif

#ifndef FALSE
#define FALSE           0
#endif

#endif

  1. 修改 stm32f1xx_it.c 的代码
    调用如下全局函数:
extern void prvvUARTTxReadyISR(void);
extern void prvvUARTRxISR(void);
extern void prvvTIMERExpiredISR( void );
用于 DMA 与 I2C 中断设置
extern DMA_HandleTypeDef hdma_i2c1_rx;
extern DMA_HandleTypeDef hdma_i2c1_tx;
extern I2C_HandleTypeDef hi2c1;

修改 USART1_IRQHandler 函数,用于 USART 串口 1 处理中断

修改后代码如下:

void USART1_IRQHandler(void)
{
    
    
  /* USER CODE BEGIN USART1_IRQn 0 */

  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */
	if(__HAL_UART_GET_IT_SOURCE(&huart1, UART_IT_RXNE)!= RESET) 
	{
    
    
		prvvUARTRxISR();//接收中断
	}

	if(__HAL_UART_GET_IT_SOURCE(&huart1, UART_IT_TXE)!= RESET) 
	{
    
    
		prvvUARTTxReadyISR();//发送中断
	}
	
  HAL_NVIC_ClearPendingIRQ(USART1_IRQn);
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 1 */
}

修改 HardFault_Handler 函数,这个函数用于设置设备中断,这里如果不修改则会直接报错重复定义,所以,这里我们直接修改为 I2C 的中断函数即可

修改后代码如下:

/**
  * @brief This function handles I2C1 event interrupt.
  */
void I2C1_EV_IRQHandler(void)
{
    
    
  /* USER CODE BEGIN I2C1_EV_IRQn 0 */

  /* USER CODE END I2C1_EV_IRQn 0 */
  HAL_I2C_EV_IRQHandler(&hi2c1);
  /* USER CODE BEGIN I2C1_EV_IRQn 1 */

  /* USER CODE END I2C1_EV_IRQn 1 */
}

增加 HAL_TIM_PeriodElapsedCallback 函数,用于定时器中断回调函数

函数代码如下:

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)	//定时器中断回调函数,用于连接porttimer.c文件的函数
{
    
    
  /* NOTE : This function Should not be modified, when the callback is needed,
            the __HAL_TIM_PeriodElapsedCallback could be implemented in the user file
   */
	prvvTIMERExpiredISR( );
}

新增 DMA 与 I2C 中断设置函数

函数代码如下:

/**
  * @brief This function handles DMA1 channel6 global interrupt.
  */
void DMA1_Channel6_IRQHandler(void)
{
    
    
  /* USER CODE BEGIN DMA1_Channel6_IRQn 0 */

  /* USER CODE END DMA1_Channel6_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_i2c1_tx);
  /* USER CODE BEGIN DMA1_Channel6_IRQn 1 */

  /* USER CODE END DMA1_Channel6_IRQn 1 */
}

/**
  * @brief This function handles DMA1 channel7 global interrupt.
  */
void DMA1_Channel7_IRQHandler(void)
{
    
    
  /* USER CODE BEGIN DMA1_Channel7_IRQn 0 */

  /* USER CODE END DMA1_Channel7_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_i2c1_rx);
  /* USER CODE BEGIN DMA1_Channel7_IRQn 1 */

  /* USER CODE END DMA1_Channel7_IRQn 1 */
}
  1. stm32f1xx_it.c 完整代码如下:
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    stm32f1xx_it.c
  * @brief   Interrupt Service Routines.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2022 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32f1xx_it.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN TD */
/* USER CODE END TD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */
extern void prvvUARTTxReadyISR(void);
extern void prvvUARTRxISR(void);
extern void prvvTIMERExpiredISR( void );
/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/* External variables --------------------------------------------------------*/
extern DMA_HandleTypeDef hdma_i2c1_rx;
extern DMA_HandleTypeDef hdma_i2c1_tx;
extern I2C_HandleTypeDef hi2c1;
extern TIM_HandleTypeDef htim3;
extern UART_HandleTypeDef huart1;
/* USER CODE BEGIN EV */

/* USER CODE END EV */

/******************************************************************************/
/*           Cortex-M3 Processor Interruption and Exception Handlers          */
/******************************************************************************/
/**
  * @brief This function handles Non maskable interrupt.
  */
void NMI_Handler(void)
{
    
    
  /* USER CODE BEGIN NonMaskableInt_IRQn 0 */

  /* USER CODE END NonMaskableInt_IRQn 0 */
  /* USER CODE BEGIN NonMaskableInt_IRQn 1 */
  while (1)
  {
    
    
  }
  /* USER CODE END NonMaskableInt_IRQn 1 */
}

/**
  * @brief This function handles I2C1 event interrupt.
  */
void I2C1_EV_IRQHandler(void)
{
    
    
  /* USER CODE BEGIN I2C1_EV_IRQn 0 */

  /* USER CODE END I2C1_EV_IRQn 0 */
  HAL_I2C_EV_IRQHandler(&hi2c1);
  /* USER CODE BEGIN I2C1_EV_IRQn 1 */

  /* USER CODE END I2C1_EV_IRQn 1 */
}

/**
  * @brief This function handles Memory management fault.
  */
void MemManage_Handler(void)
{
    
    
  /* USER CODE BEGIN MemoryManagement_IRQn 0 */

  /* USER CODE END MemoryManagement_IRQn 0 */
  while (1)
  {
    
    
    /* USER CODE BEGIN W1_MemoryManagement_IRQn 0 */
    /* USER CODE END W1_MemoryManagement_IRQn 0 */
  }
}

/**
  * @brief This function handles Prefetch fault, memory access fault.
  */
void BusFault_Handler(void)
{
    
    
  /* USER CODE BEGIN BusFault_IRQn 0 */

  /* USER CODE END BusFault_IRQn 0 */
  while (1)
  {
    
    
    /* USER CODE BEGIN W1_BusFault_IRQn 0 */
    /* USER CODE END W1_BusFault_IRQn 0 */
  }
}

/**
  * @brief This function handles Undefined instruction or illegal state.
  */
void UsageFault_Handler(void)
{
    
    
  /* USER CODE BEGIN UsageFault_IRQn 0 */

  /* USER CODE END UsageFault_IRQn 0 */
  while (1)
  {
    
    
    /* USER CODE BEGIN W1_UsageFault_IRQn 0 */
    /* USER CODE END W1_UsageFault_IRQn 0 */
  }
}

/**
  * @brief This function handles Debug monitor.
  */
void DebugMon_Handler(void)
{
    
    
  /* USER CODE BEGIN DebugMonitor_IRQn 0 */

  /* USER CODE END DebugMonitor_IRQn 0 */
  /* USER CODE BEGIN DebugMonitor_IRQn 1 */

  /* USER CODE END DebugMonitor_IRQn 1 */
}

/******************************************************************************/
/* STM32F1xx Peripheral Interrupt Handlers                                    */
/* Add here the Interrupt Handlers for the used peripherals.                  */
/* For the available peripheral interrupt handler names,                      */
/* please refer to the startup file (startup_stm32f1xx.s).                    */
/******************************************************************************/

/**
  * @brief This function handles DMA1 channel6 global interrupt.
  */
void DMA1_Channel6_IRQHandler(void)
{
    
    
  /* USER CODE BEGIN DMA1_Channel6_IRQn 0 */

  /* USER CODE END DMA1_Channel6_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_i2c1_tx);
  /* USER CODE BEGIN DMA1_Channel6_IRQn 1 */

  /* USER CODE END DMA1_Channel6_IRQn 1 */
}

/**
  * @brief This function handles DMA1 channel7 global interrupt.
  */
void DMA1_Channel7_IRQHandler(void)
{
    
    
  /* USER CODE BEGIN DMA1_Channel7_IRQn 0 */

  /* USER CODE END DMA1_Channel7_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_i2c1_rx);
  /* USER CODE BEGIN DMA1_Channel7_IRQn 1 */

  /* USER CODE END DMA1_Channel7_IRQn 1 */
}


/**
  * @brief This function handles TIM3 global interrupt.
  */
void TIM3_IRQHandler(void)
{
    
    
  /* USER CODE BEGIN TIM3_IRQn 0 */

  /* USER CODE END TIM3_IRQn 0 */
  HAL_TIM_IRQHandler(&htim3);
  /* USER CODE BEGIN TIM3_IRQn 1 */

  /* USER CODE END TIM3_IRQn 1 */
}

/**
  * @brief This function handles USART1 global interrupt.
  */
void USART1_IRQHandler(void)
{
    
    
  /* USER CODE BEGIN USART1_IRQn 0 */

  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */
	if(__HAL_UART_GET_IT_SOURCE(&huart1, UART_IT_RXNE)!= RESET) 
	{
    
    
		prvvUARTRxISR();//接收中断
	}

	if(__HAL_UART_GET_IT_SOURCE(&huart1, UART_IT_TXE)!= RESET) 
	{
    
    
		prvvUARTTxReadyISR();//发送中断
	}
	
  HAL_NVIC_ClearPendingIRQ(USART1_IRQn);
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */

  /* USER CODE END USART1_IRQn 1 */
}

/* USER CODE BEGIN 1 */

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)	//定时器中断回调函数,用于连接porttimer.c文件的函数
{
    
    
  /* NOTE : This function Should not be modified, when the callback is needed,
            the __HAL_TIM_PeriodElapsedCallback could be implemented in the user file
   */
	prvvTIMERExpiredISR( );
}


/* USER CODE END 1 */
  1. 修改 demo.c 文件
/*
 * FreeModbus Libary: BARE Demo Application
 * Copyright (C) 2006 Christian Walter <[email protected]>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * File: $Id$
 */

/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
#include "AHT.h"
/* ----------------------- Defines ------------------------------------------*/
#define REG_INPUT_START 0	
#define REG_INPUT_NREGS 5

/* ----------------------- Static variables ---------------------------------*/
static USHORT   usRegInputStart = REG_INPUT_START;
//static USHORT   usRegInputBuf[REG_INPUT_NREGS];

uint16_t   usRegInputBuf[REG_INPUT_NREGS];
uint16_t InputBuff[5];


/* ----------------------- Start implementation -----------------------------*/
/*int main( void )
{
    eMBErrorCode    eStatus;

    eStatus = eMBInit( MB_RTU, 0x0A, 0, 38400, MB_PAR_EVEN );

    eStatus = eMBEnable(  );

    for( ;; )
    {
        ( void )eMBPoll(  );

        usRegInputBuf[0]++;
    }
}*/
uint32_t CT_data[2]={
    
    0,0};
	volatile int  c1,c2,t1,t2;
eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
{
    
    
	eMBErrorCode eStatus = MB_ENOERR;
	int iRegIndex;
	int i;
		AHT20_Read_CTdata_crc(CT_data);  //crc校验后,读取AHT20的温度和湿度数据 
		c1 = CT_data[0]*1000/1024/1024;  //计算得到湿度值c1(放大了10倍)
		t1 = CT_data[1]*2000/1024/1024-500;//计算得到温度值t1(放大了10倍)
		c2 = c1/10 + (c1/10)%10;
		t2 = t1/10 + (t1/10)%10;
		InputBuff[0] = t2; //温度整数位
		InputBuff[1] = t1%10; //温度小数点后一位
		InputBuff[2] = c2;	//湿度整数位
		InputBuff[3] = c2%10;//湿度小数点后一位
	
	if( ( usAddress >= REG_INPUT_START ) && ( usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS ) )
	{
    
    
		iRegIndex = ( int )( usAddress - usRegInputStart );
		for(i=0;i<usNRegs;i++)
		{
    
    
			*pucRegBuffer=InputBuff[i+usAddress-1]>>8;
			pucRegBuffer++;
			*pucRegBuffer=InputBuff[i+usAddress-1]&0xff;
			pucRegBuffer++;
		}
	}
	else
	{
    
    
		eStatus = MB_ENOREG;
	}

	return eStatus;
}

eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs,
                 eMBRegisterMode eMode )
{
    
    
    return MB_ENOREG;
}


eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils,
               eMBRegisterMode eMode )
{
    
    
    return MB_ENOREG;
}

eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
{
    
    
    return MB_ENOREG;
}

(2)修改RT_Thread代码

  1. 找到Middlewares/RT-Thread/RTOS/kernel文件夹下的board.c文件,修改串口USART2为USART1
    在这里插入图片描述
  2. 找到Application/User/Core里app_rt_thread.c下的rtconfig.h
    在这里插入图片描述

(3)修改main代码

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2022 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under Ultimate Liberty license
  * SLA0044, the "License"; You may not use this file except in compliance with
  * the License. You may obtain a copy of the License at:
  *                             www.st.com/SLA0044
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "rtthread.h"	// RT-Thread 头文件
#include "dma.h"
#include "i2c.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
#include "mb.h"
#include "mbport.h"
#include "AHT.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
extern void MX_RT_Thread_Init(void);	// RT-Thread 初始化函数,初始化并执行各种进程
extern void MX_RT_Thread_Process(void);	// RT-Thread 主进程
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
    
    
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_I2C1_Init();
  MX_TIM3_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
	AHT20_Init();	//初始化温度传感器并进行延时确保数据准确性
//	HAL_Delay(2000);
	eMBInit( MB_RTU, 0x01, 1, 115200, MB_PAR_NONE);//初始化modbus,走modbusRTU,从站地址为0x01,端口为1。
	eMBEnable();//使能modbus
	MX_RT_Thread_Init();	//初始化 RT-Thread 进程
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    
    
		
		//( void )eMBPoll(  );//启动modbus侦听,由于在 RT-Thread 主进程中进行了监听启动,所以这里直接调用 RT-Thread 主进程
		
		MX_RT_Thread_Process();	//调用主进程
		
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
    
    
  RCC_OscInitTypeDef RCC_OscInitStruct = {
    
    0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {
    
    0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    
    
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    
    
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */

/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
    
    
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
    
    
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
    
    
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

五、电路连接

DHT20的芯片引脚定义:
在这里插入图片描述

根据设计的程序连接电路:
对于USB转TTL模块和stm32f103c8t6连接:
GND — GND
3v3 — 3v3
TXD — A10
RXD — A9

总电路:
PB6 连接 SCL(串行时钟)
PB7 连接 SDA (串行数据)

六、运行结果

在这里插入图片描述

七、实验总结

RT_Thread移植较为繁琐,RT-Thread Nano ,它是一个极简版的硬实时内核,它是由 C 语言开发,采用面向对象的编程思维,具有良好的代码风格,是一款可裁剪的、抢占式实时多任务的 RTOS。其内存资源占用极小。Nano开源免费,小巧,简单是它的优点。本次实验更加熟悉了Modbus协议的工作原理

猜你喜欢

转载自blog.csdn.net/qq_52187415/article/details/128448772