☰
STM32F130ZET6驱动SHT30温湿度传感器:HAL库I2C例程详解
2026/10/16 8:23:12 网站建设 项目流程

简介:面向STM32F130ZET6开发者的SHT30温湿度传感器HAL库例程,可帮助嵌入式爱好者快速掌握I2C通信与温湿度数据采集,适用于智能家居、环境监测、农业设备、医疗设备等场景。资源共231个文件,压缩包大小约10.22MB,主体包含I2C、UART、TIM等HAL驱动源文件(c/h),IAR与Keil的工程配置文件(eww、ewp、uvprojx、uvoptx),CubeMX生成的硬件初始化工程(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 地址是 0x44(7 位地址),如果传感器的 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.5V,F130ZET6 的 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,也就是 32768,RH = 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 跑到 72MHz,APB1 时钟 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;反过来你想用 0x45,ADDR 却没接,自然找不到设备。还有一种可能是传感器供电没起来,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 库例程的代码结构足够清爽,直接照着拆就能省掉不少排查时间,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询