zynq的spi中断设置——以GPIO中断为例
文章目录
前言
1.采用器件
XC7Z010CLG400
2.参考的手册
- 《Zynq-7000 All Programmable SoC Technical Reference Manual UG585 (v1.10) February 23, 2015》
- 《Xilinx Zynq -7000嵌人式系统设计与实现 基于ARMCortex-A9双核处理器和Vivado的设计方法》
3.前置知识
3.1 ARM的中断处理机制
3.1.1 参考资料
ARM的中断处理机制,处理硬件请求,终于有人讲明白了……——https://www.bilibili.com/video/BV14e4y1h7Ms/?spm_id_from=333.337.search-card.all.click&vd_source=4a58493d16f50229c3283d2be2c272ba
3.1.2 概念梳理
什么是异常和arm的异常处理机制
2. 异常源有哪些?
3. arm的异常模式(它和异常源不一样)
4. arm的异常响应
5. 异常向量表
6. IRQ异常举例
3.2 指针加1和C语言指针强制类型转换
3.2.1 参考资料
- 指针+1的问题 https://blog.csdn.net/wade23/article/details/4546119?spm=1001.2014.3001.5506
- C语言指针强制类型转换
2.1 C语言指针强制类型转换 https://blog.csdn.net/mhjcumt/article/details/7355127
2.2 浅谈c语言指针的强制转换 https://blog.csdn.net/qq_46284844/article/details/124274127?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-2-124274127-blog-7355127.235%5Ev39%5Epc_relevant_default_base&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-2-124274127-blog-7355127.235%5Ev39%5Epc_relevant_default_base&utm_relevant_index=5
2.3 函数指针的强制类型转换与void指针 https://blog.csdn.net/mickey35/article/details/80756348?spm=1001.2101.3001.6650.17&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-17-80756348-blog-124274127.235%5Ev39%5Epc_relevant_default_base&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-17-80756348-blog-124274127.235%5Ev39%5Epc_relevant_default_base&utm_relevant_index=25
3.2.2 概念梳理
- cpu中一个地址存储一个byte
char类型,一个值a[x]是占8bit一个byte cpu中一个地址深度为一个byte,能存储8bit数,所以
-
如3.2.1 参考资料中2.1所说点击跳转强制类型转换就是按照某个变量的类型取出该变量的值,再按照xxx to xxx的规则进行强制转转换。
-
指针 + 1 并不是指针代表的地址值 + 1。指针变量加1,即向后移动1 个位置表示指针变量指向下一个数据元素的首地址。而不是在原地址基础上加1。至于真实的地址加了多少,要看原来指针指向的数据类型是什么。点击跳转
例如:
char a[4] = {1, 2, 3 ,4};
int *p = (int*) a;
int y1;
int y2;
int y3;
int y4;
y1 = p;
y2 = p+1;
y3 = p+2;
y4 = p+3;
a为数组的首地址&a[0],为char型的指针,(int*) 强制类型转换为int型指针赋值给p,p的值仍然是数组的首地址&a[0],但是其为int型指针,指向的是int数据类型,所以p+1的值较于p的值来说多了4,是因为1个int变量其占4个字节,cpu中一个地址存储1个byte,所以多了4.
3.3 函数指针和指针函数
3.3.1 参考资料
- 函数指针和指针函数用法和区别 https://blog.csdn.net/luoyayun361/article/details/80428882?spm=1001.2014.3001.5506
3.3.2 概念梳理
指针函数本质是一个函数,其返回值为指针。
函数指针本质是一个指针,其指向一个函数。
3.4 注册函数、回调函数、handle和handler
3.4.1 参考资料
- C语言——深入浅出回调函数 https://blog.csdn.net/u011486738/article/details/115005173
- 3分钟理解回调函数(小白扫盲篇) https://zhuanlan.zhihu.com/p/631248309
- 闲话handle和handler https://www.cnblogs.com/idorax/p/6414007.html
- C 语言句柄handle https://blog.csdn.net/Godsight/article/details/52738175?spm=1001.2101.3001.6650.3&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-3-52738175-blog-80600593.235%5Ev39%5Epc_relevant_default_base&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-3-52738175-blog-80600593.235%5Ev39%5Epc_relevant_default_base&utm_relevant_index=6
3.4.2 概念梳理
我的个人理解:
首先如3.4.1 参考资料中1.所说点击跳转,回调函数就是一个通过函数指针进行调用的函数(其实回调函数只是一个功能函数而已)。当你把函数名(即函数的地址)作为参数传递给另外一个函数(即注册函数),向注册函数进行回调函数的注册时,注册函数通过函数指针来调用该函数指针所指向的函数时,我们就说这是在回调函数,被函数指针指向的函数即称为回调函数(回调函数其实只是一个名字而已,不需深究,叫被调函数也可以)。
handler(A handler is an asynchronous callback subroutine)就是注册函数中指向回调函数的函数指针
handle(A handle is an abstract reference to a resource)是对某个资源的抽象引用。handle也叫句柄,指针可以当句柄,句柄不一定都是指针,如同3.4.1 参考资料里面的4.所说点击跳转
3.5 ZYNQ-7000 GPIO的MIO和EMIO
3.5.1 参考资料
- ZYNQ-7000 GPIO使用 https://blog.csdn.net/asd1147170607/article/details/108105770
3.5.2 概念梳理
GPIO(Generous Purpose Input Output)是指CPU引出的,可以配置为输入或输出的端口,用于CPU与外界进行数据的传输。ZYNQ-7000 架构由 PL+PS 组成,所以它的 GPIO 与一般的 ARM 不同。ZYNQ 的 GPIO,分为两种,MIO(multiuse I/O)和 EMIO(extendable multiuse I/O)。MIO 有54个,分配在 GPIO 的 Bank0 和 Bank1 隶属于 PS 部分,这些 IO 与 PS 直接相连。不需要添加引脚约束,MIO 信号对 PL 部分是透明的,不可见。EMIO有64个,EMIO连接PS到PL,作为ZYNQ的拓展MIO,当 MIO 不够用时,PS 可以通过驱动 EMIO 控制 PL 部分的引脚。因此使用PS的EMIO时,需要添加PL引脚约束。
本博客后面中断设置用到EMIO
3.6 ZYNQ 中断子系统
3.6.1 参考资料
- ZYNQ 中断子系统 https://blog.csdn.net/zhoutaopower/article/details/106103266
- Zynq-PS-SDK(4) 之 GIC 配置 https://blog.csdn.net/zhoutaopower/article/details/115113160
- Linux 中断 —— ARM GIC 中断控制器 https://blog.csdn.net/zhoutaopower/article/details/90454258
3.6.2 概念梳理
如3.6.1 参考资料的2.描述——Zynq 的中断控制,由 GIC 来控制,UG585 中已经描述,GIC 的配置不用 ps_init 中来进行配置,而是在 SDK 使用到的时候,调用SDK的GIC相关的API来进行配置;GIC的中断类型分为:SGI、PPI 和 SPI;
3.6.1 参考资料1. 中描述——ZYNQ-7000 带双核 Cortex-A9 处理器的 7020 芯片,PS 端集成的是 GIC 中断控制器;GIC 是通用中断控制器(Generic Interrupt Controller)的简称。GIC 的版本是 pl390,也就是 GIC 的 v1 版本
GPIO中断的中断设置
1. 参考资料
Zynq-PS-SDK(4) 之 GIC 配置 https://blog.csdn.net/zhoutaopower/article/details/115113160
2. 总体概览和重点函数
可以看到比较核心的就是3个桥梁函数
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,&Gic);
Status = XScuGic_Connect(&Gic, GPIOINTR_ID,(Xil_InterruptHandler)XGpioPs_IntrHandler,(void *)&Gpio);
XGpioPs_SetCallbackHandler(&Gpio, (void* )&Gpio, (XGpioPs_Handler)UserDefKeyInterupt);
为什么要这么填,现在进行一个解释
2.1 Xil_ExceptionRegisterHandler()
arm利用Xil_ExceptionRegisterHandler()函数根据具体的异常id u32 Exception_id(异常源有很多个,中断只是其中一种,具体看前置知识点击跳转
),来执行对应的异常处理程序,
在vitis里面打开他的定义如下:
/*****************************************************************************/
/**
* @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 shouldbe 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
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;
}
2.1.1怎么填u32 Exception_id
Exception_id contains the ID of the exception source and shouldbe in the range of 0 to XIL_EXCEPTION_ID_LAST.See xil_exception.h for further information.
在Cortex-A 系列中异常源id如下:
#define XIL_EXCEPTION_ID_RESET 0U
#define XIL_EXCEPTION_ID_UNDEFINED_INT 1U
#define XIL_EXCEPTION_ID_SWI_INT 2U
#define XIL_EXCEPTION_ID_PREFETCH_ABORT_INT 3U
#define XIL_EXCEPTION_ID_DATA_ABORT_INT 4U
#define XIL_EXCEPTION_ID_IRQ_INT 5U
#define XIL_EXCEPTION_ID_FIQ_INT 6U
我们的异常源gpio中断是irq的中断所以填 XIL_EXCEPTION_ID_IRQ_INT
2.1.2怎么填Xil_ExceptionHandler Handler
Handler to the Handler for that exception.
Xil_ExceptionHandler是个函数指针的类型
typedef void (*Xil_ExceptionHandler)(void *data);
Gic向CPU注册异常处理回调函数,“Handler”填XScuGic_InterruptHandler,它是由arm的scu提供的指向Gic的异常处理回调函数的函数指针。
2.1.3怎么填void *Data
Data is a reference to Data that will be passed to the Handler when it gets called.
当回调的时候,void *Data会传递给XScuGic_InterruptHandler,这个void *Data要填Gic实体的指针&Gic,以连接gic层和arm,如图点击跳转
2.2 XScuGic_Connect()
Gic利用XScuGic_Connect()函数根据具体的中断号u32 Int_Id(中断的类型有多种,gpio是其中一种),连接GPIO中断信号,来执行对应的 ISR(中断服务程序)如图点击跳转
在vitis里面打开他的定义如下:
/*****************************************************************************/
/**
*
* 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;
}
2.2.1怎么填XScuGic *InstancePtr
InstancePtr is a pointer to the XScuGic instance.
XScuGic_Connect()函数要连接Gic层和Gpio层,所以需要Gic实体的指针,所以填&Gic
2.2.2怎么填u32 Int_Id
这里要填写中断类型,中断类型由中断号决定,我们设置的中断是spi共享中断下的iop类别中的gpio中断如下图,中断号为52,zynq的中断子系统看这里点击跳转
2.2.3怎么填Xil_InterruptHandler Handler
Handler to the handler for that interrupt.
Xil_InterruptHandler为函数指针的类型
typedef void (*Xil_InterruptHandler)(void *data);
GPIO向Gic注册中断处理回调函数,“Handler”填XGpioPs_IntrHandler,它是由Gic提供的指向Gpio中的中断处理回调函数的指针。
2.2.4怎么填void *CallBackRef
CallBackRef is the callback reference, usually the instance pointer of the connecting driver.
XScuGic_Connect()函数要连接Gic层和Gpio层,现在有了Gic实体的指针,需要Gpio实体的指针,所以填&Gpio,该指针要传递给XGpioPs_IntrHandler
2.3 XGpioPs_SetCallbackHandler()
Gpio 层 利用 XGpioPs_SetCallbackHandler()通过FuncPointer连接用户自定义的gpio中断函数
在vitis里面打开他的定义如下:
/*****************************************************************************/
/**
* 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;
}
2.3.1怎么填XGpioPs *InstancePtr
InstancePtr is a pointer to the XGpioPs instance.
XGpioPs_SetCallbackHandler()函数要连接Gpio层和用户自定义函数层,所以需要Gpio实体的指针,所以填&Gpio
2.3.2怎么填void *CallBackRef
CallBackRef is the upper layer callback reference passed back when the callback function is invoked.
gpio的上层为gic层,gic层的CallBackRef为Gpio实体的指针,所以填写(void* )&Gpio,该CallBackRef会传给FuncPointer,因为用户自定义的函数需要对这个Gpio实体进行操作
2.3.3怎么填XGpioPs_Handler FuncPointer
FuncPointer is the pointer to the callback function.
XGpioPs_Handler是一个函数指针类型
typedef void (*XGpioPs_Handler) (void *CallBackRef, u32 Bank, u32 Status);
用户层向Gpio层注册用户自定义gpio中断处理回调函数,“FuncPointer”填用户自定义gpio中断处理回调函数的指针,它指向用户自定义gpio中断处理回调函数。
3. 具体流程
具体流程如下图所示
讲一下第2步gic实体的初始化,这篇文章说的很清楚《Zynq-PS-SDK(4) 之 GIC 配置》点击跳转
第5步中断设置,注意我们bank是emio的bank2,其他的配置结合具体的中断来编写
4. 注意的点
4.1 几个id
4.2 CallBackRef
因为都是对同一个Gpio实体进行操作