嵌入式的学习之DHT11

补充: 红色 部分为重要部分;
          绿色 为解析或者补充,如果红色前有绿色,代表文章后有 相关知识
          蓝色 为代码,如果蓝色内部有红色如上;
          紫色 为自我理解与认知不一定正确。
DHT11的简介:
    DHT11与单片机之间采用简单的 单总线进行通信,仅仅需要 (2) 一个I/O口。传感器内部湿度和温度数据 (3) 40Bit的数据一次性传给单片机,数据采用 校验和的方式进行校验,有效的保证数据传输的准确性。DHT11的功耗很低,5V电源电压下, 工作平均最大电流为0.5mA;
    //补充:如果以后自己进行实验的话,拿到一个元器件首先看 工作电压
DHT11的简介:
    工作电压 范围:3.3V~5.5V
    工作电流:平均0.5mA
    输出:单总线数字信号
    测量范围:湿度 5~95%如何,温度-20~60
    精度:湿度+-5%,温度+-2。
DHT11的四个引脚(这里的内容自行寻找规格书,自己查找就可以了)
    1.VDD 供电3.3V~5.5V DC
    2.DATA 串行数据,单总线
    3.NC 空脚
    4.GND 接地,电源负极
DHT11单总线:
    (1)单总线:
单总线即只有一根数据线,系统中的数据交换,控制均由单总线完成。设备(主机或者从机)通过一个 开漏路三态端口连至该数据线,以允许设备在不发送数据时能够释放总线,而让其他设备使用总线。
    单总线通常要求外接一个约4.7kΩ的上拉电阻,这样,在总线闲置时,其状态为高电平。
    由于它们是主从结极,只有 主机呼叫从机时从机才能应答,因此主机访问器件都必须 严格遵守单总线序列( 如下: 第一步:初始化。 第二步:ROM命令.跟随需要交换的数据。 第三步:功能命令,跟随需要交换的数据。 每次访问单总线器件.都必须遵守这个命令序列.如果序列出现混乱,则单总线器件不会响应主机。 ,如果出现序列混乱,器件将不能响应主机。
    (2)单总线传送数据位定义
    DATA用于微处理器与DHT11之间的通讯和同步,采用单总线数据格式, 一次传送40位数据,高位先出
    数据格式:8bit湿度整数数据+8bit湿度小数数据+8bit温度整数数据+8bit温度小数数据+校验位 (如图就是全部加起来判断与前几项和是否相等)
    注意:湿度小数部分为0;
    
    (3)主机与从机,主机就是MCU,从机就是DHT11

        GPIO:PG11                            (Output)
        GPIO output level:High             (默认高电平状态)
        GPIO mode:Output Open Drain(开漏状态)(推挽无法控制)
DHT11实验一:
    初始化LED0,LED1,以及串口1(本次暂时不用),DHT11的IO端口
    读DHT11数据
    如果数据验证正确,点亮LED1,熄灭LED0,并输出当前温度
    如果数据验证不正确,点亮LED0,熄灭LED1
    .h文件固定开头
    #ifndef  __DHT11_H                                                                          //他的作用是防止重复定义
    #define __DHT11_H
    #endif
void DHT11_Init() { 初始化内容·}                                                            //代表DHT11初始化,一般情况下 Init()的意思就是初始化。
 初始化内容:
     GPIO_InitTypeDef GPIO_InitStruct = {0}                                           //  初始化GPIO(通用输入输出)端口参数的结构体类型
  __HAL_RCC_GPIOG_CLK_ENABLE();                                                     //时钟使能端
    HAL_GPIO_WritePin(GPIOG, GPIO_PIN_11, GPIO_PIN_SET);            //释放总线后的状态
     GPIO_InitStruct.Pin                //什么按键
     GPIO_InitStruct.Mode            //什么模式(推挽开漏)
     GPIO_InitStruct.Pull               //(是否有上拉电阻或下拉电阻)
     GPIO_InitStruct.Speed            //传输速度
    //如果不会写按键、模式、速度等等,选中  GPIO_InitTypeDef,F12跳转到定义的位置,找到如1. @ref GPIO_pins_define2. @ref GPIO_mode_define,选中 GPIO_pins_define,ctrl+F,点击Find Next就可以找到定义的位置了。
    HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);        //调用初始化函数(那个组如A,B,C……, 那个组的按键,模式,电路,速度
接下来的步骤需要依靠手册规格书进行
    第一步:
    第二步:
void DHT11_Start()
{
    HAL_GPIO_WritePin(GPIOG,GPIO_PIN_11,GPIO_PIN_RESET);
    delay_ms(20);                                                                        //这里没有使用系统自带的HAL_delay  想要使用要将自制的delay添加进来(下面有)
                                                                                                  //20ms是因为单总线的信号特性
    HAL_GPIO_WritePin(GPIOG,GPIO_PIN_11,GPIO_PIN_SET);
}
uint8_t DHT11_wait()
{
    uint16_t err=0;
    uint16_t timeout=0;
    while(HAL_GPIO_ReadPin(GPIOG,GPIO_PIN_11)==GPIO_PIN_SET&&timeout<=35)
    {
        delay_us(1);                      // (3)这里延迟有一点问题,会大致延迟2us在最高等级优化的情况下,具体差异在delay_us代码中,延迟时间越小问题                                                        较大,但是延迟越高,问题较小例100us约为102us左右。
        timeout++;    
    }
    if(timeout>35)
    {
        return err=1;
    }
}
    第三步
uint8_t DHT11_wait()
{
    uint16_t err=0;
    uint16_t timeout=0;
    while(HAL_GPIO_ReadPin(GPIOG,GPIO_PIN_11)==GPIO_PIN_SET&&timeout<=35)
    {
        delay_us(1);
        timeout++;    
    }
    if(timeout>35)
    {
        return err=1;
    }
    timeout=0;
//这里开始
    while(HAL_GPIO_ReadPin(GPIOG,GPIO_PIN_11)==GPIO_PIN_RESET&&timeout<=88)
    {
        delay_us(1);
        timeout++;    
    }
    if(timeout>88||timeout<78)
    {
        return err=1;
    }
        while(HAL_GPIO_ReadPin(GPIOG,GPIO_PIN_11)==GPIO_PIN_SET&&timeout<=92)
    {
        delay_us(1);                     
        timeout++;    
    }
    if(timeout>92)
    {
        return err=1;
    }
    return err;
}
    第四步:
这里可以取个巧,延时40毫秒如果是高电平就是1低电平就是0;
//这里 DHT11.c完成后可以在 主函数 内部执行     连接亮红灯,不连接亮绿灯(无现象为错误)
//        DHT11_Start();
//        if(DHT11_wait())
//        {
//            HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5,GPIO_PIN_RESET);
//            HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_SET);
//        }
//        else
//        {
//            HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_RESET);
//            HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5,GPIO_PIN_SET);
//        }
//        HAL_Delay(1000);
//读比特:
uint8_t DHT11_Read_Bit(uint8_t *bit)
{
    uint8_t err=0;
    uint16_t timeout=0;
    while(HAL_GPIO_ReadPin(GPIOG,GPIO_PIN_11)==GPIO_PIN_SET&&timeout<=92)
    {
        delay_us(1);
        timeout +=2;
    }
    if(timeout>92)
    {
        return err=1;
    }
    timeout=0;
    while(HAL_GPIO_ReadPin(GPIOG,GPIO_PIN_11)==GPIO_PIN_RESET&&timeout<=58)
    {
        delay_us(1);
        timeout +=2;
    }    
    if(timeout>58)
    {
        return err=1;
    }
    delay_us(40);
    if(HAL_GPIO_ReadPin(GPIOG,GPIO_PIN_11)==GPIO_PIN_RESET)
    {
    *bit=0;
    }
    else
    {
    *bit=1;    
    }
}
//读字节:
uint8_t DHT11_Read_Byte(uint8_t *byte)
{
    uint8_t err=0;
    uint8_t bit=0;
    uint8_t data=0;
    for(uint8_t i=0;i<8;i++)
    {
        err=DHT11_Read_Bit(&bit);
        if(err==1)
        {
            return err;
        }
         data|= bit << (7-i);
    }
    *byte=data;
    return err;
}
//读温度湿度
uint8_t DHT11_Read(uint8_t*shidu,float*wendu)
{
    uint8_t err=0;
    uint8_t buf[5]={0};
    DHT11_Start();
    err=DHT11_wait();
    if(err==1)
    {
        return err;
    }
    for(uint8_t i=0;i<5;i++)
    {
            err=DHT11_Read_Byte(&buf[i]);
        if(err==1)
        {
        return err;
        }
    }
    if(buf[0]+buf[1]+buf[2]+buf[3]==buf[4])
    {
        *shidu=buf[0];
        if(buf[3]&(1<<7))
        {
        buf[3]&=~(1<<7);
        *wendu=buf[2]+buf[3]/100.0;
        *wendu=0-*wendu;    
        }
        else
        {
        *wendu=buf[2]+buf[3]/100.0;
        }
        err=0;
    }
    else
    {
     err=1;
    }
        return err;
    
}
DEBUG的使用
    点击DEBUG点击后会跳到主函数如果没有跳到,点击魔法棒点击debug,点击Run to main()就会自动跳转到主函数了,如果没有点就会跳到startup.stm32f103.
    只有灰色的地方是可以打断点的
    黄色的箭头的意思是程序运行到这里
    {}->程序运行到下一行
    {->}进入程序内部
    Peripherals  寄存器PG11可以在GPIOG组看到相关状态
    IDR:输入寄存器
    ODR:输出寄存器   //写入什么状态ODR就是什么状态
     HAL_GPIO_WritePin(GPIOG,GPIO_PIN_11,GPIO_PIN_RESET)//写入状态
    HAL_GPIO_ReadPin(GPIOG,GPIO_PIN_11);//读取状态
    Call Stack + Locals //堆栈信息
    如果想要看全局变量在堆栈信息中:选中全局变量右键 Add'temp'(变量)watch
    如果向将10进制变为16进制就右键  Hexadecimal Display
添加.c文件.h文件
    第一步:找到工程文件,如果找不到打开mian.c文件,右键main标题 Open Containing Folder,然后新建文件夹将.c/.h文件(模块)放入
    第二步:点击三色块,点击新建(分组)起一个名,点击 Add Files(加的是.c文件就是告诉编译器.c文件需要编译)向上返回找到新建的文件夹,找到.c文件,点击OK;
    第三步添加头文件.h,点击魔法棒点击C++,点击···,点击新建点击···向上返回文件夹,选择文件夹点击OK;
    f(频率一秒钟跳转多少次)=72*10^6HZ   m=10^6;   T=1/F
    72MHZ就是1秒跳转72*10^6次       T=1/F   1MHZ就是一微秒就是10-^6秒
解析:
(1) 一个I/O:其实就是输入输出端口
   I/O 接口可以分为如下几种类型:
a.按数据传送方式,分为并行接口和串行接口,并行接口一次传输一个字节或一个字的全部位,串行接口一次传送一位。这里指的是外设和接口一侧的传输方式,主机和接口一侧的数据总是并行传输的。
b.按主机访问 I/O 接口的控制方式,分为程序查询接口、中断接口和DMA接口等。
c.按功能选择的灵活性,分为可编程接口和不可编程接口.
  (2) 40Bit:
bit(位,又名“比特”):bit的缩写是b,是计算机中的最小数据单位(属于二进制的范畴,其实就是0或者1) Byte(字节):Byte的缩写是B,是计算机文件大小的基本计算单位。比如一个字符就是1Byte,如果是汉字,则是2Byte。
#include "delay.h"


static uint16_t  g_fac_us = 0;      /* us延时倍乘数 */


/**
 * @brief       初始化延迟函数
 * @param       sysclk: 系统时钟频率, 即CPU频率(HCLK) 单位MHz
 * @retval      无
 */
void delay_init(uint16_t sysclk)
{

    SysTick->CTRL = 0;                                          /* 清Systick状态,以便下一步重设,如果这里开了中断会关闭其中断 */
    HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK_DIV8);   /* SYSTICK使用内核时钟源8分频,因systick的计数器最大值只有2^24 */

    g_fac_us = sysclk / 8;                                      /* 不论是否使用OS,g_fac_us都需要使用,作为1us的基础时基 */

}


/**
 * @brief       延时nus
 * @param       nus: 要延时的us数.
 * @note        注意: nus的值,不要大于1864135us(最大值即2^24 / g_fac_us  @g_fac_us = 9)
 * @retval      无
 */
void delay_us(uint32_t nus)
{
    uint32_t temp;
    SysTick->LOAD = nus * g_fac_us; /* 时间加载 */
    SysTick->VAL = 0x00;            /* 清空计数器 */
    SysTick->CTRL |= 1 << 0 ;       /* 开始倒数 */

    do
    {
        temp = SysTick->CTRL;
    } while ((temp & 0x01) && !(temp & (1 << 16))); /* CTRL.ENABLE位必须为1, 并等待时间到达 */

    SysTick->CTRL &= ~(1 << 0) ;    /* 关闭SYSTICK */
    SysTick->VAL = 0X00;            /* 清空计数器 */
}

/**
 * @brief       延时nms
 * @param       nms: 要延时的ms数 (0< nms <= 65535)
 * @retval      无
 */
void delay_ms(uint16_t nms)
{
    uint32_t repeat = nms / 1000;   /*  这里用1000,是考虑到可能有超频应用,
                                     *  比如128Mhz的时候, delay_us最大只能延时1048576us左右了
                                     */
    uint32_t remain = nms % 1000;

    while (repeat)
    {
        delay_us(1000 * 1000);      /* 利用delay_us 实现 1000ms 延时 */
        repeat--;
    }

    if (remain)
    {
        delay_us(remain * 1000);    /* 利用delay_us, 把尾数延时(remain ms)给做了 */
    }
}

/**
  * @brief HAL库内部函数用到的延时
           HAL库的延时默认用Systick,如果我们没有开Systick的中断会导致调用这个延时后无法退出
  * @param Delay 要延时的毫秒数
  * @retval None
  */
void HAL_Delay(uint32_t Delay)
{
     delay_ms(Delay);
}
#include "delay.h"


static uint16_t  g_fac_us = 0;      /* us延时倍乘数 */


/**
 * @brief       初始化延迟函数
 * @param       sysclk: 系统时钟频率, 即CPU频率(HCLK) 单位MHz
 * @retval      无
 */
void delay_init(uint16_t sysclk)
{

    SysTick->CTRL = 0;                                          /* 清Systick状态,以便下一步重设,如果这里开了中断会关闭其中断 */
    HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK_DIV8);   /* SYSTICK使用内核时钟源8分频,因systick的计数器最大值只有2^24 */

    g_fac_us = sysclk / 8;                                      /* 不论是否使用OS,g_fac_us都需要使用,作为1us的基础时基 */

}


/**
 * @brief       延时nus
 * @param       nus: 要延时的us数.
 * @note        注意: nus的值,不要大于1864135us(最大值即2^24 / g_fac_us  @g_fac_us = 9)
 * @retval      无
 */
void delay_us(uint32_t nus)
{
    uint32_t temp;
    SysTick->LOAD = nus * g_fac_us; /* 时间加载 */
    SysTick->VAL = 0x00;            /* 清空计数器 */
    SysTick->CTRL |= 1 << 0 ;       /* 开始倒数 */

    do
    {
        temp = SysTick->CTRL;
    } while ((temp & 0x01) && !(temp & (1 << 16))); /* CTRL.ENABLE位必须为1, 并等待时间到达 */

    SysTick->CTRL &= ~(1 << 0) ;    /* 关闭SYSTICK */
    SysTick->VAL = 0X00;            /* 清空计数器 */
}

/**
 * @brief       延时nms
 * @param       nms: 要延时的ms数 (0< nms <= 65535)
 * @retval      无
 */
void delay_ms(uint16_t nms)
{
    uint32_t repeat = nms / 1000;   /*  这里用1000,是考虑到可能有超频应用,
                                     *  比如128Mhz的时候, delay_us最大只能延时1048576us左右了
                                     */
    uint32_t remain = nms % 1000;

    while (repeat)
    {
        delay_us(1000 * 1000);      /* 利用delay_us 实现 1000ms 延时 */
        repeat--;
    }

    if (remain)
    {
        delay_us(remain * 1000);    /* 利用delay_us, 把尾数延时(remain ms)给做了 */
    }
}

/**
  * @brief HAL库内部函数用到的延时
           HAL库的延时默认用Systick,如果我们没有开Systick的中断会导致调用这个延时后无法退出
  * @param Delay 要延时的毫秒数
  * @retval None
  */
void HAL_Delay(uint32_t Delay)
{
     delay_ms(Delay);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值