**目录- 写在前面
- 软硬件环境
- 库函数接口
- 初始实现方式
- 第一次优化
- 第二次优化
- 最后的修改
- 收发数据模型
- 结尾
写在前面
串口在各种项目中可谓是太常用了,它也是搞嵌入式必须弄懂的一个通信协议,最近维护了很久的一个项目,设备内另一模块程序更新后出现了不稳定的情况,现象就是某个功能有时候正常有时候不正常,经排查是通信接口上出现了丢包导致的,通信的接口正是用的串口,然后经过多次优化,解决了问题,以此记录一下优化过程。
软硬件环境
软件:MDK5、STM32 HAL库
硬件:项目上主控芯片为stm32f407zet6(调试时使用的stm32f103c8t6),整板外设只用了5个串口,2个硬件定时器。
库函数接口
首先看一下用到的库函数接口,不重要的忽略:
__HAL_DMA_GET_COUNTER | 获取DMA剩余未接收数据 | HAL_UART_Transmit | 串口阻塞方式发送函数 | HAL_UART_Transmit_IT | 串口中断方式发送函数 | HAL_UART_Receive_IT | 串口中断方式接收数据 | HAL_UART_Transmit_DMA | 串口DMA方式发送函数 | HAL_UART_Receive_DMA | 串口DMA方式接收函数 | HAL_UART_TxCpltCallback | 串口发送完成回调函数 | HAL_UART_RxCpltCallback | 串口接收完成回调函数 | HAL_UART_RxHalfCpltCallback | 串口接收过半回调函数 |
| |
初始实现方式
由于项目中是自定义帧格式,而且每个帧长很短,不超过16字节,所以最开始串口接收使用的是DMA单字节接收,当检测接收到一个完整帧时,将收到的帧写入fifo,然后发送一个信号量,被阻塞的任务得到信号量后从fifo读取帧并作相应操作,大致流程如下:
这样的实现方式比较简单,在数据速率比较恒定的情况下是没有问题的,但最近与之通信的模块程序更新后,出现了偶尔突发数据量会很大的情况,这样就可能会丢失数据。
第一次优化
知道了问题所在后,进行第一次优化,经过分析有以下方案可以选用:
因为最终选用的第二种方式,所以说说为什么第一种方式不行,原因有以下几点:
- 对于中断方式来说一次接收多字节并未解决频繁中断的问题,还是会一个字节产生一次中断
- 单纯的DMA(或中断)必须要接收到指定数量的数据才能完全读走数据,否则数据会一直被缓存无法读取
- 通信数据帧是不定长的
|