GPIO按键中断——ZYNQ学习笔记4

一、简介

当处理器收到中断,它会停下当前正在做的任务,然后跳转到需要处理的地方去。这和轮询的方式是相反的,轮询是由软件同步获取设备的状态。在中断方式中,不需要由处理器不断地轮询设备的 I/O 端口来查看是否需要处理,设备本身会中断处理器。中断(主要是硬件中断)可以进一步被分类为以下几种类型:

• 可屏蔽中断( Maskable Interrupts, IRQ)——可通过在中断屏蔽寄存器中设定位掩码来关闭。触发可屏蔽中断的事件源不是每次都是重要的。程序设计人员需要决定该事件是否应该导致程序跳到所需处理的地方去。使用可屏蔽中断的设备包括定时器、比较器和 ADC。

• 不可屏蔽中断( Non-Maskable Interrupts, NMI)——无法通过在中断屏蔽寄存器中设定位掩码来关闭。这些是不可忽视的中断。 NMI 的事件包括上电、外部重启(用实际的按钮)和严重的设备失效。

• 处理器间中断( Inter-Processor Interrupts, IPI)——在多处理器系统中,一个处理器可能需要中断另一个处理器的操作。在这种情况下,就会产生一个 IPI,以便于处理器间通信或同步。

Zynq 芯片的 PS 部分是基于使用双核 Cortex-A9 处理器和 GIC pl390 中断控制器的 ARM 架构。中断结构与 CPU 紧密链接,并接受来自 I/O 外设( IOP)和可编程逻辑( PL)的中断。中断控制器架构如下图所示:

        从图中可以看到, CPU 接收的中断来源有三种,分别是私有外设中断( private peripheral interrupts, PPI)、软件生成的中断( software generated interrupts, SGI)和共享外设中断( shared peripheral interrupts、 SPI)。

        每个 CPU 都有一组私有外设中断,使用存储寄存器进行私有访问。 PPI 包括全局定时器、专用看门狗定时器( AWDT)、专用定时器和来自 PL 的 FIQ/IRQ。软件生成的中断通过 SGI 分配(派)器分配给一个或两个 CPU。共享外设中断由 PS 和 PL 中的各种 I/O 和存储控制器生成。它们被路由到任一个或两个 CPU,来自 PS 外设的 SPI 中断也可以路由到 PL。下面我们从下图的系统级中断环境来进一步了解中断

        首先我们来看通用中断控制器。通用中断控制器是一个用于集中管理从 PS 和 PL 发送到 CPU 的中断,启用、禁用、屏蔽中断源和确定中断源的优先级的处理中心, 将具有最高优先级的中断源分配给各个 CPU之前集中所有中断源,并在 CPU 接口接受下一个中断时以编程方式将它们发送到选定的 CPU。此外,控制器还支持用于实现安全感知系统的安全扩展。该控制器基于非矢量化的 ARM 通用中断控制器架构版本 1.0 ( GIC v1)。 GIC 寄存器通过 CPU 私有总线访问寄存器,以避免临时阻塞或互连中的瓶颈,从而实现快速读/写响应。 GIC 确保针对多个 CPU 的中断一次只能由一个 CPU 占用。所有中断源都有唯一的中断 ID 号标识,对应有它自己的可配置优先级和目标 CPU 列表每个 CPU 都可以使用软件生成的中断来中断自身、另一个 CPU 或同时中断两个 CPU。有 16 个软件生成中断,具体见表(软件中断)

每个 CPU 核连接到了一个有五个外设中断的私有组上,这五个外设中断见表PPI 的敏感类型是固定的,不能改变。需要注意的是:来自 PL 的快速中断( FIQ)信号和中断( IRQ)信号在发送给中断控制器之前,会在传输给 PS 的时候被反转。因此,这些信号因此在 PL 内低电平有效,在 PS-PL 接口处高电平有效。*(私有外设中断)

来自各种模块的大约 60 个中断的组可以被路由到 PL 或 CPU 中的一个或两个,这 60 个中断见表 5.1.3。那些目标为 CPU 的中断的优先级和中断的接收情况是由中断控制器管理的。除 IRQ# 61 至# 68 和# 84 至# 91 外,所有中断灵敏度类型均由请求源固定,无法更改。必须对 GIC 进行编程以适应这种情况。 Boot ROM不编程这些寄存器。因此, Vitis 设备驱动程序必须对 GIC 进行编程以适应这些敏感类型。(共享中断)

        对于电平敏感类型的中断,请求源必须为中断处理程序提供一种机制,以便在确认中断后清除中断。此要求适用于具有高电平敏感类型的任何 IRQF2P [n](来自 PL)。对于上升沿敏感的中断,请求源必须提供足够宽的脉冲以便 GIC 捕获。这通常至少为 2 个 CPU_2x3x 周期。此要求适用于具有上升沿灵敏度类型的任何 IRQF2P [n](来自 PL)。        


 #52为zynq系列中断ID(GPIO)需要结合INT_MASK(那些被屏蔽)与INT_STAT(判断中断发生引脚)判断具体引脚IO

INT_TYPE:该寄存器控制中断是边沿敏感还是电平敏感
INT_POLARITY:该寄存器控制中断是低电平有效还是高电平有效(或下降沿敏感或上升沿敏感)。
INT _ANY:如果 INT_TYPE 设置为边沿敏感,则该寄存器在上升沿和下降沿都会启用中断事件。如果INT_TYPE 设置为电平敏感,则忽略该寄存器。
        从 INT_TYPE、 INT_POLARITY 和 INT _ANY 寄存器我们可以看到中断触发方式可以是上升沿,下降沿,边沿,低电平或高电平。

INT_STAT:该寄存器显示是否发生了中断事件。将 1 写入该寄存器中的某个位可清除该位的中断状态。将 0 写入该寄存器中的某个位将被忽略。

INT_MASK:这个寄存器是只读的,显示哪些位当前被屏蔽,哪些位未被屏蔽/启用
INT_DIS:向该寄存器的任何位写入 1 都会屏蔽该中断信号。从该寄存器读取会返回不可预测的值。
INT_EN:向该寄存器的任何位写入 1, 可以使能中断信号(解除中断信号的屏蔽) 。从该寄存器读取将返回一个不可预测的值

中断不会占用CPU资源

中断ID查询:

 

二、试验任务

本章的实验任务是使用底板上的 PS 端的用户按键 PS_KEY0 通过中断控制底板上 PS_LED0 的亮灭。

三、C语言补习

四、程序设计

1、系统框图:

2、代码编写

#include <stdio.h>
#include "xparameters.h"
#include "xgpiops.h"
#include "sleep.h"
#include "xscugic.h"
#include "xil_exception.h"

#define GPIO_DEVICE_ID		XPAR_XGPIOPS_0_DEVICE_ID	//0
#define GPIO_INTERRUPT_ID	XPAR_XGPIOPS_0_INTR			//52
#define INTC_DEVICE_ID		XPAR_SCUGIC_SINGLE_DEVICE_ID

#define MIO_LED1      7//MIO7
#define MIO_LED2      8//MIO8
#define MIO_LED0      0//MIO0
#define MIO_KEY0      12//MIO12
#define EMIO_KEY0     54//EMIO54
#define EMIO_KEY1     55//EMIO54

void SetupInterruptSystem(XScuGic *GicInstancePtr, XGpioPs *Gpio,
				u16 GpioIntrId);
void IntrHandler(void *CallBackRef, u32 Bank, u32 Status);

XGpioPs Gpio;	//GPIO器件驱动实例
XScuGic Intc;	//GIC器件驱动实例

u8 key_flag = 0;

int main(){
	XGpioPs_Config *ConfigPtr;
	u32 pl_key0;
	u32 pl_key1;
	u8  led_val = 0;
	//初始化GPIO驱动配置

	//根据器件ID来查找器件配置信息
	ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
	//对GPIO的驱动进行初始化
	XGpioPs_CfgInitialize(&Gpio, ConfigPtr,ConfigPtr->BaseAddr);

	//设置引脚方向
	XGpioPs_SetDirectionPin(&Gpio, MIO_LED1, 1);
	XGpioPs_SetDirectionPin(&Gpio, MIO_LED2, 1);
	XGpioPs_SetDirectionPin(&Gpio, MIO_LED0, 1);
	//设置PS_KEY
	XGpioPs_SetDirectionPin(&Gpio, MIO_KEY0, 0);
	//设置PL_KEY
	XGpioPs_SetDirectionPin(&Gpio, EMIO_KEY0, 0);
	XGpioPs_SetDirectionPin(&Gpio, EMIO_KEY1, 0);

	//设置输出使能   0:输入    1:输出
	XGpioPs_SetOutputEnablePin(&Gpio, MIO_LED1, 1);
	XGpioPs_SetOutputEnablePin(&Gpio, MIO_LED2, 1);
	XGpioPs_SetOutputEnablePin(&Gpio, MIO_LED0, 1);
	printf("GPIO Interrupt TEXT!\n");

	//GPIO中断配置
	SetupInterruptSystem(&Intc, &Gpio, GPIO_INTERRUPT_ID);

	while(1){
		if(key_flag == 1){
			usleep(20000);
			pl_key1 = XGpioPs_ReadPin(&Gpio,EMIO_KEY1);
			if(pl_key1 == 0){
				led_val = !led_val;
				XGpioPs_WritePin(&Gpio, MIO_LED1, led_val);
			}
			//打开GPIO使能
			XGpioPs_IntrEnablePin(&Gpio,EMIO_KEY1);
			key_flag = 0;
		}
//		else{
//			XGpioPs_IntrEnablePin(Gpio,EMIO_KEY1);
//		}
		pl_key0 = XGpioPs_ReadPin(&Gpio,EMIO_KEY0);

		XGpioPs_WritePin(&Gpio, MIO_LED2, pl_key0);
	}
	return 0 ;
}


void SetupInterruptSystem(XScuGic *GicInstancePtr, XGpioPs *Gpio,
				u16 GpioIntrId)
{

	XScuGic_Config *IntcConfig; //GIC配置信息的驱动实例

	//根据中断控制器器件ID查找配置信息
	IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
	//根据查找到的配置信息初始化中断控制器
	XScuGic_CfgInitialize(GicInstancePtr, IntcConfig,
					IntcConfig->CpuBaseAddress);
	//关联GPIO中断处理程序
	XScuGic_Connect(GicInstancePtr, GpioIntrId,
				(Xil_ExceptionHandler)XGpioPs_IntrHandler,
				(void *)Gpio);
	//设置GPIO中断处理程序
	XGpioPs_SetCallbackHandler(Gpio, (void *)Gpio, IntrHandler);
	//设置GPIO触发中断类型为下降沿
	XGpioPs_SetIntrTypePin(Gpio,EMIO_KEY1,XGPIOPS_IRQ_TYPE_EDGE_FALLING);
	//使能GPIO中断
	XGpioPs_IntrEnablePin(Gpio,EMIO_KEY1);
	//使能GIC的GPIO中断
	XScuGic_Enable(GicInstancePtr, GpioIntrId);

	//异常初始化
	Xil_ExceptionInit();
	//注册中断请求异常处理
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
				(Xil_ExceptionHandler)XScuGic_InterruptHandler,
				GicInstancePtr);
	//使能异常处理
	Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);
}

void IntrHandler(void *CallBackRef, u32 Bank, u32 Status)
{
	XGpioPs *Gpio = (XGpioPs *)CallBackRef;

	printf("GO INTERRUPT\n");
	key_flag = 1;

	//关闭GPIO中断
	XGpioPs_IntrDisablePin(Gpio,EMIO_KEY1);

}

五、总结

六、拓展训练

#include <stdio.h>
#include "xparameters.h"
#include "xgpiops.h"
#include "sleep.h"
#include "xscugic.h"
#include "xil_exception.h"

#define GPIO_DEVICE_ID		XPAR_XGPIOPS_0_DEVICE_ID	//0
#define GPIO_INTERRUPT_ID	XPAR_XGPIOPS_0_INTR			//52
#define INTC_DEVICE_ID		XPAR_SCUGIC_SINGLE_DEVICE_ID

#define MIO_LED1      7//MIO7
#define MIO_LED2      8//MIO8
#define MIO_LED0      0//MIO0
#define MIO_KEY0      12//MIO12
#define EMIO_KEY0     54//EMIO54
#define EMIO_KEY1     55//EMIO54

void SetupInterruptSystem(XScuGic *GicInstancePtr, XGpioPs *Gpio,
				u16 GpioIntrId);
void IntrHandler(void *CallBackRef, u32 Bank, u32 Status);

XGpioPs Gpio;	//GPIO器件驱动实例
XScuGic Intc;	//GIC器件驱动实例

u8 EMIO_key1_flag = 1;
u8 MIO_key0_flag = 0;

int main(){
	XGpioPs_Config *ConfigPtr;
	u32 ps_key0;
	u32 pl_key0;
	u32 pl_key1;
	u8  led_val = 0;
	//初始化GPIO驱动配置

	//根据器件ID来查找器件配置信息
	ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
	//对GPIO的驱动进行初始化
	XGpioPs_CfgInitialize(&Gpio, ConfigPtr,ConfigPtr->BaseAddr);

	//设置引脚方向
	XGpioPs_SetDirectionPin(&Gpio, MIO_LED1, 1);
	XGpioPs_SetDirectionPin(&Gpio, MIO_LED2, 1);
	XGpioPs_SetDirectionPin(&Gpio, MIO_LED0, 1);
	//设置PS_KEY
	XGpioPs_SetDirectionPin(&Gpio, MIO_KEY0, 0);
	//设置PL_KEY
	XGpioPs_SetDirectionPin(&Gpio, EMIO_KEY0, 0);
	XGpioPs_SetDirectionPin(&Gpio, EMIO_KEY1, 0);

	//设置输出使能   0:输入    1:输出
	XGpioPs_SetOutputEnablePin(&Gpio, MIO_LED1, 1);
	XGpioPs_SetOutputEnablePin(&Gpio, MIO_LED2, 1);
	XGpioPs_SetOutputEnablePin(&Gpio, MIO_LED0, 1);
	printf("GPIO Interrupt TEXT!\n");

	//GPIO中断配置
	SetupInterruptSystem(&Intc, &Gpio, GPIO_INTERRUPT_ID);

	while(1){
		if(EMIO_key1_flag == 1){
			usleep(30000);
			pl_key1 = XGpioPs_ReadPin(&Gpio,EMIO_KEY1);
			if(pl_key1 == 0){
				led_val = !led_val;
				XGpioPs_WritePin(&Gpio, MIO_LED1, led_val);
			}
			//打开GPIO使能
			XGpioPs_IntrEnablePin(&Gpio,EMIO_KEY1);
			EMIO_key1_flag = 0;
		}
		else if(MIO_key0_flag == 1){
			usleep(30000);
			ps_key0 = XGpioPs_ReadPin(&Gpio,MIO_KEY0);
			if(ps_key0 == 0){
				led_val = !led_val;
				XGpioPs_WritePin(&Gpio, MIO_LED1, led_val);
			}
			//打开GPIO使能
			XGpioPs_IntrEnablePin(&Gpio,MIO_KEY0);
			MIO_key0_flag = 0;
		}
		pl_key0 = XGpioPs_ReadPin(&Gpio,EMIO_KEY0);

		XGpioPs_WritePin(&Gpio, MIO_LED2, pl_key0);
	}
	return 0 ;
}


void SetupInterruptSystem(XScuGic *GicInstancePtr, XGpioPs *Gpio,
				u16 GpioIntrId)
{

	XScuGic_Config *IntcConfig; //GIC配置信息的驱动实例

	//根据中断控制器器件ID查找配置信息
	IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
	//根据查找到的配置信息初始化中断控制器
	XScuGic_CfgInitialize(GicInstancePtr, IntcConfig,
					IntcConfig->CpuBaseAddress);
	//关联GPIO中断处理程序
	XScuGic_Connect(GicInstancePtr, GpioIntrId,
				(Xil_ExceptionHandler)XGpioPs_IntrHandler,
				(void *)Gpio);
	//设置GPIO中断处理程序
	XGpioPs_SetCallbackHandler(Gpio, (void *)Gpio, IntrHandler);
	//设置GPIO触发中断类型为下降沿
	XGpioPs_SetIntrTypePin(Gpio,EMIO_KEY1,XGPIOPS_IRQ_TYPE_EDGE_FALLING);
	XGpioPs_SetIntrTypePin(Gpio,MIO_KEY0,XGPIOPS_IRQ_TYPE_EDGE_FALLING);
	//使能GPIO中断
	XGpioPs_IntrEnablePin(Gpio,EMIO_KEY1);
	XGpioPs_IntrEnablePin(Gpio,MIO_KEY0);
	//使能GIC的GPIO中断
	XScuGic_Enable(GicInstancePtr, GpioIntrId);

	//异常初始化
	Xil_ExceptionInit();
	//注册中断请求异常处理
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
				(Xil_ExceptionHandler)XScuGic_InterruptHandler,
				GicInstancePtr);
	//使能异常处理
	Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);
}

void IntrHandler(void *CallBackRef, u32 Bank, u32 Status)
{
	u8 key0;
	u8 key1;
	XGpioPs *Gpio = (XGpioPs *)CallBackRef;
	key0 = XGpioPs_ReadPin(Gpio,EMIO_KEY1);
	key1 = XGpioPs_ReadPin(Gpio,MIO_KEY0);
	if( key0 == 0){
		EMIO_key1_flag = 1;
		MIO_key0_flag = 0;
		//关闭GPIO中断
		XGpioPs_IntrDisablePin(Gpio,EMIO_KEY1);
	}
	else if( key1 == 0){
		EMIO_key1_flag = 0;
		MIO_key0_flag = 1;
		//关闭GPIO中断
		XGpioPs_IntrDisablePin(Gpio,MIO_KEY0);
	}
	printf("GO INTERRUPT\n");

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值