作者:Adam P. Taylor e2v技術公司工程系統部負責人 aptaylor@theiet.org 實時計算經常要求中斷針對事件快速做出響應。只要掌握Zynq SoC中斷結構的工作原理,就不難設計出中斷驅動型系統。 在嵌入式處理中,中斷表示暫時停止處理器的當前活動。處理器會保存當前的狀態并執行中斷服務例程,以便對引起中斷的原因進行尋址。中斷可能來自下列三個地方之一:
無論中斷的來源在何處,都可將中斷的類別歸為可屏蔽和不可屏蔽兩種。您可通過在中斷掩碼寄存器中設置相應的位來安全地忽略可屏蔽中斷。但不能忽略不可屏蔽中斷,因為這類中斷通常用于定時器和看門狗監控器。 中斷的觸發既可以是邊緣觸發也可以是水平觸發。我們將在后面部分看到,賽靈思Zynq®-7000 All Programmable SoC支持中斷的這兩種配置方式。 為什么使用中斷驅動方案? 實時設計通常要求采用中斷驅動方案,因為眾多系統都會有很多輸入單元(如鍵盤、鼠標、按鈕、傳感器以及類似設備等)偶爾需要處理。這些設備的輸入單元通常會被異步至當前正在執行的進程或任務,因而用戶不可能始終準確預測事件的發生時間。 使用中斷,處理器能繼續進行處理,直到事件發生,這時處理器便可處理這一事件。此外,與輪詢方案相比,中斷驅動方案對事件的響應時間更短,在中斷驅動方案中,程序會以同步的方式主動對外部設備的狀態進行采樣。 Zynq SoC的中斷結構 隨著處理器技術不斷進步,中斷的來源也多種多樣。如圖1所示,Zynq SoC可使用通用中斷控制器(GIC)來處理中斷。GIC可處理源自以下方面的中斷:
![]() 圖1 – 紅圈顯示的是通用中斷控制器。 共享外設中斷非常有趣,因為它們非常靈活?蓪⑺鼈儚腎/O外設(共44個中斷)或FPGA邏輯(共16個中斷)路由至兩個CPU中的一個,但也可以將中斷從I/O外設路由至設備的可編程邏輯側,參見圖2。 在Zynq SoC上處理中斷 在Zynq SoC中發生中斷時,處理器會采取以下措施: 1. 將中斷顯示為掛起; 2. 處理器停止執行當前線程; 3. 處理器在協議棧中保存線程狀態,以便在中斷處理后繼續進行處理; 4. 處理器執行中斷服務例程,其中定義了如何處理中斷; 5. 在處理器從協議;謴椭,被中斷的線程繼續運行; 中斷屬于異步事件,因此可能同時發生多個中斷。為了解決這一問題,處理器會對中斷進行優先級排序,從而首先服務于優先級別最高的中斷掛起。 為了正確實現這一中斷結構,需要編寫兩個函數:一是中斷服務例程,用于定義中斷發生時的應對措施;二是用于配置中斷的中斷設置。中斷設置例程可重復使用,允許構建不同的中斷。該例程適用于系統中的所有中斷,將針對通用I/O(GPIO)設置和使能中斷。 如何在SDK中使用中斷 可使用賽靈思軟件開發套件(SDK)中的獨立板支持包(BSP)在物理硬件上支持并實現中斷。BSP具備眾多功能,可顯著降低創建中斷驅動系統的任務難度。它們位于帶有以下報頭的文件中:
為了對硬件外設進行尋址,我們需要知道想要使用的設備(也就是GIC)的地址范圍和設備ID,這些信息大多位于BSP報頭文件xparameters下。但是xparameters_ps.h(無需在您的源代碼中申報該報頭文件,因為它包含在xparameters.h文件中)提供了中斷ID。我們可在源文件中使用這個標記有中斷的“ID”(GPIO_Interrupt_ID),使用方式如下: ![]() 在這個簡單的例子中,我們將配置Zynq SoC的GPIO,以便在按下按鈕后生成中斷。為了設置中斷,我們需要兩個靜態全局變量以及上述定義的中斷ID來執行以下操作: ![]() ![]() 圖2 – 這些是處理系統與可編程邏輯之間可用的中斷。 在中斷設置功能中,我們需要初始化Zynq SoC異常;配置并初始化GIC;并將GIC連接到中斷處置硬件。Xil_exception.h和Xscugic.h文件可提供完成這一任務所需的函數。結果生成以下代碼: ![]() 當配置GPIO以使其在同一中斷配置例程中發揮中斷功能時,我們能夠配置內存庫或單個引腳。我們可通過使用在xgpiops.h中提供的函數來完成這項任務,比如: ![]() 當然,您還需要正確配置中斷。例如,您希望采用邊緣觸發或水平觸發嗎?若答案為是,那么采用這個函數能實現何種邊緣和水平呢? ![]() 在這里,xgpiops.h中五個定義中的其中一個可對IrqType定義。這五個定義是: ![]() 如果您決定使用 Bank 使能,那么您需要知道您希望使能中斷的單個引腳或多引腳位于哪個 Bank 上。Zynq SoC最多支持118個GPIO。在這種配置下,所有MIO(54個引腳)都會與EMIO(64個引腳)一起被用作GPIO。我們能將這個配置分為四個Bank,每個Bank容納32個引腳。 此外,這項設置功能還將定義中斷服務例程,發生中斷時,可用以下函數調用該例程: ![]() 中斷服務例程的繁簡程度由其應用定義。在該例中,每按一次按鈕,它便會觸發一個LED,打開和關閉這個LED。另外,在每次按下按鈕時,中斷服務例程還會向控制臺打印一條信息。 ![]() 專有定時器舉例 Zynq SoC擁有許多可用的定時器和看門狗監視器。它們既可作為一個CPU的專用資源也可作為兩個CPU的共享資源。如需在您的設計中高效利用這些組件,則需要中斷。這些定時器和看門狗監視器包括:
為了通過可用的定時器和看門狗監視器獲得最大優勢,我們需要使用Zynq SoC中斷。其中配置最簡單的就是專有定時器。和Zynq SoC的大多數外設一樣,該定時器帶有很多預定義的函數和宏指令,能幫助您高效使用這一資源。這些函數和宏指令位于: ![]() 這個文件中的函數(宏指令)能夠提供許多功能,包括初始化和自測試等。此外,文件中的函數還能啟動和停止定時器并對其進行管理(重啟;檢查是否過期;加載定時器;使能/禁用自動加載)。它們的另一項工作就是設置、使能、禁用、清除和管理定時器中斷。最后,這些函數還能獲取并設置預分頻器。 定時器本身通過以下四個寄存器來控制:
就使用GPIO而言,設置定時器所需的定時器設備ID和定時器中斷ID都包含在XParameters.h文件中。在本例中,我們將使用先前開發的按鈕中斷。當按下按鈕時,定時器將加載并開始運行(采用非自動重新加載模式)。一旦定時器過期,將生成能通過STDOUT輸出一條消息的中斷。然后清除該中斷,以便等待下一次按下按鈕。在本例中,將始終向計數器加載相同的數值;因此,在文件頂部的公告中公布了定時器計數值,如下所示: ![]() 下一步是配置和初始化專用定時器并在其中加載定時器計數值。 ![]() 此外,我們還需要更新中斷設置子例程,從而將定時器中斷連接至GIC并使能定時器中斷。 ![]() ![]() 發生中斷時,需要調用TimerIntrHandler函數,這時必須在GIC上以及定時器本身使能定時器中斷。 定時器中斷服務例程非常簡單。它僅需清除掛起的中斷,并通過STDOUT輸出一條消息,如下所示: ![]() 完成該操作后,最后還要修改GPIO中斷服務例程,從而在每次按下按鈕后啟動定時器,如下所示: ![]() 首先,我們要將定時器值加載到定時器中,然后調用定時器啟動函數,F在,我們能夠再次清除按鈕中斷并恢復處理,如圖3所示。 在開始著手設計中斷驅動系統時,很多工程師都會心生畏懼。但是,Zynq SoC架構以及通用中斷控制器(與配備SDK的驅動器相結合)可幫助您快速、高效地啟動和運行中斷驅動系統。 ![]() 圖3 – GPIO與定時器中斷事件輸出的界面示例。 |