學會Zynq(8)PL中斷示例(SPI)
PL中斷
雙核Zynq中共有20個PL到PS的中斷。IRQF[15:0]是16個共享外設中斷(SPI),可配選擇上升沿觸發或高電平觸發,中斷號為61-68和84-91。
另外還有4個私有外設中斷(PPI)IRQF2P[19:16],每個CPU都有一個來自PL的FIQ(快速中斷)和IRQ,其中斷敏感型別固定。
本文體會Zynq中PL到PS的SPI的使用方法。若設定為電平敏感,PL必須提供在中斷被確認後將其清除掉的機制。若設定為上升沿敏感,PL必須提供足夠寬的脈衝,讓GIC能夠捕獲。
硬體平臺搭建
本例中將連線到PL部分的兩個按鍵作為中斷請求源,從PL部分路由到PS的中斷介面。設定Zynq7處理系統,Interrupts中啟用PL到PS的中斷,選擇IRQ_F2P[15:0],啟用SPI。下面4個是PPI。
配置DDR和MIO電平,啟用UART,設計程式根據按鍵中斷串列埠列印相應資訊。配置完成後白板中的Zynq7 IP會出現IQR_F2P管腳,但位寬只有[0:0]。我們把中斷源以匯流排的形式連線到這個管腳後,執行Validate Design有效性檢查,會自動更新位寬。
將多路訊號拼接為匯流排可以用Concat IP核實現。這個IP可以設定埠數目和每個埠上的位寬,設為Auto可以完成自動檢測。In0在輸出匯流排的低位,In[Number of Ports - 1]在輸出匯流排的高位。
硬體連線如下圖所示。由於使用了PL部分的管腳,要新增XDC約束檔案,將sw0和sw1繫結到按鍵管腳。生成bit流檔案後,將硬體平臺匯出到SDK中。
SDK程式設計
新建工程,依次新增原始檔並編寫程式碼。init.h檔案程式碼如下:
#include <stdio.h>
#include "xscugic.h"
#include "xil_exception.h"
//---------------------------------------------------------
// 引數定義
//---------------------------------------------------------
#define SW1_INT_ID 61 //按鍵PL中斷1
#define SW2_INT_ID 62 //按鍵PL中斷2
#define INTC_DEVICE_ID XPAR_PS7_SCUGIC_0_DEVICE_ID //中斷裝置
#define INT_TYPE_RISING_EDGE 0x03 //上升沿敏感
#define INT_TYPE_HIGHLEVEL 0x01 //高電平敏感
#define INT_TYPE_MASK 0x03
#define INT_CFG0_OFFSET 0x00000C00
//---------------------------------------------------------
// 函式宣告
//---------------------------------------------------------
void IntcTypeSetup(XScuGic *InstancePtr, int intId, int intType);
int IntcInitFunction(u16 DeviceId);
init.c檔案的程式碼如下:
#include "init.h"
static XScuGic INTCInst;
//---------------------------------------------------------
// 中斷處理程式
//---------------------------------------------------------
static void SW_intr_Handler(void *param)
{
int sw_id = (int)param;
printf("SW%d int\n\r", sw_id); //根據中斷請求源列印相關資訊
}
//---------------------------------------------------------
// 中斷敏感型別設定函式
//---------------------------------------------------------
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);
}
//---------------------------------------------------------
// 中斷初始化函式
//---------------------------------------------------------
int IntcInitFunction(u16 DeviceId)
{
XScuGic_Config *IntcConfig; //儲存中斷裝置的配置資訊
int status;
/* 初始化中斷控制器 */
IntcConfig = XScuGic_LookupConfig(DeviceId);
status = XScuGic_CfgInitialize(&INTCInst, IntcConfig, IntcConfig->CpuBaseAddress);
if(status != XST_SUCCESS) return XST_FAILURE; //檢測初始化狀態
/* 設定並開啟中斷異常處理功能 */
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler, &INTCInst);
Xil_ExceptionEnable();
/* 為中斷設定中斷處理函式 */
status = XScuGic_Connect(&INTCInst, SW1_INT_ID,
(Xil_ExceptionHandler)SW_intr_Handler, (void *)1);
if(status != XST_SUCCESS) return XST_FAILURE;
status = XScuGic_Connect(&INTCInst, SW2_INT_ID,
(Xil_ExceptionHandler)SW_intr_Handler, (void *)2);
if(status != XST_SUCCESS) return XST_FAILURE;
/* 將中斷設定為上升沿敏感型別 */
IntcTypeSetup(&INTCInst, SW1_INT_ID, INT_TYPE_RISING_EDGE);
IntcTypeSetup(&INTCInst, SW2_INT_ID, INT_TYPE_RISING_EDGE);
/* 使能中斷 */
XScuGic_Enable(&INTCInst, SW1_INT_ID);
XScuGic_Enable(&INTCInst, SW2_INT_ID);
return XST_SUCCESS;
}
main.c檔案的程式碼如下:
#include "init.h"
int main(void)
{
//初始化中斷
IntcInitFunction(INTC_DEVICE_ID);
while(1);
return 0;
}
SDK Terminal中新增串列埠,下載程式。按下按鍵終端會顯示相應的資訊。由於PL中沒有對按鍵做消抖處理,直接作為中斷請求源,有時會“過於靈敏”。
相關函式使用說明
我們只使用了兩個PL中斷,在硬體平臺中可以看到,自動從低bit開始分配,即使用IRQ_F2P[1:0],對應的中斷號是62-61。為了程式設計方便在init.h中對這兩個中斷號做巨集定義。
1.中斷控制器例項
init.c定義了一個XScuGic型別的結構體變數,設計者需要為系統中的每個中斷裝置分配一個此型別的變數。其它中斷相關的API需要指向此變數的指標作為傳入引數。
中斷初始化函式中又定義了一個指向XScuGic_Config型別的結構體變數的指標,這個結構體包含了中斷裝置的配置資訊。兩個結構體的原型如下(注意XScuGic_Config也是XscuGic的一個成員):
typedef struct
{
u16 DeviceId; /**< Unique ID of device */
u32 CpuBaseAddress; /**< CPU Interface Register base address */
u32 DistBaseAddress; /**< Distributor Register base address */
XScuGic_VectorTableEntry HandlerTable[XSCUGIC_MAX_NUM_INTR_INPUTS];/**<
Vector table of interrupt handlers */
} XScuGic_Config;
typedef struct
{
XScuGic_Config *Config; /**< Configuration table entry */
u32 IsReady; /**< Device is initialized and ready */
u32 UnhandledInterrupts; /**< Intc Statistics */
} XScuGic;
2.中斷控制器初始化
中斷初始化函式中使用XScuGic_LookupConfig()函式和XScuGic_CfgInitialize()函式完成中斷控制器的初始化。其實上面這部分處理和GPIO部分的一樣,只是處理物件從“GPIO裝置”換成了“中斷裝置”。
XScuGic_LookupConfig()函式的原型介面如下。傳入引數為裝置ID,返回的是一個XScuGic_Config型別的指標。如果存在該中斷裝置,則返回該配置資訊的列表;未找到則返回NULL。
XScuGic_Config *XScuGic_LookupConfig(u16 DeviceId){}
XScuGic_CfgInitialize()函式的原型介面如下。該函式用來初始化傳入的XScuGic型別的變數(可稱作中斷控制器例項),初始化該結構體中的成員欄位。第二個引數便是用於初始化中斷控制器例項的配置資訊表。第三個引數EffectiveAddr是虛擬記憶體地址空間中的裝置基地址,如果沒有使用地址轉換功能,傳入Config->BaseAddress即可。
s32 XScuGic_CfgInitialize(XScuGic *InstancePtr,
XScuGic_Config *ConfigPtr, u32 EffectiveAddr) {}
3.中斷異常處理
中斷控制器初始化完成後,使用Xil_ExceptionRegisterHandler()和Xil_ExceptionEnable()兩個函式設定中斷異常處理功能。
Xil_ExceptionRegisterHandler()為異常註冊處理函式。處理器執行過程中遇到該異常,便會呼叫設定的處理函式。該函式原型如下,第一個為異常的ID號,第二個為設定的處理函式,第三個是處理函式被呼叫時傳給它的引數。
void Xil_ExceptionRegisterHandler(u32 Exception_id,
Xil_ExceptionHandler Handler, void *Data)
Xil_ExceptionEnable()啟用IRQ異常,沒有引數和返回值。異常處理機制並不是中斷獨有的,在xil_exception.h檔案中有所有異常情況的ID列表。下面貼出init.c中的程式碼,更進一步理解:
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler, &INTCInst);
XIL_EXCEPTION_ID_INT表示IRQ中斷異常。XScuGic_InterruptHandler是庫中提供的函式,它可以解析那些中斷時活躍的和啟用的,並呼叫相應的中斷處理程式,先服務優先順序最高的中斷。該函式是保證中斷系統正常執行的基礎,其原型如下:
void XScuGic_InterruptHandler(XScuGic *InstancePtr)
它需要一個指向XscuGic型別的指標引數。在呼叫Xil_ExceptionRegisterHandler()時通過設定第三個引數便可完成引數傳入。
4.中斷源設定
之後使用XScuGic_Connect()函式將中斷源和中斷處理程式聯絡起來,當中斷髮生時會呼叫設定的中斷處理程式。函式原型如下,Int_ID是中斷源對應的ID號;Handler是中斷處理程式;最後一個void*型別的CallBackRef在中斷處理程式被呼叫時會作為引數傳入。
s32 XScuGic_Connect(XScuGic *InstancePtr, u32 Int_Id,
Xil_InterruptHandler Handler, void *CallBackRef)
再之後便是用自定義的函式完成中斷敏感型別的設定,主要是對中斷相關暫存器的配置。最後使用XScuGic_Enable()函式啟用中斷源。函式原型如下,Int_Id為中斷源對應的ID號。
void XScuGic_Enable(XScuGic *InstancePtr, u32 Int_Id)
5.中斷處理程式
static void SW_intr_Handler(void *param)
{
int sw_id = (int)param;
printf("SW%d int\n\r", sw_id); //根據中斷請求源列印相關資訊
}
中斷處理程式只能傳入void*型別的引數,因此在內部使用時需要做適當的型別轉換。我們通過兩個按鍵中斷傳入不同的引數,便可在一箇中斷處理程式中識別出到底是哪個中斷源發出了中斷請求。
相關文章
- 學會Zynq(7)中斷系統簡介
- 學會Zynq(9)定時器使用示例(PPI)定時器
- 學會Zynq(10)lwIP簡介
- 學會Zynq(1)搭建Zynq-7000 AP SoC處理器
- 學會Zynq(3)Zynq的軟體開發基礎知識
- 學會Zynq(2)Zynq-7000處理器的配置詳解
- SPI驅動示例
- 【黑金ZYNQ7000系列原創視訊教程】06.ZYNQ來自FPGA的中斷——按鍵中斷實驗FPGA
- java SPI 程式碼示例Java
- 【黑金ZYNQ7000系列原創視訊教程】04.熟悉ZYNQ內部中斷——內部定時器中斷實驗定時器
- 學會Zynq(12)lwIP 1.4.1庫的配置與使用
- oracle PL/SQL示例OracleSQL
- 學會Zynq(6)固化程式到SD卡或QSPI FlashSD卡
- 學會Zynq(11)RAW API的TCP和UDP程式設計APITCPUDP程式設計
- Oracle PL/SQL迴圈示例OracleSQL
- Zynq學習筆記(1)筆記
- Xilinx-ZYNQ7000系列-學習筆記(7):解決ZYNQ IP核自動佈線後會更改原有配置的問題筆記
- STM8L中斷線和中斷埠使用方法
- ZYNQ學習筆記(一): uboot 編譯筆記boot編譯
- git clone 慢並且會中斷Git
- 溫習 SPI 機制 (Java SPI 、Spring SPI、Dubbo SPI)JavaSpring
- PL/SQL變數值可變在程式中會變嗎?SQL變數
- 人人都能學會的python程式設計教程8:條件判斷與迴圈Python程式設計
- 理解的Java中SPI機制Java
- Java SPI 與 Dubbo SPIJava
- 中斷的學習筆記筆記
- 聊聊自定義SPI如何與sentinel整合實現熔斷限流
- 004:ZYNQ_AXI匯流排學習筆記(1)筆記
- 深入理解 Java 中 SPI 機制Java
- 8個Date命令使用示例
- 創業要學會苦中作樂:不斷用小成就激勵團隊創業
- SpringBoot應用篇之FactoryBean及代理實現SPI機制示例Spring BootBean
- 《面試補習》-熔斷降級我學會了!面試
- Dubbo原始碼學習之-SPI介紹原始碼
- [PL/SQL]10g PL/SQL學習筆記(一)SQL筆記
- [PL/SQL]10g PL/SQL學習筆記(二)SQL筆記
- [PL/SQL]10g PL/SQL學習筆記(三)SQL筆記
- 剖析 SPI 在 Spring 中的應用Spring