【外设移植】Ai-WB2+SHT30 湿温度传感器

[复制链接]
查看345 | 回复9 | 2024-9-9 16:00:19 | 显示全部楼层 |阅读模式
本帖最后由 WangChong 于 2024-9-9 16:02 编辑

外设介绍
SHT30 是一种高精度、低功耗的温湿度传感器,由 Sensirion 公司生产。该传感器使用 I2C 通信接口,适用于广泛的温湿度监测应用,如环境监测、智能家居、工业控制等。

外设规格参数
  • 高精度
    • 温度精度:±0.3°C(在 0°C 至 60°C 范围内)
    • 湿度精度:±3% RH(在 20% 至 80% RH 范围内)
  • 宽测量范围
    • 温度范围:-40°C 到 +125°C
    • 湿度范围:0% RH 到 100% RH
  • 低功耗
    • 低功耗模式下的电流消耗可以降到 2 µA,非常适合电池供电设备。
  • 快速响应时间
    • 温湿度响应时间快,能迅速反映环境变化,适用于需要实时监测的场景。
  • 数字输出
    • SHT30 通过标准 I2C 接口进行通信,使用简单方便,并且还支持多种 I2C 地址选择,适合多个传感器的应用。
  • 防护性设计
    • 传感器具有高稳定性和抗干扰能力,适合在恶劣环境下使用。
            SHT30 传感器的典型应用包括环境监测、空调控制、空气质量检测、汽车气候控制等。

移植过程


根据数据手册得知, SHT30 是使用的 I2C 通信,同时我们使用的是 WB2,因此我们需要根据原理图确认 WB2 的 IIC 接口 PIN,由于我使用的是 WB2-12f 的开发板,所以可以在安信可社区 Wb2 专题下找到对应的原理图,同时根据博流官方 GPIO 的功能复用可以找到哪些 PIN 支持 IIC


015801v1pjjz3zt44sv5oz.png


在 SDK 下的 application/iot-solution/demo_sht3x下 已经提供了一个完整的实现。通过阅读代码我们发现, demo中使用的IIC的接口为SCL 12 和 SDA3. 我们将湿温度传感器连接到Wb2上,如下图所示, 彩色线分别为SDA和SCL


微信图片_20240909154335.jpg


烧录验证

360截图20240909154433.jpg


此时发现串口每隔一秒输出环境的湿温度信息, 由此代码验证通过

代码解读
  1. #include <stdio.h>              // 标准输入输出头文件
  2. #include <FreeRTOS.h>           // FreeRTOS 头文件
  3. #include <task.h>               // FreeRTOS 任务管理头文件
  4. #include <hosal_i2c.h>          // HOSAL I2C 驱动头文件
  5. #include <bl_gpio.h>            // GPIO 驱动头文件
  6. #include <blog.h>               // 日志打印库

  7. #define SHT31_DEFAULT_ADDR 0x0044        // SHT31 传感器默认的 I2C 地址
  8. #define SHT31_MEAS_HIGHREP 0x2400        // SHT31 传感器的高精度测量命令

  9. #pragma pack(1) // 结构体字节对齐为 1 字节
  10. struct sht3x_data
  11. {
  12.     uint8_t st_high;    // 温度高字节
  13.     uint8_t st_low;     // 温度低字节
  14.     uint8_t st_crc8;    // 温度 CRC 校验字节
  15.     uint8_t srh_high;   // 湿度高字节
  16.     uint8_t srh_low;    // 湿度低字节
  17.     uint8_t srh_crc8;   // 湿度 CRC 校验字节
  18. };
  19. #pragma pack() // 恢复默认字节对齐

  20. // CRC8 校验函数,参数为数据指针和长度
  21. static uint8_t crc8(uint8_t *data, int len)
  22. {
  23.     const uint8_t POLYNOMIAL = 0x31; // 多项式
  24.     uint8_t crc = 0xFF;              // CRC 初始值
  25.     for (int j = len; j; --j)
  26.     {
  27.         crc ^= *data++;              // 异或操作
  28.         for (int i = 8; i; --i)
  29.         {
  30.             crc = (crc & 0x80)       // 判断最高位
  31.                       ? (crc << 1) ^ POLYNOMIAL // 如果最高位是 1,左移并异或多项式
  32.                       : (crc << 1);  // 否则仅左移
  33.         }
  34.     }
  35.     return crc;
  36. }

  37. int main(void)
  38. {
  39.     // 初始化 I2C 设备结构体
  40.     static hosal_i2c_dev_t i2c0 = {
  41.         .config = {
  42.             .address_width = HOSAL_I2C_ADDRESS_WIDTH_7BIT, // 7 位地址模式
  43.             .freq = 100000,                               // I2C 频率 100kHz
  44.             .mode = HOSAL_I2C_MODE_MASTER,                // 主机模式
  45.             .scl = 12,                                    // SCL 引脚为 GPIO 12
  46.             .sda = 3,                                     // SDA 引脚为 GPIO 3
  47.         },
  48.         .port = 0, // I2C 端口 0
  49.     };

  50.     hosal_i2c_init(&i2c0); // 初始化 I2C 接口

  51.     for (;;) { // 无限循环读取数据
  52.         
  53.         struct sht3x_data data; // 定义用于存储 SHT31 数据的结构体

  54.         // SHT31 高精度测量命令
  55.         uint8_t command[2] = { SHT31_MEAS_HIGHREP >> 8, SHT31_MEAS_HIGHREP & 0xff };
  56.         // 发送测量命令给 SHT31 传感器
  57.         hosal_i2c_master_send(&i2c0, SHT31_DEFAULT_ADDR, command, sizeof command, 100);
  58.         // 接收测量数据
  59.         hosal_i2c_master_recv(&i2c0, SHT31_DEFAULT_ADDR, (uint8_t*)&data, sizeof data, 100);

  60.         char temperature_str[8]; // 用于存储温度字符串
  61.         char humidity_str[8];    // 用于存储湿度字符串

  62.         // 验证温度数据的 CRC 校验
  63.         if (crc8(&data.st_high, 2) == data.st_crc8) {
  64.             uint16_t st = data.st_high; // 获取温度高字节
  65.             st <<= 8;                   // 左移 8 位
  66.             st |= data.st_low;           // 合并低字节

  67.             // 转换为实际温度值
  68.             int temp = st;
  69.             temp *= 17500;
  70.             temp /= 0xffff; // 根据公式转换为温度
  71.             temp = -4500 + temp; // SHT31 的温度转换公式

  72.             int temperature_integer = temp / 100; // 取整数部分
  73.             
  74.             if (temp < 0) {
  75.                 temp = -temp; // 处理负数温度
  76.             }

  77.             unsigned temperature_decimal = temp % 100; // 取小数部分

  78.             // 格式化温度字符串
  79.             sprintf(temperature_str, "%d.%02u C", temperature_integer, temperature_decimal);
  80.         }
  81.         else {
  82.             sprintf(temperature_str, "%s", "N/A C"); // CRC 校验失败,返回 "N/A"
  83.         }
  84.         
  85.         // 验证湿度数据的 CRC 校验
  86.         if (crc8(&data.srh_high, 2) == data.srh_crc8) {
  87.             uint16_t srh = data.srh_high; // 获取湿度高字节
  88.             srh <<= 8;                    // 左移 8 位
  89.             srh |= data.srh_low;           // 合并低字节

  90.             // 转换为实际湿度值
  91.             unsigned humidity = srh;
  92.             humidity *= 10000;
  93.             humidity /= 0xFFFF; // 根据公式转换为湿度

  94.             unsigned humidity_integer = humidity / 100; // 取整数部分

  95.             // 格式化湿度字符串
  96.             sprintf(humidity_str, "%u %%", humidity_integer);
  97.         }
  98.         else {
  99.             sprintf(humidity_str, "N/A %%"); // CRC 校验失败,返回 "N/A"
  100.         }

  101.         // 打印温度和湿度数据
  102.         blog_info("temperature: %s\thumidity: %s\r\n", temperature_str, humidity_str);

  103.         vTaskDelay(portTICK_RATE_MS * 1000); // 延时 1 秒
  104.     }

  105.     return 0;
  106. }
复制代码

库函数

我们将上面的代码整理成库函数的方式方便调用

Main.c

  1. #include <stdio.h>
  2. #include <FreeRTOS.h>
  3. #include <task.h>
  4. #include <hosal_i2c.h>
  5. #include <bl_gpio.h>
  6. #include <blog.h>
  7. #include "bh1750.h"

  8. int main(void)
  9. {

  10.     bh1750_init();
  11.     xTaskCreate(read_bh1750, "BH1750 Task", 4096, NULL, 10, NULL);
  12.     return 0;
  13. }
复制代码


SHT3x.h

  1. #ifndef SHT_3X_H
  2. #define SHT_3X_H
  3. #include <stdio.h>
  4. #include <FreeRTOS.h>
  5. #include <task.h>
  6. #include <hosal_i2c.h>
  7. #include <bl_gpio.h>
  8. #include <blog.h>

  9. #define SHT31_DEFAULT_ADDR 0x0044 // SHT31 传感器默认的 I2C 地址
  10. #define SHT31_MEAS_HIGHREP 0x2400 // SHT31 传感器的高精度测量命令

  11. #pragma pack(1) // 结构体字节对齐为 1 字节
  12. struct sht3x_data
  13. {
  14.     uint8_t st_high;  // 温度高字节
  15.     uint8_t st_low;   // 温度低字节
  16.     uint8_t st_crc8;  // 温度 CRC 校验字节
  17.     uint8_t srh_high; // 湿度高字节
  18.     uint8_t srh_low;  // 湿度低字节
  19.     uint8_t srh_crc8; // 湿度 CRC 校验字节
  20. };
  21. #pragma pack() // 恢复默认字节对齐

  22. //初始化
  23. void sht3x_init();
  24. //读取任务
  25. void read_sht3x(void *args);
  26. #endif
复制代码


SHT3x.c


  1. #include "sht3x.h"

  2. static hosal_i2c_dev_t i2c0 = {
  3.     .config = {
  4.         .address_width = HOSAL_I2C_ADDRESS_WIDTH_7BIT,
  5.         .freq = 100000,
  6.         .mode = HOSAL_I2C_MODE_MASTER,
  7.         .scl = 12,
  8.         .sda = 17,
  9.     },
  10.     .port = 0,
  11. };

  12. void sht3x_init()
  13. {
  14.     blog_info("Initializing I2C...\n");
  15.     hosal_i2c_init(&i2c0);
  16. }

  17. // CRC8 校验函数,参数为数据指针和长度
  18. static uint8_t crc8(uint8_t *data, int len)
  19. {
  20.     const uint8_t POLYNOMIAL = 0x31; // 多项式
  21.     uint8_t crc = 0xFF;              // CRC 初始值
  22.     for (int j = len; j; --j)
  23.     {
  24.         crc ^= *data++; // 异或操作
  25.         for (int i = 8; i; --i)
  26.         {
  27.             crc = (crc & 0x80)                  // 判断最高位
  28.                       ? (crc << 1) ^ POLYNOMIAL // 如果最高位是 1,左移并异或多项式
  29.                       : (crc << 1);             // 否则仅左移
  30.         }
  31.     }
  32.     return crc;
  33. }

  34. void read_sht3x(void *args)
  35. {
  36.     for (;;)
  37.     { // 无限循环读取数据

  38.         struct sht3x_data data; // 定义用于存储 SHT31 数据的结构体

  39.         // SHT31 高精度测量命令
  40.         uint8_t command[2] = {SHT31_MEAS_HIGHREP >> 8, SHT31_MEAS_HIGHREP & 0xff};
  41.         // 发送测量命令给 SHT31 传感器
  42.         hosal_i2c_master_send(&i2c0, SHT31_DEFAULT_ADDR, command, sizeof command, 100);
  43.         // 接收测量数据
  44.         hosal_i2c_master_recv(&i2c0, SHT31_DEFAULT_ADDR, (uint8_t *)&data, sizeof data, 100);

  45.         char temperature_str[8]; // 用于存储温度字符串
  46.         char humidity_str[8];    // 用于存储湿度字符串

  47.         // 验证温度数据的 CRC 校验
  48.         if (crc8(&data.st_high, 2) == data.st_crc8)
  49.         {
  50.             uint16_t st = data.st_high; // 获取温度高字节
  51.             st <<= 8;                   // 左移 8 位
  52.             st |= data.st_low;          // 合并低字节

  53.             // 转换为实际温度值
  54.             int temp = st;
  55.             temp *= 17500;
  56.             temp /= 0xffff;      // 根据公式转换为温度
  57.             temp = -4500 + temp; // SHT31 的温度转换公式

  58.             int temperature_integer = temp / 100; // 取整数部分

  59.             if (temp < 0)
  60.             {
  61.                 temp = -temp; // 处理负数温度
  62.             }

  63.             unsigned temperature_decimal = temp % 100; // 取小数部分

  64.             // 格式化温度字符串
  65.             sprintf(temperature_str, "%d.%02u C", temperature_integer, temperature_decimal);
  66.         }
  67.         else
  68.         {
  69.             sprintf(temperature_str, "%s", "N/A C"); // CRC 校验失败,返回 "N/A"
  70.         }

  71.         // 验证湿度数据的 CRC 校验
  72.         if (crc8(&data.srh_high, 2) == data.srh_crc8)
  73.         {
  74.             uint16_t srh = data.srh_high; // 获取湿度高字节
  75.             srh <<= 8;                    // 左移 8 位
  76.             srh |= data.srh_low;          // 合并低字节

  77.             // 转换为实际湿度值
  78.             unsigned humidity = srh;
  79.             humidity *= 10000;
  80.             humidity /= 0xFFFF; // 根据公式转换为湿度

  81.             unsigned humidity_integer = humidity / 100; // 取整数部分

  82.             // 格式化湿度字符串
  83.             sprintf(humidity_str, "%u %%", humidity_integer);
  84.         }
  85.         else
  86.         {
  87.             sprintf(humidity_str, "N/A %%"); // CRC 校验失败,返回 "N/A"
  88.         }

  89.         // 打印温度和湿度数据
  90.         blog_info("temperature: %s\thumidity: %s\r\n", temperature_str, humidity_str);

  91.         vTaskDelay(portTICK_RATE_MS * 1000); // 延时 1 秒
  92.     }
  93. }
复制代码


工程

demo_sht3x.zip (306.67 KB, 下载次数: 0)

本帖被以下淘专辑推荐:

回复

使用道具 举报

djy876 | 2024-9-9 16:05:51 | 显示全部楼层
如何控制WB2发热影响SHT30的探测问题?
回复 支持 反对

使用道具 举报

bzhou830 | 2024-9-9 16:07:14 | 显示全部楼层
这么快
选择去发光,而不是被照亮
回复

使用道具 举报

wxlinus | 2024-9-9 16:25:38 | 显示全部楼层
djy876 发表于 2024-9-9 16:05
如何控制WB2发热影响SHT30的探测问题?

我也遇到过这个问题,上次画的esp32s3的板子,ldo和wifi发热都烫手,感觉还是要搞低功耗,然后把传感器隔离开
回复 支持 反对

使用道具 举报

爱笑 | 2024-9-9 17:18:55 | 显示全部楼层
王哥的暑假还没结束吗
用心做好保姆工作
回复 支持 反对

使用道具 举报

bzhou830 | 2024-9-9 17:38:29 | 显示全部楼层
wxlinus 发表于 2024-9-9 16:25
我也遇到过这个问题,上次画的esp32s3的板子,ldo和wifi发热都烫手,感觉还是要搞低功耗,然后把传感器隔 ...

很好奇,为什么stm32不发热,模组这类的发热量这么大?
选择去发光,而不是被照亮
回复 支持 反对

使用道具 举报

iiv | 2024-9-9 20:18:19 | 显示全部楼层
外国也这么热吗
回复 支持 反对

使用道具 举报

WangChong | 2024-9-9 20:35:58 | 显示全部楼层
爱笑 发表于 2024-9-9 17:18
王哥的暑假还没结束吗

早都结束了,答应园长的要赶紧搞完
回复 支持 反对

使用道具 举报

WangChong | 2024-9-9 20:39:21 | 显示全部楼层
djy876 发表于 2024-9-9 16:05
如何控制WB2发热影响SHT30的探测问题?

离远一点, 实际上这个发热量影响不到这个传感器, 但是如果在小型密闭空间就不行了
回复 支持 反对

使用道具 举报

wxlinus | 2024-9-10 08:36:13 | 显示全部楼层
bzhou830 发表于 2024-9-9 17:38
很好奇,为什么stm32不发热,模组这类的发热量这么大?

射频之类的发热很正常
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则