点击查看代码
/**
******************************************************************************
* @file main.c
* @author
* @version
* @date 2024/07/12
* @brief 实现利用DHT11传感器学习分析时序图,并设计完整的通信协议的相关接口
DHT11 -- PG9
******************************************************************************
**/
#include "stm32f4xx.h" //必须包含
#include <stdio.h>
#include <stdbool.h>
//前台程序就是中断服务程序,该程序是不需要手动调用的,当中断触发之后CPU会自动跳转过来执行该函数
void USART1_IRQHandler(void)
{
uint8_t data;
//判断中断是否发生
if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
{
//从USART1中接收一个字节
data = USART_ReceiveData(USART1); //一次只能接收一个字节
//把接收到的数据转发出去
USART_SendData(USART1,data);
}
}
//延时微秒 注意:Systick是24bit的递减计数器 约等于798915us,所以参数不可以超过这个值
void delay_us(u32 nus)
{
SysTick->CTRL = 0; // 向控制状态寄存器中写入0,目的关闭系统嘀嗒定时器
SysTick->LOAD = (nus * 21) - 1;// 指的是计数次数,定时时间 = 计数次数 * 计数周期
SysTick->VAL = 0; // 清除当前数值寄存器的值
SysTick->CTRL = 1; // 开启了定时器,并且定时器的时钟源选择了21MHZ--> 计数周期 = 1/21us
while ((SysTick->CTRL & 0x00010000)==0);//等待延时时间到达
SysTick->CTRL = 0; // 向控制状态寄存器中写入0,目的关闭系统嘀嗒定时器
}
//延时毫秒 注意:Systick是24bit的递减计数器 约等于798ms,所以参数不可以超过这个值
void delay_ms(u32 nms)
{
SysTick->CTRL = 0; // 向控制状态寄存器中写入0,目的关闭系统嘀嗒定时器
SysTick->LOAD = (nms * 21*1000) - 1;// 指的是计数次数,定时时间 = 计数次数 * 计数周期
SysTick->VAL = 0; // 清除当前数值寄存器的值
SysTick->CTRL = 1; // 开启了定时器,并且定时器的时钟源选择了21MHZ--> 计数周期 = 1/21us
while ((SysTick->CTRL & 0x00010000)==0);//等待延时时间到达
SysTick->CTRL = 0; // 向控制状态寄存器中写入0,目的关闭系统嘀嗒定时器
}
void USART1_Config(u32 baud)
{
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
//打开了GPIO端口时钟 PA9和PA10
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
//打开USART1的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
//选择GPIO引脚的复用功能
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9 , GPIO_AF_USART1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
//配置GPIO引脚 注意:复用模式
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//配置串口参数+对串口初始化
USART_InitStructure.USART_BaudRate = baud; //波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //数据位
USART_InitStructure.USART_StopBits = USART_StopBits_1; //停止位
USART_InitStructure.USART_Parity = USART_Parity_No; //无校验
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无流控
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
USART_Init(USART1, &USART_InitStructure);
//配置NVIC参数 + 对NVIC初始化
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
//选择USART1的中断源,接收到数据则触发中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
//打开串口
USART_Cmd(USART1, ENABLE);
}
//DHT11引脚输出配置
void DHT11_PinOutputModeConfig(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_Init(GPIOG, &GPIO_InitStructure);
//引脚空闲高电平
GPIO_SetBits(GPIOG,GPIO_Pin_9);
}
//DHT11引脚输入配置
void DHT11_PinInputModeConfig(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_Init(GPIOG, &GPIO_InitStructure);
}
//DHT11的初始化
void DHT11_Config(void)
{
DHT11_PinOutputModeConfig();
}
//向DHT11发送开始信号
void DHT11_SendStart(void)
{
//1.配置引脚为输出模式
DHT11_PinOutputModeConfig();
//2.把引脚电平拉低并持续20ms
GPIO_ResetBits(GPIOG,GPIO_Pin_9);
delay_ms(20);
//3.把引脚电平拉高并持续30us
GPIO_SetBits(GPIOG,GPIO_Pin_9);
delay_us(30);
}
//判断DHT11是否响应
bool DHT11_IsACK(void)
{
uint32_t cnt = 0; //作为计数器
//1.配置引脚为输入模式
DHT11_PinInputModeConfig();
//2.判断PG9引脚是否检测到低电平 为了提高程序可靠性,所以人为添加超时机制,超时时间假设为100us
while( GPIO_ReadInputDataBit(GPIOG,GPIO_Pin_9) == 1 && cnt < 100)
{
delay_us(1);
cnt++;
}
if(cnt >= 100)
return false;
cnt = 0;
//3.判断PG9引脚检测的低电平是否持续80us
while( GPIO_ReadInputDataBit(GPIOG,GPIO_Pin_9) == 0 && cnt < 100)
{
delay_us(1);
cnt++;
}
if( cnt >= 100 )
return false;
else
return true;
}
//判断DHT11发送的bit的值,并存储到一个字节的bit0位置中
uint8_t DHT11_ReadBit(void)
{
//1.等待低电平出现
while( GPIO_ReadInputDataBit(GPIOG,GPIO_Pin_9) == 1 );
//1.等待低电平结束
while( GPIO_ReadInputDataBit(GPIOG,GPIO_Pin_9) == 0 );
//2.此时高电平出现,则延时 28us < n < 70us
delay_us(40);
//3.延时结束之后,判断PG9引脚的电平状态,如果电平还是高电平,则说明是bit = 1
if( GPIO_ReadInputDataBit(GPIOG,GPIO_Pin_9) == 1 )
return 1;
else
return 0;
}
//DHT11读取1字节 DHT11提供的40bit是以MSB
uint8_t DHT11_ReadByte(void)
{
int i = 0;
//1.定义变量并初始化
uint8_t data = 0; // 0000 0000
//2.循环8次,接收一个字节
for(i = 0;i < 8;i++)
{
data <<= 1;
data |= DHT11_ReadBit();
}
return data;
}
//读取DHT11温湿度传感器的数据
bool DHT11_ReadData(uint8_t buf[5])
{
int i = 0;
//1.MCU发送开始信号
DHT11_SendStart();
//2.MCU等待DHT进行响应
if( true == DHT11_IsACK() )
{
//3.循环读取40bit
for(i=0;i<5;i++)
{
buf[i] = DHT11_ReadByte();
}
//4.对数据进行校验
if( buf[4] == buf[0] + buf[1] + buf[2] + buf[3] )
{
return true;
}
else
return false; //说明读取数据失败,原因是校验未通过
}
else
{
return false; //说明读取数据失败,原因是DHT未响应
}
}
//利用串口发送一个字符串
void USART1_SendString(const char *str)
{
while(*str)
{
USART_SendData(USART1,*str++);
while( USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET );
}
}
int main()
{
uint8_t dhtbuf[5] = {0};
uint8_t ubuf[128] = {0};
//1.硬件的初始化
USART1_Config(9600);
DHT11_Config();
while(1)
{
DHT11_ReadData(dhtbuf); //获取了一次温湿度数据
sprintf((char *)ubuf,"temp = %d ℃, humi = %d %%RH\r\n",dhtbuf[2],dhtbuf[0]);
USART1_SendString((char *)ubuf);
delay_ms(500);
}
}