|
STM32具有十分强大的中断系统,将中断分为了两个类型:内核异常和外部中断。并将所有中断通过一个表编排起来,下面是stm32中断向量表的部分内容:
# w$ s, u) H$ F% C8 f; S. s5 v6 l ; O% h7 q8 x; {; q5 }. |
+ b, U% N7 H7 j3 {% y% W1 u( |
上图-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();
复制代码
' s- g$ o1 o; O: k) ~: r其中括号内可以输入以下一个参数,代表不同的分配方式: NVIC_PriorityGroup_0 NVIC_PriorityGroup_1 NVIC_PriorityGroup_2 NVIC_PriorityGroup_3 NVIC_PriorityGroup_4 6 q1 h' `! w! y3 g- h$ G( w
EXTI外部中断STM32的所有GPIO都引入到了EXTI外部中断线上,也就是说,所有的IO口经过配置后都能够触发中断。下图就是GPIO和EXTI的连接方式: 从上图我们可以看出,一共有16个中断线: EXTI0到EXTI15. 每个中断线都对应了从PAx到PGx一共7个GPIO。也就是说,在同一时刻每个中断线只能相应一个GPIO端口的中断,不能够同时相应所有端口的中断事件,但是可以分时复用,在程序执行过程中,这个不需要我们太多的去关心。我们关心最多的是中断触发的方式:
1 h5 C% L9 ]* | Q* V# Y- `
5 |' ^- E7 ~- o% C( M
在EXTI中,有三种触发中断的方式:上升沿触发,下降沿触发,双边沿触发。根据不同的电路,我们选择不同的触发方式,以确保中断能够被正常触发。 8 c* m" U4 V9 i2 U
实例为了便于理解,这里我们将中断配置代码贴上来。 - void NVIC_Configuration(void)0 O( V) T& K3 y, I: a
- {
# [. g$ X. d; j2 U' { - NVIC_InitTypeDef NVIC_InitStructure;
. ?, N8 a/ N8 C( T) L/ W -
. v$ U- f% o7 d6 q' p6 o1 K - /* 配置NVIC为优先级组1 */2 \4 J6 x6 H9 a
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);) L; O% _. t( k& L3 Z
-
7 x8 R* @9 C2 n E Y - /* 配置中断源:按键1 */( H; U7 h2 R. w+ R: n. |* @
- NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; //配置为EXTI0通道
8 q" g) V, Q: t+ q( o$ y - /* 配置抢占优先级 */
/ F" ^: y+ j$ m - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;# v1 w0 ]1 k' H% k& n9 d- n+ H
- /* 配置子优先级 */
& j5 l- g9 V9 R; d# ~8 E - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;& t4 W- Y0 K3 ^
- /* 使能中断通道 */
6 m, q) t3 ], H" [) U - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;. P7 }! U) `# C
- NVIC_Init(&NVIC_InitStructure); //将上述配置参数传入中断初始化函数
! Y& X& L6 ^, U8 L5 o' j; n - }& f* W0 l0 E' ]. O( d
, D+ i, f! z' ^* w
复制代码 1 A: J# R3 j b" X, r2 z3 P1 U
除了中断线的配置,我们还要配置对应引脚 - void EXTI_Key_Config(void)8 I% ~: }" @6 g! C3 L/ b
- {
. f; ]* d8 K4 j3 @9 ~4 I7 n - GPIO_InitTypeDef GPIO_InitStructure; 0 q1 z# @7 T$ _( M# }# ~1 O$ `, R# ^, H
- EXTI_InitTypeDef EXTI_InitStructure;! m6 S2 P. `4 A9 ~3 G! q5 O2 p
- . E2 {. x. H7 s- T% z; g
- /*开启按键GPIO口的时钟*/3 f% \' M7 N6 K" Z b& {' ?* Z; I
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO,ENABLE);
! D1 q9 L/ ]# O, Z8 L+ S4 | -
# L$ f; M5 \9 C% R& D; R3 | - /* 配置 NVIC 中断*/6 V+ Q4 b7 p$ O% j+ q
- NVIC_Configuration();
# n! k0 w+ d. b! q( c - /*--------------------------KEY1配置-----------------------------*/3 e( g( ~$ o+ \( i/ ?
- /* 选择按键用到的GPIO */
0 a" l3 L8 k4 t/ m" |/ i9 ^! Z - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;: _# u0 \! r4 L- q# I
- /* 配置为浮空输入 */ " O: }1 \1 ?% r' v
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
4 p& ~' h1 m+ v/ M. a9 i - GPIO_Init(KEY1_INT_GPIO_PORT, &GPIO_InitStructure);
" ]- j' v; A8 B6 ~/ h# E - - d5 o8 m! M; d; q' |- f# J+ n# b0 g4 K
- /* 选择EXTI的信号源 */: t7 j" d, B* d0 S2 X( R4 F G
- GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
& }( W- F& a4 c) I - EXTI_InitStructure.EXTI_Line = EXTI_Line0;: }2 g! I; I( P% W3 Z4 e0 Y) |
-
! O% t N9 f2 v I3 C! E0 l$ K6 M - /* EXTI为中断模式 */% f8 C: a/ Z/ N- G: B9 L
- EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
7 l- R: [7 ], s7 i- Q - /* 上升沿中断 */
1 V4 a2 D( c+ r7 M/ y - EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
# _( O2 b# H) i$ p. b - /* 使能中断 */ / j- Z6 i2 _0 T f. W1 ~- X v
- EXTI_InitStructure.EXTI_LineCmd = ENABLE;. ]" X% a* J' w2 k' U
- EXTI_Init(&EXTI_InitStructure);: N& {/ _; S7 W3 b+ n
- }
$ O2 e# R# \4 X4 A* {6 s
复制代码
5 D" t$ G7 o% M! n至此,中断的配置完毕。相信你已经看出来,上述代码是将PA0配置为上升沿中断。不过,现在只能够说该中断已经配置完毕,但我们还不能使用它。我们还缺少一个中断的执行函数。 当中断被触发后,程序要马上跳转到中断函数去执行中断操作,这个函数在工程创建时默认时没有的。需要你自己去添加。而且需要注意的是,中断函数的名称必须是由标准库提供的,否则无法识别。 我们打开startup_stm32f10x_hd.s这个文件,在里面能找到这么一段代码: - ; External Interrupts5 s" w& j# a$ M5 C
- DCD WWDG_IRQHandler ; Window Watchdog8 y6 w! r1 X& p9 |4 z4 e$ k
- DCD PVD_IRQHandler ; PVD through EXTI Line detect2 x! }* {1 k4 z3 s1 ?3 {( R
- DCD TAMPER_IRQHandler ; Tamper9 h7 h% R% x+ ?" @7 C3 |9 }
- DCD RTC_IRQHandler ; RTC* Z8 @9 A! W y; a# _
- DCD FLASH_IRQHandler ; Flash( r+ a0 R9 F' U1 r9 U6 G' p
- DCD RCC_IRQHandler ; RCC0 E/ S1 p- U7 I w
- DCD EXTI0_IRQHandler ; EXTI Line 0
+ `5 }0 H5 b# j - DCD EXTI1_IRQHandler ; EXTI Line 1( }7 p+ P0 D( Y- Z9 P) P
- DCD EXTI2_IRQHandler ; EXTI Line 2
- z7 Y9 j! l) n - DCD EXTI3_IRQHandler ; EXTI Line 3
+ ^$ _& K1 Z$ r9 B9 o4 a4 G5 E - ...; l9 ~) g6 ?" t1 J
- ...1 [- Q1 p' p: K& \( d. Z" D
- ...
, o- d/ F; P& p* m5 Q4 r7 V+ I
复制代码 8 Q8 [2 r, c: _8 C
不难看出,EXTI0_IRQHandler 就是中断线0的中断函数,所以,我们把这个函数添加到工程中即可。最好添加到stm32f10x_it.c 这个文件中,方便管理。 可以在这个函数中添加你想要的功能,代码如下: - void EXTI0_IRQHandler(void)' O. d O4 ~( C/ U
- {
, q/ c: h) k% U# a - //确保是否产生了EXTI Line中断
& ^0 U8 a1 W% |5 z3 w2 k3 D9 L) u# N - if(EXTI_GetITStatus(EXTI_Line0) != RESET) ' E: }; u& E( d! b
- {0 j, c( [) i5 w7 I/ E
- /******/
. Q$ R- s" P) X3 @" t9 J) O - //LED闪烁相关代码
- [6 i' Y) v9 Y1 V - /******/
! `- ]; Z' \1 M! y' T6 } - //清除中断标志位
% m+ y* x) K8 B% \. y - EXTI_ClearITPendingBit(EXTI_Line0); + z; x% Z: V/ W7 C
- } : [1 }& a, e0 l; i4 n: p
- }+ M* J/ D# x n1 d: x
复制代码 9 ~6 U' q4 M! u% I! I
至此,整个中断的流程梳理完毕
) n5 c. D' V. ^ `1 n |