i.MX21 UART驱动开发全解析:从原理到实战避坑指南
发布时间:2026/6/17 17:02:38
分类:文化教育
浏览:1234

1. 项目概述从手册到实战深入理解i.MX21的UART如果你在嵌入式领域摸爬滚打过几年肯定对UART通用异步收发器不陌生。它就像电子设备间的“方言”简单、古老但无处不在。从早期的PC串口到现在的嵌入式设备调试、模块通信UART始终是工程师最可靠的伙伴之一。我最近在为一个基于i.MX21的老项目做维护和功能升级重新啃了一遍飞思卡尔现恩智浦那本厚厚的参考手册。手册写得固然详尽但更像一本字典缺乏将各个功能点串联起来、指导实际开发的“地图”。这次我就结合手册内容和这些年的踩坑经验把i.MX21的UART模块从原理到配置再到实战中的那些“坑”和技巧系统地梳理一遍。无论你是刚接触i.MX21的新手还是想深入理解UART内部机制的老鸟希望这篇近万字的详解能让你少走些弯路。2. UART核心原理与i.MX21模块架构2.1 异步串行通信的本质UART通信的核心是“异步”这意味着通信双方没有统一的时钟线来同步每一位数据。那么如何保证接收方能准确识别发送方发来的“0”和“1”呢答案就是波特率Baud Rate和帧格式Frame Format。你可以把UART通信想象成两个人约好每隔固定时间比如1秒传递一张纸条一个数据位。这个“固定时间”就是波特率的倒数例如115200波特率意味着每秒传输115200个比特每个比特的持续时间约为8.68微秒。发送方严格按这个节奏发送接收方也按同样的节奏去“听”只要双方时钟误差在允许范围内通常要求误差小于2-3%就能正确解码。一个完整的UART数据帧就像一列火车起始位Start Bit永远是逻辑0就像火车的车头告诉接收方“注意数据要来了”。数据位Data Bits紧跟在起始位后面通常是5-9位最常见的是8位代表一个字节的实际数据。数据位传输顺序是低位LSB在前。校验位Parity Bit可选用于简单的错误检测可以是奇校验或偶校验。发送方会计算数据位中“1”的个数通过设置校验位使得整个数据位校验位中“1”的个数为奇数奇校验或偶数偶校验。停止位Stop Bit(s)永远是逻辑1像火车的车尾标志着一帧数据的结束。可以是1位、1.5位或2位。在i.MX21的UART模块中这些概念被精确定义。例如Bit Time就是一个比特的传输时间由波特率决定。**Framing Error帧错误**发生在接收方在预期的停止位位置没有检测到逻辑1这通常意味着双方的波特率设置不匹配或线路受到严重干扰。**Parity Error奇偶校验错误**则说明接收方计算出的校验位与收到的校验位不符数据在传输中可能发生了单比特翻转。2.2 i.MX21 UART模块功能全景i.MX21处理器集成了多个UART模块UART1-UART4每个都是一个功能相当完整的独立外设。根据手册其核心特性包括全双工异步通信支持NRZ不归零标准格式和IrDA红外编码格式。深度缓冲独立的32字节发送FIFOTxFIFO和32半字16位接收FIFORxFIFO。FIFO能有效减轻CPU中断负担提升通信效率。灵活的流控制支持RTS请求发送/CTS清除发送硬件流控制这是实现可靠高速通信、防止数据丢失的关键。丰富的中断源包括发送FIFO空、接收数据就绪、接收线路空闲Idle Line、接收超时Ageing、BREAK信号检测、RTS边沿触发等多种中断允许程序以事件驱动方式高效处理串口数据。低功耗特性RTS、AIRINT红外异步唤醒、AWAKE接收异步唤醒中断可以将ARM926EJ-S核心从STOP模式唤醒这对于电池供电设备至关重要。DMA支持提供独立的发送和接收FIFO的DMA请求信号可与DMA控制器配合实现大数据块的无CPU干预传输。自动波特率检测模块可以自动检测并锁定输入数据流的波特率简化了与未知设备对接的配置过程。可编程的二进制速率乘法器BRM用于生成精确的波特率即使系统时钟与目标波特率不是整数倍关系。模块的时钟来源于PERCLK1外设时钟1并通过一个可编程的分频器RFDIV[2:0]产生参考时钟。手册特别强调系统时钟ipg_clk的频率必须大于这个UART参考时钟频率否则某些功能可能无法正常工作。这是一个容易被忽略的硬件约束条件。3. 硬件连接与GPIO复用配置详解3.1 信号定义与硬件流控制i.MX21的每个UART通道对外引出4个关键信号引脚其功能如下表所示信号名称方向有效电平功能描述UARTx_TXD输出高发送数据线。输出NRZ或IrDA编码的串行数据。UARTx_RXD输入高接收数据线。接收NRZ或IrDA编码的串行数据。UARTx_RTS输入低请求发送。这是一个输入信号。当外部设备如Modem DCE设备准备好接收数据时会拉低此引脚通知UART。UART的发送器通常会等待此信号有效低电平后才开始发送。可通过IRTS位忽略此信号。UARTx_CTS输出低清除发送。这是一个输出信号。当UART接收器准备好接收数据时会拉低此引脚通知外部设备。当接收FIFO快满时可编程触发点此引脚会被置高无效提示对方暂停发送。这里最容易混淆的就是RTS和CTS的方向。记住一个简单的对应关系在i.MX21的UART模块视角通常作为DTE设备如计算机RTS是输入CTS是输出。它用RTS来“听”对方是否允许我发用CTS来“告诉”对方我是否允许你发。连接时本端的TXD接对端的RXD本端的RTS接对端的CTS本端的CTS接对端的RTS。注意许多现代微控制器的UART模块将RTS/CTS都配置为可编程的输入/输出更加灵活。但i.MX21的UART1-3的RTS/CTS方向是固定的UART4的CTS在某些模式下可作为输入需查具体手册硬件设计时必须严格按照此定义连接否则流控制无法工作。3.2 GPIO复用配置实战i.MX21的引脚大多是多功能复用的UART信号与GPIO共用引脚。因此在使用UART前必须正确配置引脚复用控制器将相应引脚的功能切换到UART模式。根据手册Table 31-2配置主要涉及两个寄存器GPIO In Use Register (GIUS)和General Purpose Register (GPR)。GIUS某位为0时表示该引脚用于外设功能为1时表示用作通用GPIO。GPR用于选择具体是哪个外设功能。对于UART1-3这些信号是Port E引脚的主要功能Primary function配置GPR_E对应位为0即可。对于UART4其信号是Port B引脚的备用功能Alternate function需要将GPR_B对应位置1。以配置UART1为例其引脚对应关系为UART1_TXD - GPIO Port E[12]UART1_RXD - GPIO Port E[13]UART1_RTS - GPIO Port E[15]UART1_CTS - GPIO Port E[14]配置代码如下所示假设已有操作寄存器的宏定义// 配置UART1引脚复用 // 1. 清除GIUS_E相应位使能外设功能 GIUS_E ~((1 12) | (1 13) | (1 14) | (1 15)); // 2. 清除GPR_E相应位选择UART为主要功能 (对于UART1-3) GPR_E ~((1 12) | (1 13) | (1 14) | (1 15)); // 意还需要通过IOMUX控制器如果i.MX21有来确保引脚功能正确路由 // 但根据手册描述上述GPIO寄存器配置是必须的。实操心得仅仅配置GIUS和GPR可能还不够。在一些i.MX系列处理器中还需要配置IOMUXCI/O复用控制器来最终确定引脚功能。务必查阅你使用的具体芯片的IOMUX章节和引脚定义表进行完整配置。遗漏IOMUX配置是导致“引脚没输出”或“输入没反应”的常见原因。4. 核心功能模块深度解析与配置4.1 波特率生成二进制速率乘法器BRMi.MX21的UART波特率不是通过简单的分频器产生而是使用一个二进制速率乘法器BRM配合两个寄存器UBIR增量寄存器和UBMR模数寄存器来生成。这种方式可以产生非常精确的非整数分频比。其核心公式如下期望波特率 * 16 / 参考时钟频率 NUM / DENOM其中参考时钟频率 PERCLK1 / RFDIV[2:0]RFDIV在UFCR寄存器中UBIR NUM - 1UBMR DENOM - 1为什么是16倍因为UART接收器采用16倍过采样来寻找并稳定采样数据位的中心点以提高抗噪能力。所以波特率时钟实际是PERCLK1经过BRM后产生的16 * Baud Rate的频率。配置步骤根据系统时钟PERCLK1和期望的波特率如115200计算NUM和DENOM。将NUM-1写入UBIRDENOM-1写入UBMR。关键顺序必须先写UBIR再写UBMR。如果只写其中一个BRM会继续使用旧值。计算示例假设PERCLK1 16 MHzRFDIV 1即2分频目标波特率为115200。参考时钟频率 16 MHz / 2 8 MHz NUM/DENOM (115200 * 16) / 8,000,000 1843200 / 8000000 0.2304为了得到整数NUM和DENOM我们将分子分母同时乘以10000NUM 2304 DENOM 10000检查NUM和DENOM是否有公因数可以约分以得到更小的数值有时是必要的因为寄存器宽度有限。2304和10000的最大公因数是16约分后NUM 2304 / 16 144 DENOM 10000 / 16 625因此UBIR 144 - 1 143 (0x8F) UBMR 625 - 1 624 (0x270)将0x8F写入UBIR0x270写入UBMR即可得到精确的115200波特率。注意事项手册警告ipg_clk必须大于UART参考时钟频率。例如如果参考时钟是16MHz那么ipg_clk必须 16MHz。在设计系统时钟树时必须满足这个条件。4.2 发送器与发送FIFO发送器的数据通路是CPU/数据总线 -UTXD寄存器写入 -TxFIFO32字节 -发送移位寄存器- TXD引脚。发送FIFO空中断抑制是一个值得注意的特性。当使能发送空中断TXEN后中断并非在FIFO一空就立即产生。逻辑会“等待”一下看CPU是否会紧接着写入下一个数据。如果CPU在发送移位寄存器移空当前字符之前写入了新数据到FIFO则中断会被抑制避免产生不必要的中断。中断只会在以下情况产生系统复位或模块复位后。当FIFO中只有一个字符且该字符被移入移位寄存器并发送完毕后FIFO和移位寄存器都为空且没有新数据写入时。当FIFO中有两个或更多字符时最后一个字符从FIFO转移到移位寄存器的时刻。这个机制优化了中断效率特别是在使用DMA或循环缓冲区连续发送时。4.3 接收器、接收FIFO与高级功能接收器的数据通路相反RXD引脚 -投票逻辑Vote Logic-接收移位寄存器-RxFIFO32个半字 -URXD寄存器读取。投票逻辑是抗干扰的关键。它以内部分频时钟VOTE_CLK的速率对RXD信号进行高速采样通常是波特率的16倍并对连续三个采样值进行“多数表决”2 out of 3以此滤除短暂的毛刺噪声。几个重要的接收端状态与中断线路空闲检测Idle Line Detect触发条件RxFIFO为空且RXD引脚保持逻辑1空闲状态超过设定的帧数4/8/16/32帧由ICD[1:0]配置。用途非常有用当一包数据发送完毕后线路会恢复空闲。检测到空闲线意味着一帧或一个数据包传输结束。这对于处理变长数据包协议如Modbus RTU至关重要可以替代复杂的超时判断。寄存器使能IDEN检测到空闲后USR2.IDLE置位产生中断。字符超时检测Ageing Character Detect触发条件RxFIFO中有至少一个字符且在相当于传输8个字符的时间内既没有发生读FIFO操作也没有新字符收到。用途解决“最后一个字符”问题。当接收的数据量不足以触发RxFIFO的就绪中断例如触发级别设为4字节但只收到3字节时超时检测可以产生中断通知CPU去读取FIFO中残留的不完整数据防止数据永远滞留在FIFO中。寄存器使能ATEN超时后USR1.AGTIM置位产生中断。BREAK条件检测定义一帧数据中从起始位到停止位全部为逻辑0。通常用于表示通信的开始、结束或复位。处理检测到BREAK时USR2.BRCD置位且只有第一个BREAK字符会被写入RxFIFO。BREAK条件会持续产生帧错误直到检测到0到1的跳变线路恢复空闲或起始位。4.4 自动波特率检测这是一个非常方便的功能尤其在与PC或其他波特率未知的设备进行初始通信时。使能该功能ADBR1并清除检测完成标志ADET0后UART模块会监听RXD线路上的第一个下降沿起始位并测量这个起始位的长度从而计算出对方的波特率。关键协议为了验证自动检测的波特率是否正确发送方必须发送一个ASCII字符‘A’或‘a’0x41或0x61。接收方在成功无错误地收到这个字符后才会将ADET标志置1表示自动波特率检测完成并已锁定。配置流程使能自动波特率检测UCR1.ADBR 1。清除自动检测标志向USR2.ADET写1。使能自动检测中断可选UCR2.ADEN 1。等待ADET标志置位或中断产生。检测完成后UBIR和UBMR寄存器会被硬件自动设置为计算出的值。此时可以开始正常通信。避坑技巧自动波特率检测依赖于一个干净的起始位和特定的验证字符。确保在使能检测前线路处于空闲高电平状态并且对方发送的第一个字符是‘A’。在复杂的电磁环境中首次检测可能会失败需要加入重试机制。5. 中断与DMA配置实战指南5.1 中断系统详解i.MX21的UART中断源丰富合理使用可以极大提高CPU效率。所有中断最终汇聚到一个UART_INT信号。我们需要在多个寄存器中层层使能。主要中断源及使能位中断类型状态标志位 (USR1/USR2)使能控制位 (UCR1/UCR2/UCR3)说明与常见用途发送空中断TRDY(USR2[13])TXEN(UCR1[2])TxFIFO为空或满足抑制条件时触发。用于在中断服务程序ISR中填充新的发送数据。接收就绪断RRDY(USR2[0])RRDYEN(UCR2[0])RxFIFO中数据达到预设触发级别时触发。用于读取接收到的数据。触发级别通过RFIDR等寄存器设置。接收数据就绪RDR(USR2[3])DREN(UCR1[5])当有新字符准备好被RxFIFO接收时置位。与RRDY类似但可能更及时。线路空闲中断IDLE(USR2[11])IDEN(UCR1[14])检测到线路空闲时触发。用于判断数据包接收结束。接收超时中断AGTIM(USR1[8])ATEN(UCR2[3])RxFIFO中有数据但长时间未读也未收到新数据时触发。用于读取残留的不完整数据包。帧错误/溢出错误FRERR/ORE(USR2[2]/[1])通常直接查询发生帧错误或接收溢出时置位。应在ISR或主循环中检查并处理。RTS边沿中断RTSF(UCR2[9])RTSEN(UCR2[8])检测到RTS引脚指定边沿上升、下降或任意时触发。用于响应外部设备的流控制信号变化。自动波特率完成ADET(USR2[15])ADEN(UCR2[7])自动波特率检测成功完成时触发。中断服务程序ISR编写要点读取状态寄存器进入ISR后首先读取USR1和USR2判断中断来源。多个中断可能同时发生。按优先级处理通常先处理接收相关中断RRDY,RDR,IDLE,AGTIM防止数据丢失再处理发送中断TRDY。清除中断标志通过向对应状态位写1来清除中断标志例如USR2 | (1 11)来清除IDLE标志。写0无效。避免在ISR中长时间操作特别是对于高速数据流ISR应只做最必要的操作如从FIFO搬运数据到内存缓冲区将复杂的解析处理放到主循环中。5.2 DMA传输配置对于高速或大数据量的串口通信使用DMA可以解放CPU。i.MX21的UART提供两个DMA请求信号TxFIFO DMA Request和RxFIFO DMA Request。发送DMA流程配置DMA控制器设置源地址内存缓冲区、目的地址UART的UTXD寄存器、传输数据量、传输模式内存到外设。配置UART使能发送器UCR2[1]并使能发送DMA请求UCR1[13]可能相关需查证有些型号是UCR1.TXDMAEN。启动DMA传输。当TxFIFO有空闲位置时UART会向DMA控制器发出请求DMA自动将数据从内存搬移到UTXD寄存器即TxFIFO。传输完成后DMA控制器产生中断通知CPU发送完成。接收DMA流程配置DMA控制器设置源地址UART的URXD寄存器、目的地址内存缓冲区、传输数据量、传输模式外设到内存。配置UART使能接收器UCR2[0]设置接收FIFO触发级别决定何时产生DMA请求并使能接收DMA请求。启动DMA传输。当RxFIFO中数据达到触发级别时UART向DMA发出请求DMA自动将数据从URXD寄存器即RxFIFO搬移到内存。传输完成或缓冲区满后DMA产生中断。实操心得使用DMA时要特别注意数据对齐和FIFO触发级别。URXD寄存器是16位的但数据是8位。DMA传输时通常按字节访问但地址对齐要符合总线要求。触发级别设置得太低如1字节会导致频繁的DMA请求增加总线开销设置得太高如16字节会增加接收延迟。需要根据波特率和数据包大小权衡。另外DMA传输完成后UART的接收缓冲区可能还有残留数据不足触发级别此时需要结合“接收超时中断Ageing”来读取这些残留数据。6. 软件驱动框架与常见问题排查6.1 初始化与驱动框架一个健壮的UART驱动初始化应包括以下步骤// 伪代码示例 int uart_init(int uart_num, uint32_t baudrate) { // 1. 时钟使能确保UART模块和对应GPIO端口的时钟已开启。 enable_clock(UART_CLOCK); enable_clock(GPIO_PORTx_CLOCK); // 2. GPIO复用配置将TXD, RXD, RTS, CTS引脚切换到UART功能。 configure_pinmux(uart_num); // 3. 软件复位UART模块如果支持。 UARTx_UCR2 | (1 SRST_BIT); // 置位软件复位位 delay_us(10); UARTx_UCR2 ~(1 SRST_BIT); // 清除软件复位位 // 4. 禁用UART在配置期间。 UARTx_UCR1 0; UARTx_UCR2 0; UARTx_UCR3 0; // 5. 配置波特率。 uint32_t ref_clk get_perclk1_freq() / get_rfdiv(); calculate_and_set_baudrate(ref_clk, baudrate); // 6. 配置数据格式数据位、停止位、校验位。 UARTx_UCR2 | (UCR2_WS_8_BITS | UCR2_STPB_1_BIT); // 8数据位1停止位 // 如需奇偶校验配置UCR1的PT和PEN位 // 7. 配置FIFO使能FIFO设置触发级别。 UARTx_UFCR | UFCR_RXTL_1; // 设置RxFIFO触发级别例如1字节 UARTx_UFCR | UFCR_TXTL_8; // 设置TxFIFO触发级别例如8字节空时产生中断 // 8. 配置中断如果使用。 UARTx_UCR1 | (UCR1_RRDYEN | UCR1_TRDYEN); // 使能接收和发送中断 // 配置NVIC使能UART中断 // 9. 使能UART收发器。 UARTx_UCR2 | (UCR2_RXEN | UCR2_TXEN); // 使能接收和发送 // 10. 如果需要配置RTS/CTS硬件流控制。 // UARTx_UCR2 | UCR2_IRTS; // 忽略RTS禁用流控制 // 或者配置CTS触发级别等 return 0; }6.2 常见问题与排查实录在调试UART时以下是我遇到并总结的一些典型问题及排查思路问题1完全没有数据收发TXD引脚无波形。检查顺序时钟确认PERCLK1和ipg_clk已正确使能且频率符合要求ipg_clk UART参考时钟。GPIO复用这是最常见的问题。用示波器或逻辑分析仪检查引脚确认已正确配置为UART功能而非GPIO输入。务必检查IOMUX和GIUS/GPR寄存器。软件复位与使能确认已清除软件复位位SRST并且发送器TXEN和接收器RXEN已使能。波特率寄存器确认UBIR和UBMR已正确写入先写UBIR再写UBMR。计算值是否正确。问题2能发送但不能接收或接收数据全错。检查顺序线路连接确认TXD接对端的RXDRXD接对端的TXD。这是最低级也最容易犯的错误。波特率双方波特率必须严格一致。使用示波器测量一个字节的持续时间如0x55即01010101计算实际波特率是否与设置值相符。检查PERCLK1频率和RFDIV分频设置。数据格式检查数据位、停止位、校验位设置是否与对端匹配。常见的错误是8数据位1停止位对上了7数据位2停止位。电气电平i.MX21的UART是TTL/CMOS电平通常0V为逻辑03.3V为逻辑1。如果连接RS-232设备如老式PC串口必须使用电平转换芯片如MAX3232。如果连接RS-485设备需使用485收发器。问题3通信不稳定偶尔丢数据或产生帧错误。检查顺序硬件流控制在高速或长线通信中务必使用RTS/CTS硬件流控制。检查流控制是否已正确使能和连接。确认IRTS位未被设置如果设置了UART会忽略RTS信号。FIFO与中断如果使用中断检查接收FIFO触发级别是否合理。如果设置得太高小数据包可能无法触发中断。考虑结合使用“接收超时中断Ageing”。中断服务程序ISR是否执行时间过长是否及时清除了中断标志是否因为优先级问题被其他中断阻塞在ISR中读取URXD时是否一次性读取了足够的数据利用FIFO深度噪声与接地长距离通信时检查地线是否良好共地。考虑使用差分通信如RS-485或增加终端电阻、磁珠等抗干扰措施。电源噪声用示波器查看UART信号线和电源线是否有毛刺或振铃。可能需要增加串联电阻如22欧姆或小电容进行滤波。问题4低功耗模式下无法通过UART唤醒。检查顺唤醒源配置确认已使能相应的唤醒中断如AWAKE接收异步唤醒或AIRINT红外异步唤醒。在进入STOP模式前需要先清除这些状态位写1然后使能中断使能位如AWAKEN。引脚配置确保在低功耗模式下UART相关的GPIO引脚时钟和功能保持使能并且上拉/下拉配置正确避免引脚悬空引入噪声。中断类型AWAKE是异步中断可以在核心时钟关闭时响应RXD引脚上的下降沿。确保配置正确。调试UART逻辑分析仪是比示波器更强大的工具它可以同时解码多路UART信号直观显示每个字节的数值、时间戳和错误标志极大提升排查效率。从最基本的引脚信号、波特率到复杂的流控制交互、中断时序都能一目了然。