STM32具有十分强大的中断系统,将中断分为了两个类型:内核异常和外部中断。并将所有中断通过一个表编排起来,下面是stm32中断向量表的部分内容:
- w8 _3 I* c5 E) Y) c ' _; D( Y* G* a% ^! y
$ j" R7 u( p; p
上图-3到6这个区域被标黑了,这个区域就是内核异常。内核异常不能够被打断,不能被设置优先级(也就是说优先级是凌驾于外部中断之上的)。常见的内核异常有以下几种:复位(reset),不可屏蔽中断(NMI),硬错误(Hardfault),其他的也可以在表上找到。 从第7个开始,后面所有的中断都是外部中断。外部中断是我们必须学习掌握的知识,包含线中断,定时器中断,IIC,SPI等所有的外设中断,可配置优先级。外部中断的优先级分为两种:抢占优先级和响应优先级。 什么是抢占优先级?抢占优先级比较霸道,一言不和就插队。抢占优先级高的,能够打断优先级低的任务,等优先级较高的任务执行完毕后,再回来继续执行之前的任务。所以当存在多个抢占优先级不同的任务时,很有可能会产生任务的嵌套。 什么是响应优先级?响应优先级则稍微谦逊些,比较有礼貌。响应优先级又被称为次优先级,若两个任务的抢占式优先级一样,那么响应优先级较高的任务则先执行,且在执行的同时不能被下一个响应优先级更高的任务打断,所以我说它比较有有礼貌。。 中断控制器(NVIC)因为stm32的中断系统比较复杂,所以在内核中有一个专门管理中断的控制器:NVIC. NVIC负责除了SYSTICK之外的所有中断的控制,十分重要! 在标准库中,提供了一套通过NVIC来控制中断的API,我们首先来看NVIC_Init()函数,这套函数首先要定义并填充一个结构体:NVIC_InitTypeDef 该结构体的定义如下: NVIC_IRQChannel 需要配置的中断向量 NVIC_IRQChannelCmd 使能或者关闭相应中断向量的中断响应 NVIC_IRQChannelPreemptionPriority 配置相应中断向量的抢占优先级 NVIC_IRQChannelSubPriority 配置相应中断的响应优先级
结构体的四个成员都比较好理解,这里就不再累述了。 不过要注意一点的是,NVIC只可以配置16种中断向量的优先级,其抢占优先级和响应优先级都用一个4位的数字来决定。在库函数中,将其分为了5中不同的分配方式: 第0组:所有的4位都有来表示响应优先级,能够配置16种不同的响应优先级。中断优先级则都相同。 第1组:最高一位用来配置抢占优先级,剩余三位用来表示响应优先级。那么就有两种不同的抢占优先级(0和1)和8种不同的响应优先级(0~7)。 第2组:高两位用来配置抢占优先级,低位用来配置响应优先级。那么两种优先级就各有4种。 第3组:高三位用来配置抢占优先级,低位用来配置响应优先级。有8种抢占优先级和2种相应优先级。 第4组:所有位都用来配置抢占优先级,即有16种抢占优先级,没有响应属性。
这5种不同的分配方式,根据项目的实际需求来配置。 配置的API如下: - NVIC_PriorityGroupConfig();
复制代码
\+ I4 x4 }& j其中括号内可以输入以下一个参数,代表不同的分配方式: NVIC_PriorityGroup_0 NVIC_PriorityGroup_1 NVIC_PriorityGroup_2 NVIC_PriorityGroup_3 NVIC_PriorityGroup_4
: a' m, n, C3 r# H4 q* t: k( s
EXTI外部中断STM32的所有GPIO都引入到了EXTI外部中断线上,也就是说,所有的IO口经过配置后都能够触发中断。下图就是GPIO和EXTI的连接方式: 从上图我们可以看出,一共有16个中断线: EXTI0到EXTI15. 每个中断线都对应了从PAx到PGx一共7个GPIO。也就是说,在同一时刻每个中断线只能相应一个GPIO端口的中断,不能够同时相应所有端口的中断事件,但是可以分时复用,在程序执行过程中,这个不需要我们太多的去关心。我们关心最多的是中断触发的方式: 7 @* b+ E0 Z. m
z, O! t* Z% A) J; [1 F* H
在EXTI中,有三种触发中断的方式:上升沿触发,下降沿触发,双边沿触发。根据不同的电路,我们选择不同的触发方式,以确保中断能够被正常触发。 5 M: X9 t$ G+ e$ K
实例为了便于理解,这里我们将中断配置代码贴上来。 - void NVIC_Configuration(void)2 M" N* v7 j* T7 J) c- }9 _
- {/ `* W6 ]' V' G+ M8 f
- NVIC_InitTypeDef NVIC_InitStructure;
2 \9 d V* `8 H2 Q' ]) W' \2 k0 l. k - 8 Q1 u! E! |: x! g! w
- /* 配置NVIC为优先级组1 */& Y0 y$ p3 f8 k. ~. b; E* D* g2 W
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);' U" y' H+ K, S8 s# I% i: ~! b' {! v
-
( n7 |! z% r2 a" a - /* 配置中断源:按键1 */7 X1 q* k/ a, m5 T7 i, P. \
- NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; //配置为EXTI0通道! Y2 ^' z$ J- H# l/ n; Q
- /* 配置抢占优先级 */8 p2 V8 T( x8 N: |
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
! A) A4 u a9 A1 ?' y - /* 配置子优先级 */
6 ~1 N' z' R3 I2 n- T |/ I' K - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
: G3 D+ V8 W/ ^; v: m) F+ y X - /* 使能中断通道 */
/ V9 R8 C j9 H! B, U5 i7 ?. X - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;3 n% y. ^0 r+ k9 Y4 q3 n
- NVIC_Init(&NVIC_InitStructure); //将上述配置参数传入中断初始化函数
, r4 h. b ^* R* t - }
/ [. c* w, y7 x2 G+ O* f1 ~ - 4 z' V8 @! e8 B+ {
复制代码
X! T3 M2 D5 v; Z: u. K: M除了中断线的配置,我们还要配置对应引脚 - void EXTI_Key_Config(void)# G: O M2 Z' u- K* B2 b4 O* X/ q
- {
1 A( n$ Y# K; ?4 S' ] - GPIO_InitTypeDef GPIO_InitStructure; . y b5 D$ y4 n3 G$ L
- EXTI_InitTypeDef EXTI_InitStructure;( v8 [' r& i+ L( ^/ y. v0 ^
) R# y# F3 m' j: j" K: p- /*开启按键GPIO口的时钟*/
N4 Y, _ H- u+ g) F1 Z4 o9 z - RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE);
3 T2 c! S3 ?! l - ' ?( g" j% S4 l0 \2 Y& Z$ \6 z) k
- /* 配置 NVIC 中断*/. Z8 S4 y9 X1 ^$ F1 ~$ m7 p9 K
- NVIC_Configuration();
, k' \+ s8 J& N( d3 O. b - /*--------------------------KEY1配置-----------------------------*/
( B1 c( i, i* a9 ? - /* 选择按键用到的GPIO */
6 g8 J$ r* z& m2 _9 H - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;' b& o7 ?# A0 F% ~
- /* 配置为浮空输入 */ 9 i; p6 H9 t' U3 D6 K4 n) I
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
9 b2 G8 O: [9 j% e9 J# T - GPIO_Init(KEY1_INT_GPIO_PORT, &GPIO_InitStructure);2 O9 r! `9 D: B$ s N/ N
" G4 A8 l# ~! l+ K/ k" ?; \- /* 选择EXTI的信号源 */
& A1 z, {# A4 r; b2 ]- z - GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); 5 W: B- a3 J' Z/ `/ W8 V
- EXTI_InitStructure.EXTI_Line = EXTI_Line0;
1 B$ h/ w2 d3 i - 2 W/ p3 _. J' s4 }7 m, e& L
- /* EXTI为中断模式 */
9 u* l) F* D' p - EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;5 q2 D D5 ^+ U
- /* 上升沿中断 *// h; K) f f4 G! a5 R. ~* |( v' H, _
- EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
$ s( V1 ] h# h, \) O* e% ^4 M- V - /* 使能中断 */
+ r* q: z/ T8 ?4 K - EXTI_InitStructure.EXTI_LineCmd = ENABLE;0 h4 s, I& M2 q1 }
- EXTI_Init(&EXTI_InitStructure);
4 [; G2 Z: M; Q6 H/ \5 h - }
( R) S0 z1 X$ p# W1 x3 \
复制代码 : y2 D3 i0 D+ U) R z4 C6 \" p
至此,中断的配置完毕。相信你已经看出来,上述代码是将PA0配置为上升沿中断。不过,现在只能够说该中断已经配置完毕,但我们还不能使用它。我们还缺少一个中断的执行函数。 当中断被触发后,程序要马上跳转到中断函数去执行中断操作,这个函数在工程创建时默认时没有的。需要你自己去添加。而且需要注意的是,中断函数的名称必须是由标准库提供的,否则无法识别。 我们打开startup_stm32f10x_hd.s这个文件,在里面能找到这么一段代码: - ; External Interrupts: y. ?$ f* a4 [: ~
- DCD WWDG_IRQHandler ; Window Watchdog
+ `0 n. z# u P$ w - DCD PVD_IRQHandler ; PVD through EXTI Line detect' F; ]' e% E& w+ J5 E
- DCD TAMPER_IRQHandler ; Tamper
* s. |4 g; p- Z! C - DCD RTC_IRQHandler ; RTC6 A% s4 ~8 P) a# p" R4 t
- DCD FLASH_IRQHandler ; Flash
0 K* N0 E2 u" V% W/ [ - DCD RCC_IRQHandler ; RCC
$ |& |3 r+ e& k: B3 K3 ?$ \* @ - DCD EXTI0_IRQHandler ; EXTI Line 0
3 b1 O) u) D" e/ G6 M$ E - DCD EXTI1_IRQHandler ; EXTI Line 1 e9 g! u' n7 x! J
- DCD EXTI2_IRQHandler ; EXTI Line 2" }7 u+ x1 n+ J! Q- Y; L/ e4 c
- DCD EXTI3_IRQHandler ; EXTI Line 3
: R3 R" T B+ C- K - ...
9 b+ h# ]5 a8 o* N - ...
, J! M: m- G3 c' A. e0 H3 H$ z - ...
+ `+ l* R8 t# V3 F9 M/ m
复制代码
$ }$ Q1 s7 q- R9 _' k! B不难看出,EXTI0_IRQHandler 就是中断线0的中断函数,所以,我们把这个函数添加到工程中即可。最好添加到stm32f10x_it.c 这个文件中,方便管理。 可以在这个函数中添加你想要的功能,代码如下: - void EXTI0_IRQHandler(void)5 {- h& s: t, T" V" h% D
- {
: [, C' o" J- j; Y, f9 A - //确保是否产生了EXTI Line中断
8 D% q) Q" F' }- B - if(EXTI_GetITStatus(EXTI_Line0) != RESET)
2 L5 \/ {: A! n; o - { i/ P% I7 {4 y" X Z$ A
- /******/
! S: Z5 |% g% x5 e% D+ m8 z+ R. D - //LED闪烁相关代码
z q% L( u* {2 j8 t$ U) J3 a - /******/. ]( K; x& Y/ M. D
- //清除中断标志位/ m6 w5 c% |2 A0 Y) ]( A- X9 ?
- EXTI_ClearITPendingBit(EXTI_Line0); % K. a) D( x% L+ f& n
- }
) V( W/ H T9 F8 Z - }
/ d7 A2 [& m+ j0 |- j" ?, ]5 I' B
复制代码
* o$ k1 h9 M Q7 j% _( }至此,整个中断的流程梳理完毕 6 L7 A- n6 u6 _
|