简介面向STM32F130ZET6开发者的SHT30温湿度传感器HAL库例程可帮助嵌入式爱好者快速掌握I2C通信与温湿度数据采集适用于智能家居、环境监测、农业设备、医疗设备等场景。资源共231个文件压缩包大小约10.22MB主体包含I2C、UART、TIM等HAL驱动源文件c/hIAR与Keil的工程配置文件eww、ewp、uvprojx、uvoptxCubeMX生成的硬件初始化工程ioc、mxproject以及编译链接产生的目标文件o、axf、out、map和仿真调试脚本目录结构按源码、配置、编译产物分层便于定位修改。目前已有1981人学习下载。例程提供了可直接运行的示例工程涵盖I2C外设初始化、传感器命令发送、温湿度原始数据解析以及换算为实际温度与相对湿度后通过串口输出显示等关键环节配套的HAL配置与中断处理方式同样具备参考价值基于HAL库的封装写法降低了开发门槛适合学生和初级工程师对照学习也便于在此之上扩展报警记录、无线传输等功能。1. SHT30 与 STM32F130ZET6 HAL 库例程为什么先下载例程再谈传感器SHT30 温湿度传感器的 I2C 例程在 STM32F130ZET6 上能不能直接跑通关键不在传感器本身而在你对 I2C 时序和 HAL 库状态机的理解。很多人第一次拿到这套 SHT30 HAL 例程以为配好 CubeMX 就能读到温度结果常常卡在设备地址写错、引脚没有配成开漏、CRC 校验不过这几个问题上。这份资源把 SHT30 的初始化、单次测量、CRC 校验和温湿度换算都封装成了可直接调用的函数适合刚入手 F13x 系列、想快速验证传感器、又不想从头抠时序的开发者。读完这篇文章你能从原理层面看懂每行代码为什么这样写并直接把驱动搬进自己的工程。2. I2C 通信时序与 SHT30 寄存器地图拿到例程前必须看清的协议细节2.1 设备地址与 I2C 总线接线SHT30 的默认 I2C 地址是 0x447 位地址如果传感器的 ADDR 引脚被拉高到 VDD地址会变成 0x45。这个细节直接决定例程能不能找到设备。要注意 HAL 库接口里传的地址参数需要左移一位变成 8 位地址写操作用 0x88读操作用 0x89。很多人照着数据手册抄了 0x44 填进去结果 I2C 总线上一片静默这是最常见的第一坑。接线方面本例程默认挂在 I2C1 上CubeMX 里的引脚映射通常对应 PB6 SCL、PB7 SDA。实际板子如果不同以你自己原理图为准。总线上必须加外部上拉电阻SHT30 是开漏设备不能靠 MCU 内部上拉凑合。我一般选 4.7kΩ配合 400kHz 快速模式很稳如果线长超过 10cm 或者有杜邦线降到 100kHz 标准模式并用 2.2kΩ 更安全。SHT30 的供电范围覆盖 2.4V 到 5.5VF130ZET6 的 VDD 是 3.3V直接共电源、共地即可不需要电平转换。2.2 测量命令与数据帧格式SHT30 有两种工作模式周期测量和单次测量。例程用的是单次测量高重复度命令也就是发送 0x2C 0x06 这两个字节。高重复度模式下芯片会做更精细的 ADC 转换噪声更低代价是转换时间稍长。命令发送完之后要等待一段时间再发起读操作不能像读普通 EEPROM 那样发完命令立刻读否则读回来的全是陈旧数据。命令高字节命令低字节功能说明0x2C0x06单次测量高重复度推荐默认使用0x2C0x0D单次测量中重复度0x2C0x10单次测量低重复度0x200x32周期测量10Hz 高重复度持续输出0x300xA2软复位重新初始化芯片0xEF0xC8读电子 ID用于验证通信链路数据帧格式要记清楚。单次测量完成后主机再发一个起始条件SHT30 会连续输出 6 个字节温度高字节、温度低字节、温度 CRC 校验字节、湿度高字节、湿度低字节、湿度 CRC 校验字节。例程启用 CRC 校验时实际读取长度是 8 个字节其中第 3 个和第 6 个字节分别用来校验温度和湿度。HAL_I2C_Master_Receive在最后一个字节会自动发 NACK 并产生停止条件SHT30 对这种结束方式完全兼容所以读 8 字节不会出问题。举个例子假如温度寄存器原值是 0x66 0x66也就是十进制的 26214代入换算公式 T -45 175 × raw / 65535得到 25.0℃。湿度寄存器如果是 0x80 0x00也就是 32768RH 100 × raw / 65535得到 50.0%RH。这套帧格式在排错时非常直观先看 CRC 是否通过再判断温度值是否在合理范围。2.3 CRC-8 校验数据可靠性的最后一道闸SHT30 的 CRC-8 多项式是 x^8 x^5 x^4 1对应二进制 0x31初始值为 0xFF高位先出。它只对各自数据块的前两个字节计算温度 CRC 只覆盖温度的两个字节湿度 CRC 只覆盖湿度的两个字节。这个机制能挡住绝大多数总线干扰和时序错位问题。为什么要自己校验而不是直接相信读到的数因为 I2C 在长线、电机启停、电源波动等场景下会出现偶发位翻转读出来的温度可能从 25℃ 跳成 -12℃。CRC 校验通过才更新数据失败就直接丢弃本次采样这一条能让你的数据显示稳定很多。例程里的校验函数返回 0 表示校验通过非 0 表示数据被污染建议在调试阶段把失败次数打印出来如果失败率超过 1%优先查上拉电阻、线长和电源噪声而不是怀疑传感器本身。3. 把 HAL 库例程跑起来从 CubeMX 配置到串口打印温湿度3.1 CubeMX 工程配置时钟、I2C 与调试串口拿到例程后先别急着编译把 CubeMX 工程重新对一遍再生成代码。第一步是选芯片在器件库选择 STM32F130ZET6。如果某个版本的 CubeMX 器件库里没有这个具体型号选同封装的 ZET6 型号建立工程再核对引脚外设配置方式是一致的不会影响 I2C 和串口的代码结构。RCC 时钟配置里HSE 选择 Crystal/Ceramic Resonator外部晶振按 8MHz 填写。时钟树里让 HCLK 跑到 72MHzAPB1 时钟 36MHz因为 F13x 系列的 I2C1 挂在 APB1 总线上36MHz 就是 I2C 外设的输入时钟。有人图省事直接用内部 HSI温度漂移会让 I2C 时序边缘化不建议这样干。I2C1 的配置参数我习惯这样设置速度模式 Fast Mode时钟频率 400kHz地址自身地址写 0x01 占位。引脚模式保持 I2C 默认的开漏输出不要手动去 GPIO 配置里改成推挽开漏是 I2C 协议正常工作的前提。串口方面启用 USART1波特率 115200、8 位数据、1 位停止位用于调试信息输出。全部配置完成后生成代码编译能过再继续往下走。3.2 驱动代码分层命令发送、数据读取与结果解析例程的核心驱动可以拆成四个层次初始化、发送命令、读取数据、解析结果。我建议你按这个分层方式组织代码不要把所有逻辑堆在一个函数里。下面是完整的单次测量流程先发测量命令等待芯片完成转换再读回 8 个字节并做 CRC 校验。/* sht30.c 核心读取流程 */ #define SHT30_ADDR_W (0x44 1) /* 7位地址0x44左移成8位写地址 */ #define SHT30_ADDR_R (0x44 1) | 0x01 uint8_t SHT30_Raw_Read(uint16_t *rawT, uint16_t *rawH) { uint8_t cmd[2] {0x2C, 0x06}; /* 单次测量高重复度 */ uint8_t buf[8] {0}; /* 步骤1发送测量命令 */ if (HAL_I2C_Master_Transmit(hi2c1, SHT30_ADDR_W, cmd, 2, 100) ! HAL_OK) return 0; /* 步骤2等待ADC转换完成高重复度典型约4.5ms */ HAL_Delay(30); /* 步骤3读取6字节数据2字节CRC共8字节 */ if (HAL_I2C_Master_Receive(hi2c1, SHT30_ADDR_R, buf, 8, 100) ! HAL_OK) return 0; /* 步骤4温度CRC校验覆盖buf[0..1] */ if (SHT30_CRC8(buf, 2) ! buf[2]) return 0; /* 步骤5湿度CRC校验覆盖buf[4..5] */ if (SHT30_CRC8(buf[4], 2) ! buf[6]) return 0; /* 大端格式组装原始值 */ *rawT (buf[0] 8) | buf[1]; *rawH (buf[4] 8) | buf[5]; return 1; }这段代码有几个参数值得说清楚。HAL_I2C_Master_Transmit的第四个参数是超时时间单位毫秒100ms 足够覆盖任何异常情况如果总线被拉死这个超时能避免程序卡死在等待里。HAL_Delay(30)是保守值官方高重复度测量时间规格是 4.5ms 左右我留出 6 倍余量是为了兼容劣质电源和低温环境。如果你做低功耗场景可以把延时缩小到 10ms但前提是电源纹波控制得好。CRC 校验函数是通用的多项式 0x31、初值 0xFF可以复用到其他传感器上。它按位处理每个字节核心逻辑是移位后判断最高位再异或多项式。校验失败返回非零此时整个采样作废不要拿脏数据去做换算。3.3 在主循环里组织测量节奏主程序的调用逻辑不复杂但测量节奏要设计好。如果系统里还有其他 I2C 设备SHT30 的测量命令和数据读取之间必须保持原子性中间不要插入其他设备的 I2C 操作。下面是一个可用的主循环示例/* main.c 主循环片段 */ uint16_t rawT 0, rawH 0; float temp 0.0f, humi 0.0f; while (1) { if (SHT30_Raw_Read(rawT, rawH)) { /* 原始值转物理量 */ temp -45.0f 175.0f * rawT / 65535.0f; humi 100.0f * rawH / 65535.0f; /* 串口输出方便在PC端看曲线 */ printf(T%.2f RH%.2f\r\n, temp, humi); } HAL_Delay(1000); /* 1秒采一次 */ }这里的printf重定向到串口需要你在工程里实现fputc或者直接用HAL_UART_Transmit封装一个调试打印函数。测量周期 1 秒对温湿度场景足够SHT30 本身响应时间在 2 秒以内采样太快反而会把环境波动当成传感器噪声显示出来。如果做气象站这类需要平滑曲线的设备后续还可以叠加第 4 章的滤波处理。4. 数据校准与滤波让温湿度读数真正可信4.1 线性换算公式与精度边界SHT30 出厂前已经做过校准不需要像老式模拟传感器那样自己在软件里做两点校准。温度换算公式 T -45 175 × raw / 65535湿度换算公式 RH 100 × raw / 65535都是纯线性映射。这两个公式在数据手册里写得很清楚但实际工程中要注意边界行为当环境湿度超过 100%RH 或者低于 0%RH 时芯片会输出钳位值换算出来的数会撞到边界上不是真实值。温度精度方面SHT30 在 0~65℃ 范围内典型误差 ±0.2℃在 -40℃ 低温段误差会放大到 ±0.5℃ 左右。湿度精度典型 ±2%RH但在 0%RH 和 100%RH 两个端点附近误差明显变大。如果你的项目宣称精度指标需要留出这些边界余量不要拿数据手册的典型值当最大值用。4.2 异常值剔除与滑动平均CRC 校验挡的是通信层干扰但挡不住环境突变和传感器自身轻微漂移。比如开窗瞬间湿度从 40%RH 跳到 60%RH这是真实物理变化还是传感器异常需要靠数据滤波来平滑。我一般先做异常值剔除再做滑动平均两步顺序不能反。/* 简单滑动平均 异常值剔除窗口设为5次 */ #define FILTER_WINDOW 5 float temp_buf[FILTER_WINDOW] {0}; float humi_buf[FILTER_WINDOW] {0}; uint8_t temp_idx 0; float SHT30_Update_Filter(float new_value, float *buf, uint8_t *idx) { float sum 0.0f; buf[*idx] new_value; *idx (*idx 1) % FILTER_WINDOW; for (uint8_t i 0; i FILTER_WINDOW; i) sum buf[i]; return sum / FILTER_WINDOW; }在主循环里调用前先判断新值和上一次滤波结果的差值温度差超过 3℃湿度差超过 8%RH 时不进滤波窗口直接丢弃本次数据。这个阈值要结合你的应用场景调整用在恒温恒湿箱里阈值可以收紧用在户外气象站就要放宽。4.3 单次测量与周期测量怎么选例程默认用单次测量这在实际项目中通常是更合理的方案。下面把两种模式做一个直接对比对比项单次测量 0x2C 0x06周期测量 0x20 0x32功耗测量完成后自动进待机功耗低芯片持续工作按设定频率输出实时性每次需要重新发送命令数据始终就绪读距离上次转换可能有时延代码复杂度简单适合轮询式调用需要处理数据就绪判断典型场景电池供电、低频采样交流供电、每秒多次采集如果你的系统需要 1Hz 以上的采集频率并且不在乎功耗周期测量更方便如果设备是电池供电单次测量让芯片在采样间隙彻底睡死才是正确的做法。例程的两个测量函数其实都封装好了关键在你怎么组织调用节奏。5. SHT30 例程避坑与排查五个现场最常见的翻车点5.1 现象一I2C 扫描不到 0x44 设备反复确认接线无误逻辑分析仪上也看不到 ACK 应答。原因大概率是 ADDR 引脚状态不对。ADDR 悬空时默认地址是 0x44但如果 PCB 上 ADDR 被拉高到 VDD地址就是 0x45反过来你想用 0x45ADDR 却没接自然找不到设备。还有一种可能是传感器供电没起来SHT30 的 VDD 和 GND 之间有压差但电流不够芯片处于欠压状态。解决方法是先量 VDD 引脚电压确认在 2.4V 以上然后分别尝试 0x44 和 0x45 两个 7 位地址做扫描。地址左移这一层最容易看走眼确认你传入 HAL 函数的是 8 位地址而不是原始 7 位地址。5.2 现象二读出的数据全 0xFF 或 CRC 频繁失败I2C 时序看起来对但读回数据不是 0xFF 就是校验不过。这种问题十有八九是上下拉配置出了问题。F13x 的 I2C 引脚必须是开漏输出模式如果你在 CubeMX 的 GPIO 设置里手动改成了推挽输出SCL 的高电平就被强行拉高SHT30 拉低 SDA 时会和 MCU 输出打架数据全是乱的。解决方法是把 PB6、PB7 的 GPIO 模式恢复成开漏并确认外部上拉电阻存在。如果硬件已经做死了推挽软件上可以在初始化时重新配置成开漏但更推荐直接改板。5.3 现象三温度读数稳定偏高 2~3℃温度一直比室温高而且高得很有规律。这是板级热耦合问题不是传感器质量问题。SHT30 挨着 MCU 或者 LDO芯片功耗把 PCB 局部加热传感器测到的其实是板温。解决思路分两步软件上做长时间平滑滤波能减轻波动但治标不治本真正解决是把传感器从发热源旁边挪开至少 5mm或者在 PCB 上把传感器下方铺地挖空减少热量传导。如果结构上必须紧贴发热源可以考虑把测量周期拉长让传感器在待机时尽量回到环境温度。5.4 现象四HAL_I2C 卡死并返回超时错误程序跑一段时间后HAL_I2C_Master_Transmit开始一直返回HAL_TIMEOUT复位后又能正常一段时间。这是 I2C 总线被锁死了原因通常是总线上某个设备在传输中途掉电或者 SDA 被一个异常电平卡住。标准恢复手段是让 MCU 手动翻转 SCL 九次把总线状态机复位。HAL 库层面可以调用HAL_I2C_DeInit再HAL_I2C_Init重新初始化外设但这只是软件复位如果 SDA 还被拉死硬件开路也是白搭。我一般会在初始化函数里加一段总线恢复逻辑检测到超时后先把 I2C 外设释放把 SCL 配置成普通 GPIO 输出手动产生九个时钟脉冲再重新配回 I2C 功能。5.5 现象五RTOS 任务抢占导致测量偶发失败在 FreeRTOS 里跑例程低频采样没问题高频采样时偶发读到明显错误值。原因是测量命令和读取数据之间如果被其他任务打断I2C 总线上插入了别的设备通信SHT30 的数据窗口就被错过了。解决方法是给 I2C 操作加互斥信号量或者把 SHT30 的测量命令、延时、读取三个动作放进同一个临界区。优先级高的任务抢占也会拖长延时导致芯片转换完成后数据没及时取走。注意 SHT30 的数据在读出之前会一直保留所以晚读不会丢数据但别让总线在数据读取过程中被别人占用。6. 先用一台参考仪器验证整条链路再固化低功耗轮询模式6.1 串口连续采样 30 分钟的验证步骤把例程烧进板子之前先准备一台能作为基准的温湿度计放在同一个环境里 10 分钟让两者热平衡。然后板子串口以 1Hz 频率输出记录 30 分钟数据重点看三件事温度偏差是否在 ±0.3℃ 以内湿度偏差是否在 ±3%RH 以内CRC 失败次数是否为 0。如果 CRC 失败率不是 0说明布线或者电源有问题后面所有数据都不能信。如果温度和基准仪器有固定偏差那可能是传感器封装受热导致优先排查板级布局而不是改代码。6.2 把例程改成低功耗轮询的关键修改验证通过后如果项目是电池供电把例程改成低功耗轮询。核心思路是平时 SHT30 处于待机状态MCU 也休眠到了采样周期先唤醒 MCU给 SHT30 发单次测量命令等转换完成后读数据然后立刻让芯片回到待机。测量命令本身就是一次性的芯片完成单次测量后会自动睡眠所以代码上不用额外发睡眠命令。需要注意的一点是如果传感器由 GPIO 口供电以彻底关断功耗重新上电后要等待至少 20ms 再发 I2C 命令让芯片内部电源稳定后再通信。从那以后我做任何 I2C 传感器移植都强制走一遍地址左移、上拉电阻、开漏输出、时序等待这四件套检查再开始写业务代码。这套 SHT30 HAL 库例程的代码结构足够清爽直接照着拆就能省掉不少排查时间希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?