1. 项目概述与核心价值
如果你在嵌入式开发中用过UART,大概率是从某个现成的驱动库或者HAL库开始的,比如STM32的HAL_UART_Transmit()。这很方便,但当你需要实现一个高可靠、高效率的串口通信,或者遇到一些奇怪的通信丢帧、中断不触发问题时,仅仅调用库函数就显得力不从心了。这时候,你必须深入到寄存器层面,理解UART这个“黑盒”内部到底是如何工作的。寄存器配置是串口通信的“底层语言”,它直接决定了硬件的每一个行为。
本文将以德州仪器(TI)某款典型UART外设的寄存器手册为蓝本,深入解析中断、流控与FIFO配置这三个最核心也最易出错的环节。我不会只翻译手册,而是结合我十多年在工业控制和通信模块开发中踩过的坑,告诉你每个寄存器位背后的设计逻辑、配置时的“潜规则”,以及如何通过组合配置实现稳定可靠的通信。无论你是正在调试一个115200bps的传感器数据采集,还是设计一个需要硬件流控的可靠数传链路,理解这些寄存器细节都能让你从“会用”进阶到“精通”。
2. UART寄存器全景与访问模式解析
在深入具体寄存器之前,我们必须建立一个全局视图。UART的寄存器组是一个地址映射的集合,CPU通过读写这些特定地址来控制UART外设的所有行为。手册中反复出现的“Refer to Section 21.3.7.1 to determine the modes...”这句话是关键前提,它指明了寄存器访问存在模式依赖。
2.1 寄存器访问模式与“钥匙”机制
许多增强型UART(如本文所述的型号)支持多种工作模式,例如标准UART模式、IrDA红外模式、CIR消费红外模式等。不同模式下,同一物理地址可能映射到功能完全不同的寄存器。Section 21.3.7.1通常定义了模式选择寄存器(如UART_MDR),在配置任何功能前,必须先确认并设置正确的工作模式。
更关键的一点是增强功能写使能锁。细心的你会发现,在IER(中断使能寄存器)、EFR(增强功能寄存器)、FCR(FIFO控制寄存器)和MCR(调制解调器控制寄存器)的描述中,频繁出现“Can be written only when EFR[4] = 1”。EFR[4]这个位,名为ENHANCEDEN,它就是一把“钥匙”。
设计逻辑解析:为什么需要这样一把“钥匙”?这主要是出于系统安全性和可靠性的考虑。像中断使能的高位(CTS、RTS、XOFF中断等)、FIFO触发阈值、以及MCR中的一些高级控制位,一旦误写可能导致通信异常甚至死锁。将这些关键配置位“锁”起来,要求程序员必须先显式地“解锁”(设置
EFR[4]=1)才能修改,相当于增加了一道保险。这要求我们的初始化序列必须是:
- 配置基本参数(如波特率、数据格式)。
- 使能增强功能访问(
EFR[4] = 1)。- 配置高级功能(流控、高级中断、FIFO阈值)。
- (可选)关闭增强功能访问锁,防止后续代码误操作。
2.2 核心寄存器功能分类
根据功能,我们可以将UART寄存器分为以下几大类,这有助于我们理解配置时的先后依赖关系:
| 寄存器类别 | 主要寄存器 | 核心功能 | 配置依赖 |
|---|---|---|---|
| 控制与状态 | LCR(线路控制寄存器) | 设置数据格式(字长、停止位、奇偶校验)、访问波特率分频器。 | 无 |
MCR(调制解调器控制寄存器) | 控制RTS、DTR等输出信号,启用环回测试模式。 | EFR[4]用于写MCR[7:5] | |
LSR(线路状态寄存器) | 只读。反映TX/RX状态、错误(帧错误、奇偶错误、溢出等)。 | 无 | |
MSR(调制解调器状态寄存器) | 只读。反映CTS、DSR、RI、DCD等输入信号的状态及变化。 | 无 | |
| 通信参数 | DLL/DLH(分频器锁存器) | 共同组成波特率发生器分频值。计算公式:DLL/DLH = 时钟频率 / (16 * 波特率)。 | 需先设置LCR[7]=1(DIV_EN) |
| 中断管理 | IER(中断使能寄存器) | 使能或禁用各类中断源(接收、发送、错误、Modem状态等)。 | EFR[4]用于写IER[7:4] |
IIR(中断标识寄存器) | 只读。当多个中断发生时,指示当前最高优先级的中断源,并可通过读取该寄存器清除中断挂起状态。 | 无 | |
| 流量控制 | EFR(增强功能寄存器) | 总开关,启用自动硬件流控(Auto CTS/RTS)、软件流控、特殊字符检测。 | 其ENHANCEDEN位是其他高级功能的钥匙 |
TCR(传输控制寄存器) | 定义硬件流控的“HALT”和“RESTORE”触发阈值。 | 需EFR[4]=1且MCR[6]=1 | |
XON1/XON2/XOFF1/XOFF2 | 存储软件流控的启/停字符。 | 无 | |
| 数据缓冲 | FCR(FIFO控制寄存器) | 启用/禁用FIFO,设置TX/RX FIFO触发阈值,清除FIFO。 | EFR[4]用于写FCR[5:4](TX触发阈值) |
| 杂项 | SPR(便签寄存器) | 一个通用的8位读/写寄存器,用于临时存储数据,不影响UART功能。 | 无 |
这个分类表是你进行UART驱动开发的“地图”。在初始化时,你必须遵循一个合理的顺序:先设置通信基本参数(LCR,DLL/DLH),再打开“钥匙”(EFR[4]),接着配置高级功能和中断(IER,FCR,EFR其他位,TCR),最后才使能中断并开始数据传输。
3. 中断系统深度配置与实战策略
中断是UART高效工作的核心机制,它让CPU从轮询LSR状态的苦力中解放出来。TI这款UART的中断系统设计得相当精细,但也因此容易配置不当。
3.1 IER:中断使能寄存器的位级解读
IER寄存器是中断系统的总开关。手册列出了7种中断类型,但实际位分布需要仔细对照:
| 位 | 名称 | 功能描述 | 使能条件与注意事项 |
|---|---|---|---|
| 0 | RHRIT | 接收器保持寄存器中断。当RX FIFO中的数据量达到触发阈值(由FCR[7:6]或TLR设定)时产生。这是最常用、最高效的接收数据方式。 | 通常必须使能。注意它同时使能了“超时中断”(Timeout Interrupt)。 |
| 1 | THRIT | 发送器保持寄存器中断。当TX FIFO中的数据量低于触发阈值(由FCR[5:4]设定)时产生。用于在发送缓冲区有空闲时,通知CPU填入新数据。 | 在需要连续发送大量数据时使能,以实现“填鸭式”发送。 |
| 2 | LINESTSIT | 接收线路状态中断。当发生帧错误(FE)、奇偶错误(PE)、溢出错误(OE)或间隔中断(BI)时触发。这是一个错误中断集合。 | 强烈建议使能。这是你发现物理层通信问题(如波特率不匹配、线路干扰)的主要途径。 |
| 3 | MODEMSTSIT | 调制解调器状态中断。当CTS、DSR、RI或DCD信号状态发生变化时触发。 | 在需要监控Modem信号(如检测对方设备上线、挂断)时使能。 |
| 4 | SLEEPMODE | 睡眠模式。置1时,当UART模块空闲(无活动)时停止波特率时钟以省电。 | 注意:此模式下无法写入DLL/DLH。修改波特率前需先禁用睡眠模式。 |
| 5 | XOFFIT | XOFF中断。当接收到软件流控停止字符(XOFF)时触发。 | 仅在启用软件流控(通过EFR[3:0]配置)后有意义。 |
| 6 | RTSIT | RTS中断。当RTS引脚状态从低变高(有效变无效)时触发。 | 用于监控硬件流控中对方是否准备好接收。需EFR[4]=1才能写。 |
| 7 | CTSIT | CTS中断。当CTS引脚状态从低变高(有效变无效)时触发。 | 用于监控硬件流控中对方是否允许发送。需EFR[4]=1才能写。 |
配置心得:对于大多数应用,我的基础配置是IER = 0x07(即RHRIT、THRIT、LINESTSIT使能)。这覆盖了数据收发和错误处理。只有在明确需要硬件流控状态监控或软件流控时,才会在解锁后配置IER[7:4]。
3.2 IIR:中断标识与优先级处理实战
IIR是一个只读寄存器,它是中断服务程序(ISR)的“导航仪”。它的核心作用是:当多个中断条件同时满足时,告诉你当前哪个是最高优先级的待处理中断。读取IIR本身会清除中断挂起标志(IT_PENDING位变为1),但不会清除导致中断的硬件状态(例如,读IIR不会清空RX FIFO)。
IIR[5:1]的IT_TYPE字段定义了中断类型和优先级:
IT_TYPE值 | 中断源 | 优先级 | 中断服务程序(ISR)中需要执行的操作 |
|---|---|---|---|
| 0x06 | 接收超时 | 2 | 读取RHR直到RX FIFO为空。超时中断与RHRIT共享使能位(IER[0]),当FIFO中有数据但未达到触发阈值,且一段时间无新数据时触发,用于处理“零碎”数据。 |
| 0x04 | RHR中断(数据就绪) | 2 | 读取RHR,直到读取的数据量达到或超过FIFO触发阈值,或FIFO为空。 |
| 0x02 | THR中断(发送寄存器空) | 3 | 向THR写入新的待发送数据,直到TX FIFO填满或数据发送完毕。 |
| 0x00 | Modem状态中断 | 4 | 读取MSR寄存器,该操作会自动清除MSR[3:0]的状态变化标志。 |
| 0x01 | 接收线路状态中断(错误) | 1 (最高) | 立即读取LSR寄存器。根据LSR[4:2]判断具体错误类型(BI, FE, PE),并执行错误处理(如丢弃错误数据、记录日志、重置接收状态)。这是一个关键步骤,不读LSR错误标志可能无法清除。 |
| 0x07 | XOFF/特殊字符中断 | 5 | 读取IIR和LSR确认,并根据软件流控逻辑暂停发送。 |
| 0x10 | CTS/RTS/DSR变化中断 | 6 | 读取MSR寄存器获取具体哪个信号发生了变化。 |
中断服务程序(ISR)的标准处理流程: 这是一个经过大量项目验证的稳健处理流程:
void UART_ISR(void) { uint16_t iir_value; do { iir_value = READ_REG(UART_IIR); // 检查是否有中断挂起 (bit 0 = 0 表示有中断) if (iir_value & 0x01) { break; // 无中断挂起,退出循环 } switch ((iir_value >> 1) & 0x0F) { // 提取IT_TYPE字段 case 0x06: // 接收超时中断 (优先级2) case 0x04: // RHR数据就绪中断 (优先级2) handle_rx_data(); break; case 0x02: // THR发送空中断 (优先级3) handle_tx_empty(); break; case 0x00: // Modem状态变化中断 (优先级4) handle_modem_status(); break; case 0x01: // 接收线路错误中断 (优先级1 - 最高) handle_line_error(); break; case 0x07: // XOFF/特殊字符中断 (优先级5) handle_xoff(); break; case 0x10: // CTS/RTS/DSR变化中断 (优先级6) handle_cts_rts_change(); break; default: // 未知中断类型,可能是保留值,读取LSR/MSR进行清理 READ_REG(UART_LSR); READ_REG(UART_MSR); break; } } while (1); // 循环处理,直到IIR显示无中断挂起 }关键技巧:使用
do...while循环处理IIR是必须的。因为在高数据速率下,可能在处理一个中断的过程中,又产生了新的中断(例如,正在读取RX FIFO时,又收到了新数据)。循环能确保在一次ISR调用中处理完所有累积的中断事件,避免频繁进出ISR带来的性能开销。
4. 流量控制机制详解与配置指南
流量控制是保证数据可靠传输,防止缓冲区溢出的关键。TI UART支持硬件流控(RTS/CTS)和软件流控(XON/XOFF),两者可以独立或组合使用。
4.1 硬件流控:Auto CTS与Auto RTS
硬件流控通过额外的两根信号线(RTS, CTS)实现实时握手。
- RTS (Request To Send):输出信号,本设备通知对方“我准备好接收了”。
- CTS (Clear To Send):输入信号,本设备检测对方是否“允许我发送”。
自动流控意味着UART硬件会自动根据FIFO状态管理这些引脚,无需CPU干预。
4.1.1 Auto CTS(自动清除发送)
- 功能:使能后(
EFR[7] = 1),UART在发送数据前会检查CTS引脚的电平。 - 逻辑:只有当CTS引脚为低电平(有效)时,UART才会从TX FIFO中取出数据发送。如果CTS变为高电平(无效),UART会立即停止发送当前字节(完成当前字节后停止),直到CTS再次变低。
- 应用场景:控制本设备的发送。当对方设备接收缓冲区快满时,拉高CTS,本设备自动暂停发送,防止对方溢出。
- 配置步骤:
- 确保
EFR[4]=1(解锁)。 - 设置
EFR[7]=1,使能Auto CTS。 - (可选)使能
IER[7](CTSIT),以便在CTS状态变化时产生中断,用于监控链路状态。
- 确保
4.1.2 Auto RTS(自动请求发送)
- 功能:使能后(
EFR[6] = 1),UART会根据自身RX FIFO的填充情况自动控制RTS引脚的输出电平。 - 逻辑:依赖于
TCR寄存器(传输控制寄存器)的两个阈值:TCR[3:0](RXFIFOTRIGHALT):HALT阈值。当本设备RX FIFO中的数据量达到或超过此值时,UART自动将RTS引脚拉高(无效),通知对方“暂停发送”。TCR[7:4](RXFIFOTRIGSTART):RESTORE阈值。当本设备RX FIFO中的数据量低于或等于此值时(因为被CPU读走),UART自动将RTS引脚拉低(有效),通知对方“可以继续发送”。
- 关键约束:手册明确警告:必须确保
TCR[3:0] > TCR[7:4]。即HALT阈值必须大于RESTORE阈值,形成一个“滞回区间”,防止RTS引脚在临界点附近频繁跳变,造成系统振荡。 - 应用场景:控制对方设备的发送。保护本设备的接收缓冲区不被冲垮。
- 配置步骤:
- 确保
EFR[4]=1且MCR[6]=1(解锁TCR)。 - 配置
TCR寄存器,设定合理的HALT和RESTORE阈值。例如,对于64字节的RX FIFO,可设置TCR[3:0] = 0x8(HALT at 32 bytes),TCR[7:4] = 0x4(RESTORE at 16 bytes)。 - 设置
EFR[6]=1,使能Auto RTS。 - (可选)使能
IER[6](RTSIT),用于监控。
- 确保
硬件流控配置示例: 假设我们使用64字节FIFO,希望在本机接收缓冲区达到48字节时让对方暂停,降到16字节时再恢复。
// 1. 解锁增强功能 WRITE_REG(UART_EFR, READ_REG(UART_EFR) | (1 << 4)); // Set ENHANCEDEN // 2. 配置TCR寄存器 (需同时设置MCR[6]) WRITE_REG(UART_MCR, READ_REG(UART_MCR) | (1 << 6)); // Set TCRTLR to access TCR // HALT Level = 48 bytes -> 48/4 = 12 = 0xC // RESTORE Level = 16 bytes -> 16/4 = 4 = 0x4 // TCR = (RESTORE << 4) | HALT = (0x4 << 4) | 0xC = 0x4C WRITE_REG(UART_TCR, 0x4C); // 3. 使能Auto RTS和Auto CTS uint16_t efr_value = READ_REG(UART_EFR); efr_value |= (1 << 7); // Enable AUTOCTSEN efr_value |= (1 << 6); // Enable AUTORTSEN WRITE_REG(UART_EFR, efr_value); // 4. (可选)使能CTS和RTS状态变化中断 WRITE_REG(UART_IER, READ_REG(UART_IER) | (1 << 7) | (1 << 6));4.2 软件流控:XON/XOFF配置
软件流控通过在线路上插入特殊的控制字符(XON, XOFF)来实现流量控制,无需额外的硬件连线。TI UART的软件流控功能通过EFR[3:0](SWFLOWCTRL) 位进行精细配置。
4.2.1 控制字符
- XON(Transmit On):通常为0x11 (DC1),通知对方开始发送。
- XOFF(Transmit Off):通常为0x13 (DC3),通知对方停止发送。
- XON1/XON2, XOFF1/XOFF2:该UART支持两组字符,提供更强的协议兼容性或冗余。
4.2.2 发送流控与接收流控SWFLOWCTRL的4个位独立控制发送和接收行为:
- 位[3:2] 控制发送流控(本设备发送XON/XOFF):
00: 不发送流控字符。01: 发送XON1/XOFF1对。10: 发送XON2/XOFF2对。11: 发送XON1/XON2或XOFF1/XOFF2对(需成对顺序发送)。
- 位[1:0] 控制接收流控(本设备识别并响应XON/XOFF):
00: 不识别流控字符。01: 识别并响应XON1/XOFF1。10: 识别并响应XON2/XOFF2。11: 识别并响应XON1/XON2或XOFF1/XOFF2对。
4.2.3 特殊字符检测EFR[5](SPECIALCHARDETECT) 位启用特殊字符检测功能。当使能时,接收到的数据会与XOFF2寄存器中的值进行比较。如果匹配,数据会被正常存入RX FIFO,同时IIR[4]位会被置1,并产生一个“XOFF/特殊字符中断”(IIR类型为0x07)。这可以用于实现自定义的带内协议控制命令。
软件流控配置示例: 配置UART使用XON1/XOFF1(0x11/0x13)进行双向软件流控。
// 1. 解锁增强功能 WRITE_REG(UART_EFR, READ_REG(UART_EFR) | (1 << 4)); // ENHANCEDEN // 2. 设置XON1和XOFF1字符 WRITE_REG(UART_XON1_ADDR1, 0x11); // XON1 = 0x11 WRITE_REG(UART_XOFF1, 0x13); // XOFF1 = 0x13 // 注意:手册建议如果使能软件流控,XON1和XOFF1应设为不同值。 // 3. 配置软件流控模式 // 目标:EFR[3:0] = 0b1111,即发送和接收都使用XON1/XOFF1对。 // 发送控制(EFR[3:2]) = 0b01 (发送XON1/XOFF1) // 接收控制(EFR[1:0]) = 0b01 (识别XON1/XOFF1) uint16_t efr_value = READ_REG(UART_EFR); efr_value &= ~(0xF); // 清零低4位 efr_value |= (1 << 3) | (1 << 2) | (1 << 1) | (1 << 0); // 设置为0b1111,但根据手册描述,组合逻辑并非简单相加。 // 更安全的做法是根据手册Table 21-37的位定义单独设置: efr_value |= (1 << 3); // 使能发送XON1/XOFF1 // efr_value |= (1 << 2); // 如果设为1,则变为发送XON2/XOFF2,这里我们不需要。 efr_value |= (1 << 1); // 使能接收XON1/XOFF1 // efr_value |= (1 << 0); // 如果设为1,则变为接收XON2/XOFF2,这里我们不需要。 // 根据表格,要使用XON1/XOFF1对,应设置EFR[3]=1, EFR[2]=0, EFR[1]=1, EFR[0]=0。 efr_value = (efr_value & ~0xF) | (1<<3) | (1<<1); // 即0b1010 = 0xA WRITE_REG(UART_EFR, efr_value); // 4. (可选)使能XOFF中断,以便及时响应停止请求 WRITE_REG(UART_IER, READ_REG(UART_IER) | (1 << 5)); // Enable XOFFIT5. FIFO配置与性能优化实战
FIFO(先进先出)缓冲区是提升UART吞吐率、降低CPU中断负载的核心。TI UART提供64字节的TX/RX FIFO,但其触发阈值的配置颇有讲究。
5.1 FCR:FIFO控制寄存器详解
FCR是一个只写寄存器(Write-only),用于控制FIFO的使能、清除和触发阈值。
| 位 | 名称 | 功能描述 | 配置要点与陷阱 |
|---|---|---|---|
| 0 | FIFO_EN | FIFO使能位。0=禁用(使用1字节缓冲),1=启用(64字节FIFO)。 | 关键限制:只能在波特率时钟停止时(即DLL和DLH都为0)修改。这意味着必须在初始化UART、设置波特率之前就配置好FIFO。 |
| 1 | RX_FIFO_CLR | 接收FIFO清除位。写1清除RX FIFO和其计数器,该位会自动清零。 | 在通信开始前或发生错误需要清空缓冲区时使用。写入后应延时几个时钟周期再操作FIFO。 |
| 2 | TX_FIFO_CLR | 发送FIFO清除位。写1清除TX FIFO和其计数器,该位会自动清零。 | 同上。注意,清除TX FIFO会丢弃其中所有未发送的数据。 |
| 3 | DMA_MODE | DMA模式选择。0=无DMA,1=模式1(TX和RX使用独立的DMA请求)。 | 同样需在波特率时钟停止时修改。选择DMA模式后,FIFO触发阈值将用于产生DMA请求。 |
| [5:4] | TX_FIFO_TRIG | TX FIFO触发阈值。决定TX FIFO空到什么程度时产生THRIT中断(或DMA请求)。 | 依赖EFR[4]。阈值选择见下表。 |
| [7:6] | RX_FIFO_TRIG | RX FIFO触发阈值。决定RX FIFO收到多少数据时产生RHRIT中断(或DMA请求)。 | 阈值选择见下表。注意与TCR中硬件流控阈值的协调。 |
5.2 FIFO触发阈值的选择策略
FCR[7:6]和FCR[5:4]的阈值选择逻辑受SCR(附加控制寄存器)和TLR(触发水平寄存器)影响,但最常见的情况是SCR[7]=0且TLR相关位为0,此时使用固定的几档阈值。
RX FIFO触发阈值 (FCR[7:6]):
00: 8 字符01: 16 字符10: 56 字符11: 60 字符
TX FIFO触发阈值 (FCR[5:4]):
00: 8 字符01: 16 字符10: 32 字符11: 56 字符
选择策略与性能权衡:
- 高吞吐、低延迟场景(如高速数据日志):设置较低的触发阈值(如RX=8,TX=8)。这会让中断更频繁,但能确保数据被及时处理,减少端到端延迟。适合CPU负载不重或使用DMA的情况。
- 低功耗、减少中断场景(如电池供电设备):设置较高的触发阈值(如RX=56,TX=32)。这减少了中断次数,让CPU有更多时间进入睡眠模式,但代价是单次中断处理的数据量变大,可能增加软件复杂度,并引入更大的传输延迟。
- 与硬件流控的协同:
RX_FIFO_TRIG(中断触发)和TCR中的RXFIFOTRIGHALT(硬件流控RTS触发)是两个独立的阈值。通常,为了让流控能提前动作,防止FIFO溢出,应设置RXFIFOTRIGHALT<RX_FIFO_TRIG。例如,RX FIFO深度64,你可以设置:TCR HALT= 48字节(对方在收到48字节时暂停)FCR RX Trigger= 56字节(本机在收到56字节时产生中断)- 这样,在FIFO达到48字节时,RTS变高通知对方暂停,为本机CPU争取了时间在FIFO达到56字节产生中断前进行处理,提供了安全裕量。
- DMA模式下的配置:在DMA模式下,触发阈值决定了DMA请求的产生时机。通常将TX/RX FIFO触发阈值设置为DMA突发传输长度的一半或四分之一,以实现DMA和FIFO的流水线操作,最大化总线效率。
FIFO初始化配置示例:
// UART初始化序列的一部分(在设置波特率之前!) void UART_FIFO_Init(void) { // 1. 确保波特率分频器为0(停止波特率时钟) // 通常通过设置LCR[7]=1来访问DLL/DLH,然后将其清零 uint16_t lcr_backup = READ_REG(UART_LCR); WRITE_REG(UART_LCR, lcr_backup | (1 << 7)); // 设置DIV_EN,访问分频器 WRITE_REG(UART_DLL, 0x00); WRITE_REG(UART_DLH, 0x00); WRITE_REG(UART_LCR, lcr_backup); // 恢复LCR,退出分频器访问模式 // 2. 配置FCR:使能FIFO,设置触发阈值,并清除FIFO uint16_t fcr_value = 0; fcr_value |= (1 << 0); // FIFO_EN = 1 fcr_value |= (1 << 1); // RX_FIFO_CLR = 1 (清除RX) fcr_value |= (1 << 2); // TX_FIFO_CLR = 1 (清除TX) fcr_value |= (0x01 << 6); // RX_FIFO_TRIG = 01 (16字节触发) // TX_FIFO_TRIG 需要EFR[4]=1才能写,我们稍后配置 WRITE_REG(UART_FCR, fcr_value); // 3. (后续)解锁增强功能后,配置TX触发阈值 // WRITE_REG(UART_EFR, READ_REG(UART_EFR) | (1 << 4)); // ENHANCEDEN // fcr_value = READ_REG(UART_FCR); // 注意:FCR是只写的,不能回读。需要重新构建值或使用变量保存。 // fcr_value |= (0x01 << 4); // TX_FIFO_TRIG = 01 (16字节触发) // WRITE_REG(UART_FCR, fcr_value); // 4. 重新配置正确的波特率(设��DLL/DLH) // ... 波特率计算与设置代码 ... }严重警告:
FIFO_EN、DMA_MODE等位的修改必须在波特率时钟停止时进行。一个常见的错误流程是:先设置好波特率,然后尝试使能FIFO,结果导致FIFO无法正常工作或通信乱码。正确的顺序永远是:停止时钟 -> 配置FIFO/DMA -> 设置波特率 -> 开始通信。
6. 典型问题排查与调试技巧
即使按照手册配置,在实际项目中仍会遇到各种问题。以下是我总结的几个典型场景和排查思路。
6.1 中断不触发或触发异常
- 症状:数据已收到(用示波器或逻辑分析仪确认),但
RHRIT中断始终不产生。 - 排查步骤:
- 检查IER配置:确认
IER[0](RHRIT) 已置1。别忘了IER[7:4]需要EFR[4]=1才能写入。 - 检查FIFO使能与触发阈值:确认
FCR[0]=1(FIFO使能)。检查FCR[7:6]的RX触发阈值是否设置合理。如果阈值设为56,而你只发送了10个字节,中断自然不会产生。 - 检查IIR状态:在ISR中或主循环里读取
IIR寄存器。如果IIR[0]=0且IT_TYPE字段是0x04或0x06,说明中断已产生但可能被更高优先级中断抢占或标志位处理不当。 - 检查全局中断使能:确认CPU内核的全局中断以及UART外设的中断通道已在NVIC中使能。
- 检查LSR错误:如果发生了帧错误、奇偶错误等,可能会触发更高优先级的
LINESTSIT(IIR类型0x01)。必须在该中断的ISR中读取LSR来清除错误状态,否则可能会阻塞其他中断。
- 检查IER配置:确认
6.2 硬件流控失效,数据仍然丢失
- 症状:使能了Auto RTS/Auto CTS,但对方设备仍在RX FIFO满时发送数据,导致溢出。
- 排查步骤:
- 确认物理连接:用万用表或示波器检查RTS/CTS线是否正确连接且电平有效(低电平有效)。
- 验证EFR配置:读取
EFR寄存器,确认AUTORTSEN和AUTOCTSEN位确实已置1。 - 检查TCR配置:确认已正确设置
MCR[6]=1并写入了TCR寄存器。重中之重:验证TCR[3:0](HALT) >TCR[7:4](RESTORE)。如果两者相等或颠倒,流控行为将不可预测。 - 检查FIFO深度与阈值匹配:确保
TCR中设置的阈值(乘以4后的字节数)不超过实际的RX FIFO大小(64字节)。 - 监控MSR寄存器:在调试时,定期读取
MSR寄存器,观察CTS_STS和RTS相关位的变化,看流控信号是否按预期翻转。
6.3 软件流控字符被当作数据接收
- 症状:使能了软件流控,但发送的XON/XOFF字符出现在了应用层接收的数据流中。
- 排查步骤:
- 检查EFR[3:0]配置:确认
SWFLOWCTRL位设置正确。例如,如果你希望识别XON1/XOFF1,需要设置EFR[3]=1且EFR[1]=1(根据手册表格组合)。 - 检查XON/XOFF寄存器值:确认
XON1_ADDR1和XOFF1寄存器中写入的值与你协议中定义的流控字符一致(通常是0x11和0x13)。 - 理解流控字符处理:当UART识别出流控字符时,默认情况下该字符不会存入RX FIFO。如果你需要同时处理流控和保留该字符,可能需要使用“特殊字符检测”功能(
EFR[5]),并配置XOFF2寄存器,然后在中断中处理。
- 检查EFR[3:0]配置:确认
6.4 FIFO使能后通信乱码
- 症状:使能FIFO后,收发的数据出现错位、乱码或完全不对。
- 排查步骤:
- 确认初始化顺序:这是最常见的原因。回顾你的代码,是否在设置波特率(
DLL/DLH)之后才使能FIFO(FCR[0]=1)?正确的顺序必须是:停止时钟(DLL/DLH=0) -> 配置FCR -> 设置波特率。 - 检查LCR配置:确保数据格式(字长、停止位、奇偶校验)在通信双方完全一致。FIFO不影响帧格式。
- 检查FIFO清除位:在初始化时或通信异常后,尝试写
FCR[1]和FCR[2]来清除RX/TX FIFO。注意,写入清除位后,硬件需要几个时钟周期完成清除操作,建议添加短暂延时。 - 使用环回测试:将
MCR[4](LOOPBACKEN) 置1,进入内部环回模式。在此模式下,发送的数据会直接环回到接收端,可以排除外部线路问题,专注验证UART内核和FIFO的配置是否正确。
- 确认初始化顺序:这是最常见的原因。回顾你的代码,是否在设置波特率(
调试UART,尤其是涉及流控和FIFO等高级功能时,逻辑分析仪是你的最佳伙伴。用它同时抓取TX、RX、RTS、CTS信号线,以及中断引脚,可以直观地看到数据流、流控信号的变化与中断触发时刻的对应关系,很多软件上难以捉摸的问题在波形图前会一目了然。