
1. 项目概述与核心价值搞嵌入式开发尤其是涉及到串口通信UART寄存器配置绝对是绕不开的“硬骨头”。很多朋友可能觉得串口嘛不就是配置个波特率、数据位、停止位然后读写数据就完事了。但当你真正面对一个复杂的工业级MCU尤其是像TI这类厂商的芯片其UART模块的功能之丰富、寄存器之复杂常常会让人望而生畏。今天我们就来啃一啃这块硬骨头聚焦于两个直接影响通信效率和系统性能的核心机制FIFO触发水平和DMA模式配置。为什么这两个点如此重要想象一下你的设备需要通过UART以115200的波特率接收来自传感器的连续数据流。如果每收到一个字节就产生一次中断让CPU来处理CPU很快就会淹没在中断的海洋里什么主任务都干不了。FIFO先进先出缓冲区就是为了解决这个问题而生的它像一个蓄水池可以暂存多个数据。而FIFO触发水平就是这个蓄水池的“水位报警线”。当数据积累到预设的“水位”比如8个字节才通知CPU或DMA来一次性搬运这极大地减少了中断频率。更进一步DMA直接内存访问则像一个自动化的搬运工可以在不打扰CPU的情况下直接把FIFO里的数据搬到指定的内存区域实现“零CPU开销”的数据搬运。这两者结合是构建高效、可靠、实时串口通信系统的基石。本文将以TI某款芯片的UART模块寄存器手册为蓝本但原理是通用的。我会带你深入寄存器位域拆解UART_TLR触发水平寄存器和SCR辅助控制寄存器等关键寄存器不仅告诉你每个位该怎么设更会解释为什么这么设以及在实际项目中如何权衡和避坑。无论你是正在调试UART驱动的嵌入式工程师还是想深入理解外设工作原理的学生这篇文章都能给你带来实实在在的干货。2. UART寄存器体系与FIFO机制深度解析在深入FIFO触发和DMA之前我们必须对UART的寄存器体系有一个整体的认识。UART的寄存器大致可以分为几类控制类如LCR配置数据格式FCR控制FIFO、状态类如LSR查看线路状态IIR标识中断源、数据类如RHR接收保持寄存器THR发送保持寄存器以及我们今天重点关注的高级功能类如TLR、SCR等。2.1 FIFO从单字节到数据缓冲区的进化早期的UART或“UART模式”通常只有一个字节的缓冲区RHR/THR。CPU必须频繁查询或响应中断来读取每一个到达的字节或发送下一个字节效率极低。现代UART普遍集成了硬件FIFO深度通常是16、64甚至128字节。FIFO的工作流程可以这样理解接收端RX串行移位寄存器将接收到的比特流组装成字节然后压入RX FIFO队列尾部。RX FIFO有一个读指针由CPU或DMA读取RHR寄存器操作推进和一个写指针由硬件接收推进。发送端TXCPU或DMA将待发送字节写入THR寄存器实质是压入TX FIFO队列尾部。发送逻辑从TX FIFO头部取出字节送入串行移位寄存器发送出去。有了FIFO通信的实时性要求就从“每个字节”放宽到了“每N个字节”给了系统更大的调度弹性。2.2 触发水平中断与DMA请求的“发令枪”FIFO的存在引出了一个新的问题我们什么时候应该去处理FIFO里的数据是等到它完全满了吗那样可能会造成数据溢出Overrun。是只要有一个字节就处理吗那FIFO就失去了意义。这就是FIFO触发水平Trigger Level要解决的问题。它为RX FIFO和TX FIFO分别设定了一个阈值。RX FIFO触发水平当RX FIFO中累积的数据量达到或超过这个阈值时硬件会触发一个“接收数据可用”事件。这个事件可以配置为产生一个中断IRQ给CPU或者拉高一个DMA请求信号DMAREQ通知DMA控制器来搬运数据。TX FIFO触发水平当TX FIFO中剩余空间空位置达到或超过这个阈值时硬件会触发一个“发送缓冲区空”事件。同样它可以产生中断通知CPU来填充更多数据或者拉高DMA请求信号让DMA从内存搬运数据到TX FIFO。关键点在于触发水平是一个“阈值”而非“精确值”。例如设置RX触发水平为8并不意味着FIFO里一有8个字节就立刻触发而是“当数据量从7增加到8时”触发。触发后即使你还没取走数据只要数据量不低于阈值比如保持在8或以上通常不会重复触发直到数据被取走到低于阈值然后再次累积到阈值时才会触发下一次。这种机制避免了事件的频繁抖动。3. 核心寄存器详解UART_TLR与触发水平配置理解了原理我们来看具体如何配置。UART_TLR寄存器是控制FIFO触发水平的核心。3.1 UART_TLR寄存器位域精讲根据手册UART_TLR寄存器偏移地址为0x1C。它的结构非常清晰位[15:8]保留位只读为0。位[7:4] (RX_FIFO_TRIG_DMA)接收FIFO触发水平设置位。位[3:0] (TX_FIFO_TRIG_DMA)发送FIFO触发水平设置位。这个寄存器的访问有一个前提条件必须同时使能增强功能寄存器EFR[4] 1并置位调制解调器控制寄存器的某个位MCR[6] 1。这是TI UART的一个常见设计将一些高级功能如TLR、自动流控放在一个“增强功能模式”下需要通过MCR[6]来解锁。不满足条件时对该寄存器的读写可能无效或访问的是其他寄存器如XOFF2。触发水平的设定并非直接写入字符数而是受SCR寄存器和FCR寄存器的联合控制形成了一个灵活的配置矩阵。这是最容易让人困惑的地方。3.2 接收触发水平RX_FIFO_TRIG_DMA配置逻辑手册给出了一个清晰的决策表我们将其翻译成更易懂的配置步骤条件判断取决于SCR[7](RXTRIGGRANU1) 和TLR[7:4]的值模式A使用FCR的预设档位条件SCR[7] 0且TLR[7:4] 0000。行为RX触发水平由FCR[7:6]这两位决定。这是一个简化的“预设”模式通常提供几个固定档位例如00: 触发水平 1 字节 (注意有些芯片可能是8字节需查具体手册)01: 触发水平 4 字节10: 触发水平 8 字节11: 触发水平 14 字节适用场景对触发水平精度要求不高希望快速配置的场合。模式B使用TLR的4字符步进档位条件SCR[7] 0且TLR[7:4] ! 0000。行为RX触发水平直接由TLR[7:4]这4位二进制值决定但步进粒度为4个字符。计算公式触发水平 TLR[7:4]的十进制值 × 4。范围TLR[7:4]从0001(1)到1111(15)对应的触发水平为4到60字节。示例若TLR[7:4] 0101(十进制5)则触发水平 5 × 4 20字节。适用场景需要中等精度调整且触发水平是4的倍数的应用。模式C使用TLRFCR的1字符步进精细档位条件SCR[7] 1。行为无论TLR[7:4]为何值RX触发水平由TLR[7:4]和FCR[7:6]拼接成一个6位的值来决定TLR[7:4]为高4位FCR[7:6]为低2位步进为1个字符。计算公式触发水平 (TLR[7:4] 2 | FCR[7:6])的十进值。范围从1到63字节因为6位最大值为63。重要禁忌手册明确警告TLR[7:4]和FCR[7:6]不能同时为0即拼接后的值为0因为最小触发水平是1字节。全零会导致不可预测的行为。适用场景需要最精细控制触发水平的场景例如FIFO深度为64你想精确设置在32字节触发。配置流程示例假设需要设置RX触发水平为16字节确定使用模式C1字节粒度以实现精确控制。设置SCR[7] 1。计算TLR[7:4]和FCR[7:6]的值16的二进制是010000。高4位0100(4)放入TLR[7:4]低2位00放入FCR[7:6]。确保EFR[4]1且MCR[6]1。写入TLR寄存器RX_FIFO_TRIG_DMA字段写入0100。写入FCR寄存器确保FCR[7:6]为00。3.3 发送触发水平TX_FIFO_TRIG_DMA配置逻辑TX触发水平的配置逻辑与RX完全对称只是控制的寄存器位不同它由SCR[6](TXTRIGGRANU1) 和TLR[3:0]控制。同样有三种模式决策取决于SCR[6]和TLR[3:0]。模式ASCR[6]0且TLR[3:0]0000触发水平由FCR[5:4]决定预设档位如8, 16, 32, 56字符。模式BSCR[6]0且TLR[3:0]!0000触发水平 TLR[3:0]值 × 44字符步进。模式CSCR[6]1触发水平 (TLR[3:0] 2 |FCR[5:4])1字符步进范围1-63。一个关键区别TX触发水平指的是TX FIFO空余空间的阈值。例如TX FIFO深度为64设置触发水平为8。意思是当TX FIFO中已发送的数据被移出导致空闲位置达到或超过8个时就会触发“TX缓冲区空”事件通知系统可以继续写入数据了。实操心得触发水平设置的权衡艺术设置触发水平不是越大越好也不是越小越好需要权衡高触发水平如接近FIFO深度优点是可以最大化单次中断/DMA传输的数据量减少系统开销。缺点是数据在FIFO中停留时间变长增加了接收端的处理延迟Latency。对于实时性要求高的控制指令传输这可能不可接受。低触发水平如1-4字节优点是响应快延迟低。缺点是中断/DMA请求非常频繁在高速波特率下会严重消耗CPU或总线资源。经验值对于115200及以下的中低速通信且数据包较小如几十字节触发水平设为FIFO深度的1/4到1/2是个不错的起点例如64深度的FIFO设16或32。对于高速通信如921600或大数据块传输应倾向于更高的触发水平并搭配DMA使用。务必结合你的实际应用场景数据包大小、波特率、系统负载进行测试和调整。4. DMA模式配置解放CPU的关键当数据量很大或系统实时性要求极高时即使优化了触发水平频繁的中断处理仍然是负担。此时DMA就成为必选项。DMA允许外设这里是UART直接与内存交换数据无需CPU参与每次字节的搬运。4.1 SCR寄存器DMA模式的控制中心UART_SCR寄存器偏移0x40是配置DMA模式的核心。我们关注其低3位位[2:1] (DMAMODE2)这两位具体定义了DMA的工作模式。位[0] (DMAMODECTL)这是一个模式选择开关决定了由哪个寄存器来定义DMA模式。DMAMODECTL 0DMA模式由FCR[3]控制。这是传统或简化模式。DMAMODECTL 1DMA模式由SCR[2:1]即DMAMODE2控制。这提供了更灵活的模式选择。为了使配置清晰且功能强大我们通常设置DMAMODECTL 1以便使用SCR[2:1]定义的四种模式。4.2 四种DMA模式详解当SCR[0]1时根据手册DMAMODE2(SCR[2:1]) 定义了以下模式DMAMODE2 值模式名称UARTnDMAREQ[0] 功能UARTnDMAREQ[1] 功能工作描述00DMA Mode 0未使用未使用无DMA模式。所有传输依赖CPU中断。01DMA Mode 1TX DMA 请求RX DMA 请求经典独立模式。这是最常用、最直观的模式。TX和RX有各自独立的DMA请求线可以同时进行全双工DMA传输。10DMA Mode 2RX DMA 请求未使用仅接收DMA模式。只有RX FIFO可以产生DMA请求TX仍需CPU或中断处理。适用于纯数据采集场景。11DMA Mode 3TX DMA 请求未使用仅发送DMA模式。只有TX FIFO可以产生DMA请求RX仍需CPU或中断处理。适用于纯数据发送场景。UARTnDMAREQ[0]和[1]是UART模块输出给DMA控制器的硬件请求信号线。DMA控制器会监听这些信号。当RX FIFO数据达到触发水平DMAREQ[1]Mode 1或DMAREQ[0]Mode 2会拉高DMA控制器随即发起一次从UART RHR寄存器到指定内存地址的传输。TX同理。4.3 完整DMA配置流程与代码示例假设我们需要配置UART0在Mode 1全双工DMA下工作RX触发水平为16字节TX触发水平为8字节指空闲空间。步骤1基础与FIFO使能// 1. 解锁增强功能寄存器 (EFR) UART0-MCR | (1 6); // 设置MCR[6] 1 // 2. 使能FIFO并设置其复位/触发控制 (假设使用FCR预设值作为初始值后续会被TLR覆盖) UART0-FCR (1 0) | // FIFO使能 (1 1) | // 接收FIFO复位 (1 2); // 发送FIFO复位 // 等待FIFO复位完成通常需要几个时钟周期步骤2配置触发水平使用精细的1字节粒度// 3. 设置SCR[7]和SCR[6]为1启用RX/TX的1字节粒度触发模式 UART0-SCR | (1 7) | (1 6); // 4. 计算并设置TLR寄存器 // RX触发水平 16字节。16的二进制: 010000。高4位0100(4)给TLR[7:4]低2位00给FCR[7:6]。 // TX触发水平 8字节。8的二进制: 001000。高4位0010(2)给TLR[3:0]低2位00给FCR[5:4]。 uint16_t tlr_value (4 4) | (2 0); // TLR[7:4]4, TLR[3:0]2 UART0-TLR tlr_value; // 5. 确保FCR[7:6]和FCR[5:4]为00与TLR组合成16和8 UART0-FCR ~((0x3 6) | (0x3 4)); // 清零FCR[7:6]和[5:4]步骤3配置DMA模式// 6. 设置SCR[0]1使用SCR[2:1]定义DMA模式 UART0-SCR | (1 0); // 7. 设置SCR[2:1] 01选择DMA Mode 1 (全双工) UART0-SCR | (1 1); // SCR[2:1] 01b // SCR[2]保持为0步骤4使能UART中断用于DMA传输完成、错误等事件// 8. 使能接收数据可用中断当RX FIFO达到触发水平会触发DMA同时也可配置产生中断通知CPU“DMA搬运开始” // 以及线路状态中断用于溢出、帧错误等 UART0-IER | (1 0) | // 使能接收数据可用中断 (1 2); // 使能接收线路状态中断 // 注意在DMA模式下THR空中断IER[1]通常由DMA处理可以不使能或者使能用于处理最后不满一次DMA传输的数据。步骤5配置DMA控制器以ARM Cortex-M系列为例这步高度依赖具体的MCU和DMA控制器。以下是概念性伪代码// 配置DMA通道0用于UART0 RX DMA0-CH[0].SRC_ADDR (uint32_t)(UART0-RHR); // 源地址UART数据寄存器 DMA0-CH[0].DST_ADDR (uint32_t)rx_buffer; // 目的地址内存缓冲区 DMA0-CH[0].CTRL DMA_CTRL_SRC_INC_NONE | // 源地址不递增外设寄存器 DMA_CTRL_DST_INC | // 目的地址递增 DMA_CTRL_SIZE_BYTE | // 传输宽度字节 DMA_CTRL_MODE_PINGPONG | // 可选乒乓模式双缓冲 DMA_CTRL_IRQ_EN; // 使能传输完成中断 DMA0-CH[0].COUNT BUFFER_SIZE; // 传输总字节数 // 将DMA通道0的请求源映射到UART0的RX DMAREQ信号具体映射查芯片手册 // 配置DMA通道1用于UART0 TX类似方向相反 DMA0-CH[1].SRC_ADDR (uint32_t)tx_buffer; DMA0-CH[1].DST_ADDR (uint32_t)(UART0-THR); DMA0-CH[1].CTRL DMA_CTRL_SRC_INC | DMA_CTRL_DST_INC_NONE | DMA_CTRL_SIZE_BYTE | DMA_CTRL_IRQ_EN; DMA0-CH[1].COUNT DATA_SIZE_TO_SEND; // 映射通道1到UART0的TX DMAREQ信号 // 使能DMA通道 DMA0-CH[0].CTRL | DMA_CTRL_ENABLE; DMA0-CH[1].CTRL | DMA_CTRL_ENABLE;注意事项DMA与中断的协同事件源FIFO触发水平产生的是硬件DMA请求它直接驱动DMA控制器搬运数据。同时这个事件也可能被配置为产生一个CPU中断通过IER寄存器。在典型的DMA应用中我们通常使能这个中断但中断服务程序ISR里不处理具体数据而是处理DMA传输完成、半满、错误等“元事件”。数据边界DMA是“盲目的”搬运工。如果UART接收的是不定长数据包DMA会持续搬运直到你设定的长度。你需要额外的机制如空闲中断、特定字符中断来判定一个数据包的结束。常用的方法是使能UART的接收线空闲中断如果硬件支持在检测到总线空闲一段时间后触发中断在ISR中处理已由DMA搬运到内存的完整数据包。缓冲区管理使用DMA通常需要环形缓冲区或双缓冲区乒乓缓冲。DMA可以配置为循环模式自动将数据填入环形缓冲区。CPU则从缓冲区的另一头读取数据。要小心处理缓冲区的读写指针和溢出问题。5. 相关高级功能与问题排查实录配置好FIFO触发和DMA只是搭建了高效传输的通道。在实际项目中还会遇到各种边界情况和问题。下面结合其他相关寄存器分享一些实战经验和排查技巧。5.1 状态寄存器与错误处理当通信出现问题时UART_LSR线路状态寄存器和UART_IIR中断标识寄存器是你的第一站。LSR[0] (DR)接收数据就绪。即使使用DMA在DMA搬运间隙或处理小数据时查询此位仍有意义。LSR[1] (OE)溢出错误。这是FIFO使用不当的典型标志。意味着RX FIFO已满但又有新数据到来导致数据丢失。解决方法提高CPU/DMA处理速度或优化触发水平确保在FIFO满之前启动搬运。检查DMA是否被正确使能并响应请求。LSR[2] (PE)/[3] (FE)/[4] (BI)奇偶校验、帧错误、中止错误。这些属于线路质量问题需检查波特率、电平、硬件连接。IIR寄存器用于快速判断中断源。在DMA模式下你可能会使能“接收数据可用”中断IIR[2]110b来启动DMA或处理缓冲区以及“字符超时”中断IIR[6]110b来处理接收到的、但不足以触发FIFO水平线的残留数据。5.2 SCR与SSR寄存器的其他关键位SCR[4] (RXCTSDSRWAKEUPEN)这是一个“唤醒”中断使能位。当RX、CTS或DSR引脚出现下降沿时可以产生一个独立的中断状态在SSR[1]。这在设备需要从低功耗模式被串口数据唤醒时非常有用。注意这个唤醒中断不映射到IIR需要单独查询SSR[1]位并且通过清除SCR[4]来清除中断状态。SSR[2] (DMACTRRST)DMA计数器复位控制。这个位非常关键当它为1时默认如果FIFO被复位通过FCR[1]或[2]对应的DMA传输计数器也会被复位。这意味着正在进行中的DMA传输会被强行终止。如果你不希望DMA计数被FIFO复位影响需要在初始化时将其清零(SSR ~(12))。否则在程序运行中动态复位FIFO可能导致DMA传输数据不完整。5.3 常见问题排查速查表现象可能原因排查步骤与解决方案数据接收不完整/丢失1. RX FIFO溢出OE错误2. DMA未正确启动或配置错误3. 触发水平设置过高数据迟迟不搬运1. 检查LSR[1]OE是否置位。2. 检查DMA通道是否使能源/目的地址、传输宽度、模式是否正确。3.使用逻辑分析仪或示波器抓取UART的RX引脚和DMAREQ信号确认数据到达时DMAREQ是否有效拉高。4. 降低RX触发水平测试是否改善。数据发送卡住1. TX FIFO触发水平设置不当2. DMA传输未完成或配置错误3. 流控CTS被拉高1. 检查TX FIFO是否已满可通过查询状态或TXFIFOFULL位。2. 检查DMA传输是否完成或尝试改用CPU中断填充TX FIFO。3. 检查CTS引脚电平确认对方设备是否可以接收。DMA只能搬运一次数据1. DMA配置为单次模式而非循环/自动重装模式。2. DMA传输完成后没有重新使能或配置新的传输。3. SSR[2]位导致DMA计数器被意外复位。1. 检查DMA控制器的模式配置改为循环模式或配置自动重装。2. 在DMA传输完成中断中重新设置传输计数和使能通道对于非循环模式。3. 检查SSR[2]位并根据需要清零。中断和DMA同时响应导致重复处理IER中使能了“接收数据可用”中断同时DMA也配置在该事件上。在纯DMA搬运场景可以考虑禁用“接收数据可用”的CPU中断IER对应位清零仅依靠DMA。或者在中断服务程序ISR中仅进行标志位检查等轻量级操作不搬运数据。低功耗模式下无法被串口数据唤醒唤醒功能未正确配置。1. 确认SCR[4]是否置1使能唤醒中断。2. 确认芯片支持并已进入相应的低功耗模式。3. 在唤醒后的初始化流程中检查并清除SSR[1]位并复位SCR[4]以清除唤醒中断标志。5.4 调试技巧与思维模型分层验证不要一开始就上DMA。先配置为纯中断模式并设置一个较低的触发水平如1-4字节确保基础通信和中断响应正常。然后再切换到DMA模式。善用“探针”如果芯片支持用GPIO引脚在关键位置如中断服务程序入口、DMA启动/完成时输出脉冲用示波器测量时间判断程序执行流是否符合预期。理解数据流在脑海中或纸上画出数据流图UART RX引脚 - 移位寄存器 - RX FIFO - (触发水平) - DMAREQ信号 - DMA控制器 - 内存缓冲区。沿着这个路径逐一检查每个环节是否畅通。寄存器初始化顺序有些寄存器之间存在依赖关系。一个稳健的初始化顺序是先关闭模块如有配置基本参数波特率、数据格式然后使能FIFO并设置触发水平接着配置DMA模式最后使能模块和中断/DMA。务必参考芯片勘误表Errata有些芯片的寄存器有特殊的写入顺序要求。配置UART的FIFO和DMA就像给一条河流修建水库和自动灌溉系统。TLR寄存器决定了水库的“警戒水位”DMA模式则是自动灌溉的阀门和管道。理解每个寄存器位背后的设计意图结合实际的业务数据流是涓涓细流还是洪水爆发才能设计出既高效又稳定的通信系统。这个过程少不了反复调试和测试但一旦调通其对系统整体性能的提升是立竿见影的。希望这篇深入的解析能成为你手边一份有用的参考。