你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32心得:GPIO实验-基于位操作

[复制链接]
STMCU小助手 发布时间:2022-11-20 16:19
实验内容:跑马灯、蜂鸣器和按键输入: B, ^: h2 }- I6 S8 w
官方资料:, F; H' B8 E& p
1)《STM32中文参考手册V10》第8章 通用和复用功能IO(GPIO和AFIO)
( B( l! n8 a" N2 d2)《Cortex-M3权威指南(中文)》第5章 位带操作
7 A( Z" g1 O6 K. E) W( R9 d) u* t" L/ ~4 w2 p% m* q8 f& o
原理图:
' b; b, B0 d4 n; u* u6 f8 Y) d1)如图所示,跑马灯,PB5接LED0,PE5接LED1;
+ ?+ @, O3 v& A# ]/ g9 f7 A 20200326101027485.png 5 e3 j: @2 z- O! m& p: ]6 A

2 ]7 w. Z& S$ P( j$ t( N3 Z2)如图所示,蜂鸣器,PB8接BEEP(蜂鸣器);
+ V' {' L) p& B0 K- L% e" c4 H, X1 Y) B
20200324161510334.png
! T$ |1 n6 l! n7 j$ U! q
0 j- \$ ^/ T9 u( i* ?* O: @% R3)如图所示,PE4接KEY0,PE3接KEY1,PE2接KEY2,PA0接WK_UP。
, y2 ^: B$ e+ n1 h! L+ v, q3 A  L  w: \1 L- Y0 A
20200324161620799.png
7 Y& q3 P$ ^3 H7 H/ [% e0 X: U; j# R! S- j" R
工作原理不细讲,一定要看清外设与哪个GPIO管脚对接。# H1 {) N' p; _5 S
; N! `8 L+ {: M  [, Y/ ]
涉及已学GPIO库函数:
: j+ ?& H3 ]+ M1 a# d% i/ y0 v8 j
6 l+ M) w9 |! N5 d# s& F
  1. 1)void GPIO_Init(GPIO_TypeDef* GPIOx,GPIO_InitTypeDef* GPIO_InitStruct); //初始化作用
复制代码
# C4 v# u5 I, S& Y* e# g3 }
新知识:位操作地址计算方式:
% l6 ^! ^  v& F* j( X4 E) [: l+ K
20200326101310408.png
5 Z- S! Y  q* Y3 e! w2 m
, k2 T7 C& t' K/ B( v/ [4 [7 M假设要令GPIOA的第五个管脚为输出:PAout(5),查看sys.h和stm32f10x.h两个头文件可知:7 p9 [0 p; X! x9 G, B  q* Q
1 q0 S( a6 Q2 Q0 K6 I* W- p( Y
2020032610140654.png " j- f: k) G! @0 R$ o5 B6 C

- u& h% d+ w! c( X4 t* ? 2020032610144891.png
5 c! P" R+ N8 M! Y+ V7 [; B  E) k% v8 L6 |
因此,GPIOA_ODR_Addr=0x4001080C,n=5。之前给的紫色公式与头文件中的关键代码一一对应,可算出PAout(5)的别名区地址。, J9 P4 M* Y0 N
; D; v! b3 s; ^/ D8 z- h  ^
2020032610155149.png
% w2 G: a- p5 Y$ j6 x' U  K+ H4 e
因此,位操作可以这么玩:
2 b: b, \, o4 L3 f1 k$ V* ~1 A在led.h头文件中定义:
7 d& s) m7 i! `2 Z6 p% |; a
+ p) d6 Y" `: X# A9 l
  1. #define LED0 PBout(5);
复制代码
) O) S! X* ]$ r$ _$ Z
在led.c文件中定义:& {& L1 o/ I; v- m2 f( i- k
8 g0 o% @/ r+ D0 N% J5 S: y8 g; j
  1. PBout(5)=0; //设GPIOB的第五管脚为低电平。
复制代码

4 l4 ?% b2 r% z, j' k4 f; |最后再提一下为什么是GPIOA_BASE+12?STM32中文参考手册讲到,GPIOx_ODR的地址偏移是0Ch,因此是+12。对应的,GPIOx_IDR的地址偏移是0x08,因此是+8。至于GPIOA_BASE为什么等于APB2PERIPH_BASE+0x0800,没找到答案,大牛们可以解释下。
! i! s0 |# Z  D9 g, m
/ ^' L" y9 N, k( l* A  S+ g7 r 20200326101725773.png
/ L3 ~! y; n3 h+ y; U2 A
$ Z7 F3 \7 O! K% y8 S4 G& f 20200326101804571.png ! n& D% {/ ~/ x' a0 @9 E
! z* e4 r( ?0 a  b! ^! `7 k
实验步骤:
+ h5 C4 F5 G# t1 P! c) b1) 新建工程模板;+ j' W4 P; d2 S0 h
2) 新建HARDWARE空文件夹(与USER文件夹并列);
5 Q0 U6 c4 R7 Y( Y' l3) 在MDK软件中,新建led.c,beep.c和key.c三个.c文件以及对应的led.h,beep.h和key.h头文件,均保存在HARDWARE文件夹中;% k5 l9 r- q7 `7 }) Y0 n
4)led.h头文件代码分别如下:8 v0 r8 t- g3 q

) c; g8 F' m; s. F. q3 D# }" T
  1. #ifndef __LED_H, \5 N6 d  p) S! {
  2. #define __LED_H" R* z+ `- F1 _8 B' I" d; V
  3. #include "sys.h"
    : x5 @& U/ H* f- S' o$ o; k
  4. void LED_Init(void);    //初始化函数
    # d' q0 n; J$ A. s5 b: u
  5. #define LED0 PBout(5)   // PB5
    ( E1 i  T- a" W, j% N3 d7 _; Z
  6. #define LED1 PEout(5)   // PE5 8 A; x' [  o1 w3 z
  7. #endif
复制代码

1 T$ ]) E3 l% O8 \2 O$ C5)beep.h头文件代码分别如下:: `( _3 r2 {+ W9 D. h# }2 F
1 Q4 e! E( J# {: ]- T1 ]' X4 V
  1. #ifndef __BEEP_H  j0 q% ?! U! g7 q3 [+ S( f
  2. #define __BEEP_H
    ! Z- I0 ~, D3 v2 g0 h
  3. void BEEP_Init(void);   //初始化函数$ Z- h- i  K# a* y% ?
  4. #define BEEP PBout(8)   // PB8
    % h: j! h9 c5 g# E* T
  5. #endif
复制代码
7 K  v) k. T  M( X
6)key.h头文件代码分别如下:7 j1 f" x. h6 j" ~3 ]1 V( z
& J9 y& t  O9 W. G
  1. #ifndef __KEY_H: I% i! r2 z4 l  H, |5 }+ P: `( H
  2. #define __KEY_H0 g9 V1 i- y" h; s7 i
  3. #include "sys.h"0 {* [5 S. s$ S; B* O4 c
  4. #define KEY0 PEin(4)    //PE4
    ) i! g  Y$ s  X) ]) f  e2 Y
  5. #define KEY1 PEin(3)   //PE3
    $ P/ C- T' z1 }
  6. #define KEY2 PEin(2)   //PE28 s( S! Q" a4 E0 s4 [$ Q! @
  7. #define WK_UP PAin(0)  //PA0  WK_UP* O# u+ M; D+ R
  8. void KEY_Init(void);+ f1 ~) x) e/ l. n1 h
  9. u8 KEY_Scan(u8);
    * ?8 c2 H- @( z- i" Q  _6 p' M
  10. #endif
复制代码
& n. @" r: l& n2 z# m! n  K
7)led.c文件代码分别如下:
: `$ \7 o, \; {0 B7 r, l1 }# t, a
  1. #include "led.h"
    ' Y+ j2 w- r& X. N- E9 `6 y
  2. #include "sys.h"1 u! Q. v; v7 s
  3. void LED_Init(void)
    $ F9 G3 Q7 Y( q- ?4 S$ O8 X
  4. {
    5 m4 m: V; ^  ~/ c
  5. GPIO_InitTypeDef  GPIO_InitStructure;
    # C. d, ~+ ]7 [8 T( `  e8 C, H- r) O' z
  6. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);  //使能PB端口时钟
    ' M7 y4 T$ q6 r9 i1 F  O
  7. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);  //使能PE端口时钟2 W5 d0 _5 E1 z) t% h' L" z
  8. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;           //LED0-->PB.5 端口配置% n6 J! R7 m) B" E; m# c$ Y+ F
  9. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;      //推挽输出
    * e; u# n+ O8 ]' h3 Q, w$ m
  10. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;     //IO口速度为50MHz" N: w) `8 L$ Y6 Z7 {
  11. GPIO_Init(GPIOB, &GPIO_InitStructure);              //根据设定参数初始化GPIOB.5
    % h% X- ?2 X% |" p$ \6 D4 {
  12. LED0=1;                                         //PB.5 输出高,LED0灭- ]1 |; X9 N! K
  13. GPIO_Init(GPIOE, &GPIO_InitStructure);               //根据设定参数初始化GPIOE.5% t( x( V5 a2 h! X) i
  14. LED1=1;                                          //PE.5 输出高,LED1灭  
    ) x) I5 g/ ?3 `' B8 V' p2 ]5 ~
  15. }
复制代码

7 F' T* A/ M6 O2 j8)beep.c文件代码分别如下:
$ D  e1 R& w$ a- f% D; e
7 D6 F( N" G8 t9 K8 a' l
  1. #include "beep.h"
    ( @; ~( Q8 J& M0 H3 m. M  w
  2. #include "sys.h"6 @0 ]. W2 D0 m" h  `1 z  _9 P
  3. void BEEP_Init(void)) _& J9 x4 w* |( b5 v
  4. {
    / v- r( _4 [1 Z1 i
  5. GPIO_InitTypeDef  GPIO_InitStructure;
    # ]/ b2 P! l& b. f, k/ W
  6. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);  //使能PB端口时钟
    0 p% `/ c/ e9 j# g; N$ A6 ]
  7. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;           //LED0-->PB.5 端口配置) Y6 p" D  a# t% Z$ J0 j
  8. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;      //推挽输出
    ) T# _" X/ ?5 m& o) P" s. }
  9. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;     //IO口速度为50MHz
    7 n0 w+ N+ L0 R  f( f4 q: |
  10. GPIO_Init(GPIOB, &GPIO_InitStructure);              //根据设定参数初始化GPIOB.5
    ' c1 y1 G( V$ K1 ?3 p% `7 w1 ?: {
  11. BEEP=0;                                          //PB8 输出低,LED0灭
    & T( O6 M7 X& ~7 }) r  H6 M- {  w
  12. }
复制代码
' }" q" A( j4 \* F2 U. ?1 J% x7 ^* y
9)key.c文件代码分别如下:% B& w( U. x/ }5 I) Z

: p  S& J8 p& q6 v5 U
  1. #include "key.h"9 t% G7 {2 y$ Y9 X: i: q
  2. #include "sys.h"
    - {/ a: U9 ^6 R/ R4 ?% D" `. I3 K2 x
  3. #include "delay.h"
    : X& q5 R9 F" E( C  v0 f
  4. void KEY_Init(void) //IO初始化8 S5 L2 p7 Y. H7 n+ q6 I
  5. {
    / _- c6 O! u" n- [
  6. GPIO_InitTypeDef GPIO_InitStructure;
    ! K0 D3 g1 h7 v
  7. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOE,ENABLE);    //使能PA,PE端口时钟/ C3 \0 V$ `* o7 g; U  c6 v  d
  8. GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4;             //KEY0-KEY2
    5 d; s0 n* o, f( ^' Z5 q
  9. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;                                //设置成上拉输入
    6 H0 m- g# P. W: x) u" N
  10. GPIO_Init(GPIOE, &GPIO_InitStructure);                                       //初始化GPIOE2,3,4
    % m) p" w5 j  S( O* Z* b7 ?
  11. GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0;$ s8 ~0 ^- z, [4 N
  12. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;                                //PA0设置成输入,默认下拉   
    4 g4 k9 q4 X" ]% U4 w# H
  13. GPIO_Init(GPIOA, &GPIO_InitStructure);                                       //初始化GPIOA.0# r* x. i$ w' R( m( r9 X8 z
  14. }
    ( K: U6 n+ U" `& d( D& \( y* |
  15. u8 KEY_Scan(u8 mode)8 O* {9 x0 N- f" z: Q% b6 `3 [0 q
  16. {  
    9 q1 ^1 q0 i% D; l1 e7 E
  17. static u8 key_up=1;                                                          //按键按松开标志* V( p* G9 z* M6 q
  18. if(mode)key_up=1;                                                            //支持连按   
    + S6 h3 D+ L; r# e* [
  19. if(key_up&&(KEY0==0||KEY1==0||KEY2==0||WK_UP==1))
    5 h: T1 x, p; z1 ?
  20. {) d8 X/ ]9 w7 v% D9 J$ v) Q
  21.   delay_ms(10);                                                              //去抖动
    + L& W0 z7 q" A
  22.   key_up=0;- g: Z% Y) w7 ]$ e! T
  23.   if(KEY0==0)return 1;                                                       //KEY0按下,即PE4为低电平2 I2 P  }- W4 F
  24.   else if(KEY1==0)return 2;                                                  //KEY1按下,即PE3为低电平* V/ t3 z2 H  @. n
  25.   else if(KEY2==0)return 3;                                                  //KEY2按下,即PE2为低电平7 a, j% ?* C7 V8 a$ N
  26.   else if(WK_UP==1)return 4;                                                 //WK_UP按下,即PA0为高电平
    2 l. S, ^; y2 W* U# m" q3 n
  27. }else if(KEY0==1&&KEY1==1&&KEY2==1&&WK_UP==0)key_up=1;      
    4 u5 l3 O6 E8 [7 }- q. h$ `6 r
  28.   return 0;                                                                    // 无按键按下2 r( ~9 }6 m* J9 l: ]" K( r  V# g
  29. }
复制代码

% p8 H' W1 t" ^: e& X, q10)在MDK软件中,右键Target1,单击Manage Project Items…,在Project Items界面、Groups一栏,新建HARDWARE,并将led.c,beep.c和key.c三个.c文件添加进来;
+ l9 N- a9 i( [; s; L$ n6 n4 x9 X" n* i
11)在MDK软件中,单击魔术棒,在C/C++选项里,单击Include  y6 d# Y: |! J( S2 p/ v2 z2 t$ |9 B
Paths右边的…,添加led.h,beep.h和key.h三个头文件的目录(即HARDWARE);
% e4 B# |0 T. l5 A
. Q, n  |1 X5 h5 ?$ B12)新建main.c主函数,代码如下:
. ~1 r2 i" i: K5 i& h1 }) C9 u% y7 V  Z8 v
  1. #include "led.h"
    0 |! C9 Z3 r" W7 j- g& ?
  2. #include "beep.h"6 @" V' [6 B" L0 W) M
  3. #include "key.h"
    0 F/ I4 `0 t7 e( S
  4. #include "sys.h"
    % r! K6 k. @7 r# Y9 m6 s9 Y
  5. #include "delay.h"
    4 ?1 E3 Y6 d6 C
  6. int main(void)
    2 X% ]% C) X$ s' r& l5 }* d4 M
  7. {
    " M" e# l. U9 S$ |; Y
  8.    vu8 key=0;" j) @0 U1 {  d/ f6 J" Z
  9.   delay_init();
    + I5 N' h" f2 o
  10.     LED_Init();
    7 N+ X9 ?1 _1 z
  11.     BEEP_Init();, p* x, ~& l7 w& c" ~
  12.    KEY_Init();2 G* [8 L2 H* X. j3 R/ B
  13.     while(1)
    9 h& _* \: D0 S0 l
  14.   {. T3 U& @# w4 P
  15.    key=KEY_Scan(1);( p4 J* b* W( N* {
  16.       if(key)3 E/ f4 x2 B" P+ l
  17.     {        
    ! u; p4 I3 t/ L. H7 g  O
  18.        switch(key)+ i; I; P% ^/ {6 `; _" l& X
  19.      {     3 @* J6 I" _% j1 i' N+ H
  20.       case 4:
    - k" Z4 M, Z# A9 q+ T) H( E
  21.        BEEP=1;   //控制蜂鸣器叫   
    ' z$ N! D  F( ]7 S8 L# B
  22.        break;2 ^* `& K8 Q: j, |, G
  23.       case 3: , t; b# @$ D7 C- _, D
  24.         LED0=0;     //控制LED0亮      
    9 e" T- z+ Z. M& S1 X1 A
  25.        break; ) d/ R- \! ^8 G3 }5 F. T  n* l
  26.       case 2:
    6 c9 y: h+ E' S* V* l
  27.         LED1=0;     //控制LED1亮  
    8 ?& B" D5 D" V( h  T1 u
  28.        break;
    3 x/ `& N. Q1 W, W/ m& f
  29.       case 1:                              
    7 H/ a+ K9 Y* U( n
  30.         LED0=0;     //控制LED0亮 5 }7 d" L/ q! M; g- d
  31.         LED1=0;     //控制LED1亮 7 p9 t8 P) E4 T: Y
  32.        break;
    . f. O  V' a' ?* P! [
  33.      }
    ) z5 m; C7 x9 z% _. u8 U& ?, [4 Y$ C
  34.      delay_ms(300);  //延迟300ms
    0 ]/ Y# `, @8 }# J: v8 b
  35.      BEEP=0;         //控制蜂鸣器不叫9 S# B! u9 j' J) M7 `
  36.      LED0=1;         //控制LED0灭
      E: `! z0 m: T* y: |* @7 D
  37.      LED1=1;         //控制LED1灭; ]$ e5 F! Z* R% S
  38.    }else delay_ms(10);
    7 ~0 `$ N$ k; c4 x/ N1 y
  39.   }) w) m: U% m  [, a; O3 K" H: i- [
  40. }
复制代码

8 L0 M5 E4 J4 B& b, l13) 完成后即可运行看效果。; S+ q, p( A% N
————————————————3 I0 P8 U( x: i
版权声明:天亮继续睡
5 X" V6 X4 y( V% I
$ }) |. V2 |" [9 ~8 g
; a) K6 u, p4 D
赞 收藏 评论0 发布时间:2022-11-20 16:19

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版