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

uart@e0001000 {
 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 ;

猜你喜欢

转载自blog.csdn.net/u013457167/article/details/80682926