单总线协议(DHT11温湿度模块)
单总线协议的原理和应用
单总线协议指的是只采用一根信道来进行数据传输,通信指的是双方(MCU与传感器)通过一根信道进行数据交互,所以按照数据的传输方向,只能采用半双工通信方式,比较典型的传感器就是温度传感器(DS18B20、DHT11......)
基本特征





代码示例:
/*--------------------------------------------------------------
* @brief DHT11 起始信号的产生与引脚方向切换(PB13)
* @param 无
* @retval 无
* @note 拉低总线 20ms(满足 >18ms 复位要求)-> 释放总线 13us -> 切为输入,等待从机应答
*--------------------------------------------------------------*/
void DHT11_Init()
{
//1.打开GPIO端口的时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
//2.定义GPIO结构体并赋值,初始化
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //引脚输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_Init(GPIOB, &GPIO_InitStructure);
//3.默认拉高电平
GPIO_SetBits(GPIOB,GPIO_Pin_13);
//4.主机起始信号拉低20ms
GPIO_ResetBits(GPIOB,GPIO_Pin_13);
delay_ms(20);
//5.主机释放单总线时间13us
GPIO_SetBits(GPIOB,GPIO_Pin_13);
delay_us(13);
//6.设置为输入模式
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; //引脚输入
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
/*--------------------------------------------------------------
* @brief 检测 DHT11 的应答信号(约 80us 低电平 + 80us 高电平)
* @param 无
* @retval true 应答正常;false 应答超时,传感器未接或损坏
* @note 低电平、高电平的等待上限分别取 83us 与 87us,略大于手册标称值
*--------------------------------------------------------------*/
bool DHT11_Return()
{
uint8_t cnt = 0;
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_13) == 0 && cnt<=83)
{
cnt++;
delay_us(1);
}
//等待超时
if(cnt > 83)
{
USART1_SendString("Sensor not responding\r\n");
return false;
}
//计数器清零
cnt = 0;
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_13) == 1 && cnt<=87)
{
cnt++;
delay_us(1);
}
//等待超时
if(cnt > 87)
{
USART1_SendString("Sensor not responding\r\n");
return false;
}
return true;
}
/*--------------------------------------------------------------
* @brief 读取 DHT11 输出的 1 个数据位
* @param 无
* @retval 1 表示该位为 1,0 表示该位为 0
* @note 每位均以 50us 低电平起始;高电平持续 26~28us 判 0、约 70us 判 1,故延时 40us 后采样即可区分
*--------------------------------------------------------------*/
uint8_t DHT11_Getbit()
{
//等待低电平到来
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_13) == 1);
//等待高点平到来
while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_13) == 0);
delay_us(40);
if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_13) == 0)
return 0;
else
return 1;
}
/*--------------------------------------------------------------
* @brief 读取 DHT11 输出的 1 个字节(高位在前)
* @param 无
* @retval 组合后的 8bit 数据
* @note 循环 8 次调用 DHT11_Getbit,先左移一位再或入当前位
*--------------------------------------------------------------*/
uint8_t DHT11_Getbyte()
{
uint8_t data = 0;
for(char i=0;i<8;i++)
{
data = data<<1;
data |= DHT11_Getbit();
}
return data;
}
/*--------------------------------------------------------------
* @brief 读取一次完整的 DHT11 数据帧(40bit)并做校验
* @param buf 输出缓冲区,依次为 buf[0]湿度整数 / buf[1]湿度小数 / buf[2]温度整数 / buf[3]温度小数 / buf[4]校验和
* @retval true 校验通过,数据有效;false 无应答或校验失败,buf 内容不可信
* @note 校验规则:buf[0]+buf[1]+buf[2]+buf[3] 的低 8 位应等于 buf[4];相邻两次采集间隔需大于 1s
*--------------------------------------------------------------*/
bool DHT11_Getdata(uint8_t buf[5])
{
//1.初始化并给出起始信号
DHT11_Init();
//2.判断传感器是否有响应
if(DHT11_Return() == false )
{
return false;
}
else
{
//3.循环读取数据
for (char i=0;i<5;i++)
{
buf[i] = DHT11_Getbyte();
}
}
//4.校验数据
if(buf[4] == buf[0]+buf[1]+buf[2]+buf[3])
{
return true;
}
else
{
return false;
}
}
/*--------------------------------------------------------------
* @brief 程序入口:初始化串口后循环采集温湿度并上报
* @param 无
* @retval int 标准返回值,本函数为死循环,不返回
* @note 采集周期 2s,满足 DHT11 采样间隔大于 1s 的要求;当前未判断 DHT11_Getdata 的返回值,采集失败时会重复打印旧数据
*--------------------------------------------------------------*/
int main()
{
uint8_t dhtbuf[5] = {0};
uint8_t ubuf[128] = {0};
//1.硬件的初始化
Usart1_Init(9600);
while(1)
{
DHT11_Getdata(dhtbuf); //获取了一次温湿度数据
sprintf((char *)ubuf,"temp = %d ℃, humi = %d %%RH\r\n",dhtbuf[2],dhtbuf[0]);
USART1_SendString((char *)ubuf);
delay_s(2);
}
}
实验现象:

作者:bbzhu04@gmail.com ,多有不妥之处,望批评指正
本文原创,欢迎转载;转载请在文章显著位置标明原文链接: href="https://www.cnblogs.com/zhubb/p/23472630"
保留所有权限,未经许可禁止篡改、商用发布。

浙公网安备 33010602011771号