Skip to content

由 WangChong 贡献,Ai-Thinker 进行整理

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

外设介绍 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

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

烧录验证

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

代码解读

📜 点击展开完整代码
c
#include <stdio.h>              // 标准输入输出头文件
#include <FreeRTOS.h>           // FreeRTOS 头文件
#include <task.h>               // FreeRTOS 任务管理头文件
#include <hosal_i2c.h>          // HOSAL I2C 驱动头文件
#include <bl_gpio.h>            // GPIO 驱动头文件
#include <blog.h>               // 日志打印库
#define SHT31_DEFAULT_ADDR 0x0044        // SHT31 传感器默认的 I2C 地址
#define SHT31_MEAS_HIGHREP 0x2400        // SHT31 传感器的高精度测量命令
#pragma pack(1) // 结构体字节对齐为 1 字节
struct sht3x_data
{
uint8_t st_high;    // 温度高字节
uint8_t st_low;     // 温度低字节
uint8_t st_crc8;    // 温度 CRC 校验字节
uint8_t srh_high;   // 湿度高字节
uint8_t srh_low;    // 湿度低字节
uint8_t srh_crc8;   // 湿度 CRC 校验字节
};
#pragma pack() // 恢复默认字节对齐
// CRC8 校验函数,参数为数据指针和长度
static uint8_t crc8(uint8_t *data, int len)
{
const uint8_t POLYNOMIAL = 0x31; // 多项式
uint8_t crc = 0xFF;              // CRC 初始值
for (int j = len; j; --j)
{
crc ^= *data++;              // 异或操作
for (int i = 8; i; --i)
{
crc = (crc & 0x80)       // 判断最高位
? (crc << 1) ^ POLYNOMIAL // 如果最高位是 1,左移并异或多项式
: (crc << 1);  // 否则仅左移
}
}
return crc;
}
int main(void)
{
// 初始化 I2C 设备结构体
static hosal_i2c_dev_t i2c0 = {
.config = {
.address_width = HOSAL_I2C_ADDRESS_WIDTH_7BIT, // 7 位地址模式
.freq = 100000,                               // I2C 频率 100kHz
.mode = HOSAL_I2C_MODE_MASTER,                // 主机模式
.scl = 12,                                    // SCL 引脚为 GPIO 12
.sda = 3,                                     // SDA 引脚为 GPIO 3
},
.port = 0, // I2C 端口 0
};
hosal_i2c_init(&i2c0); // 初始化 I2C 接口
for (;;) { // 无限循环读取数据
struct sht3x_data data; // 定义用于存储 SHT31 数据的结构体
// SHT31 高精度测量命令
uint8_t command[2] = { SHT31_MEAS_HIGHREP >> 8, SHT31_MEAS_HIGHREP & 0xff };
// 发送测量命令给 SHT31 传感器
hosal_i2c_master_send(&i2c0, SHT31_DEFAULT_ADDR, command, sizeof command, 100);
// 接收测量数据
hosal_i2c_master_recv(&i2c0, SHT31_DEFAULT_ADDR, (uint8_t*)&data, sizeof data, 100);
char temperature_str[8]; // 用于存储温度字符串
char humidity_str[8];    // 用于存储湿度字符串
// 验证温度数据的 CRC 校验
if (crc8(&data.st_high, 2) == data.st_crc8) {
uint16_t st = data.st_high; // 获取温度高字节
st <<= 8;                   // 左移 8 位
st |= data.st_low;           // 合并低字节
// 转换为实际温度值
int temp = st;
temp *= 17500;
temp /= 0xffff; // 根据公式转换为温度
temp = -4500 + temp; // SHT31 的温度转换公式
int temperature_integer = temp / 100; // 取整数部分
if (temp < 0) {
temp = -temp; // 处理负数温度
}
unsigned temperature_decimal = temp % 100; // 取小数部分
// 格式化温度字符串
sprintf(temperature_str, "%d.%02u C", temperature_integer, temperature_decimal);
}
else {
sprintf(temperature_str, "%s", "N/A C"); // CRC 校验失败,返回 "N/A"
}
// 验证湿度数据的 CRC 校验
if (crc8(&data.srh_high, 2) == data.srh_crc8) {
uint16_t srh = data.srh_high; // 获取湿度高字节
srh <<= 8;                    // 左移 8 位
srh |= data.srh_low;           // 合并低字节
// 转换为实际湿度值
unsigned humidity = srh;
humidity *= 10000;
humidity /= 0xFFFF; // 根据公式转换为湿度
unsigned humidity_integer = humidity / 100; // 取整数部分
// 格式化湿度字符串
sprintf(humidity_str, "%u %%", humidity_integer);
}
else {
sprintf(humidity_str, "N/A %%"); // CRC 校验失败,返回 "N/A"
}
// 打印温度和湿度数据
blog_info("temperature: %s\thumidity: %s\r\n", temperature_str, humidity_str);
vTaskDelay(portTICK_RATE_MS * 1000); // 延时 1 秒
}
return 0;
}

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

Main.c

📜 点击展开完整代码
c
#include <stdio.h>
#include <FreeRTOS.h>
#include <task.h>
#include <hosal_i2c.h>
#include <bl_gpio.h>
#include <blog.h>
#include "bh1750.h"
int main(void)
{
bh1750_init();
xTaskCreate(read_bh1750, "BH1750 Task", 4096, NULL, 10, NULL);
return 0;
}

SHT3x.h

📜 点击展开完整代码
c
#ifndef SHT_3X_H
#define SHT_3X_H
#include <stdio.h>
#include <FreeRTOS.h>
#include <task.h>
#include <hosal_i2c.h>
#include <bl_gpio.h>
#include <blog.h>
#define SHT31_DEFAULT_ADDR 0x0044 // SHT31 传感器默认的 I2C 地址
#define SHT31_MEAS_HIGHREP 0x2400 // SHT31 传感器的高精度测量命令
#pragma pack(1) // 结构体字节对齐为 1 字节
struct sht3x_data
{
uint8_t st_high;  // 温度高字节
uint8_t st_low;   // 温度低字节
uint8_t st_crc8;  // 温度 CRC 校验字节
uint8_t srh_high; // 湿度高字节
uint8_t srh_low;  // 湿度低字节
uint8_t srh_crc8; // 湿度 CRC 校验字节
};
#pragma pack() // 恢复默认字节对齐
//初始化
void sht3x_init();
//读取任务
void read_sht3x(void *args);
#endif

SHT3x.c

📜 点击展开完整代码
c
#include "sht3x.h"
static hosal_i2c_dev_t i2c0 = {
.config = {
.address_width = HOSAL_I2C_ADDRESS_WIDTH_7BIT,
.freq = 100000,
.mode = HOSAL_I2C_MODE_MASTER,
.scl = 12,
.sda = 17,
},
.port = 0,
};
void sht3x_init()
{
blog_info("Initializing I2C...\n");
hosal_i2c_init(&i2c0);
}
// CRC8 校验函数,参数为数据指针和长度
static uint8_t crc8(uint8_t *data, int len)
{
const uint8_t POLYNOMIAL = 0x31; // 多项式
uint8_t crc = 0xFF;              // CRC 初始值
for (int j = len; j; --j)
{
crc ^= *data++; // 异或操作
for (int i = 8; i; --i)
{
crc = (crc & 0x80)                  // 判断最高位
? (crc << 1) ^ POLYNOMIAL // 如果最高位是 1,左移并异或多项式
: (crc << 1);             // 否则仅左移
}
}
return crc;
}
void read_sht3x(void *args)
{
for (;;)
{ // 无限循环读取数据
struct sht3x_data data; // 定义用于存储 SHT31 数据的结构体
// SHT31 高精度测量命令
uint8_t command[2] = {SHT31_MEAS_HIGHREP >> 8, SHT31_MEAS_HIGHREP & 0xff};
// 发送测量命令给 SHT31 传感器
hosal_i2c_master_send(&i2c0, SHT31_DEFAULT_ADDR, command, sizeof command, 100);
// 接收测量数据
hosal_i2c_master_recv(&i2c0, SHT31_DEFAULT_ADDR, (uint8_t *)&data, sizeof data, 100);
char temperature_str[8]; // 用于存储温度字符串
char humidity_str[8];    // 用于存储湿度字符串
// 验证温度数据的 CRC 校验
if (crc8(&data.st_high, 2) == data.st_crc8)
{
uint16_t st = data.st_high; // 获取温度高字节
st <<= 8;                   // 左移 8 位
st |= data.st_low;          // 合并低字节
// 转换为实际温度值
int temp = st;
temp *= 17500;
temp /= 0xffff;      // 根据公式转换为温度
temp = -4500 + temp; // SHT31 的温度转换公式
int temperature_integer = temp / 100; // 取整数部分
if (temp < 0)
{
temp = -temp; // 处理负数温度
}
unsigned temperature_decimal = temp % 100; // 取小数部分
// 格式化温度字符串
sprintf(temperature_str, "%d.%02u C", temperature_integer, temperature_decimal);
}
else
{
sprintf(temperature_str, "%s", "N/A C"); // CRC 校验失败,返回 "N/A"
}
// 验证湿度数据的 CRC 校验
if (crc8(&data.srh_high, 2) == data.srh_crc8)
{
uint16_t srh = data.srh_high; // 获取湿度高字节
srh <<= 8;                    // 左移 8 位
srh |= data.srh_low;          // 合并低字节
// 转换为实际湿度值
unsigned humidity = srh;
humidity *= 10000;
humidity /= 0xFFFF; // 根据公式转换为湿度
unsigned humidity_integer = humidity / 100; // 取整数部分
// 格式化湿度字符串
sprintf(humidity_str, "%u %%", humidity_integer);
}
else
{
sprintf(humidity_str, "N/A %%"); // CRC 校验失败,返回 "N/A"
}
// 打印温度和湿度数据
blog_info("temperature: %s\thumidity: %s\r\n", temperature_str, humidity_str);
vTaskDelay(portTICK_RATE_MS * 1000); // 延时 1 秒
}
}

工程

上传的附件:

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

Released under the MIT License. Build Time 2026-09-11 14:52:23