ZYNQ-7000 UART接收PC数据时未触发中断问题求助
ZYNQ-7000 UART中断模式回传功能故障排查
发送通路可正常打印初始化信息,说明UART引脚配置、发送逻辑、波特率配置基本正常,中断完全不触发按以下优先级排查修复:
一、核心代码错误(90%概率为该类问题)
1. 未显式使能UART接收通路、未复位FIFO
XUartPs_CfgInitialize执行完成后,UART控制寄存器默认将RX通路禁用(RX_DIS位默认置1),XUartPs_SetOperMode仅修改工作模式位,不会自动打开RX通路,RX FIFO不会接收引脚数据,自然不会产生任何接收中断。
在UartInit函数的XUartPs_SetRecvTimeout调用前,添加如下配置:
// 读取当前控制寄存器值 u32 cr_reg = XUartPs_ReadReg(uart_ps->Config.BaseAddress, XUARTPS_CR_OFFSET); // 清除TX_DIS、RX_DIS位,使能收发通路 cr_reg &= ~(XUARTPS_CR_TX_DIS | XUARTPS_CR_RX_DIS); // 触发TX/RX FIFO复位 cr_reg |= XUARTPS_CR_TXRST | XUARTPS_CR_RXRST; XUartPs_WriteReg(uart_ps->Config.BaseAddress, XUARTPS_CR_OFFSET, cr_reg); // 等待FIFO复位完成 while (XUartPs_ReadReg(uart_ps->Config.BaseAddress, XUARTPS_CR_OFFSET) & (XUARTPS_CR_TXRST | XUARTPS_CR_RXRST));
2. 中断初始化顺序错误、未清除上电默认挂起的中断标志
当前中断初始化顺序存在时序问题,且未清除UART模块上电时默认挂起的中断状态位,容易导致中断锁死。将UartIntrInit调整为如下逻辑:
int UartIntrInit(XScuGic *intc, XUartPs *uart_ps){ int status; XScuGic_Config *intc_cfg; intc_cfg = XScuGic_LookupConfig(INTC_DEVICE_ID); if(NULL == intc_cfg) return XST_FAILURE; status = XScuGic_CfgInitialize(intc, intc_cfg, intc_cfg->CpuBaseAddress); if(status != XST_SUCCESS) return XST_FAILURE; // 注册CPU全局IRQ处理函数 Xil_ExceptionInit(); Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler) XScuGic_InterruptHandler, (void *) intc); // 绑定UART中断处理函数 XScuGic_Connect(intc, UART_INT_IRQ_ID, (Xil_ExceptionHandler) Handler, (void *) uart_ps); // 先清除UART所有挂起的中断标志:写1清零 XUartPs_WriteReg(uart_ps->Config.BaseAddress, XUARTPS_ISR_OFFSET, 0xFFFFFFFF); // 使能需要的UART中断 XUartPs_SetInterruptMask(uart_ps, XUARTPS_IXR_RXOVR | XUARTPS_IXR_TOUT); // 先使能GIC层面的UART中断 XScuGic_Enable(intc, UART_INT_IRQ_ID); // 最后打开CPU全局中断开关 Xil_ExceptionEnable(); return XST_SUCCESS; }
3. 中断处理函数逻辑问题
当前中断处理函数存在三个会导致功能异常的问题:
- 中断上下文调用
xil_printf这类阻塞轮询发送函数,极易触发死锁,调试阶段建议用计数器标记中断触发,不要直接打印 - 用
else if判断中断位,多个中断同时挂起时会漏处理,改为逐个判断 - 读中断状态顺序错误,先读ISR再和IMR做与操作,避免时序问题
- 回传数据时优先使用传入的UART实例指针,不要直接用全局变量
修正后的中断处理函数参考:
// 调试用计数器,观察是否进入中断 volatile u32 intr_trigger_cnt = 0; void Handler(void *call_back_ref){ intr_trigger_cnt++; XUartPs *uart_instance_ptr = (XUartPs *) call_back_ref; u32 ReceivedCount = 0; u32 IsrValue; // 先读中断状态,再和中断屏蔽位与 IsrValue = XUartPs_ReadReg(uart_instance_ptr->Config.BaseAddress, XUARTPS_ISR_OFFSET); IsrValue &= XUartPs_ReadReg(uart_instance_ptr->Config.BaseAddress, XUARTPS_IMR_OFFSET); // 接收FIFO达到阈值触发 if( IsrValue & XUARTPS_IXR_RXOVR ){ // 清除中断标志 XUartPs_WriteReg(uart_instance_ptr->Config.BaseAddress, XUARTPS_ISR_OFFSET, XUARTPS_IXR_RXOVR); ReceivedCount = XUartPs_Recv(uart_instance_ptr, ReceivedBuffer + ReceivedByteNum, MAX_LEN - ReceivedByteNum); ReceivedByteNum += ReceivedCount; } // 接收超时触发(FIFO非空且一段时间无新数据),不要用else if if( IsrValue & XUARTPS_IXR_TOUT ){ XUartPs_WriteReg(uart_instance_ptr->Config.BaseAddress, XUARTPS_ISR_OFFSET, XUARTPS_IXR_TOUT); ReceivedCount = XUartPs_Recv(uart_instance_ptr, ReceivedBuffer + ReceivedByteNum, MAX_LEN - ReceivedByteNum); ReceivedByteNum += ReceivedCount; // 回传数据,小数据量可在中断里处理,大数据量建议放主循环 for(u32 sendByte=0;sendByte<ReceivedByteNum;sendByte++){ XUartPs_SendByte(uart_instance_ptr->Config.BaseAddress, ReceivedBuffer[sendByte]); } ReceivedByteNum = 0; } }
二、配置逻辑问题
当前设置的RX FIFO触发阈值为32字节,XUARTPS_IXR_RXOVR中断仅当FIFO内数据量≥32字节时才会触发,单次输入字符少于32时完全依赖超时中断触发,可先将阈值改小(比如1字节)做验证:
XUartPs_SetFifoThreshold(uart_ps, 1);
确认中断可正常进入后再调整阈值到合适值。
三、硬件排查点
如果代码修改后仍无法进入中断,按以下步骤验证硬件通路:
- 先屏蔽中断初始化代码,在主循环加轮询收发测试,确认RX硬件通路正常:
while (1) { if(XUartPs_IsReceiveData(Uart_Ps.Config.BaseAddress)){ u8 recv = XUartPs_RecvByte(Uart_Ps.Config.BaseAddress); XUartPs_SendByte(Uart_Ps.Config.BaseAddress, recv); } }
如果轮询模式下也收不到数据,检查:
- 串口线交叉连接是否正确:ZYNQ MIO48(UART1 TX)接USB转串口RX,MIO49(UART1 RX)接USB转串口TX,共地
- 串口终端关闭RTS/CTS硬件流控,波特率设置为115200、8位数据位、1位停止位、无校验
- 确认Vivado工程中MIO48/49的配置正确,没有被其他外设复用
内容的提问来源于stack exchange,提问作者Jie Han
相关产品推荐
相关产品推荐

