stm32F103C8T6控制DHT11

2023-11-07

stm32F103C8T6控制DHT11串口打印

stm32F103C8T6控制DHT11串口打印学习经验总结,本人借鉴了许多大佬们的资料,这是个人学习的见解,如发现错误之处,麻烦指导指导。
借鉴链接:https://blog.csdn.net/XiaoCaiDaYong/article/details/105640644
https://blog.csdn.net/weixin_43522198/article/details/111304708
https://blog.csdn.net/sxm123z/article/details/107235647

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

DHT11温湿度传感器的引脚说明
在这里插入图片描述
DHT11温湿度传感器的引脚说明
在这里插入图片描述DHT11数字湿温度传感器采用单总线数据格式。单个数据引脚端口完成输入输出双向传输。

其数据包由5Byte(40Bit)组成。数据分小数部分和整数部分,一次完整的数据传输为40bit,高位先出。

DHT11的数据格式为:8bit湿度整数数据+8bit湿度小数数据+8bit温度整数数据+8bit温度小数数据+8bit校验和。

其中校验和数据为前四个字节相加。 传感器数据输出的是未编码的二进制数据。数据(湿度、温度、整数、小数)之间应该分开处理。

传感器数据输出的是未编码的二进制数据。数据(湿度、温度、整数、小数)之间应该分开处理。

DHT11 开始发送数据流程
在这里插入图片描述主机发送开始信号后,延时等待 20us-40us 后读取 DH11T 的回应信号,读取总线为低电平,说明 DHT11 发送响应信号, DHT11 发送响应信号后,再把总线拉高,准备发送数据,每一 bit 数据都以低电平开始,格式见下面图示。如果读取响应信号为高电平,则 DHT11 没有响应,请检查线路是否连接正常。
首先主机发送开始信号,即:拉低数据线,保持t1(至少18ms)时间,然后拉高数据线t2(20-40us)时间,然后读取DHT11的响应,正常的话,DHT11会拉低数据线,保持t3(40-50us)时间,作为响应信号,然后DHT11拉高数据线,保t4(40~50us)时间后,开始输出数据。

主机复位信号和 DHT11 响应信号
在这里插入图片描述

数字‘ 0’信号表示方法
在这里插入图片描述在这里插入图片描述

程序要区分数据0和数据1的格式:先判断此时引脚的电平状态,如果是低电平就一直循环等待,直到高电平出现,高电平出现后延时40us,并读取延时后的电平状态,如果此时是高电平,则数据为1,否则为0

传输完40位数据后,传感器再次输出一个50us的低电平后,将数据总线释放,采集过程结束。

原理图:

在这里插入图片描述

程序分析:

1.dht11.h分析

#ifndef __DHT11_H
#define __DHT11_H
#include "stm32f10x.h"
#include "delay.h"


/* 设置GPIO脚,默认为PB11 */
#define DHT11_IO 		            GPIOB
#define DHT11_PIN		            GPIO_Pin_7
#define DHT11_APB2PeriphRCC     RCC_APB2Periph_GPIOB
/* 初始化函数,如果DHT11存在响应则返回1,否则0 */
u8 DHT11_Init(void);
/* 从DHT11读取数据,没有小数部分 */
 u8 DHT11_Read_Data(u8 *temp,u8 *humi);

#endif

设置的引脚为GPIOB,7引脚。

2.dht11.c分析

#include "DHT11.h"

GPIO_InitTypeDef GPIO_InitStructure;	//后面会改变输入输出状态
                                      //结构体声明在最开头
static void GPIO_SETOUT(void);
static void GPIO_SETIN(void);
static u8 DHT11_Check(void);


/**********************************************
函数名:static void DHT11_Rst(void)
参数说明:无
返回值:无
函数作用:主机发送开始信号
***********************************************/
//这是它的物理工作原理,根据原理拉高或拉低它的引脚来唤醒dht11
static void DHT11_Rst(void)     
{                 
	GPIO_SETOUT();											//配置成输出模式
    GPIO_ResetBits(DHT11_IO,DHT11_PIN); //拉低数据线
    Delay_ms(20);    										//拉低至少18ms
    GPIO_SetBits(DHT11_IO,DHT11_PIN); 	//拉高数据线 
	Delay_us(30);     									//主机拉高20~40us
	GPIO_ResetBits(DHT11_IO,DHT11_PIN);
}


/**********************************************
函数名:u8 DHT11_Init(void)
参数说明:无
返回值:u8 ,返回1代表初始化成功,0则失败
函数作用:配置IO口,并发送开始信号
***********************************************/
u8 DHT11_Init(void){
	
	//IO口初始化配置
	
	RCC_APB2PeriphClockCmd(DHT11_APB2PeriphRCC,ENABLE);//换IO口需要修改,时钟设置
	
	GPIO_InitStructure.GPIO_Pin = DHT11_PIN;          //调用引脚
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出,如果需要考虑到IC的电流驱动能力时要接上拉电阻(5K)
	//GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
	GPIO_InitStructure.GPIO_Speed =GPIO_Speed_50MHz;  //speed 可随意
	GPIO_Init(DHT11_IO,&GPIO_InitStructure);
	
	DHT11_Rst();//发送开始信号
	
	return DHT11_Check();//检测DHT11的响应
}


/**********************************************
函数名:static void GPIO_SETOUT(void)
参数说明:无
返回值:无
函数作用:配置IO口为推挽输出模式
***********************************************/
static void GPIO_SETOUT(void)
{
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出,如果需要考虑到IC的电流驱动能力时要接上拉电阻(5K)
	GPIO_Init(DHT11_IO,&GPIO_InitStructure);
	
}


/**********************************************
函数名:static void GPIO_SETIN(void)
参数说明:无
返回值:无
函数作用:配置IO口为浮空输入模式
***********************************************/
static void GPIO_SETIN(void)
{
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入模式
	GPIO_Init(DHT11_IO,&GPIO_InitStructure);
}


/**********************************************
函数名:static u8 DHT11_Check(void)
参数说明:无
返回值:检测到回应-->返回1,否则0
函数作用:检测DHT11的响应信号
***********************************************/
static u8 DHT11_Check(void) 	   
{   
	u8 retry=0;
	GPIO_SETIN();			//设置为输入模式	
	
  while (!GPIO_ReadInputDataBit(DHT11_IO,DHT11_PIN) && retry<100)//DHT11会拉低40~50us
	{
		retry++;
		Delay_us(1);
	}
	if(retry >= 100)	//超时未响应/未收到开始信号,退出检测
		return 0;
	else 
		retry = 0;
  while (GPIO_ReadInputDataBit(DHT11_IO,DHT11_PIN) && retry<100)//DHT11拉低后会再次拉高40~50us
	{
		retry++;
		Delay_us(1);
	}
	if(retry>=100)		//超时,DHT11工作出错,退出检测
		return 0;
	return 1;					//设备正常响应,可以正常工作
}


/**********************************************
函数名:static u8 DHT11_Read_Bit(void)
参数说明:无
返回值:返回从DHT11上读取的一个Bit数据
函数作用:从DHT11上读取一个Bit数据
***********************************************/
static u8 DHT11_Read_Bit(void)
{
 	u8 retry = 0;
	//DHT11的Bit开始信号为12-14us低电平
	while(GPIO_ReadInputDataBit(DHT11_IO,DHT11_PIN) && retry<100)//等待变为低电平(等待Bit开始信号)
	{
		retry++;
		Delay_us(1);
	}
	retry = 0;
	while(!GPIO_ReadInputDataBit(DHT11_IO,DHT11_PIN) && retry<100)//等待变高电平(代表数据开始传输)
	{
		retry++;
		Delay_us(1);
	}
	Delay_us(30);//等待30us
	//0信号为26-28us,1信号则为116-118us,所以说超过30us去读取引脚状态就可以知道传输的值了
	if(GPIO_ReadInputDataBit(DHT11_IO,DHT11_PIN)) return 1;
	else return 0;		   
}


/***********************************************************************
函数名:static u8 DHT11_Read_Byte(void)
参数说明:无
返回值:返回从DHT11上读取的一个byte数据
函数作用:从DHT11上读取一个byte数据
************************************************************************/
static u8 DHT11_Read_Byte(void)    
{        
  u8 i,dat;
  dat=0;
	
	for (i=0;i<8;i++) 
	{
   	dat<<=1; 
	  dat|=DHT11_Read_Bit();
  }	
	
  return dat;
}


/**************************************************************************
函数名:u8 DHT11_Read_Data(u8 *temp,u8 *humi)
参数说明:temp:用于存放温度值(范围:0~50°),humi:用于存放湿度值(范围:20%~90%)
返回值:1:成功读取数据,0:读取数据出错
函数作用:从DHT11上读取温湿度数据(这里省略小数值)
***************************************************************************/
u8 DHT11_Read_Data(u8 *temp,u8 *humi)
{        
 	u8 buf[5];
	u8 i;
	DHT11_Rst();
	if(DHT11_Check()==1)	//设备响应正常
	{
		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 0;		//设备未成功响应,返回0
	return 1;					//读取数据成功返回1
}


这里具体分析在其注释中,如果有想法打印float型,可以尝试把u8改成float,但是许多函数是关联的,牵一发动全身。

3.usart.h分析

#ifndef __USART_H
#define __USART_H
#include "stdio.h"	
#include "sys.h" 

/*zhengdianyuanzi*/

//#define USART_REC_LEN  			200  	//定义最大接收字节数 200
//#define EN_USART1_RX 			1		//使能(1)/禁止(0)串口1接收
//	  	
//extern u8  USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 
//extern u16 USART_RX_STA;         		//接收状态标记	
如果想串口中断接收,请不要注释以下宏定义
//void uart_init(u32 bound);
//#endif






#include "stm32f10x.h"
#include <stdio.h>

/** 
  * 串口宏定义,不同的串口挂载的总线和IO不一样,移植时需要修改这几个宏
	* 1-修改总线时钟的宏,uart1挂载到apb2总线,其他uart挂载到apb1总线
	* 2-修改GPIO的宏
  */
	
// 串口1-USART1
#define  DEBUG_USARTx                   USART1
#define  DEBUG_USART_CLK                RCC_APB2Periph_USART1
#define  DEBUG_USART_APBxClkCmd         RCC_APB2PeriphClockCmd
#define  DEBUG_USART_BAUDRATE           115200

// USART GPIO 引脚宏定义
#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOA)
#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
    
#define  DEBUG_USART_TX_GPIO_PORT       GPIOA   
#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_9
#define  DEBUG_USART_RX_GPIO_PORT       GPIOA
#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_10

#define  DEBUG_USART_IRQ                USART1_IRQn
#define  DEBUG_USART_IRQHandler         USART1_IRQHandler


// 串口2-USART2
//#define  DEBUG_USARTx                   USART2
//#define  DEBUG_USART_CLK                RCC_APB1Periph_USART2
//#define  DEBUG_USART_APBxClkCmd         RCC_APB1PeriphClockCmd
//#define  DEBUG_USART_BAUDRATE           115200

 USART GPIO 引脚宏定义
//#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOA)
//#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
//    
//#define  DEBUG_USART_TX_GPIO_PORT       GPIOA   
//#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_2
//#define  DEBUG_USART_RX_GPIO_PORT       GPIOA
//#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_3

//#define  DEBUG_USART_IRQ                USART2_IRQn
//#define  DEBUG_USART_IRQHandler         USART2_IRQHandler

// 串口3-USART3
//#define  DEBUG_USARTx                   USART3
//#define  DEBUG_USART_CLK                RCC_APB1Periph_USART3
//#define  DEBUG_USART_APBxClkCmd         RCC_APB1PeriphClockCmd
//#define  DEBUG_USART_BAUDRATE           115200

 USART GPIO 引脚宏定义
//#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOB)
//#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
//    
//#define  DEBUG_USART_TX_GPIO_PORT       GPIOB   
//#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_10
//#define  DEBUG_USART_RX_GPIO_PORT       GPIOB
//#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_11

//#define  DEBUG_USART_IRQ                USART3_IRQn
//#define  DEBUG_USART_IRQHandler         USART3_IRQHandler

// 串口4-UART4
//#define  DEBUG_USARTx                   UART4
//#define  DEBUG_USART_CLK                RCC_APB1Periph_UART4
//#define  DEBUG_USART_APBxClkCmd         RCC_APB1PeriphClockCmd
//#define  DEBUG_USART_BAUDRATE           115200

 USART GPIO 引脚宏定义
//#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOC)
//#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
//    
//#define  DEBUG_USART_TX_GPIO_PORT       GPIOC   
//#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_10
//#define  DEBUG_USART_RX_GPIO_PORT       GPIOC
//#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_11

//#define  DEBUG_USART_IRQ                UART4_IRQn
//#define  DEBUG_USART_IRQHandler         UART4_IRQHandler


// 串口5-UART5
//#define  DEBUG_USARTx                   UART5
//#define  DEBUG_USART_CLK                RCC_APB1Periph_UART5
//#define  DEBUG_USART_APBxClkCmd         RCC_APB1PeriphClockCmd
//#define  DEBUG_USART_BAUDRATE           115200

 USART GPIO 引脚宏定义
//#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOD)
//#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd
//    
//#define  DEBUG_USART_TX_GPIO_PORT       GPIOC   
//#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_12
//#define  DEBUG_USART_RX_GPIO_PORT       GPIOD
//#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_2

//#define  DEBUG_USART_IRQ                UART5_IRQn
//#define  DEBUG_USART_IRQHandler         UART5_IRQHandler


void USART_Config(void);
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch);
void Usart_SendString( USART_TypeDef * pUSARTx, char *str);
void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch);

#endif /* __USART_H */

我这串口的程序是借鉴野火和正点原子的,根据自己的喜好去调用

4.usart.c分析

#include "sys.h"
#include "usart.h"	  
// 	 
//如果使用ucos,则包括下面的头文件即可.
//#if SYSTEM_SUPPORT_OS
//#include "includes.h"					//ucos 使用	  
//#endif
//	 
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32开发板
//串口1初始化		   
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//修改日期:2012/8/18
//版本:V1.5
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2009-2019
//All rights reserved
//********************************************************************************
//V1.3修改说明 
//支持适应不同频率下的串口波特率设置.
//加入了对printf的支持
//增加了串口接收命令功能.
//修正了printf第一个字符丢失的bug
//V1.4修改说明
//1,修改串口初始化IO的bug
//2,修改了USART_RX_STA,使得串口最大接收字节数为2的14次方
//3,增加了USART_REC_LEN,用于定义串口最大允许接收的字节数(不大于2的14次方)
//4,修改了EN_USART1_RX的使能方式
//V1.5修改说明
//1,增加了对UCOSII的支持
// 	  
 

//
//加入以下代码,支持printf函数,而不需要选择use MicroLIB	  


//#if 1
//#pragma import(__use_no_semihosting)             
标准库需要的支持函数                 
//struct __FILE 
//{ 
//	int handle; 

//}; 

//FILE __stdout;       
定义_sys_exit()以避免使用半主机模式    
//_sys_exit(int x) 
//{ 
//	x = x; 
//} 
重定义fputc函数 
//int fputc(int ch, FILE *f)
//{      
//	while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   
//    USART1->DR = (u8) ch;      
//	return ch;
//}
//#endif 

///*使用microLib的方法*/
// /* 
//int fputc(int ch, FILE *f)
//{
//	USART_SendData(USART1, (uint8_t) ch);

//	while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET) {}	
//   
//    return ch;
//}
//int GetKey (void)  { 

//    while (!(USART1->SR & USART_FLAG_RXNE));

//    return ((int)(USART1->DR & 0x1FF));
//}
//*/
// 
//#if EN_USART1_RX   //如果使能了接收
串口1中断服务程序
注意,读取USARTx->SR能避免莫名其妙的错误   	
//u8 USART_RX_BUF[USART_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.
接收状态
bit15,	接收完成标志
bit14,	接收到0x0d
bit13~0,	接收到的有效字节数目
//u16 USART_RX_STA=0;       //接收状态标记	  
//  
//void uart_init(u32 bound){
//  //GPIO端口设置
//  GPIO_InitTypeDef GPIO_InitStructure;
//	USART_InitTypeDef USART_InitStructure;
//	NVIC_InitTypeDef NVIC_InitStructure;
//	 
//	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);	//使能USART1,GPIOA时钟
//  
//	//USART1_TX   GPIOA.9
//  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
//  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
//  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//复用推挽输出
//  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
//   
//  //USART1_RX	  GPIOA.10初始化
//  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
//  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
//  GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10  

//  //Usart1 NVIC 配置
//  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
//	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
//	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;		//子优先级3
//	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//IRQ通道使能
//	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器
//  
//   //USART 初始化设置

//	USART_InitStructure.USART_BaudRate = bound;//串口波特率
//	USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
//	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); //初始化串口1
//  USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
//  USART_Cmd(USART1, ENABLE);                    //使能串口1 

//}

//void USART1_IRQHandler(void)                	//串口1中断服务程序
//	{
//	u8 Res;
//#if SYSTEM_SUPPORT_OS 		//如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
//	OSIntEnter();    
//#endif
//	if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
//		{
//		Res =USART_ReceiveData(USART1);	//读取接收到的数据
//		
//		if((USART_RX_STA&0x8000)==0)//接收未完成
//			{
//			if(USART_RX_STA&0x4000)//接收到了0x0d
//				{
//				if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
//				else USART_RX_STA|=0x8000;	//接收完成了 
//				}
//			else //还没收到0X0D
//				{	
//				if(Res==0x0d)USART_RX_STA|=0x4000;
//				else
//					{
//					USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
//					USART_RX_STA++;
//					if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收	  
//					}		 
//				}
//			}   		 
//     } 
//#if SYSTEM_SUPPORT_OS 	//如果SYSTEM_SUPPORT_OS为真,则需要支持OS.
//	OSIntExit();  											 
//#endif
//} 
//#endif	

static void NVIC_Configuration(void)
{
  NVIC_InitTypeDef NVIC_InitStructure;
  
  /* 嵌套向量中断控制器组选择 */
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  
  /* 配置USART为中断源 */
  NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
  /* 抢断优先级*/
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
  /* 子优先级 */
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
  /* 使能中断 */
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  /* 初始化配置NVIC */
  NVIC_Init(&NVIC_InitStructure);
}

 /**
  * @brief  USART GPIO 配置,工作参数配置
  * @param  无
  * @retval 无
  */
void USART_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;

	// 打开串口GPIO的时钟
	DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
	
	// 打开串口外设的时钟
	DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);

	// 将USART Tx的GPIO配置为推挽复用模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);

  // 将USART Rx的GPIO配置为浮空输入模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
	
	// 配置串口的工作参数
	// 配置波特率
	USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
	// 配置 针数据字长
	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(DEBUG_USARTx, &USART_InitStructure);
	
	// 串口中断优先级配置
	NVIC_Configuration();
	
	// 使能串口接收中断
	USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);	
	
	// 使能串口
	USART_Cmd(DEBUG_USARTx, ENABLE);	    
}

/*****************  发送一个字节 **********************/
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
{
	/* 发送一个字节数据到USART */
	USART_SendData(pUSARTx,ch);
		
	/* 等待发送数据寄存器为空 */
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);	
}

/****************** 发送8位的数组 ************************/
void Usart_SendArray( USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num)
{
  uint8_t i;
	
	for(i=0; i<num; i++)
  {
	    /* 发送一个字节数据到USART */
	    Usart_SendByte(pUSARTx,array[i]);	
  
  }
	/* 等待发送完成 */
	while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET);
}

/*****************  发送字符串 **********************/
void Usart_SendString( USART_TypeDef * pUSARTx, char *str)
{
	unsigned int k=0;
  do 
  {
      Usart_SendByte( pUSARTx, *(str + k) );
      k++;
  } while(*(str + k)!='\0');
  
  /* 等待发送完成 */
  while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET)
  {}
}

/*****************  发送一个16位数 **********************/
void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch)
{
	uint8_t temp_h, temp_l;
	
	/* 取出高八位 */
	temp_h = (ch&0XFF00)>>8;
	/* 取出低八位 */
	temp_l = ch&0XFF;
	
	/* 发送高八位 */
	USART_SendData(pUSARTx,temp_h);	
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
	
	/* 发送低八位 */
	USART_SendData(pUSARTx,temp_l);	
	while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);	
}

///重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
		/* 发送一个字节数据到串口 */
		USART_SendData(DEBUG_USARTx, (uint8_t) ch);
		
		/* 等待发送完毕 */
		while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);		
	
		return (ch);
}

///重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{
		/* 等待串口输入数据 */
		while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);

		return (int)USART_ReceiveData(DEBUG_USARTx);
}


两者之间还是有一定的差别,尽可能的学习尝试调用两种函数。

5.delay.h分析


#ifndef __DELAY_H
#define __DELAY_H 			   
#include "sys.h"  

void Delay_ms(u16 ms);
void Delay_us(u32 us);
void Delay_s(u16 s);
#endif

具体分析在delay.c中

6.delay.c分析

#include "delay.h"

u32 SysTic_count = 100;
//使用系统滴答计时器精准延时

void Delay_s(u16 s)
 {
	 SysTic_count = s;
   
	 SysTick_Config(72000000);			//默认设置为sysclock,1s计数72000000次,则1ms中断一次
	 while( SysTic_count!= 0);//等待计时完成
	 SysTick->CTRL = 0;  //关闭滴答计时器
 }
 
 void Delay_ms(u16 ms)
 {
	 SysTic_count = ms;
   
	 SysTick_Config(72000);			//默认设置为sysclock,1s计数72000000次,则1ms中断一次
	 while( SysTic_count!= 0);//等待计时完成
	 SysTick->CTRL = 0;  //关闭滴答计时器
 }
 
 /* 定时微秒级函数 */
 void Delay_us(u32 us)
 {
	 SysTic_count = us;
   
	 SysTick_Config(72);			//默认设置为sysclock,1s计数72000000次,则1us中断一次
	 while( SysTic_count!= 0);//等待计时完成
	 SysTick->CTRL = 0;  //关闭滴答计时器
 }

 /* sysTick中断处理函数 */
 void SysTick_Handler(void)
 {
	 if(SysTic_count != 0) SysTic_count--;
 }

我这用的是滴答定时器,它的晶振频率为72M,也就是1秒震动72M次,所以这就产生了以上的算法,具体其他计时器要参考时钟树。还有一种算法就是72M八分频,震动9000次就是一毫秒
在这里插入图片描述如图这里需要变化,如果是12或者是10,试算一下72M,1秒的话就是72/12=6M,72/10=7.2M,要么改代码要么改这儿

7.sys.h分析

#ifndef __SYS_H
#define __SYS_H																	    
#include "stm32f10x.h"
#include "led.h"
#include "my_usart.h"
#include "delay.h"
#include "dht11.h"
#include <stdio.h>     //c语言printf的官方库
//IO口操作宏定义
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum)) 
//IO口地址映射
#define GPIOA_ODR_Addr    (GPIOA_BASE+12) //0x4001080C 
#define GPIOB_ODR_Addr    (GPIOB_BASE+12) //0x40010C0C 
#define GPIOC_ODR_Addr    (GPIOC_BASE+12) //0x4001100C 
#define GPIOD_ODR_Addr    (GPIOD_BASE+12) //0x4001140C 
#define GPIOE_ODR_Addr    (GPIOE_BASE+12) //0x4001180C 
#define GPIOF_ODR_Addr    (GPIOF_BASE+12) //0x40011A0C    
#define GPIOG_ODR_Addr    (GPIOG_BASE+12) //0x40011E0C    

#define GPIOA_IDR_Addr    (GPIOA_BASE+8) //0x40010808 
#define GPIOB_IDR_Addr    (GPIOB_BASE+8) //0x40010C08 
#define GPIOC_IDR_Addr    (GPIOC_BASE+8) //0x40011008 
#define GPIOD_IDR_Addr    (GPIOD_BASE+8) //0x40011408 
#define GPIOE_IDR_Addr    (GPIOE_BASE+8) //0x40011808 
#define GPIOF_IDR_Addr    (GPIOF_BASE+8) //0x40011A08 
#define GPIOG_IDR_Addr    (GPIOG_BASE+8) //0x40011E08 

//IO口操作,只对单一的IO口!
//确保n的值小于16!
#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  //输出 
#define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)  //输入 

#define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)  //输出 
#define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n)  //输入 

#define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr,n)  //输出 
#define PCin(n)    BIT_ADDR(GPIOC_IDR_Addr,n)  //输入 

#define PDout(n)   BIT_ADDR(GPIOD_ODR_Addr,n)  //输出 
#define PDin(n)    BIT_ADDR(GPIOD_IDR_Addr,n)  //输入 

#define PEout(n)   BIT_ADDR(GPIOE_ODR_Addr,n)  //输出 
#define PEin(n)    BIT_ADDR(GPIOE_IDR_Addr,n)  //输入

#define PFout(n)   BIT_ADDR(GPIOF_ODR_Addr,n)  //输出 
#define PFin(n)    BIT_ADDR(GPIOF_IDR_Addr,n)  //输入

#define PGout(n)   BIT_ADDR(GPIOG_ODR_Addr,n)  //输出 
#define PGin(n)    BIT_ADDR(GPIOG_IDR_Addr,n)  //输入


#endif

具体就不讲了,我们的习惯是把头文件放入sys.h中.

8.sys.c分析

#include "sys.h"

9.main.c分析

#include "stm32f10x.h"
#include "usart.h"
#include "delay.h"
#include "DHT11.h"

/*
*读取温湿度传感器DHT11的值,并用串口打印出来
*/

void clock_init(void);

u8 temp = 0,humi = 0;   //全局变量


/**************************************************************************
函数名:int main(void)
参数说明:无
返回值:无
函数作用:主函数
***************************************************************************/
 int main(void)
 {	
	 
	 char y='%';
     clock_init();
	 USART_Config();		//初始化串口
	 printf("wecome to DHT11\n");
	 //初始化DHT11(有BUG,第一次上电总是失败,按一下复位按钮又能进了)
	 if(!DHT11_Init()){
		 printf(" Error! T DHT11 HAS NO RESPOND...\n");
		 
	 }
	 printf("\r\n THE DHT11 HAS RESPOND");
	 Delay_ms(10);		//这里延时10ms主要是因为,刚刚接收到响应信息,要等DHT11发送完信息
	 
	 while(1)
	 {
		 if(DHT11_Read_Data(&temp,&humi))     //&temp的地址对应*temp地址,所以数据一样
			printf("\r\n temp: %d'C   ,humi: %d%c",temp,humi,y);  //'%'有实体意义,只能用字符表示
		 else
		 printf("\r\n EEROR! THE DHT11 HAS NO RESPOND...");//由于是库函数编程,不能准确把握函数的执行时间,
																											//所以会经常出现这条警告信息
		 Delay_ms(1000);
	 } 
 }
 
 
/**************************************************************************
函数名:void clock_init(void)
参数说明:无
返回值:无
函数作用:开启高速外部时钟,
ADCCLK设置为12MHZ,	SYSCLK设置为72Mhz,PCLK1设置为36MHZ,PKLC2设置为72mhz
***************************************************************************/
 void clock_init(void)
{
	RCC->CR = 0x1010000;
	RCC->CFGR = 0x1DC402;
}

10.stm32f10x_it.c

#include "stm32f10x_it.h" 


 
void NMI_Handler(void)
{
}
 
void HardFault_Handler(void)
{
  /* Go to infinite loop when Hard Fault exception occurs */
  while (1)
  {
  }
}
 
void MemManage_Handler(void)
{
  /* Go to infinite loop when Memory Manage exception occurs */
  while (1)
  {
  }
}

 
void BusFault_Handler(void)
{
  /* Go to infinite loop when Bus Fault exception occurs */
  while (1)
  {
  }
}
 
void UsageFault_Handler(void)
{
  /* Go to infinite loop when Usage Fault exception occurs */
  while (1)
  {
  }
}
 
void SVC_Handler(void)
{
}
 
void DebugMon_Handler(void)
{
}
 
void PendSV_Handler(void)
{
}
 

它可能要做出一点小改变.看一看和你自动生成的stm32f10x_it.c有啥区别.

在这里插入图片描述
这是我存放的位置.

在这里插入图片描述
这可能是你要建立的文件数量.

实物图
在这里插入图片描述
在这里插入图片描述
打开串口,是需要串口烧录器连接的,注意串口页面的设置,我们串口页面的设置是根据usart.c设置的
在这里插入图片描述

整体代码和串口调试工具我都放置在了百度网盘上

链接:https://pan.baidu.com/s/1fzq8YT23JOSoPxZvtsEH_g
提取码:q31u

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

stm32F103C8T6控制DHT11 的相关文章

  • [C语言]数据是如何存储的(整型篇)?(一)

    新手小白写的第一篇博客 记录自己的学习过程 希望我写的文章能给你们帮助 如果有不足的地方欢迎在评论区留言交流 首先我要介绍数据分为哪些类型 其次介绍整型在数据中是怎样存储的 最后介绍浮点型在内存中的存储 一 数据类型 基本的内置类型 cha

随机推荐

  • Docker映像存储在哪里? Docker容器路径介绍

    Docker has been widely adopted and is used to run and scale applications in production Additionally it can be used to st
  • Fundamental concepts about Lithography

    https dunham ece uw edu ee528 notes Chapter5 pdf Text Silicon VLSI technology fundamentals practice and modeling Aerial
  • 在Power Designer生成的类图中同时显示name和code

    在PowerDesigner生成的类图中同时显示name和code 问题 由显示name改为显示code 同时显示name和Code 问题 Power Designer是一款非常强大的设计工具 缺省的类图只显示name 一般地我们在name
  • 牛顿法(Newton’s method)

    牛顿法通常都是用来寻找一个根 同时也可以理解为最大化目标函数的局部二次近似 设我们的目标函数为f x 那么一个关于x0的二次近似就有 我们用f进行匹配 可以得到 如果b lt 0 g的最大值为a 得到更新规则 这是牛顿法在最优化方面的表述
  • 使用wps转换文本中001、002章节名为第001章、第002章

    1 首先ctrl f打开查找和替换 2 点击弹窗中高级搜索 选中使用通配符 3 可先输入 lt 0 9 3 尝试是否可以查找出文本内章节名 可以找到的话进行下一步 4 切换到替换tab 替换为输入框内输入第 1章 之后点击底部全部替换 即可
  • 万向区块链肖风:元宇宙的十大经济规则

    本文为万向区块链董事长兼总经理肖风为华泰证券研究所科技及电子行业首席分析师黄乐平 万向区块链首席经济学家邹传伟联合撰写的 元宇宙经济学 所作序言 元宇宙是什么 按照我的理解 元宇宙是一个由分布式网络技术 分布式账本和分布式社会 商业构成的三
  • 【Linux网络编程笔记】TCP短连接产生大量TIME_WAIT导致无法对外建立新TCP连接的原因及解决方法—实践篇

    上篇笔记主要介绍了与TIME WAIT相关的基础知识 本文则从实践出发 说明如何解决文章标题提出的问题 1 查看系统网络配置和当前TCP状态 在定位并处理应用程序出现的网络问题时 了解系统默认网络配置是非常必要的 以x86 64平台Linu
  • Python raise用法(超级详细,看了无师自通)

    当程序出现错误时 系统会自动引发异常 除此之外 Python 也允许程序自行引发异常 自行引发异常使用 raise 语句来完成 异常是一种很 主观 的说法 以下雨为例 假设大家约好明天去爬山郊游 如果第二天下雨了 这种情况会打破既定计划 就
  • 软件测试测试环境搭建很难?一天学会这份测试环境搭建教程

    如何搭建测试环境 这既是一道高频面试题 又是困扰很多小伙伴的难题 因为你在网上找到的大多数教程 乃至在一些培训机构的课程 都不会有详细的说明 你能找到的大多数项目 是在本机电脑环境搭建环境 或是别人已经搭建好的环境 你很难上手体验在服务器上
  • IntersectionObserver实现滚动加载

    加载模板及样式 template div class lazy more div
  • WebSocket :用WebSocket实现推送你必须考虑的几个问题

    目录 目录 WebSocket简介 项目背景硬件环境及客户端支持 本文研究内容 基于javaxwebsocket服务端代码源码后续补充git连接 客户端代码 问题探索 8月3日补充 中间线路断网情况 如何做到支持几千个client同时在线人
  • WebSocket集群解决方案,不用MQ

    首先不了解WebSocket的可以先看看这篇文章 以及传统的WebSocket方案是怎么做的 https www cnblogs com jeremylai7 p 16875115 html 这是用MQ解决的版本 那么这种方案存在什么问题呢
  • 使用nginx配置二级域名

    最近想把三个项目配在一个服务器上 于是想使用nginx配置二级域名实现 1 域名添加解析 我的是阿里云的域名 所以首先给自己的域名添加解析 打算使用 www codeliu com test1 codeliu com test2 codel
  • elementUI表格行的点击事件,点击表格,拿到当前行的数据

    1 绑定事件 2 定义事件 3 点击表格某行的时候 拿到数据 转载于 https www cnblogs com wuhefeng p 11316215 html
  • STM32 PID调节输出电压

    一 简介 关于PID调节的这里不做详解 就简单说下 其实就是先设定好一个期望 通过反馈系统返回输出值 然后判断这个输出实际输出的值 和我们的期望值的误差 然后PID算法根据这个误差 去调整我们的输出值 直到输出达到我们的期望值 那么我们为啥
  • 为什么程序员做外包会被瞧不起?

    二哥 有个事想询问下您的意见 您觉得应届生值得去外包吗 公司虽然挺大的 中xx 但待遇感觉挺低 马上要报到 挺纠结的 以上是读者小 K 给我发的私信 除此之外 还有个读者 down 也问我关于外包的事情 担心外包学不到技术 但很不幸的是年前
  • 11种概率分布,你了解几个?

    点击上方 小白学视觉 选择加 星标 或 置顶 重磅干货 第一时间送达 本文转自 视学算法 了解常见的概率分布十分必要 它是概率统计的基石 这是昨天推送的 从概率统计到深度学习 四大技术路线图谱 都在这里 文章中的第一大技术路线图谱如下所示
  • 【mega-nerf】调包失败&pip install失败解决方案

    Problem 1 调包失败 在这样的层级架构里调包 输出无法找到 mega nerf 直接用 sys path append 没有作用 import sys print sys path sys path append home pape
  • Java实现Excel文件生成和下载功能

    7 行代码优雅地实现 Excel 文件生成 下载功能 欢迎关注博主公众号 小哈学Java 专注于分享Java领域干货文章 关注回复 资源 免费领取全网最热的Java架构师学习PDF 转载请注明出处 https www exception s
  • stm32F103C8T6控制DHT11

    stm32F103C8T6控制DHT11串口打印 stm32F103C8T6控制DHT11串口打印学习经验总结 本人借鉴了许多大佬们的资料 这是个人学习的见解 如发现错误之处 麻烦指导指导 借鉴链接 https blog csdn net