本次活动测评开发板STM32WB55 Nucleo Pack由ST意法半导体提供,感谢意法半导体对EEWorld测评的支持!
【实验目的】
· 熟悉STM32WB55的串口模块的配置与使用。
· 掌握DHT11的通信时序
· 掌握使用GPIO处理通信时序
【实验环境】
· NUCLEO-WB55 Nucleo Pack开发板
· Keil MDK-ARM(Keil uVision 5.25.2.0)
· Keil.STM32WBxx_DFP.1.0.0.pack
· 串口调试助手
· DHT11温湿度模块
【实验资料】
· NUCLEO-WB55 Nucleo Pack开发板原理图
· STM32WB55xx Data Sheets
· STM32WB55xx Reference manual(参考手册)
· DHT11用户手册
【实验分析】
原理图:
连接如图所示,灰色线连接到电源的负极,黑色线连接到3.3V正极,白色是数据线,连接到PC10引脚。
DHT11 器件采用简化的单总线通信。单总线即只有一根数据线,系统中的数据交换、控制均由单总线完成。DATA 用于微处理器与 DHT11 之间的通讯和同步,采用单总线数据格式,一次传送 40 位数据,高位先出。
数据格式::8bit 湿度整数数据 + 8bit 湿度小数数据+8bit 温度整数数据 + 8bit 温度小数数据+8bit 校验位。
校验位数据定义:“8bit 湿度整数数据 + 8bit 湿度小数数据+8bit 温度整数数据 + 8bit 温度小数数据”8bit 校验位等于所得结果的末 8 位。
数据时序图
用户主机(MCU)发送一次开始信号后,DHT11 从低功耗模式转换到高速模式,待主机开始信号结束后,DHT11 发送响应信号,送出 40bit 的数据,幵触发一次信采集。
信号发送如图所示:
外设读取步骤
主机和从机之间的通信可通过如下几个步骤完成(外设(如微处理器)读取 DHT11 的数据的步骤)。
步骤一:
DHT11 上电后(DHT11 上电后要等待 1S 以越过不稳定状态在此期间不能发送任何指令),测试环境温湿度数据,幵记录数据,同时 DHT11 的 DATA 数据线由上拉电阻拉高一直保持高电平;此时 DHT11 的DATA 引脚处于输入状态,时刻检测外部信号。
步骤二:
微处理器的 I/O 设置为输出同时输出低电平,且低电平保持时间不能小于 18ms,然后微处理器的 I/O设置为输入状态,由于上拉电阻,微处理器的 I/O 即 DHT11 的 DATA 数据线也随之变高,等待 DHT11 作出回答信号,发送信号如图所示:
步骤三:
DHT11 的 DATA 引脚检测到外部信号有低电平时,等待外部信号低电平结束,延迟后 DHT11 的 DATA引脚处于输出状态,输出 80 微秒的低电平作为应答信号,紧接着输出 80 微秒的高电平通知外设准备接收数据,微处理器的 I/O 此时处于输入状态,检测到 I/O 有低电平(DHT11 回应信号)后,等待 80 微秒的高电平后的数据接收,发送信号如图所示:
步骤四:
由 DHT11 的 DATA 引脚输出 40 位数据,微处理器根据 I/O 电平的变化接收 40 位数据,位数据“0”的格式为: 50 微秒的低电平和 26-28 微秒的高电平,位数据“1”的格式为: 50 微秒的低电平加 70微秒的高电平。位数据“0”、“1”格式信号如图所示:
结束信号:
DHT11 的 DATA 引脚输出 40 位数据后,继续输出低电平 50 微秒后转为输入状态,由于上拉电阻随之变为高电平。但 DHT11 内部重测环境温湿度数据,幵记录数据,等待外部信号的到来。
【实验代码】
本实验在上一实验基础上进行添加
- #include <stdio.h>
- #include <stdint.h>
- #include "uart.h"
- void GPIOSetDir(GPIO_TypeDef *GPIO, int pin, int dir)
- {
- if (dir == 0){
- GPIO->MODER &= ~(0x3UL<<(pin * 2));
- GPIO->OTYPER &= ~(1<<pin);
- }else{
- GPIO->MODER &= ~(0x3UL<<(pin * 2));
- GPIO->MODER |= (0x1UL<<(pin * 2));
- GPIO->OSPEEDR |= 0x3UL<<(pin * 2);
- GPIO->OTYPER &= ~(1<<pin);
- }
- }
- void GPIOSetValue(GPIO_TypeDef *GPIO, int pin, int value)
- {
- if (value == 0){
- GPIO->ODR &= ~(1<<pin);
- }else{
- GPIO->ODR |= (1<<pin);
- }
- }
- void GPIOSetToggle(GPIO_TypeDef *GPIO, int pin)
- {
- GPIO->ODR ^= (1<<pin);
- }
- int GPIOGetValue(GPIO_TypeDef *GPIO, int pin)
- {
- return GPIO->IDR & (1<<pin);
- }
- int DHT11_Read(char *temp, char *humd)
- {
- int i;
- int timeout = 0;
- volatile int low_count, high_count;
- char data[5]; //接收40位数据
-
- //步骤二
- GPIOSetDir(GPIOC, 10, 1);
- GPIOSetValue(GPIOC, 10, 0); //输出低电平
- mdelay(30);
- GPIOSetDir(GPIOC, 10, 0); //设置成输入模式
- //步骤三
- while(GPIOGetValue(GPIOC, 10)){ //等待响应信号
- timeout ++;
- if (timeout > 10000) return -1;
- }
- while(GPIOGetValue(GPIOC, 10) == 0); //等待高电平
- while(GPIOGetValue(GPIOC, 10) != 0); //等待高电平结束
- //步骤四
- for (i = 0; i < 40; i ++){
- low_count = 0;
- high_count = 0;
- while(GPIOGetValue(GPIOC, 10) == 0){ //记录低电平
- low_count ++;
- }
- while(GPIOGetValue(GPIOC, 10) != 0){ //记录高电平
- high_count ++;
- }
- if (low_count > high_count){ //数据0
- data[i/8] &= ~(1<<(7-i%8));
- }else{//数据1
- data[i/8] |= (1<<(7-i%8));
- }
- }
- //步骤五
- while(GPIOGetValue(GPIOC, 10) == 0); //等待低电平结束
-
- if((data[0]+data[1]+data[2]+data[3]) == data[4]){//校验数据
- humd[0]=data[0];
- humd[1]=data[1];
- temp[0]=data[2];
- temp[1]=data[3];
- return 1;
- }else{ //数据校验失败
- return 0;
- }
- }
- int main(void)
- {
- int ret;
- char temp[2]={0}; //保存温度数据
- char humd[2]={0}; //保存湿度数据
- USART1_Init(115200);
- while(1){
- ret = DHT11_Read(temp, humd);
- switch (ret){
- case -1:
- printf("Timeout\n");
- break;
- case 0:
- printf("Data Error\n");
- break;
- case 1:
- printf("humd:%d.%d temp:%d.%d\n", humd[0], humd[1]
- , temp[0], temp[1]);
- break;
- }
- mdelay(1000);
- }
- }
复制代码
【实验现象】
· 连接开发板,并打开串口调试助手
· 在终端中显示如下:
此内容由EEWORLD论坛网友lvxinn2006原创,如需转载或用于商业用途需征得作者同意并注明出处