2. 温湿度传感器(SHT30):工作原理、I2C协议基础、SHT30寄存器配置、驱动代码实现与数据读取

好,咱们开始聊SHT30。这个传感器我用了好几年了,从早期的SHT10一路用过来,SHT30算是性价比非常均衡的一款。说实话,在消费级和工业级之间,它卡位卡得刚刚好。

2.1 SHT30的工作原理

SHT30是Sensirion公司出的数字温湿度传感器。它内部集成了一个电容式湿度传感元件和一个带隙温度传感元件,外加一个14位的ADC。嗯,这里要注意,它输出的已经是数字信号了,不需要你再去搞什么模拟量转换。

湿度测量范围是0%到100%RH,温度是-40°C到125°C。精度方面,典型值是±2%RH和±0.3°C。我在一个农业大棚项目里用过它,连续跑了两年,数据漂移很小,这点让我挺放心的。

它内部有个加热器,可以除冷凝水。不过我个人建议,除非你确定环境湿度极高,否则别开。加热器一开,温度读数会偏个几度,你得做补偿,挺麻烦的。

2.2 I2C协议基础

SHT30用的是I2C接口。I2C这玩意儿,说白了就是两根线:SCL(时钟线)和SDA(数据线)。所有设备都挂在这两根线上,靠地址来区分。

I2C的基本流程是这样的:

  • 主机发一个起始信号
  • 然后发7位设备地址+1位读写位
  • 从机应答
  • 然后开始传数据,每8位后跟一个应答位
  • 最后主机发停止信号

SHT30的I2C地址是0x44(ADDR引脚接GND时)。如果你把ADDR接VDD,地址就变成0x45。我在一个项目里同时用了两个SHT30,一个接GND一个接VDD,这样一根I2C总线上就能挂两个传感器,省了一个I2C接口。

关键点:SHT30的I2C通信速率最高支持1MHz,但很多单片机默认是100kHz或400kHz。我建议先用400kHz,稳定了再往上调。我曾经在1MHz下遇到过通信不稳定,降回400kHz就一切正常了。

2.3 SHT30寄存器配置

SHT30的寄存器不多,但有几个关键的你需要知道。

寄存器/命令 代码 说明
测量命令(高重复性) 0x2400 默认模式,精度最高
测量命令(中重复性) 0x240B 精度稍低,速度快
测量命令(低重复性) 0x2416 速度最快,精度最低
读取状态寄存器 0xF32D 检查传感器状态
软复位 0x30A2 复位传感器

我个人习惯用高重复性模式。虽然测量时间会长一点(约15ms),但数据更可靠。如果你做的是电池供电的设备,可以考虑用中重复性模式,省点电。

还有一个寄存器叫“加热器控制”,命令是0x306D。我前面说了,不建议轻易开它。我曾经在一个冷库项目里试过,开了加热器后温度读数直接跳了2°C,还得做软件补偿,得不偿失。

2.4 驱动代码实现

好,咱们直接上代码。这是基于STM32 HAL库写的,但逻辑是通用的,你移植到其他平台也很容易。

// SHT30驱动代码片段

#define SHT30_ADDR         0x44 << 1  // 左移1位,因为HAL库需要8位地址
#define SHT30_CMD_MEASURE  0x2400    // 高重复性测量命令
#define SHT30_CMD_RESET    0x30A2    // 软复位命令

// 发送测量命令
HAL_StatusTypeDef SHT30_SendMeasureCmd(void)
{
    uint8_t cmd[2];
    cmd[0] = (SHT30_CMD_MEASURE >> 8) & 0xFF;
    cmd[1] = SHT30_CMD_MEASURE & 0xFF;
    return HAL_I2C_Master_Transmit(&hi2c1, SHT30_ADDR, cmd, 2, 100);
}

// 读取温湿度数据
HAL_StatusTypeDef SHT30_ReadData(float *temp, float *hum)
{
    uint8_t buf[6];
    HAL_StatusTypeDef ret;

    // 先发测量命令
    ret = SHT30_SendMeasureCmd();
    if (ret != HAL_OK) return ret;

    // 等待测量完成,高重复性模式需要15ms
    HAL_Delay(20);

    // 读取6字节数据
    ret = HAL_I2C_Master_Receive(&hi2c1, SHT30_ADDR, buf, 6, 100);
    if (ret != HAL_OK) return ret;

    // 校验CRC(这里简化了,实际项目中一定要做)
    // 温度转换
    uint16_t raw_temp = (buf[0] << 8) | buf[1];
    *temp = -45.0f + 175.0f * (float)raw_temp / 65535.0f;

    // 湿度转换
    uint16_t raw_hum = (buf[3] << 8) | buf[4];
    *hum = 100.0f * (float)raw_hum / 65535.0f;

    return HAL_OK;
}

小技巧:代码里的CRC校验我故意省略了,但实际项目中千万别省。SHT30的数据包最后一位是CRC校验码,你不校验的话,万一通信受干扰,读出来的数据可能是错的。我曾经在一个电磁环境复杂的项目里吃过这个亏,数据偶尔跳变,加了CRC校验后才定位到是通信干扰。

2.5 数据读取与解析

数据读取其实就两步:发命令,等结果,读数据。但有几个细节你得注意。

第一,测量命令发完后,一定要等够时间。高重复性模式至少等15ms,中重复性等8ms,低重复性等3ms。别问我为什么知道,我曾经为了省那几毫秒,把延时改成5ms,结果读出来的数据全是0xFFFF。嗯,教训深刻。

第二,数据转换公式是固定的:

  • 温度(°C) = -45 + 175 * (原始值 / 65535)
  • 湿度(%RH) = 100 * (原始值 / 65535)

这个公式在SHT30的数据手册里有,别自己瞎改。我见过有人把温度公式里的175写成180,结果读出来的温度比实际高了2°C,排查了半天才发现是公式抄错了。

第三,如果你发现读出来的湿度超过100%或者温度超出范围,大概率是通信出了问题。先检查I2C地址对不对,再检查接线。我遇到过最奇葩的一次,是SDA线虚焊,时通时不通,读出来的数据一会儿正常一会儿乱跳。

注意事项:SHT30的供电电压是2.4V到5.5V,但如果你用3.3V供电,I2C的上拉电阻建议用4.7kΩ。用10kΩ的话,在400kHz速率下波形可能会变形。我习惯用4.7kΩ,兼容性最好。

好了,SHT30的内容就这些。说白了,它就是个用I2C通信的数字传感器,驱动写起来不复杂,但细节决定成败。你只要把时序搞对,把CRC加上,把延时给够,基本不会出问题。

下一章咱们聊气压传感器,那个比这个稍微复杂一点,但套路是一样的。