ZYNQ——GPIO之中断控制

开发环境        vivado 19.2     vitis

开发板            zynq7010

内容                按键按下,控制LED状态进行亮灭转换。

中断简介:中断是一种当满足要求的突发事件发生时通知处理器进行处理的信号。中断可以由硬件处理单元和外部设备产生,也可以由软件本身产生。对硬件来说,中断信号是一个由某个处理单元产生的异步信号,用 来引起处理器的注意。

中断分类:(主要是硬件中断)可以进一步被分类为以下几种类型:
        1.可屏蔽中断(Maskable Interrupts IRQ) ——可通过在中断屏蔽寄存器中设定位掩码来关闭。触发可 屏蔽中断的事件源不是每次都是重要的。程序设计人员需要决定该事件是否应该导致程序跳到所需处理的 地方去。使用可屏蔽中断的设备包括定时器、比较器和 ADC
        2.不可屏蔽中断(Non-Maskable Interrupts NMI) ——无法通过在中断屏蔽寄存器中设定位掩码来关闭。 这些是不可忽视的中断。NMI 的事件包括上电、外部重启(用实际的按钮)和严重的设备失效。
        3.处理器间中断(Inter-Processor Interrupts IPI) ——在多处理器系统中,一个处理器可能需要中断另一 个处理器的操作。在这种情况下,就会产生一个 IPI ,以便于处理器间通信或同步。
相关寄存器:下面我们对中断相关的寄存器作简单介绍,如下图所示:

中断触发可以是正沿、负沿、任一沿、低电平或高电平。使用INT_TYPE、INT_POLARITY和INT_ANY寄存器对触发灵敏度进行编程。

INT_POLARITY该寄存器控制中断是低电平有效还是高电平有效(或下降沿敏感或上升沿敏感)。

INT_TYPE该寄存器控制中断是边沿敏感还是电平敏感。

INT_ANY:只针对边沿触发,如果为1,则上升沿和下降沿都作为中断触发类型。

INT_STAT: 如果写1可以清除中断,如果读该寄存器则获取中断的状态。

INT_MASK: 只读寄存器,读取该寄存器可以获取哪些寄存器中断被打开,哪些被屏蔽如果某位为1,则代表中断被打开。

INT_DIS:向该寄存器的任何位写入 1 都会屏蔽该中断信号。从该寄存器读取会返回不可预测的值。

INT_EN:向该寄存器的任何位写入 1,可以启用/解除中断信号的掩码。从该寄存器读取将返回一个不 可预测的值。

        如上图所示,我们可以看到有一个与门,与门的两个输入分别是INT STATE输出的信号和INT DIS输出的信号。这就意味着当某个引脚检测到中断,并且中断没有被屏蔽,相与的结果输出到GIC(PS的中断控制器),也就是输出一个中断请求信号到中断控制器,GPIO中断ID为52

硬件环境配置

        中断控制我们采用PS端的按键,控制核心板上PS的LED,也就是MIO来控制中断。如果要用PL端的按键来控制也是一样的,在硬件平台配置EMIO接口即可。因此硬件平台的搭建只需要把DDR、时钟、MIO和UART配置好即可,UART用来打印信息,也可以方便我们进行调试。

具体配置过程见CSDN

vitis配置及编程

        我们在硬件平台配置好之后,把生成的比特流一起导出,在vitis里面创建工程,然后开始编程,在这里我们先给出最终的程序,然后再进行一个详细的说明,程序部分如下:

#include "stdio.h"
#include "xparameters.h"
#include "xgpiops.h"
#include "sleep.h"
#include "xscugic.h"
#define MIO0_LED    0
#define MIO12_KEY 11 //KEY 连接到 MIO11
#define GPIO_INTERRUPT_ID	XPAR_XGPIOPS_0_INTR//GPIO中断号是52  //PS端GPIO中断ID
#define INTC_DEVICE_ID		XPAR_SCUGIC_SINGLE_DEVICE_ID//通用中断控制器ID
#define GPIO_DEVICE_ID		XPAR_XGPIOPS_0_DEVICE_ID//PS端GPIO器件ID
XGpioPs_Config *ConfigPtr;//定义ConfigPtr为结构体指针类型 (指针理解为地址)
XGpioPs Gpio;//定义GPIO驱动实例
XScuGic Intc;//中断驱动实例
XScuGic_Config *IntcConfig; /* Instance of the interrupt controller */
void SetupInterruptSystem(XScuGic *GicInstancePtr, XGpioPs *Gpio,u16 GpioIntrId);
//void IntrHandler(void *CallBackRef, u32 Bank, u32 Status);
void IntrHandler();
u32 key_press = 0;
int main(){
	u32 led_value = 0;
	printf("GPIO interrupt TEST\n");
	//根据器件的ID查找器件的配置信息
	ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
	//初始化GPIO的驱动
	XGpioPs_CfgInitialize(&Gpio, ConfigPtr,
					ConfigPtr->BaseAddr);//&是取地址  ->表示结构体的第一个元素
	//把GPIO的方向设置为输出(0输入/1输出)
	XGpioPs_SetDirectionPin(&Gpio, MIO0_LED, 1);//(地址  引脚  方向)
	//XGpioPs_SetDirectionPin(&Gpio, MIO7_LED, 1);//(地址  引脚  方向)
	//XGpioPs_SetDirectionPin(&Gpio, MIO8_LED, 1);//(地址  引脚  方向)
	//设置输出使能(0关闭  1打开)
	XGpioPs_SetOutputEnablePin(&Gpio, MIO0_LED, 1);
	XGpioPs_WritePin(&Gpio, MIO0_LED, 1);


	SetupInterruptSystem(&Intc, &Gpio, GPIO_INTERRUPT_ID);
	//点亮
	while(1){

		if(key_press){
			led_value=~led_value;
			key_press=0;
			//中断处理之后,要清除中断状态,,UG585里面一个图的D触发器(state)
			XGpioPs_IntrClearPin(&Gpio, MIO12_KEY);
		XGpioPs_WritePin(&Gpio, MIO0_LED, led_value);//将led_value的值写入led
		//延时消抖
		usleep(500000);//延时200ms再打开中断使能信号
		XGpioPs_IntrEnablePin(&Gpio, MIO12_KEY);//打开MIO引脚中断使能信号
		}
	}
	return 0;
}


void SetupInterruptSystem(XScuGic *GicInstancePtr, XGpioPs *Gpio,u16 GpioIntrId)
{
	int Status;
	IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);//查找器件配置信息,并初始化
	XScuGic_CfgInitialize(GicInstancePtr, IntcConfig,IntcConfig->CpuBaseAddress);
	Xil_ExceptionInit();//初始化arm异常句柄
	 //来给IRQ异常注册处理程序
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
				(Xil_ExceptionHandler)XScuGic_InterruptHandler,
				GicInstancePtr);
	/*使能处理器中断*/
	Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);//只要用到中断,就是需要这3个exception函数
	 //关联中断处理函数     处理中断,结束之后再回到原过程             把GPIO的中断处理函数IntrHandler,关联到中断控制器
	XScuGic_Connect(GicInstancePtr, GpioIntrId,
					(Xil_ExceptionHandler)IntrHandler,
					(void *)Gpio);
	/* 为GPIO器件使能中断 */
	XScuGic_Enable(GicInstancePtr, GpioIntrId);
	/*设置MIO引脚为下降沿触发类型 */
	XGpioPs_SetIntrTypePin(Gpio, MIO12_KEY, XGPIOPS_IRQ_TYPE_EDGE_FALLING	 /**< Interrupt Falling edge */);
	/* Set the handler for gpio interrupts. */
	//XGpioPs_SetCallbackHandler(Gpio, (void *)Gpio, IntrHandler);


	/* Enable the GPIO interrupts of Bank 0. */
	//XGpioPs_IntrEnable(Gpio, GPIO_BANK, (1 << Input_Pin));//整个BANK设置使能
	XGpioPs_IntrEnablePin(Gpio, MIO12_KEY);//打开MIO引脚中断使能信号(不取地址的原因是因为最外面的函数定义本身就是指针)
}
//自己写一个中断服务函数IntrHandler
void IntrHandler(){
	printf("interrupt detected!\n\r");
	key_press = 1;
	XGpioPs_IntrDisablePin(&Gpio, MIO12_KEY);
}

参数介绍:GPIO_INTERRUPT_ID,GPIO的中断号是52,具体可以参考UG585里面的文档说明,如下:

        INTC_DEVICE_ID,它是通用中断控制器ID,表示的是0.

        GPIO_DEVICE_ID因为我们只用了一个按键来控制中断,所以它表示的是0,如果有两个中断,那ID应该就是0和1了,这个ID的目的就是分辨,中断是从GPIO的哪个管脚产生的。

程序设计步骤:

        1.由于我们是用的MIO来控制中断的产生,需要根据器件的ID查找期间的配置信息,并初始化GPIO的驱动,最后通过读写寄存器来配置GPIO的方向,输出使能等。

        2.调用并修改SetupInterruptSystem函数,主要实现的功能有,首先查找器件配置信息并初始化(这里是对GIC而言的),其次就是三板斧(初始化ARM异常句柄,给IRQ异常注册处理程序,使能处理器中断)和关联中断处理函数,只要用到中断,三板斧必须加上,也就是三个有Exception的函数。最后,给GPIO器件设置中断、设置MIO引脚的触发类型(我给的程序里是下降沿触发)、打开MIO引脚中断使能信号。      补充一点,就是设置MIO引脚的触发类型和中断使能信号,这两个都可以对单独的引脚进行操作,也可以对整个BANK进行操作,用的函数不一样,程序里面被注释掉的两个函数就是对BANK进行操作的。

        3.自定义一个中断服务函数,连接到关联中断处理函数(XScuGic_Connect)里面。

        4.通过自定义的中断服务函数输出的信号,来实现自己想要的功能,需要注意的一点是,中断处理之后,我们需要给对应寄存器写1清除中断的状态,并通过控制中断使能和关闭来进行下一次的中断操作。

上板验证,按下按键,LED的状态发生改变,符合预期。

                                       

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱你等于1+1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值