模拟串口的正确方式
异步串行通讯,由于数据中不带时钟信号,所以要求发送和接收双方必须事先约定好一个波特率,同时使用自己的时钟定时,而且要保证一定的准确度。
串行通讯每字节发送一个起始位用于表示信号的起始和同步检测。
发送比较容易,按照制定的时间间隔,将数据输出到IO上即可。可以用延时方法,定时方法等。
由于如果有中断存在,会影响延时的精度,所以延时的时候,一般都要关中断。所以认为这时候MCU无法处理其他任何人物。
不过发送数据用延时方式也未尝不可,
虽然发送的时候占用100%的MCU,但是由于只有在有数据发送时才占用MCU时间,所以如果安排合理也可以使用。而且波特率越高,延时所占用时间越少。
接收过程则复杂的多。接收方式也可以分成,延时,定时,和中断。
由于不能确定对方什么时候发送数据,所以延时方式基本是不能用于接收的。否则MCU什么都处理不了。
只剩下定时和中断两种选项。
定时方式需要一个可产生定时中断的定时器,或者系统的定时器的分频。
中断方式需要输入管脚具有外部中断功能,用于检测起始位。接收时还需要一个定时器,或者不用定时器,用延时方式。
下面先讨论定时方式接收。
1,定时器方式
由于保证双方时钟完全一致是不可能的,所以必须考虑到时钟频率误差的问题。这也是网上流传的各种模拟串口程序的普遍短板。
下面按照1个起始位,9个数据位,1个停止位,一共11位,9600bps来讨论。
理想情况,我们应该采集每一位的中点,假设起始位是从50%中点采集,要保证最后一位还在0.1%-99.9%的范围内,则允许4.5%以内的时钟误差。
如果起始位检测不能保证在50%中点,则实际允许的时钟误差还要小。
假设起始位定到了1%的位置,则允许的时钟误差为0.09%,如果起始位定到了0.1%的位置,则允许时钟误差为0.009%,这根本就不能用。
定时器起始位的检测,
如果用1倍于波特率的速度进行检测,则有可能检测到0.1%的位置,也可能检测到99.9%的位置。甚至可能检测不到,不可行。
用2倍波特率的速度进行检测。有可能检测到0.1%的位置,也可能检测到49.9%的位置。
第二次检测,可能检测到50.1%的位置,也可能检测到99.9%的位置。两次采集位置都不能用。
用3倍波特率的速度进行检测,第一检测有可能检测0.1%的位置,也可能检测到33.3%的位置,
第二次检测,可能位置为 33.3%-66.6%,
第三次检测,可能位置为 66.6%-99.9%
我们取第二次检测位置作为起始位。这样允许的余量还有33.3%,除以11位,还有3%的时钟允许误差。
如果用4倍波特率速度进行检测,实际上效果还不及3倍波特率的方法。允许时钟误差会变成25%/11=2.3%
用5倍波特率速度进行检测,允许时钟误差会变成40%11=3.6%
硬件UART是用16倍波特率检测,采集第7,8,9位,然后3取2的方法,即可以允许第7次采样是错误的,允许时钟误差为7/16/11=3.98%;
如果使用更高的倍数采样,极限是逼近4.5%;
由此来看来,使用3倍波特率进行采样,是一个很具有性价比的方法。
中断方式,配合定时器,可以独立开始,停止,重置。
由于中断方式,检测起始位,每次检测到的位置都是0.1%,我们只要延时半位的时间,即可得到50%的起始位中点位置。
假设9600bps的串口通讯。
所以,仅第一次定时器速度为19200次/S,用于检测是否是真的起始位,剩下的都为9600次/S即可,
也可以设定定时器速度一直为9200次/S,只在第1,3,5,7,9,11,13,15,等次数采样数据。
中断方式,不使用定时器。
当然,有了外部中断的配合,能确定起始位了,我们甚至可以不用定时器,只使用延时的办法来接收数据。
只不过在接收时,MCU又占用100%,不符合"中断时间越短越好的原则",不过在波特率足够高时,延时时间可以很短,反而效果更好。
所以,在波特率比较低时,我们可以采用定时器,在波特率比较高时,采用中断加延时。
1,用定时器实现的120-38400bps全双工模拟串口。
2,用1个外部中断实现的115200bps半双工模拟串口;