ZynqMP Vitis2021.1 PS UART接收中断及IDLE(超时)中断

2024-02-10 14:32

本文主要是介绍ZynqMP Vitis2021.1 PS UART接收中断及IDLE(超时)中断,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言:

硬件环境:Xilinx ZynqMP XCZU4EV-SFVC784-1-I

软件环境:Ubuntu20.04LTS + Vivado2021.1 + Vitis2021.1

文章目标:1、UART在正常模式下中断接收的回显例子。

                  2、Xilinx自带UART库代码有点累赘,在例1基础上优化。

                  3、在例2的基础上,添加UART在正常模式下IDLE中断的(接收超时中断)例子。

参考程序:/tools/Xilinx/Vitis/2021.1/data/embeddedsw/XilinxProcessorIPLib/drivers/uartps_v3_11

ZynqMP串口关键功能简介:

1、PS有两个UART外设,分别是UART0和UART1。

2、UART的TX 和 RX都有一个64Byte的FIFO,读写数据都需先经过FIFO。

3、UART支持4种模式。分别是正常模式, 回显模式,本地回环模式, 外部回环模式。 

更多详细信息请看《UG1085 Zynq UltraScale+ Device Technical Reference Manual.pdf》

准备工作:

1、打开Vivado2021.1,创建一个工程,勾选UART0外设,生成导出XSA文件。

2、打开Vitis2021.1,创建一个Platform Project。

 

例1:UART中断接收回显

1、创建一个空白的Application Project,添加main函数文件。

2、在main函数文件中添加如下代码。为了简化代码,故非关键函数不进行任何返回值判断。

#include "stdio.h"
#include "xparameters.h"
#include "xuartps.h"
#include "xscugic.h"#define UART_DEVICE_ID		XPAR_XUARTPS_0_DEVICE_ID
#define INTC_DEVICE_ID		XPAR_SCUGIC_SINGLE_DEVICE_ID
#define UART_INT_IRQ_ID		XPAR_XUARTPS_0_INTRvoid UserHandler(void *CallBackRef, u32 Event, unsigned int EventData);
void Handlers(void *CallBackRef);
int UartPsSend(XUartPs *InstancePtr, u8 *BufferPtr, u32 NumBytes);
int UartPsRev(XUartPs *InstancePtr, u8 *BufferPtr, u32 NumBytes);XUartPs UartPs;			/* Instance of the UART Device */
XScuGic ScuGic;			/* Instance of the Interrupt Controller */int main(void)
{XUartPs_Config *UartPs_Config;XScuGic_Config *ScuGic_Config;/* 1、初始化UART,设置波特率,正常模式 */UartPs_Config = XUartPs_LookupConfig(UART_DEVICE_ID);XUartPs_CfgInitialize(&UartPs, UartPs_Config,UartPs_Config->BaseAddress);XUartPs_SetBaudRate(&UartPs, 115200);XUartPs_SetOperMode(&UartPs, XUARTPS_OPER_MODE_NORMAL);/* 2、初始化中断控制器,中断函数连接到系统,使能中断 */ScuGic_Config = XScuGic_LookupConfig(INTC_DEVICE_ID);XScuGic_CfgInitialize(&ScuGic, ScuGic_Config,ScuGic_Config->CpuBaseAddress);Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler) XScuGic_InterruptHandler, &ScuGic);Xil_ExceptionEnable();/* 3、设置FIFO接收到1个数据触发XUARTPS_IXR_RXOVR标志中断,* 设置UART外设中断处理函数,设置UART用户回调函数,使能UART中断 */XUartPs_SetFifoThreshold(&UartPs, 1);XUartPs_SetInterruptMask(&UartPs, XUARTPS_IXR_RXOVR);XScuGic_Connect(&ScuGic, UART_INT_IRQ_ID, (Xil_ExceptionHandler)XUartPs_InterruptHandler, &UartPs);XUartPs_SetHandler(&UartPs, (XUartPs_Handler)UserHandler, &UartPs);XScuGic_Enable(&ScuGic, UART_INT_IRQ_ID);while(1);return 0;
}void UserHandler(void *CallBackRef, u32 Event, unsigned int EventData)
{u8 ch = 0;XUartPs *UartPsPtr = (XUartPs *) CallBackRef ;/* UART中断标志XUARTPS_IXR_RXOVR对* 应事件XUARTPS_EVENT_RECV_DATA */if (Event == XUARTPS_EVENT_RECV_DATA){XUartPs_Recv(UartPsPtr, &ch, 1);XUartPs_Send(UartPsPtr, &ch, 1);}
}

 注意:

  • 代码设置了接收1个字节就触发中断,故在用户回调函数UserHandler()中,一旦接收到了数    据,必须把数据从缓存区取出。否则后面再来数据中断会不触发。
  • 在用户回调函数UserHandler()中,使用入参变量EventData前需用XUartPs_Recv()初始化。
  • UART中断标志对应事件的关系,需要通过看XUartPs_InterruptHandler()函数源码获知。
  • debug模式下,中断函数不可打断点。

现象:程序下载运行。PC的串口助手发送什么数据,ZynqMP就把数据发回去。 

例2:优化例1代码

优化方向:

  • 官方例程中系统中断处理函数调用的是XUartPs_InterruptHandler()函数,再由该函数以事件的方式通知用户回调函数而且事件类型和中断标志还需看函数源码才能获取。故舍弃这个函数,但参考这个函数我们自己写一个UART中断处理函数。
  •  Xilinx UART库的XUartPs_Recv()和XUartPs_Send()源码非常累赘流程繁琐,故我们参考这两个函数,自己写收发函数。

优化后的代码如下:

#include "stdio.h"
#include "xparameters.h"
#include "xuartps.h"
#include "xscugic.h"#define UART_DEVICE_ID		XPAR_XUARTPS_0_DEVICE_ID
#define INTC_DEVICE_ID		XPAR_SCUGIC_SINGLE_DEVICE_ID
#define UART_INT_IRQ_ID		XPAR_XUARTPS_0_INTRvoid UserIRQHandler(void *CallBackRef);
int MyXUartPs_Send(XUartPs *InstancePtr, u8 *BufferPtr, u32 NumBytes);
int MyXUartPs_Recv(XUartPs *InstancePtr, u8 *BufferPtr, u32 NumBytes);XUartPs UartPs;			/* Instance of the UART Device */
XScuGic ScuGic;			/* Instance of the Interrupt Controller */int main(void)
{XUartPs_Config *UartPs_Config;XScuGic_Config *ScuGic_Config;/* 1、初始化UART,设置波特率,正常模式 */UartPs_Config = XUartPs_LookupConfig(UART_DEVICE_ID);XUartPs_CfgInitialize(&UartPs, UartPs_Config,UartPs_Config->BaseAddress);XUartPs_SetBaudRate(&UartPs, 115200);XUartPs_SetOperMode(&UartPs, XUARTPS_OPER_MODE_NORMAL);/* 2、初始化中断控制器,中断函数连接到系统,使能中断 */ScuGic_Config = XScuGic_LookupConfig(INTC_DEVICE_ID);XScuGic_CfgInitialize(&ScuGic, ScuGic_Config,ScuGic_Config->CpuBaseAddress);Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler) XScuGic_InterruptHandler, &ScuGic);Xil_ExceptionEnable();/* 3、设置FIFO接收到1个数据触发XUARTPS_IXR_RXOVR标志中断,* 设置UART外设中断处理函数,设置UART用户回调函数,使能UART中断 */XUartPs_SetFifoThreshold(&UartPs, 1);XUartPs_SetInterruptMask(&UartPs, XUARTPS_IXR_RXOVR);XScuGic_Connect(&ScuGic, UART_INT_IRQ_ID, (Xil_ExceptionHandler)UserIRQHandler, &UartPs);XScuGic_Enable(&ScuGic, UART_INT_IRQ_ID);while(1);return 0;
}void UserIRQHandler(void *CallBackRef)
{u8 ch = 0;u32 IsrStatus;XUartPs *UartPsPtr = (XUartPs *) CallBackRef ;// 读取中断状态和使能位IsrStatus = XUartPs_ReadReg(UartPsPtr->Config.BaseAddress,XUARTPS_IMR_OFFSET);IsrStatus &= XUartPs_ReadReg(UartPsPtr->Config.BaseAddress,XUARTPS_ISR_OFFSET);// RX FIFO 阈值触发中断if(IsrStatus & XUARTPS_IXR_RXOVR != 0){MyXUartPs_Recv(UartPsPtr, &ch, 1);MyXUartPs_Send(UartPsPtr, &ch, 1);}// 清除中断状态标志位XUartPs_WriteReg(UartPsPtr->Config.BaseAddress, XUARTPS_ISR_OFFSET,IsrStatus);
}int MyXUartPs_Send(XUartPs *InstancePtr, u8 *BufferPtr, u32 NumBytes)
{u32 SentCount = 0U;while (NumBytes > SentCount){// TX FIFO 未满,继续填充数据if (!XUartPs_IsTransmitFull(InstancePtr->Config.BaseAddress)){XUartPs_WriteReg(InstancePtr->Config.BaseAddress,XUARTPS_FIFO_OFFSET,((u32)BufferPtr[SentCount]));SentCount++;}}return SentCount;
}int MyXUartPs_Recv(XUartPs *InstancePtr, u8 *BufferPtr, u32 NumBytes)
{u32 ReceivedCount = 0;u32 CsrRegister;// 读取RX FIFO状态CsrRegister = XUartPs_ReadReg(InstancePtr->Config.BaseAddress,XUARTPS_SR_OFFSET);// 循环条件:读取RX FIFO状态为非空 且 接收长度未达到。while((ReceivedCount < NumBytes) &&	(((CsrRegister & XUARTPS_SR_RXEMPTY) == (u32)0))){BufferPtr[ReceivedCount] = XUartPs_ReadReg(InstancePtr->Config.BaseAddress,XUARTPS_FIFO_OFFSET);ReceivedCount++;CsrRegister = XUartPs_ReadReg(InstancePtr->Config.BaseAddress,XUARTPS_SR_OFFSET);}return ReceivedCount;
}

注意:

  • 无论是我写的MyXUartPs_Recv和MyXUartPs_Send还是官方库的XUartPs_Recv()和XUartPs_Send()函数都没有超时退出机制,在中断使用的时候注意一下或者自己再修改一下加上超时退出。

 现象:程序下载运行。PC的串口助手发送什么数据,ZynqMP就把数据发回去。 

例3:IDLE中断(接收超时中断

       一般场景下UART协议都是不定长协议,故都是在中断1个字节1个字节地收到数据,中断在接收完1个字节后还要进行协议解析,非常浪费CPU资源且中断占时长。

        IDLE空闲中断的加入就刚刚好解决了这个痛点,毕竟一帧协议数据传输都是传输到最后一个字节完成才开始解析才有意义,在协议传输中的任意时刻解析都是无意义的,且一帧协议传输完成后往往会留有足够的时间去让CPU解析处理。

        IDLE中断其实应该叫接收超时中断。一帧协议的数据肯定是连续发送的,一个数据接收完成后在等待下一个数据来临时,假如下个数据在规定时间内没传出过来,那么就代表一帧数据传输完了,可以进行处理数据了。

        而Xilinx ZynqMP就有这个接收超时中断标志位XUARTPS_IXR_TOUT,故利用该标志位即可做到IDLE空闲中断的效果。

代码如下所示:

#include "stdio.h"
#include "xparameters.h"
#include "xuartps.h"
#include "xscugic.h"#define UART_DEVICE_ID		XPAR_XUARTPS_0_DEVICE_ID
#define INTC_DEVICE_ID		XPAR_SCUGIC_SINGLE_DEVICE_ID
#define UART_INT_IRQ_ID		XPAR_XUARTPS_0_INTR
#define	CH_BUF_SIZE			256void UserIRQHandler(void *CallBackRef);
int MyXUartPs_Send(XUartPs *InstancePtr, u8 *BufferPtr, u32 NumBytes);
int MyXUartPs_Recv(XUartPs *InstancePtr, u8 *BufferPtr, u32 NumBytes);XUartPs UartPs;			/* Instance of the UART Device */
XScuGic ScuGic;			/* Instance of the Interrupt Controller */
u32 buf_index = 0;
u8 ch_buf[CH_BUF_SIZE] = {0};int main(void)
{XUartPs_Config *UartPs_Config;XScuGic_Config *ScuGic_Config;/* 1、初始化UART,设置波特率,正常模式 */UartPs_Config = XUartPs_LookupConfig(UART_DEVICE_ID);XUartPs_CfgInitialize(&UartPs, UartPs_Config,UartPs_Config->BaseAddress);XUartPs_SetBaudRate(&UartPs, 115200);XUartPs_SetOperMode(&UartPs, XUARTPS_OPER_MODE_NORMAL);/* 2、初始化中断控制器,中断函数连接到系统,使能中断 */ScuGic_Config = XScuGic_LookupConfig(INTC_DEVICE_ID);XScuGic_CfgInitialize(&ScuGic, ScuGic_Config,ScuGic_Config->CpuBaseAddress);Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler) XScuGic_InterruptHandler, &ScuGic);Xil_ExceptionEnable();/* 3、设置超时接收值,设置FIFO接收到1个数据触发标志中断及超时中断,* 设置UART外设中断处理函数,设置UART用户回调函数,使能UART中断 */XUartPs_SetRecvTimeout(&UartPs, 8);XUartPs_SetFifoThreshold(&UartPs, 1);XUartPs_SetInterruptMask(&UartPs, XUARTPS_IXR_RXOVR | XUARTPS_IXR_TOUT);XScuGic_Connect(&ScuGic, UART_INT_IRQ_ID, (Xil_ExceptionHandler)UserIRQHandler, &UartPs);XScuGic_Enable(&ScuGic, UART_INT_IRQ_ID);while(1);return 0;
}void UserIRQHandler(void *CallBackRef)
{u32 IsrStatus;XUartPs *UartPsPtr = (XUartPs *) CallBackRef ;// 读取中断状态和使能位IsrStatus = XUartPs_ReadReg(UartPsPtr->Config.BaseAddress,XUARTPS_IMR_OFFSET);IsrStatus &= XUartPs_ReadReg(UartPsPtr->Config.BaseAddress,XUARTPS_ISR_OFFSET);// RX FIFO 阈值触发中断if((IsrStatus & XUARTPS_IXR_RXOVR) != 0){MyXUartPs_Recv(UartPsPtr, &ch_buf[buf_index], 1);buf_index++;}// RX 超时中断if((IsrStatus & XUARTPS_IXR_TOUT) != 0){MyXUartPs_Send(UartPsPtr, ch_buf, buf_index);buf_index = 0;}// 清除中断状态标志位XUartPs_WriteReg(UartPsPtr->Config.BaseAddress, XUARTPS_ISR_OFFSET,IsrStatus);
}int MyXUartPs_Send(XUartPs *InstancePtr, u8 *BufferPtr, u32 NumBytes)
{u32 SentCount = 0U;while (NumBytes > SentCount){// TX FIFO 未满,继续填充数据if (!XUartPs_IsTransmitFull(InstancePtr->Config.BaseAddress)){XUartPs_WriteReg(InstancePtr->Config.BaseAddress,XUARTPS_FIFO_OFFSET,((u32)BufferPtr[SentCount]));SentCount++;}}return SentCount;
}int MyXUartPs_Recv(XUartPs *InstancePtr, u8 *BufferPtr, u32 NumBytes)
{u32 ReceivedCount = 0;u32 CsrRegister;// 读取RX FIFO状态CsrRegister = XUartPs_ReadReg(InstancePtr->Config.BaseAddress,XUARTPS_SR_OFFSET);// 循环条件:读取RX FIFO状态为非空 且 接收长度未达到。while((ReceivedCount < NumBytes) &&	(((CsrRegister & XUARTPS_SR_RXEMPTY) == (u32)0))){BufferPtr[ReceivedCount] = XUartPs_ReadReg(InstancePtr->Config.BaseAddress,XUARTPS_FIFO_OFFSET);ReceivedCount++;CsrRegister = XUartPs_ReadReg(InstancePtr->Config.BaseAddress,XUARTPS_SR_OFFSET);}return ReceivedCount;
}

        代码XUARTPS_IXR_RXOVR 中断先是1个字节1个字节接收数据放到缓存区,然后等待 XUARTPS_IXR_TOUT超时中断再处理打印出来。        

        使用接收超时中断标志位XUARTPS_IXR_TOUTX前需要设置超时的比特时钟(波特率时钟),而设置比特时钟则是用函数XUartPs_SetRecvTimeout(XUartPs *InstancePtr, u8 RecvTimeout),超时比特时钟 = RecvTimeout * 4。

        文中我设置RecvTimeout = 8,那么超时比特时钟 = 32。而一个字符按照默认帧格式是:8个数据bit,1个停止bit,起始1bit,默认无奇偶,无流控,即10bit。故超时比特时钟为32的话大概3个字节多2个比特时钟。

        9600bps:那么每1bit的时间就是1/9600秒=104.16。

        115200bps: 那么每1bit的时间就是1/115200秒=8.68us。

        我现在使用的串口波特率是115200bps,故32个bit时钟等于 8.68us * 32 = 277.76us超时。

 现象:程序下载运行。PC的串口助手发送什么数据,ZynqMP就把数据发回去。 

        若还想提升效率可以把XUARTPS_IXR_RXOVR中断阈值设置成(最大)64字节,然后一帧数据大于64字节则由XUARTPS_IXR_RXOVR中断放置缓存区,小于则由XUARTPS_IXR_TOUTX中断先把FIFO缓存区数据获取,再处理。

全篇完。

本人是一个嵌入式未入门小白,博客仅仅代表我个人主观见解,记录成长笔记。
若有与 大神大大 见解有歧义,我绝对坚信 大神大大 见解是对的,我的是错的。
感谢~!

这篇关于ZynqMP Vitis2021.1 PS UART接收中断及IDLE(超时)中断的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/697359

相关文章

第10章 中断和动态时钟显示

第10章 中断和动态时钟显示 从本章开始,按照书籍的划分,第10章开始就进入保护模式(Protected Mode)部分了,感觉从这里开始难度突然就增加了。 书中介绍了为什么有中断(Interrupt)的设计,中断的几种方式:外部硬件中断、内部中断和软中断。通过中断做了一个会走的时钟和屏幕上输入字符的程序。 我自己理解中断的一些作用: 为了更好的利用处理器的性能。协同快速和慢速设备一起工作

Android中如何实现adb向应用发送特定指令并接收返回

1 ADB发送命令给应用 1.1 发送自定义广播给系统或应用 adb shell am broadcast 是 Android Debug Bridge (ADB) 中用于向 Android 系统发送广播的命令。通过这个命令,开发者可以发送自定义广播给系统或应用,触发应用中的广播接收器(BroadcastReceiver)。广播机制是 Android 的一种组件通信方式,应用可以监听广播来执行

FreeRTOS学习笔记(四)Freertos的中断管理及临界保护

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、Cortex-M 中断管理1.1 中断优先级分组1.2 相关寄存器1.3 相关宏定义1.4 FreeRTOS 开关中断 二、临界段及其保护2.1 taskENTER_CRITICAL( ) 和 taskEXIT_CRITICAL( )2.2 taskENTER_CRITICAL_FROM_ISR( )

【H2O2|全栈】关于Photoshop | PS(4)

PS的一些杂谈(亖) 目录 PS的一些杂谈(亖) 前言 准备工作 图形工具 基本属性 混合选项 形状图层  文字工具 基本属性 进一步变化文字 组和图层 UI设计案例  预告和回顾 后话 前言 这一篇博客我将会写一下图形工具和文字工具有关的内容,涉及到锚点调节路径、自由变换和混合选项等内容。在本篇博客看完之后,是可以设置一些简单的LOGO和UI图标的。

Cortex-A7:ARM官方推荐的嵌套中断实现机制

0 参考资料 ARM Cortex-A(armV7)编程手册V4.0.pdf ARM体系结构与编程第2版 1 前言 Cortex-M系列内核MCU中断硬件原生支持嵌套中断,开发者不需要为了实现嵌套中断而进行额外的工作。但在Cortex-A7中,硬件原生是不支持嵌套中断的,这从Cortex-A7中断向量表中仅为外部中断设置了一个中断向量可以看出。本文介绍ARM官方推荐使用的嵌套中断实现机

外部中断的边缘触发和电平触发

MCS-51单片机中的边缘触发是指当输入引脚电平由高到低发生跳变时,才引起中断。而电平触发是指只要外部引脚为低电平就引起中断。         在电平触发方式下,当外部引脚的低电平在中断服务返回前没有被拉高时(即撤除中断请求状态),会引起反复的不需要的中断,造成程序执行的错误。这类中断方式下,需要在中断服务程序中设置指令,清除外部中断的低电平状态,使之变为高电平。

使用Vant Uploader 文件上传,后端java中MultipartFile接收不到文件问题解决

问题 在Uploader组件 after-read回调函数将获取的file对象上传到服务器。 <van-uploader:after-read="uploadFile"/>uploadFile(file) {const data = new FormData();data.

PS插件DR5至臻高级版下载安装教程Photoshop美颜美白牙齿磨皮使用插件百度网盘分享

DR5至臻高级版PS插件DR5至臻高级版下载安装教程Photoshop美颜美白牙齿磨皮使用插件百度网盘分享链接地址可以对眼睛、眉毛、嘴唇等五官进行精细调整,如增强眼睛的明亮度和清晰度,使眉毛更加立体,嘴唇更加饱满可以快速有效地去除人像照片中的瑕疵、痘痘、皱纹等,同时保持皮肤的纹理和细节,使皮肤看起来光滑细腻 1、下载软件 链接:https://pan.baidu.com/s/1bKjukx

【Java编程思想】线程的基本协作机制 与 线程的中断

wait/notify Java在Object类中定义了一些线程协作的基本方法,wait和notify public final void wait() throws InterruptedException;public final native void wait(long timeout) throws InterruptedException; 一个带时间参数,单位是毫秒,表示最

基于IMX6ULL的Cortex-A中断原理讲解,以及编写其中断向量表

首先借助STM32我们需要了解中断系统是如何构成的         会有一个中断源,也就是能够向CPU发出中断请求的设备或事件。中断源不分硬件和软件,也就是产生中断信号,就会执行中断服务函数         但是CPU是如何知道中断源产生后就找到对应的中断服务函数呢,这个时候就要引入中断向量表,它的主要功能是描述中断对应的中断服务函数,每个中断源都有一个唯一的中断号(也称向量号),