ZYNQ学习之旅--PS_MIO_INT

这里写目录标题

简介

中断是一种当满足要求的突发事件发生时通知处理器进行处理的信号。中断可以由硬件处理单元和外部设备产生,也可以由软件本身产生。对硬件来说,中断信号是一个由某个处理单元产生的异步信号,用来引起处理器的注意。对软件来说,中断还是一种异步事件,用来通知处理器需要改变代码的执行,不过,轮询所产生的中断的过程是同步的。
当处理器收到中断,它会停下当前正在做的任务,然后跳转到需要处理的地方去。这和轮询的方式是相反的,轮询是由软件同步获取设备的状态。在中断方式中,不需要由处理器不断地轮询设备的 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)的中断。中断控制器架构如下图所示:

ZYNQ学习之旅--PS_MIO_INT
从图中可以看到,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。
ZYNQ学习之旅--PS_MIO_INT
首先我们来看通用中断控制器。通用中断控制器是一个用于集中管理从 PS 和 PL 发送到 CPU 的中断,
启用、禁用、屏蔽和优先化中断源的处理中心,将具有最高优先级的中断源分配给各个 CPU 之前集中所有中断源,并在 CPU 接口接受下一个中断时以编程方式将它们发送到选定的 CPU。此外,控制器还支持用于实现安全感知系统的安全扩展。该控制器基于非矢量化的 ARM 通用中断控制器架构版1.0(GIC v1)。
GIC 寄存器通过 CPU 私有总线访问寄存器,以避免临时阻塞或互连中的瓶颈,从而实现快速读/写响应。GIC 确保针对多个 CPU 的中断一次只能由一个 CPU 占用。所有中断源都由唯一的中断 ID 号标识,对应有它自己的可配置优先级和目标 CPU 列表。接下来我们依次来看软件生成中断、CPU 私有外设中断和共享外设中断。
每个 CPU 都可以使用软件生成的中断中断自身、另一个 CPU 或同时中断两个 CPU。有 16 个软件生成中断,具体见表 4.1.1。向软件产生的中断寄存器(Software Generated Interrupts Register,ICDSGIR)写入SGI 中断编号并指定目标 CPU(或两个 CPU),就产生了一个 SGI。该写操作通过 CPU 自己的专用(私有)总线进行。每个 CPU 都有自己的一组 SGI 寄存器,用于生成 16 个软件生成的中断中的一个或多个。中断的清除是通过读取中断确认寄存器(Interrupt Acknowledge Register,ICCIAR)或向中断挂起清除寄存器(Interrupt Clear-Pending Register,ICDICPR)对应的位写入“1”来实现的。所有的 SGI 都是边缘触发的,且其敏感性类型是固定的,不能修改。只读的 ICDICFR0 寄存器指定了所有 16 个 SGI 的灵敏度类型。

BD设计

ZYNQ学习之旅--PS_MIO_INT
这里没有用到PL端的资源,只需要对ZYNQ核进行配置就好了,导入到SDK中进行软件开发就好了。

软件设计

ZYNQ学习之旅--PS_MIO_INT
具体代码如下所示:

/*
 * main.c
 */
/***************************** Include Files *********************************/
#include "xparameters.h"
#include "xgpiops.h"
#include "xscugic.h"
#include "xil_exception.h"
#include "xplatform_info.h"
#include <xil_printf.h>
#include "xil_types.h"
#include "sleep.h"
//定义管脚输入还是输出
#define 	input 		0
#define 	output 		1
//定义使能
#define		enable		1
#define		disable		0
//定义管脚
#define 	PS_LED1 	50
#define 	PS_LED2 	51
#define 	PS_KEY1 	0
#define 	PS_KEY2 	47
//GPIO ID定义
#define		GPIO_ID		XPAR_XGPIOPS_0_DEVICE_ID
//中断控制器ID定义
#define		SGC_INT_ID	XPAR_SCUGIC_0_DEVICE_ID
//GPIO中断号定义
#define		GPIO_INT_ID	XPAR_XGPIOPS_0_INTR
//实例化GPIO
XGpioPs XGPIOPS;
XScuGic XSCG;
int flag = 0;
int flag1 = 0;

void GPIO_Handler(void *call_back)
{
	XGpioPs *gpiops = (XGpioPs *)call_back;
	if(XGpioPs_IntrGetStatusPin(gpiops, PS_KEY1)==1){
		flag = 1;
		XGpioPs_IntrDisablePin(gpiops, PS_KEY1);
		//print("kksk\n");
	}
	if(XGpioPs_IntrGetStatusPin(gpiops, PS_KEY2)==1){
		flag1 = 1;
		XGpioPs_IntrDisablePin(gpiops, PS_KEY2);
	}
	//print("here\n");
	//xil_printf("%d\n",XGpioPs_IntrGetStatusPin(gpiops, PS_KEY1));
}

int main()
{
	xil_printf("GPIO INT test\n");
	//老规矩查找GPIO并初始化
	u32 status;
	int data = 0;
	XGpioPs_Config *xgpio_config;
	//查找GPIO的ID号
	xgpio_config = XGpioPs_LookupConfig(GPIO_ID);
	status = XGpioPs_CfgInitialize(&XGPIOPS,xgpio_config,
			xgpio_config->BaseAddr);
	if(status!=XST_SUCCESS){
		xil_printf("xgpio_config failed\n");
		return XST_FAILURE;
	}
	status = XGpioPs_SelfTest(&XGPIOPS);
	if(status!=XST_SUCCESS){
		xil_printf("XGpioPs_SelfTest failed\n");
		return XST_FAILURE;
	}
	//设置按键为输入管脚
	XGpioPs_SetDirectionPin(&XGPIOPS, PS_KEY1, input);
	XGpioPs_SetDirectionPin(&XGPIOPS, PS_KEY2, input);
	//设置LED为输出管脚
	XGpioPs_SetDirectionPin(&XGPIOPS, PS_LED1, output);
	XGpioPs_SetDirectionPin(&XGPIOPS, PS_LED2, output);
	//设置使能
	XGpioPs_SetOutputEnablePin(&XGPIOPS, PS_LED1, enable);
	XGpioPs_SetOutputEnablePin(&XGPIOPS, PS_LED2, enable);
	XGpioPs_WritePin(&XGPIOPS, PS_LED1, data);
	XGpioPs_WritePin(&XGPIOPS, PS_LED2, data);
	//查找中断控制器并初始化
	XScuGic_Config *xscf_config;
	xscf_config = XScuGic_LookupConfig(SGC_INT_ID);
	status = XScuGic_CfgInitialize(&XSCG, xscf_config,
			xscf_config->CpuBaseAddress);
	if(status!=XST_SUCCESS){
		xil_printf("XScuGic_CfgInitialize failed\n");
		return XST_FAILURE;
	}
	status = XScuGic_SelfTest(&XSCG);
	if(status!=XST_SUCCESS){
		xil_printf("XScuGic_SelfTest failed\n");
		return XST_FAILURE;
	}
	//意外中断处理函数
	Xil_ExceptionInit();
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
				(Xil_ExceptionHandler)XScuGic_InterruptHandler,
				&XSCG);
	Xil_ExceptionEnable();
	//中断回掉函数
	status = XScuGic_Connect(&XSCG, GPIO_INT_ID,
				(Xil_InterruptHandler)GPIO_Handler, (void *) &XGPIOPS);
	if(status!=XST_SUCCESS){
		xil_printf("XScuGic_Connect failed\n");
		return XST_FAILURE;
	}
	//使能GPIO中断
	XScuGic_Enable(&XSCG, GPIO_INT_ID);
	//设置下降沿中断
	XGpioPs_SetIntrTypePin(&XGPIOPS,PS_KEY1,XGPIOPS_IRQ_TYPE_EDGE_FALLING);
	XGpioPs_SetIntrTypePin(&XGPIOPS,PS_KEY2,XGPIOPS_IRQ_TYPE_EDGE_FALLING);
	//设置中断使能
	XGpioPs_IntrEnablePin(&XGPIOPS,PS_KEY1);
	XGpioPs_IntrEnablePin(&XGPIOPS,PS_KEY2);
	while(1){
		if(flag == 1){
			usleep(20000);
			if(XGpioPs_ReadPin(&XGPIOPS, PS_KEY1)==0){
				//翻转LED
				XGpioPs_WritePin(&XGPIOPS, PS_LED1, ~XGpioPs_ReadPin(&XGPIOPS, PS_LED1));
			}
			flag = 0;
			XGpioPs_IntrClearPin(&XGPIOPS, PS_KEY1);
			XGpioPs_IntrEnablePin(&XGPIOPS,PS_KEY1);
		}
		if(flag1==1){
			usleep(20000);
			if(XGpioPs_ReadPin(&XGPIOPS, PS_KEY2)==0){
			//翻转LED
				XGpioPs_WritePin(&XGPIOPS, PS_LED2, ~XGpioPs_ReadPin(&XGPIOPS, PS_LED2));
			}
			flag1 = 0;
			XGpioPs_IntrClearPin(&XGPIOPS, PS_KEY2);
			XGpioPs_IntrEnablePin(&XGPIOPS,PS_KEY2);
		}
	}
	return 0;
}

本次实验的功能是利用中断去判断按键是否按下,如果按下就使得相应的LED进行翻转。

上一篇:mpvue应用之组件数据缓存清理


下一篇:mpvue中使用小程序云开发详细步骤