
简介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°CSHT35甚至更高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接VDD0x45很多新手第一次调试时读不到数据十有八九是地址搞错了。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高频mps10重复性高0x2C10中频mps10重复性中0x2C0D低频mps10重复性低0x2400低频mps1重复性低重复性会影响测量噪声和功耗但不会显著影响精度。如果项目对响应速度要求不高、电池供电优先选0x2400如果数据波动较大、希望读数更平稳就选0x2C06。注意这是所谓的“高频”不是测量速度更快而是传感器内部采样平均次数更多噪声更低。别被命名误导了。周期测量模式常用的命令有0x21300.5 mps、0x21321 mps等这里mps是每秒测量次数。周期模式的优点是主控不必每次发起测量命令能省掉一部分通信时间适合数据需要连续刷新的场景但我个人在大多数项目里还是用单次模式逻辑更简单也不容易出状态机问题。2.3 CRC8校验不校验等于埋雷I2C本身只保证数据帧的传输完整性不保证内容正确。传感器测量值在传输过程中如果被干扰读回来可能就是错的。SHT3x对每条测量数据都附带一个CRC8校验字节这一点是它比很多国产传感器厚道的地方。但反过来说很多人的代码里压根没写校验函数等于没利用这个特性。CRC8的算法是多项式0x31x^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左移一位是0x880x45左移一位是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°C65535对应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校验失败、数据超范围四类再配合逻辑分析仪看波形基本十分钟就能定位问题所在。好的驱动不是写完就完事它是给未来半年后的自己留的退路。本文还有配套的精品资源点击获取