简介:SHT3x高精度温湿度传感器是盛思锐(Sensirion)推出的数字温湿度检测器件,采用CMOSens®技术,可同时输出温度与相对湿度。围绕该传感器整理的代码包面向嵌入式开发者、STM32用户及电子竞赛爱好者,可帮助快速完成驱动移植、数据解析与项目集成,适用于环境监测、智能家居、工业自动化等场景。压缩包共13个文件,以sht3x.c/h等C源码与头文件为核心,覆盖I²C接口配置、测量命令发送、原始数据读取及温湿度换算逻辑;同时包含Keil工程文件(uvproj/uvopt)、STM32启动汇编文件、Readme说明和PDF结构指南,文件类型覆盖源码、工程配置与文档,便于直接导入工程进行编译与调试,也适合按目录逐层学习。资源整体仅34KB,体积轻盈,目前已有995人学习。配套文档对初始化、单次/连续测量、数据解析和错误处理等关键环节做了梳理,能帮助开发者理解底层交互原理,并在实际项目中快速复用与二次开发。
1. 先说结论:为什么我最终选了SHT3x而不是DHT11
做环境监测类项目时,温湿度传感器几乎是绕不开的器件。市面上最常见的DHT11便宜、资料多、教程一搜一大把,但如果你做过几次正经的数据记录或者产品原型,大概率会碰到它的一些硬伤:精度一般、响应偏慢、时序要求苛刻,最难受的是不同批次之间一致性不太行,同一批买回来的几个模块读出来的数能差出两三度。
SHT3x是Sensirion家的数字温湿度传感器,典型精度能做到±2%RH和±0.2°C(SHT35甚至更高),I2C接口,自带CRC8校验,还支持周期测量、加热器等功能。对于需要稳定、精确数据的上位机应用,或者想在STM32、ESP32上快速集成的场景,SHT3x比DHT11省心得多。这篇文章就把我实际调SHT3x的完整思路、代码细节和踩坑记录整理出来,适合正在做嵌入式驱动、环境数据采集或想把手头DHT11项目升级一下的朋友参考。
有人可能担心SHT3x比DHT11贵,这确实是个现实问题。但换个角度想:如果项目要过计量、要长期运行、要远程上报数据,那传感器本身的成本差异远小于后期返工调试的代价。我自己是从DHT11一路用过来的,后来转SHT30之后基本没有再为温湿度数据的可靠性头疼过。
2. 硬件接口与通信协议:弄懂I2C上的这几个坑
2.1 引脚、I2C地址与上拉电阻的选型逻辑
SHT3x的标准封装是DFN-8,一共8个引脚,但实际用到的就4个:VDD、GND、SDA、SCL,外加一个可选的ADDR引脚用于修改I2C地址,还有几个不接的保留引脚。最关键的是把ADDR处理对,因为它的电平直接决定I2C地址:
| ADDR引脚状态 | I2C 7位地址 |
|---|---|
| 接GND或悬空 | 0x44 |
| 接VDD | 0x45 |
很多新手第一次调试时读不到数据,十有八九是地址搞错了。I2C总线允许挂多个SHT3x,就是靠ADDR引脚区分,一个系统里最多能挂两个。实际布线时SDA和SCL都需要接上拉电阻,常见取值4.7kΩ,但如果你用的是400kHz快速模式,建议换成2.2kΩ或3.3kΩ,否则上升沿太慢容易导致通信不稳定。我习惯在传感器模块上直接焊10kΩ上拉,然后MCU那边再开内部上拉,两个并联大约5kΩ左右,也够用。
供电方面SHT3x支持2.4V到5.5V,但需要注意的是,如果MCU的IO电平是3.3V,传感器也最好用3.3V供电,避免I2C电平不匹配。5V供电时接口电平会偏高,部分MCU的IO不一定承受得住。我见过有人用5V供电然后把SCL/SDA直接连到3.3V的ESP32,虽然短期内没烧,但长期可靠性确实是个隐患。
2.2 测量命令与时序:单次测量和周期测量怎么选
SHT3x的命令集分为两大块:单次测量模式和周期测量模式。单次测量模式适合按需读取的场景,比如低功耗设备每隔几秒醒来采一次数据;周期测量模式则适合持续监测,传感器会按设定频率自动测量并缓存结果,主机随时可以读取。
单次测量模式下的常用命令:
| 命令值 | 说明 |
|---|---|
| 0x2C06 | 高频(mps=10),重复性高 |
| 0x2C10 | 中频(mps=10),重复性中 |
| 0x2C0D | 低频(mps=10),重复性低 |
| 0x2400 | 低频(mps=1),重复性低 |
重复性会影响测量噪声和功耗,但不会显著影响精度。如果项目对响应速度要求不高、电池供电,优先选0x2400;如果数据波动较大、希望读数更平稳,就选0x2C06。注意这是所谓的“高频”不是测量速度更快,而是传感器内部采样平均次数更多,噪声更低。别被命名误导了。
周期测量模式常用的命令有0x2130(0.5 mps)、0x2132(1 mps)等,这里mps是每秒测量次数。周期模式的优点是主控不必每次发起测量命令,能省掉一部分通信时间,适合数据需要连续刷新的场景,但我个人在大多数项目里还是用单次模式,逻辑更简单,也不容易出状态机问题。
2.3 CRC8校验:不校验等于埋雷
I2C本身只保证数据帧的传输完整性,不保证内容正确。传感器测量值在传输过程中如果被干扰,读回来可能就是错的。SHT3x对每条测量数据都附带一个CRC8校验字节,这一点是它比很多国产传感器厚道的地方。但反过来说,很多人的代码里压根没写校验函数,等于没利用这个特性。
CRC8的算法是多项式0x31(x^8 + x^5 + x^4 + 1),初值0xFF,按位处理。实现并不复杂,几十行C代码就搞定。后面的代码示例里我会给出直接能用的实现,读者可以直接复制进工程。一定要在读完数据之后校验,校验失败就重读或者报错,而不是硬着头皮用错误数据。
还有一个细节:读温度后跟的CRC校验字节,对应的只是温度两个字节;读湿度后跟的CRC校验字节,对应的只是湿度两个字节。也就是说读6个字节:温高、温低、温CRC、湿高、湿低、湿CRC。别把顺序搞反了,我见过有人把第三字节当成湿度的,结果读数怎么算都不对。
3. 核心代码实现:从裸机I2C读到完整驱动
3.1 读取一帧数据的完整流程
SHT3x的读取流程其实非常清晰,就三步:发命令、等转换、读数据。转换时间跟重复性设置有关,高频模式下典型等待时间是7.5ms左右,中频是6.5ms,低频是4.5ms。工程上为了稳妥起见,我都会多留一点余量,比如发完命令后延时10ms再读。
读取流程示意图大致如下:
- 主机发送I2C起始条件
- 发送传感器地址+写位(0x88,即0x44左移一位)
- 发送两个字节的命令
- 主机发送停止条件
- 延时等待(建议10ms,保守一点15ms也可以)
- 主机发送起始条件
- 发送传感器地址+读位(0x89,即0x44左移一位加1)
- 连续读取6个字节,每读一个字节后主机回应ACK,读最后一个字节时应回NACK
- 主机发送停止条件
- 对温度、湿度数据分别做CRC校验
- 用公式计算物理值
3.2 关键代码片段:发送命令与读取数据
下面的代码是基于STM32标准库的HAL风格伪代码加实际可运行逻辑改写的,读者移植到其他平台时只需要替换底层的I2C收发函数即可。
#define SHT3X_ADDR_0 0x44 // ADDR接GND #define SHT3X_ADDR_1 0x45 // ADDR接VDD #define SHT3X_CMD_MEAS_H 0x2C06 // 单次测量:高频,重复性高 uint8_t crc8(const uint8_t *data, size_t len) { uint8_t crc = 0xFF; for (size_t i = 0; i < len; i++) { crc ^= data[i]; for (uint8_t bit = 0; bit < 8; bit++) { if (crc & 0x80) crc = (crc << 1) ^ 0x31; else crc <<= 1; } } return crc; }实际上在每个项目里,还要细分出I2C起始/停止/读写字节函数。如果用的是HAL库,可以这样封装命令发送:
uint8_t sht3x_send_command(uint16_t cmd) { uint8_t buf[2]; buf[0] = (uint8_t)(cmd >> 8); buf[1] = (uint8_t)(cmd & 0xFF); // 如果使用HAL库 if (HAL_I2C_Master_Transmit(&hi2c1, SHT3X_ADDR_0 << 1, buf, 2, 100) != HAL_OK) return 1; return 0; }发送命令时,8位地址是7位地址左移一位拼上读写位。0x44左移一位是0x88,0x45左移一位是0x8A,这个细节在HAL库的API里经常被忽略。
读取测量结果的函数:
typedef struct { float temperature; float humidity; } sht3x_data_t; uint8_t sht3x_read_data(sht3x_data_t *out) { uint8_t buf[6] = {0}; // 发测量命令 if (sht3x_send_command(SHT3X_CMD_MEAS_H)) return 1; // 等待转换完成 delay_ms(10); // 读取6字节数据:温度高、温度低、温度CRC、湿度高、湿度低、湿度CRC if (HAL_I2C_Master_Receive(&hi2c1, (SHT3X_ADDR_0 << 1) | 1, buf, 6, 100) != HAL_OK) return 2; // CRC校验 if (crc8(&buf[0], 2) != buf[2]) return 3; if (crc8(&buf[3], 2) != buf[5]) return 4; // 计算物理量 uint16_t raw_temp = ((uint16_t)buf[0] << 8) | buf[1]; uint16_t raw_humi = ((uint16_t)buf[3] << 8) | buf[4]; out->temperature = -45.0f + 175.0f * (float)raw_temp / 65535.0f; out->humidity = 100.0f * (float)raw_humi / 65535.0f; return 0; }有个容易忽略的小问题:如果使用纯软件模拟I2C,那每一帧的起始和停止条件都要严格符合时序要求。SHT3x的最高I2C频率支持到1MHz,但用软件模拟时建议控制在100kHz到400kHz之间,频率太高容易因为GPIO翻转速度不足导致数据错误。实测下来,400kHz在大部分MCU上是极限了,再高就要用硬件I2C。
3.3 从裸数据到真实温湿度:公式背后的原理
SHT3x输出的原始数据是16位无符号整数,范围0到65535,对应不同的物理范围。官方公式:
- 温度:T = -45 + 175 × raw / 65535
- 湿度:RH = 100 × raw / 65535
从公式可以看出,温度原始值0对应-45°C,65535对应130°C。湿度原始值0对应0%,65535对应100%。精度上,16位ADC换算出来的分辨率为0.01°C和0.01%RH左右,远超传感器本身的精度指标,所以不用担心中间计算会产生额外误差。
浮点运算在PC端或带FPU的MCU上都不是问题,但如果你用的是老旧的8位单片机,比如STC89C52这种,浮点运算会拖慢速度。这种情况下可以先用整数计算把结果放大100倍存储,最后需要输出时再转字符串。个人经验是,SHT3x的实际应用场景基本是32位MCU,浮点不是瓶颈,别过度优化。
4. 常见问题排查与经验心得
4.1 问题速查表
| 症状 | 可能原因 | 解决方案 |
|---|---|---|
| 一直读不到设备,I2C返回超时 | 地址错误 | 确认ADDR引脚电平,核对0x44/0x45 |
| 读到的温湿度为0或固定值 | 数据字节顺序弄反 | 检查6字节的顺序:温高、温低、温CRC、湿高、湿低、湿CRC |
| 读数有跳变或错误值 | 缺少CRC校验 | 加上CRC8校验,错误时重读 |
| 读数偏高 | 传感器附近有发热器件 | 远离功率电阻、LDO、MCU |
| 湿度读数持续偏低 | 传感器暴露在气流中或PCB受热 | 加防风罩,优化布局 |
| I2C偶尔死锁 | 总线被拉低,从机卡在中间状态 | 重启总线(将SCL翻转9次)或断电复位 |
4.2 我踩过的坑和容易忽略的细节
先说I2C总线的死锁问题。传感器在测量过程中如果主机强行发起通信,有时候从机内部状态机会卡住,把SDA线拉低不放。这个时候最有效的恢复方式是把SCL时钟翻转9次,让从机退出异常状态,然后发送一个STOP条件。我在代码里会加一个总线恢复函数,在主循环初始化时调用,能显著减少运行期故障。
第二个坑是转换时间。很多例程里写的延时是“至少7ms”,但实际上在低温或高湿环境下,传感器转换时间可能会有少量波动,保守起见延时10到15ms更稳。自己打样测试时,把延时缩到5ms,结果发现大约每几十次就有一次读回全FF或者CRC错误,恢复延时之后问题消失。传感器转换时间这类参数,规格书给的是典型值,量产阶段要考虑最坏情况。
第三个问题是自发热。SHT3x本身功耗很低,但如果你把它紧挨着LDO或者WIFI模组,那测出来的温度会明显偏高。我见过有人把传感器贴在STM32主控旁边,测出来的温度比环境温度高3°C以上。解决办法是传感器脚和主控之间留空气间隔,或者用FPC软排线引出来,让传感器处于远离热源的位置。这个细节尤其影响精密测量场景,比如粮食仓储、药品运输、实验室环境监测。
第四个是湿度传感器的老化与污染问题。SHT3x虽然比DHT11耐造,但长期暴露在有机溶剂、烟雾、高浓度粉尘环境中,湿度测量值会出现漂移。Sensirion官方建议在PCBA清洗后要让传感器在正常环境条件下通风恢复一段时间再进行标定。另外,传感器表面不要用手直接触摸,油脂会显著影响湿度响应,这一点在装配调试环节特别容易翻车,因为我们调试时常会用手拿板子对比读数。
4.3 周期测量与低功耗调优
如果设备靠电池供电,单次测量模式是首选——平时让传感器处于休眠状态,需要数据时唤醒发送命令,读完后立即进入空闲。SHT3x待机电流实测可以做到0.2μA左右,完全不影响电池寿命。而周期测量模式下,传感器会保持活跃状态,功耗虽然也不高,但对低功耗设备的整体续航还是会有影响。
另外,SHT3x内置的加热器功能建议慎用。它可以用来去除传感器表面的冷凝水,但加热期间温度读数会显著偏高,最多可以高出十几度。除非是做防凝露场景,否则别在正常测量流程里打开加热器。我在做室内空气质量监测时,环境湿度长期偏高导致传感器内部结露,读数一直稳定在99%RH,后来靠加热器工作了几秒钟才恢复,但加热后至少需要几十秒等待温度回稳,这时候记录的数据是要丢弃的。
5. 一些最终建议:哪种情况下SHT3x才是最优解
做了几个月的SHT3x驱动开发后,我最大的体会是:传感器的选型决定了项目的天花板,而代码质量决定了项目的地板。SHT3x用起来简单,几行代码就能读到数据,但要让它长期稳定、可靠地工作,还是得在I2C时序、CRC校验、硬件布局上多花心思。
如果你的项目满足以下任意一条,我强烈建议考虑SHT3x:
- 温湿度数据需要用于判断逻辑,比如控制加热除湿设备、空凋联动
- 数据需要远程传输,无法人工核对,比如大棚监控、冷链运输
- 设备安装位置不方便二次维护,比如密封的配电柜、户外气象站
- 同一批设备需要一致性好、可比的测量结果
如果只是做个课堂作业,或者对精度完全不敏感,DHT11也能用,但我还是建议你把SHT3x的驱动框架搭一遍。这个传感器虽然是I2C接口,但它的命令设计非常规范,把CRC校验、状态机处理、异常恢复这些基础功打扎实了,以后换其他I2C传感器也会顺手很多。再加上SHT3x的代码量并不大,作为学习硬件通信协议的入门案例,我觉得比单纯调一个DHT11的时序有意思多了。
最后再分享一个小习惯:代码里把I2C读写返回的错误码定义清楚并打印出来。刚开始调SHT3x时我偷懒,只要读不到就一直重试,屏幕上什么也不打印,结果一个问题查了一下午。后来把每一步返回的错误码细化,分成了设备未响应、命令发送失败、CRC校验失败、数据超范围四类,再配合逻辑分析仪看波形,基本十分钟就能定位问题所在。好的驱动不是写完就完事,它是给未来半年后的自己留的退路。
本文还有配套的精品资源,点击获取