STM32CubeMX是官方推荐的配置软件,而HAL库也是取代之前的库函数的通用库,所以STM32CubeMX+HAL库是当前主流的工程创建方式,HAL库的可移植性也高于被取代的库函数。 之前在[程序员小哈]的公众号中写过两篇基于STM32CubeMX软件的文章,今天对其进行了转载。 今天我们利用STM32CubeMX实现按键(外部中断)翻转一个LED,涉及到的新知识点:外部中断。
2 G2 q4 i% {5 o+ A 软硬件环境- Keil 5.14
- STM32F103RET6核心板【202006每月活动】
- STM32CubeMX 5.3
- 按键(PA12)
- LED(PA11)
6 w' E0 S7 f( X 6 H! f' i. |& e, K: `' d
. W/ Y! T- L7 t$ S) r7 g( ?5 FSTM32CubeMX创建工程- 打开STM32CubeMX软件,从如下箭头所示两个位置可以新建工程。3 R& C6 g7 y i
9 r/ h: D# P, L3 k. g9 m
$ N U1 c! X% E2 Z+ t _
- 选择芯片STM32F103RET6,可以在下图左侧位置进行检索,缩小待筛选范围。
. Q7 U5 X, R' c# `) k
2 G' O* Q$ v/ n5 T* y l- 查看核心板的原理图,知道LED对应GPIO的PA11,按键对应的GPIO为PA12。
# a! j( ]/ Z5 I! Z9 ^2 D& E P N/ P9 W2 B/ c6 Q1 q
' k$ {" ?. W- j! [
- 配置PA12为外部中断模式
% v1 i$ J- r2 `: y/ k2 _
/ T# N1 ?5 o# Y' M
' S, u& L/ f6 ^8 ]2 D# f- 配置PA11为输出模式. d* m$ n* R! r6 `; a& G7 x" m0 j
+ \( [$ q0 r) A; ^- y4 }/ A
) P; Y" W1 c8 [' f- GPIO配置6 h0 ~7 I3 L! r) z" V' C5 J; N: p
) \, s3 \7 l6 @8 P, h$ s5 T
注意:因为下面按键中断的回调函数中我们加了延时防抖功能,所以此处必须是下降沿触发,至于为什么?大家可以想想哈,大家可以在留言中交流一下。
. V0 K$ u! L) ?( ~
- 使能外部中断,注意中断分组# d, H7 |% Y p
& C# \, ?6 S5 Q
6 N( R1 `7 j2 w- N9 z& k& V- 配置RCC2 {* y, M0 B) h2 @7 @
1 L- W9 P% A. ~1 s; o ]
6 p& h* \3 w" p. s. Z, V7 h- SYS要如下设置,否则下载一次程序以后将无法再次下载!!!6 \) m3 b% C* e; y& N6 ~
7 E9 E# q+ k6 S+ q! g F. ?
( z2 W! Z. R+ C. @
- 配置串口1为异步串口2 Z/ L4 U, @" j2 Q
& G! K: e: S. p
因为PA12占用了串口1的USART1_RTS引脚,所以串口1配置成禁用硬件流控的异步串口即可。
& Q5 ]$ `' |% M- 时钟树配置
; Y$ P: y6 B; v7 Y4 Q1 U3 V( t: Z, W1 g7 R
3 M! @3 R( j% W, F+ z% J: N) ^9 t- 工程配置,工程名称:STM32EXTIDemo。5 m' e y+ _# W$ [ Y3 m9 J
2 }! `. {. ?' }
" i9 p2 `, r9 R
- 生成代码$ Q. q0 W) K. e+ r/ ]
- `( N" m) e$ K/ V" K0 G9 v
* n, U, g1 F# f6 H6 ~( ~/ R
4 y' c* o9 c- Q6 Y! y% J
* V* i/ t! B: {( j
usart.c文件中,添加串口printf输出代码8 X8 u( ]" c3 }" @$ S6 ^0 F
: v4 w( f' V( J3 \" v' `2 y8 l9 h
. ~9 E8 G7 L ], t+ d$ t' V- #include "stdio.h"5 I7 A( d6 j3 x' a1 T
: y9 m$ w- S7 X" K2 T+ ^- #ifdef __GNUC__3 T0 b% e6 i v9 @; D' K
- <i>/* With GCC, small printf (option LD Linker->Libraries->Small printf
, K3 ]* N& \* b5 j7 d - set to 'Yes') calls __io_putchar() */</i>! G9 [9 X( Y7 Z/ ~ f; |) j
- #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)! n9 j4 g: J9 w9 a/ A* W* P6 m
- #else
& m V# G0 N8 C$ M: T3 p/ I - #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)/ T3 Y2 ?3 [. L: ]& B
- #endif /* __GNUC__ */5 `2 C7 _ t3 b& {: x; m
- % @9 w/ \0 L: P- w; c
- ) m* l6 T5 y7 U( l- S% ?8 g
- PUTCHAR_PROTOTYPE! Z! c; t" I$ X( q* M7 B
- {4 _8 s+ E* }% a: y
- HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1, 0xFFFF);
, p8 A, s" m9 N5 p - return ch;
) a& v2 L$ W% n9 E8 Y0 S+ v - }
复制代码
5 c& K- \, Y, N& _ C
" R4 C# g5 F" w4 F7 i h
- 添加按键中断回调函数
, G' y9 u1 E4 ` @. A$ U
' W" H. B3 S1 n: u6 d
% o) x7 s% y9 b, a3 f5 U4 H
- <i>/* USER CODE BEGIN 4 */</i>
- j) V, s- C L6 K0 K" u* [
( I P% g5 L8 \; c- void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
1 s9 s# S3 Z( ?3 P9 v1 n - {9 `1 v& X6 w) b6 W6 j
- switch(GPIO_Pin)( K+ Z& J2 B X# U `1 I! ]
- {
5 h" p1 h) q( f6 { E# d - case KEY0_Pin:
4 @2 F/ v; v( n! b, J; B; ?/ y - 7 R; }, h# O5 S- f" c
- HAL_Delay(10);" G8 V" Z( T# A1 ~, f) P; ~
-
5 q1 x- L v" h3 r* L' q - if( HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin)==0) <i>//延时10ms之后再判断是否有按键被按下,防抖</i>, | J3 i& a' ^- \& e8 Z E4 D
- {% d, A9 E- a" w% J l
- printf("KEY0 Press ... \r\n");+ b8 O( m" A6 \( c/ F# k
-
0 S+ r- q6 E* E+ }8 W! ] - HAL_GPIO_TogglePin(LED0_GPIO_Port,LED0_Pin);+ k" a; H+ W) X; H5 a; C7 k
- }& ]' r8 o# r3 p5 M* |
- break;
0 c+ a; U/ ^: W/ @$ V2 M" n/ u - . V4 [& W* Y. L7 m( d% u( r
- default:break;! n. A5 P% z% y+ A/ x. S! s
- }
8 ~( B8 g; A" j: Z! V - }
复制代码 : g; d. @, R* Y- h
其中: HAL_GPIO_TogglePin() 函数用于实现翻转 IO 口的电平。 实现同样功能,还可以使用下面代码分别控制LED的亮灭:HAL_GPIO_WritePin(LED0_GPIO_Port,LED0_Pin,GPIO_PIN_RESET) // 输出低电平--亮 HAL_GPIO_WritePin(LED0_GPIO_Port,LED0_Pin,GPIO_PIN_SET) // 输出高电平--灭' t% ]1 u) W8 q- q: E) e
我们为什么添加了上面的函数,然后就能与中断函数绑定到一起呢? 因为这里涉及到一个“弱函数”的知识。 在 HAL库中,很多回调函数前面使用__weak 修饰符。weak 顾名思义是“弱”的意思,所以如果函数名称前面加上__weak 修饰符,我们一般称这个函数为“弱函数”。加上了__weak 修饰符的函数,用户可以在用户文件中重新定义一个同名函数,最终编译器编译的时候,会选择用户定义的函数,如果用户没有重新定义这个函数,那么编译器就会执行__weak 声明的函数,并且编译器不会报错。
我们搜索HAL_GPIO_EXTI_Callback()这个函数,发现的确有__weak修饰的同名函数。
4 I) P7 \! o: l+ L8 e5 c7 h这个函数在stm32f1xx_hal_gpio.c文件中的HAL_GPIO_EXTI_IRQHandler()函数中被调用,该函数对中断标志位进行了清除。
* B. G0 @5 o. }% _6 O; r) K! J: l而上面的函数正好在外部中断函数EXTI15_10_IRQHandler()中被调用。
7 P, x n3 s' k7 r! G0 S) \由上可知,当GPIO发生外部中断时,我们只要在回调函数HAL_GPIO_EXTI_Callback()中,添加逻辑代码即可。 - 配置JLINK下载工具
1 B* A$ n$ N- a' f; {. ~) @& {& k X) n5 h" M
8 L' w- w' P7 V% R
- 下载程序后,复位核心板,按下按键,串口助手窗口可以正常打印预设信息,LED亮灭来回切换,证明满足预期使用外部中断反转一个LED。 v: |" O8 S/ f# `
+ i- M' D" e' k# V
8 N7 r- F( a0 e* s$ Q6 n- B/ Z
$ O) s% V/ P! _3 u3 J& Q- ~5 t
. o- @% Q" X4 U/ T/ V& Z; y
|