一、NVIC中断机制
4 W) ~9 r. h# @6 L& Tstm32G431总共有111个中断源,所以有时难免有两个或者两个以上的中断一起来临,或者正在处理一个中断服务函数时突然又有一个中断来临,以上种种情况微控制器要怎样运行呢?
2 _6 T0 N5 m: j! D* F* N ~9 {( U* w% X, j0 ^, q
微控制器都有一个处理中断的机制,stm32系列芯片用到的机制是:NVIC
% K- {4 e+ U w/ q: a5 l8 G
! B. v3 i! `8 ^& v0 DNVIC: 嵌套向量中断控制器(Nested Vectored Interrupt Controller) f' m, s7 E# U- _5 W
7 m4 \& g5 u' s6 d: }( J. J2 cSTM32中NVIC,它是属于CM4内核的器件。NVIC 控制着整个芯片中断相关的功能,它跟内核紧密耦合,是内核里面的一个外设+ f+ t8 E# K) n0 m
6 ?1 i9 b7 y/ Y7 c4 a* w4 O" ~
$ t& s, {0 |; ?# @
2 ?2 g/ c8 U, P但是各个芯片厂商在设计芯片的时候会对CM4内核里面的NVIC进行裁剪,把不需要的部分去掉,所以说STM32的NVIC是CM4的NVIC的一个子集, @8 I, j4 H e- P* ^
( M+ D! s. z+ [, bCM4内核支持256个中断,其中包含了16个系统异常和240个外部中断,并且具有256级的可编程中断设置
* [1 `/ X" G# M$ Q8 }( d, |8 x7 q* O; h% N' u, F' c: q" }
但 STM32并没有使用CM4内核的全部东西,而是只用了它的一部分。 stm32G431芯片有111个中断,包括9个内核中断和102个可屏蔽中断,具有16级可编程的中断优先级,常用的就是这102个可屏蔽中断
; b! Q3 }, C0 Y
# L$ Z" r! \1 F, b& L6 aNVIC寄存器定义在core_cm4.h文件中
: N L+ ~/ l2 H; ?- h) c
3 {& x& C2 M6 y! N+ v7 q6 E+ s5 p4 L% b$ a& U$ I+ o
一般只用ISER,ICER和IP这三个寄存器( F: I7 R. Y* s0 _( q* f% _
/ A2 o$ ~! @% B' N' u**ISER:**中断使能寄存器组,CM4 内核支持 256 个中断,这里用 8 个 32 位寄存器来控制,每个位控制一个中断,对于STM32G4有102个可屏蔽中断,用到ISER[0~3]即可 {9 V- i5 p' k M9 P3 j( E
/ V7 t7 w2 l" H2 y$ w1 m使能某个中断,必须设置相应的 ISER 位为 1,使该中断被使能(这里仅仅是使能,还要配合中断分组、屏蔽、 IO 口映射等设置才算是一个完整的中断设置
2 T1 P9 ]; y1 ]6 w3 m+ R/ L7 G
' U+ p* p' B/ ^* n**ICER:**中断除能寄存器组,与 ISER 的作用恰好相反,是用来清除某个中断的使能的,要专门设置一个 ICER 来清除中断位,而不是向 ISER 写 0 来清除,是因为 NVIC 的这些寄存器都是写 1 有效的,写 0 是无效的
( b# L' t4 g& O( w5 L3 X1 a
) ?7 `% r( @6 P" h `4 w**IP:**中断优先级控制的寄存器组,IP 寄存器组由 240 个 8bit的寄存器组成,每个可屏蔽中断占用 8bit
s+ F4 j p1 I0 L1 Z9 c9 A
: r, c% h4 I! Z每个中断的优先级由一个8bit来设定,分为高低两个位段。高位段表示抢占优先级,低位段表示响应(子)优先级。 CM4允许最少使用位数为3位,即至少要支持8级优先级, X7 E) k, Q" D7 J7 f
& w0 |* E: e! j+ `" M% J
- A% I9 Y# X1 @) Q( O$ R6 J+ \
6 p1 e4 Q1 V" T但是,每个可屏蔽中断占用的 8bit 并没有全部使用,而是只用了高 4 位4 S+ Y+ d: D$ V: X2 P% z
' q! w' ]6 t! J# I ^% L
: {3 K' r& Z; @( v; O# j: {! V3 S* x/ V
而这两个优先级各占几个位又要根据 SCB->AIRCR中的中断分组设置来决定
5 W/ m( T, v* @/ N3 c2 k/ |
" z/ q1 s: W) e/ S) E7 z8 F如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行;高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的。而抢占优先级相同的中断,高优先级的响应优先级不可以打断低响应优先级的中断
* y4 s) L7 I3 j6 ~1 [6 O$ k$ {* {% m- Q0 e# x# Z1 P( G- Q! h6 t
注:! A) Y4 t2 l, ^3 M ^; @" f, O
(1)抢占优先级的级别高于响应优先级,而数值越小所代表的优先级就越高
0 w T! g/ O+ P8 |; S$ `, N" X
$ z {+ c, g3 ?1 R% U4 x7 z(2)抢占优先级是针对中断不同时刻发生的情况,响应优先级是针对中断同时发生的情况
5 ^' b9 `/ g" u
4 v4 V2 p4 H# W: }二、NVIC配置函数
, |! L0 \9 |% f9 ?9 `1 r( a- U* n1.HAL_NVIC_SetPriorityGrouping
- F$ r0 E0 k, ]0 ?( S9 n- i" i函数声明(在cortex.h中)1 N% b) M+ q3 d1 ^
6 d2 N6 H4 F7 y0 I7 \- void HAL_NVIC_SetPriorityGrouping(uint32_t PriorityGroup)" F$ K4 N0 k% x: ?- p
- {
# a+ E6 q+ l% I( e1 _/ v - /* Check the parameters */2 l% V3 r8 p% k# a
- assert_param(IS_NVIC_PRIORITY_GROUP(PriorityGroup));
1 r* P4 _, J7 M5 c% P; m - % @/ l& |% T, D
- /* Set the PRIGROUP[10:8] bits according to the PriorityGroup parameter value */
: K" i: ^( b$ F. ]2 J - NVIC_SetPriorityGrouping(PriorityGroup);
; |; N$ w: y: u6 h, ` - }
复制代码
% e) V% k! |% \' _4 b作用: 设置中断优先级分组
$ _7 g2 F6 R/ Q
0 v9 i! @4 U/ |. F" Q. g0 a形参选择:+ s; V4 L: h+ p: D4 H8 y. t2 f- a
NVIC_PRIORITYGROUP_0: 0位抢占式优先级,4位响应优先级;( S9 e$ e) j+ n/ y! w
NVIC_PRIORITYGROUP_1: 1位抢占式优先级,3位响应优先级;8 N) |( S- T4 i! z8 A/ l
NVIC_PRIORITYGROUP_2:2位抢占式优先级,2位响应优先级;: \, \2 a! i8 s/ V$ Y9 ^/ ^
NVIC_PRIORITYGROUP_3: 3位抢占式优先级,1位响应优先级;8 Q* I3 \7 T( M0 \1 @
NVIC_PRIORITYGROUP_4:4位抢占式优先级,0位响应优先级) m/ ?/ z' X* _
* }) h& j1 ? e
一般设置为2,这个函数在系统中只需要被调用一次,一旦分组确定就最好不要更改,否则容易造成程序分组混乱
$ @3 p( t' v" p5 Q; S
! r ?8 e$ f* k- m$ H, ]通过调用函数 NVIC_SetPriorityGrouping 来进行中断优先级分组设置1 M* d, V, k% u! J4 f! p- e% f
+ F0 o! g# a5 ?- I4 H; H3 s
- __STATIC_INLINE void __NVIC_SetPriorityGrouping(uint32_t PriorityGroup)
$ ?9 [+ t9 @. R8 s, ?$ d% A. q8 j - {% W1 {# B2 G( l2 p
- uint32_t reg_value;% Z2 X) f& r& @+ c
- uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */' @! l" h$ d: ~$ S: o+ N2 g Q6 R
9 F: F* Q- G3 n$ j- reg_value = SCB->AIRCR; /* read old register configuration */; Y1 k) u6 T1 {) ~; ^4 l+ @
- reg_value &= ~((uint32_t)(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk)); /* clear bits to change */
( h+ D: U4 H9 }( T) a$ u# e$ C - reg_value = (reg_value |
3 J1 R; E% g# u; ~* w - ((uint32_t)0x5FAUL << SCB_AIRCR_VECTKEY_Pos) |5 c6 ^( ~4 L# c% t
- (PriorityGroupTmp << SCB_AIRCR_PRIGROUP_Pos) ); /* Insert write key and priority group */
7 [8 G& b4 Y! c3 d9 x$ ]8 U - SCB->AIRCR = reg_value;$ D% I0 Z/ K/ g; Y& N
- }
复制代码 ! {& ~& Q) u. D' r; ]2 p
这个函数主要作用是通过设置 SCB->AIRCR 寄存器的值来设置中断优先1 F4 Y% E, V* Z6 N+ r
级分组
4 d6 y4 \5 x% z, f
1 ^+ t0 ^* i1 J9 J4 ^7 [+ C, L( j9 {* t" F设置优先级的分组值为2& F+ w8 j1 I1 D
+ I5 ~# ^5 b9 j( H' h! j
- HAL_NVIC_SetPriorityGrouping (NVIC_PRIORITYGROUP_2);
复制代码 8 b7 V. H! B' r
在main()函数里面,STM32CubeMX调用了HAL_Init(),该函数主要是对中断优先级分组, FLASH 以及硬件层进行初始化,并把系统中断优先级分组设置为分组4,所以在进行中断优先级分组的设置,只需要修改HAL_Init()里面设置优先级分组的代码即可。
/ F7 W9 J2 |; _9 V! @2 m' w# M! S4 m3 X9 S, q' F! `
2.HAL_NVIC_SetPriority4 }+ A0 S& y4 i2 d7 W# W8 N
函数声明: B0 I @# \* M4 x" ]& L
' }. a7 I9 T; u- void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority)
+ h4 e" ^6 ]8 p8 V" ] n2 j l - {
4 h; i3 Y) w, L' F/ _$ W - uint32_t prioritygroup;
d B l% z( y* X" V( f - 7 M- c! g& ]( q* D9 `
- /* Check the parameters */; P) z* L; r' o7 A
- assert_param(IS_NVIC_SUB_PRIORITY(SubPriority));4 b/ r# V7 L" Y" J% m& Q# q: `# P
- assert_param(IS_NVIC_PREEMPTION_PRIORITY(PreemptPriority));
. V; l0 ^4 h I6 ~7 u3 F+ R - 9 H: }% t" z& c7 f1 o/ B
- prioritygroup = NVIC_GetPriorityGrouping();2 `9 U) [: e8 k$ @! y" ?9 V
; [4 I" ^' Q* @( U4 `7 V- NVIC_SetPriority(IRQn, NVIC_EncodePriority(prioritygroup, PreemptPriority, SubPriority));2 ]: x3 J7 A( f6 N
- }
复制代码
. `- V0 h. H; Y0 E4 r7 o作用:设置中断的抢占优先级和响应优先级
5 D. t- J$ O5 Y S5 b! X# W0 N' v1 F
9 n- u1 g* u! r9 R第一个IRQn_Type类型参数,指定中断源
$ n1 v5 L& V$ [0 a2 e3 `: C( }6 b: }" X `第二个和第三个形参分别设定中断的抢占式优先级和响应优先级,这个的设置要与中断组配合使用
% X' t( K- i5 N/ b% P& q
5 Z6 J, \7 S% b- G0 f这个函数是调用NVIC_SetPriority()实现的
1 G: A4 `% W4 r9 D! ?
" \4 h8 S. F6 A7 n" h- __STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)6 ~4 ^7 `- q) p- K: X3 y; Y% F; d
- {
$ u0 b- S# i( b6 Y% G3 N - if ((int32_t)(IRQn) >= 0)# y; }* u/ m) B7 E4 L) [% x
- {
6 z h+ j3 \* D - NVIC->IP[((uint32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL);% g% y4 S" B7 w+ f0 q+ h; Z3 b
- }
) u% J' }$ u6 t# Z6 @! P - else
4 b" j) b5 p$ [ - {- Q- ]7 c* k7 d7 H/ v4 a
- SCB->SHP[(((uint32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL);# U: u5 k$ `" A% q
- }7 K, D# H. q0 P5 h* W/ J
- }
复制代码 4 Y, L5 a4 p, Y8 M! p: x
该函数通过设置SCB_SHP寄存器或者NVIC_IPRx寄存器实现功能6 k. n+ h2 K. ]# d+ k5 {
0 U9 h4 s! w5 ]; Z0 S( s3 a- o, \: Q6 v6 E
中断源:在stm32g431xx.h里面,属于IRQn_Type类型/ Y. a1 F5 t. a/ [* g6 b7 c: Z* r
' z. }' Y- a+ n+ J
$ |1 d7 N( w6 ?7 a, s! k3 z5 Q/ d# @4 Q& k
3.HAL_NVIC_EnableIRQ
# O4 F, J6 O# z' U+ L; t% y1 k函数声明% K% H/ d# F" T1 o4 z! B0 x
) f1 l/ T; m2 q2 H
- void HAL_NVIC_EnableIRQ(IRQn_Type IRQn)7 f! h/ R1 V! B C7 o q7 F: B
- {
$ v, F9 j9 r# E$ P& q - /* Check the parameters */+ S! s# K% j2 A
- assert_param(IS_NVIC_DEVICE_IRQ(IRQn));7 k# Q) _: M- d/ G5 M! I
- 1 f6 v0 X. j3 _1 b& ?. E/ Y
- /* Enable interrupt */
% W- p0 ]7 J& T& F% g8 t% G t3 J8 } - NVIC_EnableIRQ(IRQn);
. G1 A0 o) o J. p0 Z - }
复制代码
9 |- R5 M9 f, i I& m; d- b$ |: I作用:使能某个中断通道,传入中断源( w, _) G& G% A' G) i" ~ Y
: h) r6 \9 Z; ?- E* X4 T' _7 [
4.HAL_NVIC_DisableIRQ
9 K2 y& j0 I+ U" l函数声明
$ [7 E% `) N6 }4 [
4 K3 L* f, E( ~1 X! F) u8 [4 e/ q- void HAL_NVIC_DisableIRQ(IRQn_Type IRQn)# @) G# O+ i! X' \5 m/ \
- {1 v' x+ k4 ~) u+ O
- /* Check the parameters */3 u, M# M1 w$ h9 `! Z+ W. y
- assert_param(IS_NVIC_DEVICE_IRQ(IRQn));
7 S \7 [+ [0 e! g
& X- q. v( r" @0 v$ k- /* Disable interrupt */
. d8 F: B, o6 N& V: i+ P5 R - NVIC_DisableIRQ(IRQn);# l9 X/ w1 u' a) h& _. z
- }
复制代码 作用:清除某个中断使能的,传入中断源
$ ~: R& e) t* X" s, Y( y8 D4 O9 C3 f* H! r! d; ~/ g
中断优先级分组和中断优先级设置是两个不同的概念
+ C" Q+ K- q4 h+ [$ v- e7 K6 i& n9 D) Q/ Y" l
中断优先级分组是用来设置整个系统对于中断分组设置为哪个分组,分组号为 0-4,设置函数为HAL_NVIC_SetPriorityGrouping,确定了中断优先级分组号,也就确定了系统对于单个中断的抢占优先级和响应优先级设置各占几个位
# F$ N9 m2 e4 v2 {9 z( L8 p6 q
/ A# c8 J" ]4 y6 M, W3 ]8 w设置好中断优先级分组,接下来就是要对单个优先级进行中断优先级设置。也就是这个中断的抢占优先级和响应优先级的值,设置方法就是上面讲解的三个函数1 c; Q9 \1 E; h/ A# X2 W
' t8 S' K4 f' B4 V4 O5 R三、NVIC配置步骤( q2 v* I' U+ r9 t9 G1 ~9 k
1.系统运行开始的时候设置中断分组,确定组号,也就是确定抢占优先级和响应优先级的分配位数
5 X5 @! P/ d" l. m5 }' [1 H E
9 U4 }, A. l, o8 x3 z \( ~9 v设置函数为 HAL_NVIC_PriorityGroupConfig。对于 HAL 库,在文件 stm32f4xx_hal.c内部定义函数 HAL_Init 中有调用 HAL_NVIC_PriorityGroupConfig 函数进行相关设置,所以只需要修改 HAL_Init 内部对中断优先级分组设置即可
, L: S' _$ T1 M' o6 u9 O% h7 T9 ]9 d' K4 Y
2.设置单个中断的中断优先级别和使能相应中断通道,函数主要为函数HAL_NVIC_SetPriority 和函数 HAL_NVIC_EnableIRQ
. q- J7 G% O/ I& p2 }, I5 [$ C0 h2 X0 ~2 n1 ^
总结8 f* B: D1 ?1 K2 ]' r& t4 e# k
提示:这里对文章进行总结:
" L6 w! _. x: P! b1 g: |1 d2 A* {" `& y. v$ D7 s; C! n) H: e1 z X# ?8 @
STM32中有两个优先级的概念:抢占式优先级和响应优先级 ,响应优先级也称子优先级,每个中断源都需要被指定这两种优先级( a, J' o8 ~; M8 u+ d: u% b4 O
8 B; F$ `/ z4 C& _8 S' J具有高抢占式优先级的中断可以在具有低抢占式优先级的中断处理过程中被响应,即中断嵌套,或者说高抢占式优先级的中断可以嵌套在低抢占式优先级的中断中8 m1 Y5 P4 J$ W! U/ L1 J8 \" l6 W
, L0 Z$ K" Q. Q2 [* e' p2 C/ d
当两个中断源的抢占式优先级相同时,这两个中断将没有嵌套关系,当一个中断到来后,如果正在处理另一个中断,这个后到来的中断就要等到前一个中断处理完之后才能被处理' l F' X) Y( i' r
5 e3 e! \7 M8 |$ A; W) Z/ H
如果这两个中断同时到达,则中断控制器根据他们的响应优先级高低来决定先处理哪一个;
! T9 g0 J& c) v如果他们的抢占式优先级和响应优先级都相等,则根据他们在中断向量表中的排位顺序决定先处理哪一个。
# u( Z n% P# O$ I. q
# Z7 f1 @0 F7 S) z5 M$ O, E( t J8 y2 p- d$ r
% r/ u9 T- @& @3 h6 M9 ~3 f ?) L9 O
|