stm32 串口通信:环形缓冲区实战指南

STM32串口通信:环形缓冲区实战指南,给咱们讲讲这是怎么回事。在《STM32 串口详解》那篇文章里,咱们通过中断把收到的每个字节立马回传过去;再看看《STM32使用DMA接收串口数据》和《STM32使用DMA发送串口数据》,DMA帮咱们把数据搬来搬去,CPU几乎就没啥事干了。不过呢,如果数据包的长度、间隔完全猜不到,而且收完也不着急处理,直接扔到数组里肯定不行,结果就会是“捡了芝麻、丢了西瓜”。 这时候,缓冲队列就派上用场了——先把所有数据接收到缓冲区,再慢慢处理。而环形缓冲区又是其中一种很优雅的做法。现在咱们说说环形缓冲区是怎么来的。原本咱们用的是固定数组,这玩意儿可有点尴尬。个数固定死了,变量满天飞,代码看着就像“蜘蛛网”。 数据包有时候比数组长一点,有时候又比数组短一点,这都得单独去判断。更烦的是内存碎片化严重。后来咱们想了个办法:把一个数组“剪开”再“拉直”,就变成了环形缓冲区。 头指针(Head)专门管往外吐数据;尾指针(Tail)专门管往里塞数据。 当Tail跑到数组边界的时候就原地折返,接着用同一块内存。 上图就是把环形缓冲区拉直后的样子,有效数据区就是Head和Tail之间那一段亮条。 接下来咱们说说环形缓冲区有三大特点: 先进先出(FIFO)——最早进来的字节最先被读走。 满则覆盖——缓冲区满了以后,新数据会“踢”掉最老的数据,不会一直等着不动。 空间复用——Tail追上Head的时候,环形还是照样用着,内存利用率很高。 代码实现上咱们用500字节的环形缓冲区试试: 先定义结构体:RingBuff_t里面有Head、Tail、Length还有Ring_data这个数组。 初始化的时候把Head、Tail和Length都清零表示空着没用。 写入接口: Write_RingBuff函数先判断有没有满,满了就报错; 没满的话就把数据存进去Tail的位置; 然后Tail指针往后挪一位,超过边界了就折返到开头; 最后Length加一表示存进去了一个字节。 读取接口也是类似的操作: Read_RingBuff函数先判断有没有空,空了就报错; 没空的话就把头指针位置的数据取出来放到rData里; 然后Head指针往后挪一位超过边界了也折返到开头; 最后Length减一表示取走了一个字节。 如果想一次写入多个字节或者读取多个字节很简单: 写的时候把data改成数组然后循环调用写入接口就行; 读的时候把rData改成数组然后循环调用读取接口就行。 实战验证一下吧: 中断处理函数USART1_IRQHandler里面: 先看看有没有收到数据USART_InterruptStatus; 如果收到了就把USART_ReceiveData取出来用Write_RingBuff存进去; 最后别忘了清一下标志位USART_ClearInterruptStatus。 主循环里一直干活: 先调用Read_RingBuff从Head的位置取一个字节; 如果取到了就发出去USART_SendData; 然后模拟一个耗时任务SysCtlDelaySystemCoreClock除以3000毫秒暂停一下。 把环长设为500字节测试了一下高速串口真的零丢包。如果还丢包的话只要把RINGBUFF_LEN调大一点就行——内存复用让扩容几乎没啥成本。