参考了大量其他讲解,自用。
整个中断系统架构中,只包含以下三种中断:
软件生成中断(Software Generated Interrupts,SGI)
私有外设中断(Private Peripheral Interrupts,PPI)
共享外设中断(Shared Peripheral Interrupts,SPI)
每种中断都各自包含特定的中断源,如: PPI 包括全局定时器(global timer)、 私有看门狗定时器(private timer)、私有定时器(private timer)和 PL 中的 FIQ。
SGI 则是通过写入通用中断控制器(Generic Interrupt Controller,GIC)中的寄存器 生成。SPI 是由 PS 和 PL 中的各种 I/O 和内存控制器生成的。
对于一般的中低端 CPU 系统来说,上面的结构就足够实现中断的产生和处 理功能了。但是,我们 zynq 的 PS 可不是一般的处理器系统,它是一个结构复杂,性能强劲的双核 Cortex-A9 处理器系统。其包含一级缓存 L1 ,二级缓存 L2, 缓存能够提升 CPU 读写总线数据的性能。但是使用不当,也会造成 CPU 读写数 据实时性的下降,甚至因为缓存一致性的问题,把一些已经由其他外设更新但 是缓存中还是旧值的数据取用,从而产生错误。由于中断的实时性和重要性,中断控制器的相关寄存器的读写是不能因为缓存的存在而被延误的,所以 CPU 并不通过 L2 来访问 GIC 中的寄存器,而是通过一个名为窥探控制单元 Snoop Control Unit(简称 SCU),采用私有总线访问 GIC 的寄存器,以此来提升中断 的处理效率。
IRQ和FRQ:普通中断和快中断
通用中断控制器(GIC)是一种集中式资源,是联系中断和 CPU 的桥梁,也是各 CPU 之间中断互联的通道(也带有管理功能),它负责管理、分发从 PS 和 PL 发送到 CPU 的中断。
当 CPU 接口接受下一个中断时,控制器启用、禁用、 屏蔽中断源并确定其优先级,然后按照编程设定将它们发送给选定的 CPU。
当然,并不是说所有的中断请求一发出都能立马被 CPU 处理。所有中断源 都有唯一的中断标识号标识以及自己可配置的优先级和目标 CPU 列表。
图中的 中断分发器(Interrupt Controller Distributor ,ICD)保存着每个 CPU 的挂起中断列表。它会集中所有的中断源,随后根据其优先级(优先级设置值越大,优先级越小),优先将高优先级的中断源分配给指定 CPU 处理,以确保针对多个 CPU 的中断一次只能由一个 CPU 处理。
但是优先级逻辑在物理上是重复的,也 就是说不同的中断也可以设置为同一优先级。对于这种情况,中断分发器会优先将中断标识号最低的中断源分配给 CPU 处理。
软中断
每个 CPU 都可以使用软件生成的中断 (SGI) 来中断自身、另一个 CPU 或两个 CPU。
有 16 个软件生成的中断(见表 7-2)。通过将 SGI 中断号写入 ICDSGIR 寄存器并指定目标 CPU 来生成 SGI。此写入通过 CPU 自己的专用总线进行。每个 CPU 都有自己的一组 SGI 寄存器,用于生成 16 个软件生成的中断中的一个或多个。通过读取 ICCIAR(中断应答)寄存器或将 1 写入 ICDICPR(中断清除挂起)寄存器的相应位来清除中断。
所有 SGI 都是边沿触发的。 SGI 的灵敏度类型是固定的且无法更改; ICDICFR0 寄存器是只读的,因为它指定了所有 16 个 SGI 的灵敏度类型。
每个 CPU 专用的一组 16 个中断源,可以路由到最多 16 个公共中断目标,其中每个目标可以是一个或多个 CPU。
私有设备中断
每个 CPU 连接到一组私有的五个外设中断。 PPI 列于表 7-3 中。
PPI 的敏感度类型是固定的且无法更改;因此,ICDICFR1 寄存器是只读的,因为它指定了所有 5 个 PPI 的灵敏度类型。请注意,来自 PL 的快速中断 (FIQ) 信号和中断 (IRQ) 信号被反转,然后发送到中断控制器。因此,尽管 ICDICFR1 寄存器将它们反映为低电平有效,但它们在 PS-PL 接口处为高电平有效。
共享设备中断
来自各个模块的一组大约 60 个中断可以路由到一个或两个 CPU 或 PL。中断控制器管理 CPU 的这些中断的优先级和接收。
除了 IRQ #61 至 #68 和 #84 至 #91 之外,所有中断敏感度类型均由请求源固定且无法更改。 GIC 必须进行编程以适应这种情况。引导 ROM 不会对这些寄存器进行编程;因此,SDK 设备驱动程序必须对 GIC 进行编程以适应这些敏感度类型。
对于电平敏感类型的中断,请求源必须提供一种机制,以便中断处理程序在中断被应答后清除中断。此要求适用于任何具有高级别灵敏度类型的 IRQF2P[n](来自 PL)。
对于上升沿敏感的中断,请求源必须提供足够宽的脉冲供 GIC 捕获。这通常至少是 2 个 CPU_2x3x 周期。此要求适用于任何具有上升沿敏感类型的 IRQF2P[n](来自 PL)。
ICDICFR2 至 ICDICFR5 寄存器配置所有 SPI 的中断类型。每个中断都有一个 2 位字段,指定敏感类型和处理模型。
GPIO中断号为 #52,触发类型为高电平。
而 AXI GPIO 由于是 PL 逻辑构成的,因此 AXI GPIO 中断属于 PL 中断,中断号为#[91:84]和#[68:61],触发类型为高电平或上升沿。 该中断在使用时需要在 ZYNQ 核中使能对应中断端口。
具体开发情况
开发步骤:
ZYNQ-实现GPIO的中断控制_zynq gpio中断_Vuko-wxh的博客-优快云博客
第一个环节:
初始化,注册,设置回调,使能各个步骤。
- 初始化CPU异常功能
- 初始化中断控制器
- 向CPU注册异常处理回调函数(CPU在中断以后执行的函数)
- 向中断控制器中对应的中断ID和中断控制器相连接(并且注册第六步的回调函数)
- 设置中断的类型
- 设置GPIO中断的回调函数(用户自己设置)
- 使能对应引脚的中断
- 使能中断控制器
- 使能异常处理功能
void GPIO_GIC_INIT(){ int Status; Xil_ExceptionInit(); Interrupt_Gpio_ConFig = XScuGic_LookupConfig(GPIO_DEVICE_ID); //param文件获取 if (NULL == Interrupt_Gpio_ConFig) { return XST_FAILURE; } Status = XScuGic_CfgInitialize(&Interrupt_Gpio_instance, Interrupt_Gpio_ConFig, Interrupt_Gpio_ConFig->CpuBaseAddress); if (Status != XST_SUCCESS) { return XST_FAILURE; } Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler) XScuGic_InterruptHandler, &Interrupt_Gpio_instance); //注册主回调函数 Status = XScuGic_Connect(&Interrupt_Gpio_instance, Interrupt_ID_Gpio, (Xil_InterruptHandler )XGpioPs_IntrHandler, /*这里如果直接传回自己的回调函数也可以,效率会快*/ (void *)&GPIO_Decive ); if (Status != XST_SUCCESS) { return XST_FAILURE; } XGpioPs_SetIntrType(&GPIO_Decive, GPIO_BANK_2, 0xffffffff, 0x00, 0x00) ; XGpioPs_SetCallbackHandler(&GPIO_Decive, (void *) &GPIO_Decive, (XGpioPs_Handler )IntrHandler); XGpioPs_IntrEnable(&GPIO_Decive, GPIO_BANK_2, (1<<(Bottom-54))/emio的地址偏移 ); XScuGic_Enable(&Interrupt_Gpio_instance, Interrupt_ID_Gpio); Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ); //这是根据XIL_EXCEPTION_ID_INT的指定来调整的 return XST_SUCCESS; }
第二个环节:设置回调函数和中断清除
回调函数API XGpioPs_IntrGetStatusPin();检测该引脚状态,1为触发; XGpioPs_IntrClearPin()清楚该引脚挂起的中断; XGpioPs_IntrDisablePin()关闭该引脚中断; XGpioPs_IntrEnablePin()打开该引脚中断;
步骤:
1.调用回调函数的场合,中断已经开始进行,
XGpioPs_IntrGetStatusPin(const XGpioPs *InstancePtr, u32 Pin)通过检查指定引脚的状态返回True或者False,True则为中断状态。
2.XGpioPs_IntrClearPin(const XGpioPs *InstancePtr, u32 Pin)来清除该管脚的中断状态,并且使用XGpioPs_IntrDisablePin(const XGpioPs *InstancePtr, u32 Pin)对其禁用,防止多次触发,
3.执行回调函数。
4.XGpioPs_IntrEnablePin重新使能端口
static void IntrHandler(void *CallBackRef, u32 Bank, u32 Status)
{
XGpioPs *Gpio = (XGpioPs *)CallBackRef;
u32 DataRead;
int cnt=0;
u32 Interrupt_Status;
// Interrupt_Status = XGpioPs_IntrGetStatusPin(Gpio,Bottom);
// if(Interrupt_Status == 1){
XGpioPs_IntrClearPin(Gpio,Bottom);
XGpioPs_IntrDisablePin(Gpio,Bottom);
DataRead = XGpioPs_ReadPin(Gpio,Bottom);
if(DataRead == 0){
LED_value = ~LED_value;
XGpioPs_WritePin(Gpio,LED0,LED_value);
XGpioPs_WritePin(Gpio,LED1,~LED_value);
Interrupt_CNT++;
printf("Interrupt_CNT = %d\n",Interrupt_CNT);
}
while (cnt < 100){
DataRead = XGpioPs_ReadPin(Gpio,Bottom);
if(DataRead == 1){
cnt++;}
else{
cnt=0;}
usleep(1000);
}
XGpioPs_IntrEnablePin(Gpio,Bottom);
// }
}
GIC驱动程序组件。
中断控制器驱动程序对各种处理程序使用优先级的概念:
优先级是 1 到 31 范围内的整数,默认值 1 是最高优先级中断源。各种源的优先级可以根据需要通过硬件配置动态更改。
6通用中断控制器支持以下功能: 特定的单独中断 启用/禁用特定的单独中断确认 附加特定的回调函数来处理中断源 如果默认值不可接受,则为中断源分配所需的优先级。
有关连接驱动程序中断处理程序的详细信息包含在特定于中断处理的源文件 xscugic_intr.c 中。
该驱动程序旨在独立于 RTOS 和处理器。它仅适用于物理地址。任何对动态内存管理、线程或线程互斥、虚拟内存或高速缓存控制的需求都必须由该驱动程序之上的层来满足。
中断向量表
驱动程序使用中断控制器设备的设备 ID 作为配置数据表的直接索引。用户应使用XScuGic_Connect() 和 XScuGic_Disconnect() 函数在运行时使用处理程序和回调填充向量表。
每个向量表条目对应一个可以产生中断的设备。每个条目都包含一个中断处理程序函数和一个在中断发生时传递给处理程序的参数。当中断处理程序采用基地址以外的参数时,用户必须使用 XScuGic_Connect()。
嵌套中断处理
该驱动程序不支持嵌套中断。
注意:通用中断控制器不是监听控制单元的一部分,如驱动程序名称中的前缀“scu”所示。它是APU中的一个独立模块。
函数介绍:
步骤1:初始化CPU异常功能
Xil_ExceptionInit
将中断控制器中断处理程序连接到ARM 处理器中的硬件中断处理逻辑。
我在新的例子中没有看到这个函数了。
* @brief 该函数是一个通用 API,用于在所有支持的手臂上初始化异常处理程序 *处理器。对于 ARM Cortex-A53、Cortex-R5、* 和 Cortex-A9,异常处理程序正在静态初始化,并且该函数不执行任何操作。
* 然而,它仍然存在以解决向后兼容性 * 问题(在早期版本的 BSP 中,此 API 用于 * 初始化异常处理程序)。
/****************************************************************************/
/**
* @brief The function is a common API used to initialize exception handlers
* across all supported arm processors. For ARM Cortex-A53, Cortex-R5,
* and Cortex-A9, the exception handlers are being initialized
* statically and this function does not do anything.
* However, it is still present to take care of backward compatibility
* issues (in earlier versions of BSPs, this API was being used to
* initialize exception handlers).
*
* @return None.
*
*****************************************************************************/
void Xil_ExceptionInit(void)
{
return;
}
步骤2:初始化中断控制器
和配置其他的没什么不同,不详细列举。
GicConfig = XScuGic_LookupConfig(DeviceId);
if (NULL == GicConfig) {
return XST_FAILURE;
}
Status = XScuGic_CfgInitialize(&InterruptController, GicConfig,
GicConfig->CpuBaseAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
步骤3:向CPU注册异常处理回调函数Xil_ExceptionRegisterHandler()
void Xil_ExceptionRegisterHandler(u32 Exception_id,
Xil_ExceptionHandler Handler,
void *Data)
此处的Exception_id 需要传入xilinx写好的宏定义:XIL_EXCEPTION_ID_INT
此参数需要根据需求来选择,这是最常选择的。
Handler则需要传入SCU提供的函数:
XScuGic_InterruptHandler
Data需要传入指向 XScuGic 实例的指针。
例如&Interrupt_Gpio_instance
/******************************************************** **************************/
/** * @brief 为特定异常注册处理程序。当处理器遇到指定的异常时,将调用此处理程序。
* * @param Exception_id 包含异常源的 ID,并且应该
* 在 0 到 XIL_EXCEPTION_ID_LAST 的范围内。
* 有关更多信息,请参阅 xil_exception.h。
* @param Handler 到该异常的处理程序。
* @param Data 是对数据的引用,当处理程序被调用时,该数据将被传递给处理程序。
* * @返回无。
********************************************************* **************************/
/*****************************************************************************/
/**
* @brief Register a handler for a specific exception. This handler is being
* called when the processor encounters the specified exception.
*
* @param Exception_id contains the ID of the exception source and should
* be in the range of 0 to XIL_EXCEPTION_ID_LAST.
* See xil_exception.h for further information.
* @param Handler to the Handler for that exception.
* @param Data is a reference to Data that will be passed to the
* Handler when it gets called.
*
* @return None.
*
****************************************************************************/
void Xil_ExceptionRegisterHandler(u32 Exception_id,
Xil_ExceptionHandler Handler,
void *Data)
{
#if (defined (versal) && !defined(ARMR5) && EL3) || defined(ARMR52)
if ( XIL_EXCEPTION_ID_IRQ_INT == Exception_id )
{
/*
* Cortexa72 processor in versal is coupled with GIC-500, and
* GIC-500 supports only FIQ at EL3. Hence, tweaking this API
* to act on IRQ, if Exception_id is pointing to IRQ
*/
Exception_id = XIL_EXCEPTION_ID_FIQ_INT;
}
#endif
XExc_VectorTable[Exception_id].Handler = Handler;
XExc_VectorTable[Exception_id].Data = Data;
}
步骤4:连接GPIO中断信号并注册GPIO回调函数:XScuGic_Connect()
s32 XScuGic_Connect(XScuGic *InstancePtr, u32 Int_Id,
Xil_InterruptHandler Handler, void *CallBackRef)
InstancePtr
– is a pointer to the XScuGic instance.Int_Id
– contains the ID of the interrupt source and should be in the range of 0 to XSCUGIC_MAX_NUM_INTR_INPUTS - 1Handler
– to the handler for that interrupt.CallBackRef
– is the callback reference, usually the instance pointer of the connecting driver.
Xil_InterruptHandler Handler需要传传入:
GPIO 中断的中断处理程序XGpioPs_IntrHandler、
void *CallBackRef同样需要传递实例化指针
这次是传递给XGpioPs_IntrHandler,所以需要传递的是指向GPIO实例的指针,而不是是指向 XScuGic 实例的指针。
/******************************************************** ****************************/
/** * * 在中断源的 Int_Id 和关联的处理程序之间建立连接是在识别到中断时运行。
* 在此调用中作为 Callbackref 提供的参数在调用处理程序时用作处理程序的参数。
* * @param InstancePtr 是指向 XScuGic 实例的指针。
* @param Int_Id 包含中断源的 ID,并且应在 0 到 XSCUGIC_MAX_NUM_INTR_INPUTS - 1 的范围内
* @param Handler 指向该中断的处理程序。
* @param CallBackRef 是回调引用,通常是连接驱动程序的实例指针。
* * @return * * - XST_SUCCESS 如果处理程序连接正确。
* * @note *
* 警告:作为参数提供的处理程序将覆盖之前连接的任何处理程序。
********************************************************* ****************************/
/*****************************************************************************/
/**
*
* Makes the connection between the Int_Id of the interrupt source and the
* associated handler that is to run when the interrupt is recognized. The
* argument provided in this call as the Callbackref is used as the argument
* for the handler when it is called.
*
* @param InstancePtr is a pointer to the XScuGic instance.
* @param Int_Id contains the ID of the interrupt source and should be
* in the range of 0 to XSCUGIC_MAX_NUM_INTR_INPUTS - 1
* @param Handler to the handler for that interrupt.
* @param CallBackRef is the callback reference, usually the instance
* pointer of the connecting driver.
*
* @return
*
* - XST_SUCCESS if the handler was connected correctly.
*
* @note
*
* WARNING: The handler provided as an argument will overwrite any handler
* that was previously connected.
*
****************************************************************************/
s32 XScuGic_Connect(XScuGic *InstancePtr, u32 Int_Id,
Xil_InterruptHandler Handler, void *CallBackRef)
{
/*
* Assert the arguments
*/
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(Int_Id < XSCUGIC_MAX_NUM_INTR_INPUTS);
Xil_AssertNonvoid(Handler != NULL);
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
/*
* The Int_Id is used as an index into the table to select the proper
* handler
*/
InstancePtr->Config->HandlerTable[Int_Id].Handler = (Xil_InterruptHandler)Handler;
InstancePtr->Config->HandlerTable[Int_Id].CallBackRef = CallBackRef;
return XST_SUCCESS;
}
步骤5:设置GPIO中断类型XGpioPs_SetIntrType()
void XGpioPs_SetIntrType(const XGpioPs *InstancePtr, u8 Bank, u32 IntrType,
u32 IntrPolarity, u32 IntrOnAny)
或:
void XGpioPs_SetIntrTypePin(const XGpioPs *InstancePtr, u32 Pin, u8 IrqType)
配置单个Pin,这两个函数有许多不同之处
zynq开发系列4:MIO按键中断控制LED_xgpios intr example_只是有点小怂的博客-优快云博客
先介绍后一个:void XGpioPs_SetIntrTypePin(const XGpioPs *InstancePtr, u32 Pin, u8 IrqType)
/******************************************************** **************************/
/** * * 该函数用于设置单个GPIO引脚的IRQ类型。
* * @param InstancePtr 是指向 XGpioPs 实例的指针。
* @param Pin 是要设置IRQ 类型的引脚号。
* Zynq 中的有效值为 0-117,Zynq Ultrascale+ MP 中的有效值为 0-173。
* @param IrqType 是 GPIO 引脚的 IRQ 类型。使用xgpiops.h中定义的XGPIOPS_IRQ_TYPE_* *来指定IRQ类型。
* * @返回无。
* * @note 无。
********************************************************* ****************************/
#define XGPIOPS_IRQ_TYPE_EDGE_RISING 0x00U /**< 上升沿中断 */
#define XGPIOPS_IRQ_TYPE_EDGE_FALLING 0x01U /**< 下降沿中断 */
#define XGPIOPS_IRQ_TYPE_EDGE_BOTH 0x02U /**< 双沿中断 */
#define XGPIOPS_IRQ_TYPE_LEVEL_HIGH 0x03U /**< 中断高电平 */
#define XGPIOPS_IRQ_TYPE_LEVEL_LOW 0x04U /**< 低电平中断 */
/****************************************************************************/
/**
*
* This function is used for setting the IRQ Type of a single GPIO pin.
*
* @param InstancePtr is a pointer to an XGpioPs instance.
* @param Pin is the pin number whose IRQ type is to be set.
* Valid values are 0-117 in Zynq and 0-173 in Zynq Ultrascale+ MP.
* @param IrqType is the IRQ type for GPIO Pin. Use XGPIOPS_IRQ_TYPE_*
* defined in xgpiops.h to specify the IRQ type.
*
* @return None.
*
* @note None.
*
*****************************************************************************/
void XGpioPs_SetIntrTypePin(const XGpioPs *InstancePtr, u32 Pin, u8 IrqType)
{
u32 IntrTypeReg;
u32 IntrPolReg;
u32 IntrOnAnyReg;
u8 Bank;
u8 PinNumber;
Xil_AssertVoid(InstancePtr != NULL);
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
Xil_AssertVoid(Pin < InstancePtr->MaxPinNum);
Xil_AssertVoid(IrqType <= XGPIOPS_IRQ_TYPE_LEVEL_LOW);
/* Get the Bank number and Pin number within the bank. */
#ifdef versal
XGpioPs_GetBankPin(InstancePtr,(u8)Pin, &Bank, &PinNumber);
#else
XGpioPs_GetBankPin((u8)Pin, &Bank, &PinNumber);
#endif
IntrTypeReg = XGpioPs_ReadReg(InstancePtr->GpioConfig.BaseAddr,
((u32)(Bank) * XGPIOPS_REG_MASK_OFFSET) +
XGPIOPS_INTTYPE_OFFSET);
IntrPolReg = XGpioPs_ReadReg(InstancePtr->GpioConfig.BaseAddr,
((u32)(Bank) * XGPIOPS_REG_MASK_OFFSET) +
XGPIOPS_INTPOL_OFFSET);
IntrOnAnyReg = XGpioPs_ReadReg(InstancePtr->GpioConfig.BaseAddr,
((u32)(Bank) * XGPIOPS_REG_MASK_OFFSET) +
XGPIOPS_INTANY_OFFSET);
switch (IrqType) {
case XGPIOPS_IRQ_TYPE_EDGE_RISING:
IntrTypeReg |= ((u32)1 << (u32)PinNumber);
IntrPolReg |= ((u32)1 << (u32)PinNumber);
IntrOnAnyReg &= ~((u32)1 << (u32)PinNumber);
break;
case XGPIOPS_IRQ_TYPE_EDGE_FALLING:
IntrTypeReg |= ((u32)1 << (u32)PinNumber);
IntrPolReg &= ~((u32)1 << (u32)PinNumber);
IntrOnAnyReg &= ~((u32)1 << (u32)PinNumber);
break;
case XGPIOPS_IRQ_TYPE_EDGE_BOTH:
IntrTypeReg |= ((u32)1 << (u32)PinNumber);
IntrOnAnyReg |= ((u32)1 << (u32)PinNumber);
break;
case XGPIOPS_IRQ_TYPE_LEVEL_HIGH:
IntrTypeReg &= ~((u32)1 << (u32)PinNumber);
IntrPolReg |= ((u32)1 << (u32)PinNumber);
break;
case XGPIOPS_IRQ_TYPE_LEVEL_LOW:
IntrTypeReg &= ~((u32)1 << (u32)PinNumber);
IntrPolReg &= ~((u32)1 << (u32)PinNumber);
break;
default:
/**< Default statement is added for MISRA C compliance. */
break;
}
XGpioPs_WriteReg(InstancePtr->GpioConfig.BaseAddr,
((u32)(Bank) * XGPIOPS_REG_MASK_OFFSET) +
XGPIOPS_INTTYPE_OFFSET, IntrTypeReg);
XGpioPs_WriteReg(InstancePtr->GpioConfig.BaseAddr,
((u32)(Bank) * XGPIOPS_REG_MASK_OFFSET) +
XGPIOPS_INTPOL_OFFSET, IntrPolReg);
XGpioPs_WriteReg(InstancePtr->GpioConfig.BaseAddr,
((u32)(Bank) * XGPIOPS_REG_MASK_OFFSET) +
XGPIOPS_INTANY_OFFSET, IntrOnAnyReg);
}
前一个:void XGpioPs_SetIntrType(const XGpioPs *InstancePtr, u8 Bank, u32 IntrType,u32 IntrPolarity, u32 IntrOnAny)
/******************************************************** **************************/
/** * * 该函数用于设置中断类型、中断极性和 * Interrupt On Any对于指定的 GPIO Bank 引脚。
* * @param InstancePtr 是指向 XGpioPs 实例的指针。
* @param Bank 是要操作的 GPIO 的 Bank 号。
* Zynq 中的有效值为 0-3,Zynq Ultrascale+ MP 中的有效值为 0-5。
* @param IntrType 是中断类型的 32 位掩码。
* 0 表示电平敏感,1 表示边缘敏感。
* @param IntrPolarity 是中断极性的 32 位掩码。
* 0 表示低电平有效或下降沿,1 表示高电平有效或 * 上升沿。
* @param IntrOnAny 是边沿触发中断的中断触发的 32 位掩码。
* 0 表示使用配置的中断极性在单边沿触发,1 表示在双边沿触发。
* * @返回无。
* * @note 该函数用于设置指定bank中所有引脚的中断相关属性。引脚之前的 * 状态不会保持。
* 要更改单个 GPIO 引脚的中断属性,请使用函数 XGpioPs_SetPinIntrType()。
********************************************************* ****************************/
/****************************************************************************/
/**
*
* This function is used for setting the Interrupt Type, Interrupt Polarity and
* Interrupt On Any for the specified GPIO Bank pins.
*
* @param InstancePtr is a pointer to an XGpioPs instance.
* @param Bank is the bank number of the GPIO to operate on.
* Valid values are 0-3 in Zynq and 0-5 in Zynq Ultrascale+ MP.
* @param IntrType is the 32 bit mask of the interrupt type.
* 0 means Level Sensitive and 1 means Edge Sensitive.
* @param IntrPolarity is the 32 bit mask of the interrupt polarity.
* 0 means Active Low or Falling Edge and 1 means Active High or
* Rising Edge.
* @param IntrOnAny is the 32 bit mask of the interrupt trigger for
* edge triggered interrupts. 0 means trigger on single edge using
* the configured interrupt polarity and 1 means trigger on both
* edges.
*
* @return None.
*
* @note This function is used for setting the interrupt related
* properties of all the pins in the specified bank. The previous
* state of the pins is not maintained.
* To change the Interrupt properties of a single GPIO pin, use the
* function XGpioPs_SetPinIntrType().
*
*****************************************************************************/
void XGpioPs_SetIntrType(const XGpioPs *InstancePtr, u8 Bank, u32 IntrType,
u32 IntrPolarity, u32 IntrOnAny)
{
Xil_AssertVoid(InstancePtr != NULL);
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
Xil_AssertVoid(Bank < InstancePtr->MaxBanks);
#ifdef versal
if(InstancePtr->PmcGpio == (u32)TRUE) {
Xil_AssertVoid(Bank != XGPIOPS_TWO);
} else {
Xil_AssertVoid((Bank !=XGPIOPS_ONE) && (Bank !=XGPIOPS_TWO));
}
#endif
XGpioPs_WriteReg(InstancePtr->GpioConfig.BaseAddr,
((u32)(Bank) * XGPIOPS_REG_MASK_OFFSET) +
XGPIOPS_INTTYPE_OFFSET, IntrType);
XGpioPs_WriteReg(InstancePtr->GpioConfig.BaseAddr,
((u32)(Bank) * XGPIOPS_REG_MASK_OFFSET) +
XGPIOPS_INTPOL_OFFSET, IntrPolarity);
XGpioPs_WriteReg(InstancePtr->GpioConfig.BaseAddr,
((u32)(Bank) * XGPIOPS_REG_MASK_OFFSET) +
XGPIOPS_INTANY_OFFSET, IntrOnAny);
}
步骤6:设置GPIO的回调函数XGpioPs_SetCallbackHandler()
void XGpioPs_SetCallbackHandler(XGpioPs *InstancePtr, void *CallBackRef,
XGpioPs_Handler FuncPointer)
这里的void *CallBackRef仍是需要传递GPIO的实例指针
交由回调函数XGpioPs_Handler FuncPointer进行使用。
直接回调:
如果频繁发生中断,那么函数套的太多了。故可以在XScuGic_Connect()直接设置用户的中断回调函数IntrHandler()提高效率,减少一层回调。
/******************************************************** **************************/
/** * * 该函数设置状态回调函数。当中断发生时,回调函数由 XGpioPs_IntrHandler 调用。
* * @param InstancePtr 是指向 XGpioPs 实例的指针。
* @param CallBackRef 是调用回调函数时传回的上层回调引用。
* @param FuncPointer 是回调函数的指针。
* * * @return 无。
*
* @note 处理程序是在中断上下文中调用的,因此它应该快速完成其工作,
* 并将可能耗时的工作排队到任务级线程。
********************************************************* *****************************/
/*****************************************************************************/
/**
*
* This function sets the status callback function. The callback function is
* called by the XGpioPs_IntrHandler when an interrupt occurs.
*
* @param InstancePtr is a pointer to the XGpioPs instance.
* @param CallBackRef is the upper layer callback reference passed back
* when the callback function is invoked.
* @param FuncPointer is the pointer to the callback function.
*
*
* @return None.
*
* @note The handler is called within interrupt context, so it should do
* its work quickly and queue potentially time-consuming work to a
* task-level thread.
*
******************************************************************************/
void XGpioPs_SetCallbackHandler(XGpioPs *InstancePtr, void *CallBackRef,
XGpioPs_Handler FuncPointer)
{
Xil_AssertVoid(InstancePtr != NULL);
Xil_AssertVoid(FuncPointer != NULL);
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
InstancePtr->Handler = FuncPointer;
InstancePtr->CallBackRef = CallBackRef;
}
步骤7:使能对应pin的中断XGpioPs_IntrEnable()和XGpioPs_IntrEnablePin()
void XGpioPs_IntrEnable(const XGpioPs *InstancePtr, u8 Bank, u32 Mask)
此处的u32 Mask
需要传入特定的bit位。
比如:输入引脚是EMIO,则:
1<< (Input_Bank_Pin-Base_Number)
输入引脚是MIO时:
1 << Input_Bank_Pin
XGpioPs_IntrEnable(Gpio, GPIO_BANK, (1 << Input_Bank_Pin));
也可以使用void XGpioPs_IntrEnablePin(const XGpioPs *InstancePtr, u32 Pin)来指定使能。
XGpioPs_IntrEnable
/******************************************************** **************************/
/** * * 该函数启用指定* Bank 中指定引脚的中断。
* * @param InstancePtr 是指向 XGpioPs 实例的指针。
* @param Bank 是要操作的 GPIO 的 Bank 号。
* Zynq 中的有效值为 0-3,Zynq Ultrascale+ MP 中的有效值为 0-5。
* @param Mask 是要启用中断的引脚的位掩码。位位置 1 将被启用。位位置
* 为 0 将保留先前的设置。
* * @返回无。
* * @note 无。
********************************************************* **************************/
/****************************************************************************/
/**
*
* This function enables the interrupts for the specified pins in the specified
* bank.
*
* @param InstancePtr is a pointer to the XGpioPs instance.
* @param Bank is the bank number of the GPIO to operate on.
* Valid values are 0-3 in Zynq and 0-5 in Zynq Ultrascale+ MP.
* @param Mask is the bit mask of the pins for which interrupts are to
* be enabled. Bit positions of 1 will be enabled. Bit positions
* of 0 will keep the previous setting.
*
* @return None.
*
* @note None.
*
*****************************************************************************/
void XGpioPs_IntrEnable(const XGpioPs *InstancePtr, u8 Bank, u32 Mask)
{
Xil_AssertVoid(InstancePtr != NULL);
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
Xil_AssertVoid(Bank < InstancePtr->MaxBanks);
#ifdef versal
if(InstancePtr->PmcGpio == (u32)TRUE) {
Xil_AssertVoid(Bank != XGPIOPS_TWO);
} else {
Xil_AssertVoid((Bank !=XGPIOPS_ONE) && (Bank !=XGPIOPS_TWO));
}
#endif
XGpioPs_WriteReg(InstancePtr->GpioConfig.BaseAddr,
((u32)(Bank) * XGPIOPS_REG_MASK_OFFSET) +
XGPIOPS_INTEN_OFFSET, Mask);
}
XGpioPs_IntrEnablePin
/******************************************************** **************************/
/** * * 该函数使能指定引脚的中断。
* * @param InstancePtr 是指向 XGpioPs 实例的指针。
* @param Pin 是要启用中断的引脚号。
* Zynq 中的有效值为 0-117,Zynq Ultrascale+ MP 中的有效值为 0-173。
* * @返回无。
* * @note 无。
********************************************************* **************************/
/****************************************************************************/
/**
*
* This function enables the interrupt for the specified pin.
*
* @param InstancePtr is a pointer to the XGpioPs instance.
* @param Pin is the pin number for which the interrupt is to be enabled.
* Valid values are 0-117 in Zynq and 0-173 in Zynq Ultrascale+ MP.
*
* @return None.
*
* @note None.
*
*****************************************************************************/
void XGpioPs_IntrEnablePin(const XGpioPs *InstancePtr, u32 Pin)
{
u8 Bank;
u8 PinNumber;
u32 IntrReg;
Xil_AssertVoid(InstancePtr != NULL);
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
Xil_AssertVoid(Pin < InstancePtr->MaxPinNum);
/* Get the Bank number and Pin number within the bank. */
#ifdef versal
XGpioPs_GetBankPin(InstancePtr,(u8)Pin, &Bank, &PinNumber);
#else
XGpioPs_GetBankPin((u8)Pin, &Bank, &PinNumber);
#endif
IntrReg = ((u32)1 << (u32)PinNumber);
XGpioPs_WriteReg(InstancePtr->GpioConfig.BaseAddr,
((u32)(Bank) * XGPIOPS_REG_MASK_OFFSET) +
XGPIOPS_INTEN_OFFSET, IntrReg);
}
步骤8:使能中断控制器中的gpio中断XScuGic_Enable()
void XScuGic_Enable(XScuGic *InstancePtr, u32 Int_Id)
Int_Id需要和Exception_id对应,
比如:
Exception_id传入了:
#define XIL_EXCEPTION_ID_INT XIL_EXCEPTION_ID_IRQ_INT
Int_Id就需要传入:XIL_EXCEPTION_IRQ
Exception_id传入了:
#define XIL_EXCEPTION_ID_INT XIL_EXCEPTION_ID_FIQ_INT
Int_Id就需要传入:XIL_EXCEPTION_FIQ
/******************************************************** **************************/
/** * * 启用作为参数 Int_Id 提供的中断源。
* 调用此函数后,将发生指定 Int_Id 的任何未决中断条件。
* 该 API 还将中断映射到请求的 CPU。
* * @param InstancePtr 是指向 XScuGic 实例的指针。
* @param Int_Id 包含中断源的 ID,并且应该 * 在 0 到 XSCUGIC_MAX_NUM_INTR_INPUTS - 1 的范围内 * * @return None。
* * @note 无。
********************************************************* **************************/
/*****************************************************************************/
/**
*
* Enables the interrupt source provided as the argument Int_Id. Any pending
* interrupt condition for the specified Int_Id will occur after this function is
* called.
* This API also maps the interrupt to the requesting CPU.
*
* @param InstancePtr is a pointer to the XScuGic instance.
* @param Int_Id contains the ID of the interrupt source and should be
* in the range of 0 to XSCUGIC_MAX_NUM_INTR_INPUTS - 1
*
* @return None.
*
* @note None.
*
****************************************************************************/
void XScuGic_Enable(XScuGic *InstancePtr, u32 Int_Id)
{
u32 Mask;
u8 Cpu_Identifier = (u8)CpuId;
#if defined (GICv3)
u32 Temp;
#endif
/*
* Assert the arguments
*/
Xil_AssertVoid(InstancePtr != NULL);
Xil_AssertVoid(Int_Id < XSCUGIC_MAX_NUM_INTR_INPUTS);
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
#if defined (GICv3)
if (Int_Id < XSCUGIC_SPI_INT_ID_START) {
Int_Id &= 0x1f;
Int_Id = 1 << Int_Id;
Temp = XScuGic_ReDistSGIPPIReadReg(InstancePtr,XSCUGIC_RDIST_ISENABLE_OFFSET);
Temp |= Int_Id;
XScuGic_ReDistSGIPPIWriteReg(InstancePtr,XSCUGIC_RDIST_ISENABLE_OFFSET,Temp);
return;
}
#endif
XScuGic_InterruptMaptoCpu(InstancePtr, Cpu_Identifier, Int_Id);
/*
* Call spinlock to protect multiple applications running at separate
* CPUs to write to the same register. This macro also ensures that
* the spinlock mechanism is used only if spinlock is enabled by
* user.
*/
XIL_SPINLOCK();
/*
* The Int_Id is used to create the appropriate mask for the
* desired bit position.
*/
Mask = (u32)0x00000001U << (Int_Id % 32U);
/*
* Enable the selected interrupt source by setting the
* corresponding bit in the Enable Set register.
*/
XScuGic_DistWriteReg(InstancePtr, (u32)XSCUGIC_ENABLE_SET_OFFSET +
((Int_Id / 32U) * 4U), Mask);
/*
* Release the lock previously taken. This macro ensures that the lock
* is given only if spinlock mechanism is enabled by the user.
*/
XIL_SPINUNLOCK();
}
步骤9:使能异常处理Xil_ExceptionEnableMask()
/****************************************************************************/
/**
* @brief Enable the IRQ exception.
*
* @return None.
*
* @note None.
*
******************************************************************************/
#if (defined (versal) && !defined(ARMR5) && EL3) || defined(ARMR52)
#define Xil_ExceptionEnable() \
Xil_ExceptionEnableMask(XIL_EXCEPTION_FIQ)
#else
#define Xil_ExceptionEnable() \
Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ)
#endif
其他函数:
全局中断处理回调函数:XScuGic_InterruptHandler
用于注册到CPU中作为中断处理程序,输入到Xil_ExceptionRegisterHandler()中。
/******************************************************** **************************/
/**
* 该函数是驱动程序的主要中断处理程序。它必须连接到中断源,
* 以便在中断控制器的中断处于活动状态时调用它。
* 它将解析哪些中断处于活动状态并启用,
* 并调用适当的中断处理程序。它使用
* 中断类型信息来确定何时确认中断。
* 首先处理最高优先级的中断。
* * 该函数假设中断向量表已预先初始化。
* 在调用中断处理程序之前,它不会验证表中的条目是否有效。
* * * @param InstancePtr 是指向 XScuGic 实例的指针。
* * @返回无。
* * @note 无。
********************************************************* ******************************/
/*****************************************************************************/
/**
* This function is the primary interrupt handler for the driver. It must be
* connected to the interrupt source such that it is called when an interrupt of
* the interrupt controller is active. It will resolve which interrupts are
* active and enabled and call the appropriate interrupt handler. It uses
* the Interrupt Type information to determine when to acknowledge the interrupt.
* Highest priority interrupts are serviced first.
*
* This function assumes that an interrupt vector table has been previously
* initialized. It does not verify that entries in the table are valid before
* calling an interrupt handler.
*
*
* @param InstancePtr is a pointer to the XScuGic instance.
*
* @return None.
*
* @note None.
*
******************************************************************************/
void XScuGic_InterruptHandler(XScuGic *InstancePtr)
{
u32 InterruptID;
#if !defined (GICv3)
u32 IntIDFull;
#endif
XScuGic_VectorTableEntry *TablePtr;
/* Assert that the pointer to the instance is valid
*/
Xil_AssertVoid(InstancePtr != NULL);
/*
* Read the int_ack register to identify the highest priority
* interrupt ID and make sure it is valid. Reading Int_Ack will
* clear the interrupt in the GIC.
*/
#if defined (GICv3)
InterruptID = XScuGic_get_IntID();
#else
IntIDFull = XScuGic_CPUReadReg(InstancePtr, XSCUGIC_INT_ACK_OFFSET);
InterruptID = IntIDFull & XSCUGIC_ACK_INTID_MASK;
#endif
if (XSCUGIC_MAX_NUM_INTR_INPUTS <= InterruptID) {
goto IntrExit;
}
/*
* If the interrupt is shared, do some locking here if
* there are multiple processors.
*/
/*
* If pre-eption is required:
* Re-enable pre-emption by setting the CPSR I bit for non-secure ,
* interrupts or the F bit for secure interrupts
*/
/*
* If we need to change security domains, issue a SMC
* instruction here.
*/
/*
* Execute the ISR. Jump into the Interrupt service routine
* based on the IRQSource. A software trigger is cleared by
*.the ACK.
*/
TablePtr = &(InstancePtr->Config->HandlerTable[InterruptID]);
if (TablePtr != NULL) {
TablePtr->Handler(TablePtr->CallBackRef);
}
IntrExit:
/*
* Write to the EOI register, we are all done here.
* Let this function return, the boot code will restore the stack.
*/
#if defined (GICv3)
XScuGic_ack_Int(InterruptID);
#else
XScuGic_CPUWriteReg(InstancePtr, XSCUGIC_EOI_OFFSET, IntIDFull);
#endif
/*
* Return from the interrupt. Change security domains
* could happen here.
*/
}
/** @} */
GPIO 中断的中断处理程序XGpioPs_IntrHandler
/******************************************************** ****************************/
/** *
* 该函数是 GPIO 中断的中断处理程序。它检查
* 中断状态
* 所有存储体的寄存器以确定已触发中断的实际存储体。
* 然后它调用函数XGpioPs_SetBankHandler()设置的上层回调
* 处理程序。
* 当中断时调用回调
* * @param InstancePtr 是指向 XGpioPs 实例的指针。
* * @返回无。
* * @note 此函数不会保存和恢复处理器上下文 * 因此用户必须提供此处理。
********************************************************* *****************************/
/*****************************************************************************/
/**
*
* This function is the interrupt handler for GPIO interrupts.It checks the
* interrupt status registers of all the banks to determine the actual bank in
* which an interrupt has been triggered. It then calls the upper layer callback
* handler set by the function XGpioPs_SetBankHandler(). The callback is called
* when an interrupt
*
* @param InstancePtr is a pointer to the XGpioPs instance.
*
* @return None.
*
* @note This function does not save and restore the processor context
* such that the user must provide this processing.
*
******************************************************************************/
void XGpioPs_IntrHandler(const XGpioPs *InstancePtr)
{
u8 Bank;
u32 IntrStatus;
u32 IntrEnabled;
Xil_AssertVoid(InstancePtr != NULL);
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
for (Bank = 0U; Bank < InstancePtr->MaxBanks; Bank++) {
#ifdef versal
if(InstancePtr->PmcGpio == (u32)TRUE) {
if(Bank == XGPIOPS_TWO) {
continue;
}
} else {
if((Bank == XGPIOPS_ONE) || (Bank == XGPIOPS_TWO)) {
continue;
}
}
#endif
IntrStatus = XGpioPs_IntrGetStatus(InstancePtr, Bank);
IntrEnabled = XGpioPs_IntrGetEnabled(InstancePtr,Bank);
if ((IntrStatus & IntrEnabled) != (u32)0) {
XGpioPs_IntrClear(InstancePtr, Bank,
(IntrStatus & IntrEnabled));
InstancePtr->Handler(InstancePtr->
CallBackRef, Bank,
(IntrStatus & IntrEnabled));
}
}
}
/** @} */
返回指定引脚的中断使能状态函数:XGpioPs_IntrGetStatusPin
(这个函数也有返回整个BANK中断状态的版本)
/******************************************************** **************************/ 0/** * * 该函数返回指定引脚的中断使能状态。
* * @param InstancePtr 是指向 XGpioPs 实例的指针。
* @param Pin 是要知道中断启用状态的引脚号。
* Zynq 中的有效值为 0-117,Zynq Ultrascale+ MP 中的有效值为 0-173。
* * @return * - 如果发生中断则为 TRUE。
* - 如果中断未发生,则为 FALSE。
* * @note 无。
********************************************************* **************************/
/****************************************************************************/
/**
*
* This function returns interrupt enable status of the specified pin.
*
* @param InstancePtr is a pointer to the XGpioPs instance.
* @param Pin is the pin number for which the interrupt enable status
* is to be known.
* Valid values are 0-117 in Zynq and 0-173 in Zynq Ultrascale+ MP.
*
* @return
* - TRUE if the interrupt has occurred.
* - FALSE if the interrupt has not occurred.
*
* @note None.
*
*****************************************************************************/
u32 XGpioPs_IntrGetStatusPin(const XGpioPs *InstancePtr, u32 Pin)
{
u8 Bank;
u8 PinNumber;
u32 IntrReg;
Xil_AssertNonvoid(InstancePtr != NULL);
Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
Xil_AssertNonvoid(Pin < InstancePtr->MaxPinNum);
/* Get the Bank number and Pin number within the bank. */
#ifdef versal
XGpioPs_GetBankPin(InstancePtr,(u8)Pin, &Bank, &PinNumber);
#else
XGpioPs_GetBankPin((u8)Pin, &Bank, &PinNumber);
#endif
IntrReg = XGpioPs_ReadReg(InstancePtr->GpioConfig.BaseAddr,
((u32)(Bank) * XGPIOPS_REG_MASK_OFFSET) +
XGPIOPS_INTSTS_OFFSET);
return (((IntrReg & ((u32)1 << PinNumber)) != (u32)0)? (u32)TRUE : (u32)FALSE);
}
清除挂起中断函数XGpioPs_IntrClearPin
/******************************************************** **************************/
/** * * 该函数清除指定的挂起中断。该函数应该在软件处理完待处理的中断后调用。
* * @param InstancePtr 是指向 XGpioPs 实例的指针。
* @param Pin 是要清除中断状态的引脚号。
* Zynq 中的有效值为 0-117,
* Zynq Ultrascale+ MP 中的有效值为 0-173。
* * @note 无。
********************************************************* **************************/
/****************************************************************************/
/**
*
* This function clears the specified pending interrupt. This function should be
* called after the software has serviced the interrupts that are pending.
*
* @param InstancePtr is a pointer to the XGpioPs instance.
* @param Pin is the pin number for which the interrupt status is to be
* cleared. Valid values are 0-117 in Zynq and 0-173 in Zynq Ultrascale+ MP.
*
* @note None.
*
*****************************************************************************/
void XGpioPs_IntrClearPin(const XGpioPs *InstancePtr, u32 Pin)
{
u8 Bank;
u8 PinNumber;
u32 IntrReg;
Xil_AssertVoid(InstancePtr != NULL);
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
Xil_AssertVoid(Pin < InstancePtr->MaxPinNum);
/* Get the Bank number and Pin number within the bank. */
#ifdef versal
XGpioPs_GetBankPin(InstancePtr,(u8)Pin, &Bank, &PinNumber);
#else
XGpioPs_GetBankPin((u8)Pin, &Bank, &PinNumber);
#endif
/* Clear the specified pending interrupts. */
IntrReg = XGpioPs_ReadReg(InstancePtr->GpioConfig.BaseAddr,
((u32)(Bank) * XGPIOPS_REG_MASK_OFFSET) +
XGPIOPS_INTSTS_OFFSET);
IntrReg &= ((u32)1 << PinNumber);
XGpioPs_WriteReg(InstancePtr->GpioConfig.BaseAddr,
((u32)(Bank) * XGPIOPS_REG_MASK_OFFSET) +
XGPIOPS_INTSTS_OFFSET, IntrReg);
}
禁用指定引脚的中断的函数XGpioPs_IntrDisablePin
/******************************************************** **************************/
/** * * 该函数禁用指定引脚的中断。
* * @param InstancePtr 是指向 XGpioPs 实例的指针。
* @param Pin 是要禁用中断的引脚号。
* Zynq 中的有效值为 0-117,Zynq Ultrascale+ MP 中的有效值为 0-173。
* * @返回无。
* * @note 无。
********************************************************* ****************************/
/****************************************************************************/
/**
*
* This function disables the interrupts for the specified pin.
*
* @param InstancePtr is a pointer to the XGpioPs instance.
* @param Pin is the pin number for which the interrupt is to be disabled.
* Valid values are 0-117 in Zynq and 0-173 in Zynq Ultrascale+ MP.
*
* @return None.
*
* @note None.
*
*****************************************************************************/
void XGpioPs_IntrDisablePin(const XGpioPs *InstancePtr, u32 Pin)
{
u8 Bank;
u8 PinNumber;
u32 IntrReg;
Xil_AssertVoid(InstancePtr != NULL);
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
Xil_AssertVoid(Pin < InstancePtr->MaxPinNum);
/* Get the Bank number and Pin number within the bank. */
#ifdef versal
XGpioPs_GetBankPin(InstancePtr,(u8)Pin, &Bank, &PinNumber);
#else
XGpioPs_GetBankPin((u8)Pin, &Bank, &PinNumber);
#endif
IntrReg = ((u32)1 << (u32)PinNumber);
XGpioPs_WriteReg(InstancePtr->GpioConfig.BaseAddr,
((u32)(Bank) * XGPIOPS_REG_MASK_OFFSET) +
XGPIOPS_INTDIS_OFFSET, IntrReg);
}
使能指定引脚的中断函数XGpioPs_IntrEnablePin
/******************************************************** **************************/
/** * * 该函数使能指定引脚的中断。
* * @param InstancePtr 是指向 XGpioPs 实例的指针。
* @param Pin 是要启用中断的引脚号。
* Zynq 中的有效值为 0-117,Zynq Ultrascale+ MP 中的有效值为 0-173。
* * @返回无。
* * @note 无。
********************************************************* ****************************/
/****************************************************************************/
/**
*
* This function enables the interrupt for the specified pin.
*
* @param InstancePtr is a pointer to the XGpioPs instance.
* @param Pin is the pin number for which the interrupt is to be enabled.
* Valid values are 0-117 in Zynq and 0-173 in Zynq Ultrascale+ MP.
*
* @return None.
*
* @note None.
*
*****************************************************************************/
void XGpioPs_IntrEnablePin(const XGpioPs *InstancePtr, u32 Pin)
{
u8 Bank;
u8 PinNumber;
u32 IntrReg;
Xil_AssertVoid(InstancePtr != NULL);
Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);
Xil_AssertVoid(Pin < InstancePtr->MaxPinNum);
/* Get the Bank number and Pin number within the bank. */
#ifdef versal
XGpioPs_GetBankPin(InstancePtr,(u8)Pin, &Bank, &PinNumber);
#else
XGpioPs_GetBankPin((u8)Pin, &Bank, &PinNumber);
#endif
IntrReg = ((u32)1 << (u32)PinNumber);
XGpioPs_WriteReg(InstancePtr->GpioConfig.BaseAddr,
((u32)(Bank) * XGPIOPS_REG_MASK_OFFSET) +
XGPIOPS_INTEN_OFFSET, IntrReg);
}
例程
/******************************************************************************
* Copyright (C) 2010 - 2021 Xilinx, Inc. All rights reserved.
* SPDX-License-Identifier: MIT
******************************************************************************/
/*****************************************************************************/
/**
* @file xgpiops_intr_example.c
*
* This file contains a design example using the GPIO driver (XGpioPs) in an
* interrupt driven mode of operation.
*
* The example uses the interrupt capability of the GPIO to detect push button
* events and set the output LEDs based on the input.
*
* @note
* This example assumes that there is a Uart device in the HW design.
* This example is to provide support only for zcu102 on ZynqMp Platform and
* only for zc702 on Zynq Platform.
* For ZynqMP Platform, Input pin is 22(sw19 on zcu102 board) and Output Pin is
* 23(DS50 on zcu102 board).
* For Zynq Platform, Input Pins are 12(sw14 on zc702 board), 14(sw13 on
* zc702 board) and Output Pin is 10(DS23 on zc702 board). SW15 on zc702 board
* is a combination of sw13 and sw14. To operate either of the input
* pins, keep SW15 low(both should be 00).
* This example supports the VCK190 and VMK180 for Versal, but requires a PL
* shim. See Answer Record AR# 75677 for details.
* On the Versal Platform we have two GPIOPS instances :PMC GPIO and PS GPIO
* PMC GPIO contain 4 banks and 116 pins, organized as follows:
* Bank 0 I/Os: 25:0 (MIO)
* Bank 1: I/Os: 51:26 (MIO)
* Bank 3: I/Os: 83:52 (EMIO)
* Bank 4: I/Os: 115:84 (EMIO)
* PS GPIO contains 2 banks and 58 pins
* Bank 0 I/Os: 25:0 (MIO)
* Bank 3: I/Os: 57:26 (EMIO)
* See Figure 61 in AM011 Versal TRM for details.
* Driver supports both PS GPIO and PMC GPIO.
* For accessing PMC GPIOs application you need to set "GPIO.PmcGpio = 1"
* otherwise it accesses PS GPIO.
*
* <pre>
* MODIFICATION HISTORY:
*
* Ver Who Date Changes
* ----- ---- -------- -----------------------------------------------
* 1.00a sv 01/18/10 First Release
* 3.3 ms 04/17/17 Added notes about gpio input and output pin description
* for zcu102 and zc702 boards, configured Interrupt pin
* to input pin for proper working of interrupt example.
* 3.7 sne 12/04/19 Reverted versal example support.
* 3.8 sne 09/17/20 Added description for Versal PS and PMC GPIO pins.
* 3.9 sne 11/19/20 Added versal PmcGpio example support.
*
*</pre>
*
******************************************************************************/
/***************************** Include Files *********************************/
#include "xparameters.h"
#include "xgpiops.h"
#include "xscugic.h"
#include "xil_exception.h"
#include "xplatform_info.h"
#include <xil_printf.h>
/************************** Constant Definitions *****************************/
/*
* The following constants map to the names of the hardware instances that
* were created in the EDK XPS system. They are defined here such that
* the user can easily change all the needed device IDs in one place.
*/
#define GPIO_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
#ifdef versal
#define GPIO_INTERRUPT_ID XPMC_GPIO_INT_ID
#else
#define GPIO_INTERRUPT_ID XPAR_XGPIOPS_0_INTR
#endif
/* The following constants define the GPIO banks that are used. */
#ifdef versal
#define GPIO_BANK XGPIOPS_BANK3 /* Bank 3 of the GPIO Device */
#else
#define GPIO_BANK XGPIOPS_BANK0 /* Bank 0 of the GPIO Device */
#endif
/**************************** Type Definitions *******************************/
/***************** Macros (Inline Functions) Definitions *********************/
/************************** Function Prototypes ******************************/
static int GpioIntrExample(XScuGic *Intc, XGpioPs *Gpio, u16 DeviceId,
u16 GpioIntrId);
static void IntrHandler(void *CallBackRef, u32 Bank, u32 Status);
static int SetupInterruptSystem(XScuGic *Intc, XGpioPs *Gpio, u16 GpioIntrId);
/************************** Variable Definitions *****************************/
/*
* The following are declared globally so they are zeroed and so they are
* easily accessible from a debugger.
*/
static XGpioPs Gpio; /* The Instance of the GPIO Driver */
static XScuGic Intc; /* The Instance of the Interrupt Controller Driver */
static u32 AllButtonsPressed; /* Intr status of the bank */
static u32 Input_Bank_Pin; /* Pin Number within Bank */
static u32 Input_Pin; /* Switch button */
static u32 Output_Pin; /* LED button */
/****************************************************************************/
/**
*
* Main function that invokes the GPIO Interrupt example.
*
*
* @return
* - XST_SUCCESS if the example has completed successfully.
* - XST_FAILURE if the example has failed.
*
* @note None.
*
*****************************************************************************/
int main(void)
{
int Status;
xil_printf("GPIO Interrupt Example Test \r\n");
/*
* Run the GPIO interrupt example, specify the parameters that
* are generated in xparameters.h.
*/
Status = GpioIntrExample(&Intc, &Gpio, GPIO_DEVICE_ID,
GPIO_INTERRUPT_ID);
if (Status != XST_SUCCESS) {
xil_printf("GPIO Interrupt Example Test Failed\r\n");
return XST_FAILURE;
}
xil_printf("Successfully ran GPIO Interrupt Example Test\r\n");
return XST_SUCCESS;
}
/****************************************************************************/
/**
* This function shows the usage of interrupt fucntionality of the GPIO device.
* It is responsible for initializing the GPIO device, setting up interrupts and
* providing a foreground loop such that interrupts can occur in the background.
*
* @param Intc is a pointer to the XScuGic driver Instance.
* @param Gpio is a pointer to the XGpioPs driver Instance.
* @param DeviceId is the XPAR_<Gpio_Instance>_PS_DEVICE_ID value
* from xparameters.h.
* @param GpioIntrId is XPAR_<GIC>_<GPIO_Instance>_VEC_ID value
* from xparameters.h
*
* @return
* - XST_SUCCESS if the example has completed successfully.
* - XST_FAILURE if the example has failed.
*
* @note None
*
*****************************************************************************/
int GpioIntrExample(XScuGic *Intc, XGpioPs *Gpio, u16 DeviceId, u16 GpioIntrId)
{
XGpioPs_Config *ConfigPtr;
int Status;
int Type_of_board;
/* Initialize the Gpio driver. */
ConfigPtr = XGpioPs_LookupConfig(DeviceId);
if (ConfigPtr == NULL) {
return XST_FAILURE;
}
Type_of_board = XGetPlatform_Info();
switch (Type_of_board) {
case XPLAT_ZYNQ_ULTRA_MP:
Input_Bank_Pin = 22;
Input_Pin = 22;
Output_Pin = 23;
break;
case XPLAT_ZYNQ:
Input_Bank_Pin = 14;
Input_Pin = 14;
Output_Pin = 10;
break;
#ifdef versal
case XPLAT_VERSAL:
/* Accessing PMC GPIO by setting field to 1 */
Gpio->PmcGpio = 1;
Input_Bank_Pin = 4;
Input_Pin = 56;
Output_Pin = 52;
break;
#endif
}
XGpioPs_CfgInitialize(Gpio, ConfigPtr, ConfigPtr->BaseAddr);
/* Run a self-test on the GPIO device. */
Status = XGpioPs_SelfTest(Gpio);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
/* Set the direction for the specified pin to be input */
XGpioPs_SetDirectionPin(Gpio, Input_Pin, 0x0);
/* Set the direction for the specified pin to be output. */
XGpioPs_SetDirectionPin(Gpio, Output_Pin, 1);
XGpioPs_SetOutputEnablePin(Gpio, Output_Pin, 1);
XGpioPs_WritePin(Gpio, Output_Pin, 0x0);
/*
* Setup the interrupts such that interrupt processing can occur. If
* an error occurs then exit.
*/
Status = SetupInterruptSystem(Intc, Gpio, GPIO_INTERRUPT_ID);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
xil_printf("\n\rPush Switch button to exit\n\r");
AllButtonsPressed = FALSE;
/*
* Loop forever while the button changes are handled by the interrupt
* level processing.
*/
while(AllButtonsPressed == FALSE);
return XST_SUCCESS;
}
/****************************************************************************/
/**
* This function is the user layer callback function for the bank 0 interrupts of
* the GPIO device. It checks if all the switches have been pressed to stop the
* interrupt processing and exit from the example.
*
* @param CallBackRef is a pointer to the upper layer callback reference.
* @param Status is the Interrupt status of the GPIO bank.
*
* @return None.
*
* @note None.
*
******************************************************************************/
static void IntrHandler(void *CallBackRef, u32 Bank, u32 Status)
{
XGpioPs *Gpio = (XGpioPs *)CallBackRef;
u32 DataRead;
/* Push the switch button */
DataRead = XGpioPs_ReadPin(Gpio, Input_Pin);
if (DataRead != 0) {
XGpioPs_SetDirectionPin(Gpio, Output_Pin, 1);
XGpioPs_SetOutputEnablePin(Gpio, Output_Pin, 1);
XGpioPs_WritePin(Gpio, Output_Pin, DataRead);
AllButtonsPressed = TRUE;
}
}
/*****************************************************************************/
/**
*
* This function sets up the interrupt system for the example. It enables falling
* edge interrupts for all the pins of bank 0 in the GPIO device.
*
* @param GicInstancePtr is a pointer to the XScuGic driver Instance.
* @param GpioInstancePtr contains a pointer to the instance of the GPIO
* component which is going to be connected to the interrupt
* controller.
* @param GpioIntrId is the interrupt Id and is typically
* XPAR_<GICPS>_<GPIOPS_instance>_VEC_ID value from
* xparameters.h.
*
* @return XST_SUCCESS if successful, otherwise XST_FAILURE.
*
* @note None.
*
****************************************************************************/
static int SetupInterruptSystem(XScuGic *GicInstancePtr, XGpioPs *Gpio,
u16 GpioIntrId)
{
int Status;
XScuGic_Config *IntcConfig; /* Instance of the interrupt controller */
Xil_ExceptionInit();
/*
* Initialize the interrupt controller driver so that it is ready to
* use.
*/
IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
if (NULL == IntcConfig) {
return XST_FAILURE;
}
Status = XScuGic_CfgInitialize(GicInstancePtr, IntcConfig,
IntcConfig->CpuBaseAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
/*
* Connect the interrupt controller interrupt handler to the hardware
* interrupt handling logic in the processor.
*/
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler,
GicInstancePtr);
/*
* Connect the device driver handler that will be called when an
* interrupt for the device occurs, the handler defined above performs
* the specific interrupt processing for the device.
*/
Status = XScuGic_Connect(GicInstancePtr, GpioIntrId,
(Xil_ExceptionHandler)XGpioPs_IntrHandler,
(void *)Gpio);
if (Status != XST_SUCCESS) {
return Status;
}
/* Enable falling edge interrupts for all the pins in GPIO bank. */
XGpioPs_SetIntrType(Gpio, GPIO_BANK, 0x00, 0xFFFFFFFF, 0x00);
/* Set the handler for gpio interrupts. */
XGpioPs_SetCallbackHandler(Gpio, (void *)Gpio, IntrHandler);
/* Enable the GPIO interrupts of GPIO Bank. */
XGpioPs_IntrEnable(Gpio, GPIO_BANK, (1 << Input_Bank_Pin));
/* Enable the interrupt for the GPIO device. */
XScuGic_Enable(GicInstancePtr, GpioIntrId);
/* Enable interrupts in the Processor. */
Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);
return XST_SUCCESS;
}