RS232接口的可靠传输方法

一、基本概念。

       串口,一般是指RS232。RS232是协议名称,而串口还应该包括了其它协议,如:RS485、RS422等。

      串口也叫UART,有时叫USART(应该写成U(S/A)RT,但是略显累赘),U是通用,A是异步,S是同步,所以,UART跟USRT区别在于有无同步时钟,所以有的串口会有三根线(地线除外)。为了避免混淆,以下均使用RS232,而不使用串口。

      RS232发送是靠TXD和GND之间的电压来传数据(接收跟发送雷同),是共模电压,抗干扰能力差,导致传输距离非常有限。

      因为RS232的简单、低成本,所以不管是单片机、ARM、DSP都配了这种接口(FPGA可以自制)。

二、封装成帧。

      RS232的协议,分成几个部分:起始位、数据位、校验位、停止位。注意,这里用的是位(bit)。

      单纯地按照协议规定的去做,会出现一些问题。

1、奇偶校验的校验能力太差。

2、传错一个数据,没法重传。

3、在总线上挂了几个从机,主机如何区分是哪台从机发出的数据(有的设备不能把校验位当地址位用)。

4、如果没有成功发送停止位,那么接收端会不会一直等待。

5、总线上传错了一组数据,没法定位是哪个地方(哪个位)出问题。

      鉴于以上诸多问题,我们想到了一种解决办法,就是封装成帧。一帧数据包含多个byte,而一个byte的数据包含多个bit(起始位、数据位等)。

三、自定义协议。

根据实际需求自定义协议。下面举个例子(非通用协议/准则),并以“域”来表示协议里面各个功能模块。

1、帧起始。

      帧起始,一般以0x55或者0xaa作为起始的标志,为什么要用这种数字?

      十六进制的5,转为二进制是0101;十六进制的A,转为二进制是1010。这样,0x55,就是01010101了。

      0和1交替出现,一来方便接收方实现自适应波特率,二来避免干扰。

a、通过检测0和1之间的时间间隔,即可知道该帧数据的波特率。

b、如果设为0xff,那么,在总线上出现一个干扰,接收端很有可能误以为要接收数据。

2、命令域。

      命令域,可以随意一点,如:设上行为0x1d,下行为0x3a,重传为0xb7,应答为0x89。

3、地址域(可选)。

      地址域,也可以随意一点,如果所有设备的地址均不相同,那就只具备点对点的功能。

      当然,你也可以视实际情况,把某几台设备的地址设为一样,这样就有点对多点的功能。

4、长度域(可选)。

      如果是发送固定长度的帧,是可以不使用长度域;如果是不固定长度的帧,则可以加上。

4、数据域。

      数据域,一般由数据个数和数据组成。

      比如,你要传3个数,分别是0x00到0x02。那么数据域就是0x03 0x00 0x01 0x02。

5、校验域。

      校验域,可以使用校验和或者CRC校验。如,使用CRC16算法话,校验域为2个byte。

      一般除了帧起始和帧结束以外,都送进CRC校验。

      好了,这样的话,我要发一个上行的数据,到地址为0x0a的设备,一共3个数据(固定长度),分别是0xa0,0xb0,0xc0。

      那么一帧数据就是:0x55 0x1d 0x0a 0x03 0xa0 0xb0 0xc0 0x53 0xfb

      只要把上面这一帧数据,一个byte一个byte地发送出去,即可。

使用CRC-16对该帧(除去帧起始)进行计算,可以得到0x53FB。如下图所示。

7、应答和重传。

     如果接收端,收到一帧数据,经过CRC校验,数据正确,这时得传一帧简易的应答帧,以告诉主机,这个帧接收成功。

     应答帧如:0x55 0x89 0xa6 0xc1。

     如果接收端,收到一帧数据,经过CRC校验,发现数据有错,这时得传一帧简易的重传帧,以请求重传。

     重传帧,如:0x55 0xb7 0x76 0x40。

     看,串口的功能是不是强大了很多?

四、适用于FPGA的帧。

      当然,要像上面那样强化串口的功能,还是用单片机来弄,比较轻松。

      如果是FPGA的话,也有另外一种简单的封装成帧的方法。

      先插入start和stop标志位,再存进FIFO里面,如下图所示。

      最后再读出,解帧,即可。

      这一招,也能用在传输图像,比如把一帧480*320的图像传到另一台设备上,就用上述方法把每一行封装成帧,这样,能在接收端解帧,就能还原出图像了。

      当然,你还可以加上行数和列数,插在一帧数据里面,方面接收端解帧,这些都视实际需求而定。

五、缓冲区。

      缓冲区,一般是FIFO,也可以是cache、内存或者硬盘之类的存储器。

      有的单片机的串口,是带接收缓冲器的,此外,一些设备/芯片,也有缓冲区的,如Cy68013。

      这时一定要注意缓冲器的大小,缓冲器会不会溢出,如果溢出,数据可能会出错(不要以为你一直发数据,接收端就一定能收到)。

六、总结。

      上述方法,不局限于RS232,你还可以把它运用在RS485、I2C、SPI或一些并行总线。

    不管是RS232、I2C、SPI,一般是以byte为单位,传输数据的。如果你想把12位的ADC数据,通过串口发到PC机,那么,你得分成两个byte,高位补零。(要考虑代码的可重用性)

      从RS232到USB、以太网,不难看出,物理层/物理接口变化不大,而协议/数据链路层却越来越复杂。

      这也说明了,传输数据的可靠性,不能过于依赖物理层,而应该依赖协议上的一些机制(校验、重传等)。

      更详细的内容,可以参考《计算机网络基础》。

七、扩展阅读。

在《计算机网络基础》中提到数据链路层的三个基本问题:封装成帧,透明传输和差错控制。

上文已经提到了封装成帧和差错控制,但是没有透明传输。

为了实现透明传输,以避免数据部分出现帧定界,使用了字节填充的方法。

字节填充类似C语言中的转义字符,但是在串口通信中并没有使用字节填充,只用了封装成帧和差错控制已经实现了透明传输。

经过查阅资料,如下所示,确实不需要字节填充。

为什么要字节填充?

因为MAC帧(除了IEEE802.3)和PPP帧没有说明数据长度的字段,但又要兼容上层的各种协议,也就是说传输的字节数是不固定的,也不知道长度。所以才需要帧头、帧尾做界定符,同时使用字节填充来避免检测界定符时出错,最后再使用差错控制来保证通信的可靠性。

 

 

 

 

展开阅读全文
评论将由博主筛选后显示,对所有人可见 | 还能输入1000个字符

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客
应支付0元
点击重新获取
扫码支付

支付成功即可阅读