4. UART串口通信

任务目标

在Zynq平台上实现ARM与PC之间的UART通信:PS端通过UART外设发送数据,并采用中断方式接收PC下发的数据,收到后将数据原样回传(回环验证)。通过本案例掌握Zynq UART驱动配置、中断初始化和收发流程。

硬件环境

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

软件环境

  • Vivado 2020.1+(含SDK)
  • 硬件工程需包含Zynq PS核,并启用UART1(MIO 48/49),SDK中BSP自动生成uartps驱动

核心实现

代码基于Vivado SDK的BSP(standalone),使用XUartPs驱动。主程序完成:UART初始化、设置接收中断、发送提示字符串,随后进入中断等待。中断服务函数将接收到的每个字节立即回传,实现回环。

#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;

// 中断服务函数:接收一个字节并立即回传
void UartRecvHandler(void *CallBackRef, u32 Event, unsigned int Data) {
    if (Event == XUARTPS_EVENT_RECV_DATA) {
        XUartPs_Send(&UartInst, (u8 *)&Data, 1);  // 回传收到的字节
    }
}

// UART初始化
int UartInit(u16 DeviceId) {
    XUartPs_Config *Config;
    int Status;

    Config = XUartPs_LookupConfig(DeviceId);
    if (Config == NULL) return XST_FAILURE;

    Status = XUartPs_CfgInitialize(&UartInst, Config, Config->BaseAddress);
    if (Status != XST_SUCCESS) return Status;

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

    return XST_SUCCESS;
}

// 中断控制器初始化并连接UART中断
int IntcInit(u16 DeviceId, u16 UartIntrId) {
    XScuGic_Config *IntcConfig;
    int Status;

    IntcConfig = XScuGic_LookupConfig(DeviceId);
    if (IntcConfig == NULL) return XST_FAILURE;

    Status = XScuGic_CfgInitialize(&IntcInst, IntcConfig, IntcConfig->CpuBaseAddress);
    if (Status != XST_SUCCESS) return Status;

    // 连接中断服务函数
    Xil_ExceptionInit();
    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
            (Xil_ExceptionHandler)XScuGic_InterruptHandler, &IntcInst);
    Xil_ExceptionEnable();

    Status = XScuGic_Connect(&IntcInst, UartIntrId,
            (Xil_InterruptHandler)XUartPs_InterruptHandler, &UartInst);
    if (Status != XST_SUCCESS) return Status;

    XScuGic_Enable(&IntcInst, UartIntrId);
    return XST_SUCCESS;
}

int main() {
    int Status;
    u8 SendBuf[] = "Zynq UART Ready!\r\n";

    Status = UartInit(UART_DEVICE_ID);
    if (Status != XST_SUCCESS) {
        xil_printf("UART init failed!\r\n");
        return -1;
    }

    Status = IntcInit(INTC_DEVICE_ID, UART_IRPT_INTR);
    if (Status != XST_SUCCESS) {
        xil_printf("Intc init failed!\r\n");
        return -1;
    }

    // 设置接收事件回调
    XUartPs_SetHandler(&UartInst, UartRecvHandler, &UartInst);
    XUartPs_SetRecvThreshold(&UartInst, 1);   // 收到1字节即触发中断
    XUartPs_SetInterruptMask(&UartInst, XUARTPS_IXR_RXOVR | XUARTPS_IXR_RXEMPTY);

    // 发送就绪提示
    XUartPs_Send(&UartInst, SendBuf, sizeof(SendBuf) - 1);

    // 主循环空转,等待中断
    while (1) {
        // 可在此添加低功耗或任务代码
    }

    return 0;
}

运行与验证

  1. 在Vivado中创建硬件工程,使能UART1(MIO 48/49),导出硬件并启动SDK。
  2. 在SDK中新建Application Project,将上述代码替换helloworld.c,BSP需勾选xil_printf和uartps驱动。
  3. 连接USB-UART线缆,PC端打开串口终端(115200,8N1)。
  4. 下载程序到Zynq并运行(Program FPGA → Run)。
  5. 通过标准:串口终端显示Zynq UART Ready!;在终端输入任意字符(如ABC123),终端应原样显示相同字符,即回环成功,证明UART发送与中断接收均正常。

常见问题

  • 无输出:检查MIO引脚配置是否为UART1,以及串口终端波特率是否匹配。
  • 乱码:确认XUartPs_SetDataFormat设置为8N1,且终端无硬件流控。
  • 中断不触发:确认XScuGic_Connect的中断ID与Vivado中UART1中断号一致(通常为59),并检查BSP中xparameters.h的XPAR_XUARTPS_0_INTR值。