1. 串口通信的痛点与解决方案
在实际的嵌入式开发中,串口通信是最常用的外设之一,但处理不定长数据一直是个让人头疼的问题。我之前做过一个多传感器采集项目,需要同时处理指纹模块、温湿度传感器等多个串口设备的数据,每个设备的数据包长度都不固定,传统的接收方式根本应付不过来。
最原始的做法是一个字节一个字节地接收,每收到一个字节就触发一次中断。在115200的波特率下,一秒钟可能触发上万次中断,CPU光处理中断就忙不过来了,更别说执行其他任务了。后来改用定时器超时判断帧结束,虽然比字节中断好一些,但还是需要频繁处理中断,而且占用了宝贵的定时器资源。
直到我发现了串口空闲中断(IDLE Interrupt) 这个神器,配合DMA使用,简直就是处理不定长数据的完美方案。它的工作原理很简单:当串口接收到数据后,如果在一定时间内没有新的数据到达(通常是1个字节的传输时间),硬件就会自动触发空闲中断,告诉我们一帧数据已经接收完成了。
这样做的最大好处是完全解放了CPU。DMA负责在后台默默搬运数据,空闲中断只在整帧数据接收完成后才触发一次,大大减少了中断次数。在我实际测试中,CPU占用率从原来的70%多降到了不到10%,效果非常明显。
2. 硬件原理深入解析
2.1 串口空闲中断的工作原理
串口空闲中断的触发条件其实很巧妙。当RX引脚从有数据跳变到无数据状态(即保持高电平)超过一个字节的传输时间时,硬件就会自动置位IDLE标志位。这个"一个字节的传输时间"包括起始位、数据位、校验位和停止位,所以不同波特率下的具体时间会有些差异。
以常用的115200波特率为例,传输一个字节(8N1格式)需要的时间是:(1起始位 + 8数据位 + 1停止位) / 115200 ≈ 86.8μs。也就是说,只要连续86.8μs没有新数据,就会触发空闲中断。
不同系列的STM32在空闲中断的处理上有些细微差别:
- STM32F4系列:需要先读取SR状态寄存器,再读取DR数据寄存器来清除IDLE标志
- STM32L0系列:直接向ICR中断清除寄存器的IDLECF位写1即可
- STM32F1系列:处理方式又与F4不同
所以在移植代码时,一定要查阅对应型号的参考手册,否则可能会遇到无法清除中断标志的坑。
2.2 DMA的工作机制
DMA(Direct Memory Access)直译就是直接内存访问,它的核心思想是让外设直接和内存交换数据,不需要CPU参与。在我们这个场景中,DMA的作用就是自动把串口接收到的数据搬运到我们指定的内存缓冲区中。
DMA控制器有几个重要的寄存器:
- CNDTR:剩余数据传输次数,这个值会随着数据传输而递减
- CPAR:外设地址寄存器,指向串口数据寄存器
- CMAR:内存地址寄存器,指向我们的接收缓冲区
当配置好DMA后,串口每收到一个字节,DMA就会自动把这个字节从串口数据寄存器搬到内存缓冲区,同时递减CNDTR的值。整个过程完全不需要CPU参与,这才是真正的"零拷贝"传输。


3921

被折叠的 条评论
为什么被折叠?



