UART进阶应用:流控制、9位模式与FIFO操作实战解析

📅 2026/7/23 9:36:17
UART进阶应用:流控制、9位模式与FIFO操作实战解析
1. 项目概述深入理解UART的进阶功能在嵌入式开发领域UART通用异步收发传输器几乎是每个工程师入门时都会接触的第一个通信接口。它简单、直接两根线TX和RX就能让两个设备“说上话”。但如果你认为UART仅仅是配置个波特率、数据位、停止位和校验位就完事了那可能就错过了它一半的威力。在实际项目中尤其是面对高速数据流、多设备网络或高可靠性要求的场景UART那些“高级”功能——流控制、9位寻址模式和FIFO操作——就从数据手册里枯燥的寄存器描述变成了解决实际痛点的利器。我遇到过不少项目初期为了图省事只用了最基本的UART功能。结果设备在复杂电磁环境下或者当一端数据处理不及时时就会出现数据丢失排查起来异常头疼。后来才明白流控制就是解决这类“你发太快我处理不过来”问题的握手协议。而当一个主设备需要与多个从设备通信但又不想为每个从设备单独拉一根片选线时9位模式提供了一种优雅的软件寻址方案。至于FIFO它不仅仅是缓冲数据那么简单合理的配置能大幅降低CPU的中断负载让系统更流畅地处理通信任务。本文将以TI Tiva™系列微控制器的UART模块为例但所讲的原理和思路具有普适性。我们将抛开那些照本宣科的寄存器列表聚焦于这三个核心进阶功能它们解决了什么问题在寄存器层面是如何运作的以及在代码实操中又有哪些必须注意的“坑”无论你是正在调试一个传感器网络还是设计一个多节点的控制板理解这些内容都能让你的通信层设计更加稳健和高效。2. 流控制机制深度解析从硬件握手到软件协调数据通信就像两个人对话如果一方滔滔不绝另一方来不及听和理解信息就会丢失。流控制Flow Control就是确保通信双方步调一致的“对话规则”。UART主要支持硬件流控制和软件流控制两种机制。2.1 硬件流控制RTS与CTS的四种工作模式硬件流控制通过额外的两根信号线实现RTSRequest To Send请求发送和CTSClear To Send清除发送。这是一种全双工的握手机制每一端都用对方的“允许”信号来控制自己的“发送”行为。在Tiva UART中通过配置UARTCTL寄存器中的RTSEN和CTSEN位可以精确控制流控制模式。这不仅仅是“启用”或“禁用”那么简单而是有四种组合对应不同的应用场景模式RTSENCTSEN描述典型应用场景双向流控11同时启用RTS输出和CTS输入检测。本设备在准备好接收时会拉低RTS信号通知对方本设备仅在检测到对方CTS信号有效低电平时才会发送数据。高速、全双工、对数据可靠性要求极高的点对点通信。双方收发完全独立且受控。仅CTS流控01仅启用CTS输入检测。本设备发送受对方控制但本设备的接收状态不通知对方RTS无效。本设备为纯“发送者”或接收能力极强无需通知对方暂停。例如MCU向始终准备好的打印机发送数据。仅RTS流控10仅启用RTS输出。本设备会通知对方自己的接收状态但本设备发送不受对方控制。本设备为纯“接收者”或发送速率很低无需对方控制。例如MCU接收来自传感器的低速数据流。禁用流控00RTS和CTS功能均被禁用信号线可作普通GPIO或悬空。低速通信、单工通信或线缆资源紧张只能使用TX/RX两线的情况。关键细节与避坑指南信号有效电平通常RTS和CTS信号在“准备好”时为低电平0在“未准备好”时为高电平1。这是RS-232标准中的定义但具体要看收发器芯片的电平转换逻辑。务必确认你的电平转换电路如MAX3232和对方设备遵循同一标准。RTS的自动管理当RTSEN1时RTS信号由硬件自动管理。此时软件尝试通过UARTCTL寄存器的RTS位去手动修改RTS输出值是无效的并且该位的状态也应被忽略。这是一个常见的误区工程师可能会试图手动拉高/拉低RTS来控制流程但在硬件流控启用时这由UART模块的FIFO状态自动决定。例如当接收FIFO快满时硬件会自动置位RTS变为高电平表示“未准备好”通知对方暂停发送。CTS的发送阻塞当CTSEN1且CTS输入信号为无效状态通常为高电平时UART的发送器会自动停止直到CTS变为有效。这个过程对软件是透明的你写入UARTDR的数据会停留在FIFO中不会丢失。这比软件去轮询或判断要可靠和及时得多。2.2 软件流控制XON/XOFF与中断状态监控当硬件引脚资源紧张时软件流控制是一种替代方案。它不占用额外的物理引脚而是通过定义特殊的数据字符来控制数据流。最经典的是XON/XOFF协议但Tiva UART的“软件流控制”更多是指通过调制解调器状态中断来模拟类似行为。UART模块可以监控UnCTS、UnDSR、UnDCD、UnRI这些调制解调器状态输入信号的变化并产生中断。虽然这些信号本意用于连接调制解调器但在自定义系统中你可以将它们重新定义为流控制信号。操作流程如下使能中断在UARTIM寄存器中设置相应的位如CTSMIM,DSRMIM等为1使能对应信号的状态变化中断。中断处理当这些引脚的电平发生变化时会触发中断。在中断服务程序ISR中读取UARTRIS原始中断状态或UARTMIS屏蔽后中断状态寄存器来确定是哪个信号发生了变化。软件决策根据信号状态你的软件可以决定是暂停发送例如将数据缓存在应用层队列还是恢复发送。例如你可以约定当某个GPIO连接到UnCTS被拉高时表示对方缓冲区满本方应停止发送。软件流控 vs 硬件流控优点节省引脚协议灵活可自定义。缺点可靠性存疑。控制字符如XON/XOFF本身是数据流的一部分如果这些字符在传输中因噪声而损坏或恰好出现在用户数据中需要转义处理就会导致流控制逻辑混乱。此外软件响应中断、处理状态再控制发送的延迟远高于硬件信号的直接电气控制在高速数据流下可能导致数据溢出。实操心得在资源允许的情况下优先使用硬件流控制。它由硬件保证实时性几乎不增加CPU负担是可靠高速通信的基石。软件流控制仅适用于低速、非关键或引脚资源极其紧张的场景并且必须在应用层设计好错误恢复机制。3. 9位UART模式构建简易多节点网络在传统的8位UART通信中一个主机连接多个从机通常需要额外的片选CS或使能EN信号线这会导致布线复杂。9位模式巧妙地利用了一个额外的位第9位作为“地址/数据”标识位从而在单条总线TX/RX共连上实现寻址功能。3.1 工作原理与配置在9位模式下每个传输的“字”变成了9位8位数据 1位标识位。这第9位通常被用来区分当前字节是“地址帧”还是“数据帧”。使能9位模式通过设置UART9BITADDR寄存器中的9BITEN位为1来启用。设置本机地址在UART9BITADDR寄存器中写入本设备的地址一个8位值。设置地址掩码UART9BITAMASK寄存器默认为0xFF即精确匹配。你可以将其设置为0xFC二进制11111100这样当地址字节的高6位匹配时即认为地址匹配实现了地址组Group的功能。接收逻辑当接收到的字节其第9位为1时硬件认为这是一个地址字节。它会将这个地址与UART9BITADDR中预设的地址经过掩码过滤后进行比较。如果匹配该地址字节会被存入接收FIFO并可以产生一个中断需配置通知CPU“有设备在呼叫我”。此后所有第9位为0的数据字节都会被正常接收并存入FIFO。如果不匹配该地址字节会被静默丢弃且不会产生接收中断。此后所有后续第9位为0的数据字节也会被持续丢弃直到收到下一个第9位为1的地址字节。发送逻辑默认情况下所有通过UARTDR寄存器发送的字节其第9位都会被硬件清零即作为数据字节发送。如果要发送一个地址字节第9位置1需要一点“技巧”软件需要临时将UART的奇偶校验配置为“强制奇校验Sticky Parity, Odd”。这会使得硬件在发送该字节时将校验位强制置为1而这个校验位在9位模式下就被当作第9位地址标识位使用。发送完这个地址字节后需要立即将奇偶校验设置恢复为无校验或正常校验模式以便后续数据字节的第9位为0。3.2 应用场景与配置示例假设我们有一个主机和三个从机地址分别为0x01, 0x02, 0x03挂在同一条UART总线上。主机向从机0x02发送数据“Hello”的流程主机配置UART为9位模式。主机临时设置奇偶校验为“强制奇校验”然后向UARTDR写入地址0x02。硬件会发送一个9位字0x02数据位 1第9位地址标识。主机立即将奇偶校验恢复为“无校验”。主机依次发送字符‘H‘, ‘e‘, ‘l‘, ‘l‘, ‘o‘。硬件发送这些字节时第9位均为0。总线上所有从机都收到了地址帧0x02第9位1。从机0x02地址匹配它会产生中断并开始接收后续的5个数据字节。从机0x01和0x03地址不匹配它们会丢弃地址帧0x02并忽略后续所有第9位为0的数据帧。关键配置代码片段以Tiva库函数风格为例// 从机配置例如地址为0x02 UART9BitAddrSet(UART0_BASE, 0x02); // 设置自身地址 UART9BitAddrMaskSet(UART0_BASE, 0xFF); // 设置精确地址掩码 UART9BitEnable(UART0_BASE); // 使能9位模式 UARTFIFOEnable(UART0_BASE); // 通常需要使能FIFO以配合中断使用 // ... 使能接收中断等操作 // 主机发送地址帧目标地址0x02 UARTParityModeSet(UART0_BASE, UART_CONFIG_PARITY_STICK); // 临时设为强制奇校验 UARTCharPut(UART0_BASE, 0x02); // 发送地址字节第9位会被硬件置1 UARTParityModeSet(UART0_BASE, UART_CONFIG_PARITY_NONE); // 恢复无校验 // 接着发送数据帧 UARTCharPut(UART0_BASE, ‘H‘); // ... 发送剩余数据注意事项9位模式下的接收器必须工作在无校验No Parity模式。因为第9位被用作地址/数据标识不能再用于奇偶校验。此外发送地址帧时的“奇偶校验覆盖”技巧需要精细的时序控制确保只有一个字节使用此设置。在DMA或FIFO自动发送场景下这需要更复杂的软件调度。4. FIFO操作与中断优化提升通信效率的关键FIFOFirst In, First Out缓冲区是UART模块提升性能、减轻CPU负担的核心部件。Tiva UART包含两个独立的16x8位16字节深度8位宽度FIFO分别用于发送和接收。4.1 FIFO的启用与基本状态监控复位后FIFO是禁用的此时发送和接收数据寄存器更像是1字节深的“保持寄存器”。通过设置UARTLCRH寄存器中的FENFIFO Enable位为1来启用它们。启用FIFO后数据读写都通过UARTDR寄存器进行但硬件会自动管理FIFO的入队和出队。这对于软件来说是透明的你依然像操作单个寄存器一样读写UARTDR。状态监控主要通过UARTFR标志寄存器RXFE接收FIFO空为1时接收FIFO为空。这在判断是否还有数据可读时非常有用。RXFF接收FIFO满为1时接收FIFO已满。如果此时再收到数据会发生溢出错误Overrun Error新数据会丢失。TXFE发送FIFO空为1时发送FIFO为空。注意即使TXFE1最后一个字节可能还在移位寄存器中发送此时BUSY位可能仍为1。TXFF发送FIFO满为1时发送FIFO已满此时再写入UARTDR会被阻塞或丢失数据取决于具体实现。BUSY为1表示UART正在发送数据包括FIFO中的数据以及正在移位输出的数据。仅当发送移位寄存器和发送FIFO都为空时此位才清零。溢出错误OE是一个需要严重关注的状态位它在UARTRSR寄存器中。当接收FIFO已满但硬件又收到一个新字符时OE位会被置1并且新字符会丢失覆盖了移位寄存器中的内容但不会破坏FIFO内已有的数据。OE位必须由软件写UARTECR寄存器来清除。4.2 中断触发级别的精细控制如果没有FIFO每收到一个字节就会产生一次接收中断CPU频繁被中断打扰效率低下。FIFO使得我们可以“攒一批”数据再通知CPU处理。这是通过UARTIFLS中断FIFO级别选择寄存器实现的。该寄存器可以独立配置发送和接收FIFO的中断触发阈值接收中断RXIFLSEL当接收FIFO中的数据量达到或超过设定的阈值时触发接收中断。例如设置为1/2默认值则当FIFO中存有8个或以上字节时触发中断。发送中断TXIFLSEL当发送FIFO中的数据量低于设定的阈值时触发发送中断。例如设置为1/2则当FIFO中剩余空间大于等于8个字节即已发送数据少于8个时触发中断提示软件可以继续填充数据。可选的阈值有1/82字节、1/44字节、1/28字节、3/412字节、7/814字节。配置策略低延迟优先如果希望尽快响应数据可将接收阈值设为1/8或1/4。但这会增加中断频率。高吞吐量优先如果希望减少中断次数集中处理数据可将接收阈值设为3/4或7/8。但要注意如果数据产生速度很快可能在达到阈值前FIFO就已溢出。发送中断的妙用合理设置发送中断阈值可以实现“零等待”填充。例如在DMA传输或大数据量发送前先手动填充一部分数据启动发送然后当发送中断触发表明FIFO有空闲时再填充下一批数据可以保持发送链路持续饱和。4.3 接收超时中断处理不定长数据包的利器这是一个非常实用但常被忽略的功能。接收超时中断Receive Timeout Interrupt在以下情况触发接收FIFO非空但在一个特定的“超时期”内没有收到任何新数据。超时期计算当HSE高位使能位为0时超时期为32个位时间当HSE为1时为64个位时间。位时间 1 / 波特率。例如在115200波特率下一个位时间约8.68μs32位时间约278μs。应用场景处理不定长的数据包。例如一个传感器能一次发送一串以回车符结尾的数据。如果没有超时中断你只能在收到回车符后处理。但如果数据包没有固定结束符或者结束符丢失软件就会一直等待。启用超时中断后当一串连续数据发送完毕在超过278μs的静默期后中断就会触发此时你可以读取FIFO中已接收到的所有数据作为一个完整的数据包进行处理。清除条件该中断在软件读取FIFO使其变空或向UARTICR寄存器的对应位写1时清除。配置示例// 使能接收超时中断 UARTIntEnable(UART0_BASE, UART_INT_RT); // 在中断服务函数中判断 if(UARTIntStatus(UART0_BASE, true) UART_INT_RT) { // 清除超时中断 UARTIntClear(UART0_BASE, UART_INT_RT); // 读取FIFO中所有数据直到为空 while(!UARTCharsAvail(UART0_BASE)) { char data UARTCharGetNonBlocking(UART0_BASE); // 处理数据 data... } }5. 中断与DMA协同解放CPU的高级玩法对于高速、大数据量的UART通信频繁的中断仍然会给CPU带来压力。此时直接内存访问DMA是终极解决方案。Tiva UART与µDMA控制器紧密集成可以自动将接收FIFO的数据搬移到内存或将内存的数据搬移到发送FIFO整个过程无需CPU干预。5.1 DMA请求与操作模式通过UARTDMACTL寄存器启用DMARXDMAE使能接收DMA。TXDMAE使能发送DMA。DMA请求分为两种模式单次请求Single Request接收只要接收FIFO中有数据就发出请求。发送只要发送FIFO中至少有一个空位就发出请求。突发请求Burst Request接收当接收FIFO中的数据量达到或超过UARTIFLS中设置的触发水平时发出请求。这允许DMA一次传输一批数据效率更高。发送当发送FIFO中的空余位置达到或超过触发水平时发出请求。关键配置UARTDMACTL中的DMAERR位如果此位置1当发生接收错误如帧错误、奇偶校验错误时UART会自动禁用接收DMA请求。这是一个安全特性防止在通信出错时DMA还在盲目搬运错误数据。错误条件需要通过清除对应的UART错误中断来恢复。5.2 DMA传输完成与EOT中断DMA传输完成的通知机制有两个层面DMA传输完成DMA Done当µDMA控制器完成一次到TX FIFO的写入或从RX FIFO的读取后会向UART模块发送dma_done信号。UART会置位UARTRIS寄存器中的DMATXRIS或DMARXRIS位。你可以通过设置UARTIM寄存器中的DMATXIM/DMARXIM位来使能这些状态产生中断。这仅表示DMA把数据从内存搬到了FIFO或反之的任务完成了。发送结束End of Transmission, EOT数据从DMA搬到发送FIFO并不代表数据已经通过TX引脚发送出去了。要确认所有数据包括FIFO中和移位寄存器中的都已物理发送完毕需要用到EOT功能。通过设置UARTCTL寄存器中的EOT位为1当发送移位寄存器发出最后一个比特后会产生EOT中断需设置EOTIM位。这对于需要严格时序例如发送完一帧数据后立即切换引脚方向如半双工RS-485的应用至关重要。一个典型的“DMA发送EOT中断”流程配置UART和DMA发送通道。设置EOT位和EOTIM位。启动DMA传输将数据从内存搬运到UART发送FIFO。CPU可转而处理其他任务。DMA传输完成触发DMA完成中断可选此时数据可能在FIFO中排队。当最后一个比特离开发送移位寄存器时触发EOT中断。在EOT中断服务程序中你可以安全地进行后续操作例如拉高RS-485的使能引脚。避坑指南DMA和中断的配合需要仔细设计。常见错误是同时使能了FIFO级别中断和DMA导致两者冲突。通常如果使用了DMA进行大数据量传输应禁用对应的FIFO级别中断UARTIM中对应的位让DMA完全接管数据传输。中断仅用于处理错误、超时或EOT等事件。6. 初始化、配置与调试实战理解了所有高级功能后一个健壮的UART初始化配置流程就不仅仅是设置波特率那么简单了。下面是一个包含FIFO、中断和9位模式的综合初始化示例步骤并附上关键计算和调试技巧。6.1 完整的初始化步骤与计算假设系统时钟为20MHz目标配置为115200波特率8位数据位1位停止位无校验启用16级FIFO启用接收中断触发点为1/2启用接收超时中断并配置为9位模式从机地址0x55。禁用UART任何关键配置修改前先清除UARTCTL中的UARTEN位。计算并设置波特率除数BRD公式BRD SysClk / (16 * BaudRate)计算20,000,000 / (16 * 115,200) ≈ 10.8507整数部分UARTIBRD10(0xA)小数部分UARTFBRDinteger(0.8507 * 64 0.5) 54(0x36)注意写入UARTIBRD和UARTFBRD后必须再写入一次UARTLCRH寄存器新的波特率才会生效。配置线路控制UARTLCRHFEN 1启用FIFO。WLEN 3(0b11)8位数据长度。STP2 01位停止位。PEN 0,EPS 0,SPS 0无校验。该寄存器值约为0x70(FEN1, WLEN3)。注意在9位模式下接收端PEN必须为0。配置9位模式UART9BITADDR 0x55设置自身地址。UART9BITAMASK 0xFF精确地址匹配。设置UART9BITADDR中的9BITEN 1。配置中断FIFO触发水平UARTIFLS假设设置RX触发点为1/28字节TX触发点为1/82字节。对应的寄存器值需要根据数据手册的编码确定。配置中断掩码UARTIM使能接收中断RXIM和接收超时中断RTIM。使能UART最后设置UARTCTL中的UARTEN位为1。配置GPIO复用此步骤必须在使能UART模块时钟和GPIO时钟后进行将对应的引脚如PA0-PA1功能复用到UART上。6.2 调试技巧与常见问题排查即使配置正确在实际硬件调试中也可能遇到问题。以下是一些快速排查的思路问题1完全收不到数据检查基础三要素波特率、数据格式数据位、停止位、校验位两端是否绝对一致。一个常见的坑是“115200”实际是“115200 /- 误差”如果两端时钟精度不够累积误差会导致错位。可以用示波器测量位时间进行验证。检查物理连接TX接RX是否交叉连接地线是否共地这是最容易被忽略的低级错误。检查引脚复用确认GPIO的AFSEL和PCTL寄存器是否正确配置将引脚功能切换到了UART而非普通的数字输入输出。问题2能收到数据但全是乱码或特定错误帧错误FE停止位检测为低电平。检查双方停止位设置或线路上是否存在严重噪声导致停止位被拉低。奇偶校验错误PE如果启用了校验检查双方校验设置奇校验/偶校验是否一致。在9位模式下接收端必须禁用校验。溢出错误OE接收FIFO或保持寄存器已满新数据丢失。这说明你的软件读取速度跟不上接收速度。解决方案1) 提高接收中断优先级2) 启用FIFO并使用DMA3) 增加接收缓冲区大小在应用层4) 使用硬件流控制RTS/CTS让对方暂停发送。问题3使用FIFO和中断时数据不完整检查中断触发水平如果接收中断触发水平设的太高如7/8而数据包很短小于14字节可能永远无法触发中断。对于短包建议设置为1/4或1/2或结合使用接收超时中断。检查中断服务程序ISR是否读取了足够的数据在FIFO模式下中断触发后应循环读取UARTDR直到RXFE标志置位或直到读取的字节数满足预期。不要只读一次。清除中断标志确保在ISR中正确清除了对应的中断源通过写UARTICR寄存器。未清除的中断标志会导致中断持续触发陷入死循环。问题49位模式地址不匹配确认发送端第9位设置主机发送地址帧时是否正确地通过“强制奇校验”方式将第9位置1了用逻辑分析仪抓取波形查看地址字节的停止位前面那一位即第9位是否为高电平。确认接收端配置从机的9BITEN是否使能UART9BITADDR地址和UART9BITAMASK掩码设置是否正确接收端是否处于无校验模式检查总线竞争在多从机系统中确保只有一个从机在主机寻址后回复数据。多个从机同时发送会造成总线冲突。通常采用主机轮询从机仅在地址匹配后才被“激活”回复的模式。通过系统地理解流控制、9位模式和FIFO操作这些进阶功能并掌握其配置细节与调试方法你的UART应用将不再局限于简单的点对点调试输出而是能够胜任更复杂、更可靠的工业通信任务。这些功能是嵌入式通信协议的基石花时间吃透它们在日后面对I2C、SPI乃至更复杂的现场总线时你也会发现其核心思想是相通的。