第16章 STM32中斷應用概覽
全套200集視頻教程和1000頁PDF教程請到秉火論壇下載:www.firebbs.cn
野火視頻教程優酷觀看網址:http://i.youku.com/firege
本章參考資料《STM32F4xx 中文參考手冊》第十章-中斷和事件、《 ARM Cortex™-M4F 技術參考手冊》-4.3 章節:NVIC和4.4章節:SCB—4.4.5的AIRCR。
STM32中斷非常強大,每個外設都可以產生中斷,所以中斷的講解放在哪一個外設里面去講都不合適,這里單獨抽出一章來做一個總結性的介紹,這樣在其他章節涉及到中斷部分的知識我們就不用費很大的篇幅去講解,只要示意性帶過即可。
本章如無特別說明,異常就是中斷,中斷就是異常,請不要刻意鑽牛角尖較勁。
16.1 異常類型
F429在內核水平上搭載了一個異常響應系統,支持為數眾多的系統異常和外部中斷。其中系統異常有10個,外部中斷有91個。除了個別異常的優先級被定死外,其它異常的優先級都是可編程的。有關具體的系統異常和外部中斷可在標准庫文件stm32f4xx.h這個頭文件查詢到,在IRQn_Type這個結構體里面包含了F4系列全部的異常聲明。
表格 12 F429系統異常清單
| 編號 |
優先級 |
優先級類型 |
名稱 |
說明 |
地址 |
| - |
- |
- |
保留(實際存的是MSP地址) |
0X0000 0000 |
|
| -3 |
固定 |
Reset |
復位 |
0X0000 0004 |
|
| -2 |
固定 |
NMI |
不可屏蔽中斷。 RCC 時鍾安全系統 |
0X0000 0008 |
|
| -1 |
固定 |
HardFault |
所有類型的錯誤 |
0X0000 000C |
|
| 0 |
可編程 |
MemManage |
存儲器管理 |
0X0000 0010 |
|
| 1 |
可編程 |
BusFault |
預取指失敗,存儲器訪問失敗 |
0X0000 0014 |
|
| 2 |
可編程 |
UsageFault |
未定義的指令或非法狀態 |
0X0000 0018 |
|
| - |
- |
- |
保留 |
0X0000 001C- 0X0000 002B |
|
| 3 |
可編程 |
SVCall |
通過 SWI 指令調用的系統服務 |
0X0000 002C |
|
| 4 |
可編程 |
Debug Monitor |
調試監控器 |
0X0000 0030 |
|
| - |
- |
- |
保留 |
0X0000 0034 |
|
| 5 |
可編程 |
PendSV |
可掛起的系統服務 |
0X0000 0038 |
|
| 6 |
可編程 |
SysTick |
系統嘀嗒定時器 |
0X0000 003C |
表格 13 F429外部中斷清單
| 編號 |
優先級 |
優先級類型 |
名稱 |
說明 |
地址 |
| 0 |
7 |
可編程 |
- |
窗口看門狗中斷 |
0X0000 0040 |
| 1 |
8 |
可編程 |
PVD |
連接EXTI 線的可編程電壓檢測中斷 |
0X0000 0044 |
| 2 |
9 |
可編程 |
TAMP_STAMP |
連接EXTI 線的入侵和時間戳中斷 |
0X0000 0048 |
| 中間部分省略,詳情請參考STM32F4xx 中文參考手冊》第十章-中斷和事件-向量表部分 |
|||||
| 84 |
91 |
可編程 |
SPI4 |
SPI4全局中斷 |
0X0000 0190 |
| 85 |
92 |
可編程 |
SPI5 |
SPI5全局中斷 |
0X0000 0194 |
| 86 |
93 |
可編程 |
SPI6 |
SPI6全局中斷 |
0X0000 0198 |
| 87 |
94 |
可編程 |
SAI1 |
SAI1全局中斷 |
0X0000 019C |
| 88 |
95 |
可編程 |
LTDC |
LTDC全局中斷 |
0X0000 01A0 |
| 89 |
96 |
可編程 |
LTDC_ER |
LTDC_ER全局中斷 |
0X0000 01A4 |
| 90 |
97 |
可編程 |
DMA2D |
DMA2D全局中斷 |
0X0000 01A8 |
16.2 NVIC簡介
在講如何配置中斷優先級之前,我們需要先了解下NVIC。NVIC是嵌套向量中斷控制器,控制着整個芯片中斷相關的功能,它跟內核緊密耦合,是內核里面的一個外設。但是各個芯片廠商在設計芯片的時候會對Cortex-M4內核里面的NVIC進行裁剪,把不需要的部分去掉,所以說STM32的NVIC是Cortex-M4的NVIC的一個子集。
16.2.1 NVIC寄存器簡介
在固件庫中,NVIC的結構體定義可謂是頗有遠慮,給每個寄存器都預覽了很多位,恐怕為的是日后擴展功能。不過STM32F429可用不了這么多,只是用了部分而已,具體使用了多少可參考《 ARM Cortex™-M4F 技術參考手冊》-4.3.11:NVIC寄存器映射。
代碼 17 NVIC結構體定義,來自固件庫頭文件:core_cm4.h
1 typedef struct {
2 __IO uint32_t ISER[8]; // 中斷使能寄存器
3 uint32_t RESERVED0[24];
4 __IO uint32_t ICER[8]; // 中斷清除寄存器
5 uint32_t RSERVED1[24];
6 __IO uint32_t ISPR[8]; // 中斷使能懸起寄存器
7 uint32_t RESERVED2[24];
8 __IO uint32_t ICPR[8]; // 中斷清除懸起寄存器
9 uint32_t RESERVED3[24];
10 __IO uint32_t IABR[8]; // 中斷有效位寄存器
11 uint32_t RESERVED4[56];
12 __IO uint8_t IP[240]; // 中斷優先級寄存器(8Bit wide)
13 uint32_t RESERVED5[644];
14 __O uint32_t STIR; // 軟件觸發中斷寄存器
15 } NVIC_Type;
在配置中斷的時候我們一般只用ISER、ICER和IP這三個寄存器,ISER用來使能中斷,ICER用來失能中斷,IP用來設置中斷優先級。
16.2.2 NVIC 中斷配置固件庫
固件庫文件core_cm4.h的最后,還提供了NVIC的一些函數,這些函數遵循CMSI規則,只要是Cortex-M4 的處理器都可以使用,具體如下:
表格 14 符合CMSIS標准的NVIC庫函數
| NVIC庫函數 |
描述 |
| void NVIC_EnableIRQ(IRQn_Type IRQn) |
使能中斷 |
| void NVIC_DisableIRQ(IRQn_Type IRQn) |
失能中斷 |
| void NVIC_SetPendingIRQ(IRQn_Type IRQn) |
設置中斷懸起位 |
| void NVIC_ClearPendingIRQ(IRQn_Type IRQn) |
清除中斷懸起位 |
| uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn) |
獲取懸起中斷編號 |
| void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) |
設置中斷優先級 |
| uint32_t NVIC_GetPriority(IRQn_Type IRQn) |
獲取中斷優先級 |
| void NVIC_SystemReset(void) |
系統復位 |
這些庫函數我們在編程的時候用的都比較少,甚至基本都不用。在配置中斷的時候我們還有更簡潔的方法,請看中斷編程小節。
16.3 優先級的定義
16.3.1 優先級定義
在NVIC 有一個專門的寄存器:中斷優先級寄存器NVIC_IPRx(在F429中,x=0...90)用來配置外部中斷的優先級,IPR寬度為8bit,原則上每個外部中斷可配置的優先級為0~255,數值越小,優先級越高。但是絕大多數CM4芯片都會精簡設計,以致實際上支持的優先級數減少,在F429中,只使用了高4bit,如下所示:
表格 15 F429 使用4bit表達優先級
| bit7 |
bit6 |
bit5 |
bit4 |
bit3 |
bit2 |
bit1 |
bit0 |
| 用於表達優先級 |
未使用,讀回為0 |
||||||
用於表達優先級的這4bit,又被分組成搶占優先級和子優先級。如果有多個中斷同時響應,搶占優先級高的就會搶占搶占優先級低的優先得到執行,如果搶占優先級相同,就比較子優先級。如果搶占優先級和子優先級都相同的話,就比較他們的硬件中斷編號,編號越小,優先級越高。
16.3.2 優先級分組
優先級的分組由內核外設SCB的應用程序中斷及復位控制寄存器AIRCR的PRIGROUP[10:8]位決定,F429分為了5組,具體如下:主優先級=搶占優先級
| PRIGROUP[2:0] |
中斷優先級值PRI_N[7:4] |
級數 |
|||
| 二進制點 |
主優先級位 |
子優先級位 |
主優先級 |
子優先級 |
|
| 0b 011 |
0b xxxx |
[7:4] |
None |
16 |
None |
| 0b 100 |
0b xxx.y |
[7:5] |
[4] |
8 |
2 |
| 0b 101 |
0b xx.yy |
[7:6] |
[5:4] |
4 |
4 |
| 0b 110 |
0b x.yyy |
[7] |
[6:4] |
2 |
9 |
| 0b 111 |
0b .yyyy |
None |
[7:4] |
None |
16 |
設置優先級分組可調用庫函數NVIC_PriorityGroupConfig()實現,有關NVIC中斷相關的庫函數都在庫文件misc.c和misc.h中。
代碼 18 中斷優先級分組庫函數
1 /**
2 * 配置中斷優先級分組:搶占優先級和子優先級
3 * 形參如下:
4 * @arg NVIC_PriorityGroup_0: 0bit for搶占優先級
5 * 4 bits for 子優先級
6 * @arg NVIC_PriorityGroup_1: 1 bit for搶占優先級
7 * 3 bits for 子優先級
8 * @arg NVIC_PriorityGroup_2: 2 bit for搶占優先級
9 * 2 bits for 子優先級
10 * @arg NVIC_PriorityGroup_3: 3 bit for搶占優先級
11 * 1 bits for 子優先級
12 * @arg NVIC_PriorityGroup_4: 4 bit for搶占優先級
13 * 0 bits for 子優先級
14 * @注意如果優先級分組為0,則搶占優先級就不存在,優先級就全部由子優先級控制
15 */
16 void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
17 {
18 // 設置優先級分組
19 SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;
20 }
表格 16 優先級分組真值表
| 優先級分組 |
主優先級 |
子優先級 |
描述 |
| NVIC_PriorityGroup_0 |
0 |
0-15 |
主-0bit,子-4bit |
| NVIC_PriorityGroup_1 |
0-1 |
0-7 |
主-1bit,子-3bit |
| NVIC_PriorityGroup_2 |
0-3 |
0-3 |
主-2bit,子-2bit |
| NVIC_PriorityGroup_3 |
0-7 |
0-1 |
主-3bit,子-1bit |
| NVIC_PriorityGroup_4 |
0-15 |
0 |
主-4bit,子-0bit |
16.4 中斷編程
在配置每個中斷的時候一般有3個編程要點:
1、使能外設某個中斷,這個具體由每個外設的相關中斷使能位控制。比如串口有發送完成中斷,接收完成中斷,這兩個中斷都由串口控制寄存器的相關中斷使能位控制。
2、初始化NVIC_InitTypeDef結構體,配置中斷優先級分組,設置搶占優先級和子優先級,使能中斷請求。
代碼 19 NVIC初始化結構體
1 typedef struct {
2 uint8_t NVIC_IRQChannel; // 中斷源
3 uint8_t NVIC_IRQChannelPreemptionPriority; // 搶占優先級
4 uint8_t NVIC_IRQChannelSubPriority; // 子優先級
5 FunctionalState NVIC_IRQChannelCmd; // 中斷使能或者失能
6 } NVIC_InitTypeDef;
有關NVIC初始化結構體的成員我們一一解釋下:
1)NVIC_IROChannel:用來設置中斷源,不同的中斷中斷源不一樣,且不可寫錯,即使寫錯了程序不會報錯,只會導致不想要中斷。具體的成員配置可參考stm32f4xx.h頭文件里面的IRQn_Type結構體定義,這個結構體包含了所有的中斷源。
代碼 20 IRQn_Type中斷源結構體
1 typedef enum IRQn {
2 //Cortex-M4 處理器異常編號
3 NonMaskableInt_IRQn = -14,
4 MemoryManagement_IRQn = -12,
5 BusFault_IRQn = -11,
6 UsageFault_IRQn = -10,
7 SVCall_IRQn = -5,
8 DebugMonitor_IRQn = -4,
9 PendSV_IRQn = -2,
10 SysTick_IRQn = -1,
11 //STM32 外部中斷編號
12 WWDG_IRQn = 0,
13 PVD_IRQn = 1,
14 TAMP_STAMP_IRQn = 2,
15
16 // 限於篇幅,中間部分代碼省略,具體的可查看庫文件stm32f4xx.h
17
18 SPI4_IRQn = 84,
19 SPI5_IRQn = 85,
20 SPI6_IRQn = 86,
21 SAI1_IRQn = 87,
22 LTDC_IRQn = 88,
23 LTDC_ER_IRQn = 89,
24 DMA2D_IRQn = 90
25 } IRQn_Type;
2)NVIC_IRQChannelPreemptionPriority:搶占優先級,具體的值要根據優先級分組來確定,具體參考表格 16 優先級分組真值表 。
3)NVIC_IRQChannelSubPriority:子優先級,具體的值要根據優先級分組來確定,具體參考表格 16 優先級分組真值表 。
4)NVIC_IRQChannelCmd:中斷使能(ENABLE)或者失能(DISABLE)。操作的是NVIC_ISER和NVIC_ICER這兩個寄存器。
3、編寫中斷服務函數
在啟動文件startup_stm32f429_439xx.s中我們預先為每個中斷都寫了一個中斷服務函數,只是這些中斷函數都是為空,為的只是初始化中斷向量表。實際的中斷服務函數都需要我們重新編寫,中斷服務函數我們統一寫在stm32f4xx_it.c這個庫文件中。
關於中斷服務函數的函數名必須跟啟動文件里面預先設置的一樣,如果寫錯,系統就在中斷向量表中找不到中斷服務函數的入口,直接跳轉到啟動文件里面預先寫好的空函數,並且在里面無限循環,實現不了中斷。
16.5 每課一問
1、庫文件core_cm4.h主要實現了什么?回去認真看庫的源碼
2、庫文件mics.c和mics.h主要實現了什么?回去認真看庫的源碼
3、如果實現一次軟件系統復位,具體是操作哪個寄存器的哪個位實現?答案:給內核外設SCB的AIRCR寄存器的位2:SYSRESETREQ 寫1即可實現一次系統復位。
