补充:
红色
部分为重要部分;
绿色
为解析或者补充,如果红色前有绿色,代表文章后有
相关知识
;
蓝色
为代码,如果蓝色内部有红色如上;
紫色
为自我理解与认知不一定正确。
DHT11的简介:
DHT11与单片机之间采用简单的
单总线进行通信,仅仅需要
(2)
一个I/O口。传感器内部湿度和温度数据
(3)
40Bit的数据一次性传给单片机,数据采用
校验和的方式进行校验,有效的保证数据传输的准确性。DHT11的功耗很低,5V电源电压下,
工作平均最大电流为0.5mA;
//补充:如果以后自己进行实验的话,拿到一个元器件首先看
工作电压
DHT11的简介:
工作电压 范围:3.3V~5.5V
工作电流:平均0.5mA
输出:单总线数字信号
测量范围:湿度 5~95%如何,温度-20~60
精度:湿度+-5%,温度+-2。
DHT11的四个引脚(这里的内容自行寻找规格书,自己查找就可以了)
1.VDD 供电3.3V~5.5V DC
2.DATA 串行数据,单总线
3.NC 空脚
4.GND 接地,电源负极
DHT11单总线:
(1)单总线:
单总线即只有一根数据线,系统中的数据交换,控制均由单总线完成。设备(主机或者从机)通过一个
开漏路或
三态端口连至该数据线,以允许设备在不发送数据时能够释放总线,而让其他设备使用总线。
单总线通常要求外接一个约4.7kΩ的上拉电阻,这样,在总线闲置时,其状态为高电平。
由于它们是主从结极,只有
主机呼叫从机时,
从机才能应答,因此主机访问器件都必须
严格遵守单总线序列( 如下: 第一步:初始化。 第二步:ROM命令.跟随需要交换的数据。 第三步:功能命令,跟随需要交换的数据。 每次访问单总线器件.都必须遵守这个命令序列.如果序列出现混乱,则单总线器件不会响应主机。
),如果出现序列混乱,器件将不能响应主机。
(2)单总线传送数据位定义
DATA用于微处理器与DHT11之间的通讯和同步,采用单总线数据格式,
一次传送40位数据,高位先出‘
数据格式:8bit湿度整数数据+8bit湿度小数数据+8bit温度整数数据+8bit温度小数数据+校验位
(如图就是全部加起来判断与前几项和是否相等)
注意:湿度小数部分为0;

(3)主机与从机,主机就是MCU,从机就是DHT11

GPIO:PG11 (Output)
GPIO output level:High (默认高电平状态)
GPIO mode:Output Open Drain(开漏状态)(推挽无法控制)
DHT11实验一:
初始化LED0,LED1,以及串口1(本次暂时不用),DHT11的IO端口
读DHT11数据
如果数据验证正确,点亮LED1,熄灭LED0,并输出当前温度
如果数据验证不正确,点亮LED0,熄灭LED1
.h文件固定开头
#ifndef __DHT11_H
//他的作用是防止重复定义
#define __DHT11_H
#endif
void DHT11_Init() { 初始化内容·}
//代表DHT11初始化,一般情况下
Init()的意思就是初始化。
初始化内容:
GPIO_InitTypeDef GPIO_InitStruct = {0}
//
初始化GPIO(通用输入输出)端口参数的结构体类型
__HAL_RCC_GPIOG_CLK_ENABLE();
//时钟使能端
HAL_GPIO_WritePin(GPIOG, GPIO_PIN_11, GPIO_PIN_SET);
//释放总线后的状态
GPIO_InitStruct.Pin
//什么按键
GPIO_InitStruct.Mode
//什么模式(推挽开漏)
GPIO_InitStruct.Pull
//(是否有上拉电阻或下拉电阻)
GPIO_InitStruct.Speed
//传输速度
//如果不会写按键、模式、速度等等,选中
GPIO_InitTypeDef,F12跳转到定义的位置,找到如1.
@ref GPIO_pins_define2.
@ref GPIO_mode_define,选中
GPIO_pins_define,ctrl+F,点击Find Next就可以找到定义的位置了。
HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);
//调用初始化函数(那个组如A,B,C……,
那个组的按键,模式,电路,速度
)
接下来的步骤需要依靠手册规格书进行
第一步:

第二步:

void DHT11_Start()
{
HAL_GPIO_WritePin(GPIOG,GPIO_PIN_11,GPIO_PIN_RESET);
delay_ms(20);
//这里没有使用系统自带的HAL_delay 想要使用要将自制的delay添加进来(下面有)
//20ms是因为单总线的信号特性
HAL_GPIO_WritePin(GPIOG,GPIO_PIN_11,GPIO_PIN_SET);
}
uint8_t DHT11_wait()
{
uint16_t err=0;
uint16_t timeout=0;
while(HAL_GPIO_ReadPin(GPIOG,GPIO_PIN_11)==GPIO_PIN_SET&&timeout<=35)
{
delay_us(1); //
(3)这里延迟有一点问题,会大致延迟2us在最高等级优化的情况下,具体差异在delay_us代码中,延迟时间越小问题 较大,但是延迟越高,问题较小例100us约为102us左右。
timeout++;
}
if(timeout>35)
{
return err=1;
}
}
第三步

uint8_t DHT11_wait()
{
uint16_t err=0;
uint16_t timeout=0;
while(HAL_GPIO_ReadPin(GPIOG,GPIO_PIN_11)==GPIO_PIN_SET&&timeout<=35)
{
delay_us(1);
timeout++;
}
if(timeout>35)
{
return err=1;
}
timeout=0;
//这里开始
while(HAL_GPIO_ReadPin(GPIOG,GPIO_PIN_11)==GPIO_PIN_RESET&&timeout<=88)
{
delay_us(1);
timeout++;
}
if(timeout>88||timeout<78)
{
return err=1;
}
while(HAL_GPIO_ReadPin(GPIOG,GPIO_PIN_11)==GPIO_PIN_SET&&timeout<=92)
{
delay_us(1);
timeout++;
}
if(timeout>92)
{
return err=1;
}
return err;
}
第四步:


这里可以取个巧,延时40毫秒如果是高电平就是1低电平就是0;
//这里
DHT11.c完成后可以在
主函数
内部执行
连接亮红灯,不连接亮绿灯(无现象为错误)
// DHT11_Start();
// if(DHT11_wait())
// {
// HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5,GPIO_PIN_RESET);
// HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_SET);
// }
// else
// {
// HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5,GPIO_PIN_RESET);
// HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5,GPIO_PIN_SET);
// }
// HAL_Delay(1000);
//读比特:
uint8_t DHT11_Read_Bit(uint8_t *bit)
{
uint8_t err=0;
uint16_t timeout=0;
while(HAL_GPIO_ReadPin(GPIOG,GPIO_PIN_11)==GPIO_PIN_SET&&timeout<=92)
{
delay_us(1);
timeout +=2;
}
if(timeout>92)
{
return err=1;
}
timeout=0;
while(HAL_GPIO_ReadPin(GPIOG,GPIO_PIN_11)==GPIO_PIN_RESET&&timeout<=58)
{
delay_us(1);
timeout +=2;
}
if(timeout>58)
{
return err=1;
}
delay_us(40);
if(HAL_GPIO_ReadPin(GPIOG,GPIO_PIN_11)==GPIO_PIN_RESET)
{
*bit=0;
}
else
{
*bit=1;
}
}
//读字节:
uint8_t DHT11_Read_Byte(uint8_t *byte)
{
uint8_t err=0;
uint8_t bit=0;
uint8_t data=0;
for(uint8_t i=0;i<8;i++)
{
err=DHT11_Read_Bit(&bit);
if(err==1)
{
return err;
}
data|= bit << (7-i);
}
*byte=data;
return err;
}
//读温度湿度
uint8_t DHT11_Read(uint8_t*shidu,float*wendu)
{
uint8_t err=0;
uint8_t buf[5]={0};
DHT11_Start();
err=DHT11_wait();
if(err==1)
{
return err;
}
for(uint8_t i=0;i<5;i++)
{
err=DHT11_Read_Byte(&buf[i]);
if(err==1)
{
return err;
}
}
if(buf[0]+buf[1]+buf[2]+buf[3]==buf[4])
{
*shidu=buf[0];
if(buf[3]&(1<<7))
{
buf[3]&=~(1<<7);
*wendu=buf[2]+buf[3]/100.0;
*wendu=0-*wendu;
}
else
{
*wendu=buf[2]+buf[3]/100.0;
}
err=0;
}
else
{
err=1;
}
return err;
}
DEBUG的使用
点击DEBUG点击后会跳到主函数如果没有跳到,点击魔法棒点击debug,点击Run to main()就会自动跳转到主函数了,如果没有点就会跳到startup.stm32f103.
只有灰色的地方是可以打断点的
黄色的箭头的意思是程序运行到这里
{}->程序运行到下一行
{->}进入程序内部
Peripherals 寄存器PG11可以在GPIOG组看到相关状态
IDR:输入寄存器
ODR:输出寄存器 //写入什么状态ODR就是什么状态
HAL_GPIO_WritePin(GPIOG,GPIO_PIN_11,GPIO_PIN_RESET)//写入状态
HAL_GPIO_ReadPin(GPIOG,GPIO_PIN_11);//读取状态
Call Stack + Locals //堆栈信息
如果想要看全局变量在堆栈信息中:选中全局变量右键 Add'temp'(变量)watch
如果向将10进制变为16进制就右键 Hexadecimal Display
添加.c文件.h文件
第一步:找到工程文件,如果找不到打开mian.c文件,右键main标题 Open Containing Folder,然后新建文件夹将.c/.h文件(模块)放入
第二步:点击三色块,点击新建(分组)起一个名,点击 Add Files(加的是.c文件就是告诉编译器.c文件需要编译)向上返回找到新建的文件夹,找到.c文件,点击OK;
;
第三步添加头文件.h,点击魔法棒点击C++,点击···,点击新建点击···向上返回文件夹,选择文件夹点击OK;
f(频率一秒钟跳转多少次)=72*10^6HZ m=10^6; T=1/F
72MHZ就是1秒跳转72*10^6次 T=1/F 1MHZ就是一微秒就是10-^6秒
解析:
(1)
一个I/O:其实就是输入输出端口
I/O 接口可以分为如下几种类型:
a.按数据传送方式,分为并行接口和串行接口,并行接口一次传输一个字节或一个字的全部位,串行接口一次传送一位。这里指的是外设和接口一侧的传输方式,主机和接口一侧的数据总是并行传输的。
b.按主机访问 I/O 接口的控制方式,分为程序查询接口、中断接口和DMA接口等。
c.按功能选择的灵活性,分为可编程接口和不可编程接口.
(2)
40Bit:
bit(位,又名“比特”):bit的缩写是b,是计算机中的最小数据单位(属于二进制的范畴,其实就是0或者1)
Byte(字节):Byte的缩写是B,是计算机文件大小的基本计算单位。比如一个字符就是1Byte,如果是汉字,则是2Byte。
#include "delay.h"
static uint16_t g_fac_us = 0; /* us延时倍乘数 */
/**
* @brief 初始化延迟函数
* @param sysclk: 系统时钟频率, 即CPU频率(HCLK) 单位MHz
* @retval 无
*/
void delay_init(uint16_t sysclk)
{
SysTick->CTRL = 0; /* 清Systick状态,以便下一步重设,如果这里开了中断会关闭其中断 */
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK_DIV8); /* SYSTICK使用内核时钟源8分频,因systick的计数器最大值只有2^24 */
g_fac_us = sysclk / 8; /* 不论是否使用OS,g_fac_us都需要使用,作为1us的基础时基 */
}
/**
* @brief 延时nus
* @param nus: 要延时的us数.
* @note 注意: nus的值,不要大于1864135us(最大值即2^24 / g_fac_us @g_fac_us = 9)
* @retval 无
*/
void delay_us(uint32_t nus)
{
uint32_t temp;
SysTick->LOAD = nus * g_fac_us; /* 时间加载 */
SysTick->VAL = 0x00; /* 清空计数器 */
SysTick->CTRL |= 1 << 0 ; /* 开始倒数 */
do
{
temp = SysTick->CTRL;
} while ((temp & 0x01) && !(temp & (1 << 16))); /* CTRL.ENABLE位必须为1, 并等待时间到达 */
SysTick->CTRL &= ~(1 << 0) ; /* 关闭SYSTICK */
SysTick->VAL = 0X00; /* 清空计数器 */
}
/**
* @brief 延时nms
* @param nms: 要延时的ms数 (0< nms <= 65535)
* @retval 无
*/
void delay_ms(uint16_t nms)
{
uint32_t repeat = nms / 1000; /* 这里用1000,是考虑到可能有超频应用,
* 比如128Mhz的时候, delay_us最大只能延时1048576us左右了
*/
uint32_t remain = nms % 1000;
while (repeat)
{
delay_us(1000 * 1000); /* 利用delay_us 实现 1000ms 延时 */
repeat--;
}
if (remain)
{
delay_us(remain * 1000); /* 利用delay_us, 把尾数延时(remain ms)给做了 */
}
}
/**
* @brief HAL库内部函数用到的延时
HAL库的延时默认用Systick,如果我们没有开Systick的中断会导致调用这个延时后无法退出
* @param Delay 要延时的毫秒数
* @retval None
*/
void HAL_Delay(uint32_t Delay)
{
delay_ms(Delay);
}
#include "delay.h"
static uint16_t g_fac_us = 0; /* us延时倍乘数 */
/**
* @brief 初始化延迟函数
* @param sysclk: 系统时钟频率, 即CPU频率(HCLK) 单位MHz
* @retval 无
*/
void delay_init(uint16_t sysclk)
{
SysTick->CTRL = 0; /* 清Systick状态,以便下一步重设,如果这里开了中断会关闭其中断 */
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK_DIV8); /* SYSTICK使用内核时钟源8分频,因systick的计数器最大值只有2^24 */
g_fac_us = sysclk / 8; /* 不论是否使用OS,g_fac_us都需要使用,作为1us的基础时基 */
}
/**
* @brief 延时nus
* @param nus: 要延时的us数.
* @note 注意: nus的值,不要大于1864135us(最大值即2^24 / g_fac_us @g_fac_us = 9)
* @retval 无
*/
void delay_us(uint32_t nus)
{
uint32_t temp;
SysTick->LOAD = nus * g_fac_us; /* 时间加载 */
SysTick->VAL = 0x00; /* 清空计数器 */
SysTick->CTRL |= 1 << 0 ; /* 开始倒数 */
do
{
temp = SysTick->CTRL;
} while ((temp & 0x01) && !(temp & (1 << 16))); /* CTRL.ENABLE位必须为1, 并等待时间到达 */
SysTick->CTRL &= ~(1 << 0) ; /* 关闭SYSTICK */
SysTick->VAL = 0X00; /* 清空计数器 */
}
/**
* @brief 延时nms
* @param nms: 要延时的ms数 (0< nms <= 65535)
* @retval 无
*/
void delay_ms(uint16_t nms)
{
uint32_t repeat = nms / 1000; /* 这里用1000,是考虑到可能有超频应用,
* 比如128Mhz的时候, delay_us最大只能延时1048576us左右了
*/
uint32_t remain = nms % 1000;
while (repeat)
{
delay_us(1000 * 1000); /* 利用delay_us 实现 1000ms 延时 */
repeat--;
}
if (remain)
{
delay_us(remain * 1000); /* 利用delay_us, 把尾数延时(remain ms)给做了 */
}
}
/**
* @brief HAL库内部函数用到的延时
HAL库的延时默认用Systick,如果我们没有开Systick的中断会导致调用这个延时后无法退出
* @param Delay 要延时的毫秒数
* @retval None
*/
void HAL_Delay(uint32_t Delay)
{
delay_ms(Delay);
}