1. 项目概述与核心价值
搞嵌入式开发,尤其是涉及到串口通信,UART寄存器配置是绕不开的一道坎。很多朋友在初期接触时,面对手册里密密麻麻的寄存器位描述,常常感到无从下手,要么是照抄例程知其然不知其所以然,要么是配置后通信不稳定却找不到原因。我自己在调试各种MCU和专用UART芯片时,也踩过不少坑,比如FIFO深度没设对导致数据丢失,或者中断使能没配全导致程序卡在等待状态。
UART,这个看似简单的“串行通信老将”,其内部寄存器机制实则精巧而复杂。它绝不仅仅是配置个波特率、数据位、停止位那么简单。真正要让它稳定、高效地跑起来,尤其是在中断驱动、DMA配合或是利用硬件流控的场景下,你必须深入理解几个核心寄存器组:负责数据吞吐的RHR/THR、掌管中断命脉的IER/IIR、以及控制数据流节奏的FCR和TCR。这些寄存器就像乐队的指挥,各自负责不同的声部,只有协调一致,才能奏出稳定流畅的数据交响曲。
本文将以德州仪器(TI)某款集成UART/IrDA/CIR功能的模块为例,带你彻底拆解这些关键寄存器。我不会只停留在手册翻译的层面,而是结合我实际调试中的经验和教训,告诉你每个配置位背后的设计逻辑、不同模式(UART、IrDA、CIR)下的差异、以及那些手册里可能一笔带过但实践中至关重要的“坑点”。无论你是正在调试一块新的通信板卡,还是想优化现有串口驱动的性能,相信这篇详尽的寄存器解读都能给你带来直接的帮助。
2. 核心寄存器功能与设计逻辑拆解
UART模块的寄存器虽然数量不少,但我们可以按其功能划分为几个清晰的阵营:数据寄存器、中断控制寄存器、线路控制寄存器、状态寄存器以及高级功能控制寄存器。理解这个分类,是高效配置和调试的基础。
2.1 数据缓冲核心:RHR与THR的工作机制
接收保持寄存器(RHR)和发送保持寄存器(THR)是UART与CPU交换数据的直接窗口。但很多人容易混淆它们与底层移位寄存器的关系。
RHR(Receiver Holding Register):这是CPU读取接收数据的地方。但请注意,它并不是第一个接触到线上串行数据的部件。数据流是这样的:RX引脚 ->接收移位寄存器->RHR(FIFO)-> CPU。接收移位寄存器负责将逐位到来的串行数据组装成一个完整的字节(或字),然后移入RHR。当FIFO功能禁用时(FCR[0]=0),RHR退化为一个单字节的缓冲区。手册中特别强调了一点:发生溢出(Overrun)时,RHR中的数据不会被覆盖。这是一个重要的安全设计,意味着当CPU来不及读取,而新数据又持续到来导致溢出时,RHR中未被读取的旧数据会被保护起来,避免被冲掉,这为错误恢复提供了可能——你至少能知道溢出前最后一个正确字节是什么。
THR(Transmit Holding Register):这是CPU写入待发送数据的地方。数据流相反:CPU ->THR(FIFO)->发送移位寄存器-> TX引脚。CPU将数据写入THR后,UART模块的发送逻辑会自动将其加载到发送移位寄存器中,并按照设定的波特率串行发出。同样,FIFO禁用时,它是一个单字节缓冲区。
FIFO的妙用:为什么现代UART普遍集成FIFO?核心目的是降低CPU中断频率,提升系统效率。没有FIFO时,每收/发一个字节就可能产生一次中断,CPU忙于上下文切换,效率低下。使能64字节的FIFO后,你可以设置一个触发阈值(例如,收到16字节才产生中断),让CPU一次处理一批数据,大大减轻了负担。这在对实时性要求不那么苛刻,但数据量较大的场景(如文件传输、数据日志记录)中尤其有用。
实操心得:在系统初始化时,我习惯在配置波特率等参数之前,先清除FIFO(操作
FCR[2:1])。因为上电或复位后,FIFO内可能存在随机数据,先清空可以避免读到垃圾数据或产生错误的“数据就绪”中断。
2.2 中断系统的枢纽:IER与IIR的协同
中断是UART实现异步、高效通信的关键。中断使能寄存器(IER)和中断标识寄存器(IIR)构成了中断系统的“开关”和“路标”。
IER(Interrupt Enable Register):这是一个“总开关板”,允许你精细地打开或关闭特定类型的中断。TI的这个模块在不同工作模式下,IER的位定义完全不同,这是需要特别注意的:
- UART模式:关注经典的中断源,如
RHRIT(接收数据就绪)、THRIT(发送缓冲区空)、LINESTSIT(线路状态错误,如奇偶校验错、帧错误)。 - IrDA模式:增加了红外特有的中断,如
EOFIT(帧结束)、RX_FIFO_LAST_BYTE_IT(收到帧最后一个字节),这对于处理基于帧的红外协议至关重要。 - CIR模式:则关注
RXSTOPIT(接收停止)等消费电子红外遥控特有的信号。
一个关键限制:注意看描述,UART模式下的CTSIT、RTSIT、XOFFIT和SLEEPMODE位,只有在EFR[4](增强功能使能位)为1时才能被写入。这意味着如果你想使用硬件流控(CTS/RTS)或软件流控(XON/XOFF)中断,必须先解锁增强功能寄存器。这是一个常见的坑点,配置了IER却不生效,首先要检查EFR是否已正确配置。
IIR(Interrupt Identification Register):这是一个只读寄存器,当多个中断事件同时发生时,它告诉你当前优先级最高的、待处理的中断是什么。CPU在进入中断服务程序(ISR)后,第一件事就是读取IIR,根据IT_TYPE字段(UART模式)或各个中断标志位(IrDA/CIR模式)来判断中断源,并跳转到相应的处理代码。
中断处理流程示例:
- UART模块触发中断请求(IRQ线拉低)。
- CPU跳转到UART中断服务程序(ISR)。
- ISR首先读取IIR。假设读到的
IT_TYPE值为2h,查表可知是“RHR中断”(优先级2)。 - ISR据此知道是接收数据就绪,于是去读取RHR寄存器,将FIFO中的数据取出。
- 数据读取操作会自动清除该中断条件(对于RHR中断,读取RHR后,如果FIFO数据量低于触发阈值,中断标志会清除)。
- 在处理完当前最高优先级中断后,ISR应再次读取IIR,检查
IT_PENDING位是否为0。如果为0,表示还有其它 pending 的中断,需要继续读取IT_TYPE并处理,直到IT_PENDING变为1,表示所有中断已处理完毕。 - ISR返回。
避坑指南:IIR的查询是非破坏性的,读取IIR本身不会清除中断源。清除中断标志依赖于执行特定的操作,例如读取RHR清除接收中断,读取IIR本身可以清除线路状态中断等。务必根据手册描述进行正确的清除操作,否则会导致中断持续触发,系统瘫痪。
2.3 数据流与缓冲控制:FCR、TCR与TLR的深度配合
FIFO控制寄存器(FCR)、传输控制寄存器(TCR)和触发水平寄存器(TLR)这三个寄存器共同管理着数据流的节奏和缓冲策略,是优化吞吐量和实现流控的核心。
FCR(FIFO Control Register):这是FIFO的“总控开关”。
FIFO_EN (Bit 0):FIFO使能位。重要:此位只能在波特率时钟停止时(即DLL和DLH为0)更改。这意味着你通常需要在初始化阶段、配置波特率之前,就确定好是否启用FIFO。RX/TX_FIFO_TRIG (Bit 7-6, 5-4):设置接收和发送FIFO的中断触发阈值。但它们的生效条件与SCR和TLR寄存器有关,形成了一个灵活的��置体系(下文结合TLR详述)。DMA_MODE (Bit 3):选择DMA模式。同样,此位也需在波特率时钟停止时更改。TX/RX_FIFO_CLEAR (Bit 2, 1):写1清除相应的FIFO。这是软件复位FIFO状态、重新开始收发的直接方法。
TLR(Trigger Level Register) & SCR(Supplementary Control Register):它们与FCR共同决定了FIFO触发阈值的最终值,提供了比FCR更精细的控制。 手册中FCR[7:6]的描述揭示了其复杂的逻辑,我将其整理成更直观的决策表:
对于接收FIFO触发阈值(RX Trigger)的设置逻辑:
| SCR[7] | TLR[7:4] | 生效的触发阈值来源与说明 |
|---|---|---|
| 0 | 0000 | 使用**FCR[7:6]**的预设值:00=8字符,01=16字符,10=56字符,11=60字符。 |
| 0 | ≠ 0000 | 使用**TLR[7:4]**的值,计算公式:阈值 = TLR[7:4] × 4。范围是4到60字符,步进为4。 |
| 1 | 任意值 | 使用TLR[7:4]与FCR[7:6]的拼接值。将TLR[7:4]作为高4位,FCR[7:6]作为低2位,组成一个6位的值(范围1-63),阈值 = 这个6位值,步进为1字符。特别注意:拼接结果为全0(即TLR[7:4]=0000且FCR[7:6]=00)是不支持的,会导致不可预测行为。 |
对于发送FIFO触发阈值(TX Trigger)的设置逻辑(由SCR[6], TLR[3:0], FCR[5:4]控制)与之完全类似。
设计逻辑解读:这种设计提供了极大的灵活性。对于大多数简单应用,你可以忽略TLR和SCR,直接用FCR选择4个固定档位。当你需要更精细的控制(比如想设置触发阈值为28个字节),就可以通过TLR来设置(步进4)。而当你需要1字节步进的精确控制时,则需启用SCR的扩展模式,利用TLR和FCR的拼接来实现。这体现了芯片设计者对不同应用场景的考量。
TCR(Transmission Control Register):专门用于硬件流控(RTS/CTS)。它定义了接收FIFO的“水位线”。
RXFIFOTRIGHALT (Bit 3-0):当接收FIFO中的数据量达到或超过这个水位线时,UART会通过拉高RTS信号(假设设置为自动RTS模式),通知对端设备“暂停发送”。RXFIFOTRIGSTART (Bit 7-4):当接收FIFO中的数据被CPU读取,存量低于这个水位线时,UART会拉低RTS信号,通知对端“可以继续发送”。
关键约束:手册的Note里明确强调,必须确保TCR[3:0](HALT电平) >TCR[7:4](START电平)。这是为了防止“乒乓效应”——RTS信号在临界点附近频繁跳变。通常,你会设置一个合理的滞后区间,例如HALT=56(即FIFO快满时暂停),START=16(即清空一部分后恢复),这样能保证数据流的平稳。
3. 关键寄存器配置详解与实操步骤
理解了设计逻辑,我们来看如何具体配置。以下配置均假设目标是在UART 16x模式下,启用64字节FIFO,使用中断方式接收,并启用RTS/CTS硬件流控。
3.1 初始化序列与模式选择
UART模块的初始化必须遵循一定的顺序,错误的顺序可能导致配置不生效或功能异常。
步骤1:关闭UART功能并访问除数锁存器在修改任何影响波特率或工作模式的寄存器前,一个稳妥的做法是先让模块进入“配置状态”。
// 假设 UART 基地址为 0x01C20000 #define UART_BASE 0x01C20000 #define LCR (*(volatile uint32_t *)(UART_BASE + 0x0C)) // 线路控制寄存器偏移量假设 #define MDR1 (*(volatile uint32_t *)(UART_BASE + 0x20)) // 模式定义寄存器1偏移量假设 // 1. 选择UART 16x模式,但先禁用模块(进入安全配置状态) MDR1 = (MDR1 & ~0x7) | 0x7; // MODESELECT = 7h (Disable mode) // 2. 设置LCR[7]=1,使能除数锁存器访问,以配置波特率 LCR |= (1 << 7); // 设置DIV_EN位步骤2:配置波特率(DLL, DLH)波特率除数(Baud Rate Divisor)的计算公式通常为:DLL/DLH = 输入时钟频率 / (16 * 期望波特率)。需要根据芯片数据手册提供的输入时钟频率进行计算。
#define DLL (*(volatile uint32_t *)(UART_BASE + 0x00)) // 除数锁存器低字节 #define DLH (*(volatile uint32_t *)(UART_BASE + 0x04)) // 除数锁存器高字节 #define INPUT_CLK 48000000 // 示例:48MHz 输入时钟 #define BAUDRATE 115200 uint16_t divisor = (uint16_t)(INPUT_CLK / (16 * BAUDRATE)); DLL = divisor & 0xFF; // 写入低字节 DLH = (divisor >> 8) & 0xFF; // 写入高字节步骤3:配置线路参数(LCR)设置数据位、停止位、奇偶校验等。配置完成后,记得清除DIV_EN位,退出除数锁存器访问模式。
// 假设配置为 8位数据,1位停止位,无奇偶校验 LCR &= ~(1 << 7); // 清除DIV_EN位,退出除数锁存器访问模式 LCR = 0x03; // CHAR_LENGTH=3h (8 bits), NB_STOP=0 (1 stop bit), PARITY_EN=0 (No parity) // LCR[6] BREAK_EN 通常保持为0(不发送Break信号)步骤4:启用并配置FIFO(FCR)在波特率已配置(DLL/DLH非零)但模块仍处于禁用状态时,配置FIFO。
#define FCR (*(volatile uint32_t *)(UART_BASE + 0x08)) // 使能TX和RX的64字节FIFO,并设置触发阈值 // 假设使用FCR预设值:RX触发=16字符 (FCR[7:6]=01), TX触发=8字符 (FCR[5:4]=00) // 同时清除TX和RX FIFO FCR = (1 << 0) | // FIFO_EN = 1 (1 << 1) | // RX_FIFO_CLEAR = 1 (1 << 2) | // TX_FIFO_CLEAR = 1 (0 << 4) | // TX_FIFO_TRIG[0] = 0 (0 << 5) | // TX_FIFO_TRIG[1] = 0 -> 00 = 8 chars (1 << 6) | // RX_FIFO_TRIG[0] = 1 (0 << 7); // RX_FIFO_TRIG[1] = 0 -> 01 = 16 chars // 注意:FCR[1]和[2]是“自清除”位,写1后硬件会自动将其清零。步骤5:配置硬件流控阈值(TCR)如果使用自动RTS/CTS,需要设置合理的暂停和恢复阈值。
#define TCR (*(volatile uint32_t *)(UART_BASE + 0x1C)) // 假设偏移量 // 设置HALT电平为56字符,START电平为8字符。 // 注意:TCR寄存器值 = (电平 / 4)。56/4=14=0xE, 8/4=2=0x2 TCR = (0xE << 4) | (0x2 << 0); // RXFIFOTRIGSTART=2, RXFIFOTRIGHALT=14步骤6:使能中断(IER)在启用模块前,配置好需要响应的中断源。
#define IER (*(volatile uint32_t *)(UART_BASE + 0x04)) // 使能接收数据就绪中断(RHR)和接收线路状态中断(Line Status) IER = (1 << 0) | // RHRIT = 1 (1 << 2); // LINESTSIT = 1 // 如果使用硬件流控中断,需要先配置EFR[4],此处略。步骤7:最终使能UART模块(MDR1)这是最后一步,将模块切换到正常工作模式。
// 选择UART 16x模式,并退出禁用状态 MDR1 = (MDR1 & ~0x7) | 0x0; // MODESELECT = 0 (UART 16x mode)3.2 中断服务程序(ISR)编写要点
一个健壮的UART中断服务程序,必须正确处理多种中断源,并高效管理数据。
void UART_ISR(void) { uint32_t iir_value; uint8_t received_data; // 循环处理所有pending的中断 while (1) { iir_value = IIR; // 读取IIR,此操作可能会清除某些中断标志 // 检查是否有中断待处理 if (iir_value & 0x01) { // 假设IT_PENDING位在bit 0 // IT_PENDING = 1,无中断 pending,退出循环 break; } // 判断中断类型 (UART模式,假设IT_TYPE在bit 5-1) uint8_t int_type = (iir_value >> 1) & 0x1F; switch (int_type) { case 0x02: // RHR中断 (接收数据就绪) // 持续读取RHR,直到RX FIFO为空 while (!(LSR & 0x01)) { // 假设RXFIFOE位为0表示有数据 received_data = RHR; // 读取数据 // 将数据存入你的应用缓冲区(如环形缓冲区) ring_buffer_put(&rx_buf, received_data); } // 读取RHR会清除RHR中断条件 break; case 0x01: // THR中断 (发送保持寄存器空) // 检查应用层的发送缓冲区是否还有数据 if (tx_buffer_count > 0) { THR = get_next_tx_byte(); // 从缓冲区取一个字节写入THR tx_buffer_count--; } else { // 发送缓冲区已空,禁用THR中断,避免空触发 IER &= ~(1 << 1); // 禁用THRIT } break; case 0x03: // 接收线路状态错误 (最高优先级之一) // 读取LSR获取具体错误类型 uint8_t lsr_status = LSR; if (lsr_status & (1 << 4)) { // RXBI: Break中断 // 处理Break信号,可能是对方复位或线路故障 handle_break_condition(); } if (lsr_status & (1 << 3)) { // RXFE: 帧错误 // 检查硬件连接、波特率是否匹配 handle_framing_error(); } if (lsr_status & (1 << 2)) { // RXPE: 奇偶校验错误 // 检查双方奇偶校验设置 handle_parity_error(); } if (lsr_status & (1 << 1)) { // RXOE: 溢出错误 // CPU处理太慢,考虑优化代码或提高FIFO触发阈值 handle_overrun_error(); // 溢出后可能需要软件清除FIFO或重置接收状态 } // 读取LSR会清除这些错误标志(除了RXFIFOSTS) break; case 0x00: // Modem状态中断 (CTS/RTS/DSR等变化) // 读取MSR寄存器处理Modem状态变化 handle_modem_status_change(); break; // ... 处理其他中断类型 default: // 未知中断类型,可能是配置错误或硬件问题 handle_unknown_interrupt(int_type); break; } } }核心技巧:在THR中断中,当你的应用层发送缓冲区为空时,务必记得禁用THR中断(
IER[1]=0)。否则,只要发送FIFO一空就会不断产生中断,浪费CPU资源。当你有新数据需要发送时,先写一个字节到THR,再重新使能THR中断。
4. 不同工作模式下的寄存器差异与适配
TI的这个模块支持UART、IrDA和CIR三种模式,通过MDR1[2:0]选择。不同模式下,部分寄存器的含义和用法有显著区别,盲目套用会导致通信失败。
4.1 UART模式 vs. IrDA模式的关键差异
数据编码与物理层:
- UART:使用标准的NRZ(不归零)编码,逻辑1为高电平,逻辑0为低电平。
- IrDA:使用脉宽调制。以SIR(115.2kbps及以下)为例,逻辑0用一个3/16位宽的脉冲表示,逻辑1则无脉冲。这导致波特率计算基础不同。UART使用16倍过采样,而IrDA SIR模式使用16倍或32倍过采样(取决于配置),且其波特率发生器需要针对红外载波(通常为3/16位宽)进行特殊分频。务必使用芯片手册中IrDA模式专用的波特率计算公式。
中断系统(IER/IIR):
- IER:如前所述,位定义完全不同。IrDA模式关注帧(
EOFIT,LASTRXBYTEIT)和状态FIFO(STSFIFOTRIGIT)。 - IIR:UART模式使用
IT_TYPE字段编码中断源和优先级;而IrDA模式(和CIR模式)的IIR寄存器是直接映射的,每个中断源有一个独立的标志位(RHR_IT,THR_IT,EOF_IT等),可以同时指示多个中断,优先级由软件决定。
- IER:如前所述,位定义完全不同。IrDA模式关注帧(
状态寄存器(LSR):
- UART LSR:报告字节级的错误(帧错误、奇偶错误、Break)和FIFO状态。
- IrDA LSR:报告帧级的错误(
CRC错误、ABORT、FRAME_TOO_LONG)和帧边界信息(RX_LAST_BYTE)。特别注意:在IrDA模式下,错误信息(如CRC错)不是附在每一个数据字节上,而是存储在独立的状态FIFO中。你需要读取状态FIFO(通过特定寄存器)来获取刚接收完的那一帧数据的整体状态。
FIFO用法:
- 在IrDA模式下,除了数据FIFO(RHR),还有一个状态FIFO,用于存放每帧数据的元信息(如CRC校验结果、帧长度状态)。
MDR2[2:1](STSFIFOTRIG)就是用来配置这个状态FIFO的触发水平的。你需要同时管理数据流和状态流。
- 在IrDA模式下,除了数据FIFO(RHR),还有一个状态FIFO,用于存放每帧数据的元信息(如CRC校验结果、帧长度状态)。
4.2 CIR模式的特有配置
CIR(Consumer Infrared)主要用于电视、空调遥控器等消费电子。其配置更为特殊:
- 脉冲调制:
MDR2[5:4](CIRPULSEMODE)用于定义逻辑“0”或“1”对应的高电平脉冲宽度占空比(例如,3/12、4/12等),这需要与遥控器的发射协议严格匹配。 - 帧结构:CIR通常有引导码、地址码、命令码等固定结构。
EBLR(BOF长度寄存器)用于设置引导码(Burst of Frames)的判定条件,RXSTOPIT中断则用于标志一帧完整数据的接收结束。 - 引脚极性:
MDR2[6](IRRXINVERT)非常关键。大多数红外接收头会将接收到的信号进行反相输出(即有红外光时输出低电平)。这个位允许你在模块内部再次反相,以确保协议逻辑电平的正确性。默认情况下该位为0(启用反相),这与多数接收头匹配。如果你的接收头输出是同相的,就需要将此位置1。
模式切换的黄金法则:在切换
MDR1[2:0]的模式选择位之前,强烈建议先将模块设置为禁用模式(MODESELECT=7h)。在禁用模式下配置好所有相关寄存器(如波特率、FIFO、中断等),最后再切换到目标模式。这可以避免在模式切换过程中产生不可预料的信号输出或状态混乱。
5. 高级功能与调试技巧
5.1 硬件流控(RTS/CTS)的实战配置
硬件流控是保证高速或大数据量通信不丢包的利器。配置步骤如下:
- 使能增强功能:访问
TCR、IER中的流控相关位,需要先设置EFR[4]=1。具体解锁序列可能因芯片而异,常见方法是向LCR写入一个特殊值(如0xBF)来临时访问EFR。 - 配置TCR:如3.1节所述,设置
RXFIFOTRIGHALT和RXFIFOTRIGSTART,形成滞回区间。 - 使能自动RTS:通过配置相关寄存器(可能是
MCR的某个位或专门的流控寄存器),将RTS引脚的控制权交给硬件流控逻辑。 - 使能CTS中断(可选):如果需要响应对方“暂停发送”的请求,在
IER中使能CTSIT。这样当对方拉高CTS时,你会收到中断,应暂停向THR写入数据。 - 连接硬件:将本端的RTS输出连接到对端的CTS输入,将本端的CTS输入连接到对端的RTS输出。切记是交叉连接。
5.2 回环测试(Loopback)的应用
MCR[4](LOOPBACKEN)位用于启用内部回环模式。在此模式下,TX输出在内部直接连接到RX输入,同时MCR[3:0]的控制输出信号被回环到MSR[7:4]的状态输入信号。
这个功能极其有用,主要用于:
- 驱动程序自检:在不连接外部硬件的情况下,验证UART驱动程序的收发功能是否正常。你可以发送一串数据,然后检查是否能正确收到相同的数据。
- 硬件隔离调试:当通信出现问题时,首先进行回环测试。如果回环测试通过,说明UART核本身和软件驱动大概率没问题,问题可能出在外部电平转换电路、连接线或对端设备上。
回环测试操作示例:
// 1. 进入回环模式 MCR |= (1 << 4); // 设置LOOPBACKEN位 // 2. 发送测试数据 THR = 0x55; THR = 0xAA; // 3. 等待并接收数据(由于是内部回环,几乎立即收到) while ((LSR & 0x01) == 0); // 等待RX FIFO非空 uint8_t data1 = RHR; uint8_t data2 = RHR; // 4. 验证数据 if (data1 == 0x55 && data2 == 0xAA) { // 回环测试成功 } // 5. 退出回环模式,恢复正常操作 MCR &= ~(1 << 4);5.3 常见问题排查与寄存器诊断
当UART通信出现问题时,不要盲目修改代码,应系统性地通过寄存器状态进行诊断。
| 现象 | 可能原因 | 排查寄存器与步骤 |
|---|---|---|
| 完全无数据收发 | 1. 时钟/波特率错误 2. 模块未使能 3. 引脚复用未配置 | 1. 检查输入时钟源、DLL/DLH计算值。 2. 确认 MDR1[2:0]是否为期望模式(非7h)。3. 检查芯片的PinMux配置,确保UART TX/RX引脚功能已正确映射。 |
| 能发不能收,或能收不能发 | 1. 中断未正确使能/处理 2. FIFO配置不当 3. 流控信号阻塞 | 1. 检查IER寄存器,确认RHRIT或THRIT已使能。在ISR中检查IIR,确认中断被正确识别。2. 检查 FCR,确认FIFO已使能。检查LSR的TXFIFOE和RXFIFOE位,确认FIFO状态。3. 检查 MSR寄存器,查看CTS、DSR等状态位。如果使用硬件流控,确认对方设备状态。 |
| 数据错误(乱码) | 1. 波特率不匹配 2. 数据格式(LCR)不匹配 3. 线路干扰 | 1.重点检查:双方设备的波特率生成时钟源精度(晶振误差)是否在容限内(通常要求<3%)。 2. 对比双方 LCR设置:数据位、停止位、奇偶校验位必须完全一致。3. 在LSR中检查 RXPE(奇偶错)、RXFE(帧错)位是否置位。 |
| 通信一段时间后死机或丢数据 | 1. 中断未及时清除,导致重复进入 2. FIFO溢出 3. 软件缓冲区溢出 | 1. 在ISR中,确保对所有触发的中断源都执行了正确的清除操作(读RHR、读LSR、读IIR等)。 2. 检查LSR的 RXOE(溢出错误)位。如果置位,说明CPU处理速度跟不上接收速度。考虑:增大接收FIFO触发阈值、使用DMA、或优化软件。3. 检查你的应用层环形缓冲区是否已满。 |
| 中断无法触发 | 1. IER配置错误 2. 中断控制器(如NVIC)未使能 3. EFR锁未解开 | 1. 确认IER中对应位已置1。2.嵌入式系统常见坑:除了配置UART自身的中断使能,还要在MCU的中断控制器中使能该UART的中断通道,并设置优先级。 3. 对于需要EFR[4]的位(如CTSIT),确认已正确解锁并设置EFR[4]=1。 |
调试建议:在关键初始化步骤后、以及在中断服务程序中,可以适时读取并打印(通过其他通道,如另一个UART或调试器)关键寄存器的值,如LSR、IIR、MSR,这是定位硬件连接问题、配置错误或软件逻辑缺陷的最直接手段。