STM32 HAL库驱动SHT40温湿度传感器:I2C时序与CRC校验实战

发布时间:2026/10/21 9:50:22
STM32 HAL库驱动SHT40温湿度传感器:I2C时序与CRC校验实战 拿到一颗SHT40A-AW1B-R2要在STM32F4XX上用HAL库写温湿度传感器驱动很多人第一反应是I2C协议而已网上抄一段代码调通就完事。真到写驱动的时候CRC怎么验、测量时序怎么卡、总线卡死怎么救、浮点换算会不会拖慢MCU每一个细节都能让人在示波器前坐一下午。这篇就是我把这颗传感器从零到一驱动起来的完整记录覆盖选型思路、数据手册关键点、驱动代码实现、工程集成以及调试中踩过的坑。适合准备用STM32F4系列和HAL库做温湿度采集的开发者也适合第一次接触SHT4x系列传感器、想快速上手的读者。代码和思路都直接可复用按文章顺序捋一遍就能跑。1. 项目怎么定位选型思路与整体设计1.1 为什么是SHT40A而不是DHT11、HDC1080选传感器不能只看“能测温度和湿度”这一点。SHT40A-AW1B-R2这个型号带数字I2C接口出厂已经校准直接读原始值再换算不需要自己做标定这对量产来说省掉一大块麻烦。和经典的DHT11这类单总线传感器比差别非常明显。单总线协议对时序要求苛刻有些实现为了兼容各种器件延时都得反复调而且DHT11的精度摆在那做环境监测可能够用做产品级数据采集就吃力了。SHT40A的温精度典型值在±0.2°C附近湿度误差在±1.8%RH左右还带CRC校验可靠性上完全不是一个层级。型号后缀里的AW1B对应的是带防护滤膜的版本适合用在有一定环境防护要求的安装位置比如机箱、管道、户外外壳这类场景R2后缀主要是包装形式的区分对驱动编写没有实质影响。1.2 为什么用HAL库而不是标准库或LL库STM32F4的开发方式现在主流就三条路标准外设库、HAL库、LL库。标准库资料多、代码直观但官方早已不更新而且初始化代码写起来非常繁琐。HAL库最大的优势是配合CubeMX做图形化配置时钟树、引脚复用、I2C初始化全自动生成不用自己对着寄存器手册翻AF映射表。有人觉得HAL库执行效率低这得看场景。温湿度传感器本身数据率很低一秒读取一两次就算高频了I2C通信的瓶颈在传感器测量时间和总线速率上不在HAL封装那几十行代码。我实际测下来HAL_I2C_Master_Transmit、HAL_I2C_Master_Receive这套接口在400kHz总线上跑得很稳函数调用开销完全可以忽略。LL库适合对时序极端敏感、需要手搓寄存器的场景做传感器驱动属于杀鸡用牛刀。HAL库真正要注意的不是性能是超时机制和错误处理。I2C的HAL函数都有timeout参数用阻塞方式调用时如果总线异常函数会在超时后返回错误这就要求驱动里不能只读返回码还要针对超时做重试和恢复处理。这一点后面会在工程集成章节详细展开。1.3 硬件连接与引脚规划SHT40A是I2C接口典型接法四根线VDD、SDA、SCL、GND。供电范围很宽3.3V直接对接STM32F4没问题。SDA和SCL必须接上拉电阻常规选择4.7kΩ到10kΩ总线速率越高、挂载设备越多上拉电阻要适当往下调。VDD旁边放一个100nF去耦电容离传感器电源引脚越近越好这是原厂手册里反复强调的能有效滤掉电源噪声对湿度测量的干扰。引脚选择上以I2C1为例SCL和SDA选对应数据手册里的AF复用引脚即可。CubeMX里配置时把GPIO设为开漏输出带上拉速率选High或Very High剩下的复用功能系统会帮你完成。开漏加外部上拉是I2C标准接法多设备共享总线时不会出现电平冲突。2. SHT40A数据手册里的关键细节命令、时序与CRC2.1 命令体系测量命令、软复位与加热器命令SHT40A的I2C命令设计得比较清爽每个命令一字节常用主要就是测量、软复位、读序列号这几种。测量命令有三个0xFD是高重复性0xFE是中重复性0xFF是低重复性。重复性影响的是内部采样次数和测量时间高重复性数据最稳低重复性响应最快具体取舍看应用。设备状态监测、环境趋势判断这些场景直接选0xFD数据稳定性的优先级最高。软复位命令0x94也值得养成习惯。传感器刚上电时偶尔会遇到通信异常的情况设备不响应。驱动初始化阶段先发一次软复位相当于让传感器重新初始化内部状态能规避不少上电时序问题。0x89命令可以读序列号用于设备身份识别和出厂信息追溯产品调试阶段可以用它确认器件是真货还是翻新的。加热器命令则是应对高湿环境下传感器内部出现凝露的简单理解就是内部电阻丝短暂加热把水汽逼出去正常环境一般用不到。2.2 数据格式与CRC校验原理用0xFD命令触发一次测量后传感器会返回6个字节数据温度高字节、温度低字节、温度CRC、湿度高字节、湿度低字节、湿度CRC。注意CRC不是放在最后统一校验而是每个16位数据紧跟一个校验字节也就是温度部分占3字节、湿度部分占3字节。这套CRC-8算法是传感器行业里通用的方案特征多项式0x31初始值0xFF按MSB-first逐字节推进。看起来和Modbus的CRC不一样不要弄混。它的作用很直接I2C走线长、环境干扰强的时候数据可能被干扰如果只看温度和湿度跳变很难判断是真实变化还是通信错误有了CRC就能在数据进MCU之前先挡掉一层脏数据。CRC实现代码不复杂一个函数就能搞定。校验的时候对温度的前两个原始字节计算CRC和第三个字节比对对湿度的前两个原始字节计算CRC和第六个字节比对。任何一组不匹配这次读取直接丢弃重试而不是把错误数据扔给上层算法。2.3 测量时序与I2C总线速率配置SHT40A的时序有三个关键点发命令、等待测量、收数据。高重复性测量典型时间是8.2ms我的驱动里统一延时10ms再发起读取覆盖上限。中重复性典型4.5ms低重复性典型2.3ms。如果延时不够传感器没准备好主机在读寄存器时SDA会被传感器一直拉低I2C总线直接卡死。I2C速率方面SHT40A支持最高1MHz总线速率但STM32F4的I2C外设要注意时序寄存器计算。STM32F4的I2C挂在APB1上频率常见是42MHz。要跑400kHz需要算CCR和TRISE两个寄存器CCR约等于42MHz除以两倍目标频率算出来是52.5取53TRISE值和总线最大上升沿时间相关按1000ns代入计算大约取43。这些参数CubeMX会自动算但如果手工移植代码必须按自己的APB1时钟重算直接抄别人工程里的常量是最容易踩雷的地方。3. 驱动代码实现从初始化到数据换算3.1 驱动头文件与基础定义驱动头文件的第一要务是“定义清晰、别裸奔”。所有命令、地址、超时、数据长度都做成宏后续换传感器型号、调参都只改头文件不用动逻辑代码。结构体方面用一个包含温度和湿度的结构体作为输出载体上层应用直接拿这个结构体用不用关心底层原始值。地址这里要特别强调SHT40A的7位I2C地址是0x44。HAL库的I2C接口传地址时传的是最低位为0或1的8位地址形式所以调用时要写0x44 1。很多人第一次调不通问题就出在地址左移这一下。3.2 I2C初始化自查清单CubeMX生成I2C初始化后建议自己核对几个关键项避免后续排查问题时分不清是代码问题还是配置问题。第一时钟树里APB1时钟是否和CubeMX显示一致这直接决定I2C时序参数。第二I2C的两个引脚确认配成了开漏输出、复用功能、上拉。第三I2C外设使能以及中断优先级如果用了中断或DMA模式如何安排。第四确认I2C速率档位选的是400kHz还是1MHz和传感器支持的最高速率匹配。第五确认I2C模式是标准还是快速模式寄存器配置参数是否匹配。检查完这些用一个简单的设备探测函数验证总线上确实能看到0x44这个设备地址再往下写读取逻辑。这一步能确认硬件链路正常后面出了问题好定位是传感器没响应还是数据处理有问题。3.3 测量与读取全流程代码核心读取逻辑就是三件事发命令、等待、接收6字节。以下是我在项目里使用的驱动实现。// sht40.h #ifndef __SHT40_H #define __SHT40_H #include stm32f4xx_hal.h #define SHT40_I2C_ADDR (0x44U) #define SHT40_CMD_HIGH_ACC (0xFDU) #define SHT40_CMD_MED_ACC (0xFEU) #define SHT40_CMD_LOW_ACC (0xFFU) #define SHT40_CMD_SOFT_RESET (0x94U) #define SHT40_RESPONSE_SIZE (6U) #define SHT40_MEASURE_DELAY_MS (10U) #define SHT40_I2C_TIMEOUT (100U) typedef struct { float temperature; float humidity; } SHT40_Data; uint8_t SHT40_Init(I2C_HandleTypeDef *hi2c); uint8_t SHT40_SoftReset(I2C_HandleTypeDef *hi2c); uint8_t SHT40_ReadData(I2C_HandleTypeDef *hi2c, SHT40_Data *data); #endif// sht40.c #include sht40.h static uint8_t SHT40_CalcCRC(uint8_t *data, uint8_t len) { uint8_t crc 0xFF; for (uint8_t i 0; i len; i) { crc ^ data[i]; for (uint8_t bit 0; bit 8; bit) { if (crc 0x80) { crc (uint8_t)((crc 1) ^ 0x31); } else { crc (uint8_t)(crc 1); } } } return crc; } uint8_t SHT40_Init(I2C_HandleTypeDef *hi2c) { if (HAL_I2C_IsDeviceReady(hi2c, SHT40_I2C_ADDR 1, 3, SHT40_I2C_TIMEOUT) ! HAL_OK) { return 0; } return 1; } uint8_t SHT40_SoftReset(I2C_HandleTypeDef *hi2c) { uint8_t cmd SHT40_CMD_SOFT_RESET; if (HAL_I2C_Master_Transmit(hi2c, SHT40_I2C_ADDR 1, cmd, 1, SHT40_I2C_TIMEOUT) ! HAL_OK) { return 0; } HAL_Delay(1); return 1; } uint8_t SHT40_ReadData(I2C_HandleTypeDef *hi2c, SHT40_Data *data) { uint8_t cmd SHT40_CMD_HIGH_ACC; uint8_t rx[6] {0}; uint16_t t_raw 0; uint16_t rh_raw 0; if (HAL_I2C_Master_Transmit(hi2c, SHT40_I2C_ADDR 1, cmd, 1, SHT40_I2C_TIMEOUT) ! HAL_OK) { return 0; } HAL_Delay(SHT40_MEASURE_DELAY_MS); if (HAL_I2C_Master_Receive(hi2c, SHT40_I2C_ADDR 1, rx, 6, SHT40_I2C_TIMEOUT) ! HAL_OK) { return 0; } if (SHT40_CalcCRC(rx[0], 2) ! rx[2]) { return 0; } if (SHT40_CalcCRC(rx[3], 2) ! rx[5]) { return 0; } t_raw (uint16_t)((rx[0] 8) | rx[1]); rh_raw (uint16_t)((rx[3] 8) | rx[4]); >

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询