前言: J8 p. Y; [# p( V
使用的是芯片:STM32F407ZGTx K* b$ q4 X2 P( q9 G
, I" x$ e, d, k9 V! l
学习说明此文档为本人的学习笔记,注重实践,关于理论部分会给出相应的学习链接。3 W ~6 L0 h, V' t4 P2 b' E
% i2 L8 j0 m0 W3 t本文档添加了对代码的在线调试功能,有助于大家更好理解相关寄存器的变化。
( V8 S' H0 c* V3 ]8 l1 E U& G) K) w( h
6 |" Z* D& @: H- Q; i$ k9 N& V' i/ `
理论学习
5 ^! ~- `: t0 Q8 j: ]4 ^0 _: b一、蜂鸣器简介
/ `/ ?4 ?5 f w有源蜂鸣器自带了震荡电路 ,只需要对其上电即发声;无源蜂鸣器需要提供相应的方波才发声。 m: A2 Z7 Y" R) m/ f) V1 Q) |, z- F1 X
- r4 Z7 @( ]9 A
8 e( s* ~2 i, A7 Z: X v @7 Y. A: U# b
32的IO口可以直接驱动LED灯(所需电流较小)。STM32F4的单个IO可以提供25mA电流,而蜂鸣器的驱动电流为30mA左右,但是STM32F4整体才150mA,直接驱动会非常浪费资源,因此IO口并不是直接驱动,而是通过三极管扩流后再驱动蜂鸣器。% Z" e, l# ]5 K* y
) b5 {, h( F% |+ _/ v' ~& l3 d: M# [" `5 M# M; |
二、机械按键简介
6 H7 H( \& s; f) e& h A开发板上搭载的是机械微动开关按键,这种按键在使用时需要注意其抖动,按下时,信号不会立刻稳定/ K3 v$ w% C7 t Z8 ]' m2 }
: o# c8 f1 n* @- Y
需要进行按键消抖,这里通常采用软件程序消抖的方法来避免抖动。# j" }- v2 f& G: @( t7 |2 Z
2 i3 k& }' ~( e; U6 m$ O# _. f! L
5 e6 h& i2 X* n1 E, U
% q! h8 Q1 v- Z! [. P" L三、GPIO配置简介" X* y+ O) }6 C7 j$ ]
9 v8 g7 x. x* G2 c1 ^, B
5 k( D. h# T9 ]# k7 C* P8 i7 F实践学习
4 d0 [, ]% R* F. e4 o/ M一、设计规划
\& j) p* E1 |1 f蜂鸣器实验和按键实验与跑马灯类似,都是对GPIO进行操作,因此本博文将两者放到了一起进行实现。0 J+ M8 Q j( {- o
: \% J, Z- G7 c( M( {0 c
1.1 实验目标 ( D* E; T8 j8 z7 M( n
利用板载的4个按键,来控制板载的两个LED的亮灭和蜂鸣器的开关。
- t4 m8 }) C( N6 \" l
5 w& C# G$ D. m7 H0 {! z1 W# o1.2 硬件资源
- I+ B% g, i3 J$ f o% K7 B6 J3 v3 K6 |) U. C3 P+ E7 k
k4 v% N8 Q+ q" B9 ?9 r e' D' O! q4 {
: c, T5 t1 y7 \5 C9 x: c+ W0 w* W( s7 I z! L$ f \
. q4 f- o) A6 k( S. ~
S) P7 F+ t5 |) V' V$ G
除KEY_UP是高电平有效(且外部无上下拉电阻,需要在32内部设置上下拉),其他按键是低电平有效。
2 U& w1 N% N8 z: q: \' L6 f7 e3 I% J8 L$ z% \- ?& P5 o- K
* J% ?% d# T5 A; k3 @+ A5 f二、程序设计
0 h' h j1 }2 q- r% O9 _( I2.1 建立工程文件& t# H: W. v% [9 t- L5 b
如图已经添加完的工程文件目录。
! \) P1 j/ b7 h8 w
% \' n9 q+ p+ j4 L4 s, C" K7 @
0 b+ \, y/ S1 f0 [8 b! S# E; |
6 K1 T% L% `# Y8 H( X8 R$ T
: x: w' G5 \) M( r) \) t L2.2 led配置0 c4 x* S, b- v3 r1 B
板载对应的GPIO口为PF9/10,并将其配置为通用推挽输出模式,输出速度100MHz
7 v, A. M& Z& w
1 q2 A: _. h3 J5 Y) sled.h文件
4 I# i2 M2 d \! A# L \- #ifndef _LED_H
3 h/ \2 `) }; v# L - #define _LED_H
" @$ a' ^9 q9 {) `3 }+ \6 _ - #include "sys.h"
1 d: `/ O3 I3 j4 n3 g3 { - //LED端口定义
9 c8 R+ L+ p# H - #define LED0 PFout(9)
* E6 r: O1 u2 d7 p6 w. x& m0 j; v. u - #define LED1 PFout(10)
^. J8 |8 {% G, v0 I - ' A* L- D' R3 X* S3 I
- void LED_Init(void);; ^, y/ c8 w" n/ T$ t, h
- 2 z2 b: K4 o; H) }. L4 r
- #endif
复制代码 6 m8 R4 F/ l9 X+ S" C& H5 Y; H
9 u9 P. O$ f' I# G" |led.c文件
+ @6 m# d$ E# j* s W( |- #include"led.h"
. |& j5 a. U8 x# v -
" q. c- b1 a3 ^ - void LED_Init(void)" \0 X0 L2 Q$ O
- {9 {# m5 E2 ]9 j/ n$ ]- _: h
- RCC->AHB1ENR |= 1<<5; //使能GPIOF时钟
H: O: ~3 w1 t! m - GPIO_Set(GPIOF, PIN9|PIN10,
: C* [* L' Y4 w) C - GPIO_MODE_OUT, GPIO_OTYPE_PP,9 E. E5 t; t9 k6 I2 C
- GPIO_SPEED_100M, GPIO_PUPD_PU);
" m: m. B$ z; D - //初值
{9 I. d8 S; i4 [2 L/ e' o - LED0 = 1;
0 H8 |( `* j- z' U( D" q9 X F4 h - LED1 = 1;
% a" Z$ t V: ` - }
复制代码 . f2 n- _' z* Q
2.3 beep配置" [$ b' \3 ^# `3 h' S8 Y; [. y
蜂鸣器比较见到,就是一个GPIO输入 高低电平; h$ o$ G0 l9 b, h7 {8 n6 K# E
beep.h
" o' K; X e& z, }6 K `- #ifndef _BEEP_H
7 }4 v7 z6 s% Z I2 q, v" K, m - #define _BEEP_H
* f" Z/ _' q4 ~; E4 U4 d' U5 j$ A0 _ - #include "sys.h"! s7 m, h% k B4 I* e
- #define BEEP PFout(8)
8 x! X: q- L* z3 g - - ~: V' E% Z, ]! G; ?' \& ?# [
- void BEEP_Init(void);
( \; g) `( g0 R7 U W -
% O* c Q' l8 k( ?2 H. O - #endif
复制代码 * c, w, Z! h& `/ T: G( f
beep.c/ s" h% a! n4 z. y1 B( W; F, G
由于蜂鸣器是高电平有效,因此将端口配置为下拉推挽输出
+ K; @2 S0 F- I g, o* ]" t" V- #include"beep.h"8 s6 S% a: O2 [- T9 ]$ ~
-
: R# J! r' \0 y8 h( J - void BEEP_Init(void) i" D- N: a4 d( p( l. s
- {& F) A' e9 S9 ]4 L/ ?! L
- RCC->AHB1ENR |= 1<<5;. }9 a% m+ a: G. V, h8 t
- GPIO_Set(GPIOF, PIN8,! e7 D8 S$ A5 C7 f, y
- GPIO_MODE_OUT, GPIO_OTYPE_PP, 2 L, _9 W! G- y; T7 ~" V/ w
- GPIO_SPEED_100M, GPIO_PUPD_PU); B) c$ i4 @: D" P& f( c: E
- BEEP = 0; //初值关闭蜂鸣器5 H5 Q1 J# B" D9 H
- }
复制代码
3 i8 f" F/ Z3 X% |+ r- i3 N4 a2.4 key配置
: Y# K U2 t+ f9 |key.h, p3 C( M4 a0 e! Q8 }+ F
- #ifndef _KEY_H- F0 G8 Z* |( R. y& j
- #define _KEY_H; T7 S" d r7 l. k, ~/ j# t
- #include "sys.h"" V. x6 n1 V$ _$ n* @1 a& w
- #define KEY0 PEin(4) //PE4
$ y3 z" U0 ^- ~2 w+ e2 n - #define KEY1 PEin(3) //PE3- q: A$ `* ~* h+ }5 \2 B! G
- #define KEY2 PEin(2) //PE2! C# o! x) W, {7 I& n `
- #define WK_UP PAin(0) //PA0, b' j( Q4 i' ^
- #define KEY0_PRES 1 //KEY0
$ [" \: z6 ^: _: K0 Q# P/ Y - #define KEY1_PRES 2 //KEY1
- `' U! {) n4 `3 ^1 V - #define KEY2_PRES 3 //KEY2 * A3 x# V$ y+ y1 g6 }
- #define WKUP_PRES 4 //KEY_UP ( WK_UP)
. }& z. j/ r" v+ W" t3 ~ -
& k; g: v8 Q" W5 Q9 |8 B - void KEY_Init(void);# \: V+ w* w; a, F
- u8 KEY_Scan(u8 mode); //按键扫描函数
2 I$ Z9 [ b4 K" a e/ q0 t - 9 k8 k' q& F" t+ y2 s) n! g! {. T& |
- #endif
复制代码 1 `3 c% y' D$ O+ e+ K; I* G
key.c
2 |9 k4 u5 J8 Y6 @- #include"key.h"3 m: W& l" B$ z! u' \- g( T7 Z9 j4 k
- #include"delay.h" //ÐèÒªÑÓ³ÙÅжÏÏû³ý¶¶¶¯* ]" ~6 k3 \! }7 ^, t/ {4 p
- void KEY_Init(void)
" S: c" L; S) M - {+ z4 d* p8 n8 d3 v( S8 Y, v4 | H
- RCC->AHB1ENR |= 1<<0; //GPIOA( o) ?6 u Z+ s5 Z3 K1 _& |
- RCC->AHB1ENR |= 1<<4; //GPIOE# n/ N: Z: G( x
- GPIO_Set(GPIOA, PIN0, GPIO_MODE_IN,4 q- D \0 l9 S8 x7 @
- 0, 0,
$ X: G& S$ e1 [" R" p% x% u6 ~* { - GPIO_PUPD_PD); //PA0 高电平有效因此为下拉2 y5 N, d- m% \9 v
- GPIO_Set(GPIOE, PIN2|PIN3|PIN4, GPIO_MODE_IN,. o( x& F! g/ n# Q& J6 r. C" A
- 0, 0,# c4 e# S" F0 @/ {5 C* u9 v
- GPIO_PUPD_PU); //低电平有效,上拉
1 c' T- Y- k! F7 s - }
/ Y$ T- O3 G6 G - u8 KEY_Scan(u8 mode)
) s3 I; }$ Q1 w- l' t2 A - {, b' t$ H/ f1 W2 c3 s* k
- static u8 key_up = 1; //静态局部变量
' @: S( n3 I; b9 i1 D6 o# ~ - if(mode) key_up = 1; //当有按键按下时,按下标志
4 D" o7 y/ P% ?+ v - if(key_up && (KEY0==0 || KEY1==0 || KEY2==0 || WK_UP==1))
8 U! `% N- n/ _& a8 c. X* D* A - {! t' j+ V# S/ W
- delay_ms(10); //ÑÓ³ÙÅжϣ¬ÒÔÏû³ý¶¶¶¯4 T) l' I# ]( J* |' P) L- K
- key_up = 0;
4 F5 F1 X& v8 ?/ ^( m2 }& q+ p - if(KEY0==0) return 1;* e$ ]& s& d; I0 e; A: q3 S
- else if(KEY0==0) return 1;
) K# H- E1 S' b! ^6 \* Z - else if(KEY1==0) return 2;
) l1 f& B7 a) V& ] - else if(KEY2==0) return 3;5 j# x# R! m8 z5 d4 I' y- }
- else if(WK_UP==1) return 4;
2 T: i2 J0 b9 A% d6 I5 h+ z - }
2 J! K5 F& q" \3 u( @1 R7 I - else if(KEY0==1 && KEY1==1 && KEY2==1 && WK_UP==0)
9 n7 @ m8 S; {& A; Y. R - key_up =1;% p7 b) n) n! I
- return 0; //ÎÞ°´¼ü°´ÏÂ8 ~1 I, O; d2 k# q+ F9 D, h
- }
复制代码
* F! b3 P, j. d一定要注意高低电平有效,本人在敲代码的时候PA0(高电平有效)设置为了上拉(有问题);PE2-4(低电平)设置为了下拉(错误)。 如果这样设置就会出现,按键是否按下一直有效,因为按下也是相应的状态。 , C. Y/ H1 n5 S/ l1 Y
% k- R: c7 j! O' H3 ]! X& N7 x( x4 S
2.5 主函数text.c
* E7 O$ u5 V- A- #include "sys.h"; L) \4 c( @; t$ O: Z: t1 O9 i
- #include "delay.h"
( i4 F2 f9 b8 r8 B! p* o - #include "led.h"' R, G* _5 r& i
- #include "beep.h"
& u4 c" h2 b: D, x$ }: K, n - #include "key.h"
; ~( B5 R' V4 O! H; F3 m | - int main(void)8 E4 k* r5 ]0 |( [
- {3 D* `. o6 q' x! F2 g: w" L8 v/ ~% B
- u8 key;' H% L5 I* u6 ?- Y6 Q2 P$ p
- Stm32_Clock_Init(336,8,2,7); //设置时钟 168M3 R3 j) ~% I0 f0 p; y- O: Q* P, ^
- delay_init(168);0 {' g e: i3 Y9 K. N. ~9 l& {
- LED_Init();
/ s4 c' q+ M. n2 G( _$ p5 o0 |- q - BEEP_Init();
: G+ U A" Z. A" z - KEY_Init();
% w$ U& j5 P+ M, b# W% _4 ] - LED0 = 0;
! L* p! _% ^9 e/ E6 m - while(1)
* N- o6 p+ ?/ z0 C, k' |4 `' A - {
+ H( j9 Y; ~& s - key = KEY_Scan(0); 1 \ P' R8 O0 E3 J
- if(key)
' `# U. u8 c3 _9 k5 w" K. t - {
7 s. h) y# o T9 w% S: Y, L - switch(key)
) a" Y% W! {# T; L - {
' N5 P( o# M. d" R& z7 E0 ~ - case WKUP_PRES:
- m' q5 [; z3 u: Y1 G0 N - BEEP =! BEEP;
9 A; U- `' x7 y! w9 H - break;+ g) U" [+ A6 x) d& k9 i# B9 E
- case KEY2_PRES:: Y3 H2 M+ p# f: ~" V) {1 r& B
- LED0 =! LED0;" {: \. V5 x1 J) h
- break;" B) D, C6 v0 i! Q8 \5 Z8 B
- case KEY1_PRES:0 t- o! k1 S' y1 [( [( ?
- LED1 =! LED1;/ J- s7 p! c+ n, g; |/ ?
- break;
' Z/ b% S) A2 @- b- H: J - case KEY0_PRES:' X7 J1 o. k3 w5 c0 ^
- LED0 =! LED0;
; m: r @; ]$ _* a - LED1 =! LED1;4 S- U8 a3 i& I, {
- break; + m3 r( W! Y3 @- E$ g; I/ P
- }
) v1 G, L3 _( u - }else delay_ms(10);
; E2 Y+ I( S7 _0 V6 M - }
4 W! I+ B$ H# R' j7 Q# F - }
复制代码 - z# [' v7 E H7 P
' f+ q7 u# I }: L6 ~3 M* s
三、在线调试
" a# x4 Q0 _9 E4 J. L' C/ Q8 C/ x1 R下载完成完成后,打开调试,点击运行,并调出相关的寄存器。/ p1 R1 h, e$ T: J9 P
r P; L0 Y; |4 h, O( E1 ^3 W3.1 相应寄存变化' D6 M6 P$ Z2 E8 N) p9 z" P
按键GPIOE口,可以看出按键的相关位的状态,如图由于按键低电平有效(上拉),此时相应的位被拉高。
. F& o: c6 a7 r d5 H
8 ]3 e+ R0 A0 l% O( q* S
3 }8 [0 E+ u/ N0 a' J
I/ x& h j4 R5 @2 B" `9 w q4 x3 G. W# r
LED灯/蜂鸣器的GPIOF口,输出数据ODR 寄存器状态(低电平有效),默认LED0亮;蜂鸣器高电平有效(不响) 。
8 `/ x# Q6 U( I( b/ E) F: J2 [6 m. c2 u8 O5 x
3 l* ~- F( N$ S6 \9 ?
! C, S- q; W% V 当KEY0按键按下时,PE4由高电平变为低电平,LED0,LED1状态转换0 P, ?+ M) {3 ?! e2 i* m2 e
' d1 {4 {/ y0 r
: {2 Q0 ?! t. M# H2 m& g/ w% q
1 `# {! C1 m/ r
( p- a4 \- }( s B: G4 Z
K- C9 x N F, U1 w9 e 蜂鸣器类似,这里不做演示。8 t/ u) ~- g9 Q
5 r" m6 Y6 m$ t" Q6 Y1 y5 _+ a
) `+ b, V; h3 @+ v3.2 关键变量变化
$ j- a- F4 Y: z. W' [点击可以添加变量,然后添加变量;也可以直接选中变量点击右键选择“add "key"to Watch1。
2 X% ?. m4 G# e5 b+ l3 W- J" H: d! f( ^2 V
Z- ?9 Z4 D+ V# ?9 O; b# R) [
5 w9 ?9 k9 U; k: ^在变量key后面打一个断点,将代码运行到此) f9 `* ], T, L( ]* ]; A
" D! ]/ T; H; w7 H$ J/ C+ E
1 h4 g! i8 F ]0 ]
" X0 `: T3 A2 x3 N/ ]2 {
在板子上按下KEY0,可以观察到扫描函数返回值,key=1。与上面的寄存器变化相对应。: L# ~$ R: u0 X' ]; X
* {1 B D, p# w) n, C7 a8 M
" \& w* {% Z A3 H* \; c! e3 C. U. `9 H1 P8 u
四、上板验证6 Z1 l9 ^) t# L! Y8 @2 ^! u" _1 y- G
整体编译没有问题:
) }; _) @# I8 J H8 z
: c3 S" V; o6 v( y! H
- r, C1 S) C! ~; T/ W
: M! P. M7 Z9 ]+ N( s- B 下载到板卡中即可实现相应的功能。
% F0 J. D. o+ A, |: H2 q————————————————
( j0 e0 s. S, H9 n9 E# u1 u# l4 h( i! j版权声明:追逐者-桥
* z8 c& x9 ^/ |* K2 O% }+ C$ {$ a如有侵权请联系删除9 u& ~) O/ ~& q# x) ^
5 N8 @( J& P8 v* l' [8 V# H/ D" {0 J
) ~' |- Z7 b/ W- c. V& s0 y |