Zynq中 PS接收PL中断
参考:
https://blog.csdn.net/RZJMPB/article/details/50736918
https://www.cnblogs.com/milinker/p/5906004.html
http://www.osrc.cn/forum.php?mod=viewthread&tid=1255
http://www.osrc.cn/forum.php?mod=viewthread&tid=1097
https://blog.csdn.net/u014485485/article/details/79059095
https://blog.csdn.net/sheng__jun/article/details/78213115 //中断寄存器相关
米联手册以及UG585手册。
实验中搭建了基于ZC706开发板的实验工程,在PL端通过按键产生中断,PS接受到之后点亮相应的LED。主要学习了在外设(PL)产生的中断请求,在PS端进行处理。
一、搭建工程
具体实验就不在这里展开了,搭建参考米联手册《S02_CH07_ ZYNQ PL 中断请求》。
这里记录下重点步骤:
1)选择开发型号(Zc706)、设置串口(uart1)、设置电压(1.8,1.8)、设置时钟(默认配置)
2)设置内存型号
3)使能中断(特别是PL到PS的中断)
4)添加按键IP核并配置(记得中断)
5)添加LED的IP核并配置
6)之后就是手动连接中断线、自动布局;
验证设计(F6)、产生输出generate output files、产生wrap文件、产生约束文件
三部曲仿真、执行、生成bit文件;启动SDK,进行PS端开发。
二、zynq中断学习
由上图可知,zynq的中断分为三种:
1.软件中断(SGI,Software generatedinterrupts,中断号0-15)(16–26 reserved) :被路由到一个或者两个CPU上,通过写ICDSGIR寄存器产生SGI.
2.私有外设中断(PPI,private peripheralinterrupts ,中断号27-31):每个CPU都有一组PPI,包括全局定时器、私有看门狗定时器、私有定时器和来自PL的FIQ/IRQ.
3.共享外设中断(SPI,shared peripheralinterrupts,中断号32-95):由PS和PL上的各种I/O控制器和存储器控制器产生,这些中断信号被路由到相应的CPU.
中断控制器(GIC,generic interrupt controller ):用于集中管理从PS和PL产生的中断信号的资源集合。控制器可以使能、关使能、屏蔽中断源和改变中断源的优先级,并且会将中断送到对应的CPU中,CPU通过私有总线访问这些寄存器。
从下面的表格中可以看到中断向量的具体值。PL 到 PS 部分一共有 20 个中断可以使用。其中 4 个是快速中断。
ZYNQ 2 个 CPU 都具备各自 16 个软件中断。
ZYNQ CPU 私有端口中断,这些中断都是固定死的,不能修改。
ZYNQ PS 和 PL 共享中断
共享中断就是一些端口共用一个中断请求线, PL部分有16个共享中断,他们的触发方式可以设置:
这里也可以看到中断号是从61开始的。
二、SDK逻辑代码分析
中断处理基本流程:(具体函数参考 SCUGIC API)
1. 中断初始化
2. 调用中断建立函数
3. Xilinx中断触发使能
4. 连接中断操作函数
5. 配置中断触发信号
6. 使能中断控制器
其中,寄存器 地址 中断号
ICDICFR0 0xF8F01C00 #0-#15
ICDICFR1 0xF8F01C04 #27-#31(16-26保留)
ICDICFR2 0xF8F01C08 #32-#47(36保留)
ICDICFR3 0xF8F01C0C #48-#63
ICDICFR4 0xF8F01C10 #64-#79
ICDICFR5 0xF8F01C14 #80-#95(93/94/95保留)
该代码在PL端通过按键产生中断,PS接受到之后点亮相应的LED.
#include "xparameters.h"
#include "xscugic.h"
#include "xil_exception.h"
#include "xgpio.h"
#include <stdio.h>
// Parameter definitions
#define INTC_DEVICE_ID XPAR_PS7_SCUGIC_0_DEVICE_ID //中断基地址设备ID
#define LED_DEVICE_ID XPAR_LED_4BITS_DEVICE_ID
#define BTNS_DEVICE_ID XPAR_SWS_3BITS_DEVICE_ID
#define INTC_GPIO_INTERRUPT_ID XPAR_FABRIC_SWS_3BITS_IP2INTC_IRPT_INTR
#define BTN_INT XGPIO_IR_CH1_MASK // This is the interrupt mask for channel one
#define DELAY 100000000
XGpio LEDInst;
XGpio BTNInst;
XScuGic INTCInst;
static int btn_value; //按键值
//----------------------------------------------------
// 函数声明
//----------------------------------------------------
static void BTN_Intr_Handler(void *baseaddr_p);
static int InterruptSystemSetup(XScuGic *XScuGicInstancePtr);
static int IntcInitFunction(u16 DeviceId, XGpio *GpioInstancePtr);
#define INT_CFG0_OFFSET 0x00000C00
#define INT_TYPE_RISING_EDGE 0x03
#define INT_TYPE_HIGHLEVEL 0x01
#define INT_TYPE_MASK 0x03
/*
* //Xil_Out32(((((InstancePtr)->Config->DistBaseAddress)) +
* ((0x00000C00 + (intId/16)*4))), ((u32)(((u32)(mask)))))
* 此时,我们就知道了第一个参数是一个指向要处理的中断的指针:((InstancePtr)->Config->DistBaseAddress))
* 在xparameters.h中,找到了中断的基地址,XPAR_PS7_SCUGIC_0_DIST_BASEADDR 0xF8F01000
* 第二个参数是寄存器偏移。
* 寄存器的地址= F8F01000+((0x00000C00+(61/16)*4))= F8F01C0C
* 参考手册UG584 p1497 看寄存器功能
* 第三个参数设置寄存器掩码
*/
void IntcTypeSetup(XScuGic *InstancePtr, int intId, int intType)
{
int mask;
intType &= INT_TYPE_MASK;
mask = XScuGic_DistReadReg(InstancePtr, INT_CFG0_OFFSET + (intId/16)*4);
mask &= ~(INT_TYPE_MASK << (intId%16)*2);
mask |= intType << ((intId%16)*2);
XScuGic_DistWriteReg(InstancePtr, INT_CFG0_OFFSET + (intId/16)*4, mask);
}
//----------------------------------------------------
// 中断处理函数
// - called by the buttons interrupt, performs push buttons read
//----------------------------------------------------
void BTN_Intr_Handler(void *InstancePtr)
{
unsigned char led_val = 0;
// Ignore additional button presses
if ((XGpio_InterruptGetStatus(&BTNInst) & BTN_INT) !=
BTN_INT) {
return;
// Disable GPIO interrupts
XGpio_InterruptDisable(&BTNInst, BTN_INT);
}
btn_value = XGpio_DiscreteRead(&BTNInst, 1);
printf("btn value:%d\n",btn_value);
switch (btn_value){
case 1: led_val = 0x01; break;
case 2: led_val = 0x02; break;
case 4: led_val = 0x04; break;
default:break;
}
XGpio_DiscreteWrite(&LEDInst,1,led_val);
// Acknowledge GPIO interrupts
(void)XGpio_InterruptClear(&BTNInst, BTN_INT);
// Enable GPIO interrupts
XGpio_InterruptEnable(&BTNInst, BTN_INT);
}
//----------------------------------------------------
// MAIN FUNCTION
//----------------------------------------------------
int main (void)
{
int status;
// 初始化按键
status = XGpio_Initialize(&BTNInst, BTNS_DEVICE_ID);
if(status != XST_SUCCESS) return XST_FAILURE;
//初始化LED
status = XGpio_Initialize(&LEDInst, LED_DEVICE_ID);
if(status != XST_SUCCESS) return XST_FAILURE;
// 设置按键IO的方向为输入
XGpio_SetDataDirection(&BTNInst, 1, 0xFF);
//设置LED IO的方向为输出
XGpio_SetDataDirection(&LEDInst, 1, 0x00);
// 初始化中断控制器
status = IntcInitFunction(INTC_DEVICE_ID, &BTNInst);
if(status != XST_SUCCESS) return XST_FAILURE;
while(1){
}
return (0);
}
//----------------------------------------------------
// INTERRUPT SETUP FUNCTIONS
//----------------------------------------------------
int IntcInitFunction(u16 DeviceId, XGpio *GpioInstancePtr)
{
//中断控制器配置实例
XScuGic_Config *IntcConfig;
int status;
// Interrupt controller initialization
//找到scugic实例(根据设备ID查找中断向量)
IntcConfig = XScuGic_LookupConfig(DeviceId);
//初始化scugic(状态检测,对中断初始化,成功返回XST_SUCCESS)
status = XScuGic_CfgInitialize(&INTCInst, IntcConfig, IntcConfig->CpuBaseAddress);
if(status != XST_SUCCESS) return XST_FAILURE;
// Call interrupt setup function
status = InterruptSystemSetup(&INTCInst);
if(status != XST_SUCCESS) return XST_FAILURE;
// 注册中断函数
status = XScuGic_Connect(&INTCInst,
INTC_GPIO_INTERRUPT_ID,
(Xil_ExceptionHandler)BTN_Intr_Handler,
(void *)GpioInstancePtr);
if(status != XST_SUCCESS) return XST_FAILURE;
//设置中断触发类型为上升沿
IntcTypeSetup(&INTCInst,INTC_GPIO_INTERRUPT_ID,INT_TYPE_RISING_EDGE);
// Enable GPIO interrupts
XGpio_InterruptEnable(GpioInstancePtr, 1);
XGpio_InterruptGlobalEnable(GpioInstancePtr);
// 使能我们的按键中断(中断号61)
XScuGic_Enable(&INTCInst, INTC_GPIO_INTERRUPT_ID);
return XST_SUCCESS;
}
int InterruptSystemSetup(XScuGic *XScuGicInstancePtr)
{
// Register GIC interrupt handler
//通用异常处理程序,中断后统一由GIC先处理,然后在HanderTable中查找相应的处理函数
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler,
XScuGicInstancePtr);
//使能异常处理
Xil_ExceptionEnable();
return XST_SUCCESS;
}
三、zynq linux 中断号如何对应
1) 在裸机驱动中设备号与硬件中断号的对应关系
在zynq 的PL端配置外设中断 ,不涉及dts。
比如zynq中的外设中断号从#61开始,配置成按键button,按下按键时,Linux系统接收中断进行处理。
此时就需要在Linux的驱动中按照物理中断号注册,也就是硬件中断号。
//注册中断
ret = request_irq(61, TxDoneHandler, IRQF_TRIGGER_RISING,"TXDONE_INT", NULL);
if(ret)
{
printk("request TXDONE_INT failed! ret = %d\n", ret);
return -1;
}
2) 在设备树dts中设备号与硬件中断号的对应关系
在linux系统下,中断号跟BD中zynq7000 processer中配置的生成的中断号不是直接对应的,中间有一个“-32” 的关系,如下
For Shared Periperal interrupts, the value in the device tree is the (IRQ - 32) ;
例子 interrupts = <0x0 0x32 0x0>; 中间的参数0X32是中断号 50
aaa@qq.com {
compatible = "xlnx,ps7-uart-1.00.a";
reg = <0xe0001000 0x1000>;
interrupts = <0x0 0x32 0x0>;
interrupt-parent = <&gic>;
clock = <50000000>;
};
The second value is the interrupt number. The translate function adds 16
to SPIs and 32 to non-SPIs, so for interrupts generated by fabric logic
in a Zynq, the number in the DTS file should be the hardware number (as
shown in Xilinx Platform Studio, XPS) minus 32.
翻译:第二个参数是中断号。传递的过程中会区分是否为spi中断,如果是spi中断则加16,非spi则加32 ,
所以在devicetree中的生成的中断号是实际中断号减去32 ;
上一篇: 汇编------中断
下一篇: html中不推荐使用的标签