一、同步与异步
同步(synchronous)和异步(asynchronous)之间主要区别就是通信双方有没有时钟线为他们传输同步信号。
同步通信具有时钟同步信号,时钟信号起约定作用,通信双方在一个时钟内执行特定的工作。
异步通信没有时钟同步信号,所以通信双方要想传输的数据不发生错误,必须要提前规定一系列的协议。数据线什么状态表示起始位,什么状态表示逻辑1和逻辑0,什么状态表示数据传输结束,都需要提前规定,数据传输时,通信双方严格按照规定操作。
例如串口,串口的数据是通过时间来决定的。
数据线拉低一个time,认为是起始位,后面的就是数据位,在后面的每个1/2Time时刻,接收方采集数据线上的电平。然后采集8个bit后,认为下一个Time的数据线状态时校验位,没有校验位则是停止位。
串口的逻辑电平就是高电平和低电平,但是有的比较复杂的通信协议不是这样的,他们按照电平高低的时间来规定逻辑1和0。
以红外遥控器解码器为例。
起始信号:解码器发出一个9ms的低电平、4.5ms的高电平,用来表示开始数据传输。
逻辑1:0.56ms的低电平+1.69ms的高电平
逻辑0:0.56ms的低电平+0.56ms的高电平
收发双方根据上面的协议进行数据传输。
同步和异步通信各有优缺,同步传输速度快,抗干扰能力强,但是需要的信号线多,因为他们有时钟信号。异步通信只需要数据线传输数据即可,所以用到的信号线相对较少,但是因为异步通信的每一帧数据都要根据特定的协议来进行编码解码,所以通信速度要根据协议而定,相对较慢但是能满足大部分场景需求,抗干扰能力也与协议的复杂程度有关。
二、一条线实现数据收发
若两个模块之间使用一条线进行双向数据传输,那么肯定会面临这样的问题:某一时刻,模块A输出高电平,模块B输出低电平,这时电平就会发生冲突,可能会烧毁电路。
所以要让两个模块之间,不能在同一时刻驱动电路。但是只有一条数据线,没有时钟信号控制两个模块,模块之间无法约定驱动时间,所以要从硬件电路入手解决。
上图中,GPIO连接在三极管的基极,然后驱动三极管控制数据线。
GPIOA=1,三极管导通,数据线低电平
GPIOA=0,三极管截止,数据线高电平
GPIOB=1,三极管导通,数据线低电平
GPIOB=0,三极管截止,数据线高电平
真值表如下:
GPIOA | GPIOB | DATA |
---|---|---|
0 | 0 | 1(Vcc),数据线接上拉电阻电源 |
0 | 1 | 0,数据线接地 |
1 | 0 | 0,数据线接地 |
1 | 1 | 0,数据线接地 |
根据上面四种情况,可以看到当A和B同时操作数据线的时候,只有当双方都不驱动三极管的时候,数据线才为高电平。这样即使双方同时驱动数据线也不会烧毁电路。
根据上面的电路,AB双方就可以放心的传输数据了,但是还是要根据双方的协议来传输数据。
比如数据线默认高电平并保持,突然A操控数据线拉低并保持一个Time然后放弃控制权,B收到低电平信号,在Time时间之后,操控数据线拉低并保持Time,给A一个回应,然后放弃控制权,之后A就可以向B传输数据了。
可以看到,一条数据线能实现半双工通信模式。
三、几种传感器的介绍
1、DHT11温湿度传感器
DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。
查询DHT11的数据手册可以知道关于DHT11的连接方式和数据格式等信息。
典型应用电路:
可以看到,DHT11与单片机之间只用了一条线连接,用于收发数据。
通信过程:
1、数据线默认状态高电平,主机拉低然后在拉高表示要准备好接收数据了,然后主机会等待DHT11响应。
2、DHT11拉低在拉高信号线,回应主机表示DHT11已经准备好了,然后开始传输数据。
3、DHT11传输数据。
4、DHT11拉低50us信号线,表示数据传输结束,然后主机把数据线拉高到默认状态。
上面是起始信号和应答信号的时间,必须保证他们拉高拉低的时间符合规定才能够正常识别信号。
上面是DHT11数据传输协议中逻辑0和逻辑1的表示方法。
对于DHT11来说,他的时序都是us级别的,时间很短,所以需要用高精度定时器来检测数据。
2、DS18B20
DS18B20是一种单总线数字温度传感器,体积小,精度高。由于是DN单总线,所以时序要求很严格。
引脚配置图:
内部结构:
DS18B20内部主要由64位ROM和一个9字节暂存器组成。
1、64位ROM:存放DS18B20的64位地址序列号,当多个DS18B20挂载在同一条总线上的时候,可以根据序列号的不同,使能对应的DS18B20。其中低8位为DS18B20单总线温度传感器的家族号;高8位为CRC循环冗余校验码,用以校正前56位是否正确;中间的48位是一个唯一的序列号。
2、 9字节暂存器相当于RAM,用来保存一些使用时的数据,包括温度值、上限温度报警值、下限温度报警值、配置寄存器、8位CRC发生器。
RAM共9字节,每个字节保存不同的数据,如下图所示。
byte0 | 温度数据低八位 |
---|---|
byte1 | 温度数据高八位 |
byte2 | 用户设置最高报警值,可以与EEPROM双向覆盖 |
byte3 | 用户设置最低报警值,可以与EEPROM双向覆盖 |
byte4 | 配置寄存器,用来配置转换精度,9~12 位精度。 |
byte5 | 预留位 |
byte6 | 预留位 |
byte7 | 预留位 |
byte8 | 校验分析,提高数据准确性 |
对于DS18B20的使用,他提供了5个ROM指令和6个RAM指令。
ROM指令:
Read ROM [33H] | 允许总线控制器读取DS18B20的ROM序列 |
---|---|
Match ROM [55H] | 匹配ROM 命令,后面跟着64位ROM序列,此后所有操作都是对该器件进行 |
Skip ROM [CCH] | 忽略ROM, 发出的命令将对总线上所有器件起作用。 特别适用总线上只有一个DS18B20的时候 |
Search ROM [F0H] | 搜索ROM,确定挂接在同一总线上DS18B20的个数,识别64位ROM地址 |
Alarm ROM [ECH] | 警报搜索 执行此命令后,只有温度超过设定值上限或下限的芯片才会做出响应 |
RAM指令:
Convert Teamperature [44H] | 启动一次温度转换,结果存入内部RAM |
---|---|
Write Scratchpad [4EH] | 向内部暂存器写入数据。可以写byte2、3、4这三个字节 |
Read Scratchpad [BEH] | 读整个内部RAM9字节的数据 |
Copy Scratchpad [48H] | 把内部RAM中的byte2、3、4字节数据存入EEPROM |
Recall EEPROM [B8H] | 从EEPROM中把TH、TL、配置值写入内部RAM的byte2、3、4 |
Read Power Supply [B4H] | 获取DS18B20的电源模式:1-电源供电;0-数据线供电 |
熟悉了上面的指令,就可以对DS18B20进行一次访问了。
1、初始化:主机先拉低总线然后释放,然后DS18B20拉低总线在释放。
当主机检测到DS18B20这个先低后高的时序,就认为初始化成功。
2、发送ROM指令,确定要访问的DS18B20
3、发送RAM指令,实现具体功能,比如启动温度转换,读、写RAM。
每次对DS18B20进行操作的时候都要重复上面三步。
写数据:
写0:主机先拉低总线15us,在后面的45us中,继续拉低总线。
写1:主机先拉低总线15us,在这15us内,主机需要释放总线,然后总线在上拉电阻的作用下电平被拉高。
注意每个写操作之间必须有至少1us左右的间隔。
读数据:
读0:主机先发出大于1us,小于15us的低电平,然后放弃控制总线,此时总线由上拉电阻拉高。在后面的45us,由DS18B20拉低总线,主机读取数据0。
读1:控制器发出大于1us,小于15us的低电平,在这15us内,DS18B20把数据线驱动为高电平,后续45us内,主机读取数据1。
注意:所有的读时序都是由主机拉低总线至少1us产生,在主机发出低电平信号15us后,DS18B20发出的数据才有效,所以要在15us后进行主机采样。
3、红外数据协议
红外协议有:NEC、SONY、RC5、RC6等,常用的就是NEC。下面讲解下NEC的数据协议。
首先了解下NEC协议的逻辑1和逻辑0是如何表达的。
逻辑1:0.56ms低电平加2.25ms高电平,高电平时间是低电平时间的3倍。
逻辑0:0.56ms低电平加0.56ms高电平,高电平和低电平时间相同。
可以看到,无论是逻辑1还是逻辑0,他们最开始都是0.56ms的低电平。
NEC协议的一帧数据由一下及部分组成,由引导码、地址码、数据码组成。
引导码就相当于起始码,告诉设备准备开始传输数据了。
地址就是红外遥控器的编号,ID号,不同类型的发生器的地址码也不相同。
数据就是具体的传输数据。
其中对地址和数据进行取反操作是为了校验数据的准确性,减小数据传输的出错率。
引导码数据格式:
引导码是由9ms的低电平和4.5ms的高电平组成。
重复码:
如果遥控器上的键保持按下状态,则会在一帧数据发送完后发出重复码,表示当前按下的按键还是上一次按下的按键。
通常在结束脉冲发出后约40ms。重复代码将继续以 108 毫秒的间隔发出,直到按键松开。
重复代码按顺序包含以下内容:
重复码实例(绿色时低电平,白色是高电平):
可以看到,在一帧数据发送完后40ms左右,开始每隔108ms发送一个重复码。
注意:在一个数据帧和重复码后边,都有一个562.5us的低电平,表示结束
所以一个完整的数据帧和重复码应该是以下的样子:
可以发现,如果以562.5us作为单位T,那么上面所有的码和逻辑1、0都是562.5us的倍数。
2.25ms=4T
4.5ms=8T
9ms=16T
在编程的时候,可以设置引脚为双边沿触发中断,每次进入中断,计算当前中断和上次中断的时间差,就得到了脉冲的宽度,在主程序中分析时间差数据,就可以解析到数据。