【CW32模块使用】CW32L012 DHT11温湿度传感器

DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。其成本低、长期稳定、可以测量相对湿度和温度测量,并可以只使用一根数据线进行温湿度采集。

一、模块来源

模块实物展示:

wKgZO2nM3mSAKFeWAAMyn3eLkfY540.png

资料下载链接:

https://pan.baidu.com/s/1HQEL699-Yl5Jh3Hp87_FlQ

资料提取码:2sgq

二、规格参数

模块的厂家资料下载见百度网盘链接

工作电压:3-5.5V

工作电流:1MA

测量分辨率:8 bit

湿度量程: 20 - 90 %RH

湿度精度:±5 %RH

温度量程: 0 - 50 ℃

温度精度:±2 ℃

通信协议:单总线

管脚数量:3 Pin(2.54mm间距排针

三、移植过程

我们的目标是在CW32L012_快速项目开发板上能够实现读取温湿度的功能。首先要获取资料(https://wiki.lckfb.com/zh-hans/dwx-cw32f030c8t6/module/sensor/dht11.html),查看数据手册应如何实现读取数据,再移植至我们的工程。

3.1查看资料

DHT11使用的是单总线通信,即发送数据与接收数据都在一根数据线上,通过规定的时序进行控制。

从左向右看,时序一开始,主机信号就保持着高电平,所以引脚初始化完毕时,及时给引脚输出高电平。因为 模块的数据线要求空闲时,要保持高电平状态。(其实模块上已经接了上拉电阻,使数据线一直保持高电平)

根据时序图可以知道,主机(开发板)发送一次开始信号,待主机开始信号结束后,DHT11 发送响应信号,送出 温湿度数据,并触发一次数据采集给下一次数据读取作准备。因此完成一次数据读取需要进行起始信号、响应信号、数据接收、结束信号。

读取数据步骤:

起始信号:主机(开发板)接入数据线的I/O输出低电平,且低电平保持时间不能小于 18ms

wKgZO2nM3niATCDsAAA6A9a_1i4964.png

 DHT11_GPIO_Mode_IN();//数据线转为输入模式
//如果前面没有错误,则模块会发出低电平的应答信号,
//所以直接等待DHT11拉高,80us
    timeout = 5000;
while( (! DATA_GPIO_IN ) && ( timeout >0 ) )timeout--;         //等待高电平的到来
//模块当前处于拉高准备输出数据,所以直接等待DHT11拉低,80us
    timeout = 5000;//设置超时时间
//DATA GPIO_IN=0时,while条件不成立退出while 说明接收到响应信号
//当timeout<=0时,while条件不成立退出while 说明超时
while( DATA_GPIO_IN && ( timeout >0 ) )timeout-- ;         //等待低电平的到来

2.响应信号:等待模块的响应信号到来。将数据线改为输入模式,如果接入到低电平,说明接收到模块的响应。

3.数据传输:主机接收模块发送的40位数据,其中,位数据 ‘0’ 表示54us的低电平,27us的高电平;位数据 ‘1’ 表示54us的低电平,74us的高电平。两个格式的分辨主要是高电平的输出时长不同。

wKgZPGnM3oiAc1MeAAC6DSwlcnU086.png

#define CHECK_TIME 28//超过0值的高电平时间
for(i=0;i< 40;i++)//循环接收40位数据
    {
        timeout = 5000;
//等待低电平过去
while( ( !DATA_GPIO_IN ) && (timeout > 0) ) timeout--; //54us
        delay_uus(CHECK_TIME);//等待超过位数据0值的高电平时间
if ( DATA_GPIO_IN )//如果还是高电平,说明是1值
        {
val=(val< <1)+1;
        }
else//如果是低电平,说明是0值
        {
val< <=1;
        }
        timeout = 5000;
//如果当前还是高电平,等待高电平过去,准备接收下一位数据
while( DATA_GPIO_IN && (timeout > 0) ) timeout-- ;
    }

4.结束信号:模块的数据线输出 40 位数据后,是以低电平结束,它会继续输出低电平 54 微秒后转为输入状态,主机需要转为输出状态,输出高电平释放总线。

wKgZPGnLxoaADbn8AAAEOILGPWw177.jpg

DHT11_GPIO_Mode_OUT();//转为输出模式
DATA_GPIO_OUT(1);//主机释放总线

数据接收完成,但是这40位数据要如何转化为温湿度数据?并如何保证传输的数据没有错误?

DHT11模块一次完整的数据传输为40bit,高位先出。数据格式:

8bit湿度整数数据 + 8bit湿度小数数据 + 8bi温度整数数据 + 8bit温度小数数据 + 8bit校验和

注意

湿度小数部分数据一直为0。

数据传送正确时,校验和数据等于“8bit湿度整数数据+8bit湿度小数数据 +8bi温度整数数据+8bit温度小数数据”所得结果的末8位。举几个例子。

示例一:接收的40位数据分别为:

0011 0101 0000 0000 0001 1000 0000 0100 0101 0001
湿度高8位 湿度低8位 温度高8位 温度低8位 校验位

校验和为 0011 0101 + 0000 0000 + 0001 1000 + 0000 0100 = 0101 0001,与接收的数据一致

湿度为 0011 0101 + 0000 0000 = 35 + 0 = 35%RH

温度为 0001 1000 0000 0100 = 24 + 4 = 24.4℃

示例二:接收的40位数据分别为:

0011 0101 0000 0000 0001 1000 0000 0100 0100 1001
湿度高8位 湿度低8位 温度高8位 温度低8位 校验位

校验和为 0011 0101 + 0000 0000 + 0001 1000 + 0000 0100 = 0101 0001,与接收的数据不一致 计算的数据为0101 0001,接收的数据为0100 1001,两者不一致说明数据不准确,丢弃这次数据,重新接收。

以下为数据处理的实现代码:

//val为接收到的40位数据。
//           湿高8     + 湿低8      + 温高8     + 温低8
    verify_num = (val>>32) + (val>>24) + (val>>16) + (val>>8);
//计算的校验和 与 接收的校验和 的差为0说明一致,不为0说明不一致
//(val&0xff)是因为val的大小为64位,我们只需要val的最后8位校验和
    verify_num = verify_num - (val&0xff);
//进行校验
if( verify_num )//如果不为0,说明校验失败
    {
//      校验错误
return0;
    }
else//校验成功
    {
//数据处理
        humidity = (val>>32)&0xff;         //湿度前8位(小数点前数据)
        small_point = (val>>24)&0x00ff;    //湿度后8位(小数点后数据)
        small_point = small_point * 0.1;   //换算为小数点
        humidity = humidity + small_point; //小数前+小数后
        printf("湿度:%.2frn",humidity);

        temperature = (val>>16)&0x0000ff;  //温度前8位(小数点前数据)
        small_point = (val>>8)&0x000000ff; //温度后8位(小数点后数据)
        small_point = small_point * 0.1;   //换算为小数点
        temperature = temperature + small_point;//小数前+小数后
        printf("温度:%.2frn",temperature);

returnval>>8; //返回未处理的数据
    }

3.2引脚选择

该模块有3个引脚,具体引脚连接见 表 各引脚连接。

wKgZO2nLxoaAL48wAAYS2_t1p48544.jpg

3.3、移植至工程

工程模板下载请查看入门手册百度链接

然后我们打开空白工程,新建两个文件dht11.c和dht11.h

wKgZO2nM3qOAQPhYABCW17MEJXE048.jpg

在文件dht11.c中,编写如下代码。

/*
 * Change Logs:
 * Date           Author       Notes
 * 2024-06-19     LCKFB-LP    first version
 */
#include "DHT11.h"
#include "stdio.h"
#include "cw32l012_gpio.h"
float temperature = 0;
float humidity = 0;
/**
 * @brief 配置Delay_us
 *
 */
void delay_us(unsigned long __us) 
{
    uint32_t ticks;
    uint32_t told, tnow, tcnt = 0;  // 初始计数为0(关键修正)
    // 计算需要的时钟数:__us * (96MHz / 1e6) = __us * 48
    ticks = __us * 96;  // 96MHz下,1us对应96个时钟周期
    // 获取当前SysTick计数值(向下计数)
    told = SysTick->VAL;
    while (1)
    {
        tnow = SysTick->VAL;
        if (tnow != told)  // 计数值变化时才更新计数
        {
            // 向下计数:若当前值 < 之前值,说明未溢出,直接累加差值
            if (tnow < told)
                tcnt += told - tnow;
            // 若当前值 > 之前值,说明已溢出,累加(LOAD - tnow + told)
            else
                tcnt += (SysTick->LOAD - tnow) + told;
            told = tnow;
            // 计数达到目标 ticks 时退出
            if (tcnt >= ticks)
                break;
        }
    }
}
// ms延时基于us延时实现
void delay_ms(unsigned long ms) 
{
    while (ms--)
        delay_us(1000);  // 每次延时1000us(1ms),避免单次大数值溢出
}
/******************************************************************
 * 函 数 名 称:DHT11_GPIO_Init
 * 函 数 说 明:DHT11温湿度传感器初始化
 * 函 数 形 参:无
 * 函 数 返 回:无
 * 作       者:LC
 * 备       注:无
******************************************************************/
void DHT11_GPIO_Init(void)
{
    GPIO_InitTypeDef        GPIO_InitStruct; // GPIO初始化结构体
    __SYSCTRL_DHT11_GPIO_ENABLE();        // 使能GPIO时钟
    GPIO_InitStruct.Pins = GPIO_DHT11;                                // GPIO引脚
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;                // 推挽输出
    GPIO_Init(PORT_DHT11, &GPIO_InitStruct);                // 初始化
    DATA_GPIO_OUT(1);
}
/******************************************************************
 * 函 数 名 称:DHT11_GPIO_Mode_OUT
 * 函 数 说 明:配置DHT11的数据引脚为输出模式
 * 函 数 形 参:无
 * 函 数 返 回:无
 * 作       者:LC
 * 备       注:无
******************************************************************/
void DHT11_GPIO_Mode_OUT(void)
{
    GPIO_InitTypeDef        GPIO_InitStruct; // GPIO初始化结构体
    GPIO_InitStruct.Pins = GPIO_DHT11;                                // GPIO引脚
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;                // 推挽输出
    GPIO_Init(PORT_DHT11, &GPIO_InitStruct);                // 初始化
}
/******************************************************************
 * 函 数 名 称:DHT11_GPIO_Mode_IN
 * 函 数 说 明:配置DHT11的数据引脚为输入模式
 * 函 数 形 参:无
 * 函 数 返 回:无
 * 作       者:LC
 * 备       注:无
******************************************************************/
void DHT11_GPIO_Mode_IN(void)
{
    GPIO_InitTypeDef        GPIO_InitStruct; // GPIO初始化结构体
    GPIO_InitStruct.Pins = GPIO_DHT11;                                // GPIO引脚
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT_PULLUP;        // 上拉输入
    GPIO_Init(PORT_DHT11, &GPIO_InitStruct);                // 初始化
}
/******************************************************************
 * 函 数 名 称:DHT11_Read_Data
 * 函 数 说 明:根据时序读取温湿度数据
 * 函 数 形 参:无
 * 函 数 返 回:0=数据校验失败  其他=温湿度未处理的数据
 * 作       者:LC
 * 备       注:无
******************************************************************/
unsigned int DHT11_Read_Data(void)
{
    int i;
    long long val=0;
    int timeout=0;
    float small_point=0;
    unsigned char verify_num = 0;//验证值
    DATA_GPIO_OUT(0);//数据线输出低电平
    delay_ms(19);  //起始信号保持时间19ms
    DATA_GPIO_OUT(1);//主机释放总线
    delay_us( 20 );//拉高等待
    DHT11_GPIO_Mode_IN();//数据线转为输入模式
    //如果前面没有错误,则模块会发出低电平的应答信号,所以直接等待DHT11拉高,80us
    timeout = 5000;
    while( (! DATA_GPIO_IN ) && ( timeout >0 ) )timeout--;         //等待高电平的到来
    //模块当前处于拉高准备输出数据,所以直接等待DHT11拉低,80us
    timeout = 5000;//设置超时时间
    //DATA_GPIO_IN=0时,while条件不成立退出while 说明接收到响应信号
    //当timeout<=0时,while条件不成立退出while  说明超时
    while( DATA_GPIO_IN && ( timeout >0 ) )timeout-- ;         //等待低电平的到来
    #define CHECK_TIME 28 //实测发现超过0值的高电平时间
    for(i=0;i< 40;i++)//循环接收40位数据
    {
        timeout = 5000;
        while( ( !DATA_GPIO_IN ) && (timeout > 0) ) timeout--;         //等待低电平过去
        delay_us(CHECK_TIME);//超过0值的高电平时间
        if ( DATA_GPIO_IN )//如果还是高电平,说明是1值
        {
            val=(val< <1)+1;
        }
        else //如果是低电平,说明是0值
        {
            val< <=1;
        }
        timeout = 5000;
        while( DATA_GPIO_IN && (timeout > 0) ) timeout-- ;         //如果还是高电平
    }
    DHT11_GPIO_Mode_OUT();//转为输出模式
    DATA_GPIO_OUT(1);//主机释放总线
    //val为接收到的40位数据
    //           湿高8     + 湿低8      + 温高8     + 温低8
    verify_num = (val>>32) + (val>>24) + (val>>16) + (val>>8);
    //计算的校验和 与 接收的校验和 的差为0说明一致,不为0说明不一致
    verify_num = verify_num - (val&0xff);
    //进行校验
    if( verify_num  )
    {
//      校验错误
        return 0;
    }
    else //校验成功
    {
        //数据处理
        humidity = (val>>32)&0xff;//湿度前8位(小数点前数据)
        small_point = (val>>24)&0x00ff;//湿度后8位(小数点后数据)
        small_point = small_point * 0.1;//换算为小数点
        humidity = humidity + small_point;//小数前+小数后
//        printf("湿度:%.2frn",humidity);
        temperature = (val>>16)&0x0000ff;//温度前8位(小数点前数据)
        small_point = (val>>8)&0x000000ff;//温度后8位(小数点后数据)
        small_point = small_point * 0.1;//换算为小数点
        temperature = temperature + small_point;//小数前+小数后
//        printf("温度:%.2frn",temperature);
        return val>>8; //返回未处理的数据
    }
}
/******************************************************************
 * 函 数 名 称:Get_temperature
 * 函 数 说 明:获取温度数据
 * 函 数 形 参:无
 * 函 数 返 回:温度值
 * 作       者:LC
 * 备       注:使用前必须先调用 DHT11_Read_Data 读取有数据
******************************************************************/
float Get_temperature(void)
{
    return temperature;
}
/******************************************************************
 * 函 数 名 称:Get_humidity
 * 函 数 说 明:获取湿度数据
 * 函 数 形 参:无
 * 函 数 返 回:湿度值
 * 作       者:LC
 * 备       注:使用前必须先调用 DHT11_Read_Data 读取有数据
******************************************************************/
float Get_humidity(void)
{
    return humidity;
}

官方文档的delay_us和delay_ms来自于#include “board.h”,我根据cw32l012芯片的最大时钟96Mhz,进行了部分修改匹配并且移植到dht11.c文件中,方便使用,同时需要在main.c文件中进行初始化系统嘀嗒计时器,如下图所示,参考文档:八、滴答定时器 |

https://wiki.lckfb.com/zh-hans/dwx-cw32f030c8t6/module/sensor/dht11.html

在文件dht11.h中,编写如下代码。

/*
 * Change Logs:
 * Date           Author       Notes
 * 2024-06-19     LCKFB-LP    first version
 */
#ifndef _DHT11_H_
#define _DHT11_H_
//#include "main.h"
#include "cw32l012_sysctrl.h"
 /**************引脚修改此处****************/
#define __SYSCTRL_DHT11_GPIO_ENABLE()       __SYSCTRL_GPIOB_CLK_ENABLE()
#define PORT_DHT11                    CW_GPIOB
#define GPIO_DHT11                    GPIO_PIN_2
//设置DHT11输出高或低电平
#define DATA_GPIO_OUT(x)    GPIO_WritePin(PORT_DHT11, GPIO_DHT11, x ? GPIO_Pin_SET : GPIO_Pin_RESET)
//获取DHT11数据引脚高低电平状态
#define DATA_GPIO_IN        GPIO_ReadPin(PORT_DHT11, GPIO_DHT11)
extern float temperature;
extern float humidity;
void DHT11_GPIO_Init(void);//引脚初始化
unsigned int DHT11_Read_Data(void);//读取模块数据
float Get_temperature(void);//返回读取模块后的温度数据
float Get_humidity(void);//返回读取模块后的湿度数据
void delay_us(unsigned long __us);
void delay_ms(unsigned long ms); 
#endif

四、移植验证

在自己工程中的main主函数中,编写如下。

#include "../inc/main.h"
#include "cw32l012_systick.h"
#include "DHT11.h"
int32_t main(void)
{
    //配置RCC
    RCC_Configuration();
    //配置GPIO
    GPIO_Configuration();
    //配置UART
    UART_Configuration();

          DHT11_GPIO_Init();          //DHT11引脚初始化
    printf("rnCW32L012 UART Printf Examplern");
    while(1)
    {
                          //读取模块数据
        DHT11_Read_Data();
        //显示读取后的温度数据
        printf("temperature = %.2frn", Get_temperature() );
        //显示读取后的湿度数据
        printf("humidity = %.2frn", Get_humidity() );
        delay_ms(500);


    }
}

编译无误后进行烧录,此时连接串口ttl,按照下图接线方式

wKgZPGnM3rSAIudPAAZnmrUwDN4236.png

模块移植成功案例代码:

链接:https://pan.baidu.com/s/1eg89GhwPStLlf3A-nEIHtQ?pwd=CW32

提取码:CW32

为您推荐

当前非电脑浏览器正常宽度,请使用移动设备访问本站!