中断,在单片机中占有非常重要的地位。代码默认地从上向下执行,遇到条件或者其他语句,会按照指定的地方跳转。而在单片机执行代码的过程中,难免会有一些突发的情况需要处理,这样就会打断当前的代码,待处理完突发情况之后,程序会回到被打断的地方继续执行。. Y3 s4 E' |! e2 h0 b
' B, N, _. c- _5 P' ^
1 EXTI控制器
4 O# Z& _5 S4 Y$ d9 b/ T( F# u% D- w6 b* N+ Y% a
U8 i" N2 [0 q1 O4 U- }7 \8 A: b
外部中断/事件控制器(EXTI)管理了控制器的 23 个中断/事件线。每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。EXTI 可以实现对每个中断/事件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性。# B& y9 U7 r4 g* q
# v* f% k2 r X$ l9 F
- I8 h7 A; F' W) u3 P' q
外部信号进入经过1的边沿检测电路,检测是否符合(有2和3的上升沿和下降沿选择寄存器决定),产生信号,然后和4软件中断事件寄存器或值,(在这里也就说可以写入软件中断事件寄存器模拟中断和事件),之后产生信号一分为二,看5中断屏蔽寄存器和7事件屏蔽寄存器,如果中断和事件都没有屏蔽,首先会产生事件,进入脉冲发生器。其次,会进入6挂起寄存器,然后进入NVIC。
, [% Q5 N6 n3 X6 T3 \注意: 1、上面说,我们可以使用寄存器4软件模拟中断事件寄存器模式符合条件的信号进入,为什么不能用6寄存器呢?因为 3 Z" a& _2 p2 O5 F( V6 S! F# E
3 ?! N5 f: A; m6 S- s F" z寄存器是可读可清除的寄存器,通过写1清除。写0无效。所以不能使用
3 x" t4 y) b, Y1 d6 Y$ Z. D0 v
2、关于挂起寄存器,挂起就是,证明有了中断,会在触发中断。但是不会硬件清除。 9 i+ {0 V5 m0 R# z" w
1 e+ K2 v# }0 W3 n: Y" J7 T6 s7 z% H0 ^& P! \4 S& P
只能软件清除,或者修改边沿极性的时候清除。如下 , f- a L+ y6 R' G$ a
& Y! s$ j% F, |1 J5 v: P; b先说EXTI吧, - r+ E; n' B! w8 k: `- e! J
EXTI 控制器的主要特性:
: }8 d9 A1 K8 \6 H0 f: X2 C每个中断/事件线上都具有独立的触发和屏蔽
8 p* z/ ~) X* g3 K1 A2 v$ g每个中断线都具有专用的状态位
% Q4 r5 {+ j7 H: k" h; j3 O支持多达23个软件事件/中断请求
4 [& _- X9 j& T) s检测脉冲宽度低于APB2 时钟宽度的外部信号 3 y7 y; O t6 x- F2 ]1 x
下图是ST207的框架图
* Q8 v5 S U+ ^8 N; e3 R
" n: x2 G6 T- { y5 p+ a ?下图为翻译版 # p4 l! m) }/ U+ `: E# g9 M8 l
从图中看出和外部中断有关的寄存器有:上升沿触发选择、下降沿触发选择、软件中断事件寄存器、中断屏蔽寄存器、挂起请求寄存器、事件屏蔽寄存器和NVIC中断控制寄存器等。此外就是对输入线的理解了。 " M l( @3 d" A" V% W7 Z7 P9 b
另外七根 EXTI 线连接方式如下
# \3 b3 s i; m) s# g( Q6 X6 L% F也就是说对于一个外部中断线可以和多个GPIO相连,当你要使用哪一个IO的时候只要对SYSCFG_EXTICR对应的位设置就好了,在中断屏蔽寄存器或事件屏蔽寄存器对应位可以设置使用哪一个中断线
# Y' g8 j0 I0 _; W
注:ST的使用SYSCFG_EXTICR来配置,GD的采用AFIO寄存器(在GPIO寄存器中) * K) ?& H- F M0 r% R- K
, I( M) S, g2 ^, bEXTI是外部中断吧,上面的主要是针对的这22条中断线的说明,我们还知道还是有很多中断的,比如定时器中断,串口中断等等,他们不属于这22条中断线。
- ^- J E* B7 o
我们可以在中断向量表中看到 : U5 @. s0 P' t0 A# j8 X1 g
其他的中断配置都在各个模块的寄存器中了
# Q1 ~5 Y/ U# `; E: J 2 NVIC控制器在上面的EXTI寄存器都设置好后就可以设置NVIC了,关于NVIC的芯片编程手册上描述较少,但是说了 # m! [2 |1 ^8 K# |/ D6 j
$ y, [2 \# L, V所以我们就参考一下M3手册吧 1 O4 f3 X6 v4 r ~
找到AIRCR寄存器,其中8到10位为优先级分组 ) L" L' D* g4 U, ^1 [
我们在代码中使用的库函数是 0 @. j, A$ M$ R+ U. q; u. p
- void NVIC_PRIGroup_Enable(uint32_t NVIC_PRIGroup)
/ [& w+ J) a) W8 I - {
' V! Y" O8 L/ v2 r' R: p - /*Set the priority grouping value */% Y3 c0 _8 p- q" }1 ]
- SCB->AIRCR =AIRCR_VECTKEY_MASK | NVIC_PRIGroup;
8 n5 ^* p- r* }2 F - }
复制代码 ! f L3 |0 Y! ^0 `
$ s9 ?' K" R$ G' W
其中我们查到 1、SCB->AIRCR在库函数的地址是0XE000ED0C,不懂的如何查询的,请自行百度 2、查到SCB的结构体定义
' b# G0 A U: }0 F7 D我们看到SCB是SystemControl Block的简写
$ k7 f6 o* q6 A$ _( G) |
下面我们说一下分组的取值 + J; L+ I5 Y* q7 `
! e' F5 _) G! i( U" M
在misc.c中有 9 @+ Y3 G6 C$ V' [8 i: w0 u3 S
- * ==========================================================================================================================1 N0 k V* `5 ^0 c" M, r
- * NVIC_PriorityGroup | NVIC_IRQChannelPreemptionPriority |NVIC_IRQChannelSubPriority | Description
+ r2 K. `2 i( @/ L* t3 C - * ========================================================================================================================== c1 l; k) \- d) s2 N3 Y( |. [
- * NVIC_PriorityGroup_0 | 0 | 0-15 | 0 bits for pre-emption priority$ h3 K& i( ?! H; A
- * | | | 4 bits for subpriority
. C* B: n$ X* J+ }: Q7 z5 _, n- v - * --------------------------------------------------------------------------------------------------------------------------
@2 \; S9 J( z* r6 v - * NVIC_PriorityGroup_1 | 0-1 | 0-7 | 1 bits for pre-emption priority
4 J" @ F. M: o- R; o+ y- M/ H - * | | | 3 bits for subpriority
8 z9 v. h( {# i, {" Y5 | - * -------------------------------------------------------------------------------------------------------------------------- 8 d/ S% v! {1 U. c' T
- * NVIC_PriorityGroup_2 | 0-3 | 0-3 | 2 bits for pre-emption priority" l+ X4 Q5 p5 T/ r) R% o! d# T
- * | | | 2 bits for subpriority
! Q# V. }* `% ?0 ?7 d - * -------------------------------------------------------------------------------------------------------------------------- 4 B: E7 {0 t0 e) Y
- * NVIC_PriorityGroup_3 | 0-7 | 0-1 | 3 bits for pre-emption priority
4 v! P2 U" l' N/ g: p6 u - * | | | 1 bits for subpriority
3 c5 O% f" l# I' r5 F% l: D - * --------------------------------------------------------------------------------------------------------------------------
' [, z2 Y8 c1 C( B - * NVIC_PriorityGroup_4 | 0-15 | 0 | 4 bits for pre-emption priority; e# D8 w$ U: Z
- * | | | 0 bits for subpriority 4 l' B2 \4 v4 ~% K6 {1 y* }+ Q
- * ==========================================================================================================================
复制代码 # N3 N$ E1 y- ]
9 @3 y; R/ K6 ~$ S8 ~2 N
抢占优先级& 响应优先级区别0 @: d- }- t7 p* u/ S
0 L7 E- x' ?5 y1 高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的。
: Y4 W9 _7 @% F. K( b; c5 E* X- G! A$ h$ w# b8 h
2 抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断。
) g1 m' T5 v4 z) Q# k1 D6 O8 }; C
, m2 ?% v% Z% N$ Y3 抢占优先级相同的中断,当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行。2 R1 E. t4 {3 h0 a9 n* g6 v
\" `2 [) a; `4 如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行。. w5 P- U5 _! t! R% y% d, E
+ b8 [9 G# h& ]) G+ w例子:
7 Q* D, ~6 L& E3 Q5 \( ~
假定设置中断优先级组为2,然后设置 # q7 ?) }8 K/ b. C
中断3(RTC中断)的抢占优先级为2,响应优先级为1。
# A# j5 a/ b! `1 P9 A/ @& {
中断6(外部中断0)的抢占优先级为3,响应优先级为0 - U5 I6 h% J! P* x% ?
中断7(外部中断1)的抢占优先级为2,响应优先级为0。 : n P1 {- T; C8 c0 _
那么这3个中断的优先级顺序为:中断7>中断3>中断6 1 L6 `: {! N8 N
表现在代码中 - - C$ \0 Z/ D, e" [5 K, Z: v
- NVIC_InitPara NVIC_InitStructure;
, z) r$ J$ I! g - NVIC_InitStructure.NVIC_IRQ = IRQn;
+ A- @( l+ ^, A6 b" S - NVIC_InitStructure.NVIC_IRQPreemptPriority =pri;
! [2 N, t- J9 F1 V - NVIC_InitStructure.NVIC_IRQSubPriority = pri1;( ?5 S4 `; T' ?' C! U" F
- NVIC_InitStructure.NVIC_IRQEnable = ENABLE;
6 C. y+ D" x, G - NVIC_Init(&NVIC_InitStructure);
复制代码
$ a9 \0 l( ~( g. T
& _2 L1 E' O( [5 \+ |% g分组0,那么pri的取值范围0~0,pri1的取值范围0~16
& ^; [+ `) r" ?' ^1 h 分组2,那么pri的取值范围0~4,pri1的取值范围0~4 分组4,那么pri的取值范围0~16,pri1的取值范围0~0 5 w5 ?8 J c# H u: C% \
下面我们讲解一下NVIC寄存器 ; ?& T6 O2 [ _: Q: [, U8 W: {
__IO uint8_t IP[240]; //中断优先级控制的寄存器组 __IO uint32_t ISER[8]; //中断使能寄存器组 __IO uint32_t ICER[8]; //中断失能寄存器组 __IO uint32_t ISPR[8]; //中断挂起寄存器组 __IO uint32_t ICPR[8]; //中断解挂寄存器组 __IO uint32_t IABR[8]; //中断激活标志位寄存器组 " ]* H# @: w6 N p6 h3 B
中断优先级控制的寄存器组:IP[240] 全称是:InterruptPriority Registers 240个8位寄存器,每个中断使用一个寄存器来确定优先级。 比如:STM32F10x系列一共60个可屏蔽中断,使用IP[59]~IP[0]。 每个IP寄存器的高4位用来设置抢占和响应优先级(根据分组),低4位没有用到。 ( Y5 |/ k1 ^& P
/ L8 c1 _' h+ |2 ^8 k. z) X1 c
6 j! p/ `$ o- u3 [- F
中断使能寄存器组:ISER[8] 作用:用来使能中断 32位寄存器,每个位控制一个中断的使能。STM32F10x只有60个可屏蔽中断,所以只使用了其中的ISER[0]和ISER[1]。 ISER[0]的bit0~bit31分别对应中断0~31。ISER[1]的bit0~27对应中断32~59; - void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
复制代码 4 V8 z1 b! w. f& |
4 l( S' H- e0 z" G. a: _
中断失能寄存器组:ICER[8] 作用:用来失能中断 32位寄存器,每个位控制一个中断的失能。STM32F10x只有60个可屏蔽中断,所以只使用了其中的ICER[0]和ICER[1]。 ICER[0]的bit0~bit31分别对应中断0~31。ICER[1]的bit0~27对应中断32~59; 配置方法跟ISER一样。 - void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
复制代码
/ K# x) E/ m, F中断挂起控制寄存器组:ISPR[8] 作用:用来挂起中断 中断解挂控制寄存器组:ICPR[8] 作用:用来解挂中断 ; F) {( T# a9 o; C
6 d3 l+ b' w5 |- static __INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn);/ |/ E6 O, g, k$ f0 g' d" s- b5 o
- static __INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn);
! Q. R& L( L0 e5 ~. Z) @ - static __INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn)
复制代码 ( R& F' S; f- N; X. [" B
中断激活标志位寄存器组:IABR[8] 作用:只读,通过它可以知道当前在执行的中断是哪一个 如果对应位为1,说明该中断正在执行。
0 U* y. m* Q- D1 c0 D& V- static __INLINE uint32_t NVIC_GetActive(IRQn_Type IRQn)
复制代码
, B8 a1 g* x5 L$ l
7 i r k6 B8 ~! a7 |8 s7 A! m; _. {
( G$ T5 Z( P3 F6 T: o0 Q
3 code
% s) O$ e% D$ W# Y+ o
一定要使能系统时钟 因为配置GPIO和中断线的映射关系需要SYSCFG - 6 H5 k3 R. W" f& t& t: K3 i! k
- static __INLINE uint32_t NVIC_GetActive(IRQn_Type IRQn)
复制代码
! V' b0 |, ]- y只要用到外部中断,就一定要打开SYSCFG时钟
) w/ L/ `9 x3 r+ }3 }
_$ F4 f1 c* G1 p4 H
1 x- S' |! ^; k( C+ q- ^, e6 J3 y7 j) ~( F3 S2 Q ?
) O) Y: D# x9 w% s' O- X
|