前言: 在前两篇文中,通过HelloWorld例程熟悉Zynq的开发过程;通过读写MIO学习使用外设进一步熟悉开发流程,本文承接上文,介绍Zynq开发中的重要机制——中断,以MIO的中断为例,介绍中断原理,工程搭建步骤,以及程序源码解读。读者通过本文认识重要的交互机制中断,为后续为特定项目场景方案定制提供思路,为深入理解Zynq内部架构提供理论支撑。
1.什么是中断?
中断是IO操作的一种方式,也是CPU与外设的一种通信机制,除此之外还有查询、DMA等方式。三者相比各有优缺点,在查询模式下,CPU主动、定时地检查外设的状态,询问外设是否需要进行数据交换。这种方式效率较低,因为CPU在不断地轮询外设时可能浪费大量时间,无法进行其他任务,例如在QT中使用定时器,在定时器到时之后处理某件时间,这个就是查询操作。DMA即Direct Memory Access直接存储访问,是一种允许外设直接访问系统内存,绕过CPU进行数据传输的技术。通过DMA,外设可以自主地将数据从内存中读取或写入内存,而不需要CPU的持续参与。使用DMA可以有效的降低CPU负载,适合在批量数据传输的场景中。Xilinx的PCIE解决方案XDMA IP核就是采用的DMA方式。
而中断(Interrupt)是指在计算机系统中,当外设或其他事件需要CPU的处理时,主动向CPU发出信号,打断当前正在执行的程序,让CPU暂停当前的任务,转而执行一个专门的程序(称为中断服务程序,ISR, Interrupt Service Routine)来处理该事件。处理完中断事件后,CPU会恢复之前的状态,继续执行被中断的任务。例如,在使用电脑时对人们对鼠标的点击,对于CPU来说就是一个异步操作,USB3.0有四种传输模式,鼠标使用的就是USB的中断传输模式。中断可以高效快速的实时响应一些紧急任务例如系统故障报警,温度报警等,或者处理异步事件如按键按下,使用这种方式非常高效,但是当中断越多的时候,相应的中断处理机制就更复杂。
以本文对应例程为例,简要介绍中断相关概念。本文使用中断实现一个简单的功能,PS端一个按键按下的时候点亮PL端一个LED灯。按键按下是本例的异步事件,涉及中断源的概念;点亮LED即为中断来的时候处理的结果,涉及中断服务函数。
在Zynq系统中,可以产生中断的源有很多,PS端各种外设,PL端都可以产生中断,为了能够区分,在CPU与外设之间设置了一个通用中断控制器(General interrupt controller),它用于集中管理从 PS 和 PL 发送到 CPU 的中断,实现中断禁用、屏蔽、优先级分配等功能。当GIC发送给CPU并且CPU处理完该中断后,CPU会告诉GIC中断处理完毕,GIC收到后将该中断取消。
在CPU接收到中断之后,会停下来转而执行中断服务函数去做相应的处理,CPU跳转到中断服务程序是由芯片本身的硬件功能来实现的,而不是由程序员手动操作的。当特定的中断事件发生时,CPU会自动检测到该中断并跳转到预先定义好的中断服务程序的入口地址开始执行。 这个过程是由CPU内部的中断控制器和中断向量表等硬件模块来完成的,程序员只需要在编写程序时设置好中断服务程序的入口地址和相应的中断处理函数即可。zynq是通过将一类外设共享一个中断号的方式,提前写好CPU在收到该类中断时转而执行程序的内存地址,根据这个地址去找相应的处理函数,而用户需要在中断处理函数中区分是具体哪个外设。GPIO(PSMIO/EMIO)就是共享(IRQ ID#58)中断号,在Zynq的APU中将中断分成了三种类型,分别是16 个软件产生的中断(SGI),7 个私有外设中断(PPI),92 个共享外设中断(SPI)。如下图所示除了APU中断控制器还有RPU中断控制器。
2.GPIO中断
关于GPIO中断的使用,主要还是图中涉及中断的几个寄存器的配置。INT_TYPE控制中断类型是边沿敏感还是电平敏感。INT_POLARITY寄存器控制中断时低有效还是高有效(边沿或电平)。INT_ANY设置是否双边沿中断。通过上述三个寄存器设置触发方式。INT_STAT显示当前中断的位,将1写入指定位可以清除对应中断状态。INT_DIS\INT_EN\INT_MASK是和中断屏蔽的寄存器。
3.程序分析
Vivado工程中对zynq的配置就是勾选串口,勾选emio,勾选mio,然后添加PL端LED约束生成bit流导出xsa文件即可。
#include "xparameters.h"
#include "xgpiops.h"
#include "xscugic.h"
#include "xil_exception.h"
#include "xplatform_info.h"
#include <xil_printf.h>
#include "sleep.h"
#define GPIO_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID // GPIO 器件 ID
#define INTC_DEV