如何配置GPIO的外部中断
# D1 `4 o" r( [; ?1 h以STM32F103为例,记录配置GPIO外部中断的一般方法与流程。4 ^2 y8 W' n3 R$ j3 t& H
2 r, Y: Y/ M# ?# l. f9 g( S配置RCC时钟
5 r' K8 U, R7 [" X2 J7 i- YRCC时钟配置是STM32MCU顺利运行的必备步骤,笔者使用的参考代码如下:- E3 s% J! G' i, x/ ]$ D: y
7 y3 ^# Y1 C7 R( Z- void RCC_Config()
, M1 E% z' q$ N4 y - {
% I- W+ n, }. n/ J' n - RCC_DeInit();- G. a1 V) C' v3 ~- E
- //使能HSE,并等待 HSE 稳定
+ P- o9 p9 ` k0 a% | - RCC_HSEConfig(RCC_HSE_ON);! W: ~5 m% L; ?, C
- ErrorStatus HSEStartUpStatus;1 D3 `5 T' X8 k; J( M* `/ l
- HSEStartUpStatus = RCC_WaitForHSEStartUp();
, [; j! U. q! ^ - //HSE启动成功* r$ _/ B/ Y4 z- S H" T
- if (HSEStartUpStatus == SUCCESS)" K, Z \* g: ?9 _0 g
- {
9 P. Z% G( V% @7 B. ?/ ~ - //使能FLASH预存取缓冲区# Z# B7 u# Q# A+ d1 Y9 L* I. `
- FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
; g" |" V( f3 b# T! W - //SYSCLK周期与闪存访问时间的比例设置
# s- @! |/ l/ E) y+ Y - FLASH_SetLatency(FLASH_Latency_2);
# g: M% J) E- s9 l/ @: [ - //Configures the AHB clock (HCLK): AHB clock = SYSCLK
* E |9 A9 w4 o) b6 }4 v) z/ `9 ^ - RCC_HCLKConfig(RCC_SYSCLK_Div1);: W, P5 O9 c) M a8 d
- //Configures the High Speed APB clock (PCLK2): APB2 clock = HCLK2 c1 H* q' p6 O7 r1 n
- RCC_PCLK2Config(RCC_HCLK_Div1);
8 n0 x: l" V4 j+ G9 @ - //Configures the High Speed APB clock (PCLK2): APB1 clock = HCLK/2# n' T) t* L) e! L. ~+ g6 |
- RCC_PCLK1Config(RCC_HCLK_Div2);" E* Y+ V m, `, C' J. N" Q
- //Configures the PLL clock source and multiplication factor:
{4 s* W* \& b - //HSE oscillator clock selected as PLL clock entry
" {, V1 P9 I+ t" U2 N( J - //The PLL multiplication factor is 9* q4 a: G. t2 B- e$ s; A
- RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
. l' ^4 U# T# `, v& h ^ - //Enables the PLL.5 I# x/ p" ?5 R' E
- RCC_PLLCmd(ENABLE);& O9 ^+ O2 q& |4 [! g* B3 P& F! N
- //等待PLL时钟稳定
; e% P8 {7 C+ v6 p& L. j - while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)+ ?- X4 `( R6 _/ k
- ;
% J* p8 _: X9 O' h$ k* [ - //Configures the system clock (SYSCLK): PLL selected as system clock
M! _7 W: @1 `) W - RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
8 t3 q" E6 J1 M$ N; L! i( ]& @ - //读取时钟切换状态位,确保PLLCLK被选为系统时钟
0 E5 ~% p& z2 @6 T - while (RCC_GetSYSCLKSource() != 0x08). O2 I; G6 L; S' i6 c
- ;' `/ z7 ~' ]4 c0 P
- }3 P# y8 q5 K, F7 {0 F; V" ^, {
- //HSE启动失败
3 Z. B. u0 k) \ - else: X7 n% E4 M% x
- {& `9 R% R) a0 r1 ~8 G1 V! W2 c1 I3 s
- while (1)
$ g( ~: w; K" V* R7 S A - ;7 M v" V3 x, B( D7 t. k/ p4 X
- } g, T; X# k6 E& @8 m( `
- }
复制代码 ) x3 ~; Z: C1 d' O5 x7 K
GPIO配置
" O, z* q3 q; R2 R/ `7 [以PB10, PB11, PB12, PB13作为外部中断输入口为例:* B: {) V+ a8 i8 [
5 r4 F9 y5 ]5 @+ u# ?/ x, Y
- void GPIO_Config(): V% N( I/ |3 Q# Y; [
- {7 l8 g$ a& ?. S! L6 R( V1 s
- GPIO_InitTypeDef GPIO_InitStructure;
2 Y! x$ g! T5 f$ a1 ?* E- `7 t - ! t5 t. _. l) }' c4 a% U
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);5 B# k5 c" c2 C' s* C) Y
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
% T5 ?; c' k9 E0 z4 |; `1 K3 k4 v - " y5 z9 k0 k/ q7 }
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13;
9 H, P" m! r9 d - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;* g! B% D7 w; N8 {. [, F
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
2 ]7 S5 D+ @; v8 x+ @* Y -
) [0 ^8 w5 L1 M( `( S; H' w! i - GPIO_Init(GPIOB, &GPIO_InitStructure);2 P4 N1 s3 F2 _
- //选择EXTI的信号源2 d) c! I; M& y+ f/ @/ T# }, O' e
- GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource10);
# V. F; [# E! G- ~. a- O - GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource11);
: i6 |. p+ @, i2 }- U - GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource12);
, G/ g% ]% O* \ - GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource13);
& Y# u" { s% s4 S+ I Y0 L7 F - 7 u& { b4 o1 Z; V6 @: q; a
- }
复制代码 . e, L& j' |8 R" Y) t. U9 S: L
EXTI配置2 k/ i2 ~3 u w4 h2 b* X& m
EXTI (External interrupt/event controller): 外部中断/事件控制器,管理了控制器的 20个中断/事件线。每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。EXTI 可以实现对每个中断/事件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性。; ]( ?3 t" s& g$ O
5 H$ I$ _: f6 E' |) }
- void EXTI_Config()( `! o0 f7 p% t5 E6 ^& B4 y) c' o
- {
/ v! q- T5 N$ r. k. s$ c% p - EXTI_InitTypeDef EXTI_InitStructure;. q; v: b1 f6 i! s) N8 h
8 P) R2 Q" t$ z! t- EXTI_InitStructure.EXTI_Line=EXTI_Line10 | EXTI_Line11 | EXTI_Line12 | EXTI_Line13;- a% Y3 ~6 G! i# V$ Y/ G1 f
- EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
& G! }9 X: U/ M1 o* V8 O - EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;" g' U k7 X, P/ g2 \9 g
- EXTI_InitStructure.EXTI_LineCmd=ENABLE;
0 l' ^! y1 e( X# q -
; c' I, ]1 x+ s - EXTI_Init(&EXTI_InitStructure);
4 h. U0 X$ Y/ [: s7 } - }
复制代码 # q: h1 W, c0 s
NVIC配置% A0 T/ l* i# t; E
NVIC 是嵌套向量中断控制器,控制着整个芯片中断相关的功能,它跟内核紧密耦合,是内核里面的一个外设。但是各个芯片厂商在设计芯片的时候会对 Cortex-M3内核里面的 NVIC进行裁剪,把不需要的部分去掉,所以说 STM32 的 NVIC 是 Cortex-M3 的 NVIC 的一个子集。
: L# T. `' l0 m. @$ k7 k
; c( y7 p' a& x* A# Q在 NVIC 有一个专门的寄存器:中断优先级寄存器 NVIC_IPRx,用来配置外部中断的优先级,IPR 宽度为 8bit,原则上每个外部中断可配置的优先级为 0~255,数值越小,优先级越高。! h% c7 g6 V! @7 ]8 g& O0 |8 z
% E4 L9 @2 E* v但是绝大多数CM3芯片都会精简设计,以致实际上支持的优先级数减少,在F103中,只使用了高 4bit用于表达优先级的这 4bit,又被分组成抢占优先级和子优先级。
; `/ m% ]6 T# E( B1 K9 U
+ x7 L$ X- ~! a' `, J4 u! D如果有多个中断同时响应,抢占优先级高的就会 抢占 抢占优先级低的优先得到执行,如果抢占优先级相同,就比较子优先级。如果抢占优先级和子优先级都相同的话,就比较他们的硬件中断编号,编号越小,优先级越高。 p) g! ^( \/ Y9 t4 K) c3 L
# f6 M4 P1 D K: u; q8 E; M. n- void NVIC_Config()# T; v0 x/ n5 ~0 A- s
- {& w C9 t, Y% \' @3 H
- NVIC_InitTypeDef NVIC_InitStructure;* [: e n/ N2 r2 L7 W/ L
- //NVIC_PriorityGroup_2: 2 bits for pre-emption priority & 2 bits for subpriority
$ h. w$ x8 Y S2 V - NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
+ r& y- t2 ]7 A9 g1 j - % E0 x2 f3 Y6 S
- NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;" a* P2 i3 z2 M% v
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00; % {9 A# \+ W' e7 e. G' |: `
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;
( _$ s, v& [1 Z2 G1 N4 x - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;; o9 ?6 H. C* \+ {# {
- / m+ D5 f# ]7 }' m
- NVIC_Init(&NVIC_InitStructure);
1 h: O% z, X ~* b - }
复制代码
: m. O/ S# p; [中断服务函数 IRQ_Handler
6 k* L& y. t. |在启动文件 startup_stm32f10x_xd.s 中,我们已经初始化了中断向量表。实际的中断服务函数都需要重新编写,为了方便管理,可以把中断服务函数统一写在 stm32f10x_it.c 这个源文件中。关于中断服务函数的函数名必须跟启动文件里面预先设置的一样,否则系统就在中断向量表中找不到中断服务函数的入口,实现不了中断。" e+ T! [. y* ^7 h
; @0 g4 w; S2 a0 U# F* @- void EXTI15_10_IRQHandler(void)
; o% E% e8 Y, x, h2 K- x& q# b0 z - {" K) P8 R" y/ i, ]& ~8 R- \
- if(EXTI_GetITStatus(EXTI_Line10)!=RESET)9 w: s* u# q' J. w
- {
0 H+ `# Q5 f d+ ` - DoSomething_Line10(); j* F" i& F$ @& S; q
- EXTI_ClearITPendingBit(EXTI_Line10);2 B& m- s4 p; ~" S
- }) a5 a F8 ]7 o4 e k4 a/ E
- else if(EXTI_GetITStatus(EXTI_Line11)!=RESET)
1 W, m9 F* c/ K) A7 ^$ A% G) X - {4 I- O: G- r4 @- ]4 R
- DoSomething_Line11(); i! B/ @6 k+ R" r% y
- EXTI_ClearITPendingBit(EXTI_Line11);
. z+ k5 N# s" Y5 }( ]! @+ t1 G - }
6 m! @5 l% e; f( L. l# z - else if(EXTI_GetITStatus(EXTI_Line12)!=RESET)
4 Y, [- l% j5 G - {
+ B6 X* P2 N6 f' u p# X0 o% n# P - DoSomething_Line12();4 k h0 v# U7 K. B# O
- EXTI_ClearITPendingBit(EXTI_Line12);& \5 g, W( \ ^' J1 m; Z7 h
- }; p; H; i& [) Y+ a
- else if(EXTI_GetITStatus(EXTI_Line13)!=RESET)" S: |6 ~' Y* p' A$ e
- {( N, y8 [. {9 {* `4 Q0 k
- DoSomething_Line13();6 S8 A: E, N6 b- d I# t
- EXTI_ClearITPendingBit(EXTI_Line13);
' ?+ b4 g- {. E6 D( X9 k; G/ A - }
& S1 l" F: @! u! O- @7 e* N1 r - else if(EXTI_GetITStatus(EXTI_Line14)!=RESET)$ U, x+ x* K P; q& H9 ^* Y2 H7 e
- {) |" n' M6 r- ?
- DoSomething_Line14();. c8 ` _ k; U- V2 k
- EXTI_ClearITPendingBit(EXTI_Line14);
" x( w- u& v3 Z( j1 p - }( T% J; V$ G2 H7 o: B
- else if(EXTI_GetITStatus(EXTI_Line15)!=RESET)9 P" n% |( a4 ]9 G; p
- {
5 ], _) M6 R( _ - DoSomething_Line15();
# T. H& L) R9 T# o+ G& A - EXTI_ClearITPendingBit(EXTI_Line15);, Z2 N; P# }( J. ~
- } ]2 q; w G( K7 C' y/ `1 [
- else
; h2 U. J3 N! L/ K - {
% o) x6 U y1 Q* B1 p7 r. D - . h2 r: X5 _4 {
- }" k# `' U% C" v6 X- G. v0 J
- }
复制代码 , J- h$ A$ b- N
' s* b3 e5 O' e0 c5 o) ?
; k$ E! U( C) x L
3 N5 q' L1 { b( i! O6 _; e |