嵌入式系统之间的通信通常有两种方式:并行通信和串行通信。并行方式传输数据速度快,但占用的通信线多,传输数据的可靠性随距离的增加而下降,只适用于近距离的数据传送。在远距离 数据通信 中,一般采用串行通信方式,它具有占用通信线少、成本低等优点。目前 RS 232串口是PC机与通信工业中应用最广泛的一种串行 接口 ,它应用于点对点通信模式,实际使用中多采用最简单的三线方式连接,即两端设备的串口只连接收、发、地三根线,即可实现简单的全双工通信。通信协议是两端设备数据交换的语言,是通信可靠性的保证,在保证功能的前提下,通信协议应该力求简洁。
在嵌入式环境下,因设备间通信距离较远,多采用串行通信方式,但许多串行通信协议只适用于协议设 计时 的应用系统环境,不具有通用性,且有些协议存在通信失败的风险。通过分析设备间的通信需求,设计了一种分层的串行帧通信协议,该协议简单可靠,能适应多种系统环境。在嵌入式 Linux 系统环境下,该协议在实际应用系统中运行稳定。
1 系统通信需求
本系统主要完成野外环境下时间间隔 测量 和瞬态 数据采集 的功能,系统内各模块均选用 三星 公司的S3C2440芯片为处理器,操作系统使用嵌入式Linux。模块间通信的主要任务为控制命令的下发与应答、工作状态和采集数据的上报等,对通信的可靠性要求较高,无数据加密需求。
根据系统软硬件情况设定串口工作参数如下:115 200波特率,8位数据位,1位停止位,奇校验、无流控。波特率的设置需要综合考虑所选用芯片的串口性能、串口连接线长度、传输数据的最大帧长和应用过程中的误码率等;无流控则是由于串口使用三线方式连接。
2 通信协议的分层结构
为保证不同设备之间通信协议的通用性,降低实现的复杂度,将通信协议为分上下两层:上层为应用层,规范了设备间应用程序通信使用的应用 层数 据格式;下层为链路层,提供物理线路数据的发送与接收,应用层数据拆分与合并、封装与解封装以及错误检测功能。协议应用层部分根据各设备功能的不同使用不同的数据格式,而链路层部分则完全通用。应用层通信过程如图1所示。
发方设备按照约定的应用层数据格式构造应用层数据,交由链路层进行数据的拆分、封装、校验,再将生成的数据以数据帧的形式发送至物理线路;收方设备则从物理线路上接收数据,进行帧定位、解封装、错误检测、数据合并等,最后将应用层数据上交给应用层处理。收发方设备的应用层可根据用户需求的变化,不断修改应用层数据格式,并利用链路层提供的功能接口完成通信功能,故该通信协议设计的关键在于链路层,以下着重阐述链路层的设计与实现。
3 链路层设计
链路层主要包含以下功能:数据拆分与合并、数据封装与解封装、数据帧的发送和接收以及错误检测与重发机制。
3.1 数据拆分与合并
数据拆分即是把过长的应用层数据分成几部分,用多帧数据帧发送,接收端收到后再进行数据合并,上交给应用层处理。过长的应用层数据如果不进行拆分,可能导致数据帧超出设计的缓冲区大小,也可能造成发送时间太长导致超时错误。这个长度需要根据实际需求合理设置,当数据帧传输出现错误时,这帧数据就需要重新传输,长度太大将造成较大开销;长度太小,封装时产生的开销字节所占比例又太高,影响传输效率。
3.2 数据封装与解封装
数据封装即是以一定格式把拆分后的应用层数据加上功能指示、数据长度等字段,以便对方收到后知道如何处理。数据封装格式及功能指示字段含义如表1,表2所示。
3.3 帧发送与接收
链路层以帧为单位进行数据收发,一种普遍的界定帧起始与结束的方法是:在待发送数据的头部和尾部加入特殊的起始码和结束码,如果在数据中出现了这个码型,就必须在数据发送前进行转义处理,把它转换成其他码型,否则将导致帧定位错误,数据通信失败。很多协议实现者为求实现简单没有进行这种转义,存在通信失败的风险,其实在点对点协议(PPP协议)中的描述了一种转义处理方法,经简化后,实现起来也并不复杂,描述如下:
数据发送方在帧首处发送0x7E作为起始码,逐字节发送封装后的数据,遇到0x7E时,发送0x7D,0x5E字节序列,遇到0x7D时,发送0x7 D,0x5D字节序列,最后在帧尾处发送0x7E作为结束码;
数据接收方在串口数据流中搜索第一个0x7E作为帧起始(连续的0x7E则以最后一个为帧起始),逐字节接收数据,遇到0x7D时,跳过不处理,而把该字节的后一个字节加上0x20,直到遇到0x7E认为帧结束。
在链路帧发送前,应使用CRC16算法对封装数据进行校验,校验多项式为,校验值写入校验字段中;在链路帧接收后,先对其进行校验,如果 检验 成功再进行数据解封装处理,如果校验失败则按照下述重发机制进行重发。
3.4 错误检测与重发机制
综合考虑协议实现的简单性和数据收发的可靠性,决定采用停等协议进行数据收发,过程如下:
发送方发送一帧数据帧后,设置一个最长等待时间,等待接收对方的确认帧或拒绝帧,若收到确认帧则发送下一帧;若收到拒绝帧或者在超时时间内未收到确认帧或拒绝帧,则重发当前帧,因等待超时而重发的帧要设置超时指示位。当连续收到拒绝帧三次或连续超时重发三次,则认为对端不可达,取消当前帧的发送,上报错误给应用层。
接收方收到数据帧后,当超时指示位为0时,如果校验正确,则发送确认帧,并处理此帧,如果校验错误,则发送拒绝帧,不处理该帧;当超时指示位为1时,说明对方未正确收到确认帧或拒绝帧,如果上次非重发帧的校验结果是正确的,则该帧实际上已经处理过,直接发送确认帧即可;如果上次非重发帧的校验结果是错误的,则根据校验结果正常处理该帧。
4 链路层实现
链路层采用 C++ 语言实现,以便于代码在各模块程序中复用。应用层数据发送和接收流程如图2,图3所示。
5 结语
链路层的作用是可靠地把应用层数据发送到对端设备,但如果仅仅是这样,应用程序使用起来并不是很方便,如果使用面向对象编程的方法,把链路层代码封装在一个类中,向应用程序提供一些较为简单的功能接口,如发送数据,接收数据,检测对端是否可达等,就可以很好地解决易用性问题。另外当数据发送失败时,应当以返回值或事件方式通知应用程序,当有应用层数据需要处理时,最好以回调函数或事件方式激活应用层处理程序,以避免应用程序低效的循环检测。通过在协议设计和协议实现两个方面同时进行优化,该协议在实际应用过程中表现出极好的可靠性和一定的通用性,可供参考借鉴。