|
引 言 假定串口发送的数据帧结构为:
其中:包头用于同步,一般是一个或多个ASCII字符,本文中假定数据帧同步头有2字节(0xAA、0x55);包长表示数据包中除去包头和包长的字节数,一般用约定好的几个字节表示;类型为通信协议里规定的命令类型;数据为应发送的主要信息;校验通常采用单字节“异或”的方法。 2 串口通信中的帧同步方法
此种方法代码量小,编程简单,一般用于在主程序中以非中断方式接收串口数据、实时性很差、数据帧较短的场合但是,在串口速度过快且包头字节数比较多的情况下,串口实现同步花费的时间很长或很难实现同步例如,串口接收到序列Ox0O OxAA0xAA 0x55…,当遇到第一个“0xAA”时,该方法认为第1个字节正确开始比较第2个同步头第2个字节仍是“0xAA”而不是“0x55”,所以必须等待新的字节重新开始比较第1个同步头而紧随其后的是“0x55”,因而,此时包头的第1个字节也没有同步上事实上,“0x00 OxAA”是干扰字节,“0xAA 0x55”才是通信协议中的同步头。 2.2 基于FIFO队列的帧同步方法
本例中定义两个字节HEADl和HEAD2,都初始化为0xFF同步时,丢弃数组头字节HEADl,数组中的所有数据向前移动一个字节,串口接收到的新字节存入数组末字节HEAD2中,将整个数组与协议中的包头信息比较如果正确,则置位已同步标志位,然后开始接收、存储有用数据;否则,继续等待同步串口数据接收完后,不仅要清除已同步标志,还要把HEADl和HEAD2两个字节都赋值0xFF;否则,将会影响下一帧数据的同步和接收用前面提到的序列“0x00 0xAA 0xAA 0x55…”进行测试,随着串口接收中断收到新的字节帧同步队列中的数据依次为:[0xFF,0xFF]→[0x00,0xFF]→[0xAA,0x00]→[0xAA,0xAA]→[0x55,0xAA]此时,该算法检测出[HEAD2,HEAD2]==[0x55,0xAA],从而实现了同步,置位已同步标志位以便下次进入串口接收中断服务子程序时开始接收数据包的数据部分。 此种方法与逐次比较的帧同步方法相比,能够比较快速、正确地检测出同步包头;但是如果包头的字节数很多,同步过程中每次进入串口中断服务子程序都要进行大量的字节搬移,将必然耗费很长的时间为了使嵌入式系统更健壮,程序设计应把握的基本原则之一就是使中断处理程序最短所以基于FIFO队列的帧同步方法也不是最优的。 2.3 基于有限状态机的帧同步方法
此后,程序按照协议开始依次接收数据帧长度、命令类型、数据和校验位接收完后,重新设置系统接收状态为HEADl,同时对该数据帧进行校验校验正确后,利用消息机制通知主程序根据命令类型对数据帧进行处理或执行相应的命令操作。
3 结论 对一个有着完整通信协议的串口中断来说,因为要比较命令头、完成校验、解析数据等需要耗费大量的机器周期,所以嵌入式系统中的串口中断服务程序设计显得更为重要在实际的串口通信程序中,可采用状态机和消息机制相结合的方法,仅在中断服务程序中设置一个标志,而在主程序中根据相应标志来作处理,这样就回避了某些中断可能需要较长处理时间的问题在程序结构上,由于采用状态机的结构,既提高了可读性同时又提高了运行速度,因而该方法不仅是一种很好的帧同步方法,还是一种很不错的串口通信程序设计方法。 |