zoukankan      html  css  js  c++  java
  • ZYNQ入门实例——三种GPIO应用、中断系统及软硬件交叉触发调试

    一、前言

      Xlinx的ZYNQ系列SOC集成了APU、各种专用外设资源和传统的FPGA逻辑,为ARM+FPGA的应用提供助力,降低功耗和硬件设计难度的同时极大提高两者间传输的带宽。之前在研究生课题中使用过ZYNQ搭建环路系统对算法进行板级验证,但并没有深入使用和理解这个异构平台,今天算是对入门的总结。一款SOC的入门必然是GPIO的使用,而中断则是MCU能保证实时性的必杀武器。硬件调试难度高一直是FPGA的痛点,集成ARM的FPGA更是如此,cross-trigger调试有效地解决了这一问题,所以它也作为入门ZYNQ的必要技能。

    二、硬件系统搭建

      ZYNQ的三种GPIO分别是MIO、EMIO和AXI-GPIO。PS部分直接连接到芯片引脚的IO叫MIO,经过FPGA再连接到引脚的是EMIO。EMIO可以通过硬件约束指定不同的端口号和电压标准,提高了ARM IO的灵活性。而AXI-GPIO相当于是对ARM IO的补充,通过调用AXI-GPIO IP核与外部通信。以下通过一个实例来说明三种IO的使用方式。

    系统功能:使用一个MIO使连接其上的LED闪烁,使用8个EMIO同样与LED连接构成流水灯效果,另外再调用一个5bit位宽的AXI-GPIO IP核以终端模式响应电路板上5个按键。

    平台:米联客 MIZ702N (ZYNQ-7020)

       配置ZYNQ IP,使能MIO和EMIO,配置EMIO位宽是8bit。

      使能Cross Trigger和共享中断。

       之后添加AXI-GPIO IP Core,配置位宽并使能其中断功能:

      运行Run Automatic Connection最终block design系统结构:

       这里使用ILA抓取AXI-GPIO的中断信号。

    三、软件编程与AXI-GPIO中断模式解析

       Implementation,export hardware with bitstream, launch SDK. BSP中自带了硬件系统所使用到的IP的一些示例代码和文档,为入门提供了很好的帮助。

       为了方便复用,对Xilinx提供的API做进一步封装,生成gpiops.h gpiops.c gpio.h gpio.c和gic.h文件。接下来重点讲述GIC相关的代码原理。若要使用中断系统,首先要初始化GIC,和其他IP一样包括查找配置和初始赋值两个步骤,分别由LookupConfig和CfgInitialize两个函数完成。后者实际上初始化了中断处理句柄使其指向了一个空结构。要理解内部原理,需要弄清楚XScuGic的数据结构。

       其中Handler实际上是一个函数指针类型,用于定义中断产生时的回调函数。而CallBackRef用于传入InstancePtr,即Gic Instance Pointer。GIC初始化完,要将GIC与中断ID和自定义中断回调函数绑定。

       内部的核心代码依然和初始化时一致,只不过换成了输入参数:

       下一步该使能中断了,一方面是使用GIC对GPIO中断ID的响应,另一方面是使能AXI-GPIO的中断信号。最后是系统对异常的处理函数,这里将其封装在exception_enable中:

       总结来看,中断系统建立的步骤为:

    1 初始化GIC

    2 连接GIC与中断ID和回调函数

    3 使能中断

    4 使能异常处理

      那么为什么完成上述操作后,中断事件发生会立即执行自定义中断回调函数GpioHandler呢?CPU会将中断向量表存储在特定的寄存器中,读取该寄存器可以获取中断向量表内容,里边存放着各个中断ID对应的中断函数入口地址。跳转指令则有中断控制器完成。

    接下来是各个文件的软件代码:

      1 /*
      2  * main.c
      3  *
      4  *  Created on: 2020年2月22日
      5  *      Author: s
      6  */
      7 
      8 
      9 #include "xparameters.h"
     10 
     11 #include "xstatus.h"
     12 #include <xil_printf.h>
     13 #include "sleep.h"
     14 
     15 #include "gpiops.h"
     16 #include "gpio.h"
     17 #include "gic.h"
     18 
     19 
     20 XGpioPs GpioPs;    /* The driver instance for GPIO Device. */
     21 XGpio Gpio;
     22 XScuGic Intc; /* The Instance of the Interrupt Controller Driver */
     23 
     24 
     25 #define printf            xil_printf    /* Smalller foot-print printf */
     26 
     27 #define LOOP_NUM 8
     28 
     29 
     30 
     31 
     32 static u32 MIO_OUT_PIN_INDEX =7; /* LED button */
     33 static u32 EMIO_OUT_PIN_BASE_INDEX = 54;
     34 volatile u32 IntrFlag; /* Interrupt Handler Flag */
     35 
     36 void GpioHandler(void *CallbackRef);
     37 int setupIntSystem(XScuGic *IntcInstancePtr,XGpio *gpioInstancePtr
     38         ,u32 IntrId);
     39 
     40 int main()
     41 {
     42     int Status;
     43     u8 i=0;
     44     u32 sys_led_out=0x1;
     45 
     46     Status = gpiops_initialize(&GpioPs,GPIOPS_DEVICE_ID);
     47     if (Status != XST_SUCCESS) {
     48         return XST_FAILURE;
     49     }
     50 
     51     Status = gpio_initialize(&Gpio,GPIO_DEVICE_ID);
     52     if (Status != XST_SUCCESS) {
     53         return XST_FAILURE;
     54     }
     55 
     56 
     57     /*
     58      * Set the direction for the pin to be output and
     59     * Enable the Output enable for the LED Pin.
     60      */
     61     gpiops_setOutput(&GpioPs,MIO_OUT_PIN_INDEX);
     62 
     63     for(i=0;i<LOOP_NUM;i++){
     64         gpiops_setOutput(&GpioPs,EMIO_OUT_PIN_BASE_INDEX+i);
     65     }
     66 
     67     gpio_setDirect(&Gpio, 1,GPIO_CHANNEL1);
     68 
     69     Status = setupIntSystem(&Intc,&Gpio,INTC_GPIO_INTERRUPT_ID);
     70     if (Status != XST_SUCCESS) {
     71             return XST_FAILURE;
     72         }
     73 
     74     printf("Initialization finish.
    ");
     75 
     76     while(1){
     77 
     78         for(i=0;i<LOOP_NUM;i++){
     79             /* Set the GPIO output to be low. */
     80             gpiops_outputValue(&GpioPs, EMIO_OUT_PIN_BASE_INDEX+i, 0x1);
     81             usleep(200*1000);
     82             gpiops_outputValue(&GpioPs, EMIO_OUT_PIN_BASE_INDEX+i, 0x0);
     83         }
     84 
     85         gpiops_outputValue(&GpioPs, MIO_OUT_PIN_INDEX, sys_led_out);
     86         sys_led_out  = sys_led_out == 0x0 ? 0x1 : 0x0;
     87     }
     88     return 0;
     89 }
     90 
     91 int setupIntSystem(XScuGic *IntcInstancePtr,XGpio *gpioInstancePtr
     92         ,u32 IntrId)
     93 {
     94     int Result;
     95     /*
     96     * Initialize the interrupt controller driver so that it is ready to
     97     * use.
     98     */
     99 
    100     Result = gic_initialize(&Intc,INTC_DEVICE_ID);
    101     if (Result != XST_SUCCESS) {
    102             return XST_FAILURE;
    103         }
    104 
    105     XScuGic_SetPriorityTriggerType(IntcInstancePtr, IntrId,
    106                         0xA0, 0x3);
    107 
    108     /*
    109     * Connect the interrupt handler that will be called when an
    110      * interrupt occurs for the device.
    111      */
    112     Result = XScuGic_Connect(IntcInstancePtr, IntrId,
    113                 (Xil_ExceptionHandler)GpioHandler, gpioInstancePtr);
    114     if (Result != XST_SUCCESS) {
    115         return Result;
    116     }
    117 
    118     /* Enable the interrupt for the GPIO device.*/
    119     XScuGic_Enable(IntcInstancePtr, IntrId);
    120 
    121     /*
    122      * Enable the GPIO channel interrupts so that push button can be
    123     * detected and enable interrupts for the GPIO device
    124     */
    125     XGpio_InterruptEnable(gpioInstancePtr,GPIO_CHANNEL1);
    126     XGpio_InterruptGlobalEnable(gpioInstancePtr);
    127 
    128     /*
    129     * Initialize the exception table and register the interrupt
    130     * controller handler with the exception table
    131     */
    132     exception_enable(&Intc);
    133 
    134     IntrFlag = 0;
    135 
    136     return XST_SUCCESS;
    137 }
    138 
    139 void GpioHandler(void *CallbackRef)
    140 {
    141     XGpio *GpioPtr = (XGpio *)CallbackRef;
    142     u32 gpio_inputValue;
    143 
    144 
    145     /* Clear the Interrupt */
    146     XGpio_InterruptClear(GpioPtr, GPIO_CHANNEL1);
    147     printf("Input interrupt routine.
    ");
    148 
    149     //IntrFlag = 1;
    150     gpio_inputValue = gpio_readValue(GpioPtr, 1);
    151     switch(gpio_inputValue)
    152     {
    153     case 30:
    154         printf("button up
    ");
    155         break;
    156     case 29:
    157         printf("button center
    ");
    158         break;
    159     case 27:
    160         printf("button left
    ");
    161         break;
    162     case 23:
    163         printf("button right
    ");
    164         break;
    165     case 15:
    166         print("button down
    ");
    167         break;
    168     }
    169 
    170 }
    main.c
     1 /*
     2  * gpio.h
     3  *
     4  *  Created on: 2020年2月23日
     5  *      Author: s
     6  */
     7 
     8 #ifndef SRC_GPIO_H_
     9 #define SRC_GPIO_H_
    10 
    11 #include "xgpio.h"
    12 
    13 #define GPIO_DEVICE_ID XPAR_GPIO_0_DEVICE_ID
    14 #define INTC_GPIO_INTERRUPT_ID    XPAR_FABRIC_AXI_GPIO_0_IP2INTC_IRPT_INTR
    15 #define GPIO_CHANNEL1        0x1F
    16 
    17 int gpio_initialize(XGpio * InstancePtr, u16 DeviceId);
    18 void gpio_setDirect(XGpio *InstancePtr, unsigned Channel,
    19         u32 DirectionMask);
    20 void gpio_outputValue(XGpio * InstancePtr, unsigned Channel, u32 Data);
    21 u32 gpio_readValue(XGpio * InstancePtr, unsigned Channel);
    22 #endif /* SRC_GPIO_H_ */
    gpio.h
    /*
     * gpio.c
     *
     *  Created on: 2020年2月23日
     *      Author: s
     */
    
    
    #include "gpio.h"
    
    int gpio_initialize(XGpio * InstancePtr, u16 DeviceId)
    {
        return XGpio_Initialize(InstancePtr,DeviceId);
    }
    
    void gpio_setDirect(XGpio *InstancePtr, unsigned Channel,
            u32 DirectionMask)
    {
        XGpio_SetDataDirection(InstancePtr, Channel,
                         DirectionMask);
    }
    
    void gpio_outputValue(XGpio * InstancePtr, unsigned Channel, u32 Data)
    {
        XGpio_DiscreteWrite(InstancePtr, Channel, Data);
    }
    
    u32 gpio_readValue(XGpio * InstancePtr, unsigned Channel)
    {
        return XGpio_DiscreteRead(InstancePtr, Channel);
    }
    gpio.c
     1 /*
     2  * gpiops.c
     3  *
     4  *  Created on: 2020年2月23日
     5  *      Author: s
     6  */
     7 
     8 #include "gpiops.h"
     9 
    10 int gpiops_initialize(XGpioPs *InstancePtr,u16 DeviceId)
    11 {
    12     XGpioPs_Config *ConfigPtr;
    13 
    14     ConfigPtr = XGpioPs_LookupConfig(DeviceId);
    15     return XGpioPs_CfgInitialize(InstancePtr, ConfigPtr,
    16                     ConfigPtr->BaseAddr);
    17 }
    18 
    19 void gpiops_setOutput (XGpioPs *InstancePtr,u32 Pin)
    20 {
    21     XGpioPs_SetDirectionPin(InstancePtr, Pin, 1);
    22     XGpioPs_SetOutputEnablePin(InstancePtr, Pin, 1);
    23 }
    24 
    25 void gpiops_setInput(XGpioPs *InstancePtr,u32 Pin)
    26 {
    27     XGpioPs_SetDirectionPin(InstancePtr, Pin, 0);
    28 }
    29 
    30 void gpiops_outputValue(XGpioPs *InstancePtr,u32 Pin,u32 Data)
    31 {
    32     XGpioPs_WritePin(InstancePtr, Pin, Data);
    33 }
    34 
    35 
    36 u32 gpiops_readValue(XGpioPs *InstancePtr,u32 Pin)
    37 {
    38     /* Read the state of the data so that it can be  verified. */
    39     return XGpioPs_ReadPin(InstancePtr, Pin);
    40 }
    gpiops.c
     1 /*
     2  * gpio.h
     3  *
     4  *  Created on: 2020年2月23日
     5  *      Author: s
     6  */
     7 
     8 #ifndef SRC_GPIOPS_H_
     9 #define SRC_GPIOPS_H_
    10 
    11 #include "xgpiops.h"
    12 
    13 #define GPIOPS_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID
    14 
    15 
    16 
    17 int gpiops_initialize(XGpioPs *InstancePtr,u16 DeviceId);
    18 void gpiops_setOutput (XGpioPs *InstancePtr,u32 Pin);
    19 void gpiops_setInput(XGpioPs *InstancePtr,u32 Pin);
    20 void gpiops_outputValue(XGpioPs *InstancePtr,u32 Pin,u32 Data);
    21 u32 gpiops_readValue(XGpioPs *InstancePtr,u32 Pin);
    22 
    23 
    24 #endif /* SRC_GPIOPS_H_ */
    gpiops.h
     1 /*
     2  * gic.h
     3  *
     4  *  Created on: 2020年2月23日
     5  *      Author: s
     6  */
     7 
     8 #ifndef SRC_GIC_H_
     9 #define SRC_GIC_H_
    10 
    11 #include "xscugic.h"
    12 
    13 #define INTC_DEVICE_ID    XPAR_SCUGIC_SINGLE_DEVICE_ID
    14 
    15 s32 gic_initialize(XScuGic *InstancePtr,u16 DeviceId)
    16 {
    17     XScuGic_Config *IntcConfig;
    18 
    19     IntcConfig = XScuGic_LookupConfig(DeviceId);
    20     if (NULL == IntcConfig) {
    21         return XST_FAILURE;
    22     }
    23 
    24     return XScuGic_CfgInitialize(InstancePtr, IntcConfig,
    25                         IntcConfig->CpuBaseAddress);
    26 }
    27 
    28 void exception_enable(void *Data)
    29 {
    30     Xil_ExceptionInit();
    31     Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
    32             (Xil_ExceptionHandler)XScuGic_InterruptHandler, Data);
    33 
    34     /* Enable non-critical exceptions */
    35     Xil_ExceptionEnable();
    36 }
    37 
    38 
    39 #endif /* SRC_GIC_H_ */
    gic.h

    四、交叉触发调试

       右键工程文件夹->Run As/Debug As分别用于代码下载和调试。SDK基于GDB提供了强大的调试能力,支持断点运行,可查看内部寄存器、地址数值以及汇编代码等。Debug As ->Debug Configuartion,双击System Debugger新建ELF文件。勾选Reset entire system和Program FPGA,因为ELF只是软件,硬件信息存储在bitstream中。最重要的是勾选enable cross-triggering。

       点击enable cross-triggering右侧的按钮,按照如下操作使能Processor to Fabric Trigger.

       再次create使能Fabric to Processor Trigger:

       最后点击Debug下载软硬件代码并进入调试界面。

      1 首先尝试PS触发PL调试:

      指定中断回调函数起始位置一个断点。然后进入VIVADO,打开Hardware Manager连接硬件。注意此时触发模式选择IN_ONLY。此时不用设置ILA抓取信号的触发条件,因为触发由PS端的PC控制。点击Run Trigger等待触发条件。这时回到SDK点击Resume按钮使代码开始运行。按下任意按键产生中断,此时软件代码运行到断点处停止,ILA随即抓取中断信号。

       2 尝试PL触发PS调试:

       这回在VIVADO中设置触发模式为OR_TRIG_IN,并启动触发条件为上升沿触发。按下按键,C运行到满足ILA触发条件时C代码立即停止,故PL控制了PS端的程序运行。

       可以看到此时程序进入IRQHandler。

       串口终端也打印进入中断函数的信息,正确响应中断。到此示例结束。本文虽是对ZYNQ入门的整理,但涉及到的东西很多,包括GPIO应用、中断系统建立和相应机制、调用AXI总线IP核、软件设计以及软硬件交叉触发调试流程。

  • 相关阅读:
    StateListDrawable状态选择器
    Shape
    每周随笔
    每周随笔
    每周随笔
    每周随笔
    每周随笔
    每周随笔
    每周随笔

  • 原文地址:https://www.cnblogs.com/moluoqishi/p/12358509.html
Copyright © 2011-2022 走看看