
1. UART FIFO与DMA从基础概念到实战优化在嵌入式开发里UART通用异步收发传输器就像系统的“嘴巴”和“耳朵”负责与外部世界对话。但如果你处理过高速、大数据量的串口通信比如从传感器持续采集数据或向显示屏发送长帧指令肯定遇到过CPU被频繁中断“打懵”或者数据因为处理不及时而丢失的窘境。这背后的核心矛盾在于UART硬件收发一个字节的速度微秒级和CPU处理一个中断、搬运数据的速度可能涉及上下文切换微秒到毫秒级不在一个量级上。为了解决这个问题现代微控制器比如TI的AM62L系列的UART模块普遍集成了两大“神器”FIFO先进先出缓冲区和DMA直接内存访问。FIFO相当于在UART和CPU之间加了一个“蓄水池”可以暂存多个数据避免每个字节都去打扰CPU。而DMA则像是一个“专职搬运工”能在“蓄水池”达到一定水位时不经过CPU直接与内存进行大批量数据搬运。今天我就结合AM62L的技术手册和实际调试经验把这套机制掰开揉碎了讲清楚重点聊聊如何配置FIFO的中断与轮询模式以及如何玩转DMA来最大化释放CPU性能。简单来说FIFO管理解决了“何时通知CPU”的问题而DMA解决了“谁来搬运数据”的问题。两者的组合使用是构建高效、可靠串口通信系统的基石。无论是做工业PLC、智能家居网关还是车载设备吃透这套机制能让你在调试通信稳定性、优化系统功耗时心里更有底。2. FIFO工作机制深度解析不止是个缓冲区很多人把FIFO简单理解为一个数据缓存区这没错但它的管理逻辑才是精髓。AM62L的UART模块通常包含一个64字节的发送TXFIFO和一个64字节的接收RXFIFO。关键不在于它有多大而在于我们如何通过寄存器精准控制它的行为。2.1 FIFO的启停与清零精细化的控制起点控制FIFO的核心寄存器是UART_FCRFIFO控制寄存器。其中FIFO_EN位是总开关。上电复位后FIFO默认是禁用的此时UART工作在传统的单字节模式每收/发一个字节都可能产生中断对CPU负载极高。注意在启用FIFOFIFO_EN1后之前残留在UART_THR发送保持寄存器或UART_RHR接收保持寄存器的单字节数据并不会自动转入FIFO。安全的做法是在初始化流程中先启用FIFO紧接着使用RX_FIFO_CLEAR和TX_FIFO_CLEAR位进行复位。手册中提到这两个清零位会在4 x PCLK 5 x UARTi_FCLK个时钟周期后由硬件自动清零软件需要等待这个操作完成通常用短暂延时或检查位状态后才能进行后续的数据读写。2.2 触发阈值Trigger Level性能调优的关键阀门这是FIFO管理的核心参数直接决定了中断或DMA请求的触发频率。AM62L的触发阈值配置非常灵活主要通过UART_FCR和UART_TLR触发等级寄存器来设置。对于发送TXFIFO 阈值指的是FIFO中的空余空间Space。例如设置TX触发阈值为8意味着当TX FIFO中已填满的数据被发送出去使得空余空间大于或等于8字节时就会触发TX中断或DMA请求通知系统“可以再送8个字节数据过来了”。对于接收RXFIFO 阈值指的是FIFO中已存数据量Data Level。例如设置RX触发阈值为16意味着当RX FIFO中累积收到16个字节时就会触发RX中断或DMA请求通知系统“快来取走至少16个字节的数据”。配置方式有三种由UART_SCR辅助控制寄存器的相关位决定基础模式(SCR[6]或SCR[7] 0且TLR对应字段为0)使用UART_FCR中的TX_FIFO_TRIG和RX_FIFO_TRIG位域。通常提供几个固定档位如8、16、32、56字节。这是最常用的快速配置方式。DMA优化模式(SCR[6]或SCR[7] 0且TLR对应字段非0)使用UART_TLR中的TX_FIFO_TRIG_DMA和RX_FIFO_TRIG_DMA位域。提供以4字节为粒度的灵活设置如4 8 12 … 60。这在配合DMA控制器时非常有用可以对齐DMA传输的块大小。精细模式(SCR[6]或SCR[7] 1)将TLR中的DMA位域和FCR中的基础位域拼接起来实现以1字节为粒度的设置1到63。这提供了最高的灵活性。实操心得阈值的设置是一场吞吐量与实时性的权衡。阈值设得高如RX56一次中断/搬运的数据量大CPU干预频率低吞吐量高但数据在FIFO中停留时间变长实时性变差。阈值设得低如RX1实时性好但中断风暴会让CPU疲于奔命。我的经验是对于连续流数据如日志输出设高阈值对于需要快速响应的命令帧如Modbus RTU设低阈值如4或8并在软件中实现超时断帧机制。2.3 流控Flow Control下的阈值协调当使用硬件流控RTS/CTS或软件流控XON/XOFF时会涉及另一个重要寄存器UART_TCR传输控制寄存器。它包含了RX_FIFO_TRIG_START和RX_FIFO_TRIG_HALT两个阈值。RX_FIFO_TRIG_START当RX FIFO数据量低于此阈值时重新断言RTS信号拉低通知对端“我可以继续接收数据了”。RX_FIFO_TRIG_HALT当RX FIFO数据量达到此阈值时取消断言RTS信号拉高通知对端“暂停发送”。这里有一个黄金法则在中断模式下必须保证RX_FIFO_TRIG_HALT RX_FIFO_TRIG你设置的接收中断阈值。否则可能会出现一种尴尬局面FIFO数据达到了中断阈值CPU被通知来处理但同时RTS信号已经拉高让对端暂停发送了。如果CPU处理速度稍慢对端暂停后FIFO数据被取走一部分低于HALT阈值RTS又会拉低对端重新开始发送……这可能导致流控振荡和不稳定的数据传输。在DMA模式下由于每个字节到达都可能产生DMA请求这个限制不存在。3. FIFO中断模式 vs. 轮询模式场景化选择策略FIFO可以与两种CPU交互模式配合中断模式和轮询模式。这不仅仅是“开中断”和“关中断”的区别而是两种完全不同的软件架构。3.1 FIFO中断模式事件驱动的效率之选这是最常用、也是推荐的方式。通过设置UART_FCR[0] FIFO_EN 1并启用UART_IER_UART中断使能寄存器中相应的中断位如IER[0]用于RHR中断IER[1]用于THR中断来工作。接收流程外部数据持续进入RX FIFO。当FIFO中数据量达到预设的RX触发阈值时硬件自动拉高中断线。CPU跳转到中断服务程序ISR。ISR读取UART_IIR_UART中断标识寄存器判断中断源然后从UART_RHR寄存器中连续读取数据直到FIFO数据量低于触发阈值。一次中断可以处理多个字节。中断清除。对于RHR中断读取UART_RHR直至条件消失即可清除。发送流程初始时TX FIFO为空硬件立即产生THR中断。CPU在ISR中向UART_THR写入数据直到填满FIFO或达到软件设定的单次写入上限。当写入的数据量使得TX FIFO的空闲空间低于触发阈值时中断信号被撤销。随着数据被串行发出TX FIFO空间逐渐增加。当空间再次大于或等于触发阈值时再次产生THR中断请求CPU填充数据。注意事项发送中断的触发条件是“空间可用”而不是“FIFO空”。这是一个关键细节。如果你设置TX触发阈值为8那么当FIFO从满状态发送掉一个字节剩下63字节时空间1并不会触发中断。只有当发送到剩下56字节时空间8才会触发中断。这避免了过于频繁的中断。3.2 FIFO轮询模式简单场景的轻量方案通过设置UART_FCR[0] FIFO_EN 0禁用FIFO或保持FIFO启用但禁用所有UART中断UART_IER_UART 0然后定期查询UART_LSR_UART线路状态寄存器来工作。你需要轮询的关键位包括LSR[0]接收数据就绪DR。为1表示UART_RHR或FIFO中有数据可读。LSR[5]发送保持寄存器空THRE。为1表示UART_THR或FIFO有空位可以写入新数据。LSR[1-4]溢出错误OE、奇偶错误PE、帧错误FE、间隔中断BI。用于错误检测。轮询模式的优缺点非常明显优点实现简单无需管理中断向量和上下文切换代码逻辑直观。缺点CPU必须不断“询问”UART状态占用大量CPU周期效率低下。实时性取决于轮询周期可能引入不可预测的延迟。适用场景仅适用于数据量极少、对实时性要求极低、或是在一个超级循环Super Loop架构的简单任务中。在实际产品级代码中我几乎从不建议在主循环中轮询UART除非是用于调试输出。3.3 模式选择与实战建议绝大多数应用请使用中断模式。它合理利用硬件特性在数据到来时主动通知CPU是效率与实时性的平衡点。在RTOS环境中通常会在UART中断服务程序ISR中仅做最少的操作如读取数据到临时缓冲区然后通过消息队列、信号量或任务通知等方式唤醒一个高优先级的数据处理任务。这样能保持ISR短小精悍避免在ISR内进行复杂处理阻塞其他中断。轮询模式仅作为调试辅助。例如在系统初始化阶段用轮询方式发送几个调试字符或者在所有中断尚未配置好的早期启动代码中临时使用。4. DMA模式解放CPU的终极武器当数据量进一步增大比如要通过UART传输图像帧、音频采样块或大量传感器数据时即使有FIFO缓冲频繁的中断和内存拷贝仍会成为CPU的沉重负担。此时DMA直接内存访问是必选项。4.1 AM62L UART的DMA模式概览AM62L支持多种DMA模式模式0-3主要通过UART_SCR[0] DMA_MODE_CTL和UART_FCR[3] DMA_MODE以及UART_SCR[2-1] DMA_MODE_2位域来配置。模式0无DMA操作。DMA相关信号不活动。模式1推荐独立的TX和RX DMA通道。这是最强大、最常用的模式发送和接收可以独立、并发地由DMA控制器处理。模式2传统模式仅使用RX DMA请求。TX部分仍需CPU中断或轮询处理。模式3传统模式仅使用TX DMA请求。RX部分仍需CPU中断或轮询处理。配置逻辑如下这是手册的精华也是容易出错的地方如果DMA_MODE_CTL 0则DMA模式由DMA_MODE位决定0模式01模式1。如果DMA_MODE_CTL 1则DMA模式完全由DMA_MODE_2位域决定00模式001模式110模式211模式3。此时DMA_MODE位被忽略。重要提示为了配置的清晰和可维护性我强烈建议统一使用DMA_MODE_CTL 1然后通过DMA_MODE_2位域来选择模式。这样可以避免寄存器位之间的隐含覆盖关系导致的意外行为。4.2 DMA传输的工作流程与阈值设定DMA模式下的FIFO触发阈值概念与中断模式类似但目的不同它决定的是何时向DMA控制器发起传输请求。接收DMA流程RX FIFO持续接收数据。当FIFO中数据量达到RX_FIFO_TRIG_DMA或拼接后的阈值时UART模块向DMA控制器发出请求。DMA控制器启动一次传输从UART的UART_RHR“外设地址”读取阈值指定数量的字节直接搬运到内存中指定的缓冲区。传输完成后DMA请求撤销。FIFO数据被取走水位下降。当后续数据再次填满FIFO达到阈值时重复步骤2-4。发送DMA流程初始时TX FIFO为空UART模块立即向DMA控制器发出请求。DMA控制器启动一次传输从内存缓冲区中读取阈值指定数量的字节写入UART的UART_THR“外设地址”。传输完成后DMA请求撤销。FIFO被填充数据开始串行发出。随着数据发出FIFO空间逐渐增加。当空闲空间再次达到TX触发阈值时UART再次发出DMA请求DMA控制器搬运下一批数据。这里有一个至关重要的匹配原则你为UART设置的DMA触发阈值例如56字节必须与DMA控制器的传输宽度Transfer Width和突发大小Burst Size相匹配。例如如果你设置UART TX阈值为56字节那么配置DMA控制器时单次传输大小也应设为56字节。如果DMA单次只搬32字节那么搬完后FIFO仍有空间但未达到阈值DMA请求会保持有效直到你通过软件写入剩余数据或重新配置这可能导致DMA挂起或数据流中断。4.3 高级话题TX DMA阈值的独立设定在默认情况下TX DMA的触发阈值等于TX FIFO的总大小减去你在UART_TLR中设置的TX触发阈值。AM62L提供了一个高级功能允许你直接设定一个独立的TX DMA阈值通过UART_MDR3[2] SET_DMA_TX_THRESHOLD位和UART_TX_DMA_THRESHOLD寄存器实现。当SET_DMA_TX_THRESHOLD 0默认使用上述的默认计算方式。当SET_DMA_TX_THRESHOLD 1TX DMA阈值直接采用UART_TX_DMA_THRESHOLD寄存器的值。但手册警告如果TX_DMA_THRESHOLD TX触发阈值 64则会回退到默认方式以防止FIFO溢出。这个功能有什么用想象一个场景你的TX FIFO是64字节触发阈值设为8即空闲空间8时请求DMA。默认的DMA阈值就是64-856字节。这意味着DMA每次会搬56字节来填满FIFO。但如果你希望DMA以更小的块比如16字节进行传输以获得更平滑的数据流和更低的单次延迟就可以启用此功能并设置TX_DMA_THRESHOLD 16。此时当TX FIFO空闲空间8时触发请求但DMA只搬16字节就停止而不是搬56字节。4.4 安全关闭DMA的序列手册中特别强调当需要禁用DMA例如所有数据传输完成准备进入低功耗模式时必须遵循严格的软件序列否则可能导致数据丢失或DMA状态机卡死。这里以禁用TX DMA从模式1切换到模式2或0为例概括其核心思想确保数据已处理在尝试切换模式前必须确认所有RX数据已被读取所有TX数据已发送完毕。对于RX可以使用流控或直接禁用接收器。执行FIFO清零写入UART_FCR[2:1] 0b11同时清零TX和RX FIFO及其计数器。这是一个关键步骤用于重置内部状态。切换DMA模式将DMA_MODE_2位域从01模式1改为10模式2仅RX或00模式0无DMA。再次确认根据手册在某些序列后可能需要再次执行FIFO清零操作。踩坑记录我曾遇到过在高速数据传输中直接修改DMA模式寄存器导致系统锁死的情况。根本原因是DMA控制器可能还在进行未完成的传输而UART模块的状态突然改变。最安全的做法是在计划停止DMA传输前先通过软件停止DMA控制器的通道等待其完成当前传输并进入空闲状态然后再执行上述UART模块的模式切换和FIFO清零操作。这需要仔细查阅你所用的具体DMA控制器文档。5. 功耗管理与实战中的平衡艺术UART作为常开的外设其功耗管理在电池供电的物联网设备中至关重要。AM62L的UART提供了模块级和系统级的睡眠机制。5.1 模块睡眠模式Sleep Mode在UART模式下通过设置UART_IER_UART[4] SLEEP_MODE 1且UART_EFR[4] ENHANCED_EN 1来使能。进入睡眠模式需要满足严格条件RX线处于空闲状态无数据。TX FIFO和移位寄存器为空。RX FIFO为空。只有THR中断可能处于挂起状态这意味着所有数据已发送完。进入睡眠后模块内部时钟和波特率时钟会停止功耗大幅降低。唤醒事件包括RX线状态变化、数据写入TX FIFO、或Modem输入引脚状态变化。重要警告手册明确指出在睡眠模式下绝对不能对波特率发生器寄存器UART_DLL和UART_DLH进行写操作。在修改波特率前务必先通过清除SLEEP_MODE位来禁用睡眠模式。5.2 系统级空闲与唤醒UART还支持通过UART_SYSC[2] ENAWAKEUP位和UART_WER唤醒使能寄存器参与系统级的电源管理。当系统准备进入低功耗状态时可以通过Idle Req/Ack握手协议干净地关闭UART时钟并将其从中断生成模式切换到唤醒生成模式。当UART_WER中使能的唤醒事件如RX引脚边沿发生时UART可以产生唤醒信号将整个系统从低功耗状态拉回。实战中的功耗优化策略动态调整波特率在需要高速传输时使用高波特率快速完成传输后进入睡眠。在低速监测场景使用低波特率。利用FIFO和DMA减少CPU活跃时间配置合理的FIFO阈值和DMA块大小让CPU在一次唤醒中处理更多数据然后迅速回到休眠状态。DMA搬运数据时CPU甚至可以进入更深度的睡眠。及时进入模块睡眠在通信间歇期如果确认短时间内不会有数据主动检查并满足睡眠条件让UART模块进入睡眠。关闭不必要的功能如果不需要硬件流控、红外编码IrDA等高级功能确保相关模块和时钟被禁用。6. 配置流程与常见问题排查6.1 一个典型的UART带FIFO与DMA的初始化流程时钟与引脚配置使能UART模块的时钟配置对应引脚为UART功能。软件复位置位UART_SYSC[1] SOFTRESET等待复位完成通常该位会自动清零。基本参数设置配置UART_LCR线路控制寄存器设置数据位、停止位、奇偶校验。配置UART_DLL和UART_DLH设置波特率。注意修改波特率前需确保睡眠模式已禁用。FIFO配置写UART_FCR先设置FIFO_EN1启用FIFO。设置RX_FIFO_TRIG和TX_FIFO_TRIG选择基础阈值或通过UART_SCR和UART_TLR配置更精细的阈值。置位RX_FIFO_CLEAR和TX_FIFO_CLEAR位等待硬件清零短暂延时。DMA配置设置UART_SCR[0] DMA_MODE_CTL 1。根据需求设置UART_SCR[2:1] DMA_MODE_2例如01为模式1。如果需要独立TX DMA阈值配置UART_MDR3和UART_TX_DMA_THRESHOLD。中断/DMA使能若用中断模式配置UART_IER_UART使能RHR、THR等所需中断。在系统层面配置NVIC中断控制器。若用DMA模式配置DMA控制器设置源/目标地址、传输宽度、突发大小并链接到UART的DMA请求信号。使能DMA通道。使能传输通过UART_ECR寄存器独立使能接收器RX和发送器TX。6.2 常见问题与排查技巧问题现象可能原因排查步骤与解决方案数据丢失接收不完整1. RX FIFO溢出。2. 中断/DMA响应太慢。3. 流控配置错误。1. 检查UART_LSR_UART[1] OE溢出错误位。如果置位说明数据来得太快。2.提高RX FIFO触发阈值降低中断频率。3. 检查并启用硬件流控RTS/CTS确保RX_FIFO_TRIG_HALT设置合理。4. 优化ISR或DMA回调函数减少处理延迟。发送速度慢有卡顿1. TX FIFO阈值设置过高。2. DMA传输块大小与阈值不匹配。3. 系统中断被长时间关闭。1.降低TX FIFO触发阈值如从56改为8让DMA/中断更早请求数据。2. 核对DMA控制器配置的传输大小是否等于UART的TX DMA阈值。3. 检查是否有高优先级任务或中断长时间占用CPU。无法进入睡眠模式睡眠条件不满足。1. 检查UART_SSR或相关状态位确认TX/RX FIFO是否真正为空RX线是否空闲。2. 确认是否只有THR中断可能挂起即发送已完成。3. 在尝试睡眠前手动清除所有可能的中断标志。DMA传输意外停止1. DMA控制器传输完成中断后未重新配置。2. UART的DMA模式被意外更改。3. 内存缓冲区边界处理错误。1. 在DMA传输完成中断中检查是半传输完成还是全传输完成并正确重新配置源/目标地址和计数。2. 在关键代码段避免操作UART_SCR的DMA模式位。3. 使用“双缓冲”Ping-Pong Buffer机制当DMA在操作缓冲区A时CPU处理缓冲区B反之亦然实现无缝连续传输。奇偶校验或帧错误频发1. 波特率不匹配。2. 线路干扰。3. 时钟精度不够。1. 双方计算并核对波特率寄存器的值。2. 检查硬件连接在长距离或噪声环境使用差分信号如RS485并加终端电阻。3. 使用更高精度的外部晶振。调试UART的FIFO和DMA问题时逻辑分析仪或带协议解码功能的示波器是无价之宝。你可以直接抓取UART的TX、RX引脚信号以及DMA请求线、中断线的波形直观地看到数据流、触发时机和硬件信号之间的时序关系很多软件中难以捉摸的问题在波形图前会一目了然。最后再分享一个小心得在项目初期不要急于把所有优化大FIFO、高阈值、复杂DMA都加上。先用最简单的中断模式甚至轮询把通信链路调通确保数据能正确收发。然后逐步引入FIFO观察中断频率的变化。最后再上DMA并仔细调整阈值和块大小。这种渐进式的调试方法能帮你清晰地定位每一层引入的问题最终构建出一个既高效又稳定的串口通信子系统。