深入解析UART FIFO中断与DMA机制:从原理到嵌入式通信实战 1. 项目概述与核心价值在嵌入式开发尤其是涉及串口、红外或消费电子红外遥控这类串行通信的场景里数据流的平滑处理一直是个经典难题。想象一下你正在调试一个通过串口接收传感器数据的设备如果每收到一个字节CPU就被打断一次去处理那么系统大部分时间可能都花在了处理中断的上下文切换上真正处理业务逻辑的算力所剩无几。更糟的是如果发送端速度时快时慢接收端很容易因为来不及处理而导致数据丢失也就是我们常说的“溢出现象”。为了解决这个问题现代微控制器或专用的通信控制器内部几乎都集成了一个名为FIFO的硬件缓冲区。FIFO即“先进先出”队列它的作用就像一个迷你仓库。发送数据时CPU可以一次性写入多个字节到发送FIFO然后由硬件自动、按序地发送出去CPU在此期间可以去处理其他任务接收数据时硬件会把从线上收到的字节先存入接收FIFO等攒到一定数量再一次性通知CPU来取。这个“一定数量”就是可编程的触发阈值它是平衡实时性与系统开销的关键。今天我们就以德州仪器一款典型的UART/IrDA/CIR三合一模块为例深入它的“仓库管理”核心——FIFO管理与中断机制。这个模块的巧妙之处在于它通过一套高度可配置的寄存器将FIFO与中断、DMA请求紧密耦合为开发者提供了中断、轮询、DMA三种工作模式以适应从低功耗传感器到高速红外通信的不同需求。理解这套机制不仅能让你在配置串口时知其然更知其所以然更能让你在面临通信不稳定、数据丢失或系统响应迟缓等问题时快速定位到是FIFO阈值设置不当还是中断处理逻辑有误从而写出更稳健、高效的嵌入式通信代码。2. FIFO基础架构与核心寄存器解析在深入工作模式之前我们必须先搞清楚这个“硬件仓库”是怎么搭建起来的以及掌管仓库钥匙的几位“管理员”——核心控制寄存器。2.1 FIFO的物理结构与访问入口该模块的FIFO设计得非常直观。它包含两个独立的64字节硬件队列发送FIFO用于缓存待发送的数据。CPU通过向THR寄存器写入数据实际上就是将数据压入了发送FIFO的队尾。发送逻辑电路会自动从FIFO队头取出数据按照设定的波特率一位一位地发送到TX引脚。接收FIFO用于缓存已接收但尚未被CPU读取的数据。当硬件从RX引脚成功接收完一个字节后会将其放入接收FIFO的队尾。CPU通过读取RHR寄存器实际上是从接收FIFO的队头取出数据。这里有一个至关重要的细节数据在RHR寄存器中不会被覆盖。这意味着如果发生接收溢出即FIFO已满但仍有新数据到来新的数据会被直接丢弃而FIFO中已有的数据保持不变。这保证了已接收数据的完整性避免因溢出导致旧数据被污染。2.2 核心控制寄存器FCR, SCR, TLR仅仅有仓库不够我们需要一套管理系统来控制何时进货、何时出货。这套系统主要由三个寄存器实现FIFO控制寄存器这是总开关。其最低位FCR[0]是FIFO使能位。该位默认为0即FIFO功能被禁用。此时THR和RHR寄存器退化为单字节缓冲区每次读写都直接与移位寄存器交互中断或DMA请求的触发阈值固定为1字节。只有将FCR[0]置164字节的FIFO和可编程触发阈值功能才会生效。补充控制寄存器这是一个多功能寄存器其中SCR[0]位与DMA模式选择密切相关。当SCR[0]0时DMA模式由FCR[3]单独控制当SCR[0]1时DMA模式则由SCR[2:1]两位共同决定提供了更丰富的模式选择。触发水平寄存器这是整个机制的“调度中心”。TLR是一个8位寄存器其高4位TLR[7:4]用于设置接收FIFO的触发阈值低4位TLR[3:0]用于设置发送FIFO的触发阈值以“剩余空间”计。例如设置TLR[7:4] 8意味着当接收FIFO中的数据量达到或超过8字节时才会产生接收中断或DMA请求设置TLR[3:0] 16意味着当发送FIFO中的剩余空间大于或等于16字节时才会产生发送中断或DMA请求或者说当已用空间超过48字节时中断/DMA请求会解除。注意TLR的配置与FCR中的FCR[7:6]接收触发和FCR[5:4]发送触发字段是联动的。具体使用哪一组配置取决于模块的工作模式。通常在增强功能模式下TLR提供更精细的控制。2.3 状态监控LSR与SSR寄存器作为程序员我们还需要随时知道仓库的“库存”情况。这通过状态寄存器实现线路状态寄存器这是一个经典的状态寄存器在轮询模式下尤其重要。其LSR[0]位指示接收数据就绪LSR[5]位指示发送保持寄存器空即THR可写入。当FIFO禁用时查询这些位是判断能否进行读写操作的唯一方法。子状态寄存器其SSR[0]位直接指示了一个关键状态发送FIFO满。当该位为1时意味着发送FIFO已完全填满此时再向THR写入数据会导致写入失败或数据丢失。在编写发送函数时检查此位或通过中断/DMA机制来避免写满是保证数据不丢失的关键。3. FIFO的三种工作模式深度剖析理解了仓库的基本构造和管理员职责后我们来看三种不同的“运营模式”。模式的选择主要通过配置FCR[0]FIFO使能和IER中断使能寄存器来实现。3.1 中断模式事件驱动的效率之选中断模式是平衡实时性与CPU效率的常用手段。在此模式下FCR[0]1并且通过IER寄存器使能了相应的接收或发送中断。接收中断的工作流程如下硬件持续接收数据并填充接收FIFO。当FIFO中的数据量达到或超过TLR[7:4]设定的阈值时硬件自动拉低中断请求线通知CPU。CPU跳转到中断服务程序开始从RHR寄存器读取数据。关键点在于中断请求信号会一直保持有效低电平直到CPU读取了足够多的数据使得FIFO中的数据量低于设定的阈值。一旦数据量低于阈值中断请求信号被释放变为高电平。即使FIFO中仍有数据只要不超过阈值就不会产生新的中断。这避免了CPU被持续打断。发送中断的工作流程则相反当CPU将数据写入发送FIFO并启动发送后FIFO数据被逐渐发送出去。当发送FIFO完全变空时硬件立即产生发送中断通知CPU“仓库已空可以补货了”。CPU在中断服务程序中向THR写入新的数据。中断请求信号会在CPU向FIFO写入数据使得FIFO中的数据量或剩余空间跨越TLR[3:0]设定的阈值后被解除。例如设定发送阈值为8字节空间。当FIFO空时中断产生CPU开始写入。当写入的数据量使得FIFO剩余空间从64变为56即已用8字节恰好跨越阈值中断信号解除。之后即使CPU继续写入直到FIFO满也不会产生中断直到这些数据被发送完FIFO再次变空。实操心得中断阈值的设置是一门艺术。接收阈值设得太小如1-2字节中断会过于频繁系统开销大设得太大如60字节数据响应延迟高且在高速数据流下容易因处理不及时导致溢出。一个常见的起始策略是对于交互式、要求低延迟的应用如AT指令析接收阈值可设为4-8字节对于大数据量、吞吐优先的应用如文件传输可以设为16-32字节。发送阈值通常设置为FIFO深度的一半左右以保证连续发送时DMA或中断能及时补充数据。3.2 轮询模式简单直接的掌控感轮询模式是最基础、最直接的方式。在此模式下FCR[0]0FIFO禁用或即使FCR[0]1但通过IER禁用了所有中断。此时CPU需要主动、周期性地查询LSR寄存器的状态位来判断能否进行读写操作。发送在写入THR之前程序必须循环检查LSR[5]THRE发送保持寄存器空或SSR[0]TXFIFOFULL发送FIFO满位确保有空闲空间。接收在读取RHR之前程序必须循环检查LSR[0]DR数据就绪位确认已有数据到达。轮询模式的优缺点非常明显优点实现简单无需复杂的中断向量表配置和上下文保护对简单的小型系统或初始化阶段很友好。缺点CPU利用率低。在等待数据的过程中CPU处于忙等待状态无法执行其他任务。在高波特率或主频较低的系统里轮询可能无法及时响应导致数据丢失。3.3 DMA模式解放CPU的吞吐量利器当数据吞吐量成为瓶颈时DMA模式是终极解决方案。它允许数据在外设UART FIFO和内存之间直接搬运无需CPU介入。该模块支持多达4种DMA模式通过SCR[0]和FCR[3]或SCR[2:1]进行选择。DMA模式的核心信号是DMA请求其触发条件与中断模式类似但目的不同接收DMA请求当接收FIFO中的数据量达到TLR[7:4]设定的阈值时产生。DMA控制器响应此请求发起一次批量传输从接收FIFO中读取阈值指定的字节数到内存。请求会在DMA完成指定数量的传输后解除。发送DMA请求当发送FIFO变空时产生。DMA控制器响应此请求从内存搬运TLR[3:0]指定的字节数发送FIFO的剩余空间阈值到发送FIFO。请求会在写入指定数量的数据后解除。这里存在一个至关重要的配置匹配问题模块内设定的DMA触发阈值TLR必须与系统级DMA控制器的传输量配置相匹配。例如你将接收FIFO阈值设为32字节那么DMA控制器的单次传输量也应配置为32字节。如果不匹配就会导致问题DMA传输量 FIFO阈值DMA试图读取的数据量超过FIFO中实际可读的数据量可能导致DMA传输错误或读到无效数据。DMA传输量 FIFO阈值FIFO中数据已达到阈值并触发了DMA请求但DMA只搬走了一小部分导致FIFO水平很快又低于阈值无法再次触发DMA剩余数据只能等待超时中断或轮询来读取失去了DMA的批量效率。模块的文档中给出了一个生动的图示例子发送FIFO空间阈值设为56字节DMA控制器配置为每次传输56字节。当FIFO空时DMA请求激活DMA一次性写入56字节填满大部分FIFO请求解除。硬件开始发送数据当这56字节快发完FIFO再次变空时新的DMA请求产生如此循环形成一个高效的流水线。4. 中断机制详解与多模式差异中断是驱动FIFO高效工作的“警报器”。该模块的中断系统设计精巧针对UART、IrDA和CIR三种不同的物理层协议提供了差异化的中断源和优先级。4.1 中断的通用框架IER与IIR所有中断的使能/禁用都由中断使能寄存器统一控制。你想让哪个事件触发中断就去置位IER中对应的位。当有中断事件发生时中断标识寄存器的IIR[0]位会被硬件清零表示有中断待处理。同时IIR[5:1]位会编码出具体的中断类型和优先级CPU读取IIR即可判断该处理哪个事件并执行相应的清除操作通常是读/写某个特定寄存器。4.2 UART模式中断经典七类在标准的UART模式下中断被分为6个优先级共7种类型形成了一个清晰的处理链条最高优先级 - 接收线路状态错误包括溢出错、帧错误、奇偶校验错和线路间断。这些错误通常意味着物理连接或数据格式有严重问题需要立即处理。清除方法是对于FE, PE, BI错误读取RHR寄存器对于OE错误需要读取LSR寄存器。第二优先级 - 接收超时与数据就绪当接收FIFO中有数据但在一段时间内例如4个字符时间没有新数据到达也没有被读走会产生超时中断防止数据“滞留”。同时当接收FIFO中的数据量超过触发阈值FIFO使能时或有数据就绪FIFO禁用时会产生RHR中断。清除方法是读取RHR直到中断条件消失。第三优先级 - 发送保持寄存器空中断即发送FIFO空或低于阈值通知CPU可以发送新数据了。清除方法是向THR写入数据直到中断条件消失。更低优先级包括调制解调器状态变化中断、XOFF/特殊字符中断、CTS/RTS/DSR引脚状态变化中断等用于流量控制和握手协议。4.3 IrDA与CIR模式中断面向帧的优化IrDA和CIR模式基于UART但针对其帧传输特性做了优化中断逻辑有所不同。IrDA模式增加了状态FIFO中断和接收帧结束中断。状态FIFO是一个独立的8条目队列专门记录每个已接收IrDA帧的长度和错误状态CRC错误、帧过长等。这允许CPU在DMA批量接收数据后通过查询状态FIFO来高效地定位帧边界和筛选错误帧无需解析每一个字节。TX状态中断则明确指示了发送结束时是否发生了“下溢”错误即FIFO在帧结束前已空。CIR模式作为消费电子红外遥控协议它简化了中断主要关注接收停止中断根据设定的引导头长度触发和基本的收发数据中断。其IIR[5]位专门用于指示“红外数据的最后一位已发送至IR TX引脚”这对于精确控制红外载波脉冲的结束时刻至关重要。4.4 一个关键实践中断的清除务必查阅数据手册中每个中断类型对应的“清除方法”。这是中断编程中最容易出错的地方之一。例如有些中断通过读取特定数据寄存器来清除如RHR中断读RHR。有些通过读取状态寄存器来清除如线路状态错误读LSR。有些则通过读取IIR本身来清除如某些特殊字符中断。对于FIFO溢出/下溢这类严重错误清除步骤更复杂需要先复位对应的FIFO然后读取RESUME寄存器来清除内部错误标志之后模块才能恢复正常通信。错误的中断清除操作会导致中断持续触发系统陷入死循环。5. 高级主题与实战避坑指南掌握了基本模式后我们再看几个高级主题和实际开发中必然遇到的“坑”。5.1 波特率生成器的配置禁忌模块的波特率由DLL和DLH这两个分频器寄存器控制。计算公式为分频值 系统时钟频率 / (波特率 × 波特率倍数)。其中波特率倍数在UART 16x模式下为16在IrDA SIR模式下也为16在MIR模式下为41或42。严重警告在修改DLL和DLH寄存器的值之前必须先将模式选择寄存器MDR1[2:0]设置为7h即禁用模式。如果模块正在作任何模式时修改波特率分频器会导致不可预测的行为通常表现为通信彻底紊乱。正确的配置顺序是停止模块 - 设置MDR1为禁用 - 配置DLL/DLH - 设置MDR1为所需模式 - 使能模块。5.2 IrDA/CIR特有的状态FIFO与帧管理IrDA模式下的状态FIFO是提升效率的神器。它解耦了数据流和帧管理信息。DMA可以持续地将原始数据搬运到内存的环形缓冲区而CPU只需在状态FIFO达到触发阈值时被中断一次然后批量读取多个帧的状态信息再根据帧长度去内存缓冲区中切割出完整的帧数据。这极大地减少了中断频率。帧的结束有两种方式帧长度法CPU预先将帧长度写入TXFLH/L寄存器。硬件发送完指定长度的数据后自动添加结束标志。适用于帧长度固定的场景。设置EOT位法CPU在写入帧的最后一个字节到TX FIFO之前先将ACREG[0]EOT位置1。硬件会给这个字节打上“结束标记”并在发送它时自动添加结束标志。适用于变长帧。5.3 睡眠模式下的注意事项模块支持睡眠模式以降低功耗。在UART模式下通过设置IER[4]需EFR[4]1进入睡眠。进入条件苛刻RX线空闲、TX FIFO和移位寄存器空、RX FIFO空、且除THR中断外无其他中断挂起。特别注意在睡眠模式下绝对不能去修改DLL和DLH寄存器。因此如果需要改变波特率必须先退出睡眠模式。5.4 常见问题排查速查表现象可能原因排查步骤与解决方案数据接收不完整随机丢失1. 接收FIFO溢出。2. 中断服务程序处理太慢或中断被高优先级任务阻塞。3. 波特率误差过大。1. 检查LSR寄存器OE位是否置位。若是需按手册复位RX FIFO并读RESUME。2. 优化ISR代码减少处理时间或提高接收FIFO触发阈值降低中断频率。3. 核对系统时钟和DLL/DLH计算值确保波特率误差在可接受范围通常2%。发送数据卡住无法发出1. 发送FIFO已满仍持续写入。2. 线路断开或对方设备未就绪如CTS流控。3. 发送下溢错误IrDA/CIR模式。1. 在发送前检查SSR[0]TXFIFOFULL位或使用中断/DMA机制避免写满。2. 检查硬件连接如果启用了硬件流控确认CTS信号有效。3. 检查IIR寄存器查看是否有TX状态中断IrDA/CIR并处理下溢错误复位TX FIFO读RESUME。中断频繁触发系统卡顿1. FIFO触发阈值设置过小。2. 中断清除方式错误导致中断重复触发。3. 通信线缆噪声导致频繁的帧错误或间断中断。1. 适当增大TLR中的触发阈值。2. 严格对照数据手册表格确认每种中断的正确清除方式。3. 检查LSR中的FE/BI位优化硬件设计如增加滤波、使用屏蔽线。DMA传输数据错位或丢失DMA控制器传输量配置与UART模块的TLR阈值不匹配。核对并确保DMA控制器的单次传输字节数等于UART模块TLR中设置的触发字节数。配置寄存器后模块无反应1. 关键配置步骤顺序错误如未先禁用模块就改波特率。2. 寄存器访问地址或模式错误LCR[7]等位影响寄存器映射。1. 严格按照手册顺序初始化先禁用MDR17h再配波特率、FIFO、模式最后使能。2. 使用调试器或读取回配置寄存器确认写入值是否正确。注意LCR[7]和LCR[3:0]的值会影响某些寄存器的映射地址。5.5 配置流程建议根据多年经验一个稳健的初始化流程如下禁用模块将MDR1[2:0]设置为7h。配置波特率根据所需模式UART 16x/13x, IrDA SIR/MIR计算分频值写入DLL和DLH寄存器。配置线路控制设置LCR寄存器确定数据位、停止位、奇偶校验位。配置FIFO与触发写FCR寄存器使能FIFO根据应用需求延迟 vs 吞吐设置TLR寄存器中的收发阈值。选择工作模式配置SCR寄存器选择DMA模式如需要配置IER寄存器使能所需中断。选择物理层模式并使能将MDR1[2:0]设置为目标模式如UART模式模块开始工作。若用DMA配置DMA控制器将DMA的单次传输量设置为与TLR阈值一致。最后分享一个调试小技巧在项目初期可以先将FIFO深度设小虽然硬件是64字节但通过TLR可以模拟浅FIFO行为并启用中断。这会让数据阻塞或溢出问题更早、更明显地暴露出来便于在开发阶段就发现并解决潜在的流控或处理逻辑缺陷等稳定后再调整阈值至最优值。