ZYNQ入門實例——三種GPIO應用、中斷系統及軟硬體交叉觸發調試

来源:https://www.cnblogs.com/moluoqishi/archive/2020/02/24/12358509.html
-Advertisement-
Play Games

一、前言 Xlinx的ZYNQ系列SOC集成了APU、各種專用外設資源和傳統的FPGA邏輯,為ARM+FPGA的應用提供助力,降低功耗和硬體設計難度的同時極大提高兩者間傳輸的帶寬。之前在研究生課題中使用過ZYNQ搭建環路系統對演算法進行板級驗證,但並沒有深入使用和理解這個異構平臺,今天算是對入門的總結 ...


一、前言

  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.\n");
 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.\n");
148 
149     //IntrFlag = 1;
150     gpio_inputValue = gpio_readValue(GpioPtr, 1);
151     switch(gpio_inputValue)
152     {
153     case 30:
154         printf("button up\n");
155         break;
156     case 29:
157         printf("button center\n");
158         break;
159     case 27:
160         printf("button left\n");
161         break;
162     case 23:
163         printf("button right\n");
164         break;
165     case 15:
166         print("button down\n");
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核、軟體設計以及軟硬體交叉觸發調試流程。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 現如今,新型冠狀病毒疫情牽動著每一個人的神經,每天起床後的第一件事就是打開疫情地圖,看看最新的疫情數據。如何防控對於普通居民來說,減少外出,註意衛生等都是有效措施,另外保持室內通風換氣也是至關重要的,無論是飛沫傳播還是流傳的氣溶膠傳播,環境密閉導致空氣渾濁,易造成病菌滋生,增加人體感染疾病的風險。勤... ...
  • 一、項目框架準備 1.1 新建maven空項目,併在pom中引入依賴 1 <parent> 2 <groupId>org.springframework.boot</groupId> 3 <artifactId>spring-boot-starter-parent</artifactId> 4 <v ...
  • 一、Set集合 1.HashSet底層實際上是一個HashMap,HashMap底層採用了哈希表數據結構。 2.哈希表又稱為散列表,哈希表底層是一個數組,這個數組中每一個元素是一個單向鏈表,每個單向鏈表都有一個獨一無二的hash值,代表數組的下標。在某個單向鏈表中的每一個節點上的hash值是相等的, ...
  • eureka註冊中心 一、基本概念 SpringCloud封裝 了Netflix公司的eureka作為自己微服務的註冊中心。這個註冊中心和dubbo中的zookeeper很相似,簡單來說,只要你可以將你的”微服務“模塊註冊到註冊中心,就可以供其他服務調用,一般來說,只有provider會註冊到eur ...
  • (1)BigIntege:實現任意精度的整數運算。(2)BigDecimal:實現任意精度的浮點運算。 例如: 使用BigDecimal進行浮點數比較 import java.math.BigDecimal; public class HelloWorld{ public static void m ...
  • 基本數據類型 1.整數類型(byte,short,int,long)2.浮點數類型(float,double)3.字元類型(char)4.布爾類型(boolean) 整數類型 --byte 使用byte關鍵字來定義byte型變數 例如: byte a=1; byte b=2,c=3; 整數型中分配空 ...
  • 一、對象導論 1:多態的可互換對象 面向對象程式設計語言使用了後期綁定的概念。 當向對象發送消息時,被調用的代碼直到運行時才能確定。也叫動態綁定。 2:單根繼承結構 所有的類最終都繼承自單一的基類,這個終極基類的名字就是Object 3:對象的創建和生命期 對象的數據處於作用域,Java完全採用了動 ...
  • 5種PHP生成圖片驗證碼實例,包括數字驗證碼、數字+字母驗證碼、中文驗證碼、仿google驗證碼和算術驗證碼,PHP生成驗證碼的原理:通過GD庫,生成一張帶驗證碼的圖片,並將驗證碼保存在Session中。 js驗證 1 $(function() { 2 $("#getcode_num").click ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...