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

如何点亮流水灯基于STM32单片机

[复制链接]
攻城狮Melo 发布时间:2023-3-29 19:15
STM32单片机GPIO口点亮流水灯
3 D# o: z/ C: d* y# |任何一个单片机,最简单的外设莫过于 IO 口的高低电平控制了,本文将通过一个经典的流水灯程序,了解 STM32F1 的 IO 口作为输出LED:DS0 和 DS1 交替闪烁,实现类似流水灯的效果。
) ?: G  a/ s+ K/ r+ L" p

  l) N5 w% m  H4 h  _* D8 O+ ^STM32IO口简介( {. {6 v3 i* V* W( R9 Y4 P* L
本文将要实现的是控制ALIENTEK 精英 STM32开发板上的两个LED实现一个类似流水灯的效果,该实验的关键在于如何控制 STM32 的 IO 口输出。了解了 STM32 的 IO 口如何输出的,就可以实现流水灯了。
3 `( i( S% d' WSTM32上有众多IO口,每个GPI/O端口有两个32位配置寄存器(GPIOx_CRL,GPIOx_CRH),两个32位数据寄存器(GPIOx_IDR和GPIOx_ODR),一个32位置位/复位寄存器(GPIOx_BSRR),一个16位复位寄存器(GPIOx_BRR)和一个32位锁定寄存器(GPIOx_LCKR)。每个 IO 口可以自由编程,但 IO 口寄存器必须要按 32 位字被访问。STM32 的很多 IO 口都是 5V 兼容的,这些 IO 口在与 5V 电平的外设连接的时候很有优势,具体哪些 IO 口是 5V 兼容的,可以从该芯片的数据手册管脚描述章节查到(I/O Level 标 FT 的就是 5V 电平兼容的)。
7 `4 N, k  n( q) T' A# n3 T5 Y
9 x- n) M/ S( Z; W7 G/ |
20210519200849428.png 8 Q( r+ ^& h; ^' Z

$ c5 f4 f. N% M5 {9 C% ]STM32 的 IO 口相比 51 而言要复杂得多,所以使用起来也困难很多。首先 STM32 的 IO 口
8 L7 `; v  ^4 P2 q' J8 C6 D( K可以由软件配置成如下 8 种模式:
2 ]- D- t* s5 a1 h: P1、输入浮空
% c% Q; {- T# h1 @3 R6 H2 n" ^9 |: L+ T2、输入上拉
* v& o( v) ~2 ?1 c; v5 N3、输入下拉0 d; c4 ?' I7 y
4、模拟输入
! w" D9 o; ^9 ^# y/ R5、开漏输出
( g4 }! a$ h' V6、推挽输出  ~/ v0 ]1 }+ V/ U0 v1 q
7、推挽式复用功能
- Y, E2 h/ N5 f" V  `3 ]8、开漏复用功能
- i# ?4 G) T& `% o4 n5 A$ q1 e
+ Q4 W( F8 l8 N2 R
20210519201425354.png 5 F7 e. o: D- }6 d" ~
' r- I7 s5 C7 g1 _2 z1 O
STM32 的 CRL 控制着每组 IO 端口(A~G)的低 8 位的模式。每个 IO 端口的位占用 CRL 的 4 个位,高两位为 CNF,低两位为 MODE。这里我们可以记住几个常用的配置,比如 0X0 表示模拟输入模式(ADC 用)、0X3 表示推挽输出模式(做输出口用,50M 速率)、0X8 表示上/下拉输入模式(做输入口用)、0XB 表示复用输出(使用 IO 口的第二% d/ T4 y" m/ n! B# k2 x1 m
功能,50M 速率)。CRH 的作用和 CRL 完全一样,只是 CRL 控制的是低 8 位输出口,而 CRH 控制的是高 8位输出口。! P7 x8 e7 P/ W. R! n) }
这里我们点亮流水灯是使用库函数进行配置GPIO口的相关参数。
4 d& F% d, L" _- Z& ]# }
" T- A2 F% K. V7 X* s- d
; _# ~5 S2 R  l
基于固件库函数配置GPIO口
6 }3 W. \/ y. t& k! u8 @GPIO 相关的函数和定义分布在固件库文件 stm32f10x_gpio.c 和头文件 stm32f10x_gpio.h 文件中。9 N$ U. V/ k/ l* R/ y* o
在固件库开发中,操作寄存器 CRH 和 CRL 来配置 IO 口的模式和速度是通过 GPIO 初始化函数完成:
1 ?7 q8 e4 k: X7 c
  1. void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);- Y8 w- @6 J% S. b' p; s% @
复制代码
! n7 a4 x. S  `0 {5 f6 ?( }
这个函数有两个参数:0 n/ z$ p$ I# }- i, X
第一个参数是用来指定 GPIO,取值范围为 GPIOA~GPIOG。( V/ V( V) x& u
第二个参数为初始化参数结构体指针,结构体类型为 GPIO_InitTypeDef。结构体的具体定义如下。
' H3 X3 k/ L1 t' A& O# V
  1. typedef struct
    ( G8 R5 X  _; h2 z+ L' ~- C% h5 \
  2. { 0 ]3 @* m/ X0 x  g: _! S$ S$ E
  3.         int16_t GPIO_Pin; ) D3 s8 `$ d; Y+ X; J' N) A5 J
  4.         GPIOSpeed_TypeDef GPIO_Speed;
    ' |3 Y9 V7 g$ A' t% o  O( L
  5.         GPIOMode_TypeDef GPIO_Mode; ; {* @) X! K; E# W1 ~3 ]: }+ ~. m
  6. }GPIO_InitTypeDef;
    ' d2 {2 q, e1 o% z. _3 I
复制代码
4 ~' N& a$ v/ q! w9 S5 y
那么如何具体配置一个IO口呢?) V8 c2 g5 g3 U( e7 d; S
初始化结构体初始化 GPIO 的常用格式是:4 S; q3 G, F! q+ M; h( E4 `
  1. GPIO_InitTypeDef GPIO_InitStructure;
    ! u) ^, `; o2 g! C
  2. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //LED0-->PB.5 端口配置6 p, o0 H. ?  q4 ]5 M
  3. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出1 P- F& T/ z* F( H% p, V
  4. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度 50MHz
    & n" `* f" {1 \
  5. GPIO_Init(GPIOB, &GPIO_InitStructure);//根据设定参数配置 GPIO8 Z0 M" o& Y* R# h. K
复制代码

9 P/ j$ W- f4 i5 B7 z  ^& z上面代码的意思是设置 GPIOB 的第 5 个端口为推挽输出模式,同时速度为 50M。从上面初始化代码可以看出,结构体 GPIO_InitStructure 的第一个成员变量 GPIO_Pin 用来设置是要初始化哪个或者哪些 IO 口;第二个成员变量 GPIO_Mode 是用来设置对应 IO 端口的输出输入模式,第三个参数是 IO 口速度设置,有三个可选值。GPIO_Mode和GPIO_Speed均是由枚举类型定义。具体定义分别为
% t2 ~5 W1 h# R, O! l; ]" e( h  p  `7 h, G) n, J4 h) q3 ~% X1 i
GPIO_Mode:$ R1 u* W6 ^: ~# G
  1. typedef enum
    ; m! M; i6 Z5 R8 W+ J, X8 h
  2. {
    ! P2 r& V9 ^1 t! ~/ ^8 x
  3.         GPIO_Mode_AIN = 0x0, //模拟输入; f1 I' ]1 d# o9 I
  4.         GPIO_Mode_IN_FLOATING = 0x04, //浮空输入' a* X. [6 D1 }' o! f
  5.         GPIO_Mode_IPD = 0x28, //下拉输入3 d/ |2 H7 }1 Q0 g
  6.         GPIO_Mode_IPU = 0x48, //上拉输入
    7 j- {1 O% n6 G+ E1 W
  7.         GPIO_Mode_Out_OD = 0x14, //开漏输出4 c9 J" V7 x9 a( k9 v1 ~
  8.         GPIO_Mode_Out_PP = 0x10, //通用推挽输出* r/ S& i) r& f0 F+ K
  9.         GPIO_Mode_AF_OD = 0x1C, //复用开漏输出
    / A$ L! ?4 R+ G: Z7 g9 T. }
  10.         GPIO_Mode_AF_PP = 0x18 //复用推挽2 ]' ]  v3 [% n+ g4 m
  11. }GPIOMode_TypeDef;5 k$ c% I) v" r' S
复制代码
9 }5 j7 }" C4 |+ `% |- ^8 {# ?6 K
GPIO_Speed:; P' v* Q( k% ^( G
  1. typedef enum+ _+ O* s+ V3 y, F
  2. { : b' E: Y/ m# ?7 ^# Q
  3. GPIO_Speed_10MHz = 1,
    ! e7 e& V7 [. k% h
  4. GPIO_Speed_2MHz, ' I& }9 D$ \' [( ^! `/ k! P
  5. GPIO_Speed_50MHz$ q3 Z# }2 K' Y2 {! P% U& t
  6. }GPIOSpeed_TypeDef;  T0 e) w2 R6 L
复制代码
/ a$ y' w/ r3 c3 P1 q. Q! e3 B7 P
这样我们就可以初始化好一个GPIO口。
  }* v3 K1 f& e+ P
+ z+ Y8 g" w1 K2 `: }
% x8 E7 p! b/ H1 a% }
实现流水灯的基本代码1 Q3 p. G) \9 D. q5 W* k
想要实现流水灯,要先了解控制GPIO口输出高低电平的函数。
; d) w/ T2 {* Y  x8 |& G7 }; v* [9 b! E% [

8 `& c7 I- o# e* C# q输出高电平
  1. GPIO_SetBits(GPIOB, GPIO_Pin_5);//PB.5输出高电平
    1 `. C" x0 b: j0 ^9 A, [
复制代码
/ x$ K/ I. x  G* E6 G3 @) r6 I
输出低电平
' e/ |( p; |: D- @+ m7 a% W4 x1 v
  1. GPIO_ResetBits(GPIOB, GPIO_Pin_5);//PB.5输出低电平, }& u6 @4 j9 M, j' ]1 b
复制代码
7 b" W' M* |: w' y
使用IO口的基本操作为
# F) Q* _4 {6 q3 H4 t5 u& C8 {1) 使能 IO 口时钟。调用函数为 RCC_APB2PeriphClockCmd()。 2) 初始化 IO 参数。调用函数 GPIO_Init();
: M+ [- q3 W! ~2 \2 _( W, o7 R1 n2 |3) 操作 IO。* N0 Q4 W4 T5 y* a1 O2 g

; B$ ?$ c2 A$ y9 a, N% d" a
# G/ X  m3 _7 D$ V4 ]" m
实操一波: Q$ M! h" _+ x' Y2 e
先建立led.c和led.h文件
  a8 L, |& n7 j2 M! @# i* D- D4 M
  1. #include "led.h"6 {6 W0 E) M) v* h5 r+ o- M4 g5 T
  2. //初始化 PB5 和 PE5 为输出口.并使能这两个口的时钟
    : Q1 h. R0 @, b: Z! s* N
  3. //LED IO 初始化
    % x2 _1 Y5 z: G7 z& \+ _8 D1 J
  4. void LED_Init(void)  B8 ]# {% o8 Z% G" \
  5. {, V/ N" |! H; }7 v% C3 Z
  6.         GPIO_InitTypeDef GPIO_InitStructure;
    , a- ]) m7 _6 j/ ]* m
  7.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|
    4 S$ ?4 d. z( E6 W. Z
  8.         RCC_APB2Periph_GPIOE, ENABLE); //使能 PB,PE 端口时钟
    3 l% u3 S5 ^) z
  9.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //LED0-->PB.5 推挽输出
    & t2 S) |2 {4 r# y% s0 Z8 G$ L
  10.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出3 v0 X6 R$ o6 c9 t$ k5 `5 F
  11.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;- A/ v# I3 G  I1 o
  12.         GPIO_Init(GPIOB, &GPIO_InitStructure);
    8 X, x7 f  F- a2 Y- ~
  13.         GPIO_SetBits(GPIOB,GPIO_Pin_5); //PB.5 输出高' D2 {5 j( v* R2 G3 C0 a# k# D8 B
  14.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //LED1-->PE.5 推挽输出8 ]* t; D) Z% W
  15.         GPIO_Init(GPIOE, &GPIO_InitStructure);; t3 {& I8 d' W
  16.         GPIO_SetBits(GPIOE,GPIO_Pin_5); /PE.5 输出高
    5 E. e7 R9 r) F4 R1 m; f
  17. }4 C8 Q: B6 ]5 ?2 p! z
复制代码
+ T$ }9 u. ]) N# [$ w9 M4 H
在led.h文件里,可以使用如下宏定义
2 L! U) V. \# Q4 E
  1. #ifndef __LED_H
    $ u0 E3 y4 d" q. l4 ?0 z& v
  2. #define __LED_H
    * ?: y3 j- f5 K$ s
  3. #include "sys.h"- d; S1 ?% h& g7 q8 ?
  4. //LED 端口定义
    ) |" ~3 Y4 h, k: i6 ^
  5. #define LED0 PBout(5)// DS08 V" f+ g9 [* m4 }$ \1 E
  6. #define LED1 PEout(5)// DS10 j* p& H4 ?: s- A
  7. void LED_Init(void);//初始化
    0 t  R! z! \- E/ ?0 i, C
  8. #endif
    + {) L; y. b: a
复制代码

- k3 @6 m: G4 h1 D5 t+ Y* e* R  W这样就可以实现位带操作来实现操作某个 IO 口的 1 个位
  q+ _4 L$ b4 Q/ U' I( U! `/ `1 c6 d' f0 c8 F) \
我们在main.c里写下主函数
3 u1 ]" u: C8 ?4 E, l7 V
  1. #include "led.h"! Q# {& o; k7 S- {& m+ I$ S$ m
  2. #include "delay.h"
    # M$ B  C. O* m6 N
  3. #include "sys.h"% y. [( ~- H& W
  4. //ALIENTEK 精英 STM32 开发板实验 1
    ; `/ }0 l5 W( B) M
  5. //跑马灯实验
    3 s! T1 [2 U. U7 @
  6. int main(void)
    - Y, t' D6 }8 o3 V9 b" N+ ]
  7. {$ d" g$ H( q# L
  8.         delay_init(); //延时函数初始化
    9 a( z- Z( r6 f7 |1 d* ~4 m6 r
  9.         LED_Init(); //初始化与 LED 连接的硬件接口7 V) o6 D  T' S, i- x. c, U
  10.         while(1)5 J9 U1 t/ j6 u, L4 L7 q
  11.         { 4 j% b) ~5 p3 w+ O0 K2 ?" D
  12.                 LED0=0; 7 K' c  t! b: _1 C) H# I
  13.                 LED1=1;7 k+ y( p- ~- y, u) a# y
  14.                 delay_ms(300); //延时 300ms. h9 C# _* ~' D* J
  15.                 LED0=1;
    + H, M* k9 c1 s0 }& H+ \6 G
  16.                 LED1=0;6 X  ?! M7 S" R' y5 I/ G) \  B
  17.                 delay_ms(300); //延时 300ms) u  a$ F8 R5 D. |- {
  18.         } : v3 Q8 B2 O& x* t; G' L& V
  19. }
    . l1 }9 G: D( O2 H/ L/ P9 `
复制代码

' R7 y; j6 |; o4 D4 l! i  J如果没有使用宏定义,也可以使用输出电平函数进行设计8 K5 d; R. N: m2 [( z" p
  1. GPIO_SetBits(GPIOB, GPIO_Pin_5); //设置 GPIOB.5 输出 1,等同 LED0=1;! e& t* q3 a. Q  e" G( O
  2. GPIO_ResetBits(GPIOB, GPIO_Pin_5); //设置 GPIOB.5 输出 0,等同 LED0=0;+ I" H9 {/ q( v1 h: K. X, \( p1 A
复制代码
+ n5 {: s# D/ F
注:9 z- n$ A: `" B8 K; o+ Y
由于stm32精英板的led灯只有两个,如果点亮外设的led灯,最好注意一下IO口的选择,许多IO口有多种功能,在选择IO口上可以考虑那些,未接任何外设,完全独立的IO口。* y4 {' v& D& [7 A7 b$ C0 s" k
————————————————9 O* ?" O* H* J1 ~4 r3 l
版权声明:Pessimist~
& C. z9 G: W) k1 O. @如有侵权请联系删除
  L  N% S* f; p9 `
' t9 \  j3 F. w- y8 B# u* K, x" \$ m& c7 C+ c( h6 ~" X
$ W, R% ^( t4 g) R! D. G6 z
赞 收藏 评论0 发布时间:2023-3-29 19:15

举报

0个回答

所属标签

相似技术帖

官网相关资源

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