在現實的操作中,經常會遇到一個正常執行的程序或任務,需要被臨時打斷,騰出CPU的時間去執行另外一段事先安排好的程序,處理一些發生時間不確定的事件。注意,需要處理的事件不是意外事件,是已經預見到要發生的事件,而且也安排好了(事先安排好的程序)如何處理這樣的事件,只是不知道事件究竟會在什么時候發生;例如,你上網買了一樣東西,你知道快遞會今天送上門,但不知道快遞員具體是幾點幾分送到。這樣的事件可以是外部通信接口接收到數據,也可以是某個按鍵被按動,還可以是供電電源發生故障等。
中斷就是打斷CPU程序順序執行,并轉去執行另一段程序的一種機制。處理中斷事件的程序,叫做中斷處理程序。在正常運行的程序被打斷,轉去執行中斷處理程序之前,需要保存好程序運行的現場,在中斷處理程序結束后,還需要恢復被中斷的程序現場,繼續原有的運行進程。
中斷事件一般都是比較緊急的事件,需要盡快處理。例如,若不及時接受并存儲外部通信接口到達的數據,數據就有可能丟失;如果電話鈴響,你不盡快接起來,對方就會掛掉。對于中斷事件的處理一般都是比較短暫的,不會占用太多的CPU時間。
對于中斷的處理,需要打斷正常程序的執行,所以不能過多地占用CPU時間進行處理;對于需要較長處理時間的事件,通常都是把對應的數據和狀態存儲下來,隨后再擇機處理。當然這個原則也有例外,例如一個電池供電的煙霧報警器,整個系統在正常情況下處于超低功耗的待機狀態,當監測到異常時,可以直接在中斷處理程序中進行甄別,并在需要時報警,當異常狀態解除或被排除時,再返回待機狀態。
從事件的產生至中斷處理程序開始執行,這個時間間隔稱為中斷響應時間,這是中斷機制的一個十分重要的參數指標,尤其是在實時處理的系統中,希望這個間隔是可預知的,有時需要它越短越好。設想一個電機控制系統,當發生系統過載時,需要盡可能快地采取措施,讓電機停轉、斷電、剎車制動等,否則輕則電機損壞,重則發生人身事故。
1.1 LPC800的NVIC中斷控制器特性
LPC800的核心是Cortex-M0+,她的NVIC中斷控制器是ARM隨核心提供的,與CPU核心一體設計的;這個中斷控制器與其它Cortex-M系列的NVIC控制器功能上基本一致,但性能上略有變化,有興趣的讀者可以自己比較一下。
LPC800中集成的NVIC中斷控制器,具有以下特點:
▲緊密耦合的中斷控制器,提供低中斷延遲。
▲同時控制系統異常和外設的中斷。▲支持32個中斷向量。
▲支持4級中斷優先級和中斷嵌套。
▲支持SVC(管理員調用) 指令和PendSV軟件中斷。
支持不可屏蔽中斷。
▲支持用戶指定中斷向量表的地址。
下面分別更詳細地看看這些特性:
1.1.1 低中斷延遲
中斷延遲是指從接收到中斷信號至開始執行中斷處理程序的第一條指令,在最好的情況下所需要的時間,通常用多少個CPU時鐘周期來衡量中斷延遲時間。
對于LPC800使用的Cortex-M0+核心來說,中斷延遲的最短時間是15個CPU時鐘周期;在30MHz的主頻下,這只相當于0.5μs的時間。
對于MCU的應用來說,往往是需要中斷的延遲越小越好;但如果具體到不同的應用場景、不同的軟件架構,還要綜合考慮其它部件的性能和響應速度。
下圖是一個典型的中斷請求、中斷響應的示意圖。
圖1.中斷請求和響應示意圖
當外部設備發出中斷請求,NVIC控制器檢測到這個請求后,經過同步和仲裁開始進入中斷響應。控制器首先是由硬件通過堆棧保護程序現場(圖中的PUSH),然后調用這個中斷對應的中斷處理程序(ISR – Interrupt Service Routine)。ISR處理完該中斷請求后,執行返回指令退出中斷處理,此時NVIC控制器再把堆棧中保存的程序現場恢復到CPU中,最后回到被中斷的程序繼續執行。
1.1.2 系統異常和外設中斷
在早期8位MCU時,基本只有外設中斷的概念,還沒有完整的系統異常的概念,隨著MCU越來越復雜,功能不斷地增強,在32位MCU中出現了一系列的系統異常中斷源。
所謂系統異常,可以理解成一種特殊的中斷,它的中斷源來自CPU本身或系統設備,例如SysTick。
目前Cortex-M0+中定義了6個系統異常中斷。
▲系統復位:這是我們通常說的Reset。除了上電復位外,還有很多條件可以產生復位,包括:復位引腳被拉低,看門狗計時器到時,檢測到低電壓,和ARM核心提供的軟件復位請求等。
▲不可屏蔽中斷:對于一般的中斷請求,軟件可以根據具體情況確定是否需要響應。對于不可屏蔽中斷,軟件沒有選擇的余地,必須設置相應的處理程序,在發生對應事件時進行響應。一般不可屏蔽中斷都是非常緊急的事件,如果被屏蔽而得不到響應,會產生嚴重后果。例如系統掉電,軟件必須盡快保存重要數據;再例如系統檢測到失火,軟件必須啟動滅火流程等。
▲硬件失效(Hard Fault):所有不能恢復的系統錯誤,都會產生這個中斷。這是很多人經常遇到的系統異常,導致硬件失效中斷的原因很多,最多的狀況是訪問了沒有存儲器或外設寄存器的地址空間。其它的原因還包括,執行了非法的指令碼(當程序跑亂后就會經常出現這種情況);當訪問未對齊的數據時;還有就是系統受到嚴重干擾導致內部信號紊亂時,等等。
▲系統調用(Supervisor call- SVC):在Cortex-M的指令系統中有一條指令SVCall,執行這條指令后會引起SVC異常中斷,同時CPU進入Supervisor狀態。SVCall指令是用于在有操作系統并具有系統態和用戶態的劃分時使用,在這種環境下CPU會限制一般用戶程序的某些訪問權限(例如不能訪問有些地址等)。當程序需要訪問特權時,執行SVCall指令進入系統態并獲得相應的權限。在LPC800這樣的小系統中,不會用到這個異常中斷。
▲預約系統服務(PendSV):和上述SVCall一樣,PendSV同樣是需要得到系統服務,但不同的是PendSV是通過設置一個系統狀態位,當CPU處理完其它高優先級的中斷,再來響應PendSV的中斷請求。很多文檔直譯PendSV為“掛起異常”,但我覺得用“預約”更加貼切。在LPC800中,一般用戶也用不到這個中斷。
▲SystemTick:這是對應CPU內部的SystemTick定時器中斷。SystemTick是所有Cortex-M都擁有的一個基本定時器,它為整個系統尤其是操作系統提供一個時間基準。本書將在第17章 詳細介紹SysTick的用法。
在上述系統異常之外,LPC800還支持另外32個外設中斷,下表列出了這些中斷來源。
表1.外設中斷源列表
這些中斷向量只出現在LPC804中,LPC802沒有這些中斷向量。由于LPC84x的中斷源數量多于Cortex-M0+的NVIC所支持的外部中斷數目,有些中斷源共用了同一個中斷請求信號,例如序號11、29、30和31的中斷都是復用的,軟件需要通過相應的外設寄存器判斷真正的中斷源和原因。
1.1.3 中斷向量
“中斷是打斷CPU程序順序執行,并轉去執行另一段程序的一種機制”,當中斷發生時,CPU需要知道中斷處理程序的地址并轉去執行相應的處理。
在所有的CPU里面,都是為每一個中斷源安排了一個固定的地址,當發生對應的中斷時,CPU通過這個固定地址進入中斷處理程序,進入的機制有兩種。一種機制是發生中斷時,CPU直接從那個地址取出中斷處理程序的指令并從這個地址開始順序執行,8051就是采用的這種進入機制。
另一種機制是,CPU從那個固定地址取出中斷處理程序的開始地址,即所說的“中斷向量”,然后從這個開始地址順序取出指令并執行,LPC800內置的Cortex-M0+就是采用這種機制。所有中斷源的處理程序開始地址,集中放在一個固定的區域,被稱為中斷向量表。
在8051里,設計者為每個中斷源預留了固定8個字節的地址空間。這樣設計的好處是,如果用戶的中斷處理程序很短,能夠安排在8個字節內完成,則處理效率很高,中斷響應的時間也非常短。
隨著CPU的功能越來越豐富,處理的內容越來越多,幾乎不會再有那種超短的中斷處理程序,因此目前幾乎所有的CPU都只為每個中斷源預留一個中斷向量的地址空間,只存放中斷程序的入口地址。這樣做可以最大限度地減少CPU預留的地址空間長度,同時還為重定位中斷向量表提供了方便。
在LPC800中,CPU預留了地址空間0x0000 0000 ~ 0x000000BF作為異常與中斷的向量表,下表是LPC82x的中斷向量表的內容安排。
表2.LPC82x中斷源向量表內容分配一般的用戶基本不需要關心這個表的內容,因為在給出的啟動文件中已經幫你填好里面的內容。
1.1.4 中斷優先級和中斷嵌套
當出現多個中斷事件時,就需要有優先級的概念,CPU依據優先級確定如何響應多個同時出現的中斷。
在LPC800中的中斷優先級是指,一個高優先級的中斷可以打斷CPU正在處理的一個低優先級中斷。這就是中斷嵌套的概念,即一個高優先級的中斷處理程序嵌在另一個低優先級中斷處理程序中。
圖2.中斷嵌套示意圖
從上面示意圖可以看到,優先級較低的IRQ2打斷了CPU當前的處理,CPU進入相應的處理程序ISR2,在ISR2處理還沒有結束時,又發生了較高優先級的IRQ1中斷,此時CPU轉去執行IRQ1的中斷處理程序ISR1,當ISR1結束返回后,CPU繼續執行ISR2并最終返回到被打斷的處理程序中(圖中藍色部分)。
對于多個相同優先級的中斷事件,如果它們的出現有先后順序,則CPU按照先來先到的原則順序處理,不會發生嵌套。如果它們同時出現,則CPU按照中斷編號的數值大小順序處理,編號小的中斷會先被處理,也不會發生嵌套,表3的第4列給出了每個中斷的編號。
表3的第5列給出每個中斷源或異常中斷源的優先級,除了少數異常中斷的優先級是固定的以外,其它中斷源的優先級都是軟件可配置的。
在LPC800中,可配置的優先級有四級,用0~3表示,0為最高優先級。
在ARM CMSIS庫里,有兩個函數與中斷優先級相關,用戶可直接調用設置中斷優先級或查詢某個中斷源的中斷優先級:
void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority);// 設置中斷優先級
uint32_t NVIC_GetPriority(IRQn_Type IRQn);// 讀取中斷優先級
使用表1的第4列“編號”作為NVIC_SetPriority和NVIC_GetPriority函數的第一個參數,指定需要操作的中斷源;NVIC_GetPriority的第二個參數是需要設定的優先級,只能取數值0~3,任何大于3的數值都可能導致不可知的結果。
1.1.5 中斷優先級和嵌套的演示例程
下面以一個例程演示中斷嵌套的現象和效果。
本節介紹的例程源代碼,位于附屬代碼目錄的Projects/Cookbook_NVIC_GPIO子目錄下。除特別說明,所有的代碼段都出自源文件Cookbook_NVIC_GPIO.c。關于附屬代碼目錄的說明,請參見第4章 :本書中的例程和項目文件。
在這個例程中,使用了板上的兩個按鍵S2、S4和兩個LED:D7、D8。下面是此例程所用的LPC824-Lite開發板的部分線路圖:
圖3.中斷嵌套例程部分線路圖
在程序中,S4標示為KEY_USER,對應引腳中斷0;S2標示為KEY_ISP,對應引腳中斷1。按鍵對應的信號上升沿和下降沿都會產生中斷:
■當進入KEY_USER的引腳中斷0的處理程序時,點亮LED0(P0_7),退出處理程序時熄滅LED0。
■當進入KEY_ISP的引腳中斷1的處理程序時,點亮LED1(P0_13),退出處理程序時熄滅LED1。
在例程的中斷處理程序中特意設置了2秒的等待,這樣可以清楚地模擬中斷嵌套的過程。
代碼片段1. 引腳中斷0的中斷處理程序
這段代碼非常簡單,讀者可以自行完成引腳中斷1的中斷處理程序。
下面是主程序中的片斷,包含對GPIO和中斷的初始化部分。
代碼片段2. 主程序中初始化GPIO和中斷
關于GPIO和引腳中斷的部分,將在后面章節介紹。
代碼片段2初始化好各個部分后,主程序即進入一個死循環,隨后每次按下按鍵將會進入代碼片段1對應的中斷處理程序。
按照下述步驟進行操作,就可以體會到中斷優先級的效用:
下面這個圖更加直觀地展示了按鍵按下的順序和中斷處理的順序。
圖4.中斷優先級演示程序時序圖
1.2 NVIC的幾個基本概念解讀
在這一節介紹一些NVIC特有的概念和含義,對于不需要關心中斷響應細節或沒有高實時性要求的讀者,可以略過此部分內容。
1.2.1中斷的尾部鏈接(Tail-chaining)
該特性的一個形象的叫法是“咬尾中斷”。這不是一種新的中斷形式,所有中斷都有此特性。
我們知道CPU響應中斷時,在進入中斷處理程序之前,需要保存當前運行任務的執行現場,即CPU的一些內部寄存器。目前絕大多數CPU(也包括ARM的所有內核)都是把執行現場保存在堆棧中,進入中斷處理程序之前執行PUSH操作,然后在退出中斷處理程序的時候,執行POP操作再返回被中斷的任務。
從上一節的圖14所展示的場景看,從步驟5.1進入步驟5.2時,即一個中斷處理程序的結束緊接著另一個中斷處理程序的開始。CPU需要恢復進入S4中斷處理前的現場,然后緊接著再保存同一個現場并進入S2 的處理程序,S2處理結束后再次恢復原有的現場。
很顯然,CPU在從S4處理程序進入S2 處理程序之間的恢復現場+保存現場的動作是多余的,這段時間被浪費了。中斷的尾部鏈接是ARM的NVIC使用的技術,可以有效地減少這種情況的操作時間。
由于沒有找到關于Cortex-M0+的時序圖,下面以ARM發布的Cortex-M3時序圖說明尾部鏈接的實現,除了顯示的時間周期上可能有所出入,但原理是一樣的。
圖5.中斷的尾部鏈接示意圖
圖5上半部分展示了ARM7TDMI的傳統中斷處理方式,在ISR1結束后,進入ISR2 之前,需要一組POP和另一組PUSH操作。圖的下半部分是Cortex-M中NVIC的處理方式,在兩個中斷處理程序之間,省卻了對CPU現場的處理。
途中標明“尾部鏈接”的這段時間,CPU需要判斷是否有掛起未處理的中斷請求,并根據優先級獲取需要處理的中斷的入口向量,再跳轉到新的處理程序中。因此,這個時間是省不掉的。
1.2.2 晚到的高優先級中斷處理(Late arrival preemption)
前面已經介紹了中斷優先級的概念,即高優先級的中斷處理程序會打斷低優先級的中斷處理。
當某個中斷事件A來臨時,CPU會保存當前執行現場后進入該事件的處理程序,如果在執行保存現場的操作時,還未進入到中斷處理程序A前,恰好有另一個高優先級的中斷事件B到達,此時為了節省再次保存現場的操作時間,NVIC會直接跳轉到這個高優先級的中斷處理程序B中,而暫時掛起早到的那個低優先級的中斷A。
圖6.晚到的高優先級中斷處理示意圖
如果中斷事件B到達時,CPU已經進入了中斷處理程序A,則不適用于上述操作流程。
1.2.3 終止退棧 (Pop preemption)
當CPU處理某個中斷事件結束時,如果另一個中斷事件正在等待處理,則3.2.1介紹的尾部鏈接機制可以有效地加快進入下一個中斷處理程序的進程。
如果CPU結束處理某個中斷事件并已經開始執行恢復現場的操作,此時另一個中斷事件剛好到達,傳統的處理方式是,完成恢復現場的操作后馬上再執行一次新的保存現場的操作?!敖K止退棧處理”的機制可以有效地減少該種情形的處理時間,避免多余的恢復現場再保存現場的壓棧和退棧操作,從而加快中斷處理的響應速度。
圖7.終止退棧處理示意圖
上圖中可以清楚地看出傳統的處理方式,和Cortex-M改進后的處理方式的區別,結合尾部鏈接的機制,實現了中斷的快速響應,增強了系統的實時性。
1.3.LPC800的NVIC使用
1.3.1 NVIC 控制寄存器
LPC800的NVIC(嵌套中斷控制器)支持32路中斷輸入源。NVIC共有5個寄存器(組),每個中斷輸入源對應其中的一組寄存器位。
表2.NVIC控制寄存器列表
序號
|
寄存器
|
功能
|
說明
|
1
|
ISER0
|
設置中斷使能
|
每個中斷輸入源對應1位。讀出’1’表示中斷已經使能。
寫入’1’ 使能中斷,寫入’0’無效果。
|
2
|
ICER0
|
清除中斷使能
|
每個中斷輸入源對應1位。讀出’1’表示中斷已經使能。
寫入’1’表示清除中斷使能,寫入’0’無效果。
|
3
|
ISPR0
|
設置中斷掛起
|
每個中斷輸入源對應1位。讀出’1’表示中斷已經掛起。
寫入’1’表示設置中斷的掛起狀態,寫入’0’無效果。
|
4
|
ICPR0
|
清除中斷掛起
|
每個中斷輸入源對應1位。讀出’1’表示中斷已經掛起。
寫入’1’表示清除中斷的掛起狀態,寫入’0’無效果。
|
5
|
IPR0~7
|
設置中斷優先級
|
這里總共有8個32位的寄存器,每個中斷輸入源對應其中的8位,但其中只有2位有效。
|
上表中前面4個寄存器都是32位寄存器,參照表2的序號,序號0的SPI0_IRQ對應這些寄存器的第0位,依次類推。
IPR0~7這8個寄存器,共有32個8位的字節,地址最小的第0個字節對應SPI0_IRQ,依次類推。
1.3.2 中斷的使能和掛起
使能某個中斷,是指允許某個中斷源所產生的中斷信號觸發中斷,并導致調用中斷處理程序。
在LPC800中配置中斷需要有至少兩個部分。首先是在能夠產生中斷的外設中,配置相應寄存器,使其能在指定的條件下產生中斷信號;例如如果希望在UART接收到數據時產生中斷,則必須配置UART模塊中相應的寄存器。其次是在NVIC中斷控制器中打開該中斷信號的傳輸通道,并最終觸發中斷,這就是常說的使能中斷。
當一個使能的中斷對應的中斷源,產生了中斷信號(例如前述的UART接收到數據),則NVIC內部的中斷掛起狀態位就會被置’1’,讀出ISPR0或ICPR0寄存器就可以看到這一位。當CPU響應該中斷請求進入中斷處理程序后,NVIC會自動地清除這個中斷掛起狀態位。
中斷掛起狀態位表示某個中斷請求是否已經被響應,用戶程序可以利用這一位,在沒有進入它的中斷處理程序之前查詢對應的狀況。特殊情況下,也可以主動地使用ICPR0寄存器清除這個掛起狀態,這樣該中斷的處理程序將不會被調用;例如,在一個較高優先級的中斷A處理程序中,主動地判斷某個較低優先級的中斷B,并直接處理這個請求,實現更快地處理那個低優先級的中斷B事件,當然處理完后要主動地清除這個掛起狀態,否則還會再進入中斷B對應的處理程序中。
1.3.3 操作NVIC 控制寄存器
用戶可以直接操作以上寄存器設置NVIC,ARM在CMSIS函數包里也提供了一些簡單的函數。
(1) NVIC_GetPendingIRQ和NVIC_GetPriority可以使用表3第4列“編號”中的負數作為輸入參數,例如使用-1表示讀取SysTick的掛起狀態或中斷優先級。
(2) NVIC_SetPriority可以使用表3第4列“編號”中對應SVCall、PendSV和SysTick的負數作為輸入參數設置它們的中斷優先級。
1.3.4 中斷向量表和中斷處理程序的入口
常用的IAR或Keil集成開發環境中,都提供了一個啟動文件,啟動文件中給出了默認的中斷向量表。
在IAR集成開發環境中,啟動文件的名字是:IAR_cstartup_M.s
在Keil集成開發環境中,啟動文件的名字是:Keil_startup_LPC8xx.s
在中斷向量表中,對應每一個中斷或異常的位置都默認設置了一個向量,它們都指向同一個位置,在這個位置上只有一條自我循環的指令,形成一個死循環。用戶如果使能了某個中斷或異常,要在發生中斷事件時跳到自己的中斷處理程序,則需要替換中斷向量表中向量指向自己的中斷處理程序,或者簡單的做法是使用與默認的向量相同的名字,編譯器會幫你替換向量。
下表列出了所有默認的中斷向量的名稱,供讀者參考。
表4.LPC82x中斷源向量默認名稱
當出現了你期望的中斷,但沒有跳轉到你自己的中斷處理程序時,除了檢查中斷源的配置以外,也需要檢查中斷處理程序的名稱是否拼寫對了,否則CPU會在發生中斷時,跳到默認的死循環中,造成死機的現象。
1.3.5 HardFault的處理
HardFault是大家經常遇到的問題,所有不能恢復的系統錯誤,都會產生這個異常中斷。
建議用戶自己為HardFault寫一個簡單的中斷處理程序,在其中通過輸出某種調試信息或點亮某個LED的方式,讓自己可以很清楚地知道出現了HardFault。這個處理程序結尾可以進入死循環而不必返回。
當出現Hard Fault異常中斷后,用戶可以在這個處理程序中設置一個斷點,當程序再次運行停到這個斷點時,即可通過集成開發環境(IDE)的函數調用窗口,回溯到觸發這個異常中斷的指令語句,可以很輕松地發現錯誤所在。
當然,對于復雜的應用,開發人員也可以在Hard Fault中調出堆棧中的內容,與一些重要的變量一起打包作為系統狀態以日志文件的形式保存下來,供隨后分析故障時使用。在PC的很多應用軟件中我們也看到過類似的機制,當軟件由于某種原因崩潰時,通常會彈出一個窗口詢問是否需要給原廠發送診斷日志,以便日后提高軟件質量。
END
更多恩智浦AI-IoT市場和產品信息,邀您同時關注“NXP客棧”微信公眾號
? ? ?NXP客棧
恩智浦致力于打造安全的連接和基礎設施解決方案,為智慧生活保駕護航。
長按二維碼,關注我們
恩智浦MCU加油站
這是由恩智浦官方運營的公眾號,著重為您推薦恩智浦MCU的產品信息、開發技巧、教程文檔、培訓課程等內容。
長按二維碼,關注我們
原文標題:LPC800前生今世 第三章-嵌套式向量中斷控制器
文章出處:【微信公眾號:恩智浦MCU加油站】歡迎添加關注!文章轉載請注明出處。
-
mcu
+關注
關注
146文章
17176瀏覽量
351660 -
恩智浦
+關注
關注
14文章
5863瀏覽量
107709
原文標題:LPC800前生今世 第三章-嵌套式向量中斷控制器
文章出處:【微信號:NXP_SMART_HARDWARE,微信公眾號:恩智浦MCU加油站】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論