前言1 `' T3 g) T3 H( r* x n
STM32单片机的一个强大之处在于用于庞大的中断体系,掌握STM32对中断优先级的定义是学好STM32中断的前提。本次我们讲解STM32关于中断优先级NVIC的定义。我们以基本原理、寄存器介绍和库函数配置这一 顺序来讲解。
8 w1 `6 g4 ?4 n+ |( M0 J% Q" V
" x6 q5 A( m. |5 y1 `一、基础知识
, S( I" S. M& y! p1.NVIC基础知识
( y A$ ^9 W5 T; bCM3内核支持256个中断,其中包含了16个内核中断和240个外部中断,并且具有256级的可编程中断设置。/ K; K' z( b, f) F! L3 [
STM32并没有使用CM3内核的全部东西,而是只用了它的一部分。有84个中断,包括16个内核中断和68个可屏蔽中断,具有16级可编程的中断优先级。
* ~' e. h* C' V) V* J% |# t在STM32F103系列上面又只有60个可屏蔽中断(在107系列才有68个)- m0 B; o$ X- K6 }
2.抢占优先级&响应优先级( T* ?# r1 `1 w' a* i( X
抢占优先级也就是先占优先级,概念等同于51单片机中的中断。假设有两中断先后触发,已经在执行的中断抢占优先级如果没有后触发的中断抢占优先级更高,就会先处理抢占优先级高的中断。也就是说又有较高的抢占优先级的中断可以打断抢占优先级较低的中断。这是实现中断嵌套的基础。. g2 B- Q# t& i: H
响应优先级,也就是次占优先级,只在同一抢占优先级的中断同时触发时起作用,抢占优先级相同,则优先执行响应优先级较高的中断。响应优先级不会造成中断嵌套。 如果中断的两个优先级都一致,则优先执行位于中断向量表中位置较高的中断。
# r; D: l/ X _" W0 A" N$ n+ H) z* Z% x) y7 m
3.中断向量表& Y/ U5 g; x ^0 f4 F% z, ?$ l
2 \ b, I5 l) V) o1 X5 u* E+ b( Z) H
f0 N4 y ?3 K' B5 b7 \
O, _5 D% |& J6 G/ w' F
# j, i9 m3 p6 l2 R
# T8 c) o+ J% W5 u# c u7 X- t' ~
/ Z4 Q* X9 F" A/ C0 F" n! i8 I
(图片来自STM32中文参考手册)
) K [+ w# [4 g+ g9 Z9 z0 n
& U! j+ ~5 B7 V3 J. j- \4.中断优先级分组
7 m4 S1 z [8 {, |. _3 g看到中断向量表后,想必大家都会心生疑问:STM32有几十个中断,怎么管理呢?
4 S+ c+ z! B1 ^$ r) }0 g
: x$ N% X! a d5 a这就要讲解中断管理方法了,首先对STM32的中断进行分组,组0~4。同时,对每个中断设置一个抢占优先级和一个响应优先级值。
y& I% O/ G Y t: i) S# X8 {7 P. X/ g" B. f
分组配置是在寄存器SCB->AIRCR中配置:/ z& q: j6 p) E$ B+ j, W6 D
& _; K; C" ~# T [7 g/ g# [. q6 H
3 \' N# W2 Q1 v l6 n0 a( E
6 R3 { V8 g. X: t% J6 p
二、配置中断相关寄存器
, K9 C7 C; \2 K2 |) O以下是位于CM3内核中的与NVIC分组相关的寄存器:3 O7 Z( d% ^- R* q# o
& h8 N5 w" h& y8 ^8 y% G; x- typedef struct' L7 p, [2 _2 J d& Z
- {* T- v+ S8 A/ S) y ]' k8 w. w
- __IO uint32_t ISER[8]; /*!< Offset: 0x000 Interrupt Set Enable Register */
1 C0 @$ Z# k) o5 f - uint32_t RESERVED0[24];
8 i* B' M, G! H6 f - __IO uint32_t ICER[8]; /*!< Offset: 0x080 Interrupt Clear Enable Register */
( b6 T' O O9 V, s: U- W - uint32_t RSERVED1[24]; 1 k1 [0 p/ g' l8 y* l2 c- Y
- __IO uint32_t ISPR[8]; /*!< Offset: 0x100 Interrupt Set Pending Register */& z, y% O0 Z: c. B8 C
- uint32_t RESERVED2[24]; 8 O _1 o+ k- [) f. |
- __IO uint32_t ICPR[8]; /*!< Offset: 0x180 Interrupt Clear Pending Register */8 F9 |" i9 }% M& d) h I
- uint32_t RESERVED3[24];
+ b0 M2 q! I, s - __IO uint32_t IABR[8]; /*!< Offset: 0x200 Interrupt Active bit Register */
' a5 g7 R0 s9 D+ E: i3 @ - uint32_t RESERVED4[56]; 9 b' ]: c( j1 w; a* D
- __IO uint8_t IP[240]; /*!< Offset: 0x300 Interrupt Priority Register (8Bit wide) */
, D# B# G/ Y8 J8 a0 X$ B - uint32_t RESERVED5[644];
1 C5 O6 E" n, _ - __O uint32_t STIR; /*!< Offset: 0xE00 Software Trigger Interrupt Register */: Y, D, R: S8 h1 @+ R8 b
- } NVIC_Type;
复制代码 # _# ]1 u( H4 Y# g% t) L/ J5 e; z
中断优先级控制的寄存器组:IP[240]% I0 Z- A. ?' ]0 v- { {7 j7 T% _1 e% M Q
+ e. r$ P+ w( f% O% [全称是:Interrupt Priority Registers。240个8位寄存器,每个中断使用一个寄存器来确定优先级。STM32F10x系列一共60个可屏蔽中断,使用IP[59]~IP[0]。每个IP寄存器的高4位用来设置抢占和响应优先级(根据分组),低4位没有用到。
/ q) e& z$ l" y7 x* m6 }$ ]2 f$ U0 k$ v( w& D
中断失能寄存器组:ICER[8]
8 ~% f' A! y5 ?4 V3 m6 z; C3 z1 Z7 ~
作用:用来失能中断。32位寄存器,每个位控制一个中断的失能。STM32F10x只有60个可屏蔽中断,所以只使用了其中的ICER[0]和ICER[1]。ICER[0]的bit0~bit31分别对应中断0~31。ICER[1]的bit0~27对应中断32~59;配置方法跟ISER一样。
3 t. @" _# r9 q3 K/ |! e! _5 J% S/ v8 ~, R6 ?, n" y; I
中断挂起控制寄存器组:ISPR[8]& e5 k4 [( d% W( ~3 b; p J
/ C: C, L7 y3 [7 Y
作用:用来挂起中断$ ]' n. W) [$ [* I! G# @+ Y) _0 ?
, K. I" o3 E( {) o- \
中断解挂控制寄存器组:ICPR[8]
5 T8 [' J5 W v$ F! P
+ a. g0 ~8 F, E- Y& z4 b' B! s: {& z作用:用来解挂中断
( o, A! `/ R3 x9 G6 a) a; Q. v. g$ G5 \7 k& O6 n
中断激活标志位寄存器组:IABR[8]
4 p' V5 n# Z4 _8 ~/ Q& r: t: s/ h/ z! c+ l! t6 a7 l/ D* `
作用:只读,通过它可以知道当前在执行的中断是哪一个。如果对应位为1,说明该中断正在执行: o! S' G0 ~) D/ S/ J$ R
9 u' Z+ y K' |三、库函数配置
3 d1 z1 q; X. k6 F1.中断参数初始化函数
( w+ C/ c( \6 \; d5 T- void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
- j. P+ F3 k [0 m2 B( D, x. x
7 }! h0 i# N# `3 c. A3 Z" e- typedef struct
+ o8 v6 Q4 c/ h ` - {8 _, ?- u. f% M8 t8 a" z8 x
- uint8_t NVIC_IRQChannel; //设置中断通道
6 b; i* l. U1 o0 p0 L. }- o - uint8_t NVIC_IRQChannelPreemptionPriority;//设置响应优先级3 Y; _! K2 g7 o. H
- uint8_t NVIC_IRQChannelSubPriority; //设置抢占优先级( L R8 W9 O; C' b2 N s: L4 X! j# P
- FunctionalState NVIC_IRQChannelCmd; //使能/使能
; f, o% s1 _! m' u8 A - } NVIC_InitTypeDef;
4 r! p7 B5 F! a+ B4 o9 h - NVIC_InitTypeDef NVIC_InitStructure;
2 f `/ D, k. [1 W9 m2 T - NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中断
7 h$ T, I$ Z* `+ Z4 y6 J h - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1 ;// 抢占优先级为1
1 \1 _# U" q8 ^5 ?! J; G( i2 D - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;// 子优先级位2
. o$ \" V7 W0 |+ p# i d - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//IRQ通道使能/ C; ]* M) A& [
- NVIC_Init(&NVIC_InitStructure); //根据上面指定的参数初始化NVIC寄存器
复制代码
' e P1 v* _7 `2 r2.中断优先级设置步骤& b& h" W/ a$ Q8 F3 s$ H
①系统运行后先设置中断优先级分组,调用函数:9 g: y5 D8 y) @: |7 X" N
, r2 p- N% p% a9 k9 {void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
4 ] ?9 [ [& y: S% T$ V3 x" e4 Z& l4 d6 `
整个系统执行过程中,只设置一次中断分组。
, L8 ^1 a8 {* u2 I' f4 ], d7 M" m# R- ^$ m
②针对每个中断,设置对应的抢占优先级和响应优先级:
; g9 \/ o& K/ N6 J0 V' E9 {+ f5 R9 m, \* X9 h9 K: ?1 d# b
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
) m2 z" ]$ h& k% N) Z% @# I# O
& T( R6 t, { Q* C0 ?& t3 Z③ 如果需要挂起/解挂,查看中断当前激活状态,分别调用相关函数即可。
+ N+ _: z6 Z# L$ m$ H7 E8 E
6 _. f/ I0 S8 {) T* M7 q7 H' H% M
' v& C7 R4 y s; }. Y! s总结
. T& D8 L, Z" X+ R要想学习STM32中断,要先掌握STM32对优先级的分组。本讲以原理、相关寄存器介绍和库函数配置的顺序对STM32的NVIC中断优先级进行讲解,希望读者反复研究,为以后的学习打好基础。
; L7 M. s( d/ b4 V1 v. a1 M7 ~2 F' ^/ z r. i3 h
# E: M5 l$ z% i: p9 N% }; ^2 b4 K9 ~1 ^6 ]7 G
|