3. UART串口通信

任务目标

在Zynq平台上通过UART实现与PC的双向串口通信:PS端接收PC发送的字符串,并将接收到的数据原样回传(回环验证)。通过本案例掌握UART控制器初始化、中断收发配置及中断服务程序编写方法。

硬件环境

  • Zynq-7000系列开发板(如Zedboard、PYNQ-Z2等)
  • USB-UART线缆连接开发板UART接口与PC
  • PC端串口终端(如PuTTY、MobaXterm,波特率115200,8N1)

实现原理

Zynq PS端UART1控制器通过MIO引脚连接板载USB-UART桥接芯片。本案例使用中断方式接收数据:当RX FIFO接收到字符时触发中断,中断服务程序读取数据并立即写入TX FIFO完成回传。发送方向采用轮询方式,确保数据写入完成后才退出。

关键完整代码

// main.c - UART中断收发回环示例
#include "xparameters.h"
#include "xuartps.h"
#include "xscugic.h"
#include "xil_printf.h"

#define UART_DEVICE_ID  XPAR_XUARTPS_0_DEVICE_ID
#define INTC_DEVICE_ID  XPAR_SCUGIC_SINGLE_DEVICE_ID
#define UART_IRPT_INTR  XPAR_XUARTPS_0_INTR

static XUartPs UartInst;
static XScuGic IntcInst;

// 中断服务函数:读取RX数据并回写
void UartRecvHandler(void *CallBackRef, u32 Event, unsigned int ByteCount) {
    u8 RecvBuf[8];
    u32 ReceivedCount = 0;

    if (Event == XUARTPS_EVENT_RECV_DATA) {
        ReceivedCount = XUartPs_Recv(&UartInst, RecvBuf, ByteCount);
        if (ReceivedCount > 0) {
            // 将收到的数据原样发送回PC
            XUartPs_Send(&UartInst, RecvBuf, ReceivedCount);
        }
    }
}

int UartIntrSetup(XScuGic *IntcInstPtr, XUartPs *UartInstPtr, u16 UartIntrId) {
    XScuGic_Config *IntcConfig;
    int Status;

    // 初始化中断控制器
    IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
    Status = XScuGic_CfgInitialize(IntcInstPtr, IntcConfig, IntcConfig->CpuBaseAddress);
    if (Status != XST_SUCCESS) return XST_FAILURE;

    // 连接UART中断
    Status = XScuGic_Connect(IntcInstPtr, UartIntrId,
                             (Xil_InterruptHandler)XUartPs_InterruptHandler,
                             (void *)UartInstPtr);
    if (Status != XST_SUCCESS) return XST_FAILURE;

    // 使能UART中断
    XScuGic_Enable(IntcInstPtr, UartIntrId);

    // 使能异常处理
    Xil_ExceptionInit();
    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT,
                                 (Xil_ExceptionHandler)XScuGic_InterruptHandler,
                                 (void *)IntcInstPtr);
    Xil_ExceptionEnable();

    return XST_SUCCESS;
}

int main(void) {
    XUartPs_Config *UartConfig;
    int Status;

    // 初始化UART
    UartConfig = XUartPs_LookupConfig(UART_DEVICE_ID);
    Status = XUartPs_CfgInitialize(&UartInst, UartConfig, UartConfig->BaseAddress);
    if (Status != XST_SUCCESS) {
        xil_printf("UART init failed\r\n");
        return XST_FAILURE;
    }

    // 设置波特率115200,8数据位,无校验,1停止位
    XUartPs_SetBaudRate(&UartInst, 115200);
    XUartPs_SetDataFormat(&UartInst, XUARTPS_FORMAT_8N1);

    // 设置中断触发条件:RX FIFO非空即触发
    XUartPs_SetInterruptMask(&UartInst, XUARTPS_IXR_RXOVR | XUARTPS_IXR_RXEMPTY);

    // 建立中断连接
    Status = UartIntrSetup(&IntcInst, &UartInst, UART_IRPT_INTR);
    if (Status != XST_SUCCESS) {
        xil_printf("Interrupt setup failed\r\n");
        return XST_FAILURE;
    }

    // 注册UART事件处理函数
    XUartPs_SetHandler(&UartInst, UartRecvHandler, &UartInst);

    xil_printf("UART Echo Ready. Type characters in terminal...\r\n");

    while (1) {
        // 主循环空闲,等待中断
    }

    return 0;
}

运行与验证

  1. 创建工程:在Vivado中创建基于Zynq的硬件设计,使能UART1(MIO 14/15),导出硬件并启动Xilinx SDK。
  2. 编译下载:新建Application Project,将上述代码复制到main.c,编译后将程序下载到开发板运行。
  3. 连接串口:PC打开串口终端,选择对应COM口,设置波特率115200、8数据位、无校验、1停止位。
  4. 验证回环:在终端中输入任意字符(如"Hello Zynq"),观察终端是否立即回显相同内容。若每个发送的字符都原样返回,则说明UART中断收发功能正常。

通过条件:终端能实时回显所有发送的字符,无乱码、无丢失,且开发板串口输出提示信息"UART Echo Ready"。

常见问题

  • 无回显:检查MIO引脚连接是否正确,确认终端波特率与代码设置一致。
  • 乱码:多为波特率不匹配或接地不良,重新确认串口参数。
  • 中断不触发:确认XUartPs_SetInterruptMask中RXEMPTY位设置正确,该位在RX FIFO非空时触发中断。