引言 

在嵌入式中,不可避免地会遇到数据的收发。

其实,数据的收发有很多情况。

总体上,分为数据的收和发:

其中,数据发送是一个主动的行为,我们对要发送数据的数量特点等都是知道的,比如我们通过串口发送数据,这时候,使用常规发送方式,或者结合使用DMA都是可以的;

相较而言,数据的接收就会麻烦一些,因为接收数据,对于用户来说是一个被动响应的行为。接收数据也有很多种情况:

1、接收定长数据;

2、接收不定长数据;

3、接收的数据是随机到来的,比如串口屏的触摸发送键值到下位机,这种情况下,触摸行为是没有任何规律的,数据之间发送的间隔一般也比较长;

4、一直不断地接收大量数据,比如单片机需要一直接收串口数据;

5、接收数据后透传出去;

6、接收数据保存起来,等待上层应用访问;

以上几种情况分别如何处理呢?

以串口为例:

首先,不管是哪种情况,只要是底层接收数据,就一定要使用缓冲技术,为什么呢?

这是因为底层数据接收的速度很快,而我们处理数据的速度没那么快,如果只是简单的一两个语句来处理,比如给变量赋个值,做个简单的判断,没什么问题,如果语句多了,比如printf,也有可能来不及处理,关于这点可以参考这篇文章:

基于串口的BLE模组CC2640R2使用总结_路溪非溪的博客-CSDN博客

那么,这些情况的缓冲有何区别呢?

1、接受定长数据相对简单,使用普通串口接收或者DMA都可以;

2、接收不定长数据,就需要使用串口中断+空闲中断或者DMA+空闲中断,仍然参考:

基于串口的BLE模组CC2640R2使用总结_路溪非溪的博客-CSDN博客

3、接收数据随机到来,因为数据接收没那么快,一次可能只接受一两帧数据,所以不需要太大的缓冲区;具体参考:F103串口和DMA配合使用总结_路溪非溪的博客-CSDN博客

4、这个情况就需要相对大一些的缓冲区,因为数据来得比较快,可能还来不及处理就被覆盖了,具体参考这篇文章:F103串口和DMA配合使用总结_路溪非溪的博客-CSDN博客

5、接收数据后透传出去,这个基本都是底层的传输,所以接收缓冲后,就可以发出去,这个参考:基于串口的BLE模组CC2640R2使用总结_路溪非溪的博客-CSDN博客

6、接收数据保存起来供上层使用,这里就有个问题,底层接收数据保存起来,上层处理速度没那么快,就需要考虑一些问题,缓冲区设置多大合适呢?多久处理一次呢?

以上提到的就是缓冲区的用法,用来解决快速的数据接收和慢速的数据处理之间不匹配的问题,具体看这张图就能理解了:

其实,这里比较麻烦的也是比较常见的情况就是上面几种情况的综合:

接受不定长数据+不断接收数据帧+保存起来供上层使用。

具体参考:F103串口和DMA配合使用总结_路溪非溪的博客-CSDN博客

串口空闲中断+DMA可以解决前两个问题,再就是接收的数据需要放在一个缓冲区里面,上层应用需要时就来拿这个数据。

考虑这种场景:

电路中有连续的电压,单片机通过AD采样数据,注意,这时候连续的数据就被离散化了,只要采样率满足采样定理,就能无失真地还原出原有信号的样子。

假如我们要采的是正弦信号的RMS值,那么就是个恒定的直流量,其中有一些波动是很正常的事情。此时,数据接收后缓存起来,缓存一些数据后做个均值滤波等等处理,然后得到一个比较稳定的值,再将这个值存在一个变量中,此时,这个变量其实刷新的速率也是很快的。到这里,其实是底层的事情。

现在,我上层需要将这些数据拿来显示在屏幕上,如果来一个数据我们就显示一个数据,那么,就强迫上层跟着下层的速度,首先跟不跟得上不说,上层这么快显示数据没有什么必要,增加数据发送的负担不说,我们人眼也识别不过来,如果底层1s刷新1000个点,那我1秒去拿10个数据来显示也可以,也就是在定时器中,每隔100ms去拿一个数据,最好调整下这个间隔以适配我们人眼的观察视觉,而且,如果不这样,底层有时因为其他干扰数据发送时快时慢,我们上层显示也会时快时慢,如果定时去取值显示,就不会受底层速度的干扰,从而实现了底层快速和上层慢速的隔离,各司其职,用缓冲区作为“中介”。

就像这样拿数据显示:

另外还有一点,如果我发数据给串口屏太多太频繁,可能会影响到同时发送的其他数据从而造成卡顿。

这里面有一点值得注意,就是,如果这样显示,那不就是有很多数据没有拿到?首先,这里拿的是个RMS值,理想上是个恒定值,而且本来就是满足采样定理所采集到的离散值,从连续到离散,本来也有很多数据没采到,但是不影响效果。

到底什么是缓冲技术呢? 

缓冲区可以说是计算机中的一个连接站,用于连接计算机中高速、低速运行的部件。

作用:缓冲通常用于临时存储数据,以平衡不同速度的数据传输过程之间的差异。它可以用来解决数据传输速度不匹配的问题。

工作原理:缓冲区是一个存储区域,用于暂时保存数据,待数据传输速度对齐后再将数据发送出去。在数据传输过程中,如果数据接收速度较快,数据会被存储在缓冲区中。

数据处理: 缓冲区通常不会对数据进行处理或修改,它只是临时存储数据的容器。

可以参考这个总结:

缓冲是两种不同速度设备之间的传输信息时平滑传输过程的常用手段。

引入缓冲技术的原因:

1、  为了进一步缓和CPU和I/O设备之间速度不匹配的矛盾。

2、  提高CPU与I/O设备之间的并行性。

3、  为了减少中断次数和CPU的中断处理时间。如果没有缓冲,慢速I/O设备每传一个字节就要产生一个中断,CPU必须处理该中断。如果用了缓冲技术,则慢速的I/O设备将缓冲区填满时,才向CPU发出中断,从而减少了中断次数和CPU的中断处理时间。

4、  为了解决DMA或通道方式下数据传输的瓶颈问题。DMA或通道方式都适用于成批数据传输,在无缓冲的情况下,慢速I/O设备只能一个字节一个字节的传输信息,这造成DMA方式或通道方式数据传输的瓶颈。缓冲区的设置适应了DMA或通道方式的成批数据传输方式,解决了数据传输的瓶颈问题。

这里顺便提一下,另外还有个概念叫缓存,注意区分:

作用: 缓存用于存储已经计算过或获取过的数据,以便在后续访问时能够更快地获取数据,从而提高系统的响应速度。

工作原理: 缓存会将经常访问的数据复制到更快的存储介质中,如内存,以便在后续访问时无需再从原始数据源获取。这样能够减少数据访问时间,提高性能。

数据处理:缓存中的数据通常可以根据需要进行处理,以满足特定的访问要求。例如,可以将数据库查询结果存储在缓存中,以减少数据库访问频率。

实际应用:Web浏览器使用缓存来存储已经访问过的网页,以便下次访问同一网页时能够更快地加载。

两者主要区别如下:

用途不同:缓冲主要用于平衡数据传输速度差异,而缓存主要用于提高数据访问速度。

数据处理:缓冲不对数据进行处理,只是暂时存储,而缓存可以对数据进行处理以满足特定需求。

存储介质:缓冲通常用于暂时存储数据,存储在相同或类似的介质上,而缓存通常将数据存储在更快的存储介质中,如内存。

数据类型:缓冲可以用于各种数据类型,包括传输中的数据,而缓存通常用于经常被访问的数据。

总之,缓冲和缓存在数据处理中有着不同的作用和机制,了解它们的区别有助于更好地理解在不同情况下如何使用它们来优化数据传输和访问性能。

环形缓冲区 

可直接参考:

【数据结构】环形缓冲区介绍,原理讲解+代码实现。(内核__嵌入式__c语言数组)

补充

缓冲区可以将同步通信转换成异步通信。

异步通信就是你接收你的数据,我需要的时候再去处理,不需要同步,也就是说不需要你一边接收我一边处理。

这里有两种情况要注意区分:

比如,串口接收数据时,处理速度跟不上接收速度;

这里说的处理速度跟不上接收速度,不是说cpu的速度跟不上io的速度,事实上,cpu的速度远比io的速度要快。

那为什么说处理速度跟不上接收速度呢?要理解这一点,得明确一个前提,那就是这种情况通常出现在:串口在中断里接收到一帧数据后直接就处理了。这个处理过程一般都要花些时间,所以,导致在处理数据的这段时间里的io数据都丢了。

如何处理协议帧的接收速度和处理速度不匹配的问题?

解决这个问题的核心其实就是不要直接在接收中断里去处理数据,而是让中断快进快出,同时又不丢数据,很简单,就是接收数据时直接把数据存到buffer里,然后再由其他处理程序去处理。

另外还有一种处理速度跟不上接收速度的场景,那就是buffer一直在存数据,虽然cpu的速度很快,但是被其他任务占用了没有空闲,导致来不及处理buffer,然后buffer大小有限,新接收的数据没地方存,就只能丢弃,或者覆盖早期的数据。

cpu处理器虽然快,只是说处理一条指令很快,但是如果cpu没空闲,或者指令有几百几千甚至几万条,时间累积起来,就很有可能没有一次io快。

以上两种情况,其实本质是一样的,那就是cpu处理整个数据帧花的时间较长,导致io数据处理不过来。

简单来说,就是协议帧的处理速度跟不上接收的速度。极端来说,如果cpu的速度能提高十亿倍,就能解决这个问题,但现实是不可能的。

那怎么处理这个问题呢?

以下是一些处理协议帧的接收速度和处理速度不匹配问题的方法:

优化接收端处理流程

  • 采用高效的数据结构:使用合适的数据结构来存储和处理接收到的数据,以提高数据处理效率。例如,使用环形缓冲区(ringbuffer)可以方便数据的存储和读取,避免数据的丢失和覆盖。当接收速度和处理速度不匹配时,新接收到的数据可以覆盖最早的未处理数据,保证不会因为缓冲区满而丢失数据。
  • 改进解析算法:分析协议帧的解析过程,找出可能存在的性能瓶颈,并对解析算法进行优化。例如,减少不必要的计算、优化数据结构的访问方式等,以提高解析的速度。
  • 多线程或多进程处理:将数据的接收和处理分离到不同的线程或进程中,让接收线程或进程专门负责接收数据并存入缓冲区,处理线程或进程从缓冲区中取出数据进行处理。这样可以充分利用系统资源,提高接收和处理的并发性,缓解速度不匹配的问题。

调整协议帧格式或传输方式

  • 简化协议帧格式:在满足需求的前提下,尽量简化协议帧的格式,减少不必要的字段和开销,从而降低每帧数据的处理时间。例如,减少头部字段的长度、优化数据字段的编码方式等。
  • 增加确认机制或流量控制:通过增加确认机制,让发送方知道接收方的处理能力,从而调整发送速度。或者采用流量控制机制,如滑动窗口协议,限制发送方的发送速度,避免接收方因处理不过来而造成数据丢失。
  • 分段传输:如果协议帧较大,可以考虑将数据分段传输,接收方可以边接收边处理,提高处理的效率。同时,可以在分段之间添加必要的标识和顺序信息,以便正确组装和处理完整的协议帧。

硬件加速或优化

  • 使用高性能硬件:如果条件允许,可以选择性能更高的处理器、网络接口卡或其他相关硬件设备,以提升接收和处理的速度。例如,使用具有更高处理能力的CPU、支持硬件加速的网络芯片等。
  • 硬件加速技术:利用硬件加速技术,如GPU加速、FPGA加速等,来处理协议帧的解析和处理任务。这些硬件加速器可以并行处理多个数据,大大提高处理速度。

动态调整策略

  • 实时监测与反馈:实时监测接收速度和处理速度的匹配情况,根据监测结果动态调整接收端的参数或策略。例如,当发现处理速度跟不上接收速度时,可以自动调整缓冲区的大小、增加处理线程的数量或调整协议帧的接收频率等。
  • 自适应调整算法:采用自适应的算法来调整接收和处理的策略。例如,根据历史数据和当前的系统负载情况,动态地调整协议帧的解析优先级、分配更多的资源给处理任务等。

综上所述,处理协议帧的接收速度和处理速度不匹配问题需要综合考虑多个方面,包括优化接收端处理流程、调整协议帧格式或传输方式、硬件加速或优化以及动态调整策略等。通过合理地应用这些方法,可以有效地提高协议帧的接收和处理效率,确保通信的可靠性和稳定性。

C语言中,解析协议帧需要用到ringbuffer吗?具体是什么思路呢?

在C语言中,解析协议帧时不一定要用到ringbuffer(环形缓冲区),但在很多情况下使用ringbuffer是有益的。以下是具体的分析:

不用ringbuffer的情况

如果协议帧的接收和处理相对简单,数据量不大,且对实时性要求不高,那么可以直接在接收中断或主程序中进行协议帧的解析,而无需使用ringbuffer。例如,在一些简单的串口通信协议中,如果每次只接收一个字节的数据,并且在接收到一个完整的协议帧后就立即进行处理,那么可以不使用ringbuffer。

使用ringbuffer的情况及思路

数据缓冲与异步处理:当协议帧的接收速度和处理速度不匹配时,ringbuffer可以起到缓冲作用。接收端将接收到的数据先存入ringbuffer中,然后由主程序在空闲时从ringbuffer中取出数据进行解析和处理。这样可以避免数据丢失,提高系统的可靠性。

提高处理效率:对于一些复杂的协议帧格式,解析过程可能需要一定的时间。如果不使用ringbuffer,接收端需要等待协议帧完全接收并解析完成后才能继续接收下一个协议帧,这会导致接收端的阻塞,降低系统的整体性能。而使用ringbuffer后,接收端可以在接收数据的同时进行协议帧的解析,提高了系统的并发性和处理效率。

实现思路示例

  •   首先定义一个合适大小的ringbuffer结构体,包括缓冲区数组、头指针、尾指针等成员变量。

  •   在接收中断服务程序中,将接收到的数据写入ringbuffer中。如果ringbuffer已满,则根据具体情况选择丢弃数据或覆盖最早的数据。

  •   在主程序中,定期检查ringbuffer中是否有可解析的数据。如果有,则从ringbuffer中取出一定长度的数据,并根据协议帧的格式进行解析。解析成功后,进行相应的处理;如果解析失败,则根据协议的规定进行错误处理。

  •   为了确保数据的一致性和正确性,在访问ringbuffer时需要注意同步问题,避免出现数据竞争和不一致的情况。可以使用互斥锁或其他同步机制来保护对ringbuffer的访问。

总之,在C语言中解析协议帧时是否使用ringbuffer取决于具体的应用场景和需求。如果需要进行高效的数据处理和缓冲管理,或者需要处理不同速率的数据流,那么使用ringbuffer是一个不错的选择。

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐