单总线数据协议,顾名思义,就是通过一根线实现设备和控制器之间的通信,此根线同时承担着时钟和数据线的角色。

本文通过STM32F0xx单片机实现DHT11温湿度传感器单总线通讯为例进行介绍,关于DHT11详细内容,请查看手册(点击下载附件),在此也向手册的作者表示感谢。

1. 典型应用电路

为了保持芯片工作电平的稳定,强烈建议总线上添加上拉电阻,尽管STM32等系列单片机可配置为上拉模式。

2. 通讯过程

整个通信过程可分为四个步骤:

(1)总线空闲状态为高电平,主机将总线拉低必须大于18ms(发送起始信号),等待DHT11检测到起始信号;

(2)DHT11接收到主机的开始信号后,等待起始信号结束(主机将电平拉高延迟20-40us);

(3)此时主机设为输入模式,当检测到总线为低电平(说明DHT11已响应),低电平持续80us后,DHT11将总线拉高80us,准备开始传递数据;

(4)每一bit数据以50us低电平时隙开始,高电平长短决定数据为是0还是1,格式见下图:

数据0格式:

数据1格式:

(5)当数据传输完成后,等待从机将总线拉低50us后,主机将主线拉高以结束通讯。

3. 逻辑分析仪捕获时序

(1)从机应答信号

(2)读取一个字节

(3)'0'信号

(4)'1'信号

(5)整个数据包时序

4. STM32F0xx读取源码

#include "stm32f0xx.h"
#include "delay.h"


/** 采用DHT11温湿度模块,使用单总线模式 **/

//DHT输出模式  -> PA4
void static GPIO_DHT_Out_Mode(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; //开漏输出
	GPIO_Init(GPIOA,&GPIO_InitStructure);
}

//DHT输入模式  -> PA4
void static GPIO_DHT_Input_Mode(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;  //浮空输入
	GPIO_Init(GPIOA,&GPIO_InitStructure);
}

//DHT数据管脚读取  -> PA4
uint8_t static DHT_READ()
{
	return GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_4);  //读取SDA数据
}

//SDA输出为高或者低, 对应管脚 -> PB11
void static DHT11_PIN(uint8_t status)
{
	if(status==1)
	{
		GPIO_SetBits(GPIOA, GPIO_Pin_4);   //SDA为高
	}
	else if(status==0)
	{
		GPIO_ResetBits(GPIOA, GPIO_Pin_4);   //SDA为低
	}
	else {}
}

//向DHT11写入一个读取数据的引导码
void static DHT11WriteStart()
{
	GPIO_DHT_Out_Mode();
	DHT11_PIN(1);
	DHT11_PIN(0);
	delay_ms(20);    //拉低电平至少18ms,最大30ms
	DHT11_PIN(1);   //拉高等待DHT11应答
	delay_us(30);
}

//向DHT11结束信号
void static DHT11WriteStop()
{
	while(DHT_READ() == 0); 
	GPIO_DHT_Out_Mode();     //输出模式
	DHT11_PIN(1);            //释放总线
}


//从DHT11中读取到一个字节
// bit 0  -> 54us的低电平 + 23-27us的高电平
// bit 1  -> 54us的低电平 + 68-74us的高电平
uint8_t static DHT11ReadByte(void)
{
	uint8_t data=0,i;
	for(i=0; i<8; i++)
	{
		data <<= 1;
		while(DHT_READ() == 0);   //54us的低电平,等待变高电平
		delay_us(35);
		if(DHT_READ() == 1) 
		{
			data |= 0x01;
			while(DHT_READ() == 1); 
		}
	}
	return data;
}


//数据管脚  -> PA4
void Humid_GPIO_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	GPIO_SetBits(GPIOB,GPIO_Pin_4|GPIO_Pin_5); 	 //总线拉高
}


//从传感器中读取的原始数据,详细查看DHT11-新款温湿度传感器.pdf
void static ReadOrignData(uint8_t *buf)
{
	//从传感器中读取原始数据
	DHT11WriteStart();//给读取前导信号
	GPIO_DHT_Input_Mode();//设置端口为输入状态
	if(!DHT_READ())
	{
		while(DHT_READ()==0); //低电平的响应信号,83us
		while(DHT_READ()==1); //紧接着是87us的高电平数据准备信号

		buf[0] = DHT11ReadByte();//湿度高8位
		buf[1] = DHT11ReadByte();//湿度低8位
		buf[2] = DHT11ReadByte();//温度高8位
		buf[3] = DHT11ReadByte();//温度低8位
		buf[4] = DHT11ReadByte();//校验和
	}
	DHT11WriteStop();
}

//读取最终转换的值
void ReadHUMIData(uint8_t *temp , uint8_t *humi)
{
	uint8_t buffer[5];
	uint8_t i,sum=0;
	ReadOrignData(buffer);  //原始传感器数据
	
	for(i=0;i<4;i++)        //求校验和
	{
		sum += buffer[i];
	}
	if(sum == buffer[4])   //校验和通过
	{
		//(1)湿度转换,湿度的小数为零
		*humi = buffer[0];    
		//(2)温度转换,温度小数有效
		*temp = buffer[2] + buffer[3]/10.0;
	}	
}
注意:本站所有文章除特别说明外,均为原创,转载请务必以超链接方式并注明作者出处。 标签:嵌入式,接口协议