单总线协议(DHT11温湿度模块)

单总线协议的原理和应用

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

基本特征
image

image

image

image

image

代码示例:


/*--------------------------------------------------------------
 * @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);
	}
	
}


实验现象:
image

posted @ 2026-10-28 22:15  !城北徐公.!  阅读(7)  评论(0)    收藏  举报