1. 项目概述与核心价值
在嵌入式开发的日常里,串口(UART)调试和通信几乎是每个工程师都绕不开的“老朋友”。从最简单的打印日志,到复杂的设备间协议通信,UART的稳定性和效率直接关系到整个系统的可靠性和响应速度。然而,很多开发者对UART的认知可能还停留在“配置波特率、数据位、停止位”的层面,一旦遇到数据丢失、中断响应不及时或者流控制失效等问题,往往只能靠“玄学调试”或者降低波特率来妥协。
今天,我们就以Freescale(现NXP)的经典ARM9芯片MC9328MX1为例,彻底拆解其UART模块的控制寄存器。这份手册节选虽然看起来是冰冷的寄存器位描述,但背后隐藏的是一套完整的、工业级的串口通信解决方案。理解它,你就能从“会用串口”进阶到“精通串口”,知道每一个配置位背后的设计意图,能在出现问题时精准定位是硬件流控的阈值设错了,还是FIFO中断的触发条件没配好,亦或是某个错误标志位没有及时清除导致中断卡死。
MC9328MX1的UART外设功能相当丰富,远不止基础收发。它支持硬件流控(RTS/CTS)、可编程的发送/接收FIFO及其触发中断、多种错误检测与中断(奇偶校验错、帧错误、溢出)、甚至包括红外编码(IrDA)和自动波特率检测等高级功能。这些功能全都通过一系列控制寄存器(UCR2, UCR3, UCR4, UFCR)和状态寄存器(USR1, USR2)来配置和监控。本文将聚焦于这些控制与状态寄存器的配置逻辑和中断管理机制,为你呈现一份可以直接用于驱动开发的“寄存器级”指南。无论你是正在为这款老芯片维护遗产代码,还是想通过它来深入理解UART外设的设计哲学,这篇文章都将提供十足的干货。
2. UART控制寄存器深度解析与配置逻辑
要驾驭MC9328MX1的UART,不能孤立地看某个寄存器,必须建立起寄存器组之间协同工作的整体视图。其配置流程通常遵循一个清晰的层次:先进行全局和通道使能,再设置通信参数,接着配置FIFO与中断,最后处理高级功能(如流控、红外)。下面我们以最核心的UART控制寄存器2(UCR2)为起点,层层深入。
2.1 UART控制寄存器2(UCR2):通信基础与核心使能
UCR2是UART的“总开关”和“格式设定器”。它的复位值是0x0001,唯一一个置1的位是SRST(软件复位),这暗示着硬件上电后,UART模块默认处于复位状态,需要软件将其清零后才能开始工作。
关键位域详解与配置策略:
SRST (Bit 0): 软件复位
- 功能:写入0会复位发射器、接收器的状态机、所有FIFO和状态寄存器。持续4个IPG_CLK周期后,硬件会自动将其置1。
- 实操要点:这是配置UART的第一步。在修改任何关键参数(如波特率分频器、字长、FIFO模式)之前,强烈建议先拉低SRST(写0),完成配置后再将其置1(实际上硬件会自动完成,软件只需写0触发)。这可以确保状态机从一个干净的状态启动,避免因残留状态导致的数据错乱。手册明确警告,在传输或接收过程中改变字长(WS位)会导致当前字符长度不可预测,因此在动态调整参数前使用SRST复位是稳妥的做法。
RXEN (Bit 1) / TXEN (Bit 2): 接收/发送使能
- 功能:分别控制接收器和发射器的开启。这两个位需要和更高层的UART使能位(通常在UCR1中,手册未在此节给出)配合使用。
- 配置顺序:典型的启动顺序是:使能UART时钟 -> 配置UCR1(全局使能等)-> 配置UCR2(格式、使能)-> 清除SRST -> 使能RXEN/TXEN。关闭时,则建议先禁用RXEN/TXEN,再进行其他配置。特别注意,如果在中途禁用TXEN,发射器会立即停止并开始发送“1”(Mark位),这可能导致一个不完整的帧被发送出去。
WS (Bit 5): 字长选择
- 功能:0 = 7位数据;1 = 8位数据。这里的数据位不包括起始位、停止位和校验位。
- 注意事项:如前述,绝对不要在数据传输或接收过程中更改此位。必须在通信空闲时,结合SRST复位操作进行更改。
STPB (Bit 6): 停止位
- 功能:0 = 1个停止位;1 = 2个停止位。此位仅影响发送方。接收方总是期望至少1个停止位。在大多数标准通信中(如115200-8-N-1),此处应配置为0。
PREN (Bit 8) / PROE (Bit 7): 校验使能与奇偶选择
- 功能:PREN使能校验功能,PROE决定是奇校验(1)还是偶校验(0)。
- 配置逻辑:通常PREN和PROE需要配对设置。例如,偶校验:PREN=1, PROE=0;奇校验:PREN=1, PROE=1;无校验:PREN=0, PROE值忽略。
RTSEN (Bit 4) / IRTS (Bit 14) / CTSC (Bit 13) / CTS (Bit 12): 硬件流控制
- 这是最容易出错的地方之一。MC9328MX1的硬件流控非常灵活。
- RTS (Request to Send) 输出控制:
RTSEN=1:使能RTS引脚的中断功能(边沿检测)。RTEC位(Bit 10-9)选择触发边沿。IRTS=1:忽略外部输入的RTS信号。此时UART会认为RTS始终有效(低电平),可以一直发送。UARTx_RTS引脚变成一个普通输入引脚。IRTS=0时,发送器会侦听外部RTS引脚,只有其为低电平(有效)时才发送数据。
- CTS (Clear to Send) 输出控制:
CTSC=1:CTS引脚由接收器自动控制。当RxFIFO中的数据达到CTSTL(在UCR4中设置)设定的触发水平时,CTS引脚会自动变高(无效),通知对端停止发送。这是最常用的自动流控模式。CTSC=0:CTS引脚由软件通过CTS位(Bit 12)手动控制。软件写1使CTS有效(低电平,允许对端发送),写0使其无效。
- 典型自动流控配置:
IRTS=0(侦听外部RTS),CTSC=1(CTS自动控制)。同时,需要正确配置UCR4中的CTSTL,设定接收FIFO的“警戒水位”。
ESCEN (Bit 11) / ESCI (Bit 15): 转义序列检测
- 功能:用于在数据流中检测特定的“转义字符”(在UESC寄存器中定义),常用于实现带内协议控制。
ESCEN使能检测逻辑,ESCI使能检测到转义序列时产生中断。 - 应用场景:在某些自定义协议中,可以用转义序列来打断正常数据流,插入命令。需要配合UTIM(转义定时器)寄存器设置字符间超时时间。
- 功能:用于在数据流中检测特定的“转义字符”(在UESC寄存器中定义),常用于实现带内协议控制。
配置示例:假设我们需要配置UART1为115200波特率(需另外计算并设置波特率寄存器UBMR和UBIR)、8位数据、无校验、1停止位、使能自动CTS流控(RTS由外部控制)。UCR2的配置值计算如下:
- SRST=1 (操作顺序上,我们先配其他位,最后处理SRST)
- RXEN=1, TXEN=1
- RTSEN=0 (我们不使用RTS边沿中断)
- WS=1 (8位)
- STPB=0 (1停止位)
- PREN=0, PROE=0 (无校验)
- CTSC=1 (CTS自动控制)
- CTS=0 (CTSC=1时此位无效)
- IRTS=0 (侦听外部RTS)
- ESCEN=0, ESCI=0 (禁用转义序列)
- 其他保留位为0。 假设我们通过软件先将SRST写0复位,再写1退出复位。那么最终写入UCR2寄存器的值可能是:
0x4025(二进制 0100 0000 0010 0101)。注意,这个值是在SRST已为1的情况下。实际编程中,我们通常会先读取当前值,修改目标位,再写回。
2.2 UART控制寄存器3(UCR3):中断使能与特殊功能
UCR3主要管理各种中断的使能,并且UART1和UART2/3的UCR3有细微差别(UART2/3多了DTR/DSR/DCD/RI等调制解调器接口控制位)。我们以共有的部分进行说明。
核心中断使能位:
PARERREN (Bit 12) / FRAERREN (Bit 11): 奇偶校验错与帧错误中断使能
- 当接收到的字符出现奇偶校验错误或帧错误(如缺少有效的停止位)时,对应的状态位(USR1中的PARITYERR和FRAMERR)会被置位。如果这里使能了相应中断,就会产生
UART_MINT_PFERR中断。 - 调试经验:在开发初期,强烈建议使能这两个中断。一旦通信出现错乱,它们能帮你快速定位是噪声干扰(可能引发帧错误)还是双方校验配置不一致。在稳定运行的系统中,如果通信环境良好且协议有上层校验,可以考虑关闭以减轻中断负载。
- 当接收到的字符出现奇偶校验错误或帧错误(如缺少有效的停止位)时,对应的状态位(USR1中的PARITYERR和FRAMERR)会被置位。如果这里使能了相应中断,就会产生
RXDSEN (Bit 6): 接收器空闲中断使能
- 当接收器空闲(RXD线持续为高电平)超过一个字符帧的时间时,USR1中的RXDS位会置1。使能此中断后,可用于检测通信链路断开或数据传输结束。这在半双工通信或需要判断对方是否发送完毕的场景下很有用。
REF25/REF30/REF16(在UCR4中): 参考时钟频率指示
- 这些位(UCR3的Bit3, Bit2和UCR4的Bit6)用于指示UART模块的输入参考时钟频率是16MHz、25MHz还是30MHz。这个配置必须与实际的IPG_CLK分频后供给UART的时钟频率严格一致,否则波特率计算将完全错误。这是很多初学者容易忽略的硬件关联配置。
UART2/3特有的调制解调器控制位(Bit13-8): 对于需要连接调制解调器或模拟 modem 控制的场景,DTREN、DPEC、DSR、DCD、RI 这些位提供了完整的硬件握手信号控制。例如,DTREN和DPEC可以配置DTR引脚上的边沿中断,用于响应远端设备的状态变化。
2.3 UART控制寄存器4(UCR4)与FIFO控制寄存器(UFCR):流控与中断触发核心
这两个寄存器是实现高效、可靠数据吞吐的关键,它们共同决定了何时产生中断,如何控制流控。
UCR4 关键位解析:
CTSTL (Bits 15-10): CTS触发水平
- 功能:当
CTSC=1(CTS自动控制)时,此字段定义接收FIFO(RxFIFO)的“警戒水位”。当RxFIFO中的数据量达到或超过这个阈值时,CTS引脚会自动变为高电平(无效),通知对端“暂停发送”。 - 配置策略:这个值需要权衡。设得太小(如1或2),流控会过于频繁,影响吞吐效率。设得太大,则可能导致FIFO溢出前来不及阻止对端,造成数据丢失。一个经验值是设置为RxFIFO深度(32)的一半或三分之二,例如16或20。公式为:
CTSTL = 期望的字符数。例如,设置为16(二进制010000),则当RxFIFO中有16个字符时,CTS变高。
- 功能:当
DREN (Bit 0) / OREN (Bit 1) / BKEN (Bit 2) / TCEN (Bit 3): 接收/发送中断使能
DREN:接收数据就绪中断使能。当RxFIFO中有数据(RDR=1)时触发。OREN:接收溢出错误中断使能。当RxFIFO已满但还收到新数据时触发。BKEN:Break条件中断使能。当检测到Break信号(RXD线持续低电平超过一个帧时间)时触发。TCEN:发送完成中断使能。当发送FIFO和移位寄存器完全空(TXDC=1)时触发。- 中断管理逻辑:这些中断最终会汇聚到少数几个中断向量上(如
UART_MINT_RX,UART_MINT_TX,UART_MINT_UARTC)。你需要查阅芯片的中断控制器章节,了解如何映射和区分这些中断源。例如,DREN和OREN都可能触发UART_MINT_RX相关中断,需要在中断服务程序(ISR)中读取USR1/USR2来判别具体原因。
UFCR 关键位解析:
RXTL (Bits 5-0): 接收FIFO触发水平
- 功能:定义产生接收中断的阈值。当RxFIFO中的数据量达到此阈值时,
RRDY位(USR1 Bit 9)置位,如果DREN使能,则产生中断。 - 与CTSTL的区别:
RXTL管何时通知CPU来取数据,CTSTL管何时通知对端停止发数据。通常RXTL会设置得比CTSTL小。例如,RXTL=8(收到8个字节就中断),CTSTL=24(收到24个字节才让对端暂停)。这样给CPU留出了处理中断、读取数据的时间窗口,避免FIFO被填满。
- 功能:定义产生接收中断的阈值。当RxFIFO中的数据量达到此阈值时,
TXTL (Bits 15-10): 发送FIFO触发水平
- 功能:定义产生发送中断的阈值。当TxFIFO中的数据量低于此阈值时,
TRDY位(USR1 Bit 13)置位,如果发送中断使能(通常通过UCR1),则产生中断,通知CPU可以填充更多数据。 - 配置策略:为了提高发送效率,避免发送器空转,通常将
TXTL设为一个较小的值,比如4。这意味着当TxFIFO中剩余数据少于4个时,就请求CPU补充数据。结合DMA(直接内存访问)使用效果更佳,可以设置DMA请求与TRDY信号联动。
- 功能:定义产生发送中断的阈值。当TxFIFO中的数据量低于此阈值时,
RFDIV (Bits 9-7): 参考时钟分频器
- 功能:对输入时钟IPG_CLK进行分频,产生UART内部参考时钟。这是计算波特率的第一步。
- 计算公式:
UART_REF_CLK = IPG_CLK / (RFDIV值对应的除数)。除数选择:000=/6, 001=/5, 010=/4, 011=/3, 100=/2, 101=/1, 110=/7。 - 与REF[16|25|30]的关系:
REF[16|25|30]位指示的是UART_REF_CLK的频率,用于波特率计算模块。因此,你需要根据系统主频和期望的波特率,先选择RFDIV得到一个合适的UART_REF_CLK,然后设置对应的REFx位,最后再计算波特率分频值写入UBMR/UBIR。
配置流程总结: 一个完整的UART初始化配置,特别是涉及FIFO和中断的,应遵循以下顺序:
- 禁用UART(通过UCR1等全局控制位)。
- 设置
SRST=0进行软件复位。 - 配置波特率寄存器(UBMR, UBIR),这需要先确定
RFDIV和REFx。 - 配置UCR2(数据格式、基本使能)。
- 配置UFCR(FIFO触发水平、参考时钟分频)。
- 配置UCR4(CTS触发水平、各类错误中断使能)。
- 配置UCR3(其他特定中断使能)。
- 清除所有状态寄存器(USR1, USR2)中的标志位(写1清除)。
- 将
SRST位写0再自动变1,退出复位状态。 - 使能UART全局开关(UCR1)和接收/发送使能(UCR2的RXEN/TXEN)。
- 使能所需的中断(在UCR3/UCR4中,以及芯片的中断控制器中)。
3. 状态寄存器解析与中断服务程序设计要点
配置好控制寄存器只是开始,在中断驱动模式下,服务程序(ISR)如何高效、正确地处理状态寄存器中的标志位,才是稳定性的关键。MC9328MX1的UART状态信息分布在USR1和USR2两个寄存器中。
3.1 状态寄存器USR1:实时通信状态
USR1反映了通信过程中的即时状态和错误。
RRDY (Bit 9): 接收就绪
- 这是最常用的中断源。当RxFIFO中的数据量达到
RXTL设定的阈值时,此位置1。在ISR中,应循环读取数据寄存器(URXD),直到RRDY自动清除(表示FIFO数据已低于阈值)。注意:读取数据寄存器时,要同时读取其中的CHARRDY位(在数据寄存器中),以确保读取的数据是有效的。
- 这是最常用的中断源。当RxFIFO中的数据量达到
TRDY (Bit 13): 发送就绪
- 当TxFIFO中的数据量低于
TXTL设定的阈值时,此位置1。在ISR中,应继续向数据寄存器(UTXD)写入数据,直到填满FIFO或待发送数据写完。写入操作会使TRDY在数据量超过阈值后自动清零。
- 当TxFIFO中的数据量低于
FRAMERR (Bit 10) / PARITYERR (Bit 15): 帧错误与奇偶错误
- 这些是错误标志。必须用���1的方式清除。一个健壮的ISR必须检查并处理这些错误。处理方式可以是丢弃错误帧、记录错误计数、甚至触发系统复位。切勿只读不写,否则中断会持续触发。
RTSD (Bit 12): RTS状态变化
- 当
RTSEN=1且RTEC选择的边沿���RTS引脚上被检测到时,此位置1。用于检测对方流控状态的变化。同样需要写1清除。
- 当
ESCF (Bit 11): 转义序列检测
- 如果使能了转义序列检测,并在数据流中检测到预设字符,此位置1。用于实现协议解析。
3.2 状态寄存器USR2:扩展状态与错误
USR2包含了一些额外的状态和错误标志。
RDR (Bit 0): 接收数据就绪
- 只要RxFIFO中有至少1个字符,此位就为1。它与
RRDY的区别在于,RDR不依赖阈值,有数据就置位。当RxFIFO被读空,此位自动清零。DREN中断使能的是RDR条件。
- 只要RxFIFO中有至少1个字符,此位就为1。它与
ORE (Bit 1): 溢出错误
- 严重错误。当RxFIFO已满,但一个新的字符已经接收完成时,此位置1,新字符会丢失。必须写1清除。出现此错误通常意味着CPU处理速度跟不上数据接收速度,或者
CTSTL/RXTL设置不合理,流控未能及时生效。
- 严重错误。当RxFIFO已满,但一个新的字符已经接收完成时,此位置1,新字符会丢失。必须写1清除。出现此错误通常意味着CPU处理速度跟不上数据接收速度,或者
BRCD (Bit 2): Break条件检测
- 当接收端检测到RXD线持续低电平时间超过一个完整字符帧(包括起始位、数据位、校验位、停止位)时,此位置1。常用于某些特殊协议或调试中表示帧间隔。需写1清除。
TXDC (Bit 3): 发送完成
- 当TxFIFO和发送移位寄存器都为空时,此位置1。用于判断一帧数据是否完全发送完毕。在需要严格知道发送结束时间的场景(如切换半双工方向)非常有用。向TxFIFO写数据会清除此位。
ADET (Bit 15): 自动波特率检测完成
- 如果使能了自动波特率检测功能,在接收到字符‘A’或‘a’并成功检测到波特率后,此位置1。需写1清除。
3.3 中断服务程序(ISR)设计模板与避坑指南
一个稳健的UART ISR模板应遵循以下流程:
void UART1_IRQHandler(void) { uint32_t usr1 = READ_REG(UART1_USR1); uint32_t usr2 = READ_REG(UART1_USR2); // 1. 处理接收相关中断 (可能由RDR、RRDY等触发) if (usr2 & USR2_RDR_MASK) { // 或检查接收中断使能标志对应的状态位 // 循环读取,直到FIFO为空或低于阈值 while (READ_REG(UART1_USR2) & USR2_RDR_MASK) { uint32_t data = READ_REG(UART1_URXD); if (data & URXD_CHARRDY_MASK) { // 检查数据有效位 // 处理有效数据 data & 0xFF process_rx_data(data & 0xFF); } } // 注意:读取数据后,RDR/RRDY会自动更新,无需软件清除 } // 2. 处理发送相关中断 (可能由TRDY、TXDC等触发) if (usr1 & USR1_TRDY_MASK) { // 检查是否还有数据要发送 while (tx_buffer_not_empty() && !tx_fifo_full()) { uint8_t data = get_next_tx_byte(); WRITE_REG(UART1_UTXD, data); } // 如果所有数据发送完毕,可以考虑禁用发送中断以避免空中断 if (tx_buffer_empty()) { DISABLE_TX_INTERRUPT(); // 操作UCR1或相关寄存器 } } // 3. 处理错误中断 (必须清除标志位!) if (usr1 & USR1_PARITYERR_MASK) { WRITE_REG(UART1_USR1, USR1_PARITYERR_MASK); // 写1清除 parity_error_count++; // 可能的错误处理:丢弃当前FIFO数据?重置接收状态? } if (usr1 & USR1_FRAMERR_MASK) { WRITE_REG(UART1_USR1, USR1_FRAMERR_MASK); // 写1清除 frame_error_count++; } if (usr2 & USR2_ORE_MASK) { WRITE_REG(UART1_USR2, USR2_ORE_MASK); // 写1清除 overrun_error_count++; // 溢出是严重错误,可能需要更激进的处理,如清空FIFO FLUSH_RX_FIFO(); } if (usr2 & USR2_BRCD_MASK) { WRITE_REG(UART1_USR2, USR2_BRCD_MASK); // 写1清除 // Break信号处理 } // 4. 处理其他特定中断(如RTSD, ESCF, ADET等) if (usr1 & USR1_RTSD_MASK) { WRITE_REG(UART1_USR1, USR1_RTSD_MASK); // 处理RTS边沿变化 } // ... 其他状态位处理 }避坑指南:
- 清除标志位顺序:先读取状态寄存器值进行判断,再针对需要清除的位写1清除。避免先清除再判断,可能丢失在判断期间新产生的标志。
- FIFO读取循环:在
RDR中断中,使用while循环读取,但必须设置超时或最大读取次数限制,防止在异常情况下(如对方持续高速发送)ISR执行时间过长,影响其他中断响应。 - 中断使能管理:在发送完所有数据后,及时禁用发送就绪中断(
TRDY)。在需要发送时再开启。这可以避免无数据可发时,TRDY中断不断空跑消耗CPU资源。 - 错误处理:对于
ORE(溢出)错误,除了清除标志,强烈建议在ISR中清空接收FIFO(可以通过连续读数据寄存器实现),因为溢出时FIFO中的数据序列可能已经错乱,继续使用这些数据可能导致协议解析错误。 - 状态位粘滞:有些错误状态位(如
FRAMERR)可能在一次接收错误中只对应一个错误帧。但在连续接收中,ISR可能来不及处理,下一个错误又来了。确保你的ISR执行速度足够快,或者考虑在错误处理段暂时提升中断优先级。
4. 高级功能配置与实战场景分析
掌握了基础配置和中断处理,我们来看看MC9328MX1 UART的一些高级功能如何在实战中应用。
4.1 硬件流控(RTS/CTS)配置实战
场景:两个设备通过UART高速(如921600 bps)传输大量数据。为防止因处理速度不匹配导致数据丢失,需启用硬件流控。
配置步骤:
- 硬件连接:确保设备A的RTS引脚连接设备B的CTS引脚,设备A的CTS引脚连接设备B的RTS引脚。
- 设备A配置(作为数据接收方,控制CTS):
UCR2:IRTS = 0(侦听对方RTS),CTSC = 1(CTS自动控制)。UCR4:CTSTL = 16(根据RxFIFO大小32,设为一半)。假设RXTL = 8。UFCR:RXTL = 8(收到8字节就中断)。
- 设备B配置(作为数据发送方,控制RTS):
UCR2:IRTS = 0,CTSC = 0(CTS手动控制,但通常接收方自动控制CTS,发送方只需侦听CTS,所以CTSC配置不影响,主要靠IRTS)。- 实际上,对于发送方,关键是将
IRTS设为0,这样它的发送器就会受自己的CTS引脚(即对方RTS)控制。它不需要设置CTSTL。
- 工作流程:
- 设备B想发送数据,但首先会检查自己的CTS引脚(即设备A的RTS)。如果设备A未准备好(RTS为高),设备B会等待。
- 设备A上电后,其CTS(由接收FIFO控制)初始为低(有效),允许设备B发送。
- 设备B开始发送数据到设备A。
- 当设备A的RxFIFO数据量达到
CTSTL=16时,其CTS引脚自动变高(无效)。 - 设备B检测到自己的CTS变高,立即停止发送当前正在传输的字符(完成后)。
- 设备A的ISR因
RXTL=8早已触发,开始读取数据。当RxFIFO数据被读到低于CTSTL阈值时,CTS自动恢复为低。 - 设备B检测到CTS变低,恢复发送。
- 如此循环,实现自动调速。
常见问题:流控不生效,对方一直发数据导致溢出。
- 检查1:确认
CTSC是否设置为1。如果设为0,CTS将由软件控制,不会自动响应FIFO。 - 检查2:确认
CTSTL值是否合理且已正确写入。该字段在UCR4的高位,注意位偏移。 - 检查3:用逻辑分析仪或示波器观察CTS/RTS引脚波形。看CTS是否在FIFO数据多时变高,数据被读取后变低。
- 检查4:确认对方设备(发送方)的
IRTS位是否为0。如果为1,它会忽略CTS信号,一直发送。
4.2 FIFO与DMA协同工作配置
对于高速或大数据量传输,使用CPU在ISR中逐个字节搬运数据效率低下。利用TRDY和RRDY信号触发DMA传输是更好的选择。
发送DMA配置思路:
- 配置DMA通道的源地址为内存中的发送缓冲区,目标地址为UART的发送数据寄存器(UTXD)。
- 将DMA请求源设置为UART的
TRDY信号(或与之关联的特定DMA请求线)。 - 在UART端,设置
TXTL(例如=4)。当TxFIFO数据量低于4时,TRDY置位,触发DMA请求。 - DMA控制器自动将数据从内存搬移到UTXD,直到设定的传输长度完成。
- 传输完成后,DMA产生完成中断,此时可以检查
TXDC位确认所有数据已发出,或准备下一包数据。
接收DMA配置思路:
- 配置DMA通道的源地址为UART的接收数据寄存器(URXD),目标地址为内存中的接收缓冲区。
- 将DMA请求源设置为UART的
RRDY信号(当RxFIFO数据达到RXTL阈值时触发)。 - 设置
RXTL(例如=8)。当收到8个字节时,触发DMA请求。 - DMA控制器自动将数据从URXD搬移到内存。注意,需要配置DMA为每次传输读取URXD(32位),但实际数据在低8位,并且要处理
CHARRDY位(通常DMA会忽略,但软件后期处理需要知道哪些数据有效)。 - 挑战:如何知道一帧数据接收完毕?DMA可以设定固定长度,或者结合IDLE中断(
RXDS)。当检测到接收空闲(RXDS=1)时,认为一帧结束,停止DMA或进行帧处理。
关键点:使用DMA时,要特别注意TXTL/RXTL的设置与DMA突发传输大小的匹配。如果TXTL设得太大,DMA触发不够及时,发送器会空闲;设得太小,DMA请求过于频繁,增加总线负担。通常需要结合测试调整。
4.3 自动波特率检测与红外模式
自动波特率检测:
- 使能相关功能(通常涉及UCR3的
BPEN位和UART的特定模式寄存器)。 - 发送方首先发送一个字符‘A’或‘a’(0x41或0x61,其位模式包含从高到低的跳变)。
- 接收方检测这个字符的位时长,从而计算出波特率,并设置内部的波特率分频器。
- 检测成功后,状态寄存器USR2中的
ADET位会置1。 - 注意事项:自动波特率检测对起始位的识别要求较高,在噪声大的环境中可能不可靠。通常用于初始连接或调试。
红外模式(IrDA):
- 使能红外模式(通常通过UCR1等寄存器,手册未在此节显示)。
- 配置
INVT(UCR3 Bit1)和INVR(UCR4 Bit9)来设置红外发射和接收的极性。 - 配置
IRSC(UCR4 Bit5)选择投票逻辑的时钟源。 - 红外模式会将正常的UART数字脉冲编码成占空比为3/16的光脉冲。需要外接红外收发器。
- 注意通信距离和角度,红外通信易受遮挡和强光干扰。
5. 调试技巧与常见问题排查实录
即使配置看起来完全正确,UART通信仍可能出问题。以下是一些基于寄存器状态的排查经验。
问题1:能发送,不能接收;或能接收,不能发送。
- 检查1:
UCR2中的RXEN和TXEN是否都已置1。 - 检查2:
SRST位是否为1。如果为0,模块处于复位状态。 - 检查3:检查波特率寄存器(UBMR, UBIR)计算和配置是否正确。最常用方法:用示波器测量TXD引脚,看发送的波形波特率是否与预期一致。一个简单的办法是让程序持续发送0x55(二进制01010101),示波器上应该看到标准的方波,测量一个位的时间即可算出实际波特率。
- 检查4:检查引脚复用配置。MC9328MX1的UART引脚可能与其他功能复用,需要确保IOMUX(输入输出复用控制器)已正确配置为UART功能。
问题2:通信数据错乱,出现大量帧错误或奇偶错误。
- 检查1:双方的数据格式(数据位、停止位、校验位)是否完全一致。重点检查
WS、STPB、PREN、PROE。 - 检查2:地线连接是否良好。串口通信需要共地,地线不稳会引入巨大噪声。
- 检查3:波特率容差。计算出的波特率分频值可能不是整数,存在误差。高速率下(如115200以上),误差累积可能导致采样点偏移。尽量选择时钟源和分频数能使波特率误差最小的配置。
- 检查4:电气电平是否匹配。MC9328MX1是3.3V TTL电平,如果连接RS-232设备需要电平转换芯片。
问题3:中断能进入,但似乎只进入一次,之后不再触发。
- 检查1:这是最常见的原因:状态寄存器中的中断标志位没有清除。例如,发生了帧错误(
FRAMERR=1)并使能了中断,但ISR中没有写1清除这个标志位。该标志位会一直保持,阻止后续相同类型的中断产生。务必在ISR中清除所有检测到的状态标志(通过写1)。 - 检查2:中断使能位是否被意外清除。检查UCR3、UCR4中的
PARERREN、FRAERREN、DREN等位。 - 检查3:芯片级的中断控制器(如NVIC)配置,中断是否被屏蔽或优先级设置有问题。
问题4:使用FIFO和中断,但偶尔还是会丢失数据。
- 检查1:
RXTL和CTSTL设置是否合理。如果RXTL设得太大(如28),而数据涌入速度很快,可能在CPU响应中断并开始读取数据前,FIFO就已经满了(导致ORE)。尝试减小RXTL(如4或8),让中断更早触发。 - 检查2:
CTSTL是否小于FIFO深度(32)。确保流控能及时生效。CTSTL应设置为小于32且留有一定余量给CPU处理。例如设为20。 - 检查3:CPU总中断是否被长时间关闭?在ISR中处理任务是否过重?优化ISR,只做最必要的操作(如搬运数据),将复杂处理放到主循环中。
- 检查4:是否使能了
ORE中断并在其中清空了FIFO?如果没有,溢出后FIFO可能处于异常状态。
问题5:如何判断通信是否真的在工作?
- 软件环回测试:将
UCR2中的IRTS和CTSC都设为1(忽略RTS,CTS自控),然后将TXD和RXD在PCB上短接。程序发送一个数据,然后接收。如果能在接收端读到发送的数据,说明UART内核、数据通路基本正常。 - 寄存器诊断:在通信过程中,定期读取
USR1和USR2寄存器,观察RRDY、TRDY、TXDC以及各种错误标志的变化,这能最直接地反映硬件状态。 - 使用调试器:在调试器中设置对UART数据寄存器的读写断点,或者监控关键状态寄存器的值,可以动态跟踪通信流程。
通过对MC9328MX1 UART控制与状态寄存器的逐位剖析,并结合实际的配置策略、中断处理模板和调试技巧,你应该已经对如何“驾驭”而不仅仅是“使用”一个UART外设有了更深的理解。寄存器编程的魅力就在于,你直接与硬件对话,掌控每一个细节。这份掌控力,正是解决那些最棘手的嵌入式通信问题的关键。