STM32F1驱动AM2302(DHT22)温湿度传感器

       AM2302数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。它应用专用的数字模块采集技术和温湿度传感技术,确保产品具有极高的可靠性与卓越的长期稳定性。传感器包括一个电容式感湿元件和一个NTC测温元件,并与一个高性能8位单片机相连接。因此该产品具有品质卓越、超快响应、抗干扰能力强、性价比极高等优点。每个AM2302传感器都在极为精确的湿度校验室中进行校准。校准系数以程序的形式储存在OTP内存中,传感器内部在检测信号的处理过程中要调用这些校准系数。单线制串行接口,使系统集成变得简易快捷。超小的体积、极低的功耗,信号传输距离可达20米以上,使其成为各类应用甚至最为苛刻的应用场合的最佳选则。产品为 4 针单排引脚封装。连接方便,特殊封装形式可根据用户需求而提供。

        先看AM2302时序图:

001.png


单总线通信特殊说明: 

1.典型应用电路中建议连接线长度短于30米时用5.1K上拉电阻(上拉电阻要根据实际的波形状态调整),大于30米时根据实际情况降低上拉电阻的阻值。 

2.使用3.3V电压供电时连接线长度不得大于30cm。否则线路压降会导致传感器供电不足,造成测量偏差。 

3.读取传感器最小间隔时间为2S;读取间隔时间小于2S,可能导致温湿度不准或通信不成功等情况。 

4.每次读出的温湿度数值是上一次测量的结果,欲获取实时数据,需连续读取两次, 建议连续多次读取传感器,且每次读取传感器间隔大于2秒即可获得准确的数据。

看到这里相信对AM2302有了一定了解了。下面开始贴出程序,共同驱动AM2302吧。


#include "bsp_am2302.h"
#include "stm32f1xx_hal.h"
#include "stm32f1xx_hal_gpio.h"
#include "stm32f1xx_hal_tim.h"
extern int timer2_count;
unsigned char  check_buf;
static GPIO_InitTypeDef  GPIO_InitStruct;
//AM2302引脚初使化
void AM2302_PIN_Init(void)
{
__HAL_RCC_GPIOB_CLK_ENABLE();
GPIO_InitStruct.Pin = AM2302_PIN;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(AM2302_PORT, &GPIO_InitStruct);
}
//这里设置了一个调试引脚,可以抓波形看读取数据的点对不对,非常有用
void Test_GPIO_Init(void)
{
__HAL_RCC_GPIOB_CLK_ENABLE();
GPIO_InitStruct.Pin = TEST_PIN;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(TEST_PORT, &GPIO_InitStruct);
}
//将AM2302对应引脚设为输出状态
void AM2302_Pin_Config_Output()
{
__HAL_RCC_GPIOB_CLK_ENABLE();
GPIO_InitStruct.Pin = AM2302_PIN;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(AM2302_PORT, &GPIO_InitStruct);
}
//将AM2302对应引脚设为输入状态
void AM2302_Pin_Config_Input(void)
{
__HAL_RCC_GPIOB_CLK_ENABLE();
GPIO_InitStruct.Pin = AM2302_PIN;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(AM2302_PORT, &GPIO_InitStruct);
}
void AM2302_Pin_Out_High(void)
{
HAL_GPIO_WritePin(AM2302_PORT,AM2302_PIN,GPIO_PIN_SET);
}
void AM2302_Pin_Out_Low(void)
{
HAL_GPIO_WritePin(AM2302_PORT,AM2302_PIN,GPIO_PIN_RESET);
}
unsigned char AM2302_Read_Byte(void)
{
   uint8_t i, temp=0; 
             
     for(i=0;i<8;i++)      
     { 
       /*每bit以50us低电平标置开始,轮询直到从机发出 的50us 低电平 结束*/ 
        timer2_count=0; 
        while(AM2302_PIN_Read()==GPIO_PIN_RESET&&timer2_count<6);
  HAL_GPIO_WritePin(AM2302_PORT,GPIO_PIN_7,GPIO_PIN_RESET);//调试引脚输出,低电平表示AM2302输出的低电平完毕
         /*AM2302 以22~30us的高电平表示“0”,以68~75us高电平表示“1”, 
             通过检测50us后的电平即可区别这两个状态*/  
 timer2_count=0;
 while(timer2_count<5); 
       HAL_GPIO_WritePin(AM2302_PORT,GPIO_PIN_7,GPIO_PIN_SET); //调试引脚输出,高电平表示开始读数
           
          if(AM2302_PIN_Read()==GPIO_PIN_SET)//60us后仍为高电平表示数据“1”  
           {  
              /*轮询直到从机发出的剩余的 30us 高电平结束*/ 
 timer2_count=0; 
                 while(AM2302_PIN_Read()==GPIO_PIN_SET&&timer2_count<5);  
       
                 temp|=(uint8_t)(0x01<<(7-i));  //把第7-i位置1   
                   
           }  
           else  //60us后为低电平表示数据“0”  
           {                 
              temp&=(uint8_t)~(0x01<<(7-i)); //把第7-i位置0  
           }  
     }  
     return temp;  
}  
unsigned char AM2302_Read(AM2302_Data_TypeDef *AM2302_Data)
{
 AM2302_Buf_TypeDef AM2302_Buf;
 /*输出模式*/  
   AM2302_Pin_Config_Output();  
   /*主机拉低*/  
   AM2302_Pin_Out_Low();
   /*延时500us*/
   timer2_count=0;
   while(timer2_count<60);   
       
   /*总线拉高 主机延时40us*/  
   AM2302_Pin_Out_High();
   //主机设为输入 判断从机响应信号  
   AM2302_Pin_Config_Input();  
   
   timer2_count=0;
   while(timer2_count<6); 
       
 /*判断从机是否有低电平响应信号 如不响应则跳出,响应则向下运行*/
   if(AM2302_PIN_Read()==GPIO_PIN_RESET)   //T ! 
    {  
                   
      /*轮询直到从机发出 的80us 低电平 响应信号结束*/
timer2_count=0;
        while(AM2302_PIN_Read()==GPIO_PIN_RESET&&timer2_count<10);  
                
      /*轮询直到从机发出的 80us 高电平 标置信号结束*/ 
        timer2_count=0;
        while(AM2302_PIN_Read()==GPIO_PIN_SET&&timer2_count<10);  
       
      /*开始接收数据*/    
         AM2302_Buf.humi_high= AM2302_Read_Byte();  
                                       
         AM2302_Buf.humi_low= AM2302_Read_Byte();  
                       
         AM2302_Buf.temp_high= AM2302_Read_Byte();  
       
         AM2302_Buf.temp_low= AM2302_Read_Byte();  
                               
         AM2302_Buf.check_sum= AM2302_Read_Byte();  
 timer2_count=0;
       while(timer2_count<5);
         /*读取结束,引脚改为输出模式*/  
        AM2302_Pin_Config_Output();  
        /*主机拉高*/  
        AM2302_Pin_Out_High(); 
        /*检查读取的数据是否正确*/ 
       check_buf=(AM2302_Buf.humi_high + AM2302_Buf.humi_low + AM2302_Buf.temp_high+ AM2302_Buf.temp_low)&0xFF;
      if(AM2302_Buf.check_sum == check_buf) 
{
AM2302_Data->temp_dat=(AM2302_Buf.temp_high<<8)+AM2302_Buf.temp_low;
  AM2302_Data->humi_dat=(AM2302_Buf.humi_high<<8)+AM2302_Buf.humi_low;
return SUCCESS;  
}
          
        else  
          return ERROR;  
   }  
   else  
   {          
       return ERROR;  
   }  
          
}
//定时器回调函数,定时时间为10us的整数倍
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(timer2_count>1000)
  timer2_count=0;
else
timer2_count++;
}
TIM_HandleTypeDef    Tim2Handle;
//定时器设置,定时时间是10us的整数倍,对于STM32F103来说,10us是最小的定时,再小就不准了
void Timer2_10_us(int us)
{
  uint32_t uwPrescalerValue = 0;
  uwPrescalerValue = (uint32_t)(SystemCoreClock /1000000) - 1;
/* Set TIMx instance */
  Tim2Handle.Instance = TIM2;
  /* Initialize TIMx peripheral as follows:
       + Period = 10000 - 1
       + Prescaler = (SystemCoreClock/10000) - 1
       + ClockDivision = 0
       + Counter direction = Up
  */
  Tim2Handle.Init.Period            = us*10-1;    //计数器的最终值=Period+1
  Tim2Handle.Init.Prescaler         = uwPrescalerValue;   //定时器计算分频系数,CK_CNT=Fck_psc/(Prescaler+1)
  Tim2Handle.Init.ClockDivision     = 0;
  Tim2Handle.Init.CounterMode       = TIM_COUNTERMODE_UP;
  Tim2Handle.Init.RepetitionCounter = 0;
  if (HAL_TIM_Base_Init(&Tim2Handle) != HAL_OK)
  {
    while(1);
  }
  if (HAL_TIM_Base_Start_IT(&Tim2Handle) != HAL_OK)
  {
   while(1);
  }
  /*##-2- Configure the NVIC for TIMx ########################################*/
  /* Set the TIMx priority */
  HAL_NVIC_SetPriority(TIM2_IRQn, 3, 0);
  /* Enable the TIMx global Interrupt */
  HAL_NVIC_EnableIRQ(TIM2_IRQn);
  /*##-2- Start the TIM Base generation in interrupt mode ####################*/
  /* Start Channel1 */
}

单线调试时,需要注意几个地方:

1、最难的就是精确的延时,可以有两种方法:

1)用空循环,准确的计算比较难,我一般用示波器调;

2)定一个定时器,加上while循环,本文用的就是这种方法,STM32F103最小定10us比较准,再小就不准了,也耗CPU资源。


2、上拉电阻是否合适,特别淘宝上买的模块,一定要用示波器看波形,上拉太大高电平上不去,太小低电平不能到零,读出来会出错。


0 条评论

目前没有人发表评论

发表评论

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。