龙空技术网

「正点原子NANO STM32F103开发板资料连载」第六章 跑马灯实验

正点原子日常 46

前言:

而今我们对“net走马灯远吗”大概比较注重,同学们都想要了解一些“net走马灯远吗”的相关文章。那么小编在网络上网罗了一些关于“net走马灯远吗””的相关文章,希望兄弟们能喜欢,看官们一起来了解一下吧!

1)实验平台:【正点原子】 NANO STM32F103 开发板

2)摘自《正点原子STM32 F1 开发指南(NANO 板-HAL 库版)》关注官方微信号公众号,获取更多资料:正点原子

第六章 跑马灯实验

STM32 最简单的外设莫过于 IO 口的高低电平控制了,本章将通过一个经典的跑马灯程序,带大家开启 STM32F1 之旅,通过本章的学习,你将了解到 STM32F1 的 IO 口作为输出使用的方法。在本章中,我们将通过代码控制 ALIENTEK NANO STM32F103 开发板上的两个 LED:DS0 和 DS1 交替闪烁,实现类似跑马灯的效果。 本章分为如下四个小节:

6.1,STM32F1 IO 口简介

6.2,硬件设计

6.3,软件设计

6.4,仿真与下载

6.5 STM32CubeMX 配置 IO 口

6.1 STM32 IO 简介

本章将要实现的是控制ALIENTEK NANO STM32开发板上的两个 LED 实现一个类似跑马灯的效果,该实验的关键在于如何控制 STM32 的 IO 口输出。了解了 STM32 的 IO 口如何输出的,就可以实现跑马灯了。通过这一章的学习,你将初步掌握 STM32 基本 IO 口的使用,而这是迈向 STM32 的第一步。这一章节因为是第一个实验章节,所以我们在这一章将讲解一些知识为后面的实验做铺垫。为了小节标号与后面实验章节一样,这里我们不另起一节来讲。在讲解 STM32F1 的 GPIO 之前,首先打开我们光盘的第一个 HAL 库版本实验工程跑马灯实验工程(光盘目录为:“4,程序源码\标准例程-HAL 库函数版本\实验 1 跑马灯/USER/LED.uvprojx”),可以看到我们的实验工程目录:

图 6.1.1 跑马灯实验目录结构

接下来我们逐一讲解一下我们的工程目录下面的组以及重要文件。

① 组HALLIB下面存放的是ST官方提供的HAL库文件,每一个源文件stm32f4xx_hal_ppp.c

都对应一个头文件 stm32f1xx_hal_ppp.h。分组内的源文件我们可以根据工程需要添加和

删除。这里对于跑马灯实验,我们需要添加 10 个源文件。

② 组 CORE 下面存放的是固件库必须的核心头文件和启动文件。这里面的文件用户不需要

修改。大家可以根据自己的芯片型号选择对应的启动文件。

③ 组 SYSTEM 是 ALIENTEK 提供的共用代码,这些代码在第五章都有详细讲解。

④ 组 HARDWARE 下面存放的是每个实验的外设驱动代码,他的实现是通过调用 HALLIB

下面的 HAL 库文件函数实现的,比如 led.c 中函数调用 stm32f1xx_hal_gpio.c 内定义的函

数对 led 进行初始化,这里面的函数是讲解的重点。后面的实验中可以看到会引入多个

源文件。

⑤ 组 USER 下面存放的主要是用户代码。但是 system_stm32f1xx.c 文件用户不需要修改,

同时 stm32f1xx_it.c 里面存放的是中断服务函数,这两个文件的作用在 3.3 节有讲解。

main.c 函数主要存放的是主函数了。

工程分组的情况我们就讲解到这里,接下来我们就要进入我们跑马灯实验的讲解部分了。

这里需要说明一下,我们在讲解 HAL 库之前会首先对重要寄存器进行一个讲解,这样是为了

大家对寄存器有个初步的了解。大家学习 HAL 库,并不需要记住每个寄存器的作用,而只是

通过了解寄存器来对外设一些功能有基本的了解,这样对以后的学习也很有帮助。

STM32 的 IO 口相比 51 而言要复杂得多,所以使用起来也困难很多。首先 STM32 的 IO 口

可以由软件配置成如下 8 种模式:

1、输入浮空

2、输入上拉

3、输入下拉

4、模拟输入

5、开漏输出

6、推挽输出

7、推挽式复用功能

8、开漏复用功能

每个 IO 口可以自由编程,但 IO 口寄存器必须要按 32 位字被访问。STM32 的很多 IO 口都

是 5V 兼容的,这些 IO 口在与 5V 电平的外设连接的时候很有优势,具体哪些 IO 口是 5V 兼容

的,可以从该芯片的数据手册管脚描述章节查到(I/O Level 标 FT 的就是 5V 电平兼容的)。

STM32 的每个 IO 端口都有 7 个寄存器来控制。他们分别是:配置模式的 2 个 32 位的端口

配置寄存器 CRL 和 CRH;2 个 32 位的数据寄存器 IDR 和 ODR;1 个 32 位的置位/复位寄存器

BSRR;一个 16 位的复位寄存器 BRR;1 个 32 位的锁存寄存器 LCKR。大家如果想要了解每个

寄存器的详细使用方法,可以参考《STM32 中文参考手册 V10》P105~P129。

CRL 和 CRH 控制着每个 IO 口的模式及输出速率。

STM32 的 IO 口位配置表如表 6.1.4 所示:

表 6.1.4 STM32 的 IO 口位配置表

STM32 输出模式配置如表 6.1.5 所示:

表 6.1.5 STM32 输出模式配置表

接下来我们看看端口低配置寄存器 CRL 的描述,如图 6.1.6 所示:

图 6.1.6 端口低配置寄存器 CRL 各位描述

该寄存器的复位值为 0X4444 4444,从图 6.1.4 可以看到,复位值其实就是配置端口为浮空

输入模式。从上图还可以得出:STM32 的 CRL 控制着每组 IO 端口(A~G)的低 8 位的模式。

每个 IO 端口的位占用 CRL 的 4 个位,高两位为 CNF,低两位为 MODE。这里我们可以记住几

个常用的配置,比如 0X0 表示模拟输入模式(ADC 用)、0X3 表示推挽输出模式(做输出口

用,50M 速率)、0X8 表示上/下拉输入模式(做输入口用)、0XB 表示复用输出(使用 IO 口

的第二功能,50M 速率)。

CRH 的作用和 CRL 完全一样,只是 CRL 控制的是低 8 位输出口,而 CRH 控制的是高 8

位输出口。这里我们对 CRH 就不做详细介绍了。下面我们讲解一下怎样通过固件库设置 GPIO

的相关参数和输出。

GPIO 相 关 的 函 数 和 定 义 分 布 在 HAL 库文件 stm32f1xx_hal_gpio.c 和 头 文 件

stm32f1xx_ha_gpio.h 文件中。

在 HAL 库中,操作寄存器 CRH 和 CRL 来配置 IO 口的模式和速度是通过 GPIO 初始化函

数完成:

void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init);

这个函数有两个参数,第一个参数是用来指定需要初始化的 GPIO 对应的 GPIO 组,取值

范围为 GPIOA~GPIOE(注意:这里是根据芯片类型包含的头文件而取的值范围,我们是包含

stm32f103xb.h 头文件 ) 。第二个参数为初始化参数结构体指针,结构体类型为

GPIO_InitTypeDef。下面我们看看这个结构体的定义。首先我们打开我们光盘的跑马灯实验,

然后找到 HALLIB 组下面的 stm32f1xx_hal_gpio.c 文件,定位到 HAL_GPIO_Init 函数体处,双

击入口参数类型 GPIO_InitTypeDef 后右键选择“Go to definition of …”可以查看结构体的定

义:

typedef struct

{

uint32_t Pin; //指定 IO 口

uint32_t Mode; //模式设置

uint32_t Pull; //上下拉设置

uint32_t Speed; //速度设置

}GPIO_InitTypeDef;

结构体有 4 个成员变量,关于怎么来确定 4 个成员变量的取值范围,请参考 4.7 小节内容。

下面我们通过一个 GPIO 初始化实例来讲解这个结构体的成员变量的含义。

初始化 GPIO 的常用格式是:

GPIO_InitTypeDef GPIO_Initure;

GPIO_Initure.Pin=GPIO_PIN_0; //PC0

GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出

GPIO_Initure.Pull=GPIO_PULLUP; //上拉

GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速

HAL_GPIO_Init(GPIOC,&GPIO_Initure);

上面代码的意思是设置 PC0 端口为推挽输出模式,输出速度为高速,上拉。

从上面初始化代码可以看出,结构体 GPIO_Initure 的第一个成员变量 GPIO_Pin 用来设置

是要初始化哪个或者哪些 IO 口;第二个成员变量 GPIO_Mode 是用来设置对应 IO 端口的输出

输入端口模式,这些模式是上面我们讲解的 8 个模式,第三个成员变量 Pull 是用来设置上拉还

是下拉。第四个成员变量 Speed 用来设置输出速度。

这些入口参数的取值范围怎么定位,怎么快速定位到这些入口参数取值范围的枚举类型,

在我们上面章节 4.7 的“快速组织代码”章节有讲解,不明白的朋友可以翻回去看一下,这里

我们就不重复讲解,在后面的实验中,我们也不再去重复讲解怎么定位每个参数的取值范围的

方法。

看完了 GPIO 的参数配置寄存器,接下来我们看看 GPIO 输入输出电平控制相关的寄存器

首先我们看 ODR 寄存器,该寄存器用于控制 GPIOx 输出电平,该寄存器各位描述见表 6.1.6

所示:

表 6.1.6 GPIOx ODR 寄存器各位描述

该寄存器用于设置某个 IO 输出低电平(ODRy=0)还是高电平(ODRy=1),该寄存器也

仅在输出模式下有效,在输入模式(MODEyp[1:0]=00 时)下不起作用。该寄存器在 HAL 库使

用不多,操作这个寄存器的库函数主要是 HAL_GPIO_TogglePin 函数:

void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

该函数是通过操作 ODR 寄存器,达到取反 IO 口输出电平的功能。

接下来我们看看另一个非常重要的寄存器 BSSR,它叫置位/复位寄存器。该寄存器和 ODR

寄存器具有类似的作用,都可以用来设置 GPIO 端口的输出是 1 还是 0。寄存器描述如下:

表 6.1.7 BSRR 寄存器各位描述

对于低 16 位(0-15),我们往相应的位写 1,那么对应的 IO 口会输出高电平,往相应的位写

0 对 IO 口没有任何影响。高 16 位(16-31)作用刚好相反,对相应的位写 1 会输出低电平,写

0 没有任何影响。也就是说,对弈 BSRR 寄存器,你写 0 的话,对 IO 口电平是没有任何影响的。

我们要设置某个 IO 口电平,只需要相关为设置 1 即可。而 ODR 寄存器,我们要设置某个 IO

口电平,我们首先需要读出来 ODD 寄存器的值,然后对整个 ODR 寄存器重新赋值来达到设置

某个或者某些 IO 口的目的,而 BSRR 寄存器,我们不需要先读,而是直接设置即可,这在多

任务实时操作系统中作用很大。

BSSR 寄存器使用方法如下:

GPIOA->BSRR=1<<1;//设置 GPIOA.1 为高电平GPIOA->BSRR=1<<(16+1);//设置 GPIOA.1 为低电平;

库函数操作 BSSR 寄存器来设置 IO 电平的函数为:

void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin,

GPIO_PinState PinState);

该函数用来设置一组 IO 口中的一个或者多个 IO 口的电平状态。比如我们要设置 GPIOC.0

输出高,方法为:

HAL_GPIO_WritePin(GPIOC, GPIO_PIN_5,GPIO_PIN_SET);//GPIOC.0 输出高

设置 GPIOC.0 输出低电平,方法为:

HAL_GPIO_WritePin(GPIOC, GPIO_PIN_5,GPIO_PIN_RESET);//GPIOC.0 输出低

接下来我们看看 IDR 寄存器,该寄存器用于读取 GPIOx 的输入数据,该寄存器各位描述

见表 6.1.8 所示:

图 6.1.8 GPIOx IDR 寄存器各位描述

该寄存器用于读取某个 IO 的电平,如果对应的位为 0(IRRy=0),则说明该 IO 输入的是低

电平,如果是 1(IDRy=1),则表示输入的是高电平。HAL 库操作该寄存器读取 IO 输入数据相

关函数:

GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

该函数用来读取一组 IO 下一个或者多个 IO 口电平状态。比如我们要读取 GPIOC.0 的输入电平,

方法为:

HAL_GPIO_ReadPin(GPIOC,GPIO_PIN_0);//读取 PC0 的输入电平

该函数返回值就是 IO 口电平状态。

GPIO 相关的函数我们先讲解到这里。虽然 IO 操作步骤很简单,这里我们还是做个概括性

的总结,操作步骤为:

1)使能 IO 口时钟,调用函数为_HAL_RCC_GPIOX_CLK_ENABLE(其中 X=A~E)。

2)初始化 IO 参数,调用函数 HAL_GPIO_Init();

3)操作 IO 输入输出,操作 IO 的方法就是上面我们讲解的方法。

上面我们讲解了 STM32F1 IO 口的基本知识以及 HAL 操作 GPIO 的一些函数方法,下面我

们来讲解我们的跑马灯实验的硬件和软件设计。

6.2 硬件设计

本章用到的硬件只有 LED(DS0 和 DS1)。其电路在 ALIENTEK NANO STM32F103 开发

板上默认是已经连接好了的。DS0 接 PC0,DS1 接 PC1。所以在硬件上不需要动任何东西。其

连接原理图如图 6.2.1 下:

图 6.2.1 LED 与 STM32F1 连接原理图

6.3 软件设计

这是我们学习的一个实验,所以我会手把手教大家怎么从我们前面讲解的 Template 工程模

板一步一步加入 HAL 库以及 led 相关的驱动函数到我们工程,使之跟我们光盘的跑马灯实验工

程一模一样。首先大家打开我们 3.3 小节新建的 HAL 库工程模板。如果你还没有新建,也可以

直接打开我们光盘已经新建好了的工程模板,路径为:“\4,程序源码\标准例程-HAL 库函数

版本\实验 0-1 Template 工程模板-新建工程章节使用”(注意:是直接点击工程下面的 USER

目录下面的 Template.uvprojx)。

大家可以看到,我们模板里面的 HALLIB 分组下面,我们引入了所有的 HAL 库源文件和

对应头文件,如下图 6.3.1:

图 6.3.1 Template 模板工程结构

实际上,这些大家可以根据工程需要添加,比如跑马灯实验并没有用到 ADC,我们可以在

工程中删除文件 stm32f1xx_hal_adc.c,这样可以大大减少工程编译时间。跑马灯实验我们一共

使用到 HAL 库 10 个源文件,具体那 10 个请直接参考我们跑马灯实验工程,其他不用的源文

件大家可以直接在工程中删除。在工程的 Manage Project Items 页面,选择要删除文件所在的分

组,然后选中文件点击删除按钮即可。具体操作方法如下图 6.3.2 所示:

图 6.3.2 删除工程分组中的文件

接下来我们进入我们工程的目录,在工程根目录文件夹下面新建一个 HARDWARE 的文

件夹,用来存储以后与硬件相关的代码。然后在 HARDWARE 文件夹新建一个 LED 文件夹,

用来存放与 LED 相关的代码。如图 6.3.3 所示:

图 6.3.3 新建 HARDWARE 文件夹

接下来,我们回到我们的工程(如果是使用的上面新建的工程模板,那么就是

Template.uvproj,大家可以将其重命名为 LED.uvproj),按

按钮新建一个文件,然后按

保存在 HARDWARE->LED 文件夹下面,保存为 led.c,操作步骤如下图 6.3.4 和 6.3.5:

图 6.3.4 新建文件

图 6.3.5 保存 led.c

然后在 led.c 文件中输入如下代码(代码大家可以直接打开我们光盘的实验 1 跑马灯实验,

从 led.c 文件内复制过来),输入后保存即可:

//LED IO 初始化

void LED_Init(void)

{

GPIO_InitTypeDef GPIO_Initure;

__HAL_RCC_GPIOC_CLK_ENABLE(); //开启 GPIOC 时钟

GPIO_Initure.Pin=GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3|

GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7;//PC0~7

GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP;//推挽输出

GPIO_Initure.Pull=GPIO_PULLUP; //上拉

GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速

HAL_GPIO_WritePin(GPIOC,GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3|

GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7,

GPIO_PIN_SET); //PC0~7 置 1,默认初始化后灯灭

HAL_GPIO_Init(GPIOC,&GPIO_Initure);

}

该代码里面就包含了一个函数 void LED_Init(void),该函数通过调用 HAL_GPIO_Init 实验

配置 PC0~PC7 为推挽输出。关于函数 HAL_GPIO_Init 的使用方法在 6.1 小节有详细讲解。这

里需要注意的是:在配置 STM32 外设的时候,任何时候都要先使能该外设的时钟。使能 GPIOC

时钟方法为:

__HAL_RCC_GPIOC_CLK_ENABLE(); //使能 GPIOC 时钟

在设置完时钟以后,先调用 HAL_GPIO_WritePin 控制 LED0~LED7 输出 1(LED 全灭),然后

调用 HAL_GPIO_Init 函数完成对 PC0~PC7 的初始化配置。至此,LED 的初始化完毕。这样就

完成了对这八个 IO 口的初始化。这段代码的具体含义,大家可以看前面 6.1 小节,我们有详细

的讲解。

保存 led.c 代码,然后我们按同样的方法,新建一个 led.h 文件,也保存在 LED 文件夹下面。

在 led.h 中输入如下代码:

#ifndef _LED_H

#define _LED_H

#include "sys.h"

#define LED0 PCout(0) //LED0

#define LED1 PCout(1) //LED1

#define LED2 PCout(2) //LED2

#define LED3 PCout(3) //LED3

#define LED4 PCout(4) //LED4

#define LED5 PCout(5) //LED5

#define LED6 PCout(6) //LED6

#define LED7 PCout(7) //LED7

void LED_Init(void);

#endif

这段代码里面最关键就是 8 个宏定义:

#define LED0 PCout(0) //LED0

#define LED1 PCout(1) //LED1

#define LED2 PCout(2) //LED2

#define LED3 PCout(3) //LED3

#define LED4 PCout(4) //LED4

#define LED5 PCout(5) //LED5

#define LED6 PCout(6) //LED6

#define LED7 PCout(7) //LED7

这里使用的是位带操作来实现操作某个 IO 口,关于位带操作前面第五章 5.2.1 已经有详细

介绍,这里不再多说。需要说明的是,这里同样可以使用 HAL 库操作来实现 IO 口操作。如下:

HAL_GPIO_WritePin(GPIOC,GPIO_Pin_0,GPIO_PIN_SET);//PC0=1,等同 LED0=1;

HAL_GPIO_ReadPin(GPIOC,GPIO_Pin_0); //读取 PC0 的输入电平

有兴趣的朋友不妨修改我们的位带操作为库函数直接操作,这样也有利于学习。

将 led.h 也保存一下。接着,我们在 Manage Project Itms 管理界面新建一个 HARDWARE

的组,并把 led.c 加入到这个组里面,如图 6.3.6 所示:

图 6.3.6 给工程新增 HARDWARE 组

单击 OK,回到工程,然后你会发现在 Project Workspace 里面多了一个 HARDWARE 组,

在该组下面有一个 led.c 文件。如图 6.3.7 所示:

图 6.3.7 工程主界面

然后用之前介绍的方法(在 3.3 节介绍的)将 led.h 头文件的路径加入到工程里面,然后点

击 OK 回到主界面,如下图 6.3.8 所示:

图 6.3.8 添加 LED 目录到 PATH

回到主界面后,修改 main.c 文件内容如下(具体内容请参考跑马灯实验 main.c 文件):

#include "sys.h"

#include "delay.h"

#include "usart.h"

#include "led.h"

int main(void)

{

HAL_Init(); //初始化 HAL 库

Stm32_Clock_Init(RCC_PLL_MUL9); //设置时钟,72M

delay_init(72); //初始化延时函数

LED_Init(); //初始化 LED

while(1)

{

HAL_GPIO_WritePin(GPIOC,GPIO_PIN_0,GPIO_PIN_RESET);//LED0 亮

HAL_GPIO_WritePin(GPIOC,GPIO_PIN_1,GPIO_PIN_SET);//LED1 灭

delay_ms(500); //延时 500ms

HAL_GPIO_WritePin(GPIOC,GPIO_PIN_0,GPIO_PIN_SET);//LED0 灭

HAL_GPIO_WritePin(GPIOC,GPIO_PIN_1,GPIO_PIN_RESET);//LED1 亮

delay_ms(500);

//延时 500ms

}

}

代码包含了#include“led.h”这句,使得 LED0、LED1、LED_Init 等能在 main()函数里被

调用。main()函数非常简单,先调用 HAL_Init 函数初始化 HAL 库,然后调用 Stm32_Clock_Init

进行时钟系统配置,然后调用 delay_init()函数进行延时初始化。接着就是调用 LED_Init()来初

始化 PC0~PC7 为推挽输出模式,最后在 while 死循环里面实现 LED0 和 LED1 交替闪烁,间隔

为 500ms。

上面是通过库函数来实现的 IO 操作,我们也可以修改 main()函数,直接通过位带操作达到

同样的效果,大家不妨试试,位带操作的代码如下:

int main(void)

{

HAL_Init(); //初始化 HAL 库

Stm32_Clock_Init(RCC_PLL_MUL9); //设置时钟,72M

delay_init(72);

//初始化延时函数

LED_Init();

//初始化 LED

while(1)

{

LED0=0;

//LED0 亮

LED1=1;

//LED1 灭

delay_ms(500);

LED0=1;

//LED0 灭

LED1=0;

//LED1 亮

delay_ms(500);

}

}

当然我们也可以通过直接操作相关寄存器的方法来设置 IO,我们只需要将主函数修改如下

内容:

int main(void)

{

HAL_Init(); //初始化 HAL 库

Stm32_Clock_Init(RCC_PLL_MUL9); //设置时钟,72M

delay_init(72);

//初始化延时函数

LED_Init();

//初始化 LED

while(1)

{

GPIOC->BSRR=GPIO_PIN_0<<16; //LED0 亮

GPIOC->BSRR=GPIO_PIN_1; //LED1 灭

delay_ms(500);

GPIOC->BSRR=GPIO_PIN_0; //LED0 灭

GPIOC->BSRR=GPIO_PIN_1<<16; //LED1 亮

delay_ms(500);

}

}

将主函数替换为上面代码,然后重新执行,可以看到,结果跟库函数操作和位带操作一样

的效果。大家可以对比一下。这个代码在我们跑马灯实验的 main 文件有注释,大家可以替换试

试。

然后按

,编译工程,得到的结果如图 6.3.9 所示:

图 6.3.9 编译结果

可以看到没有错误,也没有警告。从编译信息可以看到,我们的代码占用 FLASH 大小为:

5256 字节(4964+478),所用的 SRAM 大小为 1920(1900+20)。

这里我们解释一下,编译结果里面的几个数据的意义:

Code:表示程序所占用 FLASH 的大小(FLASH)。

RO-data:即 Read Only-data,表示程序定义的常量(FLASH)。

RW-data:即 Read Write-date,表示已被初始化的变量(SRAM)。

ZI-data:即 Zero Init-data,表示未被初始化的变量(SRAM)

有了这个就可知道你当前使用的 flash 和 sram 大小了,所以,一定要注意的是程序大小不

是.hex 文件的大小,而是编译后的 Code 和 RO-data 之和。

接下来,我们就先进行软件仿真,验证一下是否有错误的地方,然后下载到 NANO STM32

开发板看看实际运行的结果。

6.4 仿真与下载

此代码,我们先进行软件仿真,看看结果对不对,根据软件仿真的结果,然后再下载到

ALIENTEK NANO STM32 板子上面看运行是否正确。

首先,我们进行软件仿真(请先确保 Options for Target→ Debug 选项卡里面已经设置为 Use

Simulator,参考 3.4.1 小节)。先按

开始仿真,接着按

,显示逻辑分析窗口,点击 Setup,

新建两个信号 PORTC.0 和 PORTC.1,如图 6.4.1 所示:

图 6.4.1 逻辑分析设置

Display Type 选择 bit,然后单击 Close 关闭该对话框,可以看到逻辑分析窗口出来了两个

信号,如图 6.4.2 所示:

图 6.4.2 设置后的逻辑分析窗口

接着,点击开始运行。运行一段时间之后,按按钮,暂停仿真回到逻辑分析窗口,可以看到如图 6.4.3 所示的波形:

图 6.4.3 仿真波形

这里注意 Gird 要调节到 0.2s 左右比较合适,可以通过 Zoom 里面的 In 按钮来放大波形,

通过 Out 按钮来缩小波形,或者按 All 显示全部波形。从上图中可以看到 PORTC.0 和 PORTC.1

交替输出,周期可以通过中间那根红线来测量。至此,我们的软件仿真已经顺利通过。

在软件仿真没有问题了之后,我们就可以把代码下载到开发板上,看看运行结果是否与我

们仿真的一致。运行结果如图 6.4.4 所示:

图 6.4.4 执行结果

在工程中我们另外添加了实现流水灯代码,DS0~DS7 轮流点亮,只需将代码屏蔽去掉,重

新编译一下下载到开发板就可以看到实现效果了,这里我们就不作贴图了。

至此,我们的第一章的学习就结束了,本章作为 STM32 的入门第一个例子,详细介绍了

STM32 的 IO 口操作,同时巩固了前面的学习,并进一步介绍了 MDK 的软件仿真功能。希望

大家好好理解一下。

6.5 STM32CubuMX 配置 IO 口

在讲解完使用 HAL 库操作 GPIO 口之后,本小节我们教大家怎么使用 STM32CubeMX 图

形化配置工具配置 GPIO 初始化过程。关于 STM32CubeMX 工具的入门使用在前面 4.8 小节我

们有手把手教大家入门该工具,本小节我们就不重复讲解入门部分,我们直接讲解在

STM3232CubeMX 工具中怎么来配置 GPIO 口的相关参数。

首先大家打开 STM32CubeMX 工具,参考 4.8 小节内容进行 RCC 相关配置。这里大家也

可以直接打开 4.8 小节的 STM32CubeMX 工程直接在工程上面修改,该工程保存的光盘目录为:

“4,程序源码\标准例程-HAL 库函数版本\实验 0-3 Template 工程模板-使用 STM32CubeMX 配

置”。

这里大家会发现,我们在 4.8 小节实际上已经讲解了 GPIO 的配置,并且同样是以 PC0 和

PC1 为例。这里我们将详细解析在 STM32CubeMX 中配置 IO 口详细参数的过程。使用

STM32CubeMX 配置 GPIO 口的步骤如下:

第一步,打开 STM32CubeMX 工具,在引脚图中选择要配置的 IO 口。这里我们选择 PC0

为例,在弹出的下拉菜单中选择要配置的 IO 口模式,如下图 6.5.1 所示:

图 6.5.1 选择 IO 口模式

从上图可以看出,这里我们除了配置 IO 口为输入输出之外,还可以选择 IO 口的复用功能

或者作为外部中断引脚功能,比如我们要选择 IO 口复用为 ADC1 的通道 10 引脚,那么我们只

需要选选项 ADC1_IN10 即可。对于本章跑马灯实验,PC0 是作为输出,所以我们选 GPIO_Output

即可。

第二步,进入 Configuration->GPIO,在弹出的界面配置 IO 口的详细参数。如下图 6.5.2 和

6.5.3 所示:

图 6.5.2 进入 GPIO 详细参数配置界面

图 6.5.3 配置 IO 口详细参数

在 IO 口详细参数配置界面,点击我们要配置的 IO 口,会在窗口下方显示该 IO 口配置的

详细参数表,下面我们依次来解释这些配置项的含义:

① 选项 GPIO output level 用来设置 IO 口初始化电平状态为 High(高电平)还是 Low(低电

平)。本实验我们设置为默认输出高 High。

② 选项 GPIO mode 用来设置输出模式为 Output Push Pull(推挽)还是 Output Open Drain(开

漏)。本实验我么设置为推挽输出 Output Push Pull。

③ 选择 Maximum ouput speed 用来设置输出速度为高速(High)/快速(Fast)/中速(Medium)/

低速(Low)。本实验我们设置为高速 High。

④ 选项 User Label 是用来设置初始化的 IO 口 Pin 值为我们自定义的宏,一般情况我们可

以不用设置,有兴趣的同学可以自由设置虎查看生成后的代码就很容易明白其含义。

配置完 PC0 后,PC1 配置方法和参数都一模一样,这样我们就不重复配置。

然后我们参考 4.8 小节方法,生成工程源码。接下来打开工程的 main.c 文件可以看到,该

文件内部有 STM32CubeMX 生成了函数 MX_GPIO_Init,内容如下:

static void MX_GPIO_Init(void)

{

GPIO_InitTypeDef GPIO_InitStruct;

/* GPIO Ports Clock Enable */

__HAL_RCC_GPIOD_CLK_ENABLE();

__HAL_RCC_GPIOC_CLK_ENABLE();

/*Configure GPIO pin Output Level */

HAL_GPIO_WritePin(GPIOC, GPIO_PIN_0|GPIO_PIN_1, GPIO_PIN_SET);

/*Configure GPIO pins : PC0 PC1 */

GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1;

GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;

GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;

HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

}

该函数的作用跟前面讲解的跑马灯实验中 LED_Init 函数作用一模一样,有兴趣的同学可以

直接修改跑马灯实验工程源码,把 LED_Init 函数内容修改为 MX_GPIO_Init 内容,大家会发现

实验效果一模一样。

一般情况下,STM32CubeMX 的主要作用是配置时钟系统和外设初始化函数。所以我们对

外设进行配置之后,生成外设初始化代码,然后把该代码应用到我们工程即可。关于本章中使

用 STM32CubeMX 配置 GPIO 方法,我们就给大家讲解到这里。

标签: #net走马灯远吗