STM32F030 温湿度传感器程序设计(AHT20 + 多任务调度)
目录
- 一、前言
- 二、温湿度传感器核心操作流程
- 三、点表设计:通信规则标准化
- 四、CubeMX 配置与宏定义规划
- 五、程序实现:多任务调度 + CRC 校验 + Modbus 通信
- 六、功能验证:温湿度采集与外设控制
- 七、总结
- 八、结尾
一、前言
温湿度传感器是工业与民用场景中高频使用的传感器类型,本次基于 STM32F030 开发板对接 AHT20 温湿度传感器,设计完整的 Modbus 通信程序。核心解决温湿度采集耗时(单次至少 80ms)影响 Modbus 通信响应的问题,通过 FreeRTOS 多任务调度,将温湿度采集与 Modbus 通信解耦,同时实现 DO 外设(蜂鸣器、LED)的远程控制与温湿度数据的精准采集、转换及上报。
二、温湿度传感器核心操作流程
AHT20 温湿度传感器的操作需遵循固定时序与指令规则,核心流程分为三步:
- 发送测量命令:传感器 VDD 上电后等待 5ms,发送写测量指令
0x70、0xAC、0x33、0x00,随后等待 80ms 完成测量; - 获取校准数据:测量完成后发送
0x71读指令,可获取状态字 Status、温湿度校准数据 SRH [19:0]、ST [19:0] 及校准字 CRC; - 公式转换温湿度:基于采集到的校准数据,通过专用公式将原始值转换为实际温湿度值(湿度单位 0.1% RH,温度单位 0.1℃)。
关键注意:单次温湿度采集耗时至少 80ms,若在 Modbus 请求响应流程中直接采集,会导致通信超时 / 卡顿,因此需单独创建任务循环采集温湿度。
三、点表设计:通信规则标准化
为明确 PC 与 F030 温湿度传感器的通信规则,设计如下点表,清晰定义设备地址、寄存器地址、类别及功能:
| 设备地址 | 寄存器地址 | 寄存器类别 | 用途 | 描述 |
|---|---|---|---|---|
| 03H | 0000H | DO | 控制蜂鸣器 1 | 1 - 响 |
| 03H | 0001H | DO | 控制蜂鸣器 2 | 1 - 响 |
| 03H | 0002H | DO | 控制 LED1 | 1 - 亮 |
| 03H | 0003H | DO | 控制 LED2 | 1 - 亮 |
| 03H | 0004H | DO | 控制 LED3 | 1 - 亮 |
| 03H | 0000H | AI | 读取温度 | 单位 0.1 摄氏度 16 位有符号整数 |
| 03H | 0001H | AI | 读取湿度 | 单位 0.1% RH 16 位有符号整数 |
四、CubeMX 配置与宏定义规划
1. CubeMX 核心配置
温湿度传感器基于 I2C 通信,需在 CubeMX 中启用 I2C 外设(对应 AHT20 通信链路),配置界面如下:

2. 宏定义规划
通过宏定义启用温湿度传感器功能,同时配置 Modbus 寄存器数量(匹配点表规则),并声明核心函数:
// 启用温湿度传感器功能(宏定义便于切换不同传感器逻辑)
#define USE_TMP_HUMI_SENSOR 1
#ifdef USE_TMP_HUMI_SENSOR
// 声明温湿度采集任务函数、数据获取函数
void AHT20Task(void *argument);
static void aht20_get_datas(uint16_t *temp, uint16_t *humi);
// 从机地址(对应点表中的03H)
#define SLAVE_ADDR 3
// DO寄存器(线圈状态)数量:5个(2个蜂鸣器+3个LED)
#define NB_BITS 5
// DI寄存器数量:0(本传感器未使用)
#define NB_INPUT_BITS 0
// 保持寄存器数量:0(本传感器未使用)
#define NB_REGISTERS 0
// AI寄存器(输入寄存器)数量:2个(温度+湿度)
#define NB_INPUT_REGISTERS 2
#endif
五、程序实现:多任务调度 + CRC 校验 + Modbus 通信
1. CRC 校验与温湿度采集任务
单独创建 FreeRTOS 任务循环采集温湿度,通过 CRC 校验保障数据有效性,避免影响 Modbus 通信:
#ifdef USE_TMP_HUMI_SENSOR
//**********************************************************//
//CRC校验类型: CRC8
//多项式: X8+X5+X4+1
//Poly:0011 0001 0x31
// 功能:对采集到的传感器数据进行CRC8校验,保障数据完整性
unsigned char Calc_CRC8(unsigned char *message,unsigned char Num)
{
unsigned char i;
unsigned char byte;
unsigned char crc =0xFF;
for (byte = 0;byte<Num;byte++)
{
crc^=(message[byte]);
for(i=8;i>0;--i)
{
if(crc&0x80)
crc=(crc<<1)^0x31;
else
crc=(crc<<1);
}
}
return crc;
}
// 全局变量存储温湿度采集值(供Modbus任务读取)
static uint32_t g_temp, g_humi;
// 功能:获取最新的温湿度采集值(供Modbus任务调用)
static void aht20_get_datas(uint16_t *temp, uint16_t *humi)
{
*temp = g_temp;
*humi = g_humi;
}
// 温湿度采集任务:独立循环采集,不阻塞Modbus通信
void AHT20Task(void *argument)
{
// 测量指令:0xAC(触发测量)、0x33、0x00
uint8_t cmd[] = { 0xAC, 0x33, 0x00};
// 存储传感器返回的7字节数据(状态+温湿度+CRC)
uint8_t datas[7];
// 存储CRC校验结果
uint8_t crc;
// 声明I2C句柄(由CubeMX自动生成)
extern I2C_HandleTypeDef hi2c1;
vTaskDelay(10); /* wait for ready:传感器上电就绪延时 */
while (1)
{
// 步骤1:发送测量指令(I2C从机地址0x70)
if (HAL_OK == HAL_I2C_Master_Transmit(&hi2c1, 0x70, cmd, 3, 100))
{
vTaskDelay(100); /* wait for ready:等待测量完成(80ms以上) */
// 步骤2:读取传感器返回数据
if (HAL_OK == HAL_I2C_Master_Receive(&hi2c1, 0x70, datas, 7, 100))
{
/* cal crc:计算CRC校验值 */
crc = Calc_CRC8(datas, 6);
// 校验通过则解析数据
if (crc == datas[6])
{
/* 解析湿度原始值:SRH[19:0] */
g_humi = ((uint32_t)datas[1] << 12) | ((uint32_t)datas[2] << 4) | ((uint32_t)datas[3] >> 4);
/* 解析温度原始值:ST[19:0] */
g_temp = (((uint32_t)datas[3] & 0x0f) << 16) | ((uint32_t)datas[4] << 8) | ((uint32_t)datas[5]);
/* 转换为实际值:湿度0.1%RH,温度0.1℃ */
g_humi = g_humi * 100 * 10/ 0x100000; /* 0.1% */
g_temp = g_temp * 200 * 10/ 0x100000 - 500; /* 0.1C */
}
}
}
vTaskDelay(20); // 采集间隔20ms,保障数据刷新率
}
}
#endif
2. Modbus 通信任务
在默认任务中实现 Modbus 从机逻辑,读取温湿度采集值更新 AI 寄存器,同时根据 DO 寄存器值控制外设:
void StartDefaultTask(void *argument)
{
/* USER CODE BEGIN StartDefaultTask */
/* Infinite loop */
// 存储GPIO读取状态(保留兼容)
GPIO_PinState val;
// Modbus请求报文缓冲区
uint8_t *query;
// Modbus RTU上下文
modbus_t *ctx;
// 函数返回值,判断操作是否成功
int rc;
// Modbus寄存器映射结构体
modbus_mapping_t *mb_mapping;
// 环境监测传感器ADC校准(保留兼容,本传感器未启用)
#ifdef USE_ENV_MONITOR_SENSOR
HAL_ADCEx_Calibration_Start(&hadc);
#endif
// 创建串口1的Modbus RTU上下文:波特率115200、无校验、8数据位、1停止位
ctx = modbus_new_st_rtu("uart1", 115200, 'N', 8, 1);
// 设置从机地址(匹配点表中的03H)
modbus_set_slave(ctx, SLAVE_ADDR);
// 分配请求报文缓冲区(FreeRTOS内存接口)
query = pvPortMalloc(MODBUS_RTU_MAX_ADU_LENGTH);
// 初始化寄存器映射:基于宏定义的寄存器数量配置
mb_mapping = modbus_mapping_new_start_address(0,
NB_BITS, // DO寄存器数量
0,
NB_INPUT_BITS, // DI寄存器数量
0,
NB_REGISTERS, // 保持寄存器数量
0,
NB_INPUT_REGISTERS); // AI寄存器数量
// 建立Modbus RTU连接
rc = modbus_connect(ctx);
if (rc == -1) {
//fprintf(stderr, "Unable to connect %s\n", modbus_strerror(errno));
modbus_free(ctx);
vTaskDelete(NULL);; // 连接失败则删除任务
}
// 无限循环接收并响应PC端Modbus请求
for (;;) {
do {
// 接收PC端请求报文,过滤无效请求(返回0的查询)
rc = modbus_receive(ctx, query);
/* Filtered queries return 0 */
} while (rc == 0);
/* The connection is not closed on errors which require on reply such as
bad CRC in RTU. */
// 非CRC错误则继续循环,不退出
if (rc == -1 && errno != EMBBADCRC) {
/* Quit */
continue;
}
/* update values of registers
* a. read gpio
* b. update reg
*/
// 开关量传感器DI更新逻辑(保留兼容,本传感器未启用)
#ifdef USE_SWITCH_SENSOR
/* key1 */
val = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_3);
if (val == GPIO_PIN_RESET)
{
mb_mapping->tab_input_bits[0] = 1;
}
else
{
mb_mapping->tab_input_bits[0] = 0;
}
/* key2 */
val = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_4);
if (val == GPIO_PIN_RESET)
{
mb_mapping->tab_input_bits[1] = 1;
}
else
{
mb_mapping->tab_input_bits[1] = 0;
}
/* key3 */
val = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5);
if (val == GPIO_PIN_RESET)
{
mb_mapping->tab_input_bits[2] = 1;
}
else
{
mb_mapping->tab_input_bits[2] = 0;
}
#endif
// 环境监测传感器ADC采集逻辑(保留兼容,本传感器未启用)
#ifdef USE_ENV_MONITOR_SENSOR
/* READ ADC to update tab_input_registers */
for (int i = 0; i < 2; i++)
{
HAL_ADC_Start(&hadc);
if (HAL_OK == HAL_ADC_PollForConversion(&hadc, 100))
{
mb_mapping->tab_input_registers[i] = HAL_ADC_GetValue(&hadc);
}
}
#endif
// 温湿度传感器:读取采集任务的温湿度值,更新AI寄存器
#ifdef USE_TMP_HUMI_SENSOR
/* can not start aht20 to capture , it takes 80ms, too long */
uint16_t temp, humi;
// 获取最新温湿度采集值(无阻塞)
aht20_get_datas(&temp, &humi);
// 更新AI寄存器:0号存温度,1号存湿度
mb_mapping->tab_input_registers[0] = temp;
mb_mapping->tab_input_registers[1] = humi;
#endif
// 解析请求并回复PC端(包含更新后的AI寄存器数据)
rc = modbus_reply(ctx, query, rc, mb_mapping);
if (rc == -1) {
//break;
}
// 开关量传感器DO控制逻辑(保留兼容,本传感器未启用)
#ifdef USE_SWITCH_SENSOR
/* switch1 */
if (mb_mapping->tab_bits[0])
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_SET); //switch1
else
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_RESET); //switch1
/* switch2 */
if (mb_mapping->tab_bits[1])
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, GPIO_PIN_SET); //switch2
else
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, GPIO_PIN_RESET); //switch2
if (mb_mapping->tab_bits[2])
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_11, GPIO_PIN_RESET); //LED1
else
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_11, GPIO_PIN_SET); //LED1
if (mb_mapping->tab_bits[3])
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET); //LED2
else
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET); //LED2
if (mb_mapping->tab_bits[4])
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_RESET); //LED3
else
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_SET); //LED3
#endif
// 环境监测传感器DO控制逻辑(保留兼容,本传感器未启用)
#ifdef USE_ENV_MONITOR_SENSOR
/* beep1 */
if (mb_mapping->tab_bits[0])
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_SET); //beep1
else
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_RESET); //beep1
/* beep2 */
if (mb_mapping->tab_bits[1])
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, GPIO_PIN_SET); //beep2
else
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, GPIO_PIN_RESET); //beep2
if (mb_mapping->tab_bits[2])
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_11, GPIO_PIN_RESET); //LED1
else
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_11, GPIO_PIN_SET); //LED1
if (mb_mapping->tab_bits[3])
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET); //LED2
else
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET); //LED2
if (mb_mapping->tab_bits[4])
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_RESET); //LED3
else
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_SET); //LED3
#endif
// 温湿度传感器:根据DO寄存器值控制外设
#ifdef USE_TMP_HUMI_SENSOR
/* beep1 */
// DO寄存器0号控制蜂鸣器1(PB15):1-响(高电平)、0-停(低电平)
if (mb_mapping->tab_bits[0])
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_SET); //beep1
else
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, GPIO_PIN_RESET); //beep1
/* beep2 */
// DO寄存器1号控制蜂鸣器2(PB14):1-响(高电平)、0-停(低电平)
if (mb_mapping->tab_bits[1])
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, GPIO_PIN_SET); //beep2
else
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, GPIO_PIN_RESET); //beep2
// DO寄存器2号控制LED1(PB11):1-亮(低电平)、0-灭(高电平)
if (mb_mapping->tab_bits[2])
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_11, GPIO_PIN_RESET); //LED1
else
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_11, GPIO_PIN_SET); //LED1
// DO寄存器3号控制LED2(PB12):1-亮(低电平)、0-灭(高电平)
if (mb_mapping->tab_bits[3])
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET); //LED2
else
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET); //LED2
// DO寄存器4号控制LED3(PB13):1-亮(低电平)、0-灭(高电平)
if (mb_mapping->tab_bits[4])
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_RESET); //LED3
else
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, GPIO_PIN_SET); //LED3
#endif
}
// 释放资源(实际循环不会执行到此处)
modbus_mapping_free(mb_mapping);
vPortFree(query);
modbus_close(ctx);
modbus_free(ctx);
vTaskDelete(NULL);
/* USER CODE END StartDefaultTask */
}
六、功能验证:温湿度采集与外设控制
将程序编译烧录至 STM32F030 开发板,通过 PC 端 Modbus Poll 软件测试:
- 改写 DO 寄存器值:可精准控制蜂鸣器 1/2 的启停、LED1-3 的亮灭;
- 读取 AI 寄存器值:可获取转换后的温湿度数据(单位 0.1℃/0.1% RH),实测温湿度数据如下:

七、总结
- 采用多任务调度解耦温湿度采集(80ms 耗时)与 Modbus 通信,保障通信响应及时性;
- 通过 CRC8 校验保障温湿度数据完整性,公式转换实现原始值到实际物理量的映射;
- 宏定义设计兼顾代码兼容性,可快速切换不同传感器逻辑;
- 点表标准化定义通信规则,保障 PC 与传感器的适配性。
八、结尾
本次完成了 STM32F030 温湿度传感器的全流程程序设计,核心解决了低速传感器采集与高速 Modbus 通信的冲突问题,实现了数据采集、校验、转换、上报与外设控制的闭环。这套多任务调度 + 协议适配的设计思路,可直接复用至各类低速采集、高速通信的工业传感器开发场景。感谢各位的阅读,持续关注本系列笔记,一起探索更多工业级 Modbus 传感器的优化与落地技巧!
更多推荐
所有评论(0)