01. 概述) a% W, f) }0 x8 j" @- x2 D
中断是微控制器一个很常见的特性,中断由硬件产生,当中断产生以后CPU就会中断当前的流程转而去处理中断服务,Cortex-M内核的MCU提供了一个用于中断管理的嵌套向量中断控制器(NVIC)。
5 x3 q6 |, ]" Y% o2 Y! ~& B- l$ h/ D8 c( H4 l
Cortex-M3和M4的NVIC最多支持240个IRQ中断请求,1个不可屏蔽中断NMI、1个Systick滴答定时器中断和多个异常。
1 x: N6 o# Z& v b8 G d! u
5 f9 j1 F. M" {02. 中断管理3 \2 e' t, C1 Z- I7 G3 Q
Cortex-M处理器有多个用于管理中断和异常的可编程寄存器,这些寄存器大多数都在NVIC和系统控制块SCB中,CMSIS将这些寄存器定义为结构体。以STM32F407为例,打开core_cm4.h,有以下两个结构体。; ^- U8 W9 S I* g9 B$ z1 c1 c
# f3 b) J6 g* ?4 P# K) q8 yNVIC_Type类型 p- b5 _$ k) k F
% `6 I2 J9 C8 W- K3 G( ~5 B
- /** \brief Structure type to access the Nested Vectored Interrupt Controller (NVIC).. k* b% \- v0 O( N$ j2 U3 W
- */9 s2 R* O% B% r9 ^3 ~ `9 V
- typedef struct2 j5 o$ c+ p$ W& B: |+ C* ^0 k
- {0 m2 r D8 I/ w9 v8 \( H* `$ ~
- __IO uint32_t ISER[8]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */
3 ?& x* }. z3 r, o - uint32_t RESERVED0[24];
2 F2 Q) S, e7 P. i+ ~; G5 ` - __IO uint32_t ICER[8]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */
, n& \/ i7 V3 n) `5 M% V8 h0 ^ - uint32_t RSERVED1[24];
' O+ f& N; n9 t9 a# z - __IO uint32_t ISPR[8]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */6 A" c `2 q1 o2 J4 M" G( {3 o
- uint32_t RESERVED2[24];
- c2 m$ m5 A! f z% _: N - __IO uint32_t ICPR[8]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */
5 }$ k$ K- Z( V( d% ?. B- h/ c$ r+ N - uint32_t RESERVED3[24];
) c4 R6 R3 \. e% `! } - __IO uint32_t IABR[8]; /*!< Offset: 0x200 (R/W) Interrupt Active bit Register */+ u8 j7 W; q4 p
- uint32_t RESERVED4[56];
- _6 ?% n" x( [7 V! F/ o* C6 ]) i' ?' b - __IO uint8_t IP[240]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register (8Bit wide) */
" ~6 t% R& ?4 {9 ]# T3 p - uint32_t RESERVED5[644];
2 M5 A3 ^9 L; v. t - __O uint32_t STIR; /*!< Offset: 0xE00 ( /W) Software Trigger Interrupt Register */' k' U- p7 N% ?5 l, Z
- } NVIC_Type;
复制代码
1 N' C+ ?; G: G; @) v: i* W! ASCB_Type类型! r. p% _, d4 q5 \
3 R: V0 H- t( O, [3 ?! s; E- /** \brief Structure type to access the System Control Block (SCB).
1 M+ a" t. G3 O& ^ - */' G& Z3 W( R, c; Z
- typedef struct
* r' P" G% K( [5 ^, w - {6 {4 n9 Q A1 u9 v8 L# I
- __I uint32_t CPUID; /*!< Offset: 0x000 (R/ ) CPUID Base Register */; n& M3 d: }2 J, p
- __IO uint32_t ICSR; /*!< Offset: 0x004 (R/W) Interrupt Control and State Register */" L5 T7 ~6 X% Y" T0 H0 L$ b
- __IO uint32_t VTOR; /*!< Offset: 0x008 (R/W) Vector Table Offset Register */
, {3 Q' z. E0 v" p+ `" q- p - __IO uint32_t AIRCR; /*!< Offset: 0x00C (R/W) Application Interrupt and Reset Control Register */% e" N) x8 Y6 j. t! P
- __IO uint32_t SCR; /*!< Offset: 0x010 (R/W) System Control Register */
* ]" u" G4 N+ a0 H4 Y1 N - __IO uint32_t CCR; /*!< Offset: 0x014 (R/W) Configuration Control Register */
6 y# x& W+ {6 K8 f1 G - __IO uint8_t SHP[12]; /*!< Offset: 0x018 (R/W) System Handlers Priority Registers (4-7, 8-11, 12-15) */
/ C: D! a* B' J& f2 s0 M - __IO uint32_t SHCSR; /*!< Offset: 0x024 (R/W) System Handler Control and State Register */0 N; W8 K& J) n c$ j
- __IO uint32_t CFSR; /*!< Offset: 0x028 (R/W) Configurable Fault Status Register */. }2 u$ T/ p" o" c2 \3 Z. _" f5 }
- __IO uint32_t HFSR; /*!< Offset: 0x02C (R/W) HardFault Status Register */. I3 u" `9 J! C4 H3 D
- __IO uint32_t DFSR; /*!< Offset: 0x030 (R/W) Debug Fault Status Register */
" e3 {# i2 a& H. h/ {+ T+ s - __IO uint32_t MMFAR; /*!< Offset: 0x034 (R/W) MemManage Fault Address Register *// W3 z. a4 P! [' f, W
- __IO uint32_t BFAR; /*!< Offset: 0x038 (R/W) BusFault Address Register */! Z# [- r, ]/ v1 k: g4 q g
- __IO uint32_t AFSR; /*!< Offset: 0x03C (R/W) Auxiliary Fault Status Register */8 j* l/ x! b! D- ~$ Z
- __I uint32_t PFR[2]; /*!< Offset: 0x040 (R/ ) Processor Feature Register */0 ]% D1 w0 V" r/ }
- __I uint32_t DFR; /*!< Offset: 0x048 (R/ ) Debug Feature Register */9 d3 K q) p. ?3 \6 A2 @" k _
- __I uint32_t ADR; /*!< Offset: 0x04C (R/ ) Auxiliary Feature Register */) h* \- @# L: [9 d/ G4 F3 I
- __I uint32_t MMFR[4]; /*!< Offset: 0x050 (R/ ) Memory Model Feature Register */
! K; z! E3 J" R( @/ L - __I uint32_t ISAR[5]; /*!< Offset: 0x060 (R/ ) Instruction Set Attributes Register */! h4 P8 h2 f( b2 s8 }2 A( ?
- uint32_t RESERVED0[5];
m, e1 B0 x3 X" A5 `2 t6 V/ O - __IO uint32_t CPACR; /*!< Offset: 0x088 (R/W) Coprocessor Access Control Register */6 z' ]$ m" Q4 K# u6 M
- } SCB_Type;
复制代码
& N- }! M$ M* gNVIC和SCB都位于系统控制空间SCS内,SCS的地址从0xe000e000开始,scb和NVIC的地址也在core_cm4.h中有定义
, S) J4 D0 m* `2 |! v. @: |% s3 [6 t
- /* Memory mapping of Cortex-M4 Hardware */
/ s$ Y Q- k b5 p: M6 m" v - #define SCS_BASE (0xE000E000UL) /*!< System Control Space Base Address */
5 ]( V, l' o* n. n a9 Y: T - #define NVIC_BASE (SCS_BASE + 0x0100UL) /*!< NVIC Base Address */
# v1 [- J* S6 F! Q. R. j5 `& F: A - #define SCB_BASE (SCS_BASE + 0x0D00UL) /*!< System Control Block Base Address */
( M8 Q+ J5 d5 c/ a$ P
# B, q, A9 q% g% |% z+ d* I0 H- 0 D/ x4 _+ L0 [$ Q1 J
- #define SCnSCB ((SCnSCB_Type*)SCS_BASE) /*!< System control Register not in SCB */
; l: a8 v) i |* e9 w3 h - #define SCB ((SCB_Type*)SCB_BASE) /*!< SCB configuration struct */+ ~# k; b! g8 D/ j( O% k
- #define NVIC ((NVIC_Type*)NVIC_BASE) /*!< NVIC configuration struct */) X# E, _8 q t
复制代码 % j- p- R D' B- ]% Q
03. 优先级分组% K" @6 V K; j7 _
当多个中断来临的时候处理器应该响应哪一个中断是由中断的优先级决定的,高优先级的中断(优先级编号小)肯定是首先得到响应,而且高优先级的中断可以抢占低优先级的中断,这个就是中断嵌套。Cortex-M处理器的有些中断是具有固定的优先级的,比如复位、NMI、HardFault,这些中断的优先级都是负数,优先级也是最高的。6 y- W: ?6 M8 ]# B1 M2 ^) M8 M
8 v9 X4 I( r1 R6 F6 u/ e* ]
Cortex-M处理器有是三个固定优先级和256个可编程的优先级,最多有128个抢占等级,但是实际的优先级数量是有芯片厂商来决定的。但是绝大多数的芯片都会精简设计的,导致实际上支持的优先级会更少,如8级、16级、32级等等。比如stm32就只有16级优先级。在设计芯片的时候会裁掉表达优先级的几个低端有效位,以减少优先级数,所以不管用多少位来表示优先级,都是MSB对齐的。下图都是用三位来表示优先级。
- n5 n, E& _! Q7 F& ?& i2 s# q( d! w+ f6 `2 Y$ A
9 q% z. a( H' [
/ R5 y$ z$ {: N5 U0 d优先级配置寄存器是位宽的,为什么只有128个抢占等级?8位不应该是256个抢占等级吗?为了是抢占机能变得更可控,cortex-M处理器还把256个优先级按位分为高低两段:抢占优先级(分组优先级)和亚优先级(子优先级),NVIC中有一个寄存器是“应用程序中断及复位控制寄存器(AIRCR)”,AIRCR寄存器里面有个位段名为“优先级组”。
: e5 ~+ m5 H/ Y" `+ _- ?) Q
) V) g& K a: G( [
8 R ~1 P7 ~2 P: s% \7 U/ D
: S! O9 p( q) }6 m
. i$ }: [2 \$ [. o+ s. H5 uSTM32使用了4位,因此最多有5组优先级分组设置,在msic.h中有定义:) G. R2 Z% d" U* s5 k1 l/ o
* B: `# F% x4 @7 R" s- {- /** @defgroup MISC_Preemption_Priority_Group $ E( q0 i7 L' F& v
- * @{
+ {& @+ y: M& j: R2 s6 I; s4 x - */8 q* l" C, A. ]7 i
- . u+ ^' ^3 M5 C5 z- T2 I
- #define NVIC_PriorityGroup_0 ((uint32_t)0x700) /*!< 0 bits for pre-emption priority
+ j0 Y3 c2 T& B" m" v$ i - 4 bits for subpriority */
, w2 m7 y; U$ b( m C - #define NVIC_PriorityGroup_1 ((uint32_t)0x600) /*!< 1 bits for pre-emption priority( k) S) d" v8 M j: }3 f% p
- 3 bits for subpriority */# ?- A& d+ r. P. G0 C0 X
- #define NVIC_PriorityGroup_2 ((uint32_t)0x500) /*!< 2 bits for pre-emption priority! E/ l3 ?4 u: g; F
- 2 bits for subpriority */4 h* V5 B; m& l0 E
- #define NVIC_PriorityGroup_3 ((uint32_t)0x400) /*!< 3 bits for pre-emption priority
* n. e9 P, V% \" p! `" ` - 1 bits for subpriority */
' z' L$ s4 `- ]3 `" o( Z ] - #define NVIC_PriorityGroup_4 ((uint32_t)0x300) /*!< 4 bits for pre-emption priority+ S' g4 X3 |1 D; ^7 G$ c
- 0 bits for subpriority */
, ]% `3 y, u3 B% n - 9 K. F2 m5 o: f. V0 m
- #define IS_NVIC_PRIORITY_GROUP(GROUP) (((GROUP) == NVIC_PriorityGroup_0) || \* r5 h0 `! l2 R4 e% \/ j
- ((GROUP) == NVIC_PriorityGroup_1) || \
; v% h1 A' @3 q$ m9 x! ~* K - ((GROUP) == NVIC_PriorityGroup_2) || \" @0 `" a1 X9 s5 U4 @9 ~
- ((GROUP) == NVIC_PriorityGroup_3) || \# [4 x/ } n. n, d. k$ {
- ((GROUP) == NVIC_PriorityGroup_4))
复制代码 , V7 D, z% Q% z8 B
04. 优先级设置
! C; N/ J4 i e* `6 m" D每个外部中断都有一个对应的优先级寄存器,每个寄存器占8位,因此最大宽度是8位,但是最小为3位。4个相邻的优先级寄存器拼成1个32位寄存器。如前所述,根据优先级组的设置,优先级又可以分为高低两个位段,分别是抢占优先级和亚优先级。STM32我们已经设置位组4,所以就只有抢占优先级了。优先级就餐器都可以按字节访问,当然也可以按半字、字来访问,有意义的优先级寄存器数目由芯片厂商来实现。. F7 O$ i2 u8 o0 j
3 J$ f7 T2 g4 \. E
* a, s7 Z& G5 @0 [8 g% J" a) j! f5 T. R0 F! [8 x' M
05. 特殊寄存器. i" v, P, {+ p c3 Y, L9 ^
5.1 PRIMASK和FAULTMASK寄存器4 F% ^$ G7 D8 @# T9 Y/ V
# w1 x6 Q. @1 }! f! ~7 ?) @
& Q7 r! U3 T, g2 {3 o, l1 \; K* _' l3 X) _
5.2 BASEPRI寄存器
" O+ x* o+ l2 `" V; z+ j" ]
2 z6 O' y5 E3 N- b. ?- i( ?( w
$ J' i4 K( z) S' B: y" s: \5 Z/ P
* h4 p f! r5 V* U* P4 o. ~06. FreeRTOS中断配置宏
* `( r" w8 W$ f) s6.1 configPRIO_BITS: W+ B9 j7 C' t- O2 d: Z' ]
4 ]; j1 Z/ m, f5 I0 A K9 b" p( t9 m" B
此宏用来设置MCU使用几位优先级,STM32使用的是4位,因此该宏为4。
y! c' U1 N$ f- #define configPRIO_BITS 4 /* 15 priority levels */
复制代码 - X9 O- i1 ?$ j" J- K
6.2 configLIBRARY_LOWEST_INTERRUPT_PRIORITY- /* The lowest interrupt priority that can be used in a call to a "set priority"
7 p! K" W6 Z1 F, f0 u - function. */0 u' o& t7 e, e) w! }' w
- #define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0xf
复制代码
5 v, M! ^6 d% q9 }9 n. `该宏用来设置最低优先级,STM32优先级使用了4位,而且STM32配置的使用组4,也就是4位都是抢占优先级。因此优先级数数就是16个,最低优先级就是15。所以该值为15。不同的MCU,此值不同,具体是多少要看所使用的MCU的架构。
! `# X2 J& q; j J: [' s+ E5 W2 n# r. \9 k" L
6.3 configKERNEL_INTERRUPT_PRIORITY
/ S9 S. |( Y a' h y; n- [3 ^# H# E4 S$ \
- /* Interrupt priorities used by the kernel port layer itself. These are generic
6 a4 G6 j7 e1 o( y0 w n! n - to all Cortex-M ports, and do not rely on any particular library functions. */
- S! Y6 l$ x9 a: O - #define configKERNEL_INTERRUPT_PRIORITY ( configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
复制代码
" F# e5 m3 \! m# `该宏用来设置内核中断优先级。
9 _* c0 z3 |# c z9 |! q" ]" o9 h$ a9 m1 F0 `) M1 {+ ~* W2 }
6.4 configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY
( s# q L. w: ^- /* The highest interrupt priority that can be used by any interrupt service
# g& o) J- E3 W6 x+ G+ a% a - routine that makes calls to interrupt safe FreeRTOS API functions. DO NOT CALL! g4 y+ g |& p! A9 `; i
- INTERRUPT SAFE FREERTOS API FUNCTIONS FROM ANY INTERRUPT THAT HAS A HIGHER* {) I5 a% ^' q8 w
- PRIORITY THAN THIS! (higher priorities are lower numeric values. */: q# ^/ H' s) z( K" ?0 \; R
- #define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
复制代码
4 H) G% }! [2 R此宏用来设置FreeRTOS系统可管理的最大优先级,就是我们之前讲解的BASEPRI寄存器说的那个阈值优先级,这个可以自由设置,我们这里设置为5.也就是高于5的优先级(优先级小于5)不归FreeRTOS管理。5 H. Q4 E' D( I3 n/ O( D! C% Y: K
I; \7 g* t' Q7 c) n
6.5 configMAX_SYSCALL_INTERRUPT_PRIORITY! x) P& W. M, v( a% S
- #define configMAX_SYSCALL_INTERRUPT_PRIORITY ( configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS) )
复制代码 ! }" q) Y4 a% G3 t. `8 f: p: r
此宏设置好以后,低于此优先级的中断可以安全的调用FreeRTOS的API函数,高于此优先级的中断FreeRTOS是不能禁止的。中断服务函数也不能调用FreeRTOS的API函数。0 |/ b. Q, \, B$ G' ~( I5 S- j
% D/ \4 B5 n/ G1 J2 O! Z, F% {
0 l* t% c1 B; u0 v" o- N2 N2 x
4 f4 L! N; r. L& q. N3 U07. FreeRTOS开关中断
8 ~ @8 A3 K% K) v. X: IFreeRTOS开关中断函数为portDISABLE_INTERRUPTS()和portENABLE_INTERRUPTS(),这两个函数在portmacro.h文件中有定义。
1 n/ H: ]& f8 Y: N. }; v+ p+ D* ~* |: T; o z
- #define portDISABLE_INTERRUPTS() vPortRaiseBASEPRI()" `5 I4 j O. W& c' }4 h/ l# `
- #define portENABLE_INTERRUPTS() vPortSetBASEPRI( 0 )
复制代码 . x6 q, y9 [9 w0 J! @- [, ^: E7 D
可以看出开关中断实际上是通过函数vPortSetBASEPRI( 0 )和vPortRaiseBASEPRI()来实现的,这两个函数如下:
- h6 A7 y9 @$ a- |5 y5 q3 e' r2 U7 I; n- s
- static portFORCE_INLINE void vPortRaiseBASEPRI( void )
- l! y3 |4 N: R1 V; m7 E - {4 H1 G8 V `1 G8 ^1 ^
- uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;
( |$ C! j/ R6 ^ - * ~6 D" G1 L* b! T y3 E
- __asm+ z# ]! S5 w3 s4 C- k
- {- J) Y6 S' t l; ]( w
- /* Set BASEPRI to the max syscall priority to effect a critical
" U0 M9 k6 a6 p E8 f- X - * section. */5 T; q9 q' h2 ^- k/ P: Z
- /* *INDENT-OFF* *// S' a7 H9 R7 p
- msr basepri, ulNewBASEPRI; |) Z2 C, Z1 o! L4 \2 {: x) V
- dsb
9 f3 R: {) P5 u7 ]7 l2 G6 N - isb
9 P$ ?7 {4 M: A! \ - /* *INDENT-ON* */8 _4 H+ J( z9 u5 O
- }
3 S" R. c' k7 M7 j# V - }7 |1 s" C1 C4 K6 F% [1 ]$ p
. d" m) N0 A- }, Q& j, _3 t6 \
/ n: I! q3 T' J6 c- static portFORCE_INLINE void vPortSetBASEPRI( uint32_t ulBASEPRI )
5 g( s8 D$ R- `6 x0 B n, R. [" b4 e - {
1 D" K8 v0 Q* {8 Q! K' i - __asm& ?; M5 S+ F& p/ E
- {
" `- a: A$ |: i6 O - /* Barrier instructions are not used as this function is only used to
9 r( q& N! I6 _+ e+ A# n4 H - * lower the BASEPRI value. */
0 [2 o! h# M. e7 a, ~1 O - /* *INDENT-OFF* */
7 e2 E0 K: P0 ?* U. n - msr basepri, ulBASEPRI
8 `* O8 W; _1 R2 X, G - /* *INDENT-ON* */
! V7 {; c- p" `2 u" \ - }3 _0 w; A- c8 `: p& S
- }
复制代码 , m2 a' G# T$ W: M* m0 Z
* w r5 T* W% j4 x
# d" _$ a( }; u& S! ?7 S
+ ~5 H: G5 `/ M' `0 p
|