STM32 驱动 SHT35 温湿度传感器:从 I²C 通信到 CRC 校验的完整实战


一、SHT35 简介

SHT35 是一款高精度数字式温湿度传感器,采用 I²C 通信接口,可测量环境温度和相对湿度,温度测量范围为 −40~125℃,湿度测量范围为 0~100%RH,具有测量精度高、功耗低、体积小和稳定性好等特点。通过单片机发送测量命令并读取传感器数据,广泛应用于环境监测、智能家居及工业温湿度检测等领域

二、引脚功能

引脚 功能
VCC 电源正 3.3-5V
GND 参考地
SDA 数据线
SCL 时钟线

三、驱动与数据说明

驱动说明

传感器上电后需等待小段时间,发送写命令 0x2400,等待 20ms 测量完成; 后读取 6 字节温湿度数据,再进行CRC检验

数据说明

温湿度数据计算

CRC校验

CRC计算代码如下:

c 复制代码
static uint8_t SHT35_CRC8(const uint8_t *data, uint8_t len)
{
    uint8_t crc = 0xFF;
    uint8_t i, j;

    for (i = 0; i < len; i++)
    {
        crc ^= data[i];
        for (j = 0; j < 8; j++)
        {
            if (crc & 0x80) {crc = (uint8_t)((crc << 1) ^ 0x31);}
            else {crc = (uint8_t)(crc << 1);}
        }
    }
    return crc;
}

四、程序

STM32

SHT35 .C

c 复制代码
#include "sht35.h"
#include "delay.h"

#define SHT35_ADDR  0x44
#define I2C_TIMEOUT 100000U

static uint8_t I2C1_WaitEvent(uint32_t event)
{
    uint32_t timeout = I2C_TIMEOUT;

    while (!I2C_CheckEvent(I2C1, event))
    {
        if (--timeout == 0) return 0;
    }
		
    return 1;
}

static uint8_t I2C1_WaitFlag(uint32_t flag, FlagStatus status)
{
    uint32_t timeout = I2C_TIMEOUT;

    while (I2C_GetFlagStatus(I2C1, flag) != status)
    {
        if (--timeout == 0) return 0;
    }

    return 1;
}

static void I2C1_Stop(void)
{
    I2C_GenerateSTOP(I2C1, ENABLE);
    I2C_AcknowledgeConfig(I2C1, ENABLE);
}

static void I2C1_Init(void)
{
    GPIO_InitTypeDef gpio;
    I2C_InitTypeDef i2c;

    RCC_APB2PeriphClockCmd(
    RCC_APB2Periph_GPIOB, ENABLE);

    RCC_APB1PeriphClockCmd(
    RCC_APB1Periph_I2C1, ENABLE);

    /* PB6 = SCL,PB7 = SDA */
    gpio.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
    gpio.GPIO_Speed = GPIO_Speed_50MHz;
    gpio.GPIO_Mode = GPIO_Mode_AF_OD;
    GPIO_Init(GPIOB, &gpio);

    I2C_DeInit(I2C1);
    I2C_StructInit(&i2c);

    i2c.I2C_Mode = I2C_Mode_I2C;
    i2c.I2C_DutyCycle = I2C_DutyCycle_2;
    i2c.I2C_OwnAddress1 = 0;
    i2c.I2C_Ack = I2C_Ack_Enable;
    i2c.I2C_AcknowledgedAddress =
    I2C_AcknowledgedAddress_7bit;
    i2c.I2C_ClockSpeed = 100000;

    I2C_Init(I2C1, &i2c);
    I2C_Cmd(I2C1, ENABLE);
}

void SHT35_Init(void)
{
    I2C1_Init();
}


static uint8_t SHT35_WriteCommand(uint16_t cmd)
{
    if (!I2C1_WaitFlag(I2C_FLAG_BUSY, RESET)) return 0;

    I2C_GenerateSTART(I2C1, ENABLE);
    if (!I2C1_WaitEvent(I2C_EVENT_MASTER_MODE_SELECT)) goto error;

    I2C_Send7bitAddress(I2C1, SHT35_ADDR << 1,I2C_Direction_Transmitter);

    if (!I2C1_WaitEvent(I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) goto error;

    I2C_SendData(I2C1, (uint8_t)(cmd >> 8));

    if (!I2C1_WaitEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED)) goto error;

    I2C_SendData(I2C1, (uint8_t)cmd);

    if (!I2C1_WaitEvent(I2C_EVENT_MASTER_BYTE_TRANSMITTED)) goto error;

    I2C1_Stop();
    return 1;

error:
    I2C1_Stop();
    return 0;
}


static uint8_t SHT35_ReadBytes(uint8_t *data)
{
    uint8_t i;

    if (!I2C1_WaitFlag(I2C_FLAG_BUSY, RESET)) return 0;

    I2C_AcknowledgeConfig(I2C1, ENABLE);
    I2C_GenerateSTART(I2C1, ENABLE);

    if (!I2C1_WaitEvent(I2C_EVENT_MASTER_MODE_SELECT)) goto error;

    I2C_Send7bitAddress(I2C1, SHT35_ADDR << 1,I2C_Direction_Receiver);

    if (!I2C1_WaitEvent(I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)) goto error;


    for (i = 0; i < 3; i++)
    {
        if (!I2C1_WaitFlag(I2C_FLAG_RXNE, SET)) goto error;

        data[i] = I2C_ReceiveData(I2C1);
    }

    if (!I2C1_WaitFlag(I2C_FLAG_BTF, SET)) goto error;

    I2C_AcknowledgeConfig(I2C1, DISABLE);
    data[3] = I2C_ReceiveData(I2C1);

    if (!I2C1_WaitFlag(I2C_FLAG_BTF, SET)) goto error;

    I2C_GenerateSTOP(I2C1, ENABLE);
    data[4] = I2C_ReceiveData(I2C1);
    data[5] = I2C_ReceiveData(I2C1);
    I2C_AcknowledgeConfig(I2C1, ENABLE);

    return 1;

error:
    I2C1_Stop();
    return 0;
}


static uint8_t SHT35_CRC8(const uint8_t *data, uint8_t len)
{
    uint8_t crc = 0xFF;
    uint8_t i, j;

    for (i = 0; i < len; i++)
    {
        crc ^= data[i];
        for (j = 0; j < 8; j++)
        {
            if (crc & 0x80) {crc = (uint8_t)((crc << 1) ^ 0x31);}
            else {crc = (uint8_t)(crc << 1);}
        }
    }
    return crc;
}


uint8_t SHT35_ReadData(float *temperature, float *humidity)
{
    uint8_t data[6];
    uint16_t rawT, rawRH;

    if (temperature == 0 || humidity == 0)
        return 0;
    if (!SHT35_WriteCommand(0x2400))
        return 0;
		
    Delay_ms(20);
		
    if (!SHT35_ReadBytes(data))
        return 0;
    if (SHT35_CRC8(&data[0], 2) != data[2])
        return 0;
    if (SHT35_CRC8(&data[3], 2) != data[5])
        return 0;

    rawT = ((uint16_t)data[0] << 8) | data[1];
    rawRH = ((uint16_t)data[3] << 8) | data[4];

    *temperature = -45.0f + 175.0f * (float)rawT / 65535.0f;

    *humidity = 100.0f * (float)rawRH / 65535.0f;

    if (*humidity < 0.0f)
        *humidity = 0.0f;

    if (*humidity > 100.0f)
        *humidity = 100.0f;

    return 1;
}

main.C

c 复制代码
#include "stm32f10x.h"
#include <stdio.h>
#include <Delay.h>
#include "sht35.h"
#include <stdint.h>

void USART1_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_USART1,ENABLE);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    USART_InitStructure.USART_BaudRate = 115200;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
    USART_Init(USART1, &USART_InitStructure);

    USART_Cmd(USART1, ENABLE);
}
int fputc(int ch, FILE *f)
{
    USART_SendData(USART1,(uint8_t)ch);
    while (USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
    return ch;
}

int main(void)
{
    float temperature = 0.0f;
    float humidity = 0.0f;
    int32_t t100;
    uint32_t h100;

    SystemInit();
    USART1_Init();
    SHT35_Init();

    Delay_ms(500);

    while (1)
    {
        if (SHT35_ReadData(&temperature, &humidity))
        {
            t100 = (int32_t)(temperature * 100.0f);
            h100 = (uint32_t)(humidity * 100.0f);

            if (t100 < 0)
            {
                printf("Temperature: -%ld.%02ld C, ",(long)(-t100 / 100),(long)(-t100 % 100));
            }
            else
            {
                printf("Temperature: %ld.%02ld C, ",(long)(t100 / 100),(long)(t100 % 100));
            }

            printf("Humidity: %lu.%02lu %%RH\r\n",(unsigned long)(h100 / 100),(unsigned long)(h100 % 100));
        }
        else
        {
            printf("Error!\r\n");
        }

        Delay_ms(1000);
    }
}

五、测试

---------------------------------------------------接线--------------------------------------------------

  • UART:
  • PA9 -> TX
  • PA10 -> RX
  • IIC:
  • PB6 -> SCL
  • PB7 -> SDA

开发板串口输出如下:

以上内容个人理解,如有不正欢迎指正,需要资料及工程可留言邮箱

相关推荐
Hotpot1 小时前
3 线程管理和调度
嵌入式
一直C3 小时前
嵌入式 Linux 驱动开发学习笔记:设备树、pinctrl 与 GPIO 驱动开发
驱动开发·嵌入式·arm·dt
女神下凡3 小时前
芯参谋(61):PMSM永磁同步电机驱动电路设计指南
arm开发·单片机·嵌入式硬件·硬件工程·设计规范
HRTOS3 小时前
HRTOS 4.0 中断嵌套注意事项:为什么不建议在嵌套中断中调用唤醒任务 API?
单片机·嵌入式硬件·51单片机
新晨单片机设计4 小时前
S023E-基于STM32单片机WIFI智能插座(温度、定时)【Proteus仿真+Keil程序+原理图】
stm32·单片机·proteus·wifi智能电表
硅基手札4 小时前
SPI技术系列 08 · SPI 控制器寄存器与 IP 架构
驱动开发·stm32·单片机·嵌入式硬件·mcu·架构·计算机外设
夏虫不可与之言冰5 小时前
第三课 点亮 LED 与定时器非阻塞闪烁
c语言·c++·vscode·单片机·嵌入式硬件·物联网
Chery11405 小时前
ESP32-C3FH8X技术解析:8MB Flash低功耗Wi-Fi与Bluetooth LE物联网SoC
单片机·物联网
blueSatchel5 小时前
framebuffer注册源码解读
linux·嵌入式