# STM32_uart_fifo_ringbuffer **Repository Path**: Sevenfite/stm32_uart_fifo_ringbuffer ## Basic Information - **Project Name**: STM32_uart_fifo_ringbuffer - **Description**: 实现了一个串口的用DMA传输的先入先出的环形缓冲区,基于帧,以帧为单位,对于帧的识别是基于空闲时间的。 经过了大量数据接收的验证,对于极端情况有良好的处理 - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-11-14 - **Last Updated**: 2025-10-23 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README 串口环形缓冲区例程 \ 主要文件: ./Core/myCore/SYS/my_uart.c \ 现象:使用串口助手调试,发送过去的东西会被发送回来 \ my_uart.c 功能说明:详细看my_uart.c \ 引用说明:\ 1.使用了正点原子的printf重定向代码,用于支持MDK软件 \ 2.使用了lwrb开源库,用作环形缓冲区 注意: 定义了环形缓冲区的宏后,请按例程使用环形缓冲区,旧的函数和一些使用方法可能会导致意料之外的问题 工具链: makefile + arm-none-eabi-gcc + vscode ## 功能作用 可以做到在高速的条件下接收数据,以帧为单位把数据分隔开,不要求帧有帧头与帧尾,是通过时间来判断一帧的,不同于其他较多的模式是流式传输 \ 使用时会把串口传入的数据通过DMA转移到环形缓冲区上,在要处理时再取出。如果缓冲区溢出,则会把最新的一帧数据丢弃,因为不能确保其为一帧完整的数据,并停止接收,直到旧的数据被处理后手动复位。 ## 移植 把帧分隔开的原理是通过串口空闲中断,这是高级的MCU才有的功能,如果你的MCU没有此功能,可以通过定时器自行实现 \ 对于stm32f1及以上系列,依我个人目前的认知,是都支持空闲中断的,移植时需要修改stm32xxxx_it.c的库文件,不能用HAL库的回调函数来做 \ 原因是HAL库的回调函数没有把DMA完成中断与串口空闲中断分开来,所以无法区分一帧数据在DMA完成的时候也同时结束,这是流式传输所不需要考虑的问题。 对于帧的长度的记录,使用了一个静态队列来做,即最大的缓冲帧数量是确定的,可以在.h文件中修改。如果有需要,可以自行更改为动态的队列 \ 如果输入设备不是串口,请先考虑是否需要每个帧分隔开,帧有没有帧头与帧尾,如果真的需要移植此方案,可以考虑使用定时器判定一段时间内没有新的数据,就调用空闲中断的回调函数。具体的细节请自行思考,或者可以留言给我,我以后可能会进行更新 \ 如果波特率过高,可能会导致在DMA完成到再次开启之前的数据的丢失,这时只能使用DMA循环模式,但是本人才才疏学浅,认为DMA循环模式处理不了当缓冲区溢出时的安全问题,因为DMA在覆写未处理的数据时并不会有任何提示,可能会导致重要数据的丢失,需要注意的是,如果使用DMA循环模式,HAL库的中断回调函数是可以正常使用的,不必像此例程一样为DMA和串口做不同的回调函数。因为DMA循环模式不需要重新打开DMA,所以串口空闲标志位不会被清零,串口空闲中断可以正常响应,只是会出现DMA传输完成的数据长度为0的情况,根据库函数,这是不执行回调函数,所以我们要在中断服务函数的末尾加上这种情况。DMA普通模式也有这样的问题,所以本例程也加了这种情况的处理,具体可见stm32xxxx_it.c。 注意到,使用此种接收方式会出现一些不可避免的极端问题,DMA在循环模式下会覆写正在处理的数据,导致读取到的帧的内容出错,这在一些时候是不能接受的 ## 实现原理 通过两个中断处理来实现,分别是DMA完成中断与空闲中断。 \ 在DMA完成中断中会先把环形缓冲区中的读指针先前移位,然后判断还有多少线性的空间(即向前不超过缓冲数据的尾端和不超过读指针),然后打开一次这么长的DMA接收,但是不能把空闲标志位清零,因为此时有可能是一帧数据的结尾。如果没有线性空间,则说明已经溢出,要暂停接收。要等你处理完旧的数据后,再次打开接收,并且最后一帧的数据做废,因为不能判断是否是完整的一帧。 \ 在空闲中断中会执行数据长度的入队操作。然后再打开一次新的DMA接收