前言
5 s/ _( i& D; n, Y: G% Z1 @上一节我们讲解了STM32CubeMX的基本使用和工程的配置,那么这一节我们正式来学习CubeMX配置STM32的各个外设功能了( {( X6 p+ @: v A
: [; {5 f! j3 T" D今天我们会详细的带你学习STM32CubeMX配置外部中断,并且讲解HAL库的GPIO的各种函数,带你学习不一样的STM32
7 u! |* W' Y2 T, d# M; W2 Z
5 V+ I" V; {7 u( T; ]那么话不多说,我们开始正式的讲解吧!
) J0 w# B3 L1 e+ x; W6 ^7 _$ ~1 E- g6 N: T4 K# C
准备工作:
- l5 q. e7 b& I; [% l, X Z# Z
) G1 f4 V( a* z2 b% m( y* N1、STM32开发板(我的是STM32F407ZE和STM32F103RC) j% Y' d: j& Z( [/ x
8 Y' x# C9 m. y- s5 l
2、STM32CubeMx软件、 IDE: Keil软件
: e: G2 l8 G N
; j: u. X# L' J3、STM32F1xx/STM32F4xxHAL库 2 b1 d0 h6 u$ ?
1 X4 Z$ z' l1 @0 E2 z4、按键管脚 PA11 PB4
7 q3 ~7 q. \# U& @# T3 R: y1 i
' z: I+ R/ b) {; K7 C4 F/ N, ^7 B5 LED管脚 PC4 PC5 PB0 PB1
/ \9 P7 K# t! G7 O/ O; x
3 L+ t, `' B0 u0 x+ y1新建工程$ r' ~: S5 n6 z
1.1New Project
! l u" t3 ?/ W7 M. _( F0 F6 m
9 y5 h1 z( R2 \4 ?
& i3 H: U4 o1 \. o0 \! k% |9 Z7 F& a6 T: C3 b1 ?8 c! _
1 搜索芯片型号! h( H5 u3 y4 e2 j" b
2选择芯片
% [- l: u' B0 X% z# o3创建工程- E4 C5 [9 p2 p9 G7 d4 u4 g
1.2设置RCC
3 T2 H) h- y% |1 ~" y
4 P0 v4 l n. H: c4 i- x+ p& B
8 K* a8 l' ]9 s
' b' |+ l" K: E# u7 `& o) n
1 点击RCC+ ^0 ^9 V3 P9 K- D" `0 u
2高速时钟(HSE)选择外部晶振
+ G& [9 u/ O$ _3 软件自动配置管脚3 g1 x2 B( l& G& B
) H9 O. B# k7 S5 ]
1.3GPIO初始化
. S! D: N# t- q& W( ]
+ w3 A/ |, @$ Z- J( p; uLED:4个GPIO_OUTPUT (输出模式)* q! O" e5 x* O* L3 t2 r' V0 ~
6 f+ h$ [/ x4 s; M
按键:2个GPIO_EXITx(外部中断模式)/ q0 U7 y2 C6 r1 ?
' R6 t$ X' k& E( Z' G
7 m2 }9 T5 e4 a1 g" u7 f7 |" l% s. ?( u q+ b3 c1 l
1 点击对应管脚
5 ~) @0 f- y& P# }. }( A2 设置对应模式- p; Q" k- j1 a5 [5 k
/ u/ `# Q3 x5 N( N+ lGPIO的各种模式设置) L, a# b3 }" z! F" K! ?
; p$ w6 }4 N6 M- y4 ] n9 Y
- W0 y* I: F% S4 a7 o
5 {% \1 x8 d. w+ M/ J& o8 l, wGPIO output level 引脚电平设置 高/低
$ C$ Q$ w& [1 b/ qGPIO mode GPIO模式 推挽输出/开漏输出
( Y+ B9 b" |& w8 U2 H2 B9 M; S9 O( K* CGPIO Pull-up/Pull-dowm 上拉下拉电阻 上拉电阻/下拉电阻/无上拉或下拉! f, X9 {# ~* L0 a7 X
Maxinum output speed 引脚速度设置 低速/中速/高速
: I5 e; v1 g3 Z: UUser Label 用户标签 给引脚设置名称 如LED07 m+ X6 k5 z5 M/ I* d5 R) y
% [ b- S" K8 S4 N$ L" k6 k这里可以看出看和我们使用库函数的时候配置是一样的,但我们只需要动几下鼠标便可以完成操作,这正是STM32CubeMX的强大之处, W! N% R; m& X9 I1 c5 h2 f2 V
A. `# b& B6 T
GPIO_EXIT的6种模式
5 w7 y) S; f/ X* o6 l: ^7 W' l% v3 K; @6 X* l3 R1 G* a
" m' E# o" q7 n% p3 E6 _1 p* D- x
% g7 A7 t2 N0 H
我们的按键选择下降沿触发1 [2 }4 ` b: @
% ]8 F, [- S1 E/ {0 l( H
1.4设置NVIC(嵌套向量中断控制器)
$ G, R+ J8 W/ H4 k
0 h- y' K1 \/ S( K! B% J+ _
' j; \' I2 a% d1 G. K* O+ q
0 o" {# E8 c1 @+ g+ @0 }2 K1点击NVIC2 勾选EXIT Line1 F: k* O5 l" i9 B2 h+ Y. t* f
2 interrupt 和 EXIT Line[15:12] interrupt 使能中断9 w0 W+ x4 K2 O7 i" S- F5 u7 `
7 w9 f' f( p8 a
1.5时钟源设置
) y6 i7 p4 B4 K1 i5 w
4 G4 U, J, w# c3 j/ J9 b
+ M0 \! d1 v$ s u& E+ |
% Q- V1 R* {: \: I1选择外部时钟HSE 8MHz 7 S1 j8 S" V4 D* v8 d) c
2PLL锁相环倍频9倍
6 j6 s; \, D8 M4 x; A5 M3系统时钟来源选择为PLL: K9 |! c, Z6 |9 v
4设置APB1分频器为 /2
! N4 }% b5 F, [4 h& G5 d! o. z& a32的时钟树框图 如果不懂的话请看《【STM32】系统时钟RCC详解(超详细,超全面)》
# {/ M, Q7 g" h# E5 \. m2 U3 j9 J
& P. e1 F2 ^# t2 D6 P, q. w1.6项目文件设置
' V& G) t. y& r K! [* t
# ~) c/ B/ v) v7 E. W' _
: W6 k% c$ ]" w, u& a8 b, E* ~) e) M7 T
, H7 _1 V e& n7 [0 T! N1 设置项目名称
' S0 U& T# {4 h5 w4 y9 q* m: e- ~0 f2 设置存储路径3 B6 b v: a3 I: U, V5 Q7 @- s
3 选择所用IDE
{# r" `- l. a1 d: {! k6 v" U
# a) o+ H1 f* D1 T
4 ~# a6 _8 C6 E) i' [9 j. ], {+ T8 d; l
1.7创建工程文件
; n( u+ _6 g8 j9 d* O# |. \
* o& c" X. n8 _% o) z4 T然后点击GENERATE CODE 创建工程
8 p& I7 _4 M; r) a: p5 G9 _" M5 s% X5 Y5 u& Q5 w. }1 A8 b8 v
- j+ ^, U7 |8 ?: d4 ?$ a! K/ E
" g4 S8 O" `6 L5 s7 O F3 C. X随后我们可以在stm32f1xx_it.c中看到我们所配置的中断服务函数 并且可以看到gpio的初始化分到了gpio.c里面
. e& l$ _8 x" d0 g6 {5 O+ b; b+ |' |" i/ p
* b6 a2 N, ]/ T" `& c$ i; T& Y
, c) t3 r1 E; |我们看到他有一个HAL_GPIO_EXTI_IRQHandler();函数
8 y0 @ }( i5 I5 X% b* U& _/ p; Y n" a) h- x; ]! o
Go To Definition 查看一下函数代码/ C# |, i8 l* c. q, D
. M+ G% O3 j$ Q8 j# k5 h6 a! j
0 p5 n& U# |- p A& H/ i
* G' S. H( ?0 g1 X
和我们库函数配置差不多,就是清除中断标志位,之后进入中断回调函数中( o% V1 ^( i6 _
+ O: f* v$ Z- B% f9 i6 k+ K
在HAL库中,中断运行结束后不会立刻退出,而是会先进入相对应的中断回调函数,处理该函数中的代码之后,才会退出中断,所以在HAL库中我们一般将中断需要处理代码放在中断回调函数中,2 U* p+ f" U/ q/ \7 x+ W# h
2 i4 D( ?7 V6 f( i& k配置下载工具
+ D9 N2 Q+ q+ P, d新建的工程所有配置都是默认的 我们需要自行选择下载模式,勾选上下载后复位运行! N# e% x: ^ ]6 n
! @' V; X- y0 E8 ?+ Q% i
$ ~8 N* h6 s- ^3 Z. {9 v8 j0 r
5 Z, D8 s' I* S! I7 H u! ]0 H
所有自己编写的代码请放在/* USER CODE BEGIN XXX */ /* USER CODE END XXX */之间
8 L' R M5 C X" s
* G1 n8 X, c/ ?2 l) P 这样我们修改工程的时候你自己写的代码就不会被删除
& F1 n2 o5 [( q7 q
0 E. S/ i! v7 g- ?4 e
/ b8 i, b" N$ g2 m; R
/ l% m8 L2 J1 yHAL库 GPIO函数库讲解/ c7 u+ {7 m, Y. s0 a
在正常使用中,除了STM32CubeMX配置之外,我们有时候还需要自己配置一些东西,学习并理解HAL库,也是我们必须要学习的一个地方
) d3 `- o; R5 I& F. s" g# a
$ t8 @+ {0 } }9 T首先打开stm32f4xx_hal_gpio.h 发现一共定义有8个函数
* g3 p2 X9 T: d: r* _6 K1 I7 i3 S
" T+ O. J+ P2 F: `& l6 n- void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init);5 ?& V! [; V6 d; n; P Y/ t4 G& M7 E
- void HAL_GPIO_DeInit(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin);
* u: r- r) e2 y! u! G - GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);- \+ Z& K* a2 S( b# t' c# L* h
- void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState);
3 E; q! W2 ~1 ] - void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
* L+ B2 U8 i. H4 n7 z4 i$ u0 f - HAL_StatusTypeDef HAL_GPIO_LockPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);$ K6 P1 S. [7 i1 G, O: l
- void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin);( J& Z- O: p/ n- h, E% P. A
- void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin);
复制代码- void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init);
复制代码
2 S; [" l' q7 D |( N
) H/ y% W, ^& V/ D/ \" ~! L: R功能: GPIO初始化
# d. N4 r7 H. }/ V1 @) g
0 ~+ b6 \# M3 D8 Z- v' S% v$ X实例:HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);- void HAL_GPIO_DeInit(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin);
复制代码 1 i/ Y! R( t. r: D' m% D% A7 O. V: O
功能:在函数初始化之后的引脚恢复成默认的状态,即各个寄存器复位时的值
; _' c' P" y$ O- v! {& s$ ]- R
# z# H! E( A1 b v实例:HAL_GPIO_Init(GPIOC, GPIO_PIN_4);& a- K* o" s6 Q% n Z! N2 \
" A/ c4 i- o0 Z* _9 j. ?% P- GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
复制代码 0 d/ B/ J k. q) x- d" l
功能:读取引脚的电平状态、函数返回值为0或1, e3 O) p. O$ V& d8 s: V1 B$ N
0 p0 G; X, Z* _3 e1 _. J. a实例:HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_4);9 s5 m7 W1 p8 v2 s6 w1 Z9 [
4 J8 g$ I+ t! s0 [
- void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState);
复制代码 ! p! D9 T7 I1 `
功能:引脚写0或1
. `; a) E3 W( G% j4 r7 b Q% D# D" B9 J6 G
$ |/ p2 q8 S6 d7 n; }实例:HAL_GPIO_WritePin(GPIOC, GPIO_PIN_4,0);; X) E. H3 d) Z
; o& }5 Q" f/ z. c; s( h- void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
复制代码 8 \8 N2 s/ M6 d. q8 h% I
翻转引脚的电平状态
7 k' L! `1 ]2 I3 {: K3 Y5 x( J. F3 d) o/ _) ?8 A
实例:HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_4); 常用在LED上8 M5 o9 D1 z: ]5 C
" m- V1 r1 f$ S ?. w- HAL_StatusTypeDef HAL_GPIO_LockPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);
复制代码
. h* ^1 r0 p; e0 [" \( |8 U* q功能:锁住引脚电平,比如说一个管脚的当前状态是1,当这个管脚电平变化时保持锁定时的值。# J% |2 [$ D$ s9 w8 T1 ?
, ~! W, l! H# o& M) x% `. S实例:HAL_GPIO_LockPin(GPIOC, GPIO_PIN_4);
- o! c; S9 A7 f ^) G6 m' z/ Z" \6 n1 ]1 q, n* A# w
- void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin);
复制代码
# m9 _% f C7 Z1 I/ c, V功能: 外部中断服务函数,清除中断标志位
" P* z0 X# K _2 Z/ T
( H0 w# y' G& p: ]实例:HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4);
! v) i+ x, \, Q: Z* d
' F, Z0 Z# [1 d8 l1 s" J7 j- void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin);
复制代码 8 D- g! H7 L. l
功能: 中断回调函数,可以理解为中断函数具体要响应的动作。
y6 a v& U5 L% T% A( q+ Y0 c7 m2 @6 r) Y6 \$ C
实例:HAL_GPIO_EXTI_Callback(GPIO_PIN_4); 0 ~: x' A( i9 G- |% W2 ^# ]9 H
( _) Q( `# v- X+ S; N/ I9 ]6 b# V) A, ~0 Z! _8 \
GPIO_InitTypeDef结构体定义
9 e5 y9 t6 m o; T0 G+ b* B
. p+ I: q$ K$ I$ [5 Y2 G% Q1 R9 ]- typedef struct
2 _+ T0 b8 E' \, C* z& i+ z2 Z - { y. i& Z% `, l. W. a
- uint32_t Pin; /*!< 选择引脚
# o2 k0 L; y) G8 @3 k& i - */6 g$ k" A; E& N) b% V6 p
- / Z2 z4 f, ?% i( d! R
- uint32_t Mode; /*!< 设置引脚模式
s( v7 u. A3 R" H9 v/ L - */! c# U2 z. W+ g7 H/ w; C5 d# |: a
- ; R! @! ~4 I" u \% m$ Y* R1 m! Q9 \
- uint32_t Pull; /*!< 引脚是否上拉或下拉7 d3 J+ V8 P& C3 l$ y! x
- */
# I5 y+ S ]' y B' k! d
J @7 C7 R, O% t3 z- uint32_t Speed; /*!< 设置引脚速度8 G6 g* g9 \0 U0 p, e( s9 D2 l
- */2 }. J! w0 \6 T# u8 d [ h
- } GPIO_InitTypeDef;
复制代码
8 K6 b2 C/ j; Q/ F, d同时,不管是标准库还是HAL库,都是基于寄存器开发的,寄存器编程在哪个库都是适用的
+ B2 h0 W( l2 m. f! S5 M
, N+ d0 V5 B3 Y8 ]GPIO的相关配置寄存器" K4 D! T$ n) |' S- M3 x
STM32的每组GPIO口包括7个寄存器。也就是说,每个寄存器可以控制一组GPIO的16个GPIO口。这7个寄存器分别为:/ d% j, A: }2 C0 H7 \. v
& g; C k+ Z" e+ i
GPIOx_CRL:端口配置低寄存器(32位)0 H& ?% R- [5 F8 e0 J6 `+ n
GPIOx_CRH:端口配置高寄存器(32位)* ^. C; f1 b# q; m W i
GPIOx_IDR:端口输入寄存器(32位)4 i+ L6 V1 J {, g& n M
GPIOx_ODR:端口输出寄存器(32位)
A7 E# u8 k7 X, f# z) ?GPIOx_BSRR:端口位设置/清除寄存器(32位)
6 r, v' K& s8 o, }2 N8 g0 SGPIOx_BRR:端口位清除寄存器(16位)
2 \6 _5 r- _) cGPIOx_LCKR:端口配置锁存寄存器(32位)
6 }7 {% p q6 A* j; h4 t1 g L- K6 P4 ? @* Y$ N$ q
具体我们这里就不讲了,篇幅有限,可以到《STM32中文参考手册》中查看 I; J1 c |9 K1 l% H: n
# C3 q1 M! @0 B6 k* A% [/ Q% y
关于GPIO的八种模式请参看 《STM32 GPIO八种模式及工作原理详解》
! X \1 C, \* x/ E, k) ?% J2 U+ j$ D& O# n% d% T+ `3 |3 H8 b
按键消抖:
. l( W6 P* `" o/ K! a! F. o, l( C6 J
9 P& z/ j: I0 n: j p- /**% {! t2 h p# o- E! b/ R* a
- * 函数功能: 按键外部中断回调函数
. \+ |, |3 b2 n2 H - * 输入参数: GPIO_Pin:中断引脚
5 R* s( s* z" b5 A - * 返 回 值: 无7 `8 E; F! ?/ U
- * 说 明: 无
/ Y: m/ m% X7 ~8 o. i; I5 a% v" O - */. r$ k! ~1 g% ?3 C' C6 o c8 c
- void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)- B. O+ K( w4 g" d1 l% \; O
- {/ R" B$ l* y8 S$ g! `
- if(GPIO_Pin==KEY1_GPIO_PIN)+ `! P( P; _0 ^: |# e
- {" P+ L. P" K$ [9 O
- HAL_Delay(20);/* 延时一小段时间,消除抖动 */
* a# Q6 M. Z0 L, c - if(HAL_GPIO_ReadPin(KEY1_GPIO,KEY1_GPIO_PIN)==KEY1_DOWN_LEVEL)
! `6 h! M# H0 x& B4 `- H - {
9 \6 C( V9 t( f - BEEP_TOGGLE;" x8 U7 O( K. G$ Y
- LED1_ON;+ x B' Y4 T0 M0 M! f
- LED2_ON;
: Y ^& R7 ~% \1 m7 c - LED3_ON;# M- k2 g, J5 C0 w4 T
- }
# ~! }# C4 ?" }4 g" r* @9 I - __HAL_GPIO_EXTI_CLEAR_IT(KEY1_GPIO_PIN); r* G9 s& r( b$ x+ h5 p" s9 c
- }
4 K( E. @& C) t* |* p$ O& L- x - else if(GPIO_Pin==KEY2_GPIO_PIN)
: @) t0 ]1 d) v - {
6 T$ g/ q7 V; R+ [ - HAL_Delay(20);/* 延时一小段时间,消除抖动 */1 x3 N9 X( M# t( v. l& k
- if(HAL_GPIO_ReadPin(KEY2_GPIO,KEY2_GPIO_PIN)==KEY2_DOWN_LEVEL)
8 _: E; G/ t, l- r9 Z - {3 j8 A! U: j' U2 Y( y' ?
- BEEP_TOGGLE;
( t* B: a3 Y/ `* y - LED1_OFF;
6 F& q! Y" X3 U& E' P - LED2_OFF;% w# O; j$ V7 |* r
- LED3_OFF;
* x1 G5 h5 o3 Y! y$ r - }
% @: }0 A/ {6 T+ F% u+ ~2 r - __HAL_GPIO_EXTI_CLEAR_IT(KEY2_GPIO_PIN);& c% v h0 u( `8 ~7 l C
- }
- V8 u$ |! K6 M4 E3 c+ l - }
复制代码
) M3 {) v L: O& ]7 @' ?2 s. Z3 X- h4 j/ z
|