1. UART寄存器概览与核心设计思路
在嵌入式开发中,串口(UART)调试和通信是基本功。很多人会用printf,会调波特率,但一旦遇到复杂的流控制、多模式切换或者DMA配置,往往就对着芯片手册里密密麻麻的寄存器位发懵。今天,我们就以TI AM275x系列处理器的UART模块为例,掰开揉碎了讲讲那些“高级”寄存器到底怎么玩。这不是一篇照本宣科的手册翻译,而是我这些年调试UART,从踩坑到填坑的经验总结。我们会聚焦在几个关键但常被忽略的寄存器上:软件流控制(XON/XOFF)、模式控制(MDR1/MDR2)、状态监测(MSR/SSR)以及传输控制(TCR/TLR)。理解它们,你就能从“能用串口”进阶到“精通串口”,在复杂的通信场景下也能游刃有余。
UART的核心功能是异步串行通信,但现代MCU中的UART外设早已不是简单的“发送-接收”移位器。它集成了FIFO、DMA触发器、多种硬件流控、甚至红外编码(IrDA)等高级功能。这些功能全都通过一系列寄存器来配置和监控。手册里通常按地址顺序列出每个寄存器,但实际开发时,我们需要按功能模块来理解它们。我的思路是将其分为四类:数据与缓冲控制(如RHR/THR, FCR)、通信参数配置(如LCR, DLL/DLH)、流控制与状态(今天重点),以及高级功能与模式(如IrDA, 自动波特率)。今天,我们就深入后两类中那些“令人头疼”的寄存器。
提示:阅读芯片手册时,切忌从头到尾线性阅读。先抓住核心功能寄存器(如LCR配置帧格式,DLL/DLH设置波特率),再根据需求查阅高级功能。对于AM275x这类复杂外设,建议画一张寄存器功能地图,把相关的寄存器分组标注,调试时按图索骥。
2. 软件流控制:XON/XOFF寄存器的深度解析
当通信双方速度不匹配,或者接收方处理不过来时,就需要“流控制”来踩刹车或松油门。除了大家熟悉的RTS/CTS硬件流控,软件流控(XON/XOFF)在特定场景下非常有用,比如在只有TX、RX、GND三根线的简单连接中。
2.1 XON/XOFF机制与寄存器映射
软件流控的原理很简单:接收方通过发送特殊的控制字符XOFF(通常是0x13,DC3)告诉对方“暂停发送”;当接收方准备好后,再发送XON(通常是0x11,DC1)通知对方“恢复发送”。在AM275x的UART中,这两对字符分别由四个8位寄存器存储:
UART_XON1:存储第一个XON字符。UART_XON2_ADDR2:这是一个复用寄存器。在UART模式下,存储第二个XON字符(XON_WORD2);在IrDA模式下,用作地址2。UART_XOFF1:存储第一个XOFF字符。UART_XOFF2:存储第二个XOFF字符。
为什么需要两个XON/XOFF字符?这是为了增强协议的健壮性,防止数据流中偶然出现与控制字符相同的数据字节导致误触发。通常,我们可以将XON1/XOFF1设为标准值(0x11/0x13),而将XON2/XOFF2设为另一个不常用的值(如0x12/0x14),或者直接设为0x00将其禁用,仅使用单字符流控。
以UART_XON2_ADDR2寄存器(偏移地址0x14h)为例,其结构极其简单:
- 位[31:8]:保留,读为0。
- 位[7:0] - XON_WORD2:可读写,复位值为0。这就是存储第二个XON字符的地方。
配置示例:假设我们想使用标准的单字符XON/XOFF流控,并启用UART的自动识别功能(通过IER或MCR的某个位,需结合其他寄存器),我们需要进行如下操作:
- 将
XON1寄存器写入0x11。 - 将
XOFF1寄存器写入0x13。 - 将
XON2和XOFF2寄存器写入0x00(禁用)。 - 在**线路控制寄存器(LCR)**中,确保数据位为8位(因为XON/XOFF是8位字符)。
- 在**中断使能寄存器(IER)**中,可能需要使能特定的中断来响应流控字符的接收。
2.2 配置实操与常见陷阱
配置软件流控,绝不仅仅是写几个寄存器值那么简单。关键在于理解UART模块何时比较接收到的数据与这些寄存器中的值。
在典型的UART IP中,硬件比较逻辑是实时工作的。当接收移位寄存器收到一个完整字节并存入接收FIFO(或RHR)时,硬件会将该字节与XON1/2和XOFF1/2寄存器的值进行比较。如果匹配,则不会将数据字节存入数据FIFO(防止控制字符污染数据流),而是触发一个内部事件:匹配到XOFF则暂停自身TX发送;匹配到XON则恢复发送。
这里有一个极易踩坑的点:使能机制。很多初学者配置了XON/XOFF寄存器却发现流控不生效,根本原因在于没有找到“开关”。在AM275x中,软件流控的使能通常关联着**调制解调器控制寄存器(MCR)的某个位,或者FIFO控制寄存器(FCR)**的扩展功能位。你需要仔细查阅手册中关于“Software Flow Control Enable”的描述。有时它可能隐藏在“高级功能”或“特殊模式”配置里。
另一个陷阱是与硬件流控的冲突。如果同时使能了RTS/CTS硬件流控和XON/XOFF软件流控,模块的行为可能是未定义的,或者以其中一种优先。在复杂的通信协议中,务必明确只使用一种流控方式,并在系统设计文档中写明。
注意:软件流控会占用有效数据带宽,且控制字符本身可能出现在数据流中(尽管概率低)。因此,它不适合用于传输二进制数据(如图片、压缩包),而更适用于文本协议(如AT命令、JSON)。在启用前,务必评估你的数据内容,或考虑使用转义字符等更高层的协议来避免冲突。
3. 模式选择与高级控制:MDR1, MDR2, ACRegister
这是UART模块的“模式切换中枢”。通过MDR1和MDR2,你可以让同一个物理外设在标准UART、红外SIR/MIR/FIR、消费红外CIR甚至自动波特率检测等模式间切换。ACREG则提供了红外模式下的精细控制。
3.1 模式选择寄存器(MDR1)详解
MDR1[2:0]的MODE_SELECT字段是核心中的核心,它决定了UART模块的根本工作模式:
000:UART 16x模式。最常用的标准异步串行模式,使用16倍过采样。001:SIR模式(Serial Infrared)。对应IrDA 1.0-1.2,速率最高115.2kbps,采用3/16或1.63us的脉冲调制。010:UART 16x自动波特率。模块能自动检测输入数据的波特率,非常适用于需要自适应不同设备的场景,如Bootloader。011:UART 13x模式。另一种过采样模式,可能用于与某些特定旧设备兼容。100:MIR模式(Medium Infrared)。IrDA 1.1,速率0.576Mbps和1.152Mbps。101:FIR模式(Fast Infrared)。IrDA 1.1,速率4Mbps。110:CIR模式(Consumer Infrared)。用于电视、空调遥控器等消费电子的红外编码。111:禁用模式。模块关闭以省电,但注意手册提到中断可能仍会产生。
关键操作顺序:手册明确警告,MDR1[2:0]必须在配置完波特率除数锁存器(DLL, DLH)和线路控制寄存器(LCR)之后,且在正常操作开始之前进行设置。一旦设置,在通信过程中不得更改。否则会导致不可预测的行为,通常表现为数据错乱或通信完全中断。
配置代码框架如下:
// 1. 配置波特率 (例如 115200) UART_REG->LCR |= (1 << 7); // 使能DLAB,访问DLL/DLH UART_REG->DLL = CALC_DLL_115200; // 计算出的低字节 UART_REG->DLH = CALC_DLH_115200; // 计算出的高字节 UART_REG->LCR &= ~(1 << 7); // 关闭DLAB // 2. 配置帧格式 (例如 8N1) UART_REG->LCR = 0x03; // 8位数据,无���验,1位停止位 // 3. 最后,配置工作模式 (例如 标准UART 16x模式) UART_REG->MDR1 = (UART_REG->MDR1 & ~0x07) | 0x00; // MODE_SELECT = 000MDR1的其他位也至关重要:
FRAME_END_MODE: 仅IrDA模式有效。选择帧结束的判断方式:0为帧长度法(需配合TXFLL/H设置长度),1为设置EOT位法(通过ACREG[0]手动标记帧结束)。SIP_MODE: 仅MIR/FIR模式有效。选择SIP(Serial Infrared Interaction Pulse)脉冲的发送方式:0为手动模式(通过ACREG[3]控制),1为自动模式(每次传输后自动发送)。SCT: 存储与控制传输。0表示一写入发送保持寄存器(THR)就立即开始红外传输;1表示写入THR后,需要由ACREG[2](SCTX_EN)位来手动触发传输开始。这在组包发送时非常有用。SET_TXIR和IR_SLEEP: 用于直接控制红外发射管的引脚电力和睡眠模式。
3.2 模式配置寄存器2(MDR2)与辅助控制寄存器(ACREG)
MDR2主要服务于红外模式:
IRRXINVERT: 这是一个救命位。大多数红外收发器(Transceiver)在接收时会对信号进行反相。如果此位设置错误,你将永远收不到正确数据。手册默认是0(执行反相),这是为了适配大多数反相型收发器。如果你的收发器输出已经是反相后的信号,或者你使用直接的光电二极管电路,就需要将此位置1。STS_FIFO_TRIG: 设置状态FIFO的触发阈值。当接收到的帧状态(如长度、CRC错误)信息存入状态FIFO并达到此阈值时,可能会产生中断。合理设置可以平衡中断频率和响应及时性。IRTX_UNDERRUN: 这是一个状态位,当发生发送下溢错误时由硬件置位。必须在读取IIR寄存器确认发生了TX_STATUS_IT中断后,再来读取此位以判断具体错误类型。
ACREG寄存器是红外模式下的“命令控制台”:
SEND_SIP: 手动触发发送一个SIP脉冲(MIR/FIR模式)。SCTX_EN: 当MDR1[5]=1时,向此位写1将启动之前已存入FIFO的数据帧的传输。ABORT_EN: 写1可立即中止当前帧的发送。警告:如果发送FIFO非空且MDR1[5]=1,中止当前帧后模块会立即开始发送FIFO中下一帧的数据。因此,在发送中止命令前,务必先复位TX FIFO(通过FCR[2])。EOT_EN: 在“设置EOT位”的帧结束模式下,在写入最后一字节数据到THR之前,需要先将此位置1。
这些位大多是“写1触发”(R/W1TS)类型,即写1执行动作,写0无效。操作它们需要严格的时序配合。
实操心得:调试红外模式时,最令人崩溃的问题往往是“有发无收”或“数据全错”。请按以下清单排查:1.
MDR1模式选对了吗?(SIR/MIR/FIR)2.MDR2[6]的IRRXINVERT设置是否与你的收发器匹配?(用示波器看RX引脚波形最直接)3. 波特率(或红外载波频率)计算正确吗?4. 帧格式(起始位、停止位、CRC)两端是否一致?5. 如果是SIR,ACREG[7]的PULSE_TYPE(脉冲宽度)设置是否正确?把这五点查清,问题就解决了一大半。
4. 状态监测与传输控制:MSR, SSR, TCR, TLR
搞定了模式和流控,通信过程中的“健康状况监控”和“流量调度”就靠这组寄存器了。
4.1 调制解调器状态寄存器(MSR)
MSR寄存器反映了来自调制解调器(或对端设备)控制线的状态变化。在RS-232这类全双工串口中非常有用。
- 低4位(位3-0) -
*_STS: 这些是变化标志位。当对应的CTS、DSR、RI、DCD输入引脚状态发生变化时,相应位被置1。读MSR寄存器会清除这些位。这是典型的“读清零”状态位,编程时务必注意:如果你想判断某个变化,需要先读取MSR值保存到变量中,再对该变量的特定位进行判断。 - 高4位(位7-4) -
N*_STS: 这些是当前状态位的反相值。例如,NCTS_STS位反映的就是CTS*输入引脚当前逻辑电平的反相。当CTS*引脚为低电平(有效)时,NCTS_STS为高电平(1)。这样设计是为了与历史习惯兼容。
在循环回环测试模式(Loopback Mode)下,这些状态位的来源不再是外部引脚,而是MCR寄存器的对应位(MCR[3:0])。这为自测试提供了便利。
4.2 补充状态寄存器(SSR)与系统控制寄存器(SCR)
SSR和SCR通常配合使用,管理一些扩展状态和控制功能。
SSR[1] - RX_CTS_DSR_WAKE_UP_STS: 唤醒状态位。当SCR[4](唤醒使能)置1时,RX、CTS或DSR引脚上的下降沿会触发一个唤醒事件,并将此位置1。重点:此状态位不会在IIR中产生标准中断,因此你的中断服务程序(ISR)在检查IIR发现没有中断源时,需要来检查此位。清除此位的方法是将SCR[4]写0。SCR[2:1] - DMA_MODE_2与SCR[0] - DMA_MODE_CTL: 这两个位共同决定了DMA请求模式。这是一个配置优先级的选择。当DMA_MODE_CTL=0时,DMA模式由FCR[3]决定(传统方式)。当DMA_MODE_CTL=1时,DMA模式由SCR[2:1]决定。这提供了更灵活的DMA通道映射(例如,Mode 2将RX DMA请求映射到UART_nDMA_REQ[0],而TX用其他方式)。
4.3 传输控制寄存器(TCR)与触发水平寄存器(TLR)
这两个寄存器是管理FIFO和DMA行为的“流量阀门”。
TCR寄存器用于软件流控制的阈值设置:RX_FIFO_TRIG_HALT: 接收FIFO达到此深度时,UART模块自动发送XOFF字符(如果使能),通知对端暂停发送。RX_FIFO_TRIG_START: 接收FIFO低于此深度时,UART模块自动发送XON字符(如果使能),通知对端恢复发送。 例如,设置HALT=48,START=16,意味着当RX FIFO中数据达到48字节时发XOFF,当被取走到只剩16字节时发XON。这为接收方提供了32字节的“缓冲处理区”。
TLR寄存器用于DMA传输的阈值设置:TX_FIFO_TRIG_DMA: 发送FIFO空余空间达到此值时,触发DMA请求以填充更多数据。RX_FIFO_TRIG_DMA: 接收FIFO中数据达到此值时,触发DMA请求以搬走数据。 合理设置这两个值对DMA效率至关重要。TX值设得太小(如1),会导致DMA请求过于频繁,增加总线负担;设得太大(如60),则可能在发送间隙造成FIFO清空,发送线空闲,降低吞吐率。通常设置为FIFO深度的一半或四分之一是个不错的起点。对于64字节的FIFO,TX设为16,RX设为48是常见配置,这样能平衡延迟和效率。
5. 红外模式专属寄存器组深度剖析
当MDR1配置为SIR/MIR/FIR/CIR模式时,一组新的寄存器开始生效。它们是实现红外可靠通信的关键。
5.1 帧长度寄存器(TXFLL/H, RXFLL/H)
红外通信是面向帧的,不像普通UART是流式的。因此,发送前需要告知模块本帧有多长,接收前需要告知模块允许接收的最大帧长。
TXFLL和TXFLH: 组成13位的发送帧长度(字节数)。仅在MDR1的FRAME_END_MODE选择为“帧长度法”时使用。例如,你要发送一个128字节的数据包,就需要将128写入这组寄存器。注意,这个长度包含CRC等帧尾开销。RXFLL和RXFLH: 组成12位的接收最大帧长度。这是一个保护值。如果接��到的帧长度超过此值,模块会报FRAME_TOO_LONG_ERROR。手册给出了关键公式:编程值 = 预期数据字节数 + 开销。- SIR/MIR模式:开销为
+3字节(2字节CRC + 1字节停止标志)。 - FIR模式:开销为
+6字节(2字节CRC + 2字节停止标志 + 其他帧格式开销)。 例如,在SIR模式下,你希望接收的最大数据负载是100字节,那么需要写入RXFLL/H的值为103。
- SIR/MIR模式:开销为
5.2 状态FIFO相关寄存器(SFLSR, SFREGL, SFREGH)
这是红外模式下的“诊断信息中心”。由于红外通信是帧式的,每一帧接收完毕后,除了数据本身,还有伴随的状态信息(帧长、CRC结果等)。这些信息被存入一个独立的8条目状态FIFO。
SFREGL和SFREGH:读取顺序优先!当你想获取一帧的状态信息时,必须先读取这两个寄存器,它们以只读方式给出了最近接收帧的长度(LSB和MSB)。SFLSR:读取即弹出!读完长度后,再读取SFLSR寄存器,它会给出该帧的错误状态(CRC错误、帧过长、中止、溢出),并且读取操作会使状态FIFO的读指针前进到下一项。如果你先读SFLSR,就会丢失对应的帧长度信息。 操作流程伪代码如下:if (状态FIFO非空) { frame_length = (UART_REG->SFREGH << 8) | UART_REG->SFREGL; // 先读长度 status = UART_REG->SFLSR; // 后读状态,指针自动后移 if (status & CRC_ERROR_MASK) { // 处理CRC错误 } // 根据frame_length去数据FIFO中读取对应字节数的数据 }
5.3 块控制寄存器(BLR)与恢复寄存器(RESUME)
BLR: 主要控制SIR模式下的起始标志(Burst of Flags, BOF)类型(0xC0或0xFF),以及提供一个STS_FIFO_RESET位用于软件复位状态FIFO。RESUME: 这是一个虚拟寄存器。读它永远返回0。它的唯一作用是在发生发送下溢(TX Underrun)或接收溢出(RX Overrun)错误导致传输/接收挂起时,通过执行一次对该寄存器的读操作来清除内部挂起标志,使操作恢复。这是一个非常巧妙的设计,将“恢复”动作映射为一个简单的内存读操作,节省了一个控制位。
6. 自动波特率与UART状态寄存器(UASR)
UART_UASR是自动波特率模式下的状态报告器。当MDR1设置为自动波特率模式(010)后,模块会尝试从接收到的第一个字符(通常是‘A’或0x55这样的特定模式)中检测波特率、数据位和校验位。
SPEED: 5位字段,报告检测到的波特率编码。从00001(115200)到01010(1200),00000表示未识别。BIT_BY_CHAR: 识别出的字符长度,0为7位,1为8位。PARITY_TYPE: 识别出的校验类型。
使用自动波特率的典型流程是:
- 将模块配置为自动波特率模式。
- 等待对端发送一个已知的同步字符(例如0x55,其二进制为01010101,丰富的边沿有利于测量)。
- 轮询或中断检查
UASR的SPEED字段,直到其变为非零。 - 根据
UASR的结果,动态计算出应写入DLL/DLH的波特率除数,并配置LCR的数据位和校验位。 - 将
MDR1切换回标准UART模式(000),然后开始正常通信。
注意事项:自动波特率检测并非100%可靠,尤其在噪声环境下。它通常用于初始连接建立(如Bootloader)。在产品中,如果通信环境恶劣,建议使用固定的、已知的波特率。此外,检测期间发送的同步字符格式必须严格符合8-N-1(8位数据、无校验、1位停止位),这是大多数自动波特率算法的前提。
7. 常见问题排查与调试技巧实录
7.1 软件流控不生效
- 症状: 配置了XON/XOFF寄存器,但接收方缓冲区满时,发送方并未停止。
- 排查:
- 确认使能位: 这是最常见的原因。仔细检查
MCR、FCR或EFR(如果存在)中是否有软件流控使能位(例如EFR[4]的ENHANCED_EN和EFR[6]的SPECIAL_CHAR_DETECT)。在AM275x中,可能需要配置多个寄存器才能完全使能。 - 检查字符匹配: 确认对方发送的
XOFF字符与你寄存器中配置的值完全一致(注意大小端)。可以用逻辑分析仪抓取RX线上的数据。 - 检查中断: 是否使能了相应的中断?模块检测到流控字符后可能会产生一个特殊中断,需要ISR处理。
- FIFO阈值: 确认
TCR寄存器中的RX_FIFO_TRIG_HALT和RX_FIFO_TRIG_START设置是否合理。如果阈值设得过高(接近FIFO深度),可能来不及反应。
- 确认使能位: 这是最常见的原因。仔细检查
7.2 红外模式无法收发数据
- 症状: 切换到SIR/MIR/FIR模式后,发送无输出,或接收不到数据。
- 排查:
- 电气连接与反相: 用示波器测量TXIR引脚。首先在UART模式下发数据,看是否有正常的串口波形。然后切换到红外模式,发送数据,此时应看到被调制的红外脉冲(SIR下是窄脉冲)。最关键的一步:测量接收端(RX引脚)在红外收发器之后的信号。如果收发器有反相功能,
MDR2[6]的IRRXINVERT必须与之匹配。这是硬件层面的首要检查点。 - 帧长度设置: 确认
TXFLL/H和RXFLL/H已正确设置(数据长度+开销)。一个常见的错误是只设置了数据长度,忽略了CRC和停止标志的字节数。 - 模式与速率匹配: SIR模式下的波特率就是通信速率(如115200)。但MIR(1.152Mbps)和FIR(4Mbps)是高速模式,需要确保主频和时钟分频能支持如此高的波特率生成。检查系统时钟和UART模块的输入时钟配置。
- 状态FIFO阻塞: 如果接收了几帧后不再产生中断,可能是状态FIFO满了。确保你的程序按照
SFREGL/H->SFLSR的顺序读取并清空了状态信息。
- 电气连接与反相: 用示波器测量TXIR引脚。首先在UART模式下发数据,看是否有正常的串口波形。然后切换到红外模式,发送数据,此时应看到被调制的红外脉冲(SIR下是窄脉冲)。最关键的一步:测量接收端(RX引脚)在红外收发器之后的信号。如果收发器有反相功能,
7.3 DMA传输数据不完整或错位
- 症状: 使能DMA后,数据丢失、重复或乱序。
- 排查:
- DMA模式配置冲突: 检查
SCR[0](DMA_MODE_CTL)。它决定了DMA模式的配置源。如果SCR[0]=0,你却去配置SCR[2:1],那是无效的。确保配置来源统一。 - TLR触发值不合理:
TX_FIFO_TRIG_DMA值太小,DMA请求太频繁,可能造成总线拥塞或DMA控制器来不及响应,导致FIFO变空(Underrun)。RX_FIFO_TRIG_DMA值太大,则可能导致FIFO溢出(Overrun)后DMA请求才到来。结合FIFO深度和DMA搬运的块大小进行调整。 - DMA计数器复位: 注意
SSR[2]的DMA_COUNTER_RST位。如果此位为1,当你通过FCR[1]或FCR[2]复位FIFO时,DMA计数器也会被复位。这可能导致DMA传输计数错误。根据你的应用需求(是否需要在运行时复位FIFO并保持DMA计数)来设置此位。 - 中断与DMA协同: 在DMA模式下,通常还会使能FIFO达到触发水平的中断。确保DMA搬运的字节数与你设置的中断触发水平相匹配。例如,设置RX FIFO触发水平为16,DMA配置为每次搬运32字节,那么DMA可能在第一次触发后,在FIFO数据再次积累到16字节前就搬走了所有数据,导致后续无法触发中断。这时可能需要使用“空”或“超时”中断作为补充。
- DMA模式配置冲突: 检查
7.4 唤醒功能异常
- 症状: 配置了RX引脚下降沿唤醒,但芯片无法从低功耗模式唤醒。
- 排查:
- 使能路径: 唤醒使能是一个两级开关。首先,
SCR[4](RX_CTS_DSR_WAKE_UP_ENABLE)必须置1。其次,对应的引脚功能必须配置为UART功能(而非GPIO),并且其上下拉电阻配置不能将引脚电平固定。 - 状态清除: 唤醒事件发生后,
SSR[1]位被置1。这个位不能通过直接写SSR来清除。唯一的清除方法是向SCR[4]写0。因此,你的唤醒中断服务程序(ISR)在处理完事件后,需要执行SCR &= ~(1<<4);然后再SCR |= (1<<4);来重新使能下一次唤醒。 - 中断映射: 再次强调,唤醒中断不在标准的
IIR寄存器中体现。你需要在一个全局中断服务例程中,如果发现IIR显示无中断,���去检查SSR[1]位。
- 使能路径: 唤醒使能是一个两级开关。首先,
调试UART,尤其是其高级功能,逻辑分析仪和示波器是必不可少的。不仅要看数据波形,还要看控制引脚(如RTS、CTS)的时序,以及红外模式下的调制脉冲。养成配置寄存器后立刻回读确认的习惯,可以排除很多软件配置错误。最后,仔细阅读手册的“Note”和“Caution”部分,那里往往藏着最关键的限制条件和操作顺序,能让你少走很多弯路。