news 2026/9/3 23:38:41

STM32驱动SHT30温湿度传感器:从I2C时序到工程实践详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
STM32驱动SHT30温湿度传感器:从I2C时序到工程实践详解

简介:面向STM32单片机开发者的SHT30温湿度传感器驱动工程,基于I2C通信实现温湿度采集,覆盖硬件配置、驱动开发、数据读取、CRC校验及温湿度转换公式等关键环节,适用于物联网、智能家居、环境监测等场景。工程采用STM32CubeMX完成底层初始化,并适配1.54寸TFT屏显示,支持单次采样与周期采样两种模式切换。资源共156个文件,约488KB,以C源文件、头文件、Keil工程配置(uvprojx)及编译输出文件(o、hex、axf)为主,其中源文件与头文件构成完整驱动代码,hex/axf可直接烧录验证;CubeMX配置文件(ioc)便于重新生成初始化代码。目前已有115人学习,适合需要快速移植SHT30驱动或学习I2C传感器开发的中初级嵌入式工程师。借助该工程可跳过底层寄存器配置,直接掌握采样触发、数据校验、温湿度换算与屏幕显示的实现思路,缩短项目开发周期。

1. 整体设计思路与环境准备

1.1 需求定位:为什么是SHT30

做嵌入式这几年,我接触的温湿度传感器不算少。DHT11便宜但抗干扰差,读时序的时候主控稍微忙一点就容易卡死,数据还得靠软件校准;DHT22精度还行,可响应速度慢,体积也偏大。后来项目需要加一个环境监测功能,我把SHT30列为首选,理由很直接:I2C接口,布线简单;精度0.2°C和2%RH,日常环境监测完全够用;功耗低,电池供电的便携设备也扛得住。最让我满意的是它内部集成了校准电路,出厂前已经标定好,我们不需要像DHT系列那样自己做补偿。

SHT30的数字输出格式也很友好。它返回16位温度原始值和16位湿度原始值,每段后面跟一个CRC校验字节,转换公式是官方写死的线性关系,算起来没有太多弯弯绕。相比SHT31和SHT35,SHT30的性价比更高,大部分智能化产品、农业大棚监测、机房环境看板这类场景,它的性能已经绰绰有余。

这篇文章就以我在STM32F103平台上的实际项目为背景,完整复盘从硬件接线、I2C时序到驱动代码、调试排障的整个流程。如果你正准备做自己的第一个传感器驱动工程,或者想把SHT30驱动移植到别的平台,这篇内容可以直接当参考。

1.2 工程目录与代码分层

很多初学者拿到SHT30的例程,直接把所有代码塞进一个main.c,能用但没法维护。我习惯把驱动工程分成三层:底层是I2C读写接口,中间是SHT30专属命令和数据解析,上层是应用逻辑。这样以后换传感器、换主控,只动对应层就行。

我的工程目录大致是这样:

Project/ ├── User/ │ ├── main.c │ └── sht30_app.c ├── Driver/ │ ├── sht30.c │ ├── sht30.h │ ├── i2c_soft.c │ └── i2c_soft.h ├── Hardware/ │ └── delay.c └── MDK-ARM/

这种拆法的好处是:sht30.c里面只关心SHT30的命令字和数据结构,不关心底层I2C是用硬件外设还是GPIO模拟;i2c_soft.c处理位带操作和时序,完全不认识SHT30。我这次用的STM32标准外设库,配合Keil5开发环境,硬件调试用ST-Link Utility下载固件。如果你习惯用HAL库,接口层稍作封装,核心驱动代码可以直接平移。

2. SHT30关键参数与底层通信解析

2.1 I2C地址与硬件接线

SHT30支持两个I2C从机地址,由ADDR引脚的电平决定。ADDR接地时地址是0x44,接高电平(一般是VDD)时地址是0x45。这里的地址是7位地址,I2C通信时左移一位变成8位,读写位补在最低位。很多人第一次调试读不到数据,就是忘记左移,直接在代码里写了0x44当成8位地址用。

我建议接线的时候把ADDR引脚直接接地,这样7位地址固定为0x44,换算成8位写地址是0x88,读地址是0x89。如果项目里需要挂两片SHT30,就让一片的ADDR接地、另一片的ADDR接VDD,两个地址正好分开。

硬件上除了VCC、GND之外,SCL和SDA都需要接上拉电阻。SHT30模块大多数已经板载了4.7k或者10k上拉,如果是自己画的PCB,记得在SCL和SDA各加一个4.7k电阻到VCC。没有上拉电阻的话,I2C总线拉不高电平,通信直接失败。

2.2 测量命令与数据返回格式

SHT30的命令都是16位,发送的时候先发高字节再发低字节。常用的单次测量命令如下:

命令说明重复性
0x2C 0x06高重复性测量精度与功耗较高
0x2C 0x0D中重复性测量均衡
0x2C 0x10低重复性测量低功耗

我平时建议直接用0x2C 0x06,高重复性模式下温度和湿度的噪声更低。在高速测量场景下用低重复性模式可以省电,但数据跳动会明显一些。

发起一次单次测量后,需要等待一段时间再读取数据。高重复性模式典型转换时间是12.5ms,实际代码里我一般延时至20ms左右,确保数据稳定。读取时一次连续读6个字节:

Byte0: 温度高字节 Byte1: 温度低字节 Byte2: 温度CRC Byte3: 湿度高字节 Byte4: 湿度低字节 Byte5: 湿度CRC

温度原始值和湿度原始值都是无符号16位数,范围0到65535。计算公式如下:

温度 = -45 + 175 * (rawTemp / 65535.0) 湿度 = 100 * (rawHumidity / 65535.0)

所以温度分辨率大约是0.00267°C,湿度分辨率大约是0.00153%RH,对绝大多数监测需求来说绰绰有余。

2.3 CRC校验与数据可靠性

有些开发者会忽略CRC校验,认为I2C本身有应答机制就够了。实际上,在电机启动、继电器吸合的现场,电源和信号线上的干扰很容易把数据字节改坏,这时CRC是唯一能检测到错误的防线。

SHT30的CRC多项式是x^8 + x^5 + x^4 + 1,即多项式值0x31,初始值为0xFF。实现代码如下:

uint8_t sht30_crc8(uint8_t *data, uint16_t len) { uint8_t crc = 0xFF; uint8_t i; while (len--) { crc ^= *data++; for (i = 0; i < 8; i++) { if (crc & 0x80) crc = (crc << 1) ^ 0x31; else crc <<= 1; } } return crc; }

每次读完6个字节,分别对温度两字节和湿度两字节做CRC校验,校验值不匹配就丢弃本次数据,重新发起测量。我实测下来,加上CRC校验后,系统在强干扰环境下几乎不会出现异常温湿度值。

3. 驱动代码实现:从初始化到数据解析

3.1 底层I2C接口的两种实现方式

STM32的I2C外设口碑两极分化,主要是意法半导体的硬件I2C在早期固件库的配置上有不少坑,很多人被时序问题折磨过后干脆用GPIO模拟。我在这个项目里也用了模拟I2C,简单可靠,不受引脚复用限制。核心代码如下:

#define SDA_IN() { GPIOB->CRH &= 0xFFFF0FFF; GPIOB->CRH |= 0x00008000; } #define SDA_OUT() { GPIOB->CRH &= 0xFFFF0FFF; GPIOB->CRH |= 0x00003000; } #define I2C_SCL_H() GPIO_SetBits(GPIOB, GPIO_Pin_10) #define I2C_SCL_L() GPIO_ResetBits(GPIOB, GPIO_Pin_10) #define I2C_SDA_H() GPIO_SetBits(GPIOB, GPIO_Pin_11) #define I2C_SDA_L() GPIO_ResetBits(GPIOB, GPIO_Pin_11) #define I2C_SDA_READ() GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11)

启动信号是SCL高电平时SDA拉低,停止信号是SCL高电平时SDA拉高。发送一个字节时从高位开始,逐位把数据放到SDA线上,SCL拉高再拉低完成一个时钟周期。读字节时把SDA配置为输入,每个时钟周期从SDA上读一位。

如果你用的是HAL库,也可以用硬件I2C:

HAL_I2C_Master_Transmit(&hi2c1, (uint16_t)(addr << 1), cmd, 2, 100); HAL_I2C_Master_Receive(&hi2c1, (uint16_t)(addr << 1), data, 6, 100);

硬件的优点是占用CPU少、时序由外设保证,缺点是HAL库的阻塞式调用在延时上比较浪费,中断方式又增加代码复杂度。我的建议是:如果这个项目对功耗和实时性要求不高,模拟I2C完全够用;如果后续要接多个I2C设备,硬件I2C加DMA会是更好的选择。

3.2 SHT30核心驱动代码

初始化函数很简单,上电后等待10ms让传感器稳定,然后发一条软复位命令0x30 0xA2,再等10ms。之后就可以正常通信了。

void sht30_init(void) { delay_ms(10); sht30_write_cmd(0x30, 0xA2); // soft reset delay_ms(10); } uint8_t sht30_read_temperature_humidity(float *temp, float *humi) { uint8_t buf[6]; uint16_t raw_temp, raw_humi; uint8_t temp_crc, humi_crc; sht30_write_cmd(0x2C, 0x06); // single shot, high repeatability delay_ms(20); i2c_start(); i2c_send_byte(0x88); // 0x44 << 1 | 0 if (i2c_wait_ack() != 0) { i2c_stop(); return 1; } i2c_send_byte(0x00); // read start at register 0x00 i2c_wait_ack(); i2c_start(); // restart i2c_send_byte(0x89); // 0x44 << 1 | 1 i2c_wait_ack(); for (int i = 0; i < 6; i++) { if (i < 5) buf[i] = i2c_read_byte(1); // send ACK else buf[i] = i2c_read_byte(0); // send NACK } i2c_stop(); temp_crc = sht30_crc8(&buf[0], 2); humi_crc = sht30_crc8(&buf[3], 2); if (temp_crc != buf[2] || humi_crc != buf[5]) return 2; // CRC error raw_temp = (buf[0] << 8) | buf[1]; raw_humi = (buf[3] << 8) | buf[4]; *temp = -45.0f + 175.0f * raw_temp / 65535.0f; *humi = 100.0f * raw_humi / 65535.0f; return 0; }

有一点要注意:单次测量模式下,每次读数据之前都要重新发一次测量命令,否则读出来的是旧数据。连续测量模式下传感器会自动更新,不需要每次发命令,但功耗会高一些。

3.3 数据滤波与工程应用

传感器原始数据再怎么稳,在真实环境里也会有波动。比如人从旁边走过带起一阵风,或者空调出风口刚好对着传感器,温度值就会出现瞬时跳变。

我做环境监测时习惯加一阶低通滤波:

static float last_temp = 0.0f; float now_temp = temp; if (last_temp == 0.0f) last_temp = now_temp; else last_temp = last_temp * 0.7f + now_temp * 0.3f; temp = last_temp;

0.3的权重系数是我反复试出来的,滤波后数据既不太迟钝,也不会被瞬时尖峰带偏。如果你想更严谨,可以加限幅滤波:如果相邻两次差值超过阈值,直接丢弃本次数据。对于温湿度这种变化缓慢的对象,一分钟采集一次就已经足够,滤波效果非常理想。

4. 常见问题与排查技巧实录

4.1 通信失败:地址、上拉、引脚冲突

我刚开始调试SHT30时,最常遇到的问题就是SCL和SDA波形拉不高。用示波器看波形,发现数据线只有一点几伏,明显不对劲。查了半天,发现是模块的板载上拉电阻焊的是10k,而我在面包板上又加了一根长杜邦线,线缆的寄生电容把信号边沿拖慢了。

解决方法是把I2C速率从400kHz降到100kHz,或者把上拉电阻换成4.7k。再不行就用短一点的杜邦线,毕竟面包板的寄生电容已经不小。

另一个隐蔽的坑是引脚复用冲突。我用的是STM32F103的PB10和PB11,这两个引脚刚好不是JTAG占用的口,但如果选了PB3、PB4或者PA15这几个JTAG引脚,需要先禁用JTAG功能,否则怎么调都调不通。STM32的JTAG默认占用PA13、PA14、PA15、PB3、PB4,这几个引脚在代码里直接配置成普通GPIO是没反应的,得先调用:

GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);

4.2 温湿度数据跳变与环境干扰

数据跳变这个问题,我遇到过两次完全不同的原因。第一次是传感器放在PCB板角的走线附近,板子上有一路开关电源,纹波直接耦合到SDA线上,导致CRC校验偶尔失败,数据瞬间跳到负值。解决方法是把传感器挪远一点,电源走线绕开I2C信号线,然后在传感器的VCC和GND之间加一个0.1uF去耦电容。

第二次是采集周期太短。我把单次测量命令放在一个1ms定时器中断里发,结果传感器转换时间还没到,中断又发了一次命令,数据就越读越乱。后来我单独用了一个10ms软件定时器,每次发命令后至少等20ms再读数据,问题就消失了。

还有一个容易被忽略的点:SHT30是高精度传感器,但它的响应速度不像热电偶那么快。你要是拿嘴对着哈气,数据大概要好几秒才能稳定。所以判断传感器好坏时,至少观察30秒再下结论,别一看到数据波动就觉得坏了。

4.3 ST-Link烧录与调试杂项

开发过程中还有几个和驱动本身无关但很影响效率的问题。Keil5如果没有正确安装对应芯片的Pack包,编译时会提示找不到目标芯片。解决办法是在Keil的Pack Installer里选择STM32F1系列,或者去官网下载DFP包手动安装。

如果ST-Link Utility连接不上芯片,先检查ST-Link引脚接线,SWDIO、SWCLK、GND三根先连好,目标板最好单独供电,避免ST-Link供电不够把整个板子拖崩。下载失败时按一下复位键再试,很多时候是因为芯片还在运行,调试接口没来得及响应。

4.4 供电与参考电压细节

SHT30的供电范围是2.4V到5.5V,但测量精度在3.3V供电时最理想。如果你的主控是5V供电,最好给传感器单独配一个3.3V的LDO,别直接从5V引脚供电。另外,SHT30的电源引脚对噪声比较敏感,它内部的ADC是Σ-Δ型的,电源纹波过大会直接影响测量结果。

我实测过,同一颗SHT30,在3.3V干净电源下和5V经过长导线供电下,测出来的温度能差到0.5°C左右。所以如果你的数据精度一直达不到标称值,先查电源,再查布局,最后才怀疑传感器本身。

5. 工程扩展与移植经验

5.1 多路SHT30的挂载与独立控制

一个I2C总线上最多可以挂两片SHT30,靠ADDR引脚区分地址。ADDR接地为0x44,ADDR接VDD为0x45。我在一个机柜环境监测项目里就是这么干的,上下两个位置各放一片,同时采集,数据分别上报。

初始化的时候分别对两个地址发软复位命令,测量时也是分别发命令、分别读数据。需要注意的是,同一时刻不要对两个从机同时发起测量命令,否则总线竞争会导致数据错乱。正确做法是先发第一片的测量命令,等转换完成、读完数据,再操作第二片。虽然多花一点时间,但可靠性高得多。

5.2 向其他平台移植的思路

做这个驱动之前,我还在K210开发板上跑过SHT30,后来又要迁移到STM32平台。核心驱动代码几乎没怎么动,只改了底层I2C读写函数。这说明抽象层的设计非常关键。

移植时注意以下几点:

  • 把I2C的启动、停止、读写字节封装成独立的函数,平台相关的代码只在这几个函数里出现。
  • 延时函数也尽量用统一的接口,比如delay_ms,底层实现可以依赖定时器,也可以用系统滴答。
  • CRC校验和温湿度计算是纯数学逻辑,完全跨平台,不用改。
  • 如果目标平台支持硬件I2C,直接把底层几个函数替换成对应库的API即可。

如果ST公司的HAL库版本不同,I2C的句柄类型可能不一样,但只要封装层做得好,上层根本察觉不到。

5.3 低功耗场景的设计建议

如果你的产品是电池供电,SHT30的低功耗特性就非常重要。建议用单次测量模式,每次测量完成后让传感器进入空闲状态,主控也进入睡眠,等需要数据的时候再唤醒。

我在一个温湿度记录仪项目里,用SHT30单次测量模式加STM32的停机模式,采集间隔设成60秒一次,两颗AA电池居然撑了半年多。具体做法是:主控醒来后发测量命令,然后等20ms读数据,存进Flash,再进入停机模式。SHT30在单次测量模式下的平均功耗极低,整体功耗大头反而在主控的唤醒和Flash写入上。

6. 调试工具与实测数据分享

6.1 调试工具组合推荐

没有趁手的工具,驱动调试就是抓瞎。我日常调试SHT30的组合如下:

工具用途
ST-Link V2程序下载与在线调试
逻辑分析仪抓取I2C时序波形
ST-Link Utility下载固件、查看Flash
串口助手打印温湿度数据
万用表检查上拉电阻与供电

逻辑分析仪强烈推荐入手一个,几十块钱的就能用,接上SCL和SDA以后,通信有没有ACK、数据对不对,一眼就能看出来。我第一次调通I2C,就是靠逻辑分析仪发现发送从机地址后没有得到ACK,才意识到地址左移的问题。

6.2 实测数据与稳定性对照

我在室温环境下用同一颗SHT30做了连续8小时采集测试,环境温度在25°C附近缓慢波动。加入CRC校验和滤波算法后,温度数据的最大跳变量从0.3°C降到了0.08°C,湿度数据也稳定在±0.5%RH以内。

数据稳定性提升的关键不是传感器本身,而是三点:电源干净、I2C速率别太高、采集间隔别太短。有一个朋友跟我反馈,他把采集间隔从1秒改成5秒后,数据乱跳的问题几乎消失了。原因很简单,SHT30每次测量都是独立的,频繁启动会让内部稳定时间不够,尤其是高重复性模式下,转换时间要12.5ms左右,读太快反而容易出错。

6.3 与其它传感器方案的选型建议

如果你的项目预算很紧,DHT11也能用,但要做好数据误差大、响应慢的心理准备。DHT22精度稍好,价格也不贵,但单总线协议在代码层面不如I2C舒服。如果项目对体积要求高,可以考虑SHTC3,封装更小,通信协议类似。如果对精度有极致要求,SHT35精度更高,但价格也贵不少。

从驱动开发的角度看,SHT30的寄存器结构最规整,命令字不多,数据手册写得清楚,非常适合作为I2C传感器驱动的入门练习。不少网友在留言里问我,第一次做传感器驱动选什么好,我一般就推荐SHT30,踩坑少,成就感来得快。

7. 手上这套方案的最终心得

回头看这个STM32的SHT30温湿度计驱动工程,其实核心工作就是把一个数字传感器的通信协议吃透,然后做好分层的代码封装。I2C时序、寄存器读写、CRC校验这些,说白了都是套路,一旦理解了原理,以后换成别的I2C传感器基本上半天就能搞定。

我现在自己在用的这套代码,已经在两个量产的壳子里稳定跑了大半年,没出过数据错乱的问题。如果让我重新做一遍,我可能会直接用HAL库的硬件I2C加DMA,把CPU占用再降一降。但对于学习来说,从模拟I2C开始动手,才能真正理解时序的每一个细节,这对后面排查更复杂的通信问题帮助很大。

最后再分享一个小技巧:如果你手头有之前调好的ST-Link和串口模块,调试阶段尽量把传感器的原始温度字和湿度字直接打印出来,不要只打印最终换算后的物理量。看到原始值,你才能判断数据是通信层的问题还是计算层的偏差。用熟了以后,你会发现调试效率比原来高出一大截。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/3 23:38:35

嵌入式软件面试全解析:从C语言内存到RTOS项目的模拟实战

很多刚准备嵌入式软件工程师面试的同学&#xff0c;最容易陷入的一个误区是&#xff1a;刷了一大堆“嵌入式八股文”&#xff0c;把 volatile、static、指针和大小端背得滚瓜烂熟&#xff0c;结果面试官换一个问法&#xff0c;或者把问题抛到一个实际场景里&#xff0c;当场就卡…

作者头像 李华
网站建设 2026/9/3 23:34:20

3线SPI非库驱动ADXL345:寄存器操作与调试踩坑全记录

简介&#xff1a;基于STM32平台的3线非库SPI方式连接ADXL345加速度计完整工程包&#xff0c;面向嵌入式开发者&#xff0c;可在不依赖标准库的条件下实现三轴加速度数据读取&#xff0c;适合资源受限或要求底层可控的场景。包内共22个文件&#xff0c;包含C源文件、头文件、启动…

作者头像 李华
网站建设 2026/9/3 23:32:48

AI内容审核的困境与出路:从技术对抗到人机协同治理

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/3 23:30:13

SAR成像距离多普勒算法(RDA)核心原理与工程实现详解

简介&#xff1a;针对SAR成像算法中的距离徙动校正与RDA实现&#xff0c;这套资源以MATLAB脚本形式提供了根据回波信号生成SAR图像的完整示例&#xff0c;贴近遥感成像处理的实际验证场景。面向遥感信号处理学习者、SAR成像算法入门者&#xff0c;以及需要对比RCMC改进效果的研…

作者头像 李华
网站建设 2026/9/3 23:26:56

iKF Ear Clip耳夹式耳机评测:舒适稳固佩戴与实用音质体验

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/3 23:26:17

STM32驱动国产24位高精度ADC CS1238:从时序到代码实践

简介&#xff1a;STM32平台使用的CS1238/CS1237 ADC驱动源码包&#xff0c;面向嵌入式开发与传感器数据采集场景&#xff0c;解决在STM32上快速接入12位逐次逼近型ADC芯片的驱动问题。资源体积仅2KB&#xff0c;共2个文件&#xff0c;其中C源文件实现芯片初始化、采样率配置、启…

作者头像 李华