版本:Vivado2020.2(Vitis)
任务:UART串口中断实验,实现串口中断数据回环(接收数据并发送出去)
目录
一、介绍
ZYNQ 的 UART(Universal Asynchronous Receiver/Transmitter,通用异步收发器) 是一种串行通信接口,用于在 ZYNQ 的 PS 端和外部设备之间进行异步数据传输。
UART 的主要特性
支持标准 UART 协议(8-N-1、7-bit 数据位、奇偶校验等)。
可编程波特率(最高可达 1 Mbps,取决于时钟配置)。
双缓冲(FIFO)支持(减少 CPU 中断负载)。
中断或轮询模式(可配置 DMA 传输)。
硬件流控(可选)(RTS/CTS 信号)。
集成在 PS(ARM)端,通常通过 MIO 或 EMIO 连接至外部引脚。
注意:使用UART串口有中断和轮询两种方式,之前的例程只是串口打印发送,真正使用UART 进行接收和发送时需要进行初始化,并且通过中断和轮询的方式使用串口(一般选择中断)。
二、硬件设计
(1)硬件设计同 ZYNQ笔记(一):hello world 一致,直接沿用。
(2)最后整体 bd 设计部分如图所示:设计检查、Generate Output Products、 Create HDL Wrapper、管脚约束(无PL部分,跳过)、Gnerate Bitstream(无PL部分,跳过)、Export Hardware(无PL部分,不包含比特流文件)、启动Vitis
三、软件设计
(1)这里提一下 UART 有四种工作模式:
工作模式 | 数据流向 | 主要用途 | 特点 |
---|---|---|---|
Normal Mode 正常模式 |
TxD → 外部设备 RxD ← 外部设备 |
正常通信模式, 与外部设备双向传输数据 |
标准 UART 操作,依赖 FIFO 缓冲和流控 |
Automatic Echo Mode 自动回环模式 |
RxD 接收数据,同时自动回环到 TxD 发送 | 测试 UART 自身收发功能 (无需外部设备) |
接收端数据直接回传,用于验证硬件是否正常 |
Local Loopback Mode 本地回环模式 |
TxD 数据内部回环到 RxD(不经过物理引脚) | 测试芯片内部 UART 控制器和软件逻辑(隔离外部信号干扰) | 避免外部线路影响,排查软件或驱动问题 |
Remote Loopback Mode 远程回环模式 |
RxD 接收数据直接回环到 TxD 发送 | 测试完整通信链路 (包括物理线路和外部设备) |
验证线路完整性,检测信号衰减或干扰 |
(2)设置UART中断触发类型,通过 XUartPs_SetInterruptMask 函数实现,掩码定义在 xuartps_hw.h 头文件,如图所示,本例采用 RX 接收端 FIFO(达到阈值)触发。
/** @name Interrupt Registers
*
* Interrupt control logic uses the interrupt enable register (IER) and the
* interrupt disable register (IDR) to set the value of the bits in the
* interrupt mask register (IMR). The IMR determines whether to pass an
* interrupt to the interrupt status register (ISR).
* Writing a 1 to IER Enbables an interrupt, writing a 1 to IDR disables an
* interrupt. IMR and ISR are read only, and IER and IDR are write only.
* Reading either IER or IDR returns 0x00.
*
* All four registers have the same bit definitions.
*
* @{
*/
#define XUARTPS_IXR_RBRK 0x00002000U /**< Rx FIFO break detect interrupt */
#define XUARTPS_IXR_TOVR 0x00001000U /**< Tx FIFO Overflow interrupt */
#define XUARTPS_IXR_TNFUL 0x00000800U /**< Tx FIFO Nearly Full interrupt */
#define XUARTPS_IXR_TTRIG 0x00000400U /**< Tx Trig interrupt */
#define XUARTPS_IXR_DMS 0x00000200U /**< Modem status change interrupt */
#define XUARTPS_IXR_TOUT 0x00000100U /**< Timeout error interrupt */
#define XUARTPS_IXR_PARITY 0x00000080U /**< Parity error interrupt */
#define XUARTPS_IXR_FRAMING 0x00000040U /**< Framing error interrupt */
#define XUARTPS_IXR_OVER 0x00000020U /**< Overrun error interrupt */
#define XUARTPS_IXR_TXFULL 0x00000010U /**< TX FIFO full interrupt. */
#define XUARTPS_IXR_TXEMPTY 0x00000008U /**< TX FIFO empty interrupt. */
#define XUARTPS_IXR_RXFULL 0x00000004U /**< RX FIFO full interrupt. */
#define XUARTPS_IXR_RXEMPTY 0x00000002U /**< RX FIFO empty interrupt. */
#define XUARTPS_IXR_RXOVR 0x00000001U /**< RX FIFO trigger interrupt. */
#define XUARTPS_IXR_MASK 0x00003FFFU /**< Valid bit mask */
(3)UART中断控制原理:可以简单理解为"开关+触发器"的组合,通过掩码(IMR)和中断状态(ISR)两个寄存器协同工作:一种中断触发类型各对应一个掩码位和状态位,当任一触发类型的掩码位和状态位都有效时,UART产生中断请求信号。
因为UART支持多种中断触发方式,所以中断处理函数部分需要对中断类型进行判断,可以根据不同的触发方式分情况进行处理。
中断函数具体操作方式:1.读取状态寄存器、掩码寄存器(通过(2)设置),并进行相与(相与之后的结果就是表示当前串口的中断状态)、2. 将相与结果再与上需要判断的中断触发类型掩码,进行 if 判断(相于后只有触发类型一致时,相应位才得1,其余位为0,通过 if 判断)。
(4)完整设计代码:
#include "xparameters.h"
#include "xil_printf.h"
#include "xuartps.h"
#include "xscugic.h"
//==========================自定义宏==========================//
#define UART_DEVICE_ID XPAR_XUARTPS_0_DEVICE_ID //宏定义UART器件ID
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID //宏定义中断控制器(GIC)ID
#define UART_INTR_ID XPAR_XUARTPS_1_INTR //宏定义UART中断号(中断ID)
//===========================实例化===========================//
XUartPs Uart; //UART驱动实例
XScuGic Intc; //中断控制器驱动实例
//========================函数变量声明========================//
static int Uart_Intr_Init(); //UART中断初始化
static void IntrHandler(void *CallBackRef); //中断处理函数
static void Setup_Intr_System(XScuGic *intr, XUartPs *uart,u16 uart_intr_id); //建立中断系统
//===========================主函数===========================//
int main()
{
//串口中断初始化
Uart_Intr_Init();
//建立中断系统
Setup_Intr_System(&Intc, &Uart, UART_INTR_ID);
//打印Debug信息
xil_printf("UART Interrupt Test\r\n");
while(1)
return 0;
}
//========================中断处理函数========================//
/* @param CallBackRef 用户自定义回调参数(对应UART实例指针)
*/
void IntrHandler(void *CallBackRef)
{
u8 rec_data;
u32 IntrStatus;
//将回调参数转为UART实例指针,用于操作硬件(例规范化设计)
XUartPs *UartInstPtr = (XUartPs *) CallBackRef;
//读取中断ID寄存器,获取中断触发类型
IntrStatus = XUartPs_ReadReg(UartInstPtr->Config.BaseAddress,
XUARTPS_IMR_OFFSET);//读取掩码
IntrStatus &= XUartPs_ReadReg(UartInstPtr->Config.BaseAddress,
XUARTPS_ISR_OFFSET);//读取状态
//判断中断类型并执行中断处理(与上对应中断类型掩码)
//本例只设置了一种中断类型,有多种时通过if判断分情况处理
if (IntrStatus & (u32)XUARTPS_IXR_RXOVR)
{
//接收发送的字节
rec_data = XUartPs_RecvByte(XPAR_XUARTPS_0_BASEADDR);
//发送数据
XUartPs_SendByte(XPAR_XUARTPS_0_BASEADDR,rec_data);
//清除中断状态
XUartPs_WriteReg(UartInstPtr->Config.BaseAddress,
XUARTPS_ISR_OFFSET, XUARTPS_IXR_RXOVR);
}
}
//======================UART中断初始化=======================//
int Uart_Intr_Init()
{
//定义UART控制器配置信息(指针)
XUartPs_Config *UartConfig;
//根据UART ID,查找配置信息
UartConfig = XUartPs_LookupConfig(UART_DEVICE_ID);
//初始化UART控制器驱动
XUartPs_CfgInitialize(&Uart, UartConfig, UartConfig->BaseAddress);
//设置工作模式:正常模式
XUartPs_SetOperMode(&Uart, XUARTPS_OPER_MODE_NORMAL);
//设置波特率:115200
XUartPs_SetBaudRate(&Uart,115200);
//(可选)串口自检:基本设置完成后,调用该函数进行串口自检并返回状态
int Status = XUartPs_SelfTest(&Uart);
if (Status != XST_SUCCESS) {
xil_printf("UART SelfTest Failed\r\n");
return XST_FAILURE;
}
return XST_SUCCESS;
}
//=======================建立中断系统=======================//
/* 建立中断系统,UART接收到数据时产生中断
* @param intr 是指向 XScuGic驱动实例的指针
* @param uart 是指向 XUartPs驱动实例的指针
* @param uart_intr_id 是 UART控制器ID
*/
void Setup_Intr_System(XScuGic *intr, XUartPs *uart, u16 uart_intr_id)
{
//定义中断控制器配置信息(指针)
XScuGic_Config * IntcConfig;
//根据中断控制器ID,查找GIC配置信息
IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
//初始化中断控制器驱动
XScuGic_CfgInitialize(intr, IntcConfig, IntcConfig->CpuBaseAddress);
//设置中断异常处理功能
Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler) XScuGic_InterruptHandler,
(void *) intr);
//使能处理器中断
Xil_ExceptionEnable();
//关联中断处理函数
XScuGic_Connect(intr, uart_intr_id,
(Xil_ExceptionHandler) IntrHandler,
(void *) uart);
//设置FIFO阈值:1字节,即接收多少字节数据触发中断
XUartPs_SetFifoThreshold(uart, 1);
//设置UART中断触发类型:接收端FIFO触发中断(添加触发类型直接或“|”,各触发类型掩码位是独立的)
XUartPs_SetInterruptMask(uart, XUARTPS_IXR_RXOVR);
//使能UART中断
XScuGic_Enable(intr, uart_intr_id);
}
四、效果
上板后串口打印Debug信息,随后每向板卡发送数据,板卡会将数据接收并发送回来。