STM32CubeMX教程-GPIO输入之外部中断
' p7 P% B8 Q. G2 n
/ g5 C) Q! g! g1 V' x6 ]3 h本篇主要讲述GPIO外部中断输入检测,实际物理输入方式是按键,但是实际上更适合外部设备信号的输入检测,物理按键输入因为有抖动,还需要硬件或者软件去抖才比较可靠。这个例子不考虑防抖的情况。
+ i6 t8 h0 A, s) G0 V软件版本:
) i& w$ h9 N2 c2 }STM32CubeMX V4.25.0 % y3 X" L! w0 t! G+ [
System Workbench V2.4
5 E! S& `9 B1 }' b$ y7 o- e硬件:OneNet 麒麟座V2.3/ b6 V. t) e1 W" k7 c7 i
在STM32CubeMX中新建项目,选择正确的MCU型号# j, m& E+ b. w1 C* s
设置RCC和SYS,然后根据板子实际情况设置时钟(麒麟座外部晶振是12M,STM32F103x的最高主频是72M)
根据板子的具体连接设置4个GPIO_OUTPUT (连接到LED)和4个GPIO_EXIT*(外部中断模式,连接到物理按键)。8 g4 h# ~7 b9 Y) r2 u, j3 |
5 V( X: Q6 f0 ?6 k" o: S9 F GPIO OUTPUT引脚设置
+ [8 c$ Y# u9 o
: y2 p" N9 N2 B 麒麟座按键有外部上拉,所以GPIO_EXIT*不需要配置内部上拉,检测下降沿(Falling Edge),GPIO_EXIT*设置如下% T# ] z7 V5 ]' S7 C" ~
2 W0 U$ F9 F2 _: Y# w5 }; B
LED名称标号我用了1/2/3/4,SW名称标号我用了2/3/4/5,实例中标号也未必相同,但是要做好一一对应关系。
) j/ r9 Q! V. G5 b 在NVIC(嵌套向量中断控制器)中,勾选EXIT Line2 interrupt 和 EXIT Line[15:12] interrupt 使能中断。右边两个选项设置抢占优先级和响应优先级。此处我们选择默认的,不修改。
8 {# L4 p- x3 }- [ P6 E: c) T. T
: k a, i! _2 a& E& H$ n) e3 ^+ i6 m 同样修改Project - setting ,ToolChain/IDE选择 SW4STM32
! ~* s, f; i( }/ s
& I+ z; U( G: R0 e 勾选这里。
9 O; g6 y, ~5 ~9 P- O- g; @4 O& \$ F
, z+ T# i$ A' C/ _; ^0 ~( n 生成代码后点击Open Project在Eclipse中打开项目,然后在 在stm32f7xx_it.c中断服务函数文件中,我们可以找到EXTI2 和EXTI15_10中断的服务函数。& H; W2 [; }' `6 M3 s6 ]0 c
7 h; [9 \% E6 E4 d
! c c/ B, d: T3 E# c4 @% n
- ; \/ [: z+ m% q: y' C$ b3 E% s
- /******************************************************************************/
& L: l( s' K( K; A - /* STM32F1xx Peripheral Interrupt Handlers */8 @$ V4 ?& \- q
- /* Add here the Interrupt Handlers for the used peripherals. */
: @5 u" Z$ G( C* I( r2 E2 w - /* For the available peripheral interrupt handler names, */3 [, k0 p4 ^9 A
- /* please refer to the startup file (startup_stm32f1xx.s). */+ o% I/ M$ {9 B5 q
- /******************************************************************************/
- I! Y& j6 W1 d- q. m7 z$ W3 | - /**
. J& ^& o8 J8 Z& T9 V3 Q. N - * @brief This function handles EXTI line2 interrupt.8 A. b2 D3 {( c/ x" `: C1 d# F
- */
5 q) g7 J8 q1 l; L+ r' Y. o% y - void EXTI2_IRQHandler(void)- V8 o% W4 c0 o6 C0 D
- {
( r2 A, i+ N0 z# w( k' g - /* USER CODE BEGIN EXTI2_IRQn 0 */1 }; ^. Y6 h1 g" N
- /* USER CODE END EXTI2_IRQn 0 *// I+ X; K$ _8 H' Z
- HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2);, {: y( l; P; E- w6 k; E
- /* USER CODE BEGIN EXTI2_IRQn 1 */
* A7 V! b3 e8 R - /* USER CODE END EXTI2_IRQn 1 */
5 J7 r/ {: h( K& o, _$ t* r+ h - }1 Z# Z) i7 o+ e4 N# S
- /**; t; X( c; }+ x0 Y9 i( q
- * @brief This function handles EXTI line[15:10] interrupts.
5 c$ ?$ {0 }. e+ D& W0 X$ K - */
7 ^: |( K5 \( y( o, ^8 E9 L - void EXTI15_10_IRQHandler(void)0 w1 L( s% \" \. E
- {/ A) {" A I4 e
- /* USER CODE BEGIN EXTI15_10_IRQn 0 */, @- n6 T) w( l5 H1 K8 \" R" A
- /* USER CODE END EXTI15_10_IRQn 0 */
% M/ w: M3 ]9 s! H - HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_11);0 k5 D4 F" O* X$ L0 z
- HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_12);$ _1 e3 q, W1 g
- HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13);
" {+ x, ^+ M7 t2 z5 O0 q/ U5 q - /* USER CODE BEGIN EXTI15_10_IRQn 1 */
! v$ i7 G/ _3 i6 j3 Q - /* USER CODE END EXTI15_10_IRQn 1 */
+ S0 `5 d" a5 Y _5 S7 u2 D7 L - }
复制代码
" d! z+ D; m) [% r
( a/ ^6 ?: B# x' n: h右键点击HAL_GPIO_EXTI_IRQHandler 选择"Open Declaration"会跳转到如下代码:1 d7 ?. _2 T. [. F9 C/ C
8 D2 w% u( F6 b8 I t- |: E# u; J& E) A) C2 l4 `. O, K: g+ c$ ]. V
. e m. k; d6 L: ^( B _- /**
- k" |6 n& f! ?& @+ h4 ? r - * @brief This function handles EXTI interrupt request.
$ W" Y' `3 F) J1 u - * @param GPIO_Pin: Specifies the pins connected EXTI line
/ I3 c0 j# {3 q- I0 Q, m6 C, Q' f0 N - * @retval None% y* H. s' O2 g& l1 [
- */) O6 g0 X8 h) {0 P; q
- void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
) \1 f( G8 S+ Q - { F! b* d8 Q7 r' a5 c
- /* EXTI line interrupt detected */4 `, ]0 h6 |- {
- if (__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET)
& @1 S0 N, c8 ~. r - {( x' j- x) A6 t2 r
- __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);. m6 Z3 Y# T! ]" i
- HAL_GPIO_EXTI_Callback(GPIO_Pin);
* s( C, r9 S+ I# C& t3 c - }
8 b7 j5 t2 m5 n; m; l. W - }+ \" O7 m2 o7 k7 f% P* i% C
- /**
I& G& K/ y( E B: j2 z4 ] E - * @brief EXTI line detection callbacks.& P6 u4 I7 p% w) h
- * @param GPIO_Pin: Specifies the pins connected EXTI line: R/ m7 e8 m3 E2 p
- * @retval None
1 D1 a& J. m9 }/ w - */' d8 `& a. ?7 V( k* F
- __weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
9 m& y( T9 `7 l; [# e - {- }, g0 {. ]% o5 Z: b% U
- /* Prevent unused argument(s) compilation warning */3 k, Z0 [: w, D
- UNUSED(GPIO_Pin);3 }( V" t. N# J, Y
- /* NOTE: This function Should not be modified, when the callback is needed,! r# a- l7 B: i
- the HAL_GPIO_EXTI_Callback could be implemented in the user file
# A" k, E% L( i1 n3 ^. q9 d - */
8 A+ D5 Q; E4 ~4 |- A C4 i - }
复制代码
$ g, v' F4 l: D5 S2 x, e: ^. m3 ]0 C+ R. J! G/ e
上述代码中可以看到GPIO外部中断处理函数首先清除中断标识位,然后调用中断回调函数HAL_GPIO_EXTI_Callback()。往下看这个回调函数定义的时候使用了__weak修饰符(关于__weak修饰符, 可以参看此博文),里面没有任何有作用的代码,我们需要重新定义这个函数。编辑gpio.c(或者main.c也可以),进行如下修改:
6 I1 y) s l# @7 A4 o7 w& A2 I5 [1 }
5 ], v3 V4 N& l1 z- I" Z: B
- 1 k' Q* H6 ?* l. E
- /* USER CODE BEGIN 2 */1 I: K5 i# t$ u) [7 g% _
- void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)7 k' l h; q4 L4 o8 x
- {
- C& V/ F1 A; N6 G# G+ ? - if(GPIO_Pin == SW2_Pin)
1 a: s- c7 X; U! o9 q - {
4 x! e1 ~" T: l) m+ M - HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin);
1 ]2 t* R+ a. P" W# c8 j - }
1 F/ ~7 n5 ~: N2 K- ? - if(GPIO_Pin == SW3_Pin)
' K" Z2 k8 [$ O) `5 @9 V) Q+ k - {
9 v0 z3 M: }1 [) }) ] p - HAL_GPIO_TogglePin(LED2_GPIO_Port,LED2_Pin);. N5 s1 X% M0 y! C9 k# d0 p$ }
- }
1 e" {5 N& _5 Q" `' b) p - if(GPIO_Pin == SW4_Pin)
. d7 a/ P, a% k: h0 D - {
' Q4 L% ~& k8 Y- \4 s - HAL_GPIO_TogglePin(LED3_GPIO_Port,LED3_Pin);+ }$ c! C* W1 p2 F- Y
- }# j4 u7 ]. N, g, s. m# ^
- if(GPIO_Pin == SW5_Pin): w# s: }/ l d7 m
- {- @$ t6 y+ R. T- s% C( ~8 `7 z
- HAL_GPIO_TogglePin(LED4_GPIO_Port,LED4_Pin);( H4 y& i0 m& h5 Q& ^% F* ~
- }
$ G" K1 F5 V1 M0 o0 ^+ |8 X- \) O - }# ?( `3 i* g% V- G9 @- M
- /* USER CODE END 2 */
复制代码
, ^8 O6 _, G" E2 Q0 t1 B& X
, b9 x# i' |4 q9 s \# q) t然后右键点击项目,选择Properties, Run-Debug Settings, 点击右侧的New,在弹出对话框中选择Ac6 STM32 Debugging。& _2 y1 C0 ?7 v( R8 h
" V/ C/ a- B% Z7 U& X. R8 V
然后任务栏上点击Run图,当然会报错的,原因请查看另一篇我的博客,所以需要右键点击 项目名Run.cfg ,给它改个名字,0 \4 `) M( i0 X; o d7 E! F
; G( i0 s0 o8 W, q7 c - @ P3 e! }9 I% K6 f6 r
然后右键点击项目树里面的项目名称,选择“Propeties”,然后在Run/Debug Settings-选择项目名-Edit-Main-C/C++Application那里点击“Search Project”,然后选择出现的默认的elf文件:; E4 B: g( u3 |6 W! G2 ?8 ?+ C
X# L. l3 ^( c$ Y1 l: \2 n 然后在Debugger-User Defined-Browse 那里选择你自己改名的配置文件:4 o. a9 H }4 y, m/ F
9 ^1 H: K9 b( @9 s 然后右键点击那个新的cfg文件,选择"Open With - Text Editor", 进行如下更改:
- x9 \5 d* }* q4 j! f
) D& `/ Z. i5 R, v- n- N. b- source [find interface/stlink.cfg] : U, y9 J) Y: R
- //更改为 source [find interface/stlink-v2.cfg]# k: b/ R4 g1 _
8 B8 y# y! |6 h, a, f: d0 p. K J/ c- reset_config srst_only srst_nogate connect_assert_srst
; m. L; ]* |- q3 V) S; z - //这一行改为 reset_config none
复制代码
2 K5 ^3 C. C {1 U& E3 ~$ K. c# h, j5 U9 u. @% \. }
Run一下,就可以了。然后再Run一下,就实现四个按键分别控制LED的开关切换了。但是这里没有防抖处理,你会发现有时候LED会闪一下,或者没反应,其实是很快地点亮然后熄灭一次或者若干次,只是由于速度太快电流很小导致你看不到。下一篇我们会通过使用SysTick中断来实现按键去抖扫描。在实际项目中,最好加上硬件防抖,因为抖动产生的反向电压可能会冲击GPIO导致损坏。
W9 b7 X, _4 E( w3 |7 N' g* L: Y* O: D6 ?; D
/ C. G0 b/ c8 T+ W! o+ g. R A: S8 _
* O- b" t. ^; h _$ n- a
: \; v3 q/ [/ J; a) X- @; o+ p) L# \( H. J ]
* X% a" Q% T6 H
, V# d: T& W9 `' f) Y$ @2 _! z2 h+ f/ s+ Q) Q0 u$ X. L
% E/ l, n' W `; i7 Z) \- I$ p# j- R4 e; s. M8 T# |
' w* e. Y0 ]+ ]4 b+ ?# o+ | |