1. 项目概述与核心价值
在嵌入式系统开发,尤其是工业控制、电机驱动和通信网关这类对实时性有严苛要求的领域,串行通信的效率和可靠性往往是项目成败的关键。我们经常使用UART(通用异步收发传输器)进行板间通信、调试信息输出或连接传感器,但传统的UART外设通常由主CPU直接管理,频繁的中断和轮询会消耗大量CPU周期,在高速或高并发数据流下容易成为性能瓶颈。
最近在基于德州仪器(TI)AM263P这类高性能微控制器设计工业通信模块时,我深入研究了其内置的PRU-ICSS子系统中的UART模块。这个模块远不止是一个简单的串口,它更像是一个为实时通信量身定制的“通信协处理器”。其核心价值在于,它将UART的数据搬运和流控等耗时任务,通过智能的中断仲裁和直接的DMA事件机制,从主CPU中解放出来。这意味着主CPU可以更专注于应用层逻辑,而底层字节流的接收、发送、错误处理都能由硬件高效、自动地完成。对于需要同时处理多个串口、EtherCAT或PROFINET等工业协议栈的场景,这种设计能显著提升系统的整体响应速度和确定性。
本文将结合AM263P的技术参考手册,为你深入拆解PRU-ICSS UART模块的内部工作机制,特别是其中断系统和DMA事件支持这两大核心特性。我会从模块的功能框图讲起,带你理解数据流和控制流的路径,然后详细分析四种中断源(THREINT, RDAINT, RTOINT, RLSINT)是如何产生、仲裁和清除的。接着,我们会聚焦DMA模式,看URXEVT(接收事件)和UTXEVT(发送事件)如何与UDMA控制器无缝协作,实现“零CPU干预”的数据搬运。最后,我会分享在实际配置和调试中的关键步骤、常见陷阱以及我的避坑经验,例如如何正确设置FIFO触发阈值以避免数据丢失,以及如何权衡中断模式与轮询模式的选择。无论你是正在评估AM263P的架构师,还是正在调试底层驱动的工程师,相信这些从手册字里行间和实际调试中总结出的细节,都能为你带来直接的帮助。
2. PRU-ICSS UART模块架构深度解析
要驾驭一个复杂的外设,首先得看懂它的“地图”——功能框图。PRU-ICSS UART模块的框图(对应手册中的Figure 7-51)清晰地划分了数据通路、控制逻辑和外部接口,理解它是进行后续所有配置和调试的基础。
2.1 核心功能单元拆解
整个模块可以划分为几个关键部分:
数据通路核心:这是UART工作的主干。发送部分由
Transmitter Holding Register(THR,发送保持寄存器)和Transmitter FIFO(发送FIFO)以及Transmitter Shift Register(TSR,发送移位寄存器)组成。CPU或DMA要发送的数据,首先被写入THR(如果FIFO未启用)或直接压入发送FIFO。然后,硬件自动将数据从THR/FIFO加载到TSR中,由Transmitter Timing and Control(发送时序与控制)单元按照设定的波特率,将并行数据转换成串行比特流,从UART0_TXD引脚输出。接收部分则是一个对称的逆过程:UART0_RXD引脚上的串行数据流,由Receiver Timing and Control(接收时序与控制)单元采样并移入Receiver Shift Register(RSR,接收移位寄存器)。当一个完整的字符(包括起始位、数据位、校验位和停止位)接收完毕后,数据会被自动转移到Receiver Buffer Register(RBR,接收缓冲寄存器)或Receiver FIFO(接收FIFO)中,等待CPU或DMA读取。波特率发生器:这是异步通信的“心跳”。它由一个16位的
Divisor Latch(分频器锁存器,分为高8位MS和低8位LS)驱动。模块的输入时钟(例如192MHz)通过这个分频器产生最终的波特率时钟。手册中的Table 7-70给出了在192MHz输入时钟和13倍过采样模式下的波特率计算示例。例如,要得到128000的波特率,分频值(Divisor Value)需设置为115,实际产生的波特率为128428.094,误差为0.33%。这个误差在异步通信中是允许的,通常要求小于2%。计算波特率的公式为:Baud Rate = (Input Clock Frequency) / (16 * Divisor * Oversampling Factor)。在13倍过采样模式下,公式中的16需替换为13。控制与状态寄存器集:这是软件与硬件交互的窗口。关键寄存器包括:
Line Control Register (LCR):用于配置数据位长度(5-8位)、停止位数、校验位类型(奇校验、偶校验或无校验)。FIFO Control Register (FCR):位于UART_INT_FIFO寄存器的高位部分,用于使能发送/接收FIFO、设置接收FIFO的触发阈值(1, 4, 8, 14字节)。Line Status Register (LSR1):一个非常重要的只读寄存器,软件通过查询它的位来了解当前状态。例如,DR(bit 0)位指示接收缓冲器/ FIFO中是否有数据;THRE(bit 5)位指示发送保持寄存器是否为空;TEMT(bit 6)位指示发送移位寄存器和保持寄存器是否都为空;BI,FE,PE,OE位则分别指示中断、帧错误、奇偶校验错误和溢出错误。Interrupt Enable Register (IER):即UART_INT_EN寄存器,用于使能特定的中断源。Interrupt Identification Register (IIR):即UART_INT_FIFO[3:1]位域。当发生中断时,CPU读取此寄存器可以快速判断是哪种中断类型(优先级最高的那个),而无需轮询所有状态位。
中断与事件控制逻辑:这是模块的“智能”所在。如图7-52所示,所有中断请求(THREINT, RDAINT, RTOINT, RLSINT)都会汇集到一个仲裁器。这个仲裁器根据固定的优先级(RLSINT最高,其次是RDAINT/RTOINT,THREINT最低)决定哪个中断能上报给CPU。同时,该逻辑还负责在FIFO模式下生成DMA事件信号(URXEVT和UTXEVT),直接触发UDMA控制器进行数据搬运。
2.2 复位与电源管理考量
模块的复位分为软件复位和硬件复位,理解其区别对稳定启动至关重要。
- 软件复位:通过
UART_PWR寄存器的UTRST(bit 14)和URRST(bit 13)位独立控制发送器和接收器的复位。将其置0可使对应部分进入复位状态,状态机被重置,但所有UART寄存器的值保持不变。这在需要动态重启某个方向的数据流而不影响配置时非常有用。例如,在通信链路出现不可恢复错误时,可以单独复位接收器来清空FIFO和错误状态,而保持波特率等配置不变。 - 硬件复位:当处理器的RESET引脚被拉低时,整个芯片被复位。此时,UART模块的状态机和所有寄存器都会被强制恢复到默认值。这意味着上电后或全局复位后,必须由软件重新初始化所有寄存器,特别是分频器锁存器(Divisor Latch)。手册7.3.8.3.8.1节特别警告:如果分频器未编程,波特率时钟(BCLK)将保持为逻辑1,导致UART完全无法工作。
电源管理方面,PRU-ICSS UART模块的功耗模式由设备级的复位控制管理器(RCM)统一控制。在系统进入低功耗状态时,RCM会关闭该模块的时钟或电源域。因此,在设计中如果涉及到睡眠唤醒,需要确保在唤醒后重新初始化UART模块,或者确认RCM的配置能保持UART在所需功耗模式下的工作状态。
注意:在实际调试中,最容易被忽略的就是硬件全局复位后的初始化序列。一定要在系统初始化代码中,在访问UART收发数据之前,完整地配置
LCR、DLL/DLM(分频器)、FCR、IER等寄存器。一个良好的习惯是,将UART初始化函数设计为幂等的,即多次调用不会产生副作用,便于在系统不同���段安全地调用。
3. 中断机制详解:从事件产生到CPU响应
中断是CPU响应外部事件最有效的方式。PRU-ICSS UART的中断系统设计精良,支持多种事件类型,并提供了灵活的使能和控制机制。
3.1 四大中断源及其应用场景
根据手册Table 7-71,模块支持四种中断类型,它们各有其触发条件和应用场景:
发送保持寄存器空中断(THREINT):
- 触发条件:当发送保持寄存器(THR)或发送FIFO为空时产生。这意味着最后一个字节已经从缓冲器转移到了发送移位寄存器(TSR),硬件已经准备好接受新的待发送数据。
- 应用场景:这是实现流式发送的关键。在非FIFO模式下,每发送完一个字节就会产生此中断,CPU可以及时写入下一个字节,避免发送线路空闲。在FIFO模式下,当整个发送FIFO为空时才会产生此中断,此时CPU或DMA可以一次性填充多个字节(最多16个),大大减少了中断频率。在DMA模式下,此中断事件会转换为
UTXEVT信号,用于触发DMA传输。
接收数据就绪中断(RDAINT):
- 触发条件:
- 非FIFO模式:只要接收缓冲寄存器(RBR)中有新数据,立即产生中断。
- FIFO模式:当接收FIFO中的数据量达到预设的触发水平(1, 4, 8, 14字节)时产生中断。这是一种“水位线”中断机制,可以有效避免每收到一个字节就打断CPU一次。
- 应用场景:这是最常用的接收中断。在FIFO模式下,合理设置触发水平可以在中断开销和数据接收延迟之间取得平衡。例如,在115200波特率下,接收一个字节约需87微秒。如果触发水平设为8,则大约每700微秒产生一次中断,CPU一次性读取8个字节,效率远高于每87微秒中断一次。
- 触发条件:
接收超时中断(RTOINT):
- 触发条件:仅FIFO模式有效。当接收FIFO中有数据,但在超过4个字符时间内既没有新字符到来,也没有字符被读取走,则产生此中断。
- 应用场景:这是一个非常重要的“兜底”机制。想象一下,接收FIFO中只有2个字节,而触发水平设为4,那么
RDAINT永远不会产生。如果没有RTOINT,这两个字节就会永远滞留在FIFO中,造成数据丢失或处理延迟。RTOINT确保了即使数据量不足触发水平,也能在一定延迟后通知CPU来处理“残留”的数据。字符时间取决于帧格式,例如,对于8数据位、无校验、1停止位的格式,一个字符是10位(1起始+8数据+1停止)。在115200波特率下,一个字符时间约为86.8微秒,4个字符时间约为347微秒。
接收线路状态中断(RLSINT):
- 触发条件:当发生溢出错误(OE)、奇偶校验错误(PE)、帧错误(FE)或线路中断(BI)时产生。
- 优先级:这是优先级最高的中断。当错误发生时,它会抢占其他中断,确保CPU能优先处理通信故障。
- 应用场景:用于实现健壮的通信协议。一旦发生此类中断,CPU应立刻读取
LSR1寄存器,检查具体的错误位,并采取相应措施,如清空FIFO、重置接收器、记录错误日志或通知上层协议。
3.2 中断使能、识别与清除流程
中断的处理遵循一个清晰的流程,理解这个流程是编写稳定中断服务程序(ISR)的前提。
使能:通过设置
UART_INT_EN寄存器的相应位来使能所需的中断。例如,设置ETBEI(bit 1)使能THREINT,设置ERBI(bit 0)使能RDAINT和RTOINT,设置ELSI(bit 2)使能RLSINT。一个常见的误区是以为使能了中断就会自动产生,实际上中断的产生还依赖于FIFO控制寄存器(FCR)的配置。例如,即使使能了RDAINT,如果FIFO未被启用(IIR_FIFOEN位为0),则永远不会按FIFO触发水平产生中断,只会按非FIFO模式(每个字节)产生。产生与记录:当中断事件发生且相应使能位为1时,该事件会被记录在
UART_INT_FIFO[3:1]的IIR_INTID位域中,并提交给仲裁器。如果使能位为0,则该事件被静默忽略。仲裁与上报:仲裁器根据固定优先级(RLSINT > RDAINT/RTOINT > THREINT)选择最高优先级的中断,将其作为单一的
UART0 interrupt request to CPU信号发送给CPU。这意味着同一时刻,CPU只“看到”一个中断原因。识别:CPU进入中断服务程序后,第一件事就是读取
UART_INT_FIFO寄存器(即IIR)。IIR_INTID位域的值指明了当前待处理的中断类型(见表7-72)。例如,0110(二进制)表示RLSINT,0100表示RDAINT,1100表示RTOINT,0010表示THREINT。这里有一个关键点:读取IIR这个动作本身,在某些情况下会影响到中断状态。清除:清除中断标志的方法因中断类型而异,必须严格按手册操作,否则会导致中断重复触发或丢失。表7-72详细列出了清除方法:
- RLSINT:对于溢出错误(OE),读取
LSR1寄存器即可清除。对于奇偶、帧错误或中断,则需要读取所有错误数据(即从RBR/FIFO中读出那个出错的字节)才能清除。这意味着ISR中在处理RLSINT时,通常需要先读LSR1判断错误类型,再读RBR来清空错误数据。 - RDAINT:
- 非FIFO模式:读取
RBR寄存器即可清除。 - FIFO模式:当CPU或DMA控制器读取数据,使得FIFO中的字节数低于设定的触发水平时,中断被清除。如果之后数据量又达到触发水平,会再次产生中断。
- 非FIFO模式:读取
- RTOINT:在FIFO模式下,当CPU或DMA从接收FIFO中读取一个字符,或者有新字符到达FIFO,或者软件复位接收器(
URRST=0)时,中断被清除。 - THREINT:向
THR(即UART_RBR_TBR)写入1到16个字符,或者读取IIR_INTID位域,都可以清除此中断。在FIFO模式下,通常采用写入数据的方式来清除。
- RLSINT:对于溢出错误(OE),读取
实操心得:在编写UART中断服务程序时,务必遵循“读取IIR -> 根据ID处理 -> 按规则清除”的流程。对于RLSINT,处理顺序尤为重要:先读LSR1保存错误状态,再读RBR丢弃错误数据,最后再处理可能的正确数据。避免在ISR中做耗时操作,对于接收大量数据,应在ISR中快速将数据拷贝到软件环形缓冲区,然后退出中断,在主循环中处理。另外,在使能中断前,最好先读取一次IIR和LSR1,以清除任何可能存在的陈旧中断标志。
4. DMA事件支持:解放CPU的利器
对于高速或大数据量的串口通信,频繁的中断仍然会给CPU带来负担。PRU-ICSS UART的DMA事件支持功能,可以将数据搬运工作完全交给UDMA(通用DMA)控制器,实现近乎“零CPU开销”的数据传输。
4.1 DMA事件触发机制
DMA事件仅在FIFO模式下有效。模块会生成两个独立的事件信号:
接收事件(URXEVT):
- 触发条件:当接收FIFO中的数据量达到预设的触发水平(1, 4, 8, 14字节)或发生接收超时(RTO)条件时。
- 关键限制:手册中特别强调,如果接收FIFO顶部的数据是错误的(即伴有RLS错误),即使FIFO达到了触发水平,也不会产生URXEVT。这是为了防止DMA将错误数据搬移���内存中。错误数据必须由CPU通过中断(RLSINT)来处理。
- DMA响应:UDMA控制器在收到URXEVT后,会自动发起一次或多次读取操作,通过
UART_RBR_TBR寄存器将FIFO中的数据搬移到指定的内存缓冲区。搬移的字节数可以配置为等于触发水平,或者在链式DMA模式下持续搬移直到FIFO为空。
发送事件(UTXEVT):
- 触发条件:当发送FIFO为空(最后一个字节已从FIFO转移到发送移位寄存器TSR)时产生。此外,当通过设置
UART_PWR[14] UTRST位将发送器从复位状态释放时,也会产生一个UTXEVT事件。这可用于在初始化后自动触发第一次DMA发送。 - DMA响应:UDMA控制器在收到UTXEVT后,会自动从内存中读取数据,并写入
THR(同样是UART_RBR_TBR寄存器)来填充发送FIFO。
- 触发条件:当发送FIFO为空(最后一个字节已从FIFO转移到发送移位寄存器TSR)时产生。此外,当通过设置
4.2 DMA与中断的协同与配置要点
DMA和中断并非互斥,而是可以协同工作,以适应不同的场景:
- 纯DMA模式:关闭UART本身的中断(
IER寄存器相应位置0),完全依靠URXEVT和UTXEVT事件来触发DMA传输。CPU只在DMA传输完成(半满、全满等)时,通过DMA通道完成中断得到通知。这种模式CPU负载最低,适合持续、稳定的数据流。 - DMA与错误中断结合:使能RLSINT中断,但禁用RDAINT和THREINT。数据搬运由DMA负责,而通信错误(帧错误、奇偶错误等)则由CPU通过RLSINT中断及时处理。这种模式兼顾了效率和可靠性。
- DMA与超时中断结合:使能RTOINT中断。DMA负责处理达到触发水平的数据块,而RTOINT负责处理最后不足一块的“尾巴”数据,确保数据不滞留。
配置DMA通道的关键步骤:
- 配置UART:使能FIFO(设置
FCR),设置接收FIFO触发水平,根据需求配置IER。 - 配置UDMA通道:
- 为接收通道设置触发源为
PRU_ICSSG0_UART0_URXEVT(具体事件编号需查芯片数据手册)。 - 为发送通道设置触发源为
PRU_ICSSG0_UART0_UTXEVT。 - 设置源/目标地址(UART数据寄存器地址、内存缓冲区地址)。
- 设置传输数据量(
aCount)和同步方式(通常为EVENT,即每次事件触发一次传输)。
- 为接收通道设置触发源为
- 使能与启动:必须确保在UART产生事件之前,相应的DMA通道已经使能。手册7.3.8.3.5节明确警告:如果DMA通道未就绪,它将错过这个事件,除非UART产生新的事件,否则不会发生数据传输。因此,正确的顺序是:配置并启用DMA通道 -> 启动UART收发(或已有数据到达)。
4.3 FIFO模式详解:中断模式与轮询模式
PRU-ICSS UART的FIFO支持两种服务模式,为开发者提供了灵活性:
- FIFO中断模式:如上文所述,使能FIFO(
IIR_FIFOEN=1)并设置相应的中断使能位(IER)。此时,硬件会在特定事件(数据就绪、发送空、超时、错误)发生时主动中断CPU。这是最常用、最高效的模式。 - FIFO轮询模式:使能FIFO(
IIR_FIFOEN=1),但禁用所有中断(IER相关位清零)。CPU通过定期查询LSR1状态寄存器来检测事件。DR(bit 0):指示接收FIFO中是否有数据(至少1字节)。THRE(bit 5):指示发送保持寄存器(THR)是否为空。TEMT(bit 6):指示发送移位寄存器(TSR)和THR是否都为空。RXFIFOE(bit 7):指示接收FIFO中是否存在任何错误。BI,FE,PE,OE位:指示具体的错误类型。
模式选择建议:
- 中断模式适用于大多数场景,尤其是需要快速响应的实时系统。
- 轮询模式适用于对实时性要求不高、或者CPU本身就在一个紧密循环中运行的系统(例如一个简单的控制循环)。它可以避免中断上下文切换的开销,但需要仔细设计轮询频率,否则可能导致FIFO溢出或发送不及时。在轮询模式下,硬件不会提示接收FIFO触发水平到达或接收超时,软件需要自己管理缓冲区水位。
注意事项:发送和接收FIFO的模式是独立控制的。你可以让接收工作在中断模式(及时响应数据到达),而发送工作在轮询模式(在主循环中检查
THRE位并发送),反之亦然。这种灵活性允许你根据数据流的不对称性来优化系统性能。
5. 高级功能与实战配置指南
除了核心的数据传输和中断/DMA,PRU-ICSS UART还提供了一些高级功能,用于增强通信的可靠性和便利性。
5.1 自动流控(Autoflow Control)
在高速或远距离通信中,防止接收端数据溢出至关重要。PRU-ICSS UART支持硬件自动流控,通过UART0_RTS(请求发送)和UART0_CTS(清除发送)两根信号线实现。
工作原理(参考图7-53):
- 接收端通过监测自身接收FIFO的水位来控制
UART0_RTS输出。当FIFO数据量达到设定的触发水平(1,4,8,14)时,UART0_RTS被置为无效(通常为高电平),通知发送端“我快满了,请暂停发送”。 - 发送端在发送每个字节之前,会检查
UART0_CTS输入引脚。如果UART0_CTS为有效(低电平),则继续发送;如果为无效(高电平),则暂停发送,直到UART0_CTS恢复有效。 - 将本端的
UART0_RTS连接到对端的UART0_CTS,即可形成一个完整的硬件流控环。
- 接收端通过监测自身接收FIFO的水位来控制
时序细节:
UART0_RTS的无效化发生在触发条件满足时,但由于信号传播和对方检测的延迟,发送端可能已经开始了下一个字节的传输。因此,手册指出发送端在RTS无效后可能还会多发送一个字节(见图7-54)。在设置触发水平时,需要为这个“额外字节”留出FIFO空间。UART0_CTS必须在当前字节的最后一个停止位的中间时刻之前变为无效,才能成功阻止下一个字节的发送(见图7-55)。这要求流控信号路径的延迟必须足够小。
配置方法:通过设置
UART_MCTR[5] AFE(Autoflow Enable)位为1来使能自动流控。使能后,RTS和CTS信号由硬件自动管理,无需软件干预,可以彻底消除因CPU处理不及时导致的接收溢出错误(OE)。
5.2 回环控制(Loopback)与仿真考虑
- 回环模式:通过设置
UART_MCTR[4] LOOP位为1,可以将UART的发送输出内部连接到接收输入。此模式用于自测试,在不连接外部硬件的情况下,验证UART的发送、接收数据路径、中断以及Modem控制功能是否正常。在编写驱动程序或系统自检时非常有用。 - 仿真考虑:
UART_PWR[0] FREE位决定了在仿真器暂停(如遇到断点)时UART的行为。- 如果
FREE = 0,UART会在完成当前字的传输后停止,或立即停止(如果当时没有在传输)。这便于调试,可以冻结通信状态。 - 如果
FREE = 1,UART会无视仿真器暂停,继续正常运行。这在调试与时间紧密相关的通信协议时可能有用,但会使调试过程更复杂。 - 手册还提到,仿真器的读操作通常是透明的,但写操作可能会改变寄存器值并影响UART操作,在硬件仿真调试时需要格外小心。
- 如果
5.3 完整初始化与配置流程示例
下面以一个典型的应用场景为例,展示如何配置PRU-ICSS UART工作在115200波特率、8位数据、无校验、1停止位,使能接收FIFO和中断,并准备使用DMA。
引脚复用配置:首先,通过芯片的PinMux工具或寄存器,将
UART0_RXD和UART0_TXD(以及UART0_RTS/UART0_CTS,如果使用流控)对应的GPIO引脚功能设置为UART模式。模块时钟使能:确保PRU-ICSS子系统及其UART模块的时钟已被使能(通过Power Sleep Controller, PSC或相应的时钟控制寄存器)。
软件复位(可选):如果需要,通过设置
UART_PWR[14] UTRST = 0和UART_PWR[13] URRST = 0来复位发送器和接收器,然后将其置1释放复位。设置波特率:
- 根据输入时钟频率(如192MHz)和期望波特率(115200),计算分频器值。假设使用13倍过采样,计算
Divisor = 192000000 / (115200 * 13) ≈ 128.2,取整为128。 - 为了允许访问分频器锁存器,需要设置
Line Control Register (LCR)的DLAB(Divisor Latch Access Bit)位为1。通常LCR[7]就是DLAB位。 - 写入
Divisor Latch (LS)和Divisor Latch (MS)寄存器。对于分频值128,DLL = 128 (0x80),DLM = 0。
- 根据输入时钟频率(如192MHz)和期望波特率(115200),计算分频器值。假设使用13倍过采样,计算
设置通信格式:
- 保持
DLAB=1,配置LCR寄存器。设置数据位长度(例如8位对应WLS[1:0]=11),停止位(1位对应STB=0),校验位(无校验对应PEN=0)。然后,切记将DLAB位清零,以便后续访问数据寄存器。
- 保持
配置FIFO与中断:
- 配置
FIFO Control Register (FCR),即UART_INT_FIFO的高位部分。设置FIFOEN位为1以启用FIFO。设置接收FIFO触发阈值,例如设为8字节(RFITR[1:0]=01)。 - 配置
Interrupt Enable Register (IER)。例如,使能接收数据就绪和线路状态中断:ERBI=1,ELSI=1。暂时不使能发送中断(ETBEI=0),如果我们打算用DMA或轮询发送。
- 配置
(可选)配置自动流控:如果需要,设置
Modem Control Register (MCTR)的AFE位为1,并确保硬件上连接了RTS和CTS线。(可选)配置DMA:
- 在UDMA控制器中,配置一个接收通道,源地址为UART的
UART_RBR_TBR寄存器地址,目标地址为内存中的环形缓冲区,触发源选择URXEVT。 - 配置一个发送通道,源地址为内存缓冲区,目标地址为
UART_RBR_TBR,触发源选择UTXEVT。 - 先使能UDMA通道,然后再开始UART通信。
- 在UDMA控制器中,配置一个接收通道,源地址为UART的
启动收发:此时,UART已经就绪。如果有数据从
RXD引脚到来,当接收FIFO中数据达到8字节或发生超时时,会产生URXEVT事件触发DMA搬运,或者产生RDAINT/RTOINT中断通知CPU。当需要发送数据时,如果使用DMA,只需将数据放入缓冲区并启动DMA发送通道;如果使用轮询,则检查LSR1[5] THRE位,为1时即可向THR写入数据。
6. 常见问题排查与调试技巧
在实际开发中,UART通信不出数据或者数据出错是家常便饭。下面是我总结的一些常见问题及其排查思路。
6.1 问题速查表
| 现象 | 可能原因 | 排查步骤 |
|---|---|---|
| 完全无收发 | 1. 引脚复用未配置。 2. 模块时钟未使能。 3. 分频器(DLL/DLM)未正确初始化。 4. 发送器/接收器处于软件复位状态( UTRST/URRST=0)。 | 1. 检查PinMux配置。 2. 检查PSC或时钟配置寄存器。 3. 确认 LCR[7] DLAB=1时写入了DLL/DLM,然后DLAB=0。4. 检查 UART_PWR寄存器的UTRST和URRST位是否为1。 |
| 能发送,不能接收(或反之) | 1. 单向的软件复位未解除。 2. 中断或DMA事件未正确使能/配置。 3. 线路连接错误(RX/TX接反)。 | 1. 检查UART_PWR寄存器中对应的复位位。2. 检查 IER寄存器使能位,或DMA通道配置和触发源。3. 用示波器或逻辑分析仪检查引脚波形。 |
| 接收数据错误(乱码) | 1. 波特率不匹配(计算错误或时钟源错误)。 2. 数据格式不匹配(数据位、停止位、校验位)。 3. 电气问题(电平不匹配、噪声干扰)。 | 1. 核对双方波特率计算值,用示波器测量实际位宽。 2. 核对双方 LCR配置。3. 检查电平转换电路,增加滤波电容,检查地线连接。 |
| 接收FIFO溢出(OE错误) | 1. 数据到达太快,CPU/DMA处理不及时。 2. 接收中断服务程序(ISR)或DMA搬运太慢。 3. 未使用流控,且发送端持续发送。 | 1. 提高CPU优先级,优化ISR/DMA效率。 2. 增大接收FIFO触发阈值,让CPU/DMA每次处理更多数据。 3. 启用硬件自动流控(RTS/CTS)。 |
| 中断不触发 | 1. 全局中断未开启(CPSR的I位)。 2. PRU-ICSS到CPU的中断路径未配置(INTC)。 3. UART特定中断在 IER中未使能。4. FIFO未使能,却期望FIFO模式中断。 | 1. 确认CPU全局中断使能。 2. 配置PRU-ICSS INTC,将UART中断映射到CPU可识别的系统中断号。 3. 检查 UART_INT_EN寄存器。4. 检查 FCR(IIR_FIFOEN)是否已使能FIFO。 |
| DMA不工作 | 1. DMA通道未在UART事件发生前使能。 2. DMA触发事件源选择错误。 3. DMA传输完成中断未处理,导致通道停滞。 4. 接收FIFO顶部数据错误,抑制了URXEVT。 | 1. 确保先配置并启用DMA通道,再激活UART通信。 2. 核对UDMA事件输入编号。 3. 在DMA完成中断中重新配置或启动下一次传输。 4. 检查 LSR1中的错误位,并读取RBR清除错误数据。 |
6.2 调试技巧与实操心得
从最简模式开始:在调试初期,关闭所有高级功能。先不使用FIFO、中断和DMA,仅配置好波特率和基本格式,采用轮询方式进行最简单的字节收发测试。例如,循环查询
LSR1[0] DR位,有数据则读取;查询LSR1[5] THRE位,为空则写入一个测试字符(如0x55或0xAA,其二进制位01010101/10101010便于观察波形)。用逻辑分析仪或示波器抓取TXD引脚波形,确认波特率和数据格式正确。善用回环模式:在硬件连接不确定时,首先使能回环模式(
LOOP=1)。这样,自己发送的数据会被自己接收。通过轮询发送一个已知数据序列,然后轮询接收,看是否能正确回读。这可以快速排除外部电路问题,确认UART内核本身和软件配置是否正确。寄存器检查清单:编写一个调试函数,打印或通过其他接口输出所有关键UART寄存器的值(
LCR,LSR1,IER,IIR,FCR,MCR,DLL,DLM等)。在出现问题时,对比这些值与预期值,能快速定位配置错误。中断服务程序(ISR)要精简:ISR中只做最必要的操作:读取IIR判断中断源、根据中断类型读取数据或状态、清除中断标志。将数据处理、协议解析等耗时任务放到主循环或任务中。避免在ISR内调用可能阻塞的函数(如
printf)。DMA缓冲区管理:使用双缓冲(Ping-Pong Buffer)或环形缓冲区来配合DMA。例如,为UART接收设置两个DMA缓冲区A和B。当DMA填满缓冲区A并产生完成中断时,在中断中切换DMA目标地址到缓冲区B,并处理缓冲区A中的数据。这样可以实现数据接收和处理的并行,避免数据丢失。
关注超时机制:即使使用了FIFO和DMA,也务必在软件层面实现超时机制。例如,启动一个定时器,在收到最后一个数据包后开始计时。如果超过预定时间(如100ms)没有新数据到来,则认为一帧数据接收完成,即使FIFO未达到触发水平或未发生RTOINT,也应强制处理当前缓冲区中的数据。这能有效应对数据帧不完整或通信意外中断的情况。
电源与复位管理:在系统进入低功耗模式前,如果UART通信需要保持,需确认其所在电源域的状态。从低功耗模式唤醒后,如同硬件复位后一样,必须重新初始化UART模块,因为寄存器状态可能已丢失。将UART初始化代码放在唤醒后的执行路径中。