stm32F103C8T6控制DHT11
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); //拉低至少18msGPIO_SetBits(DHT11_IO,DHT11_PIN); //拉高数据线 Delay_us(30); //主机拉高20~40usGPIO_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; //设备未成功响应,返回0return 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_IRQHandlervoid 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); //'%'有实体意义,只能用字符表示elseprintf("\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
stm32F103C8T6控制DHT11相关推荐
- esp8266原理图_ESP32/ESP8266使用MicroPython控制DHT11/DHT22
背景知识视频教程 高级ESP32 - 国外课栈viadean.com 使用NodeMCU(由ESP8266支持),MicroPython和PyCharm进行物联网 - 国外课栈viadean.co ...
- stm32f103c8t6控制ESP8266与手机APP通信
stm32f103c8t6控制ESP8266与手机APP通信 之前已经发表过利用stm32控制蓝牙模块与手机APP通信的文章,那么现在我就来说说用WIFI模块来控制与手机APP的通信 首先需要的器件如 ...
- STM32F103C8T6控制电机驱动模块298N驱动电机调速以及正反转(附代码资源包)
STM32F103C8T6控制电机驱动模块298N驱动电机调速以及正反转 一. 硬件选型 1. stm32f103c8t6 2. L298N 3. 电机 4. 其他 二. 硬件连接(连接以接一路电机为 ...
- 51单片机控制DHT11温湿度传感器,并使用OLED屏幕显示
目录 前言 一.MCU主控选型 二.DHT11温湿度传感器 1.模块介绍 2.软件实现 DHT11.c DHT11.h 三.OLED显示屏 1.模块介绍 2.软件实现 OLED.c OLED.h OL ...
- stm32f103c8t6控制蓝牙模块实现led灯亮灭
stm32f103c8t6控制蓝牙模块实现led灯亮灭 之前利用蓝牙模块是用arduino控制板的,现在已经渐渐接触stm32,所以,这次想着用stm32来控制蓝牙模块. 首先的话,需要配置蓝牙模块的 ...
- STM32F103C8T6控制LED灯轮流闪烁
目录 一.实验原理 1.STM32F103C8T6单片机简介 2.地址映射和寄存器映射原理 3.GPIO端口初始化设置 (1)时钟配置 (2)输入与输出设置 输入模式 输出模式 (3)最大速率设置 ( ...
- 使用STM32F103C8T6控制L298N电机驱动麦克纳姆轮小车(HAL库)
文章目录 一.L298N驱动介绍 二.麦克纳姆轮特点及其运动 三.CubeMx配置 1.RCC 2.SYS 3.GPIO 四.代码 一.L298N驱动介绍 驱动部分介绍选https://blog.cs ...
- STM32F103C8T6控制的OLED IIC 4针
简介 介绍一个c8t6驱动OLED的程序,之前做设计的时候,在网上找的内容较少,所以和大家分享下.分享下采用C8T6设计时出现的问题,避免大家走弯路.由于能力有限,程序大部分来自网路,只用于学习交流. ...
- STM32F103C8T6+ESP8266WIFI+DHT11模块连接巴法云
STM32F103C8T6通过ESP8266模块连接巴法云 一.硬件介绍 二.准备工作 三.STM32主要代码介绍 一.硬件介绍 1.MCU:STM32F103C8T6 2.ESP8266:正点原子W ...
最新文章
- mysql默认无密码的问题
- canvas绘制圆形
- 网络营销专员浅析在网络营销推广中有不少渠道可适当“利用”一下
- 7 Java NIO Selector-翻译
- wpf 使用位图画图为什么断断续续_WPF的未来是微软WinUi!
- 【Gradle】配置详解(持续更正补充)
- offes给excel增加下拉选项_财务“救星”:Excel不止可以下拉单元格,还可以进行成本核算...
- 网站跨域访问解决方法
- Android零散技术点
- 在html中如何写日期的代码,日期html代码
- (十)瑞芯微rk3568 中qt工程ffmpeg 拉取rtsp视频流
- html入门圣思园视频,Java Web学习历程-(基于圣思园视频教程)
- greendao的简单使用
- 连续时间 Markov 链从某一状态 i 转移到其他状态之前在 i 逗留的时间服从指数分布
- 反向跟单——羊群效应
- php ios表情包,[iOS] 自定义表情包
- 2022年PC推荐-组装机及品牌机 2022年8月16日(持续更新)
- 学会Python有哪些可以做的兼职?所有途径全在这里了...
- 箱线图(Boxplot)
- 【故障检测】基于 KPCA 的故障检测研究(Matlab代码实现)
热门文章
- 设备通过国标GB28181/海康Ehome接入EasyCVR,视频无法打开的原因分析及解决方法
- I2C 连接 12864 OLED 屏幕
- 【必看】前端代码规范
- word文档找不到smartart_图文详解Word文档插入SmartArt图形的方法
- MyBatis持久层框架
- C++中标准库 输出 puts()函数
- 苹果xr黑屏转圈圈解决方法_苹果xr黑屏转圈打不开也关不掉怎么办
- 有一个8位机,采用单总线结构......(计算机组成原理课后习题)
- OpenLayers:加载GeoServer发布的WMTS、TMS服务
- 易查分显示教师编号不正确