原理图
介绍
main.c
bsp_dht11.h
bsp_dht11.c
core_delay.h
core_delay.c
原理图
介绍
传感器,但是精度不是特别高,我这边直接借用野火官方例程">DHT11是我们最常见的温湿度传感器,但精度不是特别高。我直接借用官方野火例程
main.c
int main(void) { DHT11_Data_TypeDef DHT11_Data; /* 定时器的初始化系统 */ SysTick_Init(); USART_Config();//初始化串口1 printf("\r\n***野火STM32 dht11 温湿度传感器实验***\r\n"); /*初始化DTT11的引脚*/ DHT11_Init (); while(1) { /*调用DHT11_Read_TempAndHumidity读取温湿度,若成功,则输出信息*/ if( DHT11_Read_TempAndHumidity ( & DHT11_Data ) == SUCCESS) { printf("\r\n读取DHT11成功!\r\n\r\n湿度为%d.%d %RH ,温度为 %d.%d℃ \r\n",\ DHT11_Data.humi_int,DHT11_Data.humi_deci,DHT11_Data.temp_int,DHT11_Data.temp_deci); } else { printf("Read DHT11 ERROR!\r\n"); } Delay_ms(2000); } }
bsp_dht11.h
#ifndef __DHT11_H #define __DHT11_H #include "stm32f10x.h" /************************** DHT11 定义数据类型********************************/ typedef struct { uint8_t humi_int; ///湿度的整数部分 uint8_t humi_deci; ///湿度小数部分 uint8_t temp_int; //温度的整数部分 uint8_t temp_deci; ///温度的小数部分 uint8_t check_sum; //校验和 } DHT11_Data_TypeDef; /************************** DHT11 连接引脚定义********************************/ #define DHT11_Dout_SCK_APBxClock_FUN RCC_APB2PeriphClockCmd #define DHT11_Dout_GPIO_CLK RCC_APB2Periph_GPIOC #define DHT11_Dout_GPIO_PORT GPIOC #define DHT11_Dout_GPIO_PIN GPIO_Pin_0 /************************** DHT11 函数宏定义********************************/ #define DHT11_Dout_0 GPIO_ResetBits ( DHT11_Dout_GPIO_PORT, DHT11_Dout_GPIO_PIN ) #define DHT11_Dout_1 GPIO_SetBits ( DHT11_Dout_GPIO_PORT, DHT11_Dout_GPIO_PIN ) #define DHT11_Dout_IN() GPIO_ReadInputDataBit ( DHT11_Dout_GPIO_PORT, DHT11_Dout_GPIO_PIN ) /************************** DHT11 函数声明 ********************************/ void DHT11_Init ( void ); uint8_t DHT11_Read_TempAndHumidity ( DHT11_Data_TypeDef * DHT11_Data ); ; #endif /* __DHT11_H */
bsp_dht11.c
#include "./dht11/bsp_dht11.h" #include "./dwt_delay/core_delay.h" static void DHT11_GPIO_Config ( void ); static void DHT11_Mode_IPU ( void ); static void DHT11_Mode_Out_PP ( void ); static uint8_t DHT11_ReadByte ( void ); /* 可以在下面的宏定义中把后面的延时函数替换换SysTick延迟函数,就是想用那个换成那个。 */ #define DHT11_DELAY_US(us) CPU_TS_Tmr_Delay_US(us) #define DHT11_DELAY_MS(ms) CPU_TS_Tmr_Delay_MS(ms) /** * @brief DHT11 初始化函数 * @param 无 * @retval 无 */ void DHT11_Init ( void ) { DHT11_GPIO_Config (); DHT11_Dout_1; // 拉高GPIOB10 } /* * 函数名:DHT11_GPIO_Config * 描述 :配置DHT11用到的I/O口 * 输入 :无 * 输出 :无 */ static void DHT11_GPIO_Config ( void ) { /*定义一个GPIO_InitTypeDef结构类型*/ GPIO_InitTypeDef GPIO_InitStructure; /*开启DHT11_Dout_GPIO_PORT的外设时钟*/ DHT11_Dout_SCK_APBxClock_FUN ( DHT11_Dout_GPIO_CLK, ENABLE ); /*选择要控制的DHT11_Dout_GPIO_PORT引脚*/ GPIO_InitStructure.GPIO_Pin = DHT11_Dout_GPIO_PIN; /*引脚模式设置为通用推拉输出*/ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; /*引脚率为50MHz */ GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; /*调用库函数,初始化DHT11_Dout_GPIO_PORT*/ GPIO_Init ( DHT11_Dout_GPIO_PORT, &GPIO_InitStructure ); } /* * 函数名:DHT11_Mode_IPU * 描述 :使DHT11-DATA引脚变成上拉输入模式 *输入 :无
* 输出 :无
*/
static void DHT11_Mode_IPU(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/*选择要控制的DHT11_Dout_GPIO_PORT引脚*/
GPIO_InitStructure.GPIO_Pin = DHT11_Dout_GPIO_PIN;
/*设置引脚模式为浮空输入模式*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU ;
/*调用库函数,初始化DHT11_Dout_GPIO_PORT*/
GPIO_Init(DHT11_Dout_GPIO_PORT, &GPIO_InitStructure);
}
/*
* 函数名:DHT11_Mode_Out_PP
* 描述 :使DHT11-DATA引脚变为推挽输出模式
* 输入 :无
* 输出 :无
*/
static void DHT11_Mode_Out_PP(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/*选择要控制的DHT11_Dout_GPIO_PORT引脚*/
GPIO_InitStructure.GPIO_Pin = DHT11_Dout_GPIO_PIN;
/*设置引脚模式为通用推挽输出*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
/*设置引脚速率为50MHz */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
/*调用库函数,初始化DHT11_Dout_GPIO_PORT*/
GPIO_Init(DHT11_Dout_GPIO_PORT, &GPIO_InitStructure);
}
/*
* 从DHT11读取一个字节,MSB先行
*/
static uint8_t DHT11_ReadByte ( void )
{
uint8_t i, temp=0;
for(i=0;i<8;i++)
{
/*每bit以50us低电平标置开始,轮询直到从机发出 的50us 低电平 结束*/
while(DHT11_Dout_IN()==Bit_RESET);
/*DHT11 以26~28us的高电平表示“0”,以70us高电平表示“1”,
*通过检测 x us后的电平即可区别这两个状 ,x 即下面的延时
*/
DHT11_DELAY_US(40); //延时x us 这个延时需要大于数据0持续的时间即可
if(DHT11_Dout_IN()==Bit_SET)/* x us后仍为高电平表示数据“1” */
{
/* 等待数据1的高电平结束 */
while(DHT11_Dout_IN()==Bit_SET);
temp|=(uint8_t)(0x01<<(7-i)); //把第7-i位置1,MSB先行
}
else // x us后为低电平表示数据“0”
{
temp&=(uint8_t)~(0x01<<(7-i)); //把第7-i位置0,MSB先行
}
}
return temp;
}
/*
* 一次完整的数据传输为40bit,高位先出
* 8bit 湿度整数 + 8bit 湿度小数 + 8bit 温度整数 + 8bit 温度小数 + 8bit 校验和
*/
uint8_t DHT11_Read_TempAndHumidity(DHT11_Data_TypeDef *DHT11_Data)
{
/*输出模式*/
DHT11_Mode_Out_PP();
/*主机拉低*/
DHT11_Dout_0;
/*延时18ms*/
DHT11_DELAY_MS(18);
/*总线拉高 主机延时30us*/
DHT11_Dout_1;
DHT11_DELAY_US(30); //延时30us
/*主机设为输入 判断从机响应信号*/
DHT11_Mode_IPU();
/*判断从机是否有低电平响应信号 如不响应则跳出,响应则向下运行*/
if(DHT11_Dout_IN()==Bit_RESET)
{
/*轮询直到从机发出 的80us 低电平 响应信号结束*/
while(DHT11_Dout_IN()==Bit_RESET);
/*轮询直到从机发出的 80us 高电平 标置信号结束*/
while(DHT11_Dout_IN()==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_Mode_Out_PP();
/*主机拉高*/
DHT11_Dout_1;
/*检查读取的数据是否正确*/
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;
}
/*************************************END OF FILE******************************/
core_delay.h
#ifndef __CORE_DELAY_H
#define __CORE_DELAY_H
#include "stm32f10x.h"
#define USE_DWT_DELAY 1 /* 使用dwt内核精确延时 */
#if USE_DWT_DELAY
#define USE_TICK_DELAY 0 /* 不使用SysTick延时 */
#else
#define USE_TICK_DELAY 1 /* 使用SysTick延时 */
#endif
/*简单任务管理*/
#define TASK_ENABLE 0
#define NumOfTask 3
#if USE_DWT_DELAY
//#define Delay_ms(ms) CPU_TS_Tmr_Delay_MS(ms)
//#define Delay_us(us) CPU_TS_Tmr_Delay_US(us)
///* 最大延时 60s=2的32次方/72000000 */
//#define Delay_s(s) CPU_TS_Tmr_Delay_S(s)
/* 获取内核时钟频率 */
#define GET_CPU_ClkFreq() (SystemCoreClock)
#define SysClockFreq (SystemCoreClock)
/* 为方便使用,在延时函数内部调用CPU_TS_TmrInit函数初始化时间戳寄存器,
这样每次调用函数都会初始化一遍。
把本宏值设置为0,然后在main函数刚运行时调用CPU_TS_TmrInit可避免每次都初始化 */
#define CPU_TS_INIT_IN_DELAY_FUNCTION 1
/*******************************************************************************
* 函数声明
******************************************************************************/
uint32_t CPU_TS_TmrRd(void);
void CPU_TS_TmrInit(void);
//使用以下函数前必须先调用CPU_TS_TmrInit函数使能计数器,或使能宏CPU_TS_INIT_IN_DELAY_FUNCTION
//最大延时值为60秒
void CPU_TS_Tmr_Delay_US(uint32_t us);
#define CPU_TS_Tmr_Delay_MS(ms) CPU_TS_Tmr_Delay_US(ms*1000)
#define CPU_TS_Tmr_Delay_S(s) CPU_TS_Tmr_Delay_MS(s*1000)
#endif
#endif /* __CORE_DELAY_H */
core_delay.c
/************************************************************
* @brief core_delay.c
* @author jiejie
* @github https://github.com/jiejieTop
* @date 2018-xx-xx
* @version v1.0
* @note 使用内核寄存器精确延时
***********************************************************/
#include "./dwt_delay/core_delay.h"
/*
**********************************************************************
* 时间戳相关寄存器定义
**********************************************************************
*/
/*
在Cortex-M里面有一个外设叫DWT(Data Watchpoint and Trace),
该外设有一个32位的寄存器叫CYCCNT,它是一个向上的计数器,
记录的是内核时钟运行的个数,最长能记录的时间为:
60s=2的32次方/72000000
(假设内核频率为72M,内核跳一次的时间大概为1/72M=13.8ns)
当CYCCNT溢出之后,会清0重新开始向上计数。
使能CYCCNT计数的操作步骤:
1、先使能DWT外设,这个由另外内核调试寄存器DEMCR的位24控制,写1使能
2、使能CYCCNT寄存器之前,先清0
3、使能CYCCNT寄存器,这个由DWT_CTRL(代码上宏定义为DWT_CR)的位0控制,写1使能
*/
#if USE_DWT_DELAY
#define DWT_CR *(__IO uint32_t *)0xE0001000
#define DWT_CYCCNT *(__IO uint32_t *)0xE0001004
#define DEM_CR *(__IO uint32_t *)0xE000EDFC
#define DEM_CR_TRCENA (1 << 24)
#define DWT_CR_CYCCNTENA (1 << 0)
/**
* @brief 初始化时间戳
* @param 无
* @retval 无
* @note 使用延时函数前,必须调用本函数
*/
void CPU_TS_TmrInit(void)
{
/* 使能DWT外设 */
DEM_CR |= (uint32_t)DEM_CR_TRCENA;
/* DWT CYCCNT寄存器计数清0 */
DWT_CYCCNT = (uint32_t)0u;
/* 使能Cortex-M DWT CYCCNT寄存器 */
DWT_CR |= (uint32_t)DWT_CR_CYCCNTENA;
}
/**
* @brief 读取当前时间戳
* @param 无
* @retval 当前时间戳,即DWT_CYCCNT寄存器的值
*/
uint32_t CPU_TS_TmrRd(void)
{
return ((uint32_t)DWT_CYCCNT);
}
///**
// * @brief 读取当前时间戳
// * @param 无
// * @retval 当前时间戳,即DWT_CYCCNT寄存器的值
// * 此处给HAL库替换HAL_GetTick函数,用于os
// */
//uint32_t HAL_GetTick(void)
//{
// return ((uint32_t)DWT_CYCCNT*1000/SysClockFreq);
//}
/**
* @brief 采用CPU的内部计数实现精确延时,32位计数器
* @param us : 延迟长度,单位1 us
* @retval 无
* @note 使用本函数前必须先调用CPU_TS_TmrInit函数使能计数器,
或使能宏CPU_TS_INIT_IN_DELAY_FUNCTION
最大延时值为8秒,即8*1000*1000
*/
void CPU_TS_Tmr_Delay_US(__IO uint32_t us)
{
uint32_t ticks;
uint32_t told,tnow,tcnt=0;
/* 在函数内部初始化时间戳寄存器, */
#if (CPU_TS_INIT_IN_DELAY_FUNCTION)
/* 初始化时间戳并清零 */
CPU_TS_TmrInit();
#endif
ticks = us * (GET_CPU_ClkFreq() / 1000000); /* 需要的节拍数 */
tcnt = 0;
told = (uint32_t)CPU_TS_TmrRd(); /* 刚进入时的计数器值 */
while(1)
{
tnow = (uint32_t)CPU_TS_TmrRd();
if(tnow != told)
{
/* 32位计数器是递增计数器 */
if(tnow > told)
{
tcnt += tnow - told;
}
/* 重新装载 */
else
{
tcnt += UINT32_MAX - told + tnow;
}
told = tnow;
/*时间超过/等于要延迟的时间,则退出 */
if(tcnt >= ticks)break;
}
}
}
#endif
/*********************************************END OF FILE**********************/