slytherinsun 发表于 2025-8-28 14:33

【APM32F402R Micro-EVB开发板测评】7、FreeRTOS任务定时读取SHT20相对湿度

本帖最后由 slytherinsun 于 2025-8-29 13:20 编辑

SHT20温湿度计简介
SHT20是一款数字输出的温湿度传感器,相对湿度测量范围0% - 100%,精度±3%,温度测量范围-40°C - 125°C,精度±0.3°C,支持I²C(400KHz)通讯接口。
本次测评使用自行制作的SHT20模块,使用I²C接口进行通讯,模块的I²C地址为0x40(不包含读写状态位)。
1.工作模式
SHT20的操作不同于BMP280,上电配置完成后需要主动发送测量命令进行温湿度的检测,发送一次测量命令即进行一次检测。
其工作模式特指的是在通讯过程中对SCL时钟线的控制方式,分别为Hold / No Hold Master Mode。
Hold Master是指发送了测量命令后,在测量转换阶段设备会阻塞时钟线SCL,直到测量完成才会释放时钟线。

No Hold Master Mode是指发送了测量命令后,在测量转换阶段设备会释放时钟线SCL,但是在测量完成前进行读取时设备不会回复ACK。

本次测试使用No Hold Master模式。
2.相关控制命令
SHT20是以" + [命令] + [数据(可选)]"的方式进行控制,以" + [数据] + [数据(可选)] + [数据(可选)]"的方式进行读取。
3.用户寄存器配置
SHT20只包含一个用户寄存器用于对设备进行配置,以" + [寄存器写命令] + [寄存器配置数据]"的方式进行配置,以" + [寄存器读命令]" + " + [数据]"的方式进行读取寄存器值。
用户配置寄存器包含的功能如下图,主要用于配置温湿度的分辨率数据位。

4.测量转换流程
SHT20支持标准的I²C读写协议,和对其的操作基于上一篇"【APM32F402R Micro-EVB开发板测评】6、FreeRTOS任务定时读取BMP280气压和温度"中的I²C读写接口。
4.1复位和初始化
先对SHT20进行复位和初始化的配置,即直接在芯片I²C地址后写入0xFE进行复位,等待复位完成后可以通过"写命令"操作 + "读取"操作来读取用户寄存器,通过"写命令 + [数据]"操作对用户寄存器进行配置。
4.2触发测量并读取数据
根据芯片手册查看对应分辨率数据位配置下数据的转换时间进行延时读取,配置延时参数如下图,等待转换完成后发送读取命令即可获取温湿度值。
每次读取可以获取3字节的数据,包含14Bit的温湿度数据,2Bit的状态数据,8Bit的CRC-8校验值。校验数据的Bit1为0时表示温度数据,Bit1为1时表示湿度数据,Bit0未分配。具体如下图:

4.3数据校验和计算
获取的温湿度数据需要进行CRC校验,SHT20的数据使用CRC-8,多项式为x8 + x5 + x4 +1的校验算法,只有读取到的数据通过了校验才是有效值,能够转换为可读的温湿度值。

5.代码编写
5.1复位初始化
对SHT20进的复位操作同样只需要在线程开始入口处执行一次,实现如下:
void Environ_Thread(void *argument)
{
    UNUSED(argument);

    volatile uint32_t env_flags = 0;
    volatile uint32_t env_times = 0;
    int32_t adc_temp, adc_pres, temp, temp_fine;
    uint32_t barometer;
    BMP280_trimming_param bmp280_trim = {0};
    SHT2X_Trans_Data      sht_data = {0};
    bmp280_reset(BMP280_I2C_ADDR);
    osDelay(5);
    bmp280_init(BMP280_I2C_ADDR, &bmp280_trim);
    osDelay(5);
    sht2x_init(SHT2X_I2C_ADDR);
    osDelay(50);
    while (1)
    {
      env_times++;
      env_flags = osThreadFlagsWait(0x00000001, osFlagsWaitAny, osWaitForever);
      osThreadFlagsClear(env_flags);
      bmp280_force_convert(BMP280_I2C_ADDR, BMP280_OSRS_X2, BMP280_OSRS_X16);
      osDelay(50);
      bmp280_read_adc(BMP280_I2C_ADDR, &adc_pres, &adc_temp);
      osDelay(2);
      sht2x_read_temp_humi(SHT2X_I2C_ADDR, &sht_data);
      temp = bmp280_calc_temperature(&temp_fine, adc_temp, &bmp280_trim);
      barometer = bmp280_calc_barometer(&temp_fine, adc_pres, &bmp280_trim);
      printf("Envir->baro(hPa)temp(°C)humi(RH):%.2f,%.1f,%.1f\n", (double)barometer / 100.0, (double)temp / 100.0, sht_data.humidity);
    }
}int8_t sht2x_init(uint8_t addr)
{
    int8_t ret;
    uint8_t usr_reg;
    I2C_DMA_Write_Byte(addr, SHT2X_SOFT_RESET);
    osDelay(50);
    I2C_DMA_Read_Byte(addr, SHT2X_READ_USER_REG, &usr_reg);
    if (0 < (int8_t)usr_reg) {
      usr_reg &= SHT2X_USER_REG_RESV;
      usr_reg |= SHT2X_DISABLE_OTP_RELOAD;
      ret = I2C_DMA_Write_Byte_Data(addr, SHT2X_WRITE_USER_REG, usr_reg);
    }
    else {
      ret = -1;
    }
    return ret;
}
5.2触发转换,读取转换值并校验计算
在线程循环中周期触发转换,实现如下:
int8_t sht2x_read_temp_humi(uint8_t addr, SHT2X_Trans_Data* trans_data)
{
    int8_t ret = -1;
    uint8_t read_buf = {0};
    uint8_t crc = 0;
    SHT2X_Orig_Data orig_data;

    I2C_DMA_Write_Byte(addr, SHT2X_TRIG_HUMID_NOHOLD);
    osDelay(30);
    I2C_DMA_Dirct_Read_Bytes(addr, read_buf, 3);
    ret = SHT2x_CheckCrc(read_buf, 3, &crc);
    if(ret != 0) {
      printf("error, humi data verification failed\n");
      orig_data.s_rh = 0xFFFF;
      ret = -1;
    }
    else {
      orig_data.s_rh = (uint16_t)((read_buf << 8) | (read_buf & 0xFC));
      ret = 0;
    }

    I2C_DMA_Write_Byte(addr, SHT2X_TRIG_TEMP_NOHOLD);
    osDelay(85);
    I2C_DMA_Dirct_Read_Bytes(addr, read_buf, 3);
    ret = SHT2x_CheckCrc(read_buf, 3, &crc);
    if(ret != 0) {
      printf("error, temp data verification failed\n");
      orig_data.s_t = 0xFFFF;
      ret = -1;
    }
    else {
      orig_data.s_t = (int16_t)((read_buf << 8) | (read_buf & 0xFC));\
      ret = 0;
    }

    osDelay(2);
    if(0 == ret) {
      trans_data->temperature = orig_data.s_t * 175.72 / 65536 - 46.85;
      trans_data->humidity    = orig_data.s_rh * 125.0 / 65536 - 6;
    }
    else {
      ;
    }
    return ret;
}
int8_t SHT2x_CheckCrc(const uint8_t data[], uint8_t nbytes, uint8_t *checksum)
{
    uint8_t crc = 0;
    uint8_t bytectr;
    for(bytectr = 0; bytectr < (nbytes - 1); ++bytectr) {
      crc ^= (data);
      for(uint8_t bit = 8; bit > 0; --bit) {
            if (crc & 0x80) {
                crc = (crc << 1) ^ POLYNOMIAL;
            }
            else {
                crc = (crc << 1);
            }
      }
    }
    *checksum = crc;
    if(crc != data) {
      return -1;
    }
    else
      return 0;
}
6.结果展示
本例程直接在BMP280例程中增加的,在串口日志中可以看到在周期性的打印温度、气压和湿度值,对着温湿度计吹气后可以看到湿度数据从低到高又降低。


页: [1]
查看完整版本: 【APM32F402R Micro-EVB开发板测评】7、FreeRTOS任务定时读取SHT20相对湿度