STM32F103 HAL库 DHT11温度传感器实验
写在前面
绝对不坑人,保证有用,亲测有效。
作为刚刚接触STM32的小白,我深知找不到非常有效的参考资料的痛苦,同时也避免以后忘记相关的方法,因此写下这篇博客,作为学习笔记。
元器件准备:由于我使用的是野火的STM32F103mini开发板,因此没有购买单独的温度传感器模块,使用了自带的温度传感器。
对于DHT11的资料介绍就不在这里赘述,我相信你肯定已经找到了许多相关资料,我们废话少说,直接上代码。
第一部分:CUBEMX 配置
KEY1,KEY2 用于产生按键中断
LED1,LED2 是开发板上的LED,用于按键中断实验,也用于发送提示信息
Beep 是蜂鸣器的引脚,蜂鸣器用于在温度异常的时候发出报警信号
实验需要使用串口,用户指令通过串口发送到单片机,单片机的信息通过串口发送到上位机,串口的具体配置如下:
这部分保持默认配置即可。
需要使能串口接收中断。用于处理用户指令。
DHT11引脚的配置。
其他的引脚只需要配置为默认的推挽输出即可。
时钟以及工程配置不在赘述。
接下来是代码部分。
/*
/*DHT11.C文件*/
# include "DHT11.h"
# include "tim.h"
/*根据DHT11的工作特性,需要改变引脚的工作状态*/
// 输入模式
void DHT11_IO_IN(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.Pin = GPIO_PIN_0;
GPIO_InitStructure.Mode = GPIO_MODE_INPUT;
HAL_GPIO_Init(GPIOC,&GPIO_InitStructure);
}
// 输出模式
void DHT11_IO_OUT(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.Pin = GPIO_PIN_0;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOC,&GPIO_InitStructure);
}
/* 触发DHT11工作状态的信号,涉及到IO口电平的拉高以及拉低,
一次在.h文件中进行宏定义,方便程序移植*/
void DHT11_Start (void)
{
DHT11_IO_OUT(); //设置为输出
DHT11_DQ_OUT_LOW; //拉低DQ
HAL_Delay(20); //拉低至少18ms
DHT11_DQ_OUT_HIGH; //DQ=1
Delay_us (30); //主机拉高20~40us
}
/*为了确保DHT11处于工作状态,这里需要定义一个函数,用于检测DHT11是否处于工作状态*/
/*检测原理:DHT11收到触发信号后可以正常工作,将会产生80us的低电平,80us 的高电平*/
/*通过检测电平状态,确定是否处于工作状态,返回值为0 表示正常工作,返回值为1,表示工作异常*/
uint8_t DHT11_Check (void)
{
//避免程序被卡死在某一个循环
uint8_t Retry = 0 ;
//设置为输入模式
DHT11_IO_IN ();
//需要读取电平,为了方便程序移植,使用宏定义
//通过循环等待低电平结束,如果超过等待期限,则跳出循环
while (DHT11_IN == GPIO_PIN_RESET && Retry < 100)
{
Retry ++ ;
Delay_us (1);
};
//通过判断Retry 的值来判断是否有DHT11
//如果大于100,直接结束函数,返回工作异常
if(Retry >= 100)
{
return 1;
}
//如果小于100,说明正常工作,Retry = 0 ;进入高电平检测阶段
else Retry = 0 ;
/*低电平检测完成之后进入高电平检测*/
while (DHT11_IN == GPIO_PIN_SET && Retry < 100)
{
Retry ++ ;
Delay_us (1);
}
//通过判断Retry 的值来判断是否有DHT11
//如果大于100,直接结束函数,返回工作异常
if(Retry >= 100) return 1;
//如果一切正常,默认返回值为0
return 0 ;
}
/*完成以上阶段之后,进入数据读取阶段,数据读取的方式为:读取一个字->读取一个字节->读取完整数据*/
/*读取一个字节*/
uint8_t DHT11_ReadBit(void)
{
uint8_t Retry = 0;
/*数据读取都是以50us 的低电平开始,然后进入高电平,如果高电平持续时间为26-28us则为数据“0”*/
/*如果高电平为70us 则为数据“1”*/
//等待高电平结束.这里的高电平为响应信号的80us 高电平
while(DHT11_IN == GPIO_PIN_SET && Retry<100)
{
Retry++;
Delay_us(1);
};
Retry = 0 ;
//等待低电平结束(数据传输之前的50us低电平)
while (DHT11_IN == GPIO_PIN_RESET && Retry < 100)
{
Retry ++ ;
Delay_us (1);
};
//延时等待
Delay_us (40);
//延时结束仍然为高电平
if(DHT11_IN == GPIO_PIN_SET ) return 1 ;
//延时结束已经变成低电平
else return 0 ;
}
/*读取一字节:循环调用八次读取一字的函数,将读取到的数据放在一字节的存储空间中*/
uint8_t DHT11_ReadByte(void)
{
uint8_t i, Data = 0 ;
//循环八次
for(i = 0 ;i < 8 ;i ++)
{
Data <<=1 ;
Data |= DHT11_ReadBit() ;
}
return Data ;
}
//从DHT11读取一次数据
//temp:温度值(范围:0~50°)
//humi:湿度值(范围:20%~90%)
//返回值:0,正常;1,读取失败
uint8_t DHT11_ReadData(uint16_t *temp,uint16_t *humi)
{
//定义五位数组,用于存放读取的数据
uint8_t buf[5];
uint8_t i;
//发送开始信号
DHT11_Start ();
if(DHT11_Check()==0)
{
for(i=0;i<5;i++)//读取40位数据
{
buf[i]=DHT11_ReadByte();
}
if((buf[0]+buf[1]+buf[2]+buf[3])==buf[4])
{
*humi=(buf[0]<<8) + buf[1];
*temp=(buf[2]<<8) + buf[3];
}
}
//如果检测到DHT11工作异常,返回异常信号
else return 1;
return 0;
}
//初始化DHT11的IO口 DQ 同时检测DHT11的存在
//返回1:不存在
//返回0:存在
uint8_t DHT11_Init(void)
{
DHT11_Start ();
return DHT11_Check();
}
DHT11.h文件
# ifndef __DHT11_H
# define __DHT11_H
#include "main.h"
/*IO电平高低状态宏定义*/
//拉高
# define DHT11_DQ_OUT_HIGH HAL_GPIO_WritePin(GPIOC,GPIO_PIN_0,GPIO_PIN_SET)
//拉低
# define DHT11_DQ_OUT_LOW HAL_GPIO_WritePin(GPIOC,GPIO_PIN_0,GPIO_PIN_RESET)
/*电平读取宏定义*/
# define DHT11_IN HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_0)
// 输入模式
void DHT11_IO_IN(void);
// 输出模式
void DHT11_IO_OUT(void);
/* 触发DHT11工作状态的信号,涉及到IO口电平的拉高以及拉低,
一次在.h文件中进行宏定义,方便程序移植*/
void DHT11_Start (void);
/*延时函数申明,在tim.c文件中定义,如果此处不申明,会有警告*/
void Delay_us (uint32_t us);
/*一字数据读取*/
uint8_t DHT11_ReadBit(void);
/*读取一字节数据*/
uint8_t DHT11_ReadByte(void);
/*读取一次完整数据*/
uint8_t DHT11_ReadData(uint16_t *temp,uint16_t *humi);
/*DHT11状态检测函数*/
uint8_t DHT11_Check (void);
//初始化DHT11的IO口 DQ 同时检测DHT11的存在
uint8_t DHT11_Init(void);
# endif
main.c
/*用户变量定义,需要放在最开始部分*/
/*定义接收缓冲区*/
uint8_t Rx_Data = 0 ;
/*定义温度全局变量*/
uint16_t temperature;
/*定义湿度全局变量*/
uint16_t humidity;
/*用于发送起始信号*/
uint8_t START = 0 ;
/*用于程序的循环结构变量*/
uint8_t i = 0 ;
/* 用户宏定义*/
# define Beep_Start HAL_GPIO_WritePin (Beep_GPIO_Port ,Beep_Pin ,GPIO_PIN_SET );
# define Beep_Stop HAL_GPIO_WritePin (Beep_GPIO_Port ,Beep_Pin ,GPIO_PIN_RESET );
/*用户代码*/
/* USER CODE BEGIN 2 */
/*打印开启系统提示,提高系统的人机交互体验*/
printf ("**************************************\r\n");
printf ("Hello! Welcome to use this system!\r\n");
printf ("**************************************\r\n");
/*用于检测DHT11是否正常工作,初始化失败不断提醒用户 DHT11 Checked failed!!!*/
while(DHT11_Init())
{
printf("DHT11 Checked failed!!!\r\n");
HAL_Delay(1000);
};
/*初始化成功*/
printf ("**************************************\r\n");
printf ("DHT11 Checked Sucess!!!\r\n");
printf ("**************************************\r\n");
/*用户需要开启温度湿度异常警告时,输入指令*/
printf ("If you need to send an alert message, please enter 0xA0 !\r\n ");
printf ("**************************************\r\n");
/*关闭异常警告*/
printf ("If you need to stop alert message, please enter 0xA1 !\r\n ");
printf ("**************************************\r\n");
/*温湿度输出指令*/
printf ("If you need to view the current indoor temperature, please enter 0xA2 !\r\n ");
printf ("**************************************\r\n");
/*按键中断指令*/
printf ("If you need to turn on LED1, please press KEY 1! \r\n");
printf ("**************************************\r\n");
printf ("If you need to toggle LED2, please press KEY 2! \r\n");
printf ("**************************************\r\n");
/*使能串口通信接收中断*/
HAL_UART_Receive_IT (&huart1 ,&Rx_Data ,1);
/* USER CODE END 2 */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
/*用于等待用户输入指令*/
while(!START );
START = 0 ;
/*五次打印出实时温度与湿度*/
while(i<5)
{
//调用数据读取函数,读取完整数据
DHT11_ReadData (&temperature,&humidity);
//打印出温度
printf("DHT11 Temperature = %d.%d degree\r\n",temperature>>8,temperature&0xff);
//打印出湿度
printf("DHT11 Humidity = %d.%d%%\r\n",humidity>>8,humidity&0xff);
//翻转电平,提示打印完成
HAL_GPIO_TogglePin(LED1_GPIO_Port ,LED1_Pin );
HAL_Delay(1000);
HAL_GPIO_TogglePin(LED1_GPIO_Port ,LED1_Pin );
/*循环体变量自加*/
i++;
}
/*提示用户温度打印完毕*/
printf ("**************************************\r\n");
printf ("Indoor temperature and humidity have been printed! \r\n");
printf ("**************************************\r\n");
}
/* USER CODE END 3 */
}
/* USER CODE BEGIN 4 */
/*按键中断回调函数*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
uint8_t i ;
/*判断中断来源*/
if(GPIO_Pin == KEY1_Pin )
{
/*中断处理任务,翻转电平*/
HAL_GPIO_TogglePin (LED1_GPIO_Port ,LED1_Pin );
__HAL_GPIO_EXTI_CLEAR_IT (KEY1_Pin );
}
else if (GPIO_Pin == KEY2_Pin )
{
for(i = 0;i<10;i++)
{
HAL_GPIO_TogglePin (LED2_GPIO_Port ,LED2_Pin );
HAL_Delay (500);
__HAL_GPIO_EXTI_CLEAR_IT (KEY1_Pin );
}
}
}
/*串口接收与发送中断回调函数*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
/*判断具体的接受中断来源*/
if(huart == &huart1 )
{
/*判断接收指令*/
if(Rx_Data == 0xA0)
{
/*具体的中断处理任务*/
Beep_Start ;
printf ("**************************************\r\n");
printf ("Beep Open !\r\n");
printf ("**************************************\r\n");
/*处理完毕,再次开启接收中断*/
HAL_UART_Receive_IT (&huart1 ,&Rx_Data ,1);
}
else if (Rx_Data == 0xA1)
{
/*具体的中断处理任务*/
Beep_Stop ;
printf ("**************************************\r\n");
printf ("Beep Off !\r\n");
printf ("**************************************\r\n");
/*处理完毕,再次开启接收中断*/
HAL_UART_Receive_IT (&huart1 ,&Rx_Data ,1);
}
else if (Rx_Data == 0xA2)
{
/*具体的中断处理任务*/
START = 1 ;//输入指令0xA2,开启温湿度采集
/*处理完毕,再次开启接收中断*/
HAL_UART_Receive_IT (&huart1 ,&Rx_Data ,1);
}
else
{
/*输入错误提示*/
printf ("**************************************\r\n");
printf ("Input Error!\r\n");
printf ("**************************************\r\n");
/*处理完毕,再次开启接收中断*/
HAL_UART_Receive_IT (&huart1 ,&Rx_Data ,1);
}
}
}
/* USER CODE END 4 */
*/
效果图如上。
只要建立相对应的文件,把代码复制到正确的位置,保证能够运行,亲测有效。
如果有不到位的地方还请大佬批评指正。