SHT30温湿度传感器全解析——概况,性能,MCU连接,样例代码

常见温湿度传感器测量范围:(价格仅供参考,具体性能要看折线图)

型号DHT11DHT20AHT10AHT20AHT30SHT20
价格¥ 2.49¥3.04¥ 1.9¥1.4¥ 1.3¥5.5
温度测量范围20—90%RH0—100%RH0—100%RH0—100%RH0—100%RH0—100%RH
湿度测量精度±5%RH±3%RH±2%RH±2%RH±2%RH±2%RH
温度测量范围0—50℃-40—80℃-40—85℃-40—85℃-40—85℃-40—125℃
温度测量精度±2℃±0.5℃±0.3℃±0.3℃±0.3℃±0.3℃
工作电压DC5V/3.3VDC5V/3.3VDC3.3VDC5V/3.3VDC5V/3.3VDC5V/3.3V
型号SHT30SHT31SHT35SHT40SHT41SHT45
价格¥ 3.9¥ 6.9¥ 18.5¥ 3.25¥6.6¥ 19
温度测量范围0—100%RH0—100%RH0—100%RH0—100%RH0—100%RH0—100%RH
湿度测量精度±2%RH±2%RH±1.5%RH±1.8%RH±1.8%RH±1.5%RH
温度测量范围-40—125℃-40—125℃-40—125℃-40—125℃-40—125℃-40—125℃
温度测量精度±0.2℃±0.2℃±0.1℃±0.2℃±0.2℃±0.1℃
工作电压DC5V/3.3VDC5V/3.3VDC5V/3.3VDC3.3VDC3.3VDC3.3V

SHT30概述

在这里插入图片描述
数据:
SHT30是一款完全校准的线性化的温湿度数字传感器,增强了数字信号。I2C通讯频率达1MHz。具有高可靠性及高稳定性。

供电:
供电电压2.15V-5.5V;数据接口电压可5V或3.3V(不同供电系统可对接,但尽量避免)。

功能:
单次读取或周期性读取温度和湿度传感器数值,通过公式转换为摄氏度和相对湿度值。

连接:
使用I2C进行连接,也可使用普通GPIO引脚模拟I2C进行通讯,但通讯速率较低。

SHT30引脚说明

在这里插入图片描述

引脚名称功能
1SDA数据信号
2ADDR连接到高电平或低电平,不可以悬空
3ALERT报警引脚,不用必须悬空
4SCL时钟信号
5VDD电源正极
6nRESET低电平复位;如果不使用,建议悬空;也可以使用大于2Ω电阻连接到VDD
7R无功能,请连到电源地
8VSS电源地
9pad中间引脚与电源地相通,散热引脚

SHT30电气特性

在这里插入图片描述
温度误差:
在这里插入图片描述
湿度误差:
在这里插入图片描述

SHT30 MCU连接

在这里插入图片描述
Rp是I2C上拉电阻,I2C总线上只连接一个设备时,推荐阻值为10K,其他视情况而定,该数值不能太小也不能太大。

SDA,SCL接上拉电阻后再接MCU,用于数据读取。

nRESET不用时悬空。
ALERT不用时悬空。

SHT30通信协议

SHT30的通讯协议是基于I2C的,这里主要理解I2C通讯协议。

SHT30写命令顺序:

1、设置I2C Start。此时I2C状态为0x08:已发送起始条件
2、发送SHT30的I2C写地址。(注意,上图中ADDR接地,则设备地址为0x44;若ADDR接高电平,则设备地址为0x45。同时该地址为7位,再加读写标志位1位才凑够8byte,写时读写标志位为零,则该byte为0x44<<1。)
3、等待收到SHT30的确认ACK。 此时I2C状态为0x18:已发送设备地址和写命令,已接收到ACK
4、发送控制命令高字节。
5、等待收到SHT30的确认ACK。 此时I2C状态为0x28:已发送1byte数据,已接收到ACK
6、发送控制命令低字节。
7、等待收到SHT30的确认ACK。 此时I2C状态为0x28:已发送1byte数据,已接收到ACK
8、如无后续数据,设置I2C Stop。(SHT30写命令只有这一种)

如周期性数据测量模式命令(Command 0x2220)

在这里插入图片描述

SHT30读命令顺序:(先和写命令一样,写入一个命令数据,再读取温湿度数据)

1、设置I2C Start。此时I2C状态为0x08:已发送起始条件
2、发送SHT30的I2C写地址。(注意,上图中ADDR接地,则设备地址为0x44;若ADDR接高电平,则设备地址为0x45。同时该地址为7位,再加读写标志位1位才凑够8byte,写时读写标志位为零,则该byte为0x44<<1。)
3、等待收到SHT30的确认ACK。 此时I2C状态为0x18:已发送设备地址和写命令,已接收到ACK
4、发送控制命令高字节。
5、等待收到SHT30的确认ACK。 此时I2C状态为0x28:已发送1byte数据,已接收到ACK
6、发送控制命令低字节。
7、等待收到SHT30的确认ACK。 此时I2C状态为0x28:已发送1byte数据,已接收到ACK

8、如使用周期性测量命令设定过SHT30,则无需等待,重新设置I2C Start。 此时I2C状态为0x08:已发送起始条件
9、发送SHT30的I2C读地址。(注意,上图中ADDR接地,则设备地址为0x44;若ADDR接高电平,则设备地址为0x45。同时该地址为7位,再加读写标志位1位才凑够8byte,读时读写标志位为1,则该byte为0x44<<1|0x01。)
10、等待收到SHT30的确认ACK。 此时I2C状态为0x40:已发送设备地址和读命令,已接收到ACK
11、设置使能主机应答功能,即主机进行接收数据后发送ACK确认。
12、接收温度高字节。 此时I2C状态为0x50:已接收数据字节,并已返回ACK信号
13、接收温度低字节。 此时I2C状态为0x50:已接收数据字节,并已返回ACK信号
14、接收温度CRC字节。 此时I2C状态为0x50:已接收数据字节,并已返回ACK信号
15、接收湿度高字节。 此时I2C状态为0x50:已接收数据字节,并已返回ACK信号
16、接收湿度低字节。 此时I2C状态为0x50:已接收数据字节,并已返回ACK信号
17、设置关闭主机应答功能,最后一个数据不需要ACK。
17、接收湿度CRC字节。 此时I2C状态为0x58:已接收到最后一个数据,NACK已返回
18、设置I2C Stop.

如读取周期性数据测量数据命令(Command 0xE000)

在这里插入图片描述

SHT30命令说明

单次测量命令:(单次读取使用,通讯中需要等待测量结果)
在这里插入图片描述
在这里插入图片描述

周期测量命令:(mps,每秒多少次。设置使用,用于设置周期测量模式)
在这里插入图片描述
在这里插入图片描述

读取周期测量结果命令:(读取使用,读取上一次周期测量的测量结果。)
在这里插入图片描述
在这里插入图片描述
加速相应时间命令:(周期测量命令一样的功能,不过更快,周期更短)
在这里插入图片描述
停止周期测量命令:(设置使用,用于停止周期测量))
在这里插入图片描述
软件重置命令:(使用命令进行重置,此外使用I2C协议的一般重置功能也能进行重置。经实验下图最后一个ACK收不到,可能是因为收到重置命令后立即重置所以就没有发送ACK。
在这里插入图片描述
加热器控制命令:(可加热几摄氏度,用于检测设备好坏。)
在这里插入图片描述
读取状态寄存器:(可获取各种设备状态)
在这里插入图片描述
设备状态说明:
在这里插入图片描述
清除状态寄存器:
在这里插入图片描述

SHT30校验和计算

校验使用CRC-8:
在这里插入图片描述
代码说明:

uint8_t CheckCrc8(uint8_t* const message, uint8_t initial_value)
{
    uint8_t  remainder;        //余数
    uint8_t  i = 0, j = 0;  //循环变量
 
    /* 初始化 */
    remainder = initial_value;
 
    for(j = 0; j < 2;j++)
    {
        remainder ^= message[j];
 
        /* 从最高位开始依次计算  */
        for (i = 0; i < 8; i++)
        {
            if (remainder & 0x80)
            {
                remainder = (remainder << 1)^CRC8_POLYNOMIAL;
            }
            else
            {
                remainder = (remainder << 1);
            }
        }
    }

    /* 返回计算的CRC码 */
    return remainder;
}

计算温度:(ST 传感器温度返回值)
在这里插入图片描述
计算湿度:(SRH 传感器湿度返回值)
在这里插入图片描述
代码说明:

    /* 转换温度数据 */
    recv_temperature = ((uint16_t)dat[0]<<8)|dat[1];
    *temperature = -45 + 175*((float)recv_temperature / 65535);
 
    /* 转换湿度数据 */
    recv_humidity = ((uint16_t)dat[3]<<8)|dat[4];
    *humidity = 100 * ((float)recv_humidity / 65535);

SHT30 样例代码(基于HC32L130)

(样例代码使用周期性测量模式。先设置周期性测量模式,再通过读取周期性测量结果获得温湿度数据。)
sht3x.h文件

#ifndef __SHT3X_H__
#define __SHT3X_H__

#include "gpio.h"

/*
//ADDR Pin Conect to VSS 
#define    SHT30_ADDR_WRITE    0x44<<1         //10001000
#define    SHT30_ADDR_READ     (0x44<<1)+1     //10001011
*/

//SHT30 I2C地址
#define I2C_DEVADDR 0x44
//CRC-8多项式
#define CRC8_POLYNOMIAL 0x31

typedef enum
{
    /* 软件复位命令 */
    SOFT_RESET_CMD = 0x30A2,  
  
    /*
    单次测量模式
    命名格式:Repeatability_CS_CMD
    CS:Clock stretching
    */
    HIGH_ENABLED_CMD    = 0x2C06,
    MEDIUM_ENABLED_CMD  = 0x2C0D,
    LOW_ENABLED_CMD     = 0x2C10,

    HIGH_DISABLED_CMD   = 0x2400,
    MEDIUM_DISABLED_CMD = 0x240B,
    LOW_DISABLED_CMD    = 0x2416,
 
    /*
    周期测量模式
    命名格式:Repeatability_MPS_CMD
    MPS:measurement per second
    */
    HIGH_0_5_CMD   = 0x2032,
    MEDIUM_0_5_CMD = 0x2024,
    LOW_0_5_CMD    = 0x202F,
    HIGH_1_CMD     = 0x2130,
    MEDIUM_1_CMD   = 0x2126,
    LOW_1_CMD      = 0x212D,
    HIGH_2_CMD     = 0x2236,
    MEDIUM_2_CMD   = 0x2220,
    LOW_2_CMD      = 0x222B,
    HIGH_4_CMD     = 0x2334,
    MEDIUM_4_CMD   = 0x2322,
    LOW_4_CMD      = 0x2329,
    HIGH_10_CMD    = 0x2737,
    MEDIUM_10_CMD  = 0x2721,
    LOW_10_CMD     = 0x272A,

    /* 周期测量模式读取数据命令 */
    READOUT_FOR_PERIODIC_MODE = 0xE000,
} SHT30_CMD;

void I2C_Port_Init(void);
void I2C_Cfg_Init(void);
void SHT30_reset(void);
uint8_t SHT30_Init(void);
uint8_t SHT30_Send_Cmd(SHT30_CMD cmd);
uint8_t SHT30_Read_Dat(uint8_t* dat);
uint8_t SHT30_Dat_To_Float(uint8_t* const dat, float* temperature, float* humidity);

#endif /* __SHT3X_H__ */

sht3x.c文件

#include "sht3x.h"
#include "gpio.h"
#include "i2c.h"

// I2C端口配置
void I2C_Port_Init(void)
{
    stc_gpio_cfg_t stcGpioCfg;
    
    DDL_ZERO_STRUCT(stcGpioCfg);
    
    Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio,TRUE);   //开启GPIO时钟门控 
    
    stcGpioCfg.enDir = GpioDirOut;                           ///< 端口方向配置->输出    
    stcGpioCfg.enOD = GpioOdEnable;                          ///< 开漏输出
    stcGpioCfg.enPu = GpioPuEnable;                          ///< 端口上拉配置->使能
    stcGpioCfg.enPd = GpioPdDisable;                         ///< 端口下拉配置->禁止
    stcGpioCfg.bOutputVal = TRUE;
    
    Gpio_Init(GpioPortB,GpioPin13,&stcGpioCfg);               ///< 端口初始化
    Gpio_Init(GpioPortB,GpioPin14,&stcGpioCfg);
    
    Gpio_SetAfMode(GpioPortB,GpioPin13,GpioAf2);              ///< 配置PB13为SCL
    Gpio_SetAfMode(GpioPortB,GpioPin14,GpioAf2);              ///< 配置PB14为SDA
}

// I2C 模块配置
void I2C_Cfg_Init(void)
{
    stc_i2c_cfg_t stcI2cCfg;
    
    DDL_ZERO_STRUCT(stcI2cCfg);                            ///< 初始化结构体变量的值为0
    
    Sysctrl_SetPeripheralGate(SysctrlPeripheralI2c1,TRUE); ///< 开启I2C0时钟门控
    
    stcI2cCfg.u32Pclk = Sysctrl_GetPClkFreq();             ///< 获取PCLK时钟
    stcI2cCfg.u32Baud = 100000;                            ///< 100kHz,SHT30最大支持1MHz,但要I2C fast mode,且硬件设计过关
    stcI2cCfg.enMode = I2cMasterMode;                      ///< 主机模式
    stcI2cCfg.u8SlaveAddr = 0x55;                          ///< 从地址,主模式无效
    stcI2cCfg.bGc = FALSE;                                 ///< 广播地址应答使能关闭
    I2C_Init(M0P_I2C1,&stcI2cCfg);                         ///< 模块初始化
}

// CRC-8校验
uint8_t CheckCrc8(uint8_t* const message, uint8_t initial_value)
{
    uint8_t  remainder;        //余数
    uint8_t  i = 0, j = 0;  //循环变量
 
    /* 初始化 */
    remainder = initial_value;
 
    for(j = 0; j < 2;j++)
    {
        remainder ^= message[j];
 
        /* 从最高位开始依次计算  */
        for (i = 0; i < 8; i++)
        {
            if (remainder & 0x80)
            {
                remainder = (remainder << 1)^CRC8_POLYNOMIAL;
            }
            else
            {
                remainder = (remainder << 1);
            }
        }
    }

    /* 返回计算的CRC码 */
    return remainder;
}

// SHT30写命令函数,只进行写命令操作
en_result_t I2C_SHT30WriteCmd(M0P_I2C_TypeDef* I2CX,uint8_t *pu8Data)
{
    en_result_t enRet = Error;
    uint8_t sendCount=0,u8State;
    I2C_SetFunc(I2CX,I2cStart_En);										 ///发送起始条件
    while(1)
    {
        while(0 == I2C_GetIrq(I2CX))
        {;}
        u8State = I2C_GetState(I2CX);
        switch(u8State)
        {
            case 0x08:                                 ///已发送起始条件
								I2C_ClearFunc(I2CX, I2cStart_En);
                I2C_WriteByte(I2CX,(I2C_DEVADDR<<1));  ///发送设备地址+W写标志0
                break;
            case 0x18:                                 ///已发送SLW+W,已接收ACK
            case 0x28:                                 ///已发送I2Cx_DATA中的数据,已接收ACK
								if(sendCount<2)
								{
										I2C_WriteByte(I2CX,pu8Data[sendCount++]);		 ///发送数据
								}else
								{
										//发送两字节命令后收到ACK,退出
										sendCount++;
								}
                break;
            case 0x20:                                 ///已发送SLW+W,已接收非ACK
            case 0x38:                                 ///上一次在SLA+写数据时丢失仲裁
                I2C_SetFunc(I2CX,I2cStart_En);         ///当I2C总线空闲时发送起始条件
                break;
            case 0x30:                                 ///已发送I2Cx_DATA中的数据,已接收非ACK,将传输一个STOP条件
                I2C_SetFunc(I2CX,I2cStop_En);          ///发送停止条件
                break;
            default:
                break;
        }            
        if(sendCount>2)
        {
						I2C_SetFunc(I2CX,I2cStop_En);              ///此顺序不能调换,出停止条件
            I2C_ClearIrq(I2CX);
            break;
        }
        I2C_ClearIrq(I2CX);                            ///清除中断状态标志位
    }
    enRet = Ok;
    return enRet;
}

// 主机读取数据函数,只进行读数据操作
en_result_t I2C_MasterReadData(M0P_I2C_TypeDef* I2CX,uint16_t u8Cmd,uint8_t *pu8Data,uint32_t u32Len)
{
    en_result_t enRet = Error;
    uint8_t u8State=0;
    uint8_t receiveCount=0;
    uint8_t sendAddrCount=0;
	  uint8_t sendCmdCount=0;
    I2C_SetFunc(I2CX,I2cStart_En);
    
    while(1)
    {
        while(0 == I2C_GetIrq(I2CX))
        {;}
        u8State = I2C_GetState(I2CX);
        switch(u8State)
        {
            case 0x08:                                 ///< 已发送起始条件,将发送SLA+W
								sendAddrCount++;
								if(sendAddrCount<=1)
								{
										I2C_ClearFunc(I2CX,I2cStart_En);
										I2C_WriteByte(I2CX,I2C_DEVADDR<<1); 
								}
								if(sendAddrCount>1)
								{
										I2C_ClearFunc(I2CX,I2cStart_En);
										I2C_WriteByte(I2CX,I2C_DEVADDR<<1|0x01);///< 发送SLA+R,开始从从机读取数据						
								}
                break;
            case 0x18:                                 ///< 已发送SLA+W,并接收到ACK
                I2C_WriteByte(I2CX,(uint8_t)(u8Cmd>>8)); ///<命令高8位
                            
                break;
            case 0x28:                                 ///< 已发送数据,接收到ACK, 此处是已发送从机内存地址u8Addr并接收到ACK
                sendCmdCount++;
                I2C_WriteByte(I2CX,(uint8_t)u8Cmd);    ///<命令低8位
                if(sendCmdCount>1)
                    I2C_SetFunc(I2CX,I2cStart_En);     ///< 发送重复起始条件
                break;
            case 0x10:                                 ///< 已发送重复起始条件
                I2C_ClearFunc(I2CX,I2cStart_En);
                I2C_WriteByte(I2CX,I2C_DEVADDR|0x01);	 ///< 发送SLA+R,开始从从机读取数据
                break;
            case 0x40:                                 ///< 已发送SLA+R,并接收到ACK
                if(u32Len>1)
                {
                    I2C_SetFunc(I2CX,I2cAck_En);       ///< 使能主机应答功能
                }
                break;
            case 0x50:                                 ///< 已接收数据字节,并已返回ACK信号
                pu8Data[receiveCount++] = I2C_ReadByte(I2CX);
                if(receiveCount==u32Len-1)
                {
                    I2C_ClearFunc(I2CX,I2cAck_En);     ///< 已接收到倒数第二个字节,关闭ACK应答功能
                }
                break;
            case 0x58:                                 ///< 已接收到最后一个数据,NACK已返回
                pu8Data[receiveCount++] = I2C_ReadByte(I2CX);
                I2C_SetFunc(I2CX,I2cStop_En);          ///< 发送停止条件
                break;
            case 0x38:                                 ///< 在发送地址或数据时,仲裁丢失
                I2C_SetFunc(I2CX,I2cStart_En);         ///< 当总线空闲时发起起始条件
                break;
            case 0x48:                                 ///< 发送SLA+R后,收到一个NACK
                I2C_SetFunc(I2CX,I2cStop_En);          ///< 发送停止条件
                I2C_SetFunc(I2CX,I2cStart_En);         ///< 发送起始条件
                break;
            default:
                I2C_SetFunc(I2CX,I2cStart_En);         ///< 其他错误状态,重新发送起始条件
                break;
        }
        I2C_ClearIrq(I2CX);                            ///< 清除中断状态标志位
        if(receiveCount==u32Len)                                ///< 数据全部读取完成,跳出while循环
        {
                break;
        }
    }
    enRet = Ok;
    return enRet;
}

// 向SHT30发送一条指令(16bit)
uint8_t SHT30_Send_Cmd(SHT30_CMD cmd)
{
    uint8_t cmd_buffer[2];
    cmd_buffer[0] = cmd >> 8;
    cmd_buffer[1] = cmd;
    return I2C_SHT30WriteCmd(M0P_I2C1, cmd_buffer);
}

// 复位SHT30
void SHT30_reset(void)
{
    SHT30_Send_Cmd(SOFT_RESET_CMD);
    delay1us(20);
}

// 初始化SHT30 周期测量模式0x2220
uint8_t SHT30_Init(void)
{
    return SHT30_Send_Cmd(MEDIUM_2_CMD);
}

// 从SHT30读取一次数据
uint8_t SHT30_Read_Dat(uint8_t* dat)
{
    return I2C_MasterReadData(M0P_I2C1,READOUT_FOR_PERIODIC_MODE, dat, 6);
}

// 将SHT30接收的6个字节数据进行CRC校验,并转换为温度值和湿度值
uint8_t SHT30_Dat_To_Float(uint8_t* const dat, float* temperature, float* humidity)
{
    uint16_t recv_temperature = 0;
    uint16_t recv_humidity = 0;
 
    /* 校验温度数据和湿度数据是否接收正确 */
    if(CheckCrc8(dat, 0xFF) != dat[2] || CheckCrc8(&dat[3], 0xFF) != dat[5])
        return 1;
 
    /* 转换温度数据 */
    recv_temperature = ((uint16_t)dat[0]<<8)|dat[1];
    *temperature = -45 + 175*((float)recv_temperature/65535);
 
    /* 转换湿度数据 */
    recv_humidity = ((uint16_t)dat[3]<<8)|dat[4];
    *humidity = 100 * ((float)recv_humidity / 65535);
 
    return 0;
}

main.c文件

#include "sht3x.h"
#include "gpio.h"
#include "ddl.h"
#include "i2c.h"

uint8_t recv_dat[6] = {0};

float temperature = 0.0;
float humidity = 0.0;

int main(void)
{
		I2C_Cfg_Init();
		I2C_Port_Init();
      
    ///< 向I2C总线发起开始信号
    I2C_SetFunc(M0P_I2C1,I2cStart_En);  

		SHT30_Init();

		while (1)
		{
				delay1ms(1000);
				if(SHT30_Read_Dat(recv_dat) == Ok)
				{
						SHT30_Dat_To_Float(recv_dat, &temperature, &humidity);
				}
		}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dunkle.T

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值