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

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

[复制链接]
攻城狮Melo 发布时间:2023-3-29 19:15
STM32单片机GPIO口点亮流水灯: S0 e! W) q! I! @+ }! \( _
任何一个单片机,最简单的外设莫过于 IO 口的高低电平控制了,本文将通过一个经典的流水灯程序,了解 STM32F1 的 IO 口作为输出LED:DS0 和 DS1 交替闪烁,实现类似流水灯的效果。& g; v3 G2 o6 [9 B% C# c

% S. U$ F2 O+ zSTM32IO口简介, |5 C* c, i! y( y
本文将要实现的是控制ALIENTEK 精英 STM32开发板上的两个LED实现一个类似流水灯的效果,该实验的关键在于如何控制 STM32 的 IO 口输出。了解了 STM32 的 IO 口如何输出的,就可以实现流水灯了。
+ L2 U& F7 l7 d! H9 o# _" J1 CSTM32上有众多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 电平兼容的)。* ?, P- o; [) z% T* f  p6 f9 p9 Z
7 o: |. k/ K: r1 P1 C* A" U
20210519200849428.png
2 U) {0 I& j1 B

) Z" f3 S; m" M  T4 F/ g1 V# ZSTM32 的 IO 口相比 51 而言要复杂得多,所以使用起来也困难很多。首先 STM32 的 IO 口: c% R8 ^& A& ~
可以由软件配置成如下 8 种模式:
! V9 u; J  p) `' |  B1、输入浮空
9 M6 \: ~3 |' R. I" A7 C' o2、输入上拉  f* B) D. q# G4 N
3、输入下拉
# `- S; F* x" n9 O6 j# f4 ]( B* B" O4 D4、模拟输入) i$ Z) [5 T* |
5、开漏输出
6 O, _& O$ @4 O. [+ J8 N* b6、推挽输出
2 O/ z4 `+ ?& b$ d/ `+ s& F7、推挽式复用功能4 ]6 Z5 U  D% q2 I9 m3 \
8、开漏复用功能
+ b& z2 j7 b# w! |8 W
8 ^! k* F+ m" ?6 q) b6 U6 `9 a
20210519201425354.png
6 `( Z3 {$ d9 e- ^* D8 y  I! ~4 d- ?
STM32 的 CRL 控制着每组 IO 端口(A~G)的低 8 位的模式。每个 IO 端口的位占用 CRL 的 4 个位,高两位为 CNF,低两位为 MODE。这里我们可以记住几个常用的配置,比如 0X0 表示模拟输入模式(ADC 用)、0X3 表示推挽输出模式(做输出口用,50M 速率)、0X8 表示上/下拉输入模式(做输入口用)、0XB 表示复用输出(使用 IO 口的第二3 w" u! R$ ~1 E
功能,50M 速率)。CRH 的作用和 CRL 完全一样,只是 CRL 控制的是低 8 位输出口,而 CRH 控制的是高 8位输出口。$ }" G. z7 ?( {
这里我们点亮流水灯是使用库函数进行配置GPIO口的相关参数。
. V4 [9 R" @' [) n* D2 x  [! m0 e, V, n
5 W& n9 _, Z6 D: G
基于固件库函数配置GPIO口$ }; M" {9 h( o" F& z# W! {4 h2 ^
GPIO 相关的函数和定义分布在固件库文件 stm32f10x_gpio.c 和头文件 stm32f10x_gpio.h 文件中。
; P4 b7 M! E+ s. [$ W. Q0 s在固件库开发中,操作寄存器 CRH 和 CRL 来配置 IO 口的模式和速度是通过 GPIO 初始化函数完成:8 M! G2 y( d: i
  1. void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
    " E$ U; A/ H& F/ k
复制代码
* N+ x, n4 x3 I) T
这个函数有两个参数:
8 c, S. c3 L  \" d+ p# H- V- L第一个参数是用来指定 GPIO,取值范围为 GPIOA~GPIOG。; C8 i, ^5 ^- u: f( n% Z; l
第二个参数为初始化参数结构体指针,结构体类型为 GPIO_InitTypeDef。结构体的具体定义如下。
$ L3 r$ U& v% a
  1. typedef struct& u2 E9 F! ?5 H5 j
  2. { ( g* @# I: y$ c- r( _( f- w  R
  3.         int16_t GPIO_Pin; 8 k  z/ ]" a7 ?1 F! K
  4.         GPIOSpeed_TypeDef GPIO_Speed; ; Q: F3 h" F" e( j) b  ]/ K8 b
  5.         GPIOMode_TypeDef GPIO_Mode;
    - n3 g& h4 [' g; M* }
  6. }GPIO_InitTypeDef;
    4 e# v* R: g& m4 x; J( f: _+ L+ i, K& x
复制代码

. q6 _. ?, A. o5 b9 Q# j  n  ]% T那么如何具体配置一个IO口呢?
! o2 X( |6 L9 Q! h初始化结构体初始化 GPIO 的常用格式是:; j9 b7 J0 S6 T4 d
  1. GPIO_InitTypeDef GPIO_InitStructure;# ?/ d- @2 {$ t* n
  2. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //LED0-->PB.5 端口配置
    ; h& @" [; w, G7 V, a& g+ N: \3 L
  3. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
    . J- n  K; _2 q) H* Z- o
  4. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//速度 50MHz, W0 k+ N9 E, V  v% W! p
  5. GPIO_Init(GPIOB, &GPIO_InitStructure);//根据设定参数配置 GPIO
    9 n3 S2 |  T9 r0 _0 n
复制代码

; R! C  B: S6 g/ j/ i: v上面代码的意思是设置 GPIOB 的第 5 个端口为推挽输出模式,同时速度为 50M。从上面初始化代码可以看出,结构体 GPIO_InitStructure 的第一个成员变量 GPIO_Pin 用来设置是要初始化哪个或者哪些 IO 口;第二个成员变量 GPIO_Mode 是用来设置对应 IO 端口的输出输入模式,第三个参数是 IO 口速度设置,有三个可选值。GPIO_Mode和GPIO_Speed均是由枚举类型定义。具体定义分别为
. @0 t4 _! S  v' A8 t" Y. g  i1 `! j6 @+ P8 ^# A
GPIO_Mode:
' @; q6 X1 f* H1 ~4 A9 p
  1. typedef enum
    : Y8 a" S# {9 W1 s; u7 L) H. A
  2. { ; u1 i- s9 C- A4 R' {/ z
  3.         GPIO_Mode_AIN = 0x0, //模拟输入
    $ F& U0 H: Q$ B" ~; r$ @
  4.         GPIO_Mode_IN_FLOATING = 0x04, //浮空输入8 i+ F* g7 |9 s% c" {
  5.         GPIO_Mode_IPD = 0x28, //下拉输入( ~4 [/ n- ~( t# j3 R4 O  F1 [
  6.         GPIO_Mode_IPU = 0x48, //上拉输入4 R" V! K; @7 O
  7.         GPIO_Mode_Out_OD = 0x14, //开漏输出8 o. Z7 E; o& ~9 |9 L
  8.         GPIO_Mode_Out_PP = 0x10, //通用推挽输出4 c; j3 p0 k$ l  x
  9.         GPIO_Mode_AF_OD = 0x1C, //复用开漏输出
    % R/ L5 u, i  @6 F7 R; w
  10.         GPIO_Mode_AF_PP = 0x18 //复用推挽) |6 Z& @' {* v9 O% K1 F1 _
  11. }GPIOMode_TypeDef;) ~) ^) C4 \1 ]+ _# V3 U9 r1 W8 o. [
复制代码
. K$ l& J( @* }" g: f. f, y
GPIO_Speed:
1 A1 {1 t, t5 _: o
  1. typedef enum: o2 m; i9 ?, u5 Z+ ]
  2. {
      ?9 J! x9 ~/ {5 H4 G6 Q% z
  3. GPIO_Speed_10MHz = 1,
    + V  ^. a( A* }7 [. Z6 u: z, U  `
  4. GPIO_Speed_2MHz,
    / Z9 Y1 C5 d& {9 d: y' H
  5. GPIO_Speed_50MHz
    # q1 `4 y, y4 W  k2 }+ X
  6. }GPIOSpeed_TypeDef;
    : Y& k; j! \/ X; S" h" X
复制代码
* t% _) C( C8 o  n, X; {! J' y* w
这样我们就可以初始化好一个GPIO口。5 `* @+ q% _2 S" g! H1 M6 }9 v

. H2 H* C3 O3 n: w/ ~8 h  }2 B

; ^+ w+ ]0 S/ A$ c实现流水灯的基本代码
7 z* [$ R% _) t* \想要实现流水灯,要先了解控制GPIO口输出高低电平的函数。1 z" F, u8 n& o9 O+ u: \
* b9 W6 \6 R" z! W
& i& P  n5 G. |: m1 J
输出高电平
  1. GPIO_SetBits(GPIOB, GPIO_Pin_5);//PB.5输出高电平
    - ?8 |" J2 n/ }; l6 }" D; z
复制代码

9 `2 f" d9 r2 u6 f) t0 @& R6 ]输出低电平$ N8 h. Y  u/ Y% |6 Y
  1. GPIO_ResetBits(GPIOB, GPIO_Pin_5);//PB.5输出低电平
    5 F9 c, V  s7 Z( _# S
复制代码

6 _$ k# r" u+ T0 R- e: t. p使用IO口的基本操作为
' d3 W/ v% a  n; z: ?& K- P1) 使能 IO 口时钟。调用函数为 RCC_APB2PeriphClockCmd()。 2) 初始化 IO 参数。调用函数 GPIO_Init();
- q7 Q* W; B7 c3) 操作 IO。
: I. ^' ~! J% T' }$ m! i
4 D* B& O1 D6 e$ v
1 [. y, s4 {* A# A( n( t: V! G( Y
实操一波
2 F1 I4 i7 {8 ^( K先建立led.c和led.h文件; V5 Z6 V* a% z# y* m$ O0 X0 c
  1. #include "led.h"
    ) ~! F) p0 S5 j
  2. //初始化 PB5 和 PE5 为输出口.并使能这两个口的时钟 ! d! N8 [5 v4 q+ _( N* @
  3. //LED IO 初始化
    5 K2 C/ G2 T. v0 N3 }* `3 R! f
  4. void LED_Init(void)
    : _# O9 {( F* y0 p
  5. {
    ' ^( j4 _8 Q- c- l) u
  6.         GPIO_InitTypeDef GPIO_InitStructure;% u- B  K. v* d, U0 y3 J, s
  7.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|3 b% d1 d) V) E! O
  8.         RCC_APB2Periph_GPIOE, ENABLE); //使能 PB,PE 端口时钟
    . c6 C4 q/ k) p% }
  9.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //LED0-->PB.5 推挽输出
    9 j! m  R5 ?' @
  10.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出& a; f' ?' Z. ^- a/ I
  11.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;* o0 b; @: P. D7 h$ n4 A# c! u
  12.         GPIO_Init(GPIOB, &GPIO_InitStructure);
    & c8 |) q7 B$ e; ~/ H7 y
  13.         GPIO_SetBits(GPIOB,GPIO_Pin_5); //PB.5 输出高6 F( X, h9 P# z" Y
  14.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; //LED1-->PE.5 推挽输出8 W6 G; X4 i+ O0 v4 \! q" [
  15.         GPIO_Init(GPIOE, &GPIO_InitStructure);2 l! E/ ~8 h* p$ S  N- G# }
  16.         GPIO_SetBits(GPIOE,GPIO_Pin_5); /PE.5 输出高4 Z  M% Y+ t! \* o! x
  17. }
    ) }1 P% C0 ]& C3 i( c7 B5 u
复制代码
* h# V& x7 i9 Y# R- Z- N
在led.h文件里,可以使用如下宏定义/ A3 b$ E/ g  l
  1. #ifndef __LED_H% G8 q$ Z: \& H8 C! ?
  2. #define __LED_H. R) Q) q* ?' K
  3. #include "sys.h"
    $ m/ K+ _8 z$ c" A8 J. c! H, `
  4. //LED 端口定义
    " N, ?# r0 o  l8 b5 h. t7 `
  5. #define LED0 PBout(5)// DS0
    . t5 c' Z( w- v) x8 X& }3 x6 K8 O+ B
  6. #define LED1 PEout(5)// DS18 w4 A9 k* d3 n9 H3 U; l: Z
  7. void LED_Init(void);//初始化 # m3 ]1 w& K& O) v( e
  8. #endif
    5 Q; a- D/ s! ]& P
复制代码

4 t6 H1 s& g8 _* W这样就可以实现位带操作来实现操作某个 IO 口的 1 个位
9 a9 W3 W6 m, p) I' p
/ E. C4 @2 U$ M4 d1 F我们在main.c里写下主函数) F, z, S! U# ^7 F
  1. #include "led.h"
    0 l5 O0 H% ]5 M
  2. #include "delay.h"7 m) `+ P7 t) U4 ?2 d* i; _
  3. #include "sys.h"
    2 P! k1 v) a* k
  4. //ALIENTEK 精英 STM32 开发板实验 1
    % ?. B9 a5 v  Y6 g0 [
  5. //跑马灯实验 " z7 b( z$ a+ P: D* P; f: _9 x
  6. int main(void)
    8 T4 |2 u/ v. V# _
  7. {) S2 K5 L6 e% d8 |# Y+ H& `
  8.         delay_init(); //延时函数初始化 / l& P. r5 X: V3 ?5 g
  9.         LED_Init(); //初始化与 LED 连接的硬件接口/ V% c  }4 b0 Z
  10.         while(1)1 O# B: G& a/ [$ r" p9 x
  11.         {
    ( I6 W1 h- o9 D
  12.                 LED0=0; 5 g4 }: _& |6 j% ?9 K3 a. y
  13.                 LED1=1;+ h. T- k& V& f) t6 h
  14.                 delay_ms(300); //延时 300ms" ]% {7 @5 b/ P7 {
  15.                 LED0=1;
    3 c9 q9 I6 _9 @( |( L4 ^) E# E
  16.                 LED1=0;
    " B2 P; Q/ l! F
  17.                 delay_ms(300); //延时 300ms
    4 s$ J$ F- g8 N6 k( s
  18.         } % E+ d% E0 h: F1 b4 L
  19. }
    . T5 ]% z, F; y9 {2 l, |3 |1 j
复制代码

9 Q) o0 T1 R0 f如果没有使用宏定义,也可以使用输出电平函数进行设计
4 V5 j% `$ D) h& C8 ?
  1. GPIO_SetBits(GPIOB, GPIO_Pin_5); //设置 GPIOB.5 输出 1,等同 LED0=1;
    & H; C3 b+ p6 |+ V9 a3 H/ W
  2. GPIO_ResetBits(GPIOB, GPIO_Pin_5); //设置 GPIOB.5 输出 0,等同 LED0=0;
    8 F8 u+ [0 C7 T; [& G3 X2 Z! U
复制代码
5 i+ M) h' }) B* ~7 X
注:
+ K! O1 @$ ~! p9 Z( z由于stm32精英板的led灯只有两个,如果点亮外设的led灯,最好注意一下IO口的选择,许多IO口有多种功能,在选择IO口上可以考虑那些,未接任何外设,完全独立的IO口。
& _% X9 A  g# C9 s————————————————
$ P2 b+ Z- u$ ]6 j版权声明:Pessimist~
" ?! B$ `4 g  }( U* q. ]如有侵权请联系删除/ B" G& f# n6 z" W: Y

/ M! U& C) `4 m3 r$ \" K$ m6 h" m! x) E" h; p8 _+ E! Z0 }

7 v" v: P3 m& K$ H6 F2 m
赞 收藏 评论0 发布时间:2023-3-29 19:15

举报

0个回答

所属标签

相似技术帖

官网相关资源

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