今天在论坛看到有网友提出了对串口DMA接收上的一些改进建议。大致是,如果考虑高速持续的串口数据情况下,重装串口DMA会浪费掉一定的时间。这个时间内有可能出现数据接受被丢弃的情况。网友的建议是做自动切换的双缓冲区DMA,但是我有一点不一样的想法。 双缓冲示意图 按道理讲,肯定是直接用串口中断实时性最好,但是串口中断又有点过于消耗系统资源,频繁打断主程序也对其他功能运行不利。所以DMA有没有办法做出类似串口中断的用法呢?
我的想法是通过设定一块较大的缓冲区,然后利用DMA_DONE寄存器读取已经完成存储的字节,因为此时读取完成的数据部分和DMA正在读取占用的内存区域不一样,所以应该是不会产生控制冲突(这部分我在论坛上搜索了一下,似乎有例程实现了环形读取缓冲区,那么应该是没问题的),所以此时就可以通过处理已经读取的部分,并且记下当前处理到的字节数。等待下次查询的时候继续处理新接收到的一部分。这样,就可以实现边采样边接收。处理数据的时间大大增加,也就不必等到全部接受完再进行处理了。 不过上面这个方法仍然会存在重新配置DMA时,万一来了数据丢失问题。这部分我的建议是通过使用给DMA_CR寄存器中加入一个独立的标志位DMA_CIRC。如果启动DMA时,打开了这个标位置,则DMA刷新到设定的xdata区域最后时,可以自动跳回其实区域继续刷新。比如可以通过到最后通过清零地址偏移实现重新绕回初始地址(这部分不知是否容易实现?),进而创造一个大小可控的环形DMA数据区域。而程序中通过记忆上次处理到的位置和读取最新的DMA_DONE即可处理增量的数据区域。 这样,既不用死等数据到来,也不用像串口中断那样频繁打断,每次处理一部分的数据,即可实现CPU算力的最高效利用。并且,这个功能还可以通过串口超时中断进行配合使用。 比如,检测到串口超时中断后,再处理这一整个的数据帧(至少要比缓冲区小的多)。处理完成后,只需要记忆自己处理到了哪里,就可以继续完成其他任务,并不需要关心串口接收数据到哪里了,可以继续等下次超时中断自动触发。并且也不需要重复配置DMA寄存器,实现一块缓冲区的的循环使用。
如此一来,相信DMA用来进行接收数据的等待将更加易用。
|