STM32 驱动 DHT30 测量环境温湿度(兼容SHT30):从时序解析到完整代码实战

文章目录


摘要:本文详细介绍 DHT30 温湿度传感器模块的引脚功能、I2C 通信时序与数据格式,并给出基于 STM32F103 的完整驱动代码(含 CRC8 校验)与实测串口输出,帮助读者快速上手该传感器的开发与调试。

一、DHT30 简介

DHT30 是一款小尺寸的温湿度传感器模块,采用针型连接器设计,便于集成与更换。模块内部集成了全新优化的 ASIC 专用芯片、高性能的 MEMS 半导体电容式湿度传感元件,以及标准的片上温度传感元件,具有功耗低、精度高的特点,即使在恶劣环境下也能保持稳定的性能。

二、引脚功能

引脚 功能
1-VDD 电源正
2-SDA 数据线
3-GND 电源负
4-SCL 时钟线

三、时序与数据说明

时序说明

传感器读取流程 :

传感器的VDD上电后需等待5ms,发送写测量命令0x70 0xAC 0x33 0x00,等待80ms测量完成;
在等待80ms测量完成后,发送 0x71 读传感器,可获取状态字 Status、温湿度校准数据SRH19:0、ST19:0 以及校准字 CRC

数据说明

1.状态字 Status

2.温湿度数据计算

3. CRC校验

将测量读取到的Status、SRH19:0、ST19:0进行CRC8检验,CRC初始值为0xFF,CRC8校验多项式为:CRC7:0=1+x4+x5+x8, CRC计算代码如下:

c 复制代码
//**********************************************************//
//CRC校验类型:CRC8
//多项式:X8+X5+X4+1
//Poly:0011 0001 0x31
unsigned char Calc_CRC8(unsigned char *message,unsigned char Num)
{
unsigned char i;
unsigned char byte;
unsigned char crc =0xFF;
for (byte = 0;byte<Num;byte++)
{
crc^=(message[byte]);
for(i=8;i>0;--i)
{
if(crc&0x80)
crc=(crc<<1)^0x31;
else
crc=(crc<<1);
}
}
Return crc;
}
//**********************************************************/

四、程序

STM32

DHTC11 .C

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

/*=========================================================
 * DHT30
 *
 * 7位I2C地址:0x38
 * 写地址:0x70
 * 读地址:0x71
 *=========================================================*/

#define DHT30_ADDR     0x38


/*=========================================================
 * USART1
 *
 * PA9  -> TX
 * PA10 -> RX
 *
 * 115200 8-N-1
 *=========================================================*/

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

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA |RCC_APB2Periph_USART1,ENABLE);

    /* PA9 -> TX */
    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);

    /* PA10 -> RX */
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    /* USART */
    USART_InitStructure.USART_BaudRate = 9600;
    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);
}


/*=========================================================
 * printf 重定向
 *=========================================================*/

int fputc(int ch, FILE *f)
{
    USART_SendData(USART1, (uint8_t)ch);

    while (USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);

    return ch;
}



/*=========================================================
 * I2C1初始化
 *
 * PB6 -> SCL
 * PB7 -> SDA
 *
 * 100kHz
 *=========================================================*/

void I2C1_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    I2C_InitTypeDef I2C_InitStructure;

    /* GPIOB时钟 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
    /* I2C1时钟 */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);

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

    /* I2C参数 */
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
    I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
    I2C_InitStructure.I2C_ClockSpeed = 100000;
    I2C_InitStructure.I2C_OwnAddress1 = 0x00;
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;

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


/*=========================================================
 * DHT30 CRC8
 *
 * 初始值:0xFF
 * 多项式:0x31
 *=========================================================*/

uint8_t DHT30_CRC8(uint8_t *data,uint8_t len)
{
    uint8_t i;
    uint8_t j;
    uint8_t crc = 0xFF;

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


/*=========================================================
 * DHT30开始测量
 *
 * 发送:
 * 0x70 0xAC 0x33 0x00
 *=========================================================*/

uint8_t DHT30_StartMeasure(void)
{
    uint32_t timeout;
    /* 等待I2C空闲 */
    timeout = 100000;
    while (I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY))
    {
        if (--timeout == 0)
					return 0;
    }
    /* START */
    I2C_GenerateSTART(I2C1,ENABLE);
    timeout = 100000;
    while (!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT))
    {
        if (--timeout == 0)
					return 0;
    }
    /* 发送DHT30写地址:0x70 */
    I2C_Send7bitAddress(I2C1,DHT30_ADDR << 1,I2C_Direction_Transmitter);
    timeout = 100000;
    while (!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
    {
        if (I2C_GetFlagStatus(I2C1,I2C_FLAG_AF))
        {
            I2C_ClearFlag(I2C1,I2C_FLAG_AF);
            I2C_GenerateSTOP(I2C1,ENABLE);
            return 0;
        }
        if (--timeout == 0)
        {
            I2C_GenerateSTOP(I2C1,ENABLE);
            return 0;
        }
    }

    /* 0xAC */
    I2C_SendData(I2C1,0xAC);
    timeout = 100000;
    while (!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED))
    {
        if (--timeout == 0)
        {
            I2C_GenerateSTOP(I2C1,ENABLE);
            return 0;
        }
    }
		
    /* 0x33 */
    I2C_SendData(I2C1,0x33);
    timeout = 100000;
    while (!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED))
    {
        if (--timeout == 0)
        {
            I2C_GenerateSTOP(I2C1,ENABLE);
            return 0;
        }
    }

    /* 0x00 */
    I2C_SendData(I2C1,0x00);
    timeout = 100000;
    while (!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED))
    {
        if (--timeout == 0)
        {
            I2C_GenerateSTOP(I2C1,ENABLE);
            return 0;
        }
    }

    /* STOP */
    I2C_GenerateSTOP(I2C1,ENABLE);

    return 1;
}


/*=========================================================
 * 读取DHT30 7字节
 *
 * data[0] = Status
 * data[1] = RH[19:12]
 * data[2] = RH[11:4]
 * data[3] = RH[3:0] + T[19:16]
 * data[4] = T[15:8]
 * data[5] = T[7:0]
 * data[6] = CRC
 *=========================================================*/

uint8_t DHT30_ReadData(uint8_t *data)
{
    uint32_t timeout;
    uint8_t i;

    /* 打开ACK */
    I2C_AcknowledgeConfig(I2C1,ENABLE);

    /* 等待总线空闲 */
    timeout = 100000;

    while (I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY))
    {
			if (--timeout == 0)
				return 0;
    }

    /* START */
    I2C_GenerateSTART(I2C1,ENABLE);
    timeout = 100000;
    while (!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT))
    {
			if (--timeout == 0)
				return 0;
    }

    /* DHT30读地址:0x71 */
    I2C_Send7bitAddress(I2C1,DHT30_ADDR << 1,I2C_Direction_Receiver);
    timeout = 100000;
    while (!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED))
    {
        if (--timeout == 0)
        {
            I2C_GenerateSTOP(I2C1,ENABLE);
            return 0;
        }
    }
    /* 读取前6字节 */
    for (i = 0; i < 6; i++)
    {
        timeout = 100000;
        while (!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED))
        {
            if (--timeout == 0)
            {
                I2C_AcknowledgeConfig(I2C1,ENABLE);
                I2C_GenerateSTOP(I2C1,ENABLE);
                return 0;
            }
        }
        data[i] = I2C_ReceiveData(I2C1);
    }

    /* 最后一个字节关闭ACK */
    I2C_AcknowledgeConfig(I2C1,DISABLE);
    timeout = 100000;
    while (!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED))
    {
        if (--timeout == 0)
        {
            I2C_AcknowledgeConfig(I2C1,ENABLE);
            I2C_GenerateSTOP(I2C1,ENABLE);
            return 0;
        }
    }

    /* STOP */
    I2C_GenerateSTOP(I2C1,ENABLE);

    /* CRC */
    data[6] = I2C_ReceiveData(I2C1);

    /* 恢复ACK */
    I2C_AcknowledgeConfig(I2C1,ENABLE);

    return 1;
}


/*=========================================================
 * DHT30读取温湿度
 *=========================================================*/

uint8_t DHT30_Read(float *temperature,float *humidity)
{
    uint8_t data[7];
    uint8_t crc;

    uint32_t raw_humidity;
    uint32_t raw_temperature;

    /* 启动测量 */
    if (!DHT30_StartMeasure())
    {
        printf("Measure Start Error\r\n");
        return 0;
    }
		
    /*
     * 等待DHT30完成测量
     * 手册要求约80ms
     */
    Delay_ms(100);
		
    /* 读取7字节 */
    if (!DHT30_ReadData(data))
    {
        printf("I2C Read Error\r\n");
        return 0;
    }

    /* CRC校验 */
    crc = DHT30_CRC8(data,6);

    if (crc != data[6])
    {
        printf("CRC Error\r\n");
        return 0;
    }

    /* BUSY检查 */
    if (data[0] & 0x80)
    {
        printf("DHT30 Busy\r\n");
        return 0;
    }

    /*
     * 湿度20位
     */
    raw_humidity =((uint32_t)data[1] << 12) |((uint32_t)data[2] << 4) |((data[3] >> 4) & 0x0F);

    /*
     * 温度20位
     */
    raw_temperature =((uint32_t)(data[3] & 0x0F) << 16) |((uint32_t)data[4] << 8) |data[5];

    /*
     * 湿度 RH = raw / 2^20 × 100
     */
    *humidity = ((float)raw_humidity * 100.0f) / 1048576.0f;

    /*
     * 温度 T = raw / 2^20 × 200 - 50
     */
    *temperature =((float)raw_temperature * 200.0f) / 1048576.0f - 50.0f;
		
    return 1;
}


/*=========================================================
 * MAIN
 *=========================================================*/
int main(void)
{
    float temperature;
    float humidity;

    /* USART1 */
    USART1_Init();

    /* I2C1 */
    I2C1_Init();

    /*
     * DHT30上电延时稳定
     */
    Delay_ms(100);

    while (1)
    {
			if(DHT30_Read(&temperature,&humidity))
				{
					printf("T=%.2f C, H=%.2f %%RH\r\n",temperature,humidity);
        }

        Delay_ms(1000);
    }
}

五、驱动测试现象

---------------------------------------------------DHT30接线--------------------------------------------------

PA9 ->TX

PA10->RX

PB6 ->SCL

PB7 ->SDA

注:(收到优信电子的DHT30模块后可以看到IIC接口并没有上拉,建议实际使用给模块的IIC接口上拉4.7K电阻)

开发板串口输出如下:

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

相关推荐
捷米特网关模块通讯2 小时前
IOLink 总线与 SIO 开关量一体化采集方案 降低非标设备硬件及软件开发成本
传感器·工业自动化·工业智能网关·协议转换网关
小李不想当小白3 小时前
DMA直接存储器存取(STM32标准库学习笔记)
笔记·stm32·单片机·嵌入式硬件·学习·分享
朗锐智科_机器视觉5 小时前
光电传感器:机器视觉的“触发与到位“
人工智能·机器视觉·传感器·工控机·图像采集卡
Zw-awa8 小时前
点亮第一盏 LED:STM32 的 GPIO 到底在控制什么
stm32
剑指offer.8 小时前
嵌入式硬件-ARM芯片的启动
c语言·嵌入式硬件·嵌入式
晊晌_h9 小时前
ARM 学习 |ARM 汇编指令实战笔记
嵌入式·arm·arm汇编
嵌入式阿蔡9 小时前
ROS2嵌入式量产2026:从科研原型到工业部署
嵌入式
DaTou大头9 小时前
STM32第六篇
单片机·嵌入式·电路基础
嵌入式阿蔡9 小时前
RISC-V汽车芯片2026:从电子后视镜到发动机ECU的量产突破
嵌入式