嵌入式接口协议(6)—单总线通讯协议
admin 于 2018年03月17日 发表在 嵌入式开发笔记
单总线数据协议,顾名思义,就是通过一根线实现设备和控制器之间的通信,此根线同时承担着时钟和数据线的角色。
本文通过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; } }