搜索
bottom↓
回复: 0

《STM32MP1 M4裸机CubeIDE开发指南 V1.5.2》第二十七章

[复制链接]

出0入234汤圆

发表于 2022-8-10 10:03:34 | 显示全部楼层 |阅读模式
本帖最后由 正点原子 于 2022-8-10 10:03 编辑

1)实验平台:正点原子STM32MP157开发板
2) 章节摘自【正点原子】STM32MP1 M4裸机CubeIDE开发指南 V1.5.2
3)购买链接:https://detail.tmall.com/item.htm?id=631745687288
4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/arm-linux/zdyzmp157.html1.0.0 文档
5)正点原子官方B站:https://space.bilibili.com/394620890
6)STM32MP157技术交流QQ群:703341432 lQLPJxaFi2zaB4UWWrDAMgIsFEW2pwLb3abnwDMA_90_22.png
lQDPJxaFi2nfFizMjM0CbLCPlxn_FVheIQLb3aGrwFQA_620_140.jpg

lQLPJxaFi2nfFhLMkM0BXrDNvOUyeU_FPgLb3aGvQNIA_350_144.png
第二十七章  DHT11数字温湿度传感器实验

本章,我们将介绍数字温湿度传感器DHT11的使用,与前一章的DS18B20温度传感器相比,该传感器不但能测温度,还能测湿度。我们将学习如何获取DHT11传感器的温湿度数据,并把数据通过串口打印出来。
本章分为如下几个小节:
27.1、DHT11简介;
27.2、硬件设计;
27.3、软件设计;
27.4、编译和测试;
27.1 DHT11简介
27.1.1 DHT11简介

DHT11是一款温湿度一体化的数字传感器。该传感器包括一个电阻式测湿元件和一个NTC测温元件,并与一个高性能8位单片机相连接。通过单片机等微处理器简单的电路连接就能够实时的采集本地湿度和温度。DHT11与单片机之间能采用简单的单总线进行通信,仅仅需要一个I/O口。传感器内部湿度和温度数据共有40 bit的数据一次性传给单片机,数据采用校验和方式进行校验,有效的保证数据传输的准确性。DHT11功耗很低,5V电源电压下,工作平均最大电流0.5mA。
DHT11的技术参数如下:
        工作电压范围:3.3V ~ 5.5V
        工作电流:平均0.5mA
        输出:单总线数字信号
        测量范围:湿度20~90%RH,温度0~50℃
        精度:湿度±5%,温度±2℃
        分辨率:湿度1%,温度1℃
DHT11的管脚排列如图43.1.1所示:
image002.jpg
图27.1.1.1 DHT11管脚排列图

        DHT11广泛应用于暖通空调、除湿器、农业、冷链仓储、测试及检测设备、消费品、
汽车、自动控制、数据记录器、气象站、家电、湿度调节器、医疗等相关温湿度检测控制领域。
27.1.2 DHT11工作时序简介
1. 单总线结构
        DHT11器件采用单总线通信,只有一根数据线,系统中的数据交换、控制均由单总线完成。单总线接一个约4.7kΩ的上拉电阻,当总线空闲时,总线状态为高电平。关于单总线我们在前面的DS18B20章节已经讲解过,后面编写程序时注意的是,要配置总线占用的IO口为开漏输出模式。
2. 数据结构
        单总线上的设备属于主从结构,只有主机呼叫从机时,从机才能应答,因此主机访问器件都必须严格遵循单总线序列,如果出现序列混乱,器件将不响应主机。虽然DHT11与DS18B20类似,都是单总线访问,但是DHT11的访问,相对DS18B20来说简单很多。下面我们先来看看DHT11的数据结构。
        单总线上一次传送5 byte(40 bit)的数据,且高位先发送,数据分小数部分和整数部分,DHT11的数据格式为:
        8bit湿度整数数据+8bit湿度小数数据+8bit温度整数数据+8bit温度小数数据+8bit校验位。
        其中校验和数据为前面四个字节相加。传感器数据输出的是未编码的二进制数据,数据(湿度、温度、整数、小数)之间应该分开处理。
        (1)温度是正数的情况
        例如,某次从DHT11读到的数据如下图所示:
  byte4                    byte3                  byte2                    byte1                  byte0
00110101      00000000      00011000      00000100      01010001
湿度高8位    湿度低8位    温度高8位    温度低8位    校验位
  整数          小数          整数          小数         校验位
        根据以上的数据进行计算:
        1)检查数据接收
        00110101+00000000+00011000+00000100=01010001
        计算结果和校验位相等,接收数据正确,如果计算得到的值和校验位不一致,那么本次接收的数据不正确,放弃,重新接收数据。
        2)计算湿度和温度的值
湿度:
         00110101(整数)=35H=53%RH
         00000000(小数)=00H=0.0%RH
         最后湿度=>53%RH+0.0%RH=53.0%RH
温度:
         00011000(整数)=18H=24℃
         00000100 (小数)=04H=0.4℃
         最后温度=>24℃+0.4℃=24.4℃
        (2)温度是负数的情况
如果,当温度低于0℃时,温度数据的低8位的最高位置为1,计算时,该位可以视为0。如果温度是负数,校验位和湿度计算和上面的情况一样,这里说明一下温度计算,如-10.1℃表示为00001010 10000001,计算方法为:
0000 1010(整数)=0AH=10℃
00000001(小数)=01H=0.1℃
最后温度=>-(10℃+0.1℃)=-10.1℃
3. DHT11时序
        DHT11上电后(DHT11上电后要等待1S以越过不稳定状态,在此期间不能发送任何指令),待1s过后,可以测试环境温湿度数据,DHT11的DATA数据线由上拉电阻拉高一直保持高电平,此时DHT11的DATA引脚处于输入状态,时刻检测外部信号。下面我们看看DHT11的时序:
        1)复位信号和应答信号时序
        首先主机发送开始信号(也叫复位信号),即:拉低数据线,保持t1(至少18ms,最大不得超过30ms)时间,然后主机拉高数据线t2(20~40us)时间,然后主机读取DHT11的响应,正常的话,DHT11会拉低数据线,并保持t3(40~50us)时间来作为响应信号,然后DHT11拉高数据线,保持t4(40~50us)时间后,DHT11开始输出数据。
image004.jpg
图27.1.2.1 开始信号和应答信号

        2)DHT11输出数字‘0’时序
        DHT11每发送的1 bit 数据都以低电平开始,低电平延时时间约为12~14us,然后DHT11拉高总线,如果总线拉高26~28us,表示发送0,然后DHT11再将总线拉低12~14us,为下一次发送数据做准备。(注意,此时序图中,为按位发送。)
image006.jpg
图27.1.2.2 DHT11输出数字‘0’

        3)DHT11输出数字‘1’时序
        如果DHT11拉低总线12~14us后,再将总线拉高116~118us,表示发送数字1,然后DHT11再将总线拉低12~14us,为下一次发送数据做准备。(注意,此时序图中,为按位发送。)
image008.jpg
图27.1.2.3 DHT11输出数字‘1’

27.2 硬件设计
1. 例程功能
        把DHT11传感器插在预留的接口上,测试当前的环境温度和湿度,并将温度和湿度通过串口打印出来。实验中通过观察LED0闪烁提示程序在运行。预留的接口在开发板底板的位置如下:
image010.jpg
图27.2.1 开发板硬件示意图

        上图中的开发板预留接口的4个引脚从左到右依次为VCC、DQ、GND、GND,所以DHT11这样插入开发板(注意,不要插错!):
image012.jpg
图27.2.2硬件连接示意图

2. 硬件资源
        1)LED灯:LED0
        2)UART4
        3)DS18B20(接在PF2上)
07E35C98-8381-45e5-A847-999EED1D62C9.png
表27.2.1硬件资源

3. 原理图
        如下图,DS18B20接在PF2上,我们通过该IO口模拟单总线的时序来控制DS18B20:
image014.jpg
图27.2.3原理图部分

27.3 软件设计
        本实验配置好的实验工程已经放到了开发板光盘中,路径为:开发板光盘A-基础资料\1、程序源码\11、M4 CubeIDE裸机驱动例程\CubeIDE_project\ 20 DHT11。
27.3.1 新建和配置工程
        新建工程DHT11,然后配置LED0和UART4,再配置PF2为开漏输出、上拉、高速模式:
image016.jpg
图26.3.1.1 配置GPIO

        本节实验会用到微秒延时函数,需要将第二十三章实验的delay.h和delay.c文件。将上一章节实验的BSP文件夹拷贝到M4工程的Core/Src下,然后在BSP文件夹下新建dht11.c文件,在BSP/Include下新建dht11.h文件,最后的部分工程如下:
image018.jpg
图26.3.2 工程部分目录

27.3.2 添加用户代码
        关于LED0和UART4相关的代码请参考前面实验章节部分,DHT11驱动代码我们把它放在dht11.c和dht11.h文件中。首先我们先看一下dht11.c头文件里面的内容。
1. dht11.h文件代码
        dht11.h文件代码如下:
#ifndef __DHT11_H
#define __DHT11_H
#include"gpio.h"
/* DHT11 引脚 定义 */
#define DHT11_DQ_GPIO_PORT            GPIOF
#define DHT11_DQ_GPIO_PIN             GPIO_PIN_2
/* PF口时钟使能 */
#define DHT11_DQ_GPIO_CLK_ENABLE()    do{ __HAL_RCC_GPIOF_CLK_ENABLE(); }while(0)   

/* IO操作函数 */
#define DHT11_DQ_OUT(x)   do{ x ? \
        HAL_GPIO_WritePin(DHT11_DQ_GPIO_PORT, DHT11_DQ_GPIO_PIN, GPIO_PIN_SET) : \        HAL_GPIO_WritePin(DHT11_DQ_GPIO_PORT, DHT11_DQ_GPIO_PIN, GPIO_PIN_RESET); \                                                        }while(0)   /* 数据端口输出 */
/* 数据端口输入 */
#define DHT11_DQ_IN       HAL_GPIO_ReadPin(DHT11_DQ_GPIO_PORT, DHT11_DQ_GPIO_PIN)  

uint8_t dht11_init(void);               /* 初始化DHT11 */
uint8_t dht11_check(void);              /* 检测是否存在DHT11 */
uint8_t dht11_read_data(uint8_t *temp,uint8_t *humi);   /* 读取温湿度 */

#endif
以上主要是对DHT11的相关引脚、时钟使能和IO操作函数进行宏定义,方便程序中调用。
2. dht11.c文件代码
        根据前面对DHT11的时序分析,我们按照时序部分进行程序设计,首先是复位DHT11函数,如下:
(1)复位DHT11
/**
* @brief       复位DHT11
* @param       无
* @retval      无
*/
static void dht11_reset(void)
{
    DHT11_DQ_OUT(0);            /* 拉低DQ */
    delay_ms(20);               /* 拉低至少18ms */
    DHT11_DQ_OUT(1);            /* DQ=1 */
    delay_us(30);               /* 主机拉高20~40us */
}
(2)等待DHT11应答
/**
* @brief       等待DHT11的回应
* @param       无
* @retval      0, DHT11正常
*                      1, DHT11异常/不存在
*/
uint8_t dht11_check(void)
{
    uint8_t retry = 0;
    uint8_t rval = 0;
    while (DHT11_DQ_IN && retry < 100)      /* DHT11会拉低40~80us */
    {
        retry++;
        delay_us(1);
    }
    if (retry >= 100)                               /* 超时,DHT11异常 */
    {
        rval = 1;
    }
    else
    {
        retry = 0;
        while (!DHT11_DQ_IN && retry < 100) /* DHT11拉低后会再次拉高40~80us */
        {
            retry++;
            delay_us(1);
        }
        if (retry >= 100) rval = 1;                /* 超时,DHT11异常 */
    }  
    return rval;
}
        应答信号也是按照前面的时序图进行编写的,首先DHT11将总线拉低,所以程序先等待总线变为低电平,这里在测试引脚电平的时候,设置最大的检测时间是100us,如果超过100us内电平没有变化,则认为DHT11异常。如果DHT11拉低总线后,再释放总线,则总线为高电平,所以,在测试总线拉低以后,再检测总线是否被拉高。
(3)从DHT11读取一个位
        和DS18B20不一样,DHT11不需要写的过程,直接可以读取温湿度数据:
1   /**
2    * @brief       从DHT11读取一个位
3    * @param       无
4    * @retval      读取到的位值: 0 / 1
5    */
6   uint8_t dht11_read_bit(void)
7   {
8       uint8_t retry = 0;
9       while (DHT11_DQ_IN && retry < 100)  /* 等待变为低电平 */
10      {
11          retry++;
12          delay_us(1);
13      }
14      retry = 0;
15      while (!DHT11_DQ_IN && retry < 100) /* 等待变高电平 */
16      {
17          retry++;
18          delay_us(1);
19      }
20      delay_us(40);                               /* 等待40us */
21      if (DHT11_DQ_IN)                            /* 根据引脚状态返回 bit */
22      {
23          return 1;
24      }
25      else
26      {
27          return 0;
28      }
29  }
        DHT11输出数字0或者1都是先以低电平开始的,所以第9~13行在等待DHT11变为低电平,同样,测试的最大时间为100us。DHT11通过拉高总线发送数字0或1,第15~19行在等待总线拉高,总线拉高后,延时40us后检查DHT11的IO的状态,如果此时引脚为高电平,则认为从DHT11读取到1,反之则从DHT11读取到0。
(4)从DHT11读取一个字节
        读取一个字节需要对读取一个位循环8次,如下:
/**
* @brief       从DHT11读取一个字节
* @param       无
* @retval      读到的数据
*/
static uint8_t dht11_read_byte(void)
{
    uint8_t i, data = 0;

    for (i = 0; i < 8; i++)          /* 循环读取8位数据 */
    {
        data <<= 1;                         /* 高位数据先输出, 先左移一位 */
        data |= dht11_read_bit();           /* 读取1bit数据 */
    }
    return data;
}
(5)从DHT11读取一次数据
        DHT11的数据组成我们前面已经分析过了,40位(5个字节)数据中,高位先发送,我们设置5和Buffer用于存储这5个字节的数据,所以接收到的数据中,Buffer4是检验位,Buffer3和Buffer2分别是温度的小数和整数部分,而Buffer1和Buffer0分别是湿度的小数和整数部分。        我们先获取到这5个字节的数据,然后先通过校验位检查接收到的数据是否正确,如果正确的话,通过指针的方式指向温度和湿度的整数位。
/**
* @brief       从DHT11读取一次数据
* @param       temp: 温度值(范围:0~50°)
* @param       humi: 湿度值(范围:20%~90%)
* @retval      0, 正常.
*              1, 失败
*/
uint8_t dht11_read_data(uint8_t *temp, uint8_t *humi)
{
    uint8_t buf[5];
    uint8_t i;
    dht11_reset();
    if (dht11_check() == 0)
    {
        for (i = 0; i < 5; i++)         /* 读取40位数据 */
        {
            buf
= dht11_read_byte();/* 读到的值存在buf中 */
        }
        /* 通过校验位检查读取到的数据是否正确 */
        if ((buf[0] + buf[1] + buf[2] + buf[3]) == buf[4])
        {
            *humi = buf[0];         /* 湿度整数和小数分别在buf[0]和buf[1]中 */
            *temp = buf[2];         /* 温度整数和小数分别在buf[2]和buf[3]中 */
        }
    }
    else                                    /* DHT11没有应答 */
    {
        return 1;
    }
    return 0;
}
(6)初始化DHT11的IO口
        最后,不要忘了要初始化IO口才可以使用IO口,如下是DHT11的IO口初始化函数,配置IO口为开漏输出、上拉、高速模式,单总线中一定要配置总线为开漏输出,这点我们在前面部分已经分析过。
/**
* @brief       初始化DHT11的IO口 DQ 同时检测DHT11的存在
* @param       无
* @retval      0, 正常
*              1, 不存在/不正常
*/
uint8_t dht11_init(void)
{
    GPIO_InitTypeDef gpio_init_struct;
    DHT11_DQ_GPIO_CLK_ENABLE();                  /* 开启DQ引脚时钟 */
    gpio_init_struct.Pin = DHT11_DQ_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_OD;                      /* 开漏输出 */
    gpio_init_struct.Pull = GPIO_PULLUP;                        /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;  /* 高速 */
        /* 初始化DHT11_DQ引脚 */
    HAL_GPIO_Init(DHT11_DQ_GPIO_PORT, &gpio_init_struct);
    /* DHT11_DQ引脚模式设置,开漏输出,上拉, 这样就不用再设置IO方向了, 开漏输出的时候(=1), 也可以读取外部信号的高低电平 */

    dht11_reset();                     /* 复位DHT11 */      
    return dht11_check();             /* 等待DHT11的回应 */      
}
3. main.c文件代码
        main.c文件的部分代码如下,我们需要在标红的字体之间手动添加代码:
1   #include "main.h"
2   #include "usart.h"
3   #include "gpio.h"
4   /* USER CODE BEGIN Includes */
5   #include "./BSP/Include/led.h"
6   #include "./BSP/Include/delay.h"
7   #include "./BSP/Include/dht11.h"
8   /* USER CODE END Includes */
9
10  void SystemClock_Config(void);
11
12  int main(void)
13  {
14    HAL_Init();
15    if(IS_ENGINEERING_BOOT_MODE())
16    {
17      SystemClock_Config();
18    }
19    MX_GPIO_Init();
20    MX_UART4_Init();
21    /* USER CODE BEGIN 2 */
22    HAL_UART_Receive_IT(&huart4,&RxBuffer,1); /* 以中断方式接收函数 */
23    led_init();                                                /* 初始化LED */
24    delay_init(209);                                   /* 延时初始化延时函数 */
25    uint8_t t = 0;
26    uint8_t temperature;
27    uint8_t humidity;
28    /* USER CODE END 2 */
29    while (1)
30    {
31      /* USER CODE BEGIN 3 */
32      while (dht11_init())                       /* DHT11初始化函数 */
33      {
34        printf("DHT11 Error!\r\n");             /* 如果DHT11不在,打印信息 */
35        delay_ms(1000);                           /* 延时1s */
36      }
37      while (1)
38      {
39        t++;
40        if (t == 20)
41        {
42          t = 0;
43          dht11_read_data(&temperature, &humidity); /* 读DHT11 */
44          /* 打印温度值和湿度值 */
45          printf("DHT11 Temp=%d\r, Humi=%d%%\r\n", temperature, humidity);
46          LED0_TOGGLE();                                 /* LED0闪烁 */
47        }
48        delay_ms(10);                                    /* 延时10ms*/
49      }
50    }
51    /* USER CODE END 3 */
52  }
27.4 编译和测试
        开发板上电,进入仿真运行后结果如下,串口打印此时测试的环境温度为24℃,湿度为63%,且LED0在闪烁。
image020.jpg
图27.4.1 运行结果

回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-19 09:04

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表