STM32标准库函数驱动SHT30温湿度传感器详解

STM32标准库函数驱动SHT30温湿度传感器详解

一、SHT30传感器简介

SHT30是Sensirion推出的高精度数字温湿度传感器,支持I²C通信接口,具备以下特性:

  • 温度测量范围:-40°C ~ +125°C (±0.2°C精度)

  • 湿度测量范围:0% ~ 100% RH (±2% RH精度)

  • 工作电压:2.4V ~ 5.5V

  • 低功耗特性

二、模式选择说明

SHT30提供两种基本工作模式:

1. 单次测量模式(Single-Shot Mode)

  • 发送测量命令后执行单次测量

  • 支持不同重复性等级:

    • 高重复性:0x2C06 / 0x2400

    • 中重复性:0x2C0D / 0x240B

    • 低重复性:0x2C10 / 0x2416

  • 支持时钟拉伸(Clock Stretching)使能/禁用

2. 周期测量模式(Periodic Mode)

  • 可配置测量频率:

    • 0.5mps:0.5次/秒

    • 1mps:1次/秒

    • 2mps:2次/秒

    • 4mps:4次/秒

    • 10mps:10次/秒

  • 需先发送启动周期测量命令,后通过读取命令获取数据

三、I²C通信时序详解

1. 设备地址

  • 7位地址:0x44(默认)或0x45(ADDR引脚接VDD时)

2. 单次测量时序

START → 写地址(0x88) → ACK → 命令高位 → ACK → 命令低位 → ACK → STOP
(等待测量完成)
START → 读地址(0x89) → ACK → 读取6字节数据 → NACK → STOP

3. 数据格式

[温度高位][温度低位][温度CRC][湿度高位][湿度低位][湿度CRC]

四、硬件连接

SHT30引脚STM32引脚
VDD3.3V
GNDGND
SDAPB8
SCLPB9

五、完整代码框架

IIC.h

#ifndef __IIC_H
#define __IIC_H

#include "stm32f10x.h"  // 包含STM32F10x系列微控制器的头文件
#include "Delay.h"      // 包含延时函数的头文件

// 定义IIC通信中的应答类型枚举
typedef enum 
{
    IIC_ACK = 0,    // 应答信号
    IIC_NACK = 1    // 非应答信号
} IIC_AckType;

// 定义IIC相关的硬件配置
#define     RCC_IIC     RCC_APB2Periph_GPIOB  // IIC使用的GPIO端口时钟(GPIOB)
#define     IIC_PORT    GPIOB                 // IIC使用的GPIO端口(GPIOB)
#define     SCL_PIN     GPIO_Pin_9            // IIC的SCL引脚(PB9)
#define     SDA_PIN     GPIO_Pin_8            // IIC的SDA引脚(PB8)

// 函数声明

// 初始化IIC接口
void IIC_Init(void); 

// 写SCL引脚的电平状态
void IIC_W_SCL(uint8_t BitValue);

// 写SDA引脚的电平状态
void IIC_W_SDA(uint8_t BitValue);

// 读取SDA引脚的电平状态
uint8_t IIC_R_SDA(void);

// 发送IIC起始信号
void IIC_Start(void);

// 发送IIC停止信号
void IIC_Stop(void); 

// 发送应答信号
void IIC_SendAck(IIC_AckType State);

// 接收应答信号
IIC_AckType IIC_ReceiveAck(void);

// 发送一个字节数据
void IIC_SendByte(uint8_t Byte);

// 接收一个字节数据
uint8_t IIC_ReceiveByte(IIC_AckType State);

#endif

IIC.c

/**
 * @brief 初始化IIC接口
 * @param 无
 * @return 无
 * @note 配置SCL和SDA引脚为开漏输出模式,并拉高引脚电平,使IIC总线处于空闲状态。
 */
void IIC_Init(void) 
{
    RCC_APB2PeriphClockCmd(RCC_IIC, ENABLE);  // 使能GPIOB的时钟
    GPIO_InitTypeDef GPIO_InitStructure;      // 定义GPIO初始化结构体
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;  // 配置为开漏输出模式
    GPIO_InitStructure.GPIO_Pin = SCL_PIN | SDA_PIN;  // 选择SCL和SDA引脚
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 配置引脚速度为50MHz
    GPIO_Init(IIC_PORT, &GPIO_InitStructure); // 初始化GPIO
    GPIO_SetBits(IIC_PORT, SCL_PIN | SDA_PIN); // 将SCL和SDA引脚置高
    Delay_us(5);                              // 延时5微秒
}

/**
 * @brief 设置SCL引脚的电平状态
 * @param BitValue 电平状态(0:低电平,1:高电平)
 * @return 无
 * @note 设置SCL引脚的电平,并延时5微秒以确保电平稳定。
 */
void IIC_W_SCL(uint8_t BitValue)
{
    GPIO_WriteBit(IIC_PORT, SCL_PIN, (BitAction)BitValue); // 设置SCL引脚电平
    Delay_us(5);                                           // 延时5微秒
}

/**
 * @brief 设置SDA引脚的电平状态
 * @param BitValue 电平状态(0:低电平,1:高电平)
 * @return 无
 * @note 设置SDA引脚的电平,并延时5微秒以确保电平稳定。
 */
void IIC_W_SDA(uint8_t BitValue)    
{ 
    GPIO_WriteBit(IIC_PORT, SDA_PIN, (BitAction)BitValue); // 设置SDA引脚电平
    Delay_us(5);                                           // 延时5微秒
}

/**
 * @brief 读取SDA引脚的电平状态
 * @param 无
 * @return SDA引脚的电平状态(0:低电平,1:高电平)
 * @note 读取SDA引脚的电平,并延时5微秒以确保电平稳定。
 */
uint8_t IIC_R_SDA(void)
{                                                                
    uint8_t BitValue;
    BitValue = GPIO_ReadInputDataBit(IIC_PORT, SDA_PIN); // 读取SDA引脚电平
    Delay_us(5);                                         // 延时5微秒
    return BitValue;                                     // 返回读取的电平值
}

/**
 * @brief 发送IIC起始信号
 * @param 无
 * @return 无
 * @note 起始信号是IIC通信的开始标志,时序为:SDA从高到低,SCL保持高电平。
 */
void IIC_Start(void) 
{
    IIC_W_SDA(1);  // 拉高SDA
    IIC_W_SCL(1);  // 拉高SCL
    IIC_W_SDA(0);  // 拉低SDA,产生起始信号
    IIC_W_SCL(0);  // 拉低SCL,准备发送数据
}

/**
 * @brief 发送IIC停止信号
 * @param 无
 * @return 无
 * @note 停止信号是IIC通信的结束标志,时序为:SDA从低到高,SCL保持高电平。
 */
void IIC_Stop(void) 
{
    IIC_W_SDA(0);  // 拉低SDA
    IIC_W_SCL(1);  // 拉高SCL
    IIC_W_SDA(1);  // 拉高SDA,产生停止信号
}

/**
 * @brief 发送应答信号
 * @param State 应答状态(IIC_ACK:应答,IIC_NACK:非应答)
 * @return 无
 * @note 根据State参数发送应答或非应答信号,时序为:SDA在SCL高电平期间保持相应状态。
 */
void IIC_SendAck(IIC_AckType State)
{
    switch (State)
    {
        case IIC_ACK :
                IIC_W_SDA(0);  // 发送应答信号(拉低SDA)
            break;
        case IIC_NACK :
                IIC_W_SDA(1);  // 发送非应答信号(拉高SDA)
            break;
        default:
                IIC_W_SDA(1);  // 默认发送非应答信号
            break;
    }
    IIC_W_SCL(1);  // 拉高SCL,从机读取应答信号
    IIC_W_SCL(0);  // 拉低SCL,准备后续操作
}

/**
 * @brief 接收应答信号
 * @param 无
 * @return 应答状态(IIC_ACK:应答,IIC_NACK:非应答)
 * @note 在SCL高电平期间读取SDA引脚状态,判断从机是否应答。
 */
IIC_AckType IIC_ReceiveAck(void)
{
    uint8_t Cnt = 0;
    IIC_W_SDA(1);  // 释放SDA控制权
    IIC_W_SCL(1);  // 拉高SCL,从机可发送应答信号
    while ((IIC_R_SDA() == 1) && Cnt < 100)  // 等待从机应答
    {
        Cnt++;
        if(Cnt == 100)  // 超时未收到应答
        {
            return IIC_NACK;
        }
    } 
    IIC_W_SCL(0);  // 拉低SCL,结束应答检测
    return IIC_ACK;  // 返回应答状态
}

/**
 * @brief 发送一个字节数据
 * @param Byte 要发送的数据(8位)
 * @return 无
 * @note 从高位到低位依次发送数据的每一位,SCL在每位数据稳定后产生时钟信号。
 */
void IIC_SendByte(uint8_t Byte)
{
    uint8_t i;
    for (i = 0; i < 8; i++)  // 逐位发送
    {
        if((Byte & 0x80) == 0x80)  // 判断最高位
        {
            IIC_W_SDA(1);  // 发送1
        } 
        else
        {
            IIC_W_SDA(0);  // 发送0
        }
        IIC_W_SCL(1);  // 拉高SCL,从机读取数据
        IIC_W_SCL(0);  // 拉低SCL,准备发送下一位
        Byte = Byte << 1;  // 左移一位,准备发送下一位
    }
}

/**
 * @brief 接收一个字节数据
 * @param State 是否发送应答信号(IIC_ACK:发送应答,IIC_NACK:发送非应答)
 * @return 接收到的数据(8位)
 * @note 从高位到低位依次读取数据的每一位,SCL在每位数据稳定后产生时钟信号。
 */
uint8_t IIC_ReceiveByte(IIC_AckType State)
{
    uint8_t i;
    uint8_t Data = 0x00;
    IIC_W_SDA(1);  // 释放SDA控制权
    for (i = 0; i < 8; i ++)  // 逐位接收
    {
        IIC_W_SCL(1);  // 拉高SCL,从机发送数据
        if (IIC_R_SDA() == 1)  // 读取SDA电平
        {
            Data |= (0x80 >> i);  // 将数据位存入Data
        }
        IIC_W_SCL(0);  // 拉低SCL,准备接收下一位
    }
    IIC_SendAck(State);  // 发送应答信号
    return Data;  // 返回接收到的数据
}

SHT30.h

#ifndef __SHT30_H
#define __SHT30_H

#include "stm32f10x.h"  // 包含STM32F10x系列微控制器的头文件
#include "IIC.h"        // 包含IIC通信的头文件
#include "stdio.h"      // 包含标准输入输出头文件

// 定义SHT30传感器数据存储结构体
typedef struct 
{
    float temperature; // 温度,单位为℃
    float humidity;    // 湿度,单位为%RH
} SHT30_Data;

// 定义IIC操作类型枚举
typedef enum 
{
    READ = 0,           // 读操作
    WRITE               // 写操作
} Operation;


// SHT30 IIC 地址(7位地址)
#define SHT30_IIC_ADDRESS (uint8_t)0x44

// CRC校验多项式(X^8 + X^5 + X^4 + 1)
#define POLYNOMIAL (uint8_t)0x31

// SHT30数据长度(温度 + 湿度 + CRC校验)
#define SHT30_DATA_LENGTH   6

// SHT30 测量命令
#define SHT30_CMD_MEASURE_HIGHREP      (uint16_t)0x2C06 // 高重复性测量
#define SHT30_CMD_MEASURE_MEDREP       (uint16_t)0x240D // 中重复性测量
#define SHT30_CMD_MEASURE_LOWREP       (uint16_t)0x2410 // 低重复性测量

// 全局变量声明
extern SHT30_Data SHT30; // 存储SHT30传感器的温度和湿度数据

// 函数声明
void SHT30_SendHeader(Operation op);
void SHT30_SendCmd(uint16_t cmd);
uint8_t SHT30_CRC(uint8_t *data, uint8_t len);
void SHT30_Measure(SHT30_Data *SHT30, uint16_t Cmd);

#endif

 SHT30.c



// 包含自定义的SHT30传感器头文件(定义数据结构、操作命令等)
#include "SHT30.h"

// 声明全局传感器数据结构体,用于存储测量结果
SHT30_Data SHT30;  

/**
 * @brief 发送I2C通信起始头和设备地址
 * @param op 操作类型:READ-读取操作 / WRITE-写入操作
 * @note 此函数负责I2C通信的起始信号和设备地址发送,
 *       根据操作类型构造不同的从机地址字节
 */
void SHT30_SendHeader(Operation op)
{
    IIC_Start();  // 产生I2C起始信号
    
    if(op == READ)  // 读取操作模式
    {
        // 构造读地址:(7位地址 << 1) | 0x01(读标志位)
        IIC_SendByte((SHT30_IIC_ADDRESS << 1) | 0x01); 
        // 等待从机应答,若未收到ACK则持续重试
        while (IIC_ReceiveAck() == IIC_NACK) 
        {
            printf("SHT30读操作失败!请检查设备连接!\n"); 
        }
    }      
    else if(op == WRITE)  // 写入操作模式
    {
        // 构造写地址:(7位地址 << 1) & 0xFE(写标志位)
        IIC_SendByte((SHT30_IIC_ADDRESS << 1) & 0xFE);
        // 等待从机应答
        while (IIC_ReceiveAck() == IIC_NACK) 
        {
            printf("SHT30写操作失败!请检查设备连接!\n");
        }
    }
    else  // 无效操作类型
    {
        IIC_Stop();  // 发送停止信号保证总线释放
    }    
}

/**
 * @brief 发送16位命令到传感器
 * @param cmd 要发送的16位命令(参见数据手册命令列表)
 * @note 命令分高8位和低8位两次发送,每次发送后需检查应答
 */
void SHT30_SendCmd(uint16_t cmd)
{
    // 发送命令高字节(MSB First)
    IIC_SendByte((cmd >> 8) & 0xFF); 
    // 检查ACK应答
    while (IIC_ReceiveAck() == IIC_NACK)
    {
        printf("SHT30命令高字节发送失败!\n");
    }
    
    // 发送命令低字节(LSB)
    IIC_SendByte(cmd & 0xFF); 
    while (IIC_ReceiveAck() == IIC_NACK)
    {
        printf("SHT30命令低字节发送失败!\n");
    }
    
    IIC_Stop();  // 发送停止信号结束本次传输
    
    // 延迟50ms等待传感器处理命令(具体时间需参考数据手册)
    Delay_ms(50);
}

/**
 * @brief CRC8校验计算(多项式0x31)
 * @param data 待校验数据指针
 * @param len 数据长度
 * @return 计算得到的CRC校验值
 * @note 使用SHT30指定的CRC8校验算法,多项式:x^8 + x^5 + x^4 +1 (0x31)
 */
uint8_t SHT30_CRC(uint8_t *data, uint8_t len)
{
    uint8_t bit;        // 位计数器
    uint8_t crc = 0xFF; // 初始值
    uint8_t byteCtr;    // 字节计数器
    
    // 逐字节处理数据
    for(byteCtr = 0; byteCtr < len; byteCtr++) 
    {
        crc ^= data[byteCtr];  // 异或当前字节
        
        // 逐位处理(8次循环)
        for(bit = 8; bit > 0; bit--) 
        {
            // 检测最高位是否为1
            if((crc & 0x80) == 0x80) 
            {
                crc = (crc << 1) ^ POLYNOMIAL; // 左移并异或多项式
            } 
            else 
            {
                crc = (crc << 1); // 简单左移
            }
        }
    }
    return crc; // 返回最终校验值
}

/**
 * @brief 执行温湿度测量并处理数据
 * @param SHT30 传感器数据结构体指针
 * @param Cmd 测量命令(参见数据手册,如0x2C06表示高速测量)
 * @note 完整测量流程:发送命令->等待测量->读取数据->校验->转换物理量
 */
void SHT30_Measure(SHT30_Data *SHT30, uint16_t Cmd)
{
    uint8_t i;
    uint8_t Buff[SHT30_DATA_LENGTH]; // 数据缓冲区(通常为6字节)
    
    // 阶段1:发送测量命令
    SHT30_SendHeader(WRITE); // 启动写操作
    SHT30_SendCmd(Cmd);      // 发送测量命令
    
    // 阶段2:读取测量数据
    SHT30_SendHeader(READ); // 切换为读模式
    
    // 读取6字节数据(温度3字节,湿度3字节)
    for(i = 0; i < SHT30_DATA_LENGTH; i++)
    {
        // 最后一个字节发送NACK结束读取
        if(i == SHT30_DATA_LENGTH - 1)
        {
            Buff[i] = IIC_ReceiveByte(IIC_NACK);
        }
        else
        {
            Buff[i] = IIC_ReceiveByte(IIC_ACK);
        }
    }
    IIC_Stop(); // 结束本次读取
    
    // 阶段3:数据校验与转换
    /* 校验结构:
     * 字节0-1:温度数据
     * 字节2:温度CRC
     * 字节3-4:湿度数据
     * 字节5:湿度CRC
     */
    if((SHT30_CRC(Buff, 2) == Buff[2]) && 
       (SHT30_CRC(Buff + 3, 2) == Buff[5]))
    {
        // 温度转换公式(数据手册提供)
        SHT30->temperature = (-45 + (175.0 * 
                           ((Buff[0] << 8) | Buff[1]) / 65535.0));
        
        // 湿度转换公式(数据手册提供)
        SHT30->humidity = 100 * 
                        (((Buff[3] << 8) | Buff[4]) / 65535.0);
    }
    else
    {
        printf("SHT30数据校验失败!可能数据损坏!\n"); 
    }     
}

 main.c

#include "stm32f10x.h"           
#include "IIC.h"
#include "Usart.h"
#include "SHT30.h"
int main(void) 
{
	Usart_Init(BAUDRATE);
	IIC_Init();
	while(1) 
	{
		SHT30_Measure(&SHT30, SHT30_CMD_MEASURE_HIGHREP);
		printf("温度:%.2f℃ 相对湿度:%.2f%%RH",SHT30.temperature,SHT30.humidity)
	}                                            
}

六、注意事项

  1. 确保I²C总线时序满足传感器要求(典型100kHz)

  2. 测量完成后需等待足够时间(单次模式典型12ms)

  3. 建议始终进行CRC校验确保数据可靠性

七、优化建议

  • 使用DMA传输提高效率

  • 实现中断驱动模式

  • 添加温度补偿算法

  • 实现低功耗模式控制

(注:实际使用时需根据具体硬件平台调整引脚配置和延时参数)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值