通用异步串口实现单总线硬件控制器的研究和实现方法(菜农)
转载本帖应注明雁塔菜地: http://blog.**/hotpower/ 菜农HotPower@126.com 2008.2.29(四年等一回的日子) 于雁塔菜地
常用的单总线有2大类: 1-Wire和HDQ16
1-Wire和HDQ16都采用单线数据双向通讯,特别是有些1-Wire系列器件甚至不需电源。 单总线和I2C,SPI相比接线简单,但时序要求很严格,一般采用IO模拟定时器辅助的方法.
做主机其缺点还不太明显,若作为从机设备时,IO模拟一般还需外部中断协助. 最大特点是CPU占有率太高.甚至根本无法实现.故双机之间很少采用单总线通讯, 更别指望一主多从或多主多从通讯.
在TI的HDQ16数据文档中,有文介绍用通用异步串口实现单总线硬件控制器. 但感觉只是理论推导,实际有处重大(犯罪的)错误,故感觉没经过实战论证. 也怀疑作者故意隐瞒重点。 因为下波特率为57600,8位数据位,2个停止位时,根本不可能产生190uS以上的低电平. 11*17.3uS=190.3uS不错,可停止是高电平,故只能(1+8)*17.3uS=155.7uS. 即使使用的UART可以提供16位数据,依然不能成立,为何看完此文后便知.
通用异步串口实现单总线硬件控制器的接线方法很简单,为防止TXD和RXD线与, TI采用非门加MOS管实现TXD漏极开路,菜农采用二极管.结果一样.
这样加上拉电阻并短接RXD后即构成单总线1-Wire或HDQ16.
一.单总线时序的研究:
单总线一般由3大时序构成(简化): 1.总线复位 因为在空闲时,总线由上拉电阻拉到高电平,这个状态很有"物理意义"---对从机充电. 此时从机实际是在等待或休眠状态,故必须要由外部唤醒.
为保证从机的唤醒和联接的可靠及加速单线通讯的速率,故需长时间的复位(拉低电平). 使其复位周期和数据周期拉大距离以便区分。
HDQ16的总线复位周期由190uS的低电平和45uS的高电平组成,在高电平期间不需从机应答. 1-Wire的总线复位周期由480uS的低电平和480uS的高电平组成,在高电平期间可从机应答.
1-Wire与HDQ16的最大不同在于总线复位周期里包含从机的应答时序.
2.写'0' 在空闲中总线被下拉(同步)就表示启动单总线的数据操作过程的开始. 由于总线被下拉导致通讯而阻塞(线与),故本时序只能作为主机操作过程.读回数据恒为0.
3.写'1' 这个操作实际是写'1'和读'0'及读'1'三个时序的综合. 因为TXD的开漏输出,故单总线实际就是51的准双向IO. 只有在此期间方可读取从机数据。
将写'0'和写'1'时序进一步合并后,即可实现单总线读写一体化操作.
二.串口时序的研究: 串口一般也由3大时序构成:
1.总线复位(同步) 在空闲中总线被下拉(同步)就表示启动串口的数据操作过程的开始.即常说的"起始位S". 它也标志了双方异步过程的同步开始.双方的计时器也开始计时工作.
2.读写数据 由于无硬件同步信号,故双方必须对每个数据位的周期进行事先约定。数据串D0D1..DX
3.总线释放(停止) 为了下次(帧)通讯,必须释放总线。从而留给从机处理时间,宽度可调. MCU一半都有1,2位停止位,1.5位很少.即常说的"停止位P".
三.UART和1-Wire/Hdq16的不同点和共同之处 从以上分析可以看出它们的共同之处: 1.空闲状态到工作状态的切换都是采用拉低总线来唤醒从机以实现同步.(起始位S) 2.每位数据都是由一定宽度的高低电平组成.并严格保持一定的有效时间.(数据串D0D1..DX) 3.释放总线结束表示数据的结束.(停止位P)
最大的不同在于"每帧"UART是多位(5,6,7,8等)数据位,而单总线为一位数据. 当UART数据位串为连续的0或1时,就可组成一位单总线的数据位!!! 即该方法是用数据位数的宽度来凑够单总线的时序脉宽.
四.实现方法举例(起始位S为低电平,停止位P为高电平,N为数据位数,B为停止为数)
例:(S=0,P=1,N=5,B=2)BPS=38400 T=34.6us (HDQ16复位时序) 0x00 ->S 00000 PP 即6T个0,2T个1 波形: 低电平207uS, 高电平69.2uS 周期:276.2uS
例:(S=0,P=1,N=6,B=1)BPS=38400 T=34.6us (HDQ16复位时序) 0x00 ->S 000001 P 即6T个0,2T个1 波形: 低电平207uS, 高电平69.2uS 周期:276.2uS
例:(S=0,P=1,N=8,B=2)BPS=38400 T=34.6us (HDQ16复位时序) 0x00 ->S 00000111 PP 即6T个0,5T个1 波形: 低电平207uS, 高电平173uS 周期:380.6uS
例:(S=0,P=1,N=8,B=2)BPS=38400 T=34.6us (HDQ16复位时序) 0x00 ->S 00000000 PP 即9T个0,2T个1 波形: 低电平311.4uS, 高电平69.2uS 周期:380.6uS
例:(S=0,P=1,N=8,B=2)BPS=57600 T=17.3us (HDQ16写'1'/读'0'/读'1'时序) 0xfe ->S 01111111 PP 即2T个0,9T个1 波形: 低电平34.6uS, 高电平155.7uS 周期:190.3uS
例:(S=0,P=1,N=8,B=2)BPS=57600 T=17.3us (HDQ16写'0'时序) 0xc0 ->S 00000011 PP 即7T个0,4T个1 波形: 低电平121.1uS,高电平69.2uS 周期:190.3uS
例:(S=0,P=1,N=8,B=1)BPS=9600 T=104us (1-Wire复位/应答时序) 0xf0 ->S 00001111 P 即5T个0,5T个1 波形: 低电平520uS, 高电平520uS 周期:1040uS
例:(S=0,P=1,N=6,B=1)BPS=115200 T=8.68us (1-Wire写'1'/读'0'/读'1'时序) 0x3f ->S 111111 P 即1T个0,7T个1 波形: 低电平8.68uS, 高电平60.76uS 周期:69.44uS
例:(S=0,P=1,N=6,B=1)BPS=115200 T=8.68us (1-Wire写'0'时序) 0x3f ->S 000000 P 即7T个0,1T个1 波形: 低电平60.76uS, 高电平8.68uS 周期:69.44uS
五.程序实现方法(以硬件调试通过的HDQ16为例)
1.结构配置 LPC_Uart_Config_t Uart1Config = {BD57600, WordLength8, true, false, ParitySelOdd, false, true, FIFORXLEV2, IER_RBR | IER_RLS, POLLING_MODE,}; 2.HDQ16复位时序 void HDQStart (void);
void HDQStart (void) { LPC_INT8U ch; Uart1Config.BaudRate = BD38400;//改写波特率用于发送复位信号 UART_Init(UART1);//串口初始化 UART_PutCharByPolling(UART1, 0);//发送低电平311.4uS, 高电平69.2uS ch = UART_GetCharByPolling(UART1);//清空并监测应答或干扰信号等 Uart1Config.BaudRate = BD57600;//改写波特率用于发送接收数据 UART_Init(UART1);//串口初始化 }
3.串口收发一体化程序代码
LPC_INT8U HDQReadWriteByte (LPC_INT8U data) { LPC_INT8U i, ch; LPC_INT8U value = 0; for (i = 0; i < 8; i ++) { if (data & 1) { UART_PutCharByPolling(UART1, 0xfe);//1 } else { UART_PutCharByPolling(UART1, 0xc0);//0 } ch = UART_GetCharByPolling(UART1); data >>= 1; value >>= 1; if (ch > 0xf8) { value |= 0x80; } } return value; }
LPC_INT8U HDQReadWriteWord (LPC_INT16U data) { LPC_INT8U i, ch; LPC_INT16U value; for (i = 0; i < 16; i ++) { if (data & 1) { UART_PutCharByPolling(UART1, 0xfe);//1 } else { UART_PutCharByPolling(UART1, 0xc0);//0 } ch = UART_GetCharByPolling(UART1); data >>= 1; value >>= 1; if (ch > 0xf8) { value |= 0x8000; } } return value; }
4.读写应用数据 LPC_INT16U HDQReadWriteData (LPC_INT8U command) { //LPC_INT8U valuel; //LPC_INT8U valueh; LPC_INT16U value; HDQStart();//190us HDQReadWriteByte(command); // valuel = HDQReadWriteByte(0xff);//收发一体化程序读要写'1' // valueh = HDQReadWriteByte(0xff);//收发一体化程序读要写'1' // value = (valueh << 8) | valuel; value = HDQReadWriteWord(0xffff);//收发一体化程序读要写'1' return value; }
5.单总线的协议硬件控制器终成正果 LPC_INT16U data = 0; data = HDQReadWriteData(0x1c);//读用户系列号
由于采用了串口作为单总线的协议硬件控制器,故在传送期间不怕中断,时序不会错乱. 这里主要列举了菜农本人硬件通过的HDQ16,近日将对1-Wire系列进行硬件测试.
同理可知,SPI硬件模块也作为单总线的协议硬件控制器,MOSI-TX,MISO-RX |