STM32实现的温湿度监测:第一部分

系列文章目录

一、基于stm32的温度检测案例(一)

文章目录

目录

系列文章目录

一、基于stm32的温度检测案例(一)

文章目录

前言

一、实验器材简介

1.1、基于STM32F042F6P6的最小单片机系统

1.2、串口通信工具CH340G(USB转TTL)​编辑

1.3、仿真器(ST-LINK V2)

二、检测STM32F042F6P6功能是否正常

2.1、程序验证

 2.2、上电验证

三、代码演示

3.1、时钟树

3.2、Systick配置(Cortex–M0)

3.3、GPIO配置

3.2.1、方法功能测试

3.3、串口调试

3.3.1、串口配置

四、DHT11温湿度检测模块

4.1、引脚与接口

4.2、内部结构

4.3、根据时序写代码

4.2.1、时钟、端口、引脚、高低电平的配置

4.2.2、通讯过程

前言

使用基于stm32f042F6P6的最小单片机系统通过单总线与DHT11温湿度检测传感器通信,实现对当前环境下的温湿度检测。使用串口通信将DHT11模块检测到的数据在PC上显示。

一、实验器材简介

1.1、基于STM32F042F6P6的最小单片机系统

1.2、串口通信工具CH340G(USB转TTL)

1.3、仿真器(ST-LINK V2)

二、检测STM32F042F6P6功能是否正常

2.1、程序验证

通过观看原理图发现板子上有两个灯,一个电源指示灯与一个接在PB1引脚上的led。编写代码驱动接在PB1上的 LED,检查LED是否正常工作从而推论硬件是否有问题(量产的板子一般"冒得问题”)。

1、二极管工作原理:从标有三角那一端(P),流向标一横那一端(N)。实物:从标有“+”号那一端流向标有“-”号那一端。正向导通(P->N),反向截止(N->p)。

2、拉电流”,是指驱动LED负载的电流方向为  从电路输出端流向LED负载.  单片机给管脚高电平(推),LED熄灭
3、灌电流”是指驱动LED负载的电流方向为    从LED负载流向电路输出端。   单片机给管脚低电平 (挽),LED点亮

4、推挽输出(英语:Push–pull output)是一种使用一对选择性地从相连负载灌电流或者拉电流的器件的电路。它常常使用一对参数相同的功率三极管或MOSFET管,以推挽方式存在于电路中。

5、补充:推挽电路使用两个参数相同的三极管或MOSFET,以推挽方式存在于电路中。电路工作时,两只对称的开关管每次只有一个导通,所以导通损耗小、效率高。输出既可以向负载灌电流,也可以从负载抽取电流。推拉式输出级既提高电路的负载能力,又提高开关速度。

6、I/o口默认输出为高电平状态。

 2.2、上电验证

用ST_LINK V2连接板子与电脑,通过电脑的USB口给板子供电,查看板子上的电源指示灯是否正常点亮。(电脑的usb口输出5v左右的电压,不可作为驱动高于5v的板子的电源,会烧电脑usb口甚至电脑主板)。

三、代码演示

3.1、时钟树

3.2、Systick配置(Cortex–M0)

#include "public.h"
	//    us延时倍乘数			
static st_u8  fac_us=0;	

	//    ms延时倍乘数
static st_u16 fac_ms=0;						
/*********************************************************************************
                                 初始化延迟函数
SYSCLK:系统时钟频率
*********************************************************************************/
void SysTick_Init(st_u8 SYSCLK)
{
	//   SYSTICK的时钟固定为AHB时钟的1/8     6M
	SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); 
	fac_us=SYSCLK/8;					
	fac_ms=(st_u16)fac_us*1000;		// 6000us  		   
}								    


		
/*********************************************************************************
                              延时nus
                              nus为要延时的us数.
*********************************************************************************/
void delay_us(st_u32 nus)
{		
	st_u32 temp;	    	 
	SysTick->LOAD=nus*fac_us; 					      //    时间加载	  		 
	SysTick->VAL=0x00;        					      //    清空计数器
	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;	//    开始倒数	  
	do
	{
		temp=SysTick->CTRL;
	}while((temp&0x01)&&!(temp&(1<<16)));		  //  等待时间到达   
	SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;	//  关闭计数器
	SysTick->VAL =0X00;      					        //  清空计数器	 
}


/*********************************************************************************
                   延时nms
                 鈔ms的范围SysTick->LOAD为24位寄存器,所以,最大延时为:nms<=0xffffff*8*1000/SYSCLK
                 SYSCLK单位为Hz,nms单位为ms
                     对72M条件下,nms<=1864 
                      对24M条件下,nms<=5592
*********************************************************************************/
void delay_ms(st_u16 nms)
{	 		  	  
	st_u32 temp;		   
	SysTick->LOAD=(st_u32)nms*fac_ms;				   //  时间加载(SysTick->LOAD为24bit)
	SysTick->VAL =0x00;							           //  清空计数器
	SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ;	 //  开始倒数  
	do
	{
		temp=SysTick->CTRL;
	}while((temp&0x01)&&!(temp&(1<<16)));		  //  等待时间到达   
	SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;	//  关闭计数器
	SysTick->VAL =0X00;       				      	//  清空计数器	  	    
} 


3.3、GPIO配置

/*********************************************GPIO配置部分*********************************************/
/*********************************************
                  GPIO输出封装
1、时钟 RCC_AHBPeriph_x
2、端口 GPIOx
3、引脚 GPIO_PIN
4、输出模式  GPIOMode
5、输出类型  GPIOOType
5、指定所选引脚的操作上拉/下拉  GPIOPuPd
*********************************************/
void GPIOInit_Dout(st_u32 RCC_AHBPeriph_x,GPIO_TypeDef* GPIOx,st_u32 GPIO_PIN,GPIOMode_TypeDef GPIOMode,
	GPIOOType_TypeDef GPIOOType,GPIOPuPd_TypeDef GPIOPuPd)
{
	 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_x, ENABLE);      //  使能端口时钟
	
	GPIO_InitTypeDef GPIO_InitStructure;
 
  /* Configure PC10 and PC11 in output pushpull mode */
  GPIO_InitStructure.GPIO_Pin = GPIO_PIN;              //  引脚
  GPIO_InitStructure.GPIO_Mode = GPIOMode;             //  输入/输出模式
  GPIO_InitStructure.GPIO_OType = GPIOOType;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_PuPd = GPIOPuPd;            	//  上拉 or 下拉
  GPIO_Init(GPIOx, &GPIO_InitStructure);
}

/*********************************************
                  GPIO输入封装
1、时钟  RCC_AHBPeriph_x
2、端口  GPIOx
3、引脚  GPIO_PIN
4、输入模式  GPIOMode
5、指定所选引脚的操作上拉/下拉  GPIOPuPd
*********************************************/
void GPIOInit_Din(st_u32 RCC_AHBPeriph_x,GPIO_TypeDef* GPIOx,st_u32 GPIO_PIN,GPIOMode_TypeDef GPIOMode,GPIOPuPd_TypeDef GPIOPuPd)
{
	GPIO_InitTypeDef GPIO_InitStructure;
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_x, ENABLE);     //  使能时钟
  /* Configure PC10 and PC11 in output pushpull mode */
  GPIO_InitStructure.GPIO_Pin = GPIO_PIN;              //  引脚
  GPIO_InitStructure.GPIO_Mode = GPIOMode;             //  输入/输出模式
  //GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_PuPd = GPIOPuPd;            	//  上拉 or 下拉
  GPIO_Init(GPIOx, &GPIO_InitStructure);
}

3.2.1、方法功能测试

通过一个LED的来验证,函数功能是否正常

//  LED初始化
void LED_Init(void)
{
	// 时钟  端口  引脚  输出模式   推挽输出类型  上拉/下拉
  GPIOInit_Dout(LED_RCC,LED_PORT,LED_PIN,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);
	GPIO_SetBits(LED_PORT,LED_PIN);    //  初始化后默认PB1输出高电平
}
//  测试LED  高低电频每隔500ms交替一次  低电平期间LED点亮  高电平期间LED熄灭
void LED_Test(void)
{
       delay_ms(500);
		LED_L;
		delay_ms(500);
		LED_H;
}

.h文件中定义

#ifndef _LED_H
#define _LED_H
/*********************************************
     时钟  端口  引脚宏定义
*********************************************/
#define LED_RCC   RCC_AHBPeriph_GPIOB
#define LED_PORT  GPIOB 
#define LED_PIN   GPIO_Pin_1

#define LED_H    GPIO_SetBits(LED_PORT,LED_PIN)    //  高电平
#define LED_L    GPIO_ResetBits(LED_PORT,LED_PIN)  //  低电平

extern void LED_Init(void);
extern void  LED_Test(void);
#endif 
 

主函数

#define ROOT
#include "public.h"

int main(void)
{
//  初始化systick
	SysTick_Init(48);
	LED_Init();
	//Uart2_Init1();
	while(1)
	{
    LED_Test();
		//Uart2_Test();
	}
}

void SystemInit(void)
{

}

3.3、串口调试

外接5电源到板子,串口调试工具与板子供地共GND,串口调试工具的RX、TX与板子的RX、TX交叉相接能正常收发数据。 

3.3.1、串口配置

库函数配置串口


/*********************************************  
中断优先级配置
cortex-m0 智能配置 0~3
********************************************/
void NVIC_Config(st_u8 IRQn,st_u8 IRQChannelPriority)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  NVIC_InitStructure.NVIC_IRQChannel = IRQn;
	
	NVIC_InitStructure.NVIC_IRQChannelPriority = IRQChannelPriority;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
}

/*********************************************串口配置*********************************************/
/**********
STM32共有两种串口通信接口,分别是:UART通用异步收发器,USART:通用同步异步收发器
单工:数据传输只支持数据在一个方向上传输   一个通道
半双工:允许数据在两个方向上传输,但是,在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信;  两个通道
全双工:允许数据同时在两个方向上传输,因此,全双工通信是两个单工通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力。  两个通道

同步通信与异步通信
同步通信指的是带有时钟同步信号,比如:SPI通信、IIC通信;
异步通信指的是不带时钟同步信号比如:UART(通用异步收发器),单总线
**********/
void USART_Config(USART_TypeDef* USARTx,st_u32 Baudrate)
{
	if(USARTx==USART1)
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
	else
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
	
	USART_InitTypeDef USART_InitStructure;
  USART_InitStructure.USART_BaudRate = 9600;
  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(USARTx, &USART_InitStructure);
	
  USART_ITConfig(USARTx, USART_IT_RXNE, ENABLE);
  USART_Cmd(USARTx, ENABLE);
  }

/*********************************************
	          串口2通信初始化1
	*********************************************/
void Uart2_Init1(void)
{
  NVIC_Config(USART2_IRQn,0x00);
	//  TX        时钟                端口   引脚      复用模式     推挽输出       上拉/下拉
	//  推(拉电流) 驱动负载的电流方向为  从电路输出端流向负载
	//  挽(灌电流) 驱动负载的电流方向为  从负载流向电路输出端 
	GPIOInit_Dout(RCC_AHBPeriph_GPIOA,GPIOA,GPIO_Pin_2|GPIO_Pin_3,GPIO_Mode_AF,GPIO_OType_PP ,GPIO_PuPd_NOPULL);
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource2,GPIO_AF_1);     // USART2_Tx
	GPIO_PinAFConfig(GPIOA,GPIO_PinSource3,GPIO_AF_1);     // USART2_Rx
	USART_Config(USART2,9600);
}

中断服务函数

#include "public.h"

//  加入以下代码,支持printf函数,而不需要选择use MicroLIB	
#if 1
//#pragma import(__use_no_semihosting) 
#pragma import(__use_no_semihosting_swi)

struct  __FILE {st_32 handle;};   //  标准库需要的支持函数

FILE  _stdout;

void sys_exit(st_32 x){             // 定义_sys_exit()以避免使用半主机模式 
   x=x;
}
//  发送数据
//  重定向 c 库函数 printf 到串口,重定向后可使用 printf 函数
int fputc(int ch,FILE*p)
{
	//#if 1
  USART_SendData(USART2,(st_u8)ch);
	// USART_FLAG_TXE  数据寄存器为空的标记
	while(USART_GetFlagStatus(USART2,USART_FLAG_TXE)==RESET);
	return ch;
	//#else
	//while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   
 // USART1->TDR = (st_u8) ch;      
	//return ch;
	//#endif

}

//  接收数据
//  重定向 c 库函数 scanf 到串口,重写向后可使用 scanf、getchar 等函数
 int fgetc(FILE *f)
 {
 // 等待串口输入数据 
 while (USART_GetFlagStatus(USART2, USART_IT_RXNE) == RESET);
 return (st_32)USART_ReceiveData(USART2);
 }
 
 //  发送字符
void Usart_SendByte( USART_TypeDef * pUSARTx,st_u8 ch)
{
 
USART_SendData(pUSARTx,ch);
 while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}

//  发送字符串
void Usart_SendString( USART_TypeDef * pUSARTx, char *str)
{
  st_u32 k=0;
  do {
   Usart_SendByte( pUSARTx, *(str + k));
   k++;
} while (*(str + k)!='\0');
 while (USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET) { /* 等待发送完成 */
   }
 }
#endif
 
 /*********************************************第一种方式*********************************************/
 #if USART2_RX_EN
  //USART2_RX_BUF[USART2_REC_LEN];
 // USART2_RX_STA=0;

 
 void USART2_IRQHandler(void)
 {
	INTX_DISABLE();  //  中断屏蔽
  st_u8 r;
	if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)  //接收中断
	{
		r =USART_ReceiveData(USART2);//(USART1->DR);	//读取接收到的数据
		if((USART2_RX_STA&0x8000)==0)//接收未完成
		{
			if(USART2_RX_STA&0x4000)//接收到了0x0d
			{
				if(r!=0x0a)USART2_RX_STA=0;//接收错误,重新开始
				else USART2_RX_STA|=0x8000;	//接收完成了 
			}
			else //还没收到0X0D
			{	
				if(r==0x0d)USART2_RX_STA|=0x4000;
				else
				{
					USART2_RX_BUF[USART2_RX_STA&0X3FFF]=r;
					USART2_RX_STA++;
					if(USART2_RX_STA>(USART2_REC_LEN-1))USART2_RX_STA=0;//接收数据错误,重新开始接收	  
				}		 
			}
		}   		 
	} 
	INTX_ENABLE();  //  放开中断屏蔽
 }
 #endif

 

四、DHT11温湿度检测模块

4.1、引脚与接口

  • VCC:DTH11的供电电压为 3-5.5V。传感器上电后,要等待 1s 以越过不稳定状态在此期间无需发送任何指令。
  • DATA:mcu与DHT11单总线通信引脚。一次通讯时间4ms左右,数据分小数部分和整数部分.
  • GND:接地。
  • DTH11工作流程:

  • MCU发送一次开始信号后,DHT11从低功耗模式转换到高速模式,等待主机开始信号结束后,
  • DHT11发送响应信号,送出40bit的数据,并触发一次信号采集, 用户可选择读取部分数据.
  • 从模式下,DHT11接收到开始信号触发一次温湿度采集, 如果没有接收到主机发送开始信号,DHT11不会主动进行温湿度采集.采集数据后转换到低速模式。
  • 4.2、内部结构

    传感器包括一个电阻式感湿元件和一个NTC测 温元件,并与一个高性能8位单片机相连接。

    4.3、根据时序写代码

    4.2.1、时钟、端口、引脚、高低电平的配置

    #ifndef  _DHT_11
    #define  _DHT_11 
    //  时钟  端口  引脚   
    #define DHT11_RCC RCC_AHBPeriph_GPIOA
    #define DHT11_PORT GPIOA
    #define DHT11_PIN GPIO_Pin_7
    
    
    #define DHT11_H    GPIO_SetBits(DHT11_PORT,DHT11_PIN)    //  高电平
    #define DHT11_L    GPIO_ResetBits(DHT11_PORT,DHT11_PIN)  //  低电平
    
    
    
    #endif
    
    int8_t GPIOSet(GPIO_TypeDef* GPIOx, uint16_t GPIOPin, uint16_t GPIOMode)
    {
    	if(GPIOMode == 0)
    	{
    		GPIO_ResetBits(GPIOx,GPIOPin);
    	}
    	else
    	{
    		GPIO_SetBits(GPIOx,GPIOPin);
    	}
    	return GPIOMode;
    }
    
    //  uint8_t GPIOGet(GPIO_TypeDef* GPIOx, uint16_t GPIOPin)
    uint8_t GPIOGet(GPIO_TypeDef* GPIOx, uint16_t GPIOPin)
    {
    	return (GPIO_ReadInputDataBit(GPIOx, GPIOPin));
    }
    //  输出配置
    void DHT11_GPIO_Out(void)
    {
    	//  时钟   端口  引脚  输出模式  推挽输出   不上拉/不下拉
      GPIOInit_Dout(DHT11_RCC,DHT11_PORT,DHT11_PIN,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);
    }
    
    //  输入配置
    void DHT11_GPIO_In(void)
    {
    	
    	//   时钟  端口  引脚  输入模式   上拉输入
      GPIOInit_Din(DHT11_RCC,DHT11_PORT,DHT11_PIN,GPIO_Mode_IN,GPIO_PuPd_UP);
    }

    4.2.2、通讯过程

    三种写时序的方式

    
    //  输出配置
    void DHT11_GPIO_Out(void)
    {
    	//  时钟   端口  引脚  输出模式  推挽输出   不上拉/不下拉
      GPIOInit_Dout(DHT11_RCC,DHT11_PORT,DHT11_PIN,GPIO_Mode_OUT,GPIO_OType_PP,GPIO_PuPd_NOPULL);
    }
    
    //  输入配置
    void DHT11_GPIO_In(void)
    {
    	 //  
    	 // GPIOInit_Din(DHT11_RCC,DHT11_PORT,DHT11_PIN,GPIO_Mode_IN,GPIO_PuPd_DOWN);
    	
    	  //GPIOInit_Din(DHT11_RCC,DHT11_PORT,DHT11_PIN,GPIO_Mode_IN,GPIO_PuPd_NOPULL);
    	
    	 //   时钟  端口  引脚  输入模式   上拉输入    经过测试此处必须设置为上拉才能上数据
        GPIOInit_Din(DHT11_RCC,DHT11_PORT,DHT11_PIN,GPIO_Mode_IN,GPIO_PuPd_UP);
    	
    }
    
    void DHT11_Init(void)
    {
      DHT11_GPIO_Out();
    	DHT11_H;
    	
    }
    
    #if 1
    /* 
     * 从DHT11读取一个字节,MSB先行
     */
    static st_u8 DHT11_ReadByte ( void )
    {
    	st_u8 i, temp=0;
    	
    
    	for(i=0;i<8;i++)    
    	{	 
    		/*每bit以50us低电平标置开始,轮询直到从机发出 的50us 低电平 结束*/  
    		while(DHT11_Input==Bit_RESET);
    
    		/*DHT11 以26~28us的高电平表示“0”,以70us高电平表示“1”,
    		 *通过检测 x us后的电平即可区别这两个状 ,x 即下面的延时 
    		 */
    		delay_us(40); //延时x us 这个延时需要大于数据0持续的时间即可	   	  
    
    		if(DHT11_Input==Bit_SET)/* x us后仍为高电平表示数据“1” */
    		{
    			/* 等待数据1的高电平结束 */
    			while(DHT11_Input==Bit_SET);
    
    			temp|=(st_u8)(0x01<<(7-i));  //把第7-i位置1,MSB先行 
    		}
    		else	 // x us后为低电平表示数据“0”
    		{			    
    			temp&=(st_u8)~(0x01<<(7-i)); //把第7-i位置0,MSB先行  高位先出。  高位 1111 1111  地位  左边高位,右边地位  
    		}
    	}
    	
    	return temp;
    	
    }
    
    /*
     * 一次完整的数据传输为40bit,高位先出
     * 8bit 湿度整数 + 8bit 湿度小数 + 8bit 温度整数 + 8bit 温度小数 + 8bit 校验和 
     */
    st_u8 DHT11_Read_TempAndHumidity(DHT11_Data_TypeDef *DHT11_Data)
    {  
    	/*输出模式*/
    	DHT11_GPIO_Out();
    	/*主机拉低*/
    	DHT11_L;
    	/*延时18ms*/
    	delay_us(18);
    
    	/*总线拉高 主机延时30us*/
    	DHT11_H; 
    
    	delay_ms(30);   //延时30us
    
    	/*主机设为输入 判断从机响应信号*/ 
    	DHT11_GPIO_In();
    
    	/*判断从机是否有低电平响应信号 如不响应则跳出,响应则向下运行*/   
    	if(DHT11_Input==Bit_RESET)     
    	{
    		/*轮询直到从机发出 的80us 低电平 响应信号结束*/  
    		while(DHT11_Input==Bit_RESET);
    
    		/*轮询直到从机发出的 80us 高电平 标置信号结束*/
    		while(DHT11_Input==Bit_SET);
    
    		/*开始接收数据*/   
    		DHT11_Data->humi_int= DHT11_ReadByte();
    
    		DHT11_Data->humi_deci= DHT11_ReadByte();
    
    		DHT11_Data->temp_int= DHT11_ReadByte();
    
    		DHT11_Data->temp_deci= DHT11_ReadByte();
    
    		DHT11_Data->check_sum= DHT11_ReadByte();
    
    
    		/*读取结束,引脚改为输出模式*/
    		DHT11_GPIO_Out();
    		/*主机拉高*/
    		DHT11_H;
    
    		/*检查读取的数据是否正确*/
    		// 一次完整的数据传输为40bit,高位先出。数据格式:8bit湿度整数数据+8bit湿度小数数据+8bi温度整数数据+8bit温度小数数据+8bit校验和
    		if(DHT11_Data->check_sum == DHT11_Data->humi_int + DHT11_Data->humi_deci + DHT11_Data->temp_int+ DHT11_Data->temp_deci)
    			return SUCCESS;
    		else 
    			return ERROR;
    	}
    	
    	else
    		return ERROR;
    	
    }
    #elif select1
    //从DHT11读取一个位
    //返回值:1/0
    st_u8 DHT11_Read_Bit(void) 			 
    {
     	st_u8 retry=0;
    	while(DHT11_Input==0 &&retry<100)//等待变为低电平 12-14us 开始
    	{
    		retry++;
    		delay_us(1);
    	}
    	retry=0;
    	while(!DHT11_Input&&retry<100)//等待变高电平	 26-28us表示0,116-118us表示1
    	{
    		retry++;
    		delay_us(1);
    	}
    	delay_us(40);//等待40us
    	if(DHT11_Input)return 1;
    	else return 0;		   
    }
    
    //从DHT11读取一个字节
    //返回值:读到的数据
    st_u8 DHT11_Read_Byte(void)    
    {        
        st_u8 i,dat;
        dat=0;
    	for (i=0;i<8;i++) 
    	{
       		dat<<=1; 
    	    dat|=DHT11_Read_Bit();
        }						    
        return dat;
    }
    
    //从DHT11读取一次数据
    //temp:温度值(范围:0~50°)
    //humi:湿度值(范围:20%~90%)
    //返回值:0,正常;1,读取失败
    st_u8 DHT11_Read_Data(st_u8 *temp,st_u8 *humi)    
    {        
     	st_u8 buf[5];
    	st_u8 i;
    	DHT11_Rst();
    	if(DHT11_Check()==0)
    	{
    		for(i=0;i<5;i++)//读取40位数据
    		{
    			buf[i]=DHT11_Read_Byte();
    		}
    		if((buf[0]+buf[1]+buf[2]+buf[3])==buf[4])
    		{
    			*humi=buf[0];
    			*temp=buf[2];
    		}
    		
    	}else return 1;
    	return 0;	    
    }
    #else
    // 转译采集DHT11发送给IO口的电平信号(8位)
    // 从DHT11读取一位(8字节)信号
    st_u8 DHT11_Read_Byte(void)
    {
       st_u8 ReadData = 0; // 存放8bit数据,即8个单次读取的1bit数据的组合
    	 st_u8 temp;         // 临时存放信号电平(0或1)
    	 st_u8 retry =0;     //  retry用于防止卡死
    	 st_u8 i;
    	for(i=0;i<8;i++)      // 一次温湿度信号读取八位
    	{
    	    while(DHT11_Input==0&&retry<100)
    			{
    			  // 等待直到DHT11输出高电平:
    				// 当PA7=1,上升沿,表示开始接受数据,可以判断0 or 1,跳出循环,执行后续判断(若PA7=0,将一直循环等待)
    			   delay_us(1);
    				 retry++;
    			};
    			 retry=0;
    			delay_us(40);
    			//  根据时序图,DHT传回高电平信号维持26us~28us表示0,	维持70us表示1
    		  //  延时30us后,如果IO读取到仍是高电平,说明采集到1;如果IO读取到低电平,说明采集到0
    			//  读取电平信号暂存temp内,随后会压入ReadData中
         while(DHT11_Input==1&&retry<100)
    		 {
    			  delay_us(1);
    				 retry++;
    		 };
    		 retry=0;
    		 ReadData <<=1;  //  ReadData内信号先全部左移一位,空出末尾位置
    		 ReadData|=temp;
    	}
    	return ReadData;
    }
    
    st_u8 DHT_Read(void)
    {
    	 st_u8 retry=0;
    	 st_u8 i;
    		
    	 DHT11_GPIO_Out();    //IO设置为输出模式。在传输的最开始,MCU要向DHT11发送信号
    	 DHT11_L;   //IO->DHT11:先拉低电平18ms(应时序要求)
    	 delay_us(18);
    	DHT11_H;   //IO->DHT11:随后拉高电平20us
    	 delay_us(20);
    	
    	//MCU通过IO向DHT11发送请求完毕。接下来DHT11向IO发送响应,IO转为输入模式。在这之后就开始信号的转译读取。
    	DHT11_GPIO_In();
    	delay_us(20);
    	if(DHT11_Input==0) //DHT11发回低电平响应(读取到低电平,说明DHT11有响应)
    	{
    		//接下来,DHT11拉低电平一段时间后拉高电平一段时间
    		while(DHT11_Input==0 && retry<100)  
    		{
    		   delay_us(1);
    			 retry++;
    		}
    		 retry=0;
    		while(DHT11_Input==1 && retry<100) 
    		{
    		   delay_us(1);
    			 retry++;
    		}
    		retry=0;
    		
    		//一共传输40位,一次DHT_Read_Byte返回8位,共读取5次。存储在Data[]中。(Data[]定义为全局)
    		for(i=0; i<5; i++)
    		{
    			 Data[i] = DHT11_Read_Byte();  //每次读取一字节(8位)
    		}
    		delay_us(50);
    		//说明:Data[0]湿度, Data[2]温度。Data[1]和Data[3]分别为0和2的小数位。Data[4]用于校验。
    	}
    	 st_u32 sum=Data[0]+Data[1]+Data[2]+Data[3];  //校验
    	 if((sum)==Data[4])    return 1;  
    	   else   return 0;
    }
    #endif
    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32实现的温湿度监测:第一部分

    发表评论