尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

STM32 HAL库驱动SHT30温湿度传感器:I2C通信与滤波实战

STM32 HAL库驱动SHT30温湿度传感器:I2C通信与滤波实战 简介一套SHT30温湿度传感器与STM32F130ZET6微控制器的HAL库完整例程面向嵌入式开发者和STM32学习者解决I2C通信驱动、传感器数据读取、解析与显示等关键问题。资源包共231个文件压缩后10.22MB涵盖c/h源码、I2C初始化配置、MDK与IAR工程文件uvprojx/ewp、编译输出文件axf、o、d及调试辅助脚本便于在不同工具链下直接编译下载。已有1977人学习使用适合需要快速上手SHT30或深入理解STM32 HAL库的开发人员。例程包含完整I2C主机收发流程、SHT30命令发送与温湿度数据转换逻辑并集成串口输出方便实时查看结果代码基于HAL库分层清晰可快速迁移到其他STM32型号或扩展为智能家居、环境监测等项目。配套工程还包含定时器、UART等外设配置有助于理解嵌入式外设协同工作方式在工程和应用开发中具备较高参考价值。SHT30 温湿度传感器 STM32 HAL 库例程实战从读寄存器到稳定滤波一篇说透手头有一块丝印标着 STM32F130ZET6 的开发板其实就是大家更熟悉的 STM32F103ZET6恰好要接一个 SHT30 温湿度传感器做环境数据采集。SHT30 是 Sensirion 家的数字传感器走 I2C 接口精度比 DHT11 高一个量级温度典型精度 ±0.2℃湿度 ±2%RH而且出厂校准过不用自己算非线性补偿非常适合直接扔进单片机工程里用。这篇博文就把我的整套调试过程完整记录下来内容包括 SHT30 的 I2C 通信协议、HAL 库驱动代码编写、CRC 校验避坑、实测数据滤波以及几个容易卡住人的常见问题。不管你用的是 F103C8T6 还是 F407只要内核是 STM32、工程是基于 HAL 库的这套代码逻辑都可以直接搬过去。整篇不是抄手册的流水账而是我实际踩过坑之后整理出来的可复现方案。1. 项目整体思路与方案选型1.1 为什么选 SHT30 而不是 DHT11很多入门教程喜欢用 DHT11 做温湿度采集因为便宜、例程多。但 DHT11 有几个让人头疼的地方精度太差温度 ±2℃湿度 ±5%RH做环境监测基本只能看个大概通信协议是非标准的单总线时序要求延时非常精确用延时函数模拟还能跑但一旦被中断打断时序飘了数据就废了。SHT30 走标准 I2C 协议硬件时序由外设自己保证CPU 只需要发命令、收数据稳定性和精度都靠谱得多。SHT30 还有几个实用特性宽电压 2.4V 到 5.5V主流 3.3V 系统直接供电有单次测量、周期测量、报警模式多种工作方式I2C 地址可通过 ADDR 引脚切换一条总线上可以挂两个传感器。这些特性让它成了我项目中温湿度采集的首选。1.2 硬件 I2C 还是模拟 I2CHAL 库的硬件 I2C 经常被人吐槽“难用”网上大量帖子说 STM32 硬件 I2C 有 bug建议用 GPIO 模拟。我自己也遇到过HAL_I2C_Master_Transmit卡死的情况但最后查出来是中断优先级配置问题不是硬件 I2C 本身的锅。F103 系列经过这么多年迭代硬件 I2C 的问题早就在实际应用中验证过了只要配置正确完全可以用。不过这里给出我自己的选型建议如果只是单次读取一两个传感器模拟 I2C 写起来更直观、出错也好排查如果项目里挂的 I2C 设备多或者对时序稳定性有要求优先用硬件 I2C配合超时机制和错误复位逻辑。本文默认用硬件 I2C1后面会给出具体的 CubeMX 配置。提示SHT30 的数据手册里写明了 I2C 时钟最高支持 1MHz但 F103 的硬件 I2C 在标准模式下默认是 100kHz采样部分用这个速度足够了。1.3 关于型号的一点说明标题里写的 STM32F130ZET6我查了一圈ST 官方产品线里没有这个型号。实际对应的是 STM32F103ZET6可能是店家丝印或文档的笔误。这不影响代码逻辑因为 HAL 库工程只要外设配置一样型号差异基本不感知。后面所有内容都拿具体固件和代码说话你把工程切到自己的板子上引脚改一改就能用。2. 硬件连接与 CubeMX 初始化配置2.1 引脚接线SHT30 一共 8 个脚但我们裸板接传感器模块一般只需要用到电源、地、SCL、SDA 和 ADDR。常见传感器模块上已经帮你把上拉电阻和滤波电容都焊好了直接用杜邦线连接即可。SHT30 引脚STM32F103ZET6 引脚说明VIN3.3V传感器模块供电GNDGND共地SCLPB6I2C1_SCL时钟线SDAPB7I2C1_SDA数据线ADDRGND接低时 I2C 地址为 0x44ADDR 接 GND 后SHT30 的 7 位地址是 0x44左移一位后的写地址是 0x88读地址是 0x89。如果你用了多个传感器把第二个的 ADDR 接到 VCC地址就变成 0x45这样可以一条总线上挂两个非常方便。2.2 CubeMX 配置 I2C1在 STM32CubeMX 里先选择芯片型号然后在 Pinout Configuration 界面找Connectivity - I2C1勾选I2C引脚会自动关联到 PB6 和 PB7。如果板子上的 I2C 引脚被复用到其他外设也可以手动点引脚软件会帮你识别冲突。I2C1 的参数面板里我一般这样配I2C Speed ModeStandard Mode100kHzI2C Clock Speed100000Rising Time、Falling Time保持默认即可低速一般不用调外设初始化生成后MX_I2C1_Init函数会自动写入。需要注意 F103 的 I2C 1 挂载在 APB1 总线上如果 APB1 时钟是 36MHz生成代码会帮你自动计算分频值这部分不用手动干预。实操建议如果你用的是淘宝买的“OLED SHT30 组合模块”SCL 和 SDA 最好不要直接用模块上的排针去怼开发板的排母有些模块方向定义相反先看丝印再接线避免反接烧芯片。2.3 时钟树与中断优先级CubeMX 里Clock Configuration页面的 APB1 时钟如果低到影响 I2C 分频通常不会发生但习惯上检查一下调试器选择 Serial Wire外部晶振配置为 8MHzSYSCLK 设为 72MHzAPB1 分频得到 36MHz这是 F103 的经典配置。如果要在阻塞模式下读取 SHT30不需要开启 I2C 中断但如果后面打算用中断方式传输一定要在NVIC Settings里勾选 I2C1 的事件中断和错误中断并且注意抢占优先级不要和系统其他中断冲突。优先级设置不对是我遇到过硬件 I2C 卡死的最主要原因。3. SHT30 驱动代码实现与细节解析3.1 先搞清楚 SHT30 的 I2C 命令格式SHT30 的通信逻辑非常规整主机发 16 位命令两个字节等待传感器执行然后读取数据或者读状态。单次测量模式下最常用的命令是 0x2C 0x06表示高重复性测量。手册里还给了 0x2C 0x10中重复性和 0x2C 0x0D低重复性区别主要在于测量时间和噪声水平。我自己的实测经验是高重复性测量时间手册标的是 15.5ms 左右为了保险命令发出后延时 20ms 再读数据。如果你的系统里还有其他任务最好用定时器做非阻塞延时但这里先以最简单的阻塞方式把功能跑通。3.2 单次测量读取函数下面这段是核心读取函数使用 HAL 库的阻塞接口#include sht30.h #include i2c.h #define SHT30_ADDR_W 0x88 #define SHT30_ADDR_R 0x89 static uint8_t SHT30_CalcCRC(uint8_t *data, uint8_t len); uint8_t SHT30_Read_Data(float *temp, float *humi) { uint8_t cmd[2] {0x2C, 0x06}; uint8_t buf[6] {0}; uint16_t rawTemp 0, rawHumi 0; // 1. 发送单次测量命令 if (HAL_I2C_Master_Transmit(hi2c1, SHT30_ADDR_W, cmd, 2, 100) ! HAL_OK) { return 1; } HAL_Delay(20); // 2. 读取 6 字节数据温度高字节、低字节、CRC、湿度高字节、低字节、CRC if (HAL_I2C_Master_Receive(hi2c1, SHT30_ADDR_R, buf, 6, 100) ! HAL_OK) { return 2; } // 3. CRC 校验 if (SHT30_CalcCRC(buf[0], 2) ! buf[2] || SHT30_CalcCRC(buf[3], 2) ! buf[5]) { return 3; } rawTemp ((uint16_t)buf[0] 8) | buf[1]; rawHumi ((uint16_t)buf[3] 8) | buf[4]; // 4. 换算公式数据手册明确给出 *temp -45.0f 175.0f * (float)rawTemp / 65535.0f; *humi 100.0f * (float)rawHumi / 65535.0f; return 0; }几个容易忽略的细节HAL_I2C_Master_Transmit的第二个参数是“8 位地址”SHT30 的 7 位地址 0x44 要左移一位变成 0x88这个和裸机 I2C 写地址是一致的。读取 6 字节的顺序不能错先温度高字节再温度低字节然后是温度数据的 CRC接着湿度高字节、低字节、湿度 CRC。超时参数我填的 100ms传感器没有 ACK 或总线上有其他设备干扰时超时机制能让系统快速退出不至于死锁。3.3 CRC 校验别怕官方标准套路直接抄SHT30 的 CRC 校验和常见的传感器不太一样用的是 CRC-8 多项式 x^8 x^5 x^4 1也就是多项式值 0x31初始值 0xFF。如果收到的数据和 CRC 不匹配说明传输可能被干扰稳妥的做法是丢弃这次数据重新测量。static uint8_t SHT30_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 j 0; j 8; j) { if (crc 0x80) { crc (crc 1) ^ 0x31; } else { crc 1; } } } return crc; }这个函数只有十几行建议不要自己发明算法。直接抄手册附录的参考代码就行。需要注意一点校验的是“该数据的两个原始字节”不是换算后的结果。也就是说温度 CRC 只看 buf[0] 和 buf[1]湿度 CRC 只看 buf[3] 和 buf[4]。3.4 数据换算公式背后的意义SHT30 输出的原始值是 16 位无符号数范围 0 到 65535。温度公式是-45 175 * raw / 65535也就是说 raw 为 0 时对应 -45℃raw 为 65535 时对应 130℃。湿度公式是100 * raw / 65535直接把 16 位满量程映射到 0%RH 到 100%RH。这个映射关系是线性的所以代码里用浮点数计算完全没问题。如果你不想用浮点也可以用整数运算但要注意除法和乘法的顺序避免溢出。比如rawHumi * 100最大是 6553500还在 uint32 范围内所以可以先乘后除。温度也一样175 * raw最大约 11468625只要用 uint32 中转即可。3.5 周期测量模式简讲如果你的应用需要持续监测温度单次测量模式每次都要发命令再等延时效率并不高。SHT30 支持周期测量模式设置好测量频率后传感器自己定时更新数据主机只需要定时去读就行。周期测量的设置命令是 0x20 0x10高重复性、10Hz之类的组合具体见手册“Periodic Data Acquisition Mode”章节。启用后读取数据仍然走 0xE0 命令格式和单次测量一致。我个人建议项目初期先别用周期模式等单次模式跑通了再优化也不迟。4. 实测数据与滤波处理4.1 原始读数到底稳不稳接好硬件、烧进程序后我用串口把温湿度原始数据打印出来观察。在恒温环境下SHT30 的读数本身已经算稳定温度通常能稳定在 ±0.1℃ 范围内湿度会有一定程度的波动这可能受空气流动影响。测量频率越高数值跳动的感觉越明显但这不完全是传感器噪声很多时候是环境本身的微小变化。如果你发现读数每隔零点几秒就跳一下且跳变幅度超过 0.3℃ 或者 2%RH先检查接线和供电。SHT30 模块如果供电电压不稳数据确实会出现异常跳变。4.2 滑动平均滤波几行代码解决抖动对于湿度这种容易抖动的数据我习惯在应用层做一次滑动平均滤波不需要用太高深的卡尔曼算法。简单说就是维护一个数组存最近 N 次读数求平均值作为输出。#define FILTER_N 5 static float humi_buf[FILTER_N]; static uint8_t humi_idx 0; float SHT30_Filter_Humi(float new_humi) { float sum 0.0f; humi_buf[humi_idx] new_humi; humi_idx (humi_idx 1) % FILTER_N; for (uint8_t i 0; i FILTER_N; i) { sum humi_buf[i]; } return sum / FILTER_N; }这个实现有一个小问题数组初始化全为 0开机前几次读数会把平均值拉得很低。解决办法是在初始化时把数组填满第一个读数或者直接忽略前 N 次输出。N 的选择也不宜过大5 到 10 次足够了N 太大反应会变迟钝环境真的变化时你反而看不出趋势。4.3 OLED 显示与扩展方向传感器数据读出来了总得有个展示的地方。我顺手把数据接到了 OLED 上用的是 0.96 寸 SSD1306HAL 库的模拟 I2C 驱动SHT30 继续走硬件 I2C1OLED 走 GPIO 模拟 I2C两条总线互不干扰。OLED 引脚比较随意只要注意没有和其他外设复用就行。显示代码不复杂核心就是把浮点数格式化成字符串然后调用 OLED 的显示函数。这里有个小建议显示刷新率不要超过 5Hz因为 OLED 刷新太快会有残影且白白消耗 MCU 时间。5. 常见问题与排查技巧实录我把调试过程中遇到过的、以及群里同学问过的高频问题整理成一张速查表方便你对照排查问题现象可能原因解决办法HAL_I2C_Master_Transmit卡住中断优先级配置不当或者总线上 SDA 被拉低检查 NVIC 配置给 I2C 事件中断设置合理优先级尝试复位 I2C 外设或重新初始化读回来的数据全是 0xFF地址错误或模块供电异常确认 ADDR 引脚电平量模块 VIN 电压是否为 3.3V检查 I2C 总线上拉电阻温度湿度偏差很大换算公式写错或 CRC 未校验对照 3.4 节的公式重新计算确认你读的是原始值不是已经处理过的值湿度一直显示 100%原始值不正确rawHumi 溢出打印原始值验证确认没有把温度和湿度字节错位传感器周围温度比环境高传感器靠近发热元件或长时间持续测量传感器远离 MCU 和电源芯片必要时降低测量频率串口打印数据零散乱码串口波特率不对或引脚复用冲突检查 CubeMX 里 ASIC 串口配置和调试器占用引脚冲突时换一组串口5.1 硬件 I2C 卡死的快速恢复方案即使在配置正确的情况下I2C 总线也可能因为外部干扰出现卡死。稳健的做法是在初始化后检测总线状态如果发现 SDA 一直为低先复位总线。HAL 库提供了__HAL_I2C_ENABLE和__HAL_I2C_DISABLE宏可以在错误恢复时先禁用外设再重新使能。一个简单的恢复思路读取错误标志然后调用HAL_I2C_DeInit和HAL_I2C_Init重新初始化 I2C 外设同时把 GPIO 引脚全部拉高并产生几个时钟脉冲释放被卡住的从机。这个方法我实测过对 99% 的 I2C 卡死场景都有效。5.2 别忽略超时机制HAL 库的超时参数是阻塞接口的灵魂。如果你把超时设为HAL_MAX_DELAY一旦总线上出问题程序会永远卡在等待中这在产品里是绝对不能接受的。我在上电初始化阶段会用一个较短的超时比如 50ms读不到传感器就打印错误进入重试状态。宁可显示“传感器未连接”也不能让整个系统死掉。5.3 读取频率怎么定才合理SHT30 本身测量周期可以很快但结合实际应用场景建议数据刷新率设在 1Hz 到 10Hz 之间。环境监测 1Hz 完全够用风扇控制这类场景可以适当加快。频率太高不仅增加功耗和时间片占用数据波动也会更明显滤波处理会消耗额外的 CPU 资源。6. 一点经验总结这套代码和方案我前前后后调了两天最大的收获不是跑通功能而是把 I2C 读取这类底层外设的细节彻底摸清了。SHT30 是我见过最“友好”的传感器之一寄存器少、手册清楚、通信协议标准非常适合作为学习 STM32 I2C 外设的练手项目。如果后面有时间我会把周期测量模式和警报模式也整理出来SHT30 的警报模式可以配置温度或湿度阈值触发后 ALERT 引脚拉低很适合做温湿度上限报警。再配合按键切换显示页面、串口数据上传等扩展整个项目会非常完整。你在调试中如果遇到代码跑不通的情况别急着怀疑硬件 I2C 有 bug先看地址、看上拉、看优先级的顺序排查多半是这几类问题。本文还有配套的精品资源点击获取
返回列表