2 - ZYNQ 中断

0 前言

本文参考博主仲夏夜之梦~的博文【裸机开发】认识中断向量表和博主Maverick的博文 ZYNQ中断跳转过程,仅作为个人学习记录。

1 中断类型

在ZYNQ中,中断大致可以分为七种类型,不同的中断类型对应着一种CPU工作模式。当中断产生时,CPU会先切换到对应的工作模式,然后再处理中断。

  • Reset:CPU复位以后就会进入复位中断,我们可以在复位中断服务函数里面做一些初始化工作,如初始化DDR
  • Undefined Instruction:如果指令不能识别的话就会产生此中断
  • SWI:Linux的系统调用会用SWI指令来引起软中断,通过软中断来陷入到内核空间
  • Prefetch Abort:预取指令的出错的时候会产生此中断
  • Data Abort:访问数据出错的时候会产生此中断
  • IRQ外设中断都会引起此中断的发生
  • FIQ:快速中断,如果需要快速处理中断的话就可以使用此中断
偏移地址中断类型中断模式
0x00复位中断(Reset)SVC
0x04未定义指令中断(Undefined Instruction)Undef
0x08软件中断(Software Interrupt)SVC
0x0C指令预取中止中断(Prefetch Abort)Abort
0x10数据中止中断(Data Abort)Abort
0x14保留(Reserved)-
0x18IRQ中断(IRQ Interrupt)IRQ
0x1CFIQ中断(FIQ Interrupt)FIQ

2 中断执行流程

2.1 简单流程

当某个中断发生时,中断执行的简单流程如下图所示:

  • 先判断属于哪一类
  • 去中断向量表找对应类的中断服务函数
  • 执行对应的中断服务函数
  • 返回程序继续执行

在这里插入图片描述

2.2 详细流程

当某个中断发生时,中断执行的详细流程如下图所示:

  • 中断向量表跳转:根据中断类型跳转到对应的中断服务函数
  • 执行中断服务函数:该函数的功能是保护现场、中断处理、恢复现场,中断处理过程首先需要判断中断源,因此需要调用中断解析函数
  • 执行中断解析函数:中断解析函数的作用是通过读取中断寄存器的值,获取中断ID,并调用相应中断ID的中断处理函数
  • 执行中断处理函数:完成清除中断标志位、实现具体中断功能、重新开启中断等操作

在这里插入图片描述

注意 中断服务函数和中断处理函数的区别,个人理解:

  • 中断服务函数对应处理器的中断向量表
  • 中断处理函数对应具体的中断源

3 IRQ中断系统

ZYNQ IRQ中断系统由三部分组成,即中断源、中断控制器和处理器核心,如下图所示。

在这里插入图片描述

3.1 IRQ中断源

中断源有三种:

  • SGI
    • Software Generated Interrupts,即软件生成中断
    • 由软件写入通用中断控制器(GIC)的寄存器而触发
    • 在多核处理器系统中用于实现各种通信和同步机制,如任务调度、锁机制、屏障同步等
    • 数量16个
  • PPI
    • Private Perpheral Interrupts,即私有外设中断
    • 是CPU的私有外设中断,每个CPU都有一组独立的PPI中断,用于处理来自特定外设的中断请求
    • 包括全局计时器、专用看门狗计时器、专用计时器以及从PL端输入的FIQ/IRQ等中断源
    • 数量每个CPU核心各5个
  • SPI
    • Shared Peripheral Interrupts,即共享外设中断
    • 是由PS和PL端的共享外设产生的中断
    • 用于处理来自多个外设的中断请求,如DMA、UART、Timer、IIC、SDIO等,确保这些外设能够及时向CPU报告状态变化或请求服务
    • 数量60个(PS端44个+PL端16个)

3.2 IRQ中断号

每个中断源在中断控制器GIC中都被分配了一个唯一的ID号,即中断ID。这个ID号用于在中断处理过程中唯一地标识和区分不同的中断源。

3.2.1 软件生成中断SGI的ID

在这里插入图片描述

3.2.2 私有外设中断PPI的ID

在这里插入图片描述

3.2.3 共享外设中断的ID

在这里插入图片描述
在这里插入图片描述

3.3 IRQ中断控制器

中断控制器GIC(Generic Interrupt Controller)位于中断源和处理器核心之间,是用于管理中断的核心组件。中断控制器GIC通过集中管理中断请求,确保CPU能够高效地处理来自各个外设和内部组件的中断信号,其功能主要包括:

  • 中断接收
    • GIC接收来自PS(Processing System)和PL(Programmable Logic)端的多个外设和内部组件的中断请求
    • 中断请求包括软件生成的中断(SGI)、CPU私有外设中断(PPI)和共享外设中断(SPI)
  • 中断仲裁
    • 当多个中断请求同时发生时,GIC会根据中断的优先级进行仲裁,确保高优先级的中断能够优先得到处理
    • 优先级可以通过软件编程进行配置
  • 中断分发
    • 仲裁完成后,GIC会将中断请求分发到指定的CPU进行处理
    • 对于SPI中断,GIC可以将其分发到一个或两个CPU中的任何一个
    • 对于SGI和PPI中断,它们通常被分发到特定的CPU进行处理
  • 中断管理
    • GIC提供了丰富的中断管理功能,包括中断的使能、禁用、屏蔽、优先级配置等
    • 功能允许开发者根据系统的实际需求对中断进行灵活的配置和管理

中断控制器GIC结构如下图所示,ICC(Interrupt Controller CPU)连接SGI和PPI,ICD(Interrupt Controller CPU))连接SPI。

在这里插入图片描述

4 在SDK中使用IRQ中断

4.1 IRQ中断处理流程

IRQ中断处理流程如下图所示:

  • 外设产生IRQ中断并提交给GIC
  • GIC接收IRQ中断并转发给处理器
  • 处理器接收IRQ中断后调用IRQ中断服务函数
  • IRQ中断服务函数调用外设中断处理函数

在这里插入图片描述

在ZYNQ SDK中处理IRQ中断的流程如下图所示:

在这里插入图片描述

在ZYNQ SDK中处理IRQ中断的代码如下所示:

	//
	int Status;
	XScuGic_Config *GicConfigPtr; /* Instance of the interrupt controller */

	/*
	 * step1. 初始化中断控制器GIC
	 */
	GicConfigPtr= XScuGic_LookupConfig(GIC_DEVICE_ID);
	if (NULL == GicConfigPtr) {
		return XST_FAILURE;
	}

	Status = XScuGic_CfgInitialize(GicInstPtr, GicConfigPtr, GicConfigPtr->CpuBaseAddress);
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*
	 * step2. 在处理器中初始化异常处理功能
	 */
	Xil_ExceptionInit();

	/*
	 * step3. 在处理器中为IRQ注册中断服务函数
	 */
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler, GicInstPtr);

	/*
	 * step4. 在处理器中使能IRQ中断
	 */
	Xil_ExceptionEnable();

	/*
	 * step5. 在GIC中为外设注册中断处理函数
	 */
	XScuGic_Connect();

	/*
	 * step6. 在GIC中设置中断优先级和触发类型
	 */
	XScuGic_SetPriorityTriggerType();
	
	/*
	 * step7. 在GIC中使能外设中断
	 */
	 XScuGic_Enable();
	 

总结

IRQ中断分三级:

  • 外设产生中断
    • 设置外设中断触发方式(外设如何检测外部输入信号)
    • 使能外设中断(外设能够产生中断)
    • 外设检测到外部中断信号后,产生一个新的中断信号提交给GIC
  • GIC接收中断
    • 注册外设中断处理函数
    • 设置外设中断触发方式(GIC如何检测外设提交的中断)
    • 使能外设中断(GIC能够接收外设产生的中断)
    • GIC接收外设提交的中断信号,经过中断识别、优先级仲裁等处理后,转发或分发给处理器
  • 处理器处理中断
    • 注册IRQ中断服务函数(注意,处理器能够处理的中断也叫异常,本文为了统一概念,统称为中断,IRQ中断只是其中之一
    • 使能IRQ中断(处理器能够接收GIC提交的IRQ中断)
    • 处理器接收GIC提交的IRQ中断后,暂停当前正在执行的程序,调用IRQ中断服务函数和外设中断处理函数来处理中断

4.2 MIO中断实例

PS侧按键触发中断控制PS侧LED灯亮和灭,实例代码如下所示:

/*****************************************************************************/
#include "stdio.h"
#include "xparameters.h"
#include "xgpiops.h"
#include "xscugic.h"
#include "xil_exception.h"
#include "sleep.h"
/*****************************************************************************/
#define GIC_DEVICE_ID		XPAR_SCUGIC_SINGLE_DEVICE_ID
#define PS_GPIO_DEVICE_ID  	XPAR_XGPIOPS_0_DEVICE_ID
// GPIO的中断号 52
#define PS_GPIO_INTR_ID		XPAR_XGPIOPS_0_INTR
// PS端LED
#define PS_MIO_LED			0
#define PS_MIO_KEY			50
/*****************************************************************************/

XGpioPs_Config *PsGpioConfigPtr;
XScuGic_Config *GicConfigPtr;

XGpioPs PsGpioInst;
XScuGic GicInst;

void SetupInterruptSystem(XScuGic *GicInstPtr, XGpioPs *PsGpioInstPtr, u16 PsGpioIntrId);
void PsGpioIntrHandler(void *CallBackRef);

int KeyPress = 0;
int LedValue = 0;
/*****************************************************************************/
int main()
{
	//
	printf("GPIO Interrupt Test.\n\r");

	// 初始化GPIO外设驱动实例
	PsGpioConfigPtr = XGpioPs_LookupConfig(PS_GPIO_DEVICE_ID);
	XGpioPs_CfgInitialize(&PsGpioInst, PsGpioConfigPtr, PsGpioConfigPtr->BaseAddr);

	// 设置GPIO外设MIO引脚为输出(0=输入,1=输出)
	XGpioPs_SetDirectionPin(&PsGpioInst, PS_MIO_LED, 1);

	// 设置GPIO外设MIO引脚为输入(0=输入,1=输出)
	XGpioPs_SetDirectionPin(&PsGpioInst, PS_MIO_KEY, 0);

	// 设置GPIO外设MIO引脚输出使能(0=关闭,1=打开)
	XGpioPs_SetOutputEnablePin(&PsGpioInst, PS_MIO_LED, 1);

	// 设置GPIO外设中断系统
	SetupInterruptSystem(&GicInst, &PsGpioInst, PS_GPIO_INTR_ID);

	//
	while(1)
	{
		if(KeyPress == 1)
		{
			//
			KeyPress = 0;
			LedValue = ~ LedValue;

			// 设置GPIO外设MIO引脚输出值
			XGpioPs_WritePin(&PsGpioInst, PS_MIO_LED, LedValue);

			// 延时消抖(有效果,但无法完全消除)
			usleep(300000);

			// 使能GPIO外设MIO引脚中断
			XGpioPs_IntrEnablePin(&PsGpioInst, PS_MIO_KEY);
		}
	}

	//
	return 0;
}

void SetupInterruptSystem(XScuGic *GicInstPtr, XGpioPs *PsGpioInstPtr, u16 PsGpioIntrId)
{
	// 初始化中断控制器GIC
	GicConfigPtr = XScuGic_LookupConfig(GIC_DEVICE_ID);
	XScuGic_CfgInitialize(GicInstPtr, GicConfigPtr, GicConfigPtr->CpuBaseAddress);

	// 在处理器中初始化异常处理功能
	Xil_ExceptionInit();
	// 在处理器中为IRQ异常注册中断服务函数
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler, GicInstPtr);
	// 在处理器中使能IRQ中断
	Xil_ExceptionEnable();  // <=> Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ)

	// 在GIC中为GPIO外设注册中断处理函数
	XScuGic_Connect(GicInstPtr, PsGpioIntrId, (Xil_ExceptionHandler)PsGpioIntrHandler, (void *)PsGpioInstPtr);

	// 在GIC中设置GPIO外设中断优先级(0xA0)和触发类型(高电平触发)
	XScuGic_SetPriorityTriggerType(GicInstPtr, PsGpioIntrId, 0xA0, 0x1);

	// 在GIC中使能GPIO外设中断
	XScuGic_Enable(GicInstPtr, PsGpioIntrId);

	// 设置GPIO外设MIO引脚中断触发类型(下降沿触发)
	XGpioPs_SetIntrTypePin(PsGpioInstPtr, PS_MIO_KEY, XGPIOPS_IRQ_TYPE_EDGE_FALLING);

	// 使能GPIO外设MIO引脚中断
	XGpioPs_IntrEnablePin(PsGpioInstPtr, PS_MIO_KEY);

}

void PsGpioIntrHandler(void *CallBackRef)
{
	//
	XGpioPs *PsGpioInstPtr = (XGpioPs *)CallBackRef;

	// 禁用GPIO外设MIO引脚中断
	XGpioPs_IntrDisablePin(PsGpioInstPtr, PS_MIO_KEY);

	// 清除GPIO外设MIO引脚中断状态
	XGpioPs_IntrClearPin(PsGpioInstPtr, PS_MIO_KEY);

	//
	KeyPress = 1;

	//
	printf("Interrupt Detected.\r\n");

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值