串口驱动中使用FIFO 您所在的位置:网站首页 如何禁用串口输出信号设置 串口驱动中使用FIFO

串口驱动中使用FIFO

2024-07-02 06:19| 来源: 网络整理| 查看: 265

FIFO是什么?

FIFO( First Input First Output) 简单说就是指先进先出。它是一种数据存储或传输模式,可以硬件实现也可以软件实现,大多数用来缓存数据,减少操作次数,提高传输效率。

串口实现中其实在两处运用了FIFO。一处是串口控制器硬件内部可能会提供收发FIFO支持,一处是在驱动框架层提供了软件实现的数据收发FIFO。这里只介绍硬件FIFO的特性和用法。

没有硬件FIFO的串口只有一个数据接收寄存器和一个数据发送寄存器(这两寄存器可能是同一地址,读时访问接收寄存器,写时访问发送寄存器),每接收或发送一个字节后必须等待收发完成后才能进行下一字节操作,否则就会出现发送或接收处错误,造成数据丢失。而有FIFO的串口控制器,只要收发数据长度不超过FIFO 深度就不会出现溢出错误,同时能大幅减少收发完成中断,减少CPU占用。

无FIFO或FIFO较浅的串口控制器,对系统的实时性要求较高。比如串口以115200波特率进行收发,每字节包括1个起始位8个数据位1个停止位(没考虑有校验位的情况)传输时间大约是86微秒。如果是轮询模式,为保证在满载传输的极限情况下也不丢数据的话,则轮询周期就要小于86微秒,自然每次轮询的执行耗时就必须小于86微秒,对于与一些主频和内存性能比较低的处理器,86微秒的执行耗时还是很有挑战的。同时还要注意轮询任务还可能会被中断和高优先级任务抢占,而中断与中断之间,任务与任务之间也可能是嵌套抢占的,那对于轮询任务就存在一个最大的抢占时间,这个抢占时间加上自身的执行耗时假设为Tmax,则Tmax不超过86微秒才能保证最坏情况下不丢失数据,这个要求其实就很苛刻了。

如果计算性能不够又必须保证最坏情况下也不能丢帧,解决办法有三种方法:

降低波特率,比如波特率是9600时,Tmax小于1041微秒即可。串口控制器提供接受FIFO,FIFO深度越大实时性要求越低。比如同样是115200波特率下,FIFO深度为32时,则Tmax小于2777微秒即可。使用DMA模式,DMA模式其实相当于一个深度可任意配置的FIFO。比如同样是115200波特率下,DMA缓冲区设置为128,则Tmax小于11111微秒即可。 发送操作 轮询模式

方法1:

等待发送FIFO空。写入1字节到发送FIFO。等待FIFO空,即等待发送完成,返回步骤1再进行一下字节发送。

这中方式的操作步骤和效果其实都和无FIFO下相同,传输效率和FIFO深度无关,CPU消耗时长等于发送数据在串口上的传输时间,效率最低。

方法2:

等待发送FIFO非满。写入n字节到发送FIFO,直到FIFO满或无数据可写入。等待FIFO非满,返回步骤1,如此循环直到无数据可写入。

如果一帧数据全部写入,FIFO都未满,则CPU消耗时只等于n字节的内存传输时间。否则因为内存传输远快于串口传输后半段的数据其实是每发送完一字节就要向FIFO添加一字节,效果和方法1相似。应用上如果是间隔的短帧则效率较高,而如果是长帧或连续流数据则效率趋近于方法1。

方法3:

等待FIFO非满。写入n字节到发送FIFO,直到FIFO满或无数据可写入。等待FIFO空,返回步骤1,如此循环直到无数据可写入。

应用上如果是间隔的短帧和方法2效果一样。如果是满载发送状态则是填满FIFO后,等待FIFO全空,再重新填满发送,需要轮询的频率就很低,当传输FIFO深度的数据耗时大于系统一个心跳时间时,轮询中就可以加入休眠操作,让出CPU。这样实际的CPU消耗也不会很大,但这需要FIFO深度够长串口波特率够低,系统心跳够快的情况下才可以。

中断模式

有FIFO的串口控制器需要关闭发送完成中断(每发送完成一字节都会触发),打开FIFO空中断。 方法1: 1.写入1字节到发送FIFO。 2. 等待发送FIFO空中断。 3. 在中断服务函数中,如果是发送FIFO空中断,检测是否还有数据要发送,如果有就继续写入1字节到发送FIFO。如此循环直到数据发送完成。

这种方式其实没有起到FIFO的作用,和无FIFO的效果是一样的,启动时字发送1字节,后续发送都是在中断中进行的,每次发送1字节。不需要循环等待发送完成,CPU主要消耗在中断服务上,且每发一字节就要触发一次中断。

方法2:

写入n字节到发送FIFO,直到FIFO满或无数据可写入。等待发送FIFO空中断。在中断服务函数中,如果是发送FIFO空中断,检测是否还有数据要发送,如果有就继续写数据到发送FIFO,直到FIFO满或无数据可写入。如此循环直到数据发送完成。

与方法1相比,利用硬件FIFO的支持,大幅减少了中断触发的次数,从而减少了CPU中断服务时间,提高了效率。这种模式在效率上已近很接近于DMA模式了,而且更通用更容易实现,绝大部分的串口驱动都是这种模式。

方法3: 过程与方法2一样,但触发FIFO空的条件并不是FIFO里的数据已完全传输完成,而是FIFO中剩余的数据小于某长度时(如小于2字节)就触发中断。这样处理会使得中断触发次数略微增加,但保证了在需要满载发送时,在串口线路上数据是一直连续没有中断的。

接收操作 轮询模式

方法一: 1.轮询接收FIFO状态是否为空。 2.如果接收FIFO非空,则读取FIFO中的数据,直达接收FIFO为空。

轮询模式下CPU消耗取决于轮询的频率,频率越高接收越实时,CPU消耗也越大;频率越低,CPU消耗越小,但实时性越低,甚至会因为接收不及时造成接收溢出错误而丢失数据。 相比于没有FIFO的串口控制器,有FIFO的可以设置更低的轮询频率而不会出现数据丢失的风险。

中断模式

有FIFO的串口控制器需要关闭接收完成中断(每接收一字节都会触发),打开接收FIFO满中断。 方法1: 1.等待接收FIFO中断。 2.在中断服务函数中,只要接收FIFO非空,就读取全部就收数据。

这种模式下CPU的消耗主要消耗在中断服务上,中断触发的频率越高消耗就越大。这个过程很简单,但也存在一些问题。主要是应该或能够设置什么条件下触发串口中断。首先一点,其实无论是任何串口中断触发了中断服务函数的调用过程,都应该去检查接收FIFO是否为空。如果是接收FIFO非空就触发中断,因为内存访问速度远大于串口传输速度,实际就会是每收1字节就会触发一次中断,中断频率是最高的,和无FIFO串口效果一样。如果是FIFO全满才触发中断,则能大幅减少中断频率。但也会存在问题,一个是如果中断响应较慢而数据是满载接收的,就会增加接收溢出的概率。再有就是如果接收完一帧数据后FIFO未满,后面又很久无数据接收,就会造成接收响应严重滞后,在很多应用情景下这是不能接收的。如果是半满或接近于满时触发中断,就不会有接收溢出风险,但接收响应还是可能会严重滞后。

为了解决以上问题,就得增加一种机制,在接收不满且超出一定时间无数据接收后,就要处理已收到的数据。可以通过软件方法来实现,如定时器,轮询线程等,这些方法通用性强但效率低。

这个问题明显存在的,设计串口控制器的工程师自然也能想到,所以很多串口控制器现在都具备一个硬件接收超时或空闲检查功能,在接收状态改变为空闲状态后,超过配置时间还没有数据接收,也会触发一个超时接收中断。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有