|
Cortex-M3内核支持256个中断,其中包含了16个内核中断(异常)和240个外部中断,并且具有256级的可编程中断设置。但是,STM32并没有使用CM3内核的全部东西,而是只用了它的一部分。STM32有84个中断,包括16个内核中断(异常)和68个可屏蔽中断,具有16级可编程的中断优先级。而STM32F103系列上面,16个内核中断(异常)不变,而可屏蔽中断只有60个(在107系列才有68个)。 6 W" B7 G! w5 S: b4 t: e
注意一下:CM3的外部中断和STM32的外部中断不是一个概念。CM3:除了内核异常之外的都是外部中断;STM32:外部中断EXTI只有6个 其实,内核中断也叫内核异常。 ) Z3 D/ W8 r; C8 `( y
- 中断是指系统停止当前正在运行的程序转而其他服务,可能是程序接收了比自身高优先级的请求,或者是人为设置中断,中断是属于正常现象。
- 异常是指由于cpu本身故障、程序故障或者请求服务等引起的错误,异常属于不正常现象。; T. ^, [ X. i, A! N
. ]6 n0 ]$ g4 B1 r8 n' I
1 V( p4 r& x3 k5 O; c8 [
因此,下面就直接介绍一下STM32F103系列的16个内核中断(异常)、60个中断: 1 ^# L+ m6 |6 |8 d X
; q7 x- V8 @3 E& F1 o0 J$ O* v
* g+ H0 t4 Y% z; C7 r/ _# H( e s7 `
在中断向量表中从优先级7-66(中断号从0-59)代表着STM32F103的60个中断。优先级号越小,优先级越高。当表中的某处异常或中断被触发,程序计数器指针(PC)将跳转到该异常或中断的地址处执行,该地址处存放这一条跳转指令,跳转到该异常或中断的服务函数处执行相应的功能。因此,异常和中断向量表只能用汇编语言编写。
$ V3 u7 T) D" Q) Z; w: w2 x8 ^
在MDK中,有标准的异常和中断向量表文件可以使用(startup_stm32f10x_hd.s),在其中标明了中断处理函数的名称,不能随意定义。而中断通道NVIC_IRQChannel(即IRQn_Type类型)是在stm32f10x.h文件中进行了宏定义。 - o: t5 \- I! B
什么是NVIC?即嵌套向量中断控制器(Nested Vectored Interrupt Controller)。CM3的中有一个强大而方便的NVIC,它是属于Cortex内核的器件,中断向量表中60个中断都由它来处理。NVIC是Cortex-M3核心的一部分,关于它的资料不在《STM32的技术参考手册》中,应查阅ARM公司的《Cortex-M3技术参考手册》。Cortex-M3的向量中断统一由NVIC管理。
+ R; u2 p: o% v% {; h
NVIC的核心功能是中断优先级分组、中断优先级的配置、读中断请求标志、清除中断请求标志、使能中断、清除中断等,它控制着STM32中断向量表中中断号为0-59的60个中断!!外部中断信号从核外发出,信号最终要传递到NVIC(嵌套向量中断控制器)。NVIC跟内核紧密耦合,它控制着整个芯片中断的相关功能。 1 c' k+ e6 Z$ c4 `" a% q
STM32中断优先级分组中断优先级分组寄存器这60个中断,怎么管理呢?这就涉及到STM32的中断分组。STM32可以将中断分成5个组,分别为组0-4;同时,对每个中断设置一个抢占优先级和响应优先级。分组配置是由SCB->AIRCR寄存器的bit10-8来定义的。SCB->AIRCR是在哪里的呢?由于这是CM3内核定义的,在文档《Cortex-M3权威指南(中文)》中能查找到。
' X9 _9 |* f$ i+ C8 y) |3 x& \
具体的分配关系如下所示: % ]% o3 Q I) A$ a* E9 @! H
AIRCR中断分组设置表5 a, g5 M& l" S, w
2 C e# h! c8 r) W# o4 `* U
( i, |2 `" d& }& b l7 x/ T) N6 b m& D) \- ~( c
* R1 U* p* ^, h1 X3 s6 o7 p
其中AIRCR寄存器来确定是用哪种分组,IP寄存器是相对应于那种分组抢占优先级和响应优先级的分配比例。例如组设置成3,那么此时所有的60个中断优先寄存器高4位中的最高3位是抢占优先级,低1位为响应优先级。CM3中定义了8个Bit用于设置中断源的优先级,而STM32只选用其中的4个Bit。 b/ W8 A8 G7 B3 {$ m; F. `
抢占优先级的级别高于响应优先级,而数值越小所代表的的优先级越高。 - n' [8 l5 J1 b7 E0 h
介绍一下抢占优先级、响应优先级的区别:
9 a3 F8 w' Z- I5 W! k
- 高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的;
- 抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断;
- 抢占优先级相同的中断,当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行;
- 如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行;
( i; o. h6 X/ m" t" o8 W
- r. S- t/ \- Y- C 0 j$ X8 Y! Y2 h& C
" W5 P+ e2 u& e- ~$ x s/ D% |除此之外有两点需要注意:
+ E& M5 @( U$ X. V) Z5 a
- 打断的情况只会与抢占优先级有关, 和响应优先级无关!
- 一般情况下,系统代码执行过程中,只设置一次中断优先级分组,比如分组2,设置好分组之后一般不会再改变分组。随意改变分组会导致中断管理混乱,程序出现意想不到的执行结果。+ f; F" p- c2 U. A& l
1 s V( I: g7 w
" P: g# @* `# p
# z7 J( ]$ z i5 |4 i
中断优先级分组库函数
5 U2 d5 {# m1 F0 G' S, DCM3核的优先级分组方式,使用的设置函数NVIC_SetPriorityGrouping()。 ' F+ d- u# F$ o0 o" G. p
接下来介绍STM32的中断优先级分组函数NVIC_PriorityGroupConfig(),用来进行中断分组设置的,此函数是在固件库下misc.c文件中(文件目录是: A' y1 v' r2 j! c2 J, I$ R7 _% N* R) o
STM32F10x_StdPeriph_Lib_V3.5.0\Libraries\STM32F10x_StdPeriph_Driver\src\misc.c): : j1 O: }+ c9 r4 n+ B
- ; \) a! y( y; u+ [; A5 A) j: N
- void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
2 V" p% m' Y2 @+ b& S9 W - {
7 |; M3 X! I+ n2 ?1 w; O4 U6 Q - /* Check the parameters */
; M" @" g/ z2 R: A - assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));
" W# _: g8 @* w# h -
. E: X6 q1 k* q4 Q - /* Set the PRIGROUP[10:8] bits according to NVIC_PriorityGroup value */
+ l& u; L0 z- q4 x1 C: @: ~ - SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;& }, K- I6 ^+ o" f! @8 Y! g# r
- }
复制代码 4 C# Z6 `' {; E7 p) K$ l
函数的参数的取值,是在同文件中进行宏定义的:
/ {1 z! W4 } Y# m
- #define NVIC_PriorityGroup_0 ((uint32_t)0x700) /*!< 0 bits for pre-emption priority& {: N; O. ~7 [! O
- 4 bits for subpriority */
% k$ S+ b3 {: Y6 G+ b1 V) b - #define NVIC_PriorityGroup_1 ((uint32_t)0x600) /*!< 1 bits for pre-emption priority+ p; M' r% i: a" J+ [
- 3 bits for subpriority */
; u& f2 I+ q6 V) h9 q - #define NVIC_PriorityGroup_2 ((uint32_t)0x500) /*!< 2 bits for pre-emption priority/ S) u# |7 }- C: I/ y: w! X
- 2 bits for subpriority */
- Y! Q" Y2 m! P, H; s - #define NVIC_PriorityGroup_3 ((uint32_t)0x400) /*!< 3 bits for pre-emption priority
1 C8 k0 Q' C) w- q" ~ - 1 bits for subpriority */
% _6 @$ N+ P: j& F0 F: E/ G - #define NVIC_PriorityGroup_4 ((uint32_t)0x300) /*!< 4 bits for pre-emption priority
% P3 O. g+ ^% _ - 0 bits for subpriority */ C g8 n/ P/ Y1 @% Z3 e2 i
-
复制代码 4 T7 @8 a% {( G
$ _/ C9 @; p. a7 |9 z- K; t
STM32中断优先级管理
! w# P, y, E, F; e$ n. E6 K
1 A4 k9 N6 l f% h$ i中断优先级设置寄存器
" o' X. t! |+ l! H* z分组设置好了之后,怎么设置单个中断的抢占优先级和响应优先级? 6 ^$ x$ L5 J4 ^8 R% c
MDK为与NVIC相关的寄存器定义了如下的结构体,控制着中断向量表中60个中断(由于与中断内核有关,定义在core_cm3.h文件中): , b$ m# _, h& k) z5 M5 H
- * N5 G+ Z1 s, y. @5 n# `0 |8 f
- typedef struct
% o' R4 V8 M6 X8 _9 { - {5 X& F, G' b5 Y' ^
- __IO uint32_t ISER[8]; /*!< Offset: 0x000 Interrupt Set Enable Register */1 w2 s1 f3 @' A
- uint32_t RESERVED0[24]; ! g! K) t: _- L2 X: F, f
- __IO uint32_t ICER[8]; /*!< Offset: 0x080 Interrupt Clear Enable Register */
, F$ V3 E% V* j# G0 t1 e6 D - uint32_t RSERVED1[24];
/ ^7 F& N* L$ D; t1 c! X - __IO uint32_t ISPR[8]; /*!< Offset: 0x100 Interrupt Set Pending Register */
. I+ x- Z) _4 m3 V" s! l6 K7 f, H3 E - uint32_t RESERVED2[24];
; d5 c' l+ d0 h$ ` - __IO uint32_t ICPR[8]; /*!< Offset: 0x180 Interrupt Clear Pending Register */
3 k( @0 m( x" ~& ^. D - uint32_t RESERVED3[24];
- ^1 z: ?' A3 `' b& N - __IO uint32_t IABR[8]; /*!< Offset: 0x200 Interrupt Active bit Register */
% s% g3 n, l/ h - uint32_t RESERVED4[56]; * p& ^2 ?& r0 x2 i. A4 a
- __IO uint8_t IP[240]; /*!< Offset: 0x300 Interrupt Priority Register (8Bit wide) */
4 M7 T& _% y8 b" t7 |7 [8 Z - uint32_t RESERVED5[644];
5 |" f1 r9 K* [; k. G( { - __O uint32_t STIR; /*!< Offset: 0xE00 Software Trigger Interrupt Register */8 r E* j- b% u3 \. {- l
- } NVIC_Type;
复制代码 / F6 U, P E+ G9 S! M, d
. H* X4 u2 m" ^0 C! T我们依次介绍一下这些寄存器: 2 o( O& ]; L1 d( _) m' f6 o
先介绍几个寄存器组长度为8,这些寄存器是32位寄存器。由于STM32只有60个可屏蔽中断,8个32位寄存器中只需要2个就有64位了,每1位控制一个中断。 % u1 b+ K. ~' s- O+ i
- ISER[8](Interrupt Set-Enable Registers):中断使能寄存器。其中只使用到了ISER[0]和ISER[1],ISER[0]的bit0~bit31分别对应中断0~31。ISER[1]的bit0~27对应中断32~59。要使能某个中断,就必须设置相应的ISER位为1,使该中断被使能(这仅仅是使能,还要配合中断分组、屏蔽、I/O口映射等设置才算完整)。具体每一位对应哪个中断参考stm32f103x.h里面第140行。
- ICER[8](Interrupt Clear-Enable Registers):中断移除寄存器。该寄存器的作用于ISER相反。这里专门设置一个ICER来清除中断位,而不是向ISER位写0,是因为NVIC的寄存器写1有效,写0无效。
- ISPR[8](Interrupt Set-Pending Registers):中断挂起控制寄存器。通过置1可以将正在进行的中断挂起,执行同级或者更高级别的中断。写0无效。
- ICPR[8](Interrupt Clear-Pending Registers):中断解挂控制寄存器。通过置1可以将正在挂起的中断解挂。写0无效。
- IABR[8](Interrupt Active-Bit Registers):中断激活标志位寄存器。这是一个只读寄存器,可以知道当前在执行的中断是哪一个(为1),在中断执行完后硬件自动清零。; G6 ?( c, Z, Z8 s( l
- 2 |$ W1 u, G. f
* f2 _! h& ~& A3 P- ?- l$ Q
最后,介绍一个寄存器组长度为240,这个寄存器为8位寄存器。240个8位寄存器,每个中断使用一个寄存器来确定优先级。由于CM3由240个外部中断,所以这个寄存器组的数目就是240(注意与上面寄存器的区别,一个是一个寄存器控制一个,一个是一位控制一个)。
7 }% Y Q) [4 Z& L$ C* Z
- IP[240](Interrupt Priority Registers):中断优先级控制的寄存器。这是用来控制每个中断的优先级。由于STM32F10x系列一共60个可屏蔽中断,故使用IP[59]~IP[0]。其中每个IP寄存器的高4位[7:4]用来设置抢占和响应优先级(根据分组),低4位没有用到。而两个优先级各占几个位又要由上面讲到的中断优先级分组决定。- n3 H; k, j5 K
1 k. ^' S+ M6 _2 h$ S% \* `/ [
+ S8 X' J9 x R0 @+ `$ F( p3 w" X5 m( U. u9 K9 K7 D9 {
中断优先级设置库函数1 y) c# c: l9 r, {6 }6 w- q! T8 _
接下来介绍如何使用库函数实现中断优先级管理,这里使用NVIC_Init()函数来进行对每个中断优先级的设置(misc.c文件中) * X8 u# B' {8 V* Q5 l0 {5 f
- K" o8 U) X* e+ s
- void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)) ]' L V- U* F6 Y3 }6 l& }& X
- {# L; `/ Z: i! U" u
- uint32_t tmppriority = 0x00, tmppre = 0x00, tmpsub = 0x0F;
. t3 a& g4 t& L - . w' i& W/ i+ ^" g$ O# P' ?! F
- /* Check the parameters */6 O: b0 t# w6 f& u) e! V
- assert_param(IS_FUNCTIONAL_STATE(NVIC_InitStruct->NVIC_IRQChannelCmd));8 D9 P# e5 j: G |8 Z, s c) J
- assert_param(IS_NVIC_PREEMPTION_PRIORITY(NVIC_InitStruct->NVIC_IRQChannelPreemptionPriority)); - Q5 Z/ ]. M6 x! ^% W( \0 C; K
- assert_param(IS_NVIC_SUB_PRIORITY(NVIC_InitStruct->NVIC_IRQChannelSubPriority));: ^' x; l* n( E$ @2 M
- * l. H, t# S; j$ e1 Y. `8 ]* c
- if (NVIC_InitStruct->NVIC_IRQChannelCmd != DISABLE)
0 \5 Y3 m6 ] v% i - {) z# H6 ]5 n, |/ E
- /* Compute the Corresponding IRQ Priority --------------------------------*/ 8 w* \! p( e9 W
- tmppriority = (0x700 - ((SCB->AIRCR) & (uint32_t)0x700))>> 0x08;: b3 d' s& ?' Z* y
- tmppre = (0x4 - tmppriority);
, ?$ ?+ G3 H: f* w! s( S8 Z/ J - tmpsub = tmpsub >> tmppriority; p& ~1 {0 }3 `% d% S" A
- ( H9 F' V' P! s. Q) a# C2 d m7 a
- tmppriority = (uint32_t)NVIC_InitStruct->NVIC_IRQChannelPreemptionPriority << tmppre;& P; u+ N) l( ~* v( s: O
- tmppriority |= NVIC_InitStruct->NVIC_IRQChannelSubPriority & tmpsub;
* u: _) x" r0 u H- Q+ T& V$ { - tmppriority = tmppriority << 0x04;* y" D. p0 U: B6 P; e
- $ J) N8 i) L: b
- NVIC->IP[NVIC_InitStruct->NVIC_IRQChannel] = tmppriority;1 y4 u! C2 z+ k Y3 |
- % M& [1 A& l6 n$ y# o: `5 A
- /* Enable the Selected IRQ Channels --------------------------------------*/& H: v" D3 }6 @& T
- NVIC->ISER[NVIC_InitStruct->NVIC_IRQChannel >> 0x05] =
% y4 u; p, u; v- G, T - (uint32_t)0x01 << (NVIC_InitStruct->NVIC_IRQChannel & (uint8_t)0x1F);
* G( x% q7 t, d3 P# X - }
1 t; @/ H" d4 _. D6 X - else
% [) A3 r6 l r/ S% `) ` - {
; b9 A9 g0 w% s) ^" d& S - /* Disable the Selected IRQ Channels -------------------------------------*/
9 Q# j& Z% e u4 y, H - NVIC->ICER[NVIC_InitStruct->NVIC_IRQChannel >> 0x05] =) Y3 X2 x9 I& i. _5 E- v
- (uint32_t)0x01 << (NVIC_InitStruct->NVIC_IRQChannel & (uint8_t)0x1F);
1 @$ z D( e- F% l4 J - }2 V! }& y% i8 w* i6 Y4 D3 p
- }. Z/ U% \3 B; }1 \# o, U& s# }2 p
复制代码 5 t4 n4 S7 y3 T4 }/ q4 B' }
其中,NVIC_InitTypeDef为一个结构体,它的成员变量为:
" V. d" e m# l& d L
* i; u1 b" e: f- typedef struct
: C' w, x: _) x6 u7 H I' E - {! Z; Y8 h% O2 _; t. q
- uint8_t NVIC_IRQChannel; /*!< Specifies the IRQ channel to be enabled or disabled.# K( z7 K+ e, {
- This parameter can be a value of @ref IRQn_Type ( _ ~8 m$ S$ h# A: F7 q: _ r
- (For the complete STM32 Devices IRQ Channels list, please$ @) G( _" y" @8 y R
- refer to stm32f10x.h file) */& U4 i/ _( t5 k, D( p
- 9 d3 s( K) ?; f2 B3 t$ G y2 E
- uint8_t NVIC_IRQChannelPreemptionPriority; /*!< Specifies the pre-emption priority for the IRQ channel# z. e4 l+ ^* L4 Z0 ~7 e
- specified in NVIC_IRQChannel. This parameter can be a value" W8 n; @1 g" H* E
- between 0 and 15 as described in the table @ref NVIC_Priority_Table */- E* }3 N( }* p$ A! U
-
5 A6 a" p; A' ~) N - uint8_t NVIC_IRQChannelSubPriority; /*!< Specifies the subpriority level for the IRQ channel specified0 g' o m+ G# U7 m& e
- in NVIC_IRQChannel. This parameter can be a value
% B( Q8 A& f" i9 z - between 0 and 15 as described in the table @ref NVIC_Priority_Table */
- b7 ^) ^ V% D% p -
4 m4 I8 S- P8 B2 v5 j; A1 M - FunctionalState NVIC_IRQChannelCmd; /*!< Specifies whether the IRQ channel defined in NVIC_IRQChannel
1 [2 Q4 y- ?0 }; B0 f, }7 U - will be enabled or disabled. J* `) j% s. W% l' j* H5 ^
- This parameter can be set either to ENABLE or DISABLE */ * ~2 }; j* E7 Y: w, l) c$ J. {
- } NVIC_InitTypeDef;
! k2 ?3 S6 j. A$ W( V
复制代码
3 x- {: K y1 f0 FNVIC_InitTypeDef结构体有4个成员变量: - NVIC_IRQChannel:定义初始化的是哪一个中断,这个可以在stm32f10x.h文件中查到每个中断对应的名字,如USART1_IRQn;
- NVIC_IRQChannelPreemptionPriority:定义此中断的抢占优先级别;
- NVIC_IRQChannelSubPriority:定义此中断的响应优先级别;
- NVIC_IRQChannelCmd:该中断是否使能。) x7 W" t* ^) x: [
, f7 Z: M# {8 d $ M. k' h3 d/ D& @
; |2 `7 I: N G' B
其实我们看NVIC_Init()函数内部使能中断,也是通过ISER寄存器配置的。这与我么之前的内容并不矛盾。函数内部使用NVIC->ISER,而NVIC是一个宏定义, - #define NVIC ((NVIC_Type *) NVIC_BASE) /*!< NVIC configuration struct */
复制代码
" f6 L: s2 K: y4 E1 V也就是直接操作结构体来实现操作ISER寄存器。 比如,使能串口1中断,抢占优先级为1,响应优先级为2,初始化的方法为:
6 V1 Z, u4 P H- o( a) I
- " H. C' g0 o, z2 n7 r8 @; B2 Z
- NVIC_InitTypeDef NVIC_InitStructure;
5 |1 U1 X+ F# Q. t" C, C( `0 P - NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中断
2 G4 Q# {- @0 Q0 r5 Y5 ? - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1 ;// 抢占优先级为1
3 [8 t+ L- J6 u' R A4 n. L - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;// 子优先级位2
/ x. Q; `' X; t0 X, v& P - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//IRQ通道使能( Z' H T$ G5 Q& a
- NVIC_Init(&NVIC_InitStructure); //根据上面指定的参数初始化NVIC寄存器
复制代码
0 V) n9 R! A: Y4 H* I9 A
! c0 A; _ T' C( X6 s总结与分析
. t% ` |. F$ y( C3 y0 t最后总结一下中断优先级设置的步骤: 4 U; ~5 n0 Z4 C1 U; q
1、系统运行后先设置中断优先级分组。调用函数: - void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
复制代码
0 y* I" `8 L: |! Z! M. T1 R整个系统执行过程中,只设置一次中断分组;
% V& r9 }6 }/ L" h
2、针对每个中断,设置对应的抢占优先级和响应优先级: - void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
复制代码 6 e; R. O: I+ P- e1 `- m
3、如果需要挂起/解挂,查看中断当前激活状态,分别调用相关函数即可。
; F# h% _9 z; O" l" w, ^7 h; e* q# O& ?4 u7 g
|