专业的编程技术博客社区

网站首页 > 博客文章 正文

「正点原子NANO STM32F103开发板资料连载」第四章 开发入门(二)

baijin 2024-09-09 01:12:38 博客文章 18 ℃ 0 评论

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

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

4.7 MDK 中使用 HAL 库快速组织代码技巧

这一节主要讲解在使用 MDK 中使用 HAL 库开发的一些小技巧,仅供初学者参考。这节的

知识大家可以在学习第一个跑马灯实验的时候参考一下,对初学者应该很有帮助。我们就用最

简单的 GPIO 初始化函数为例。

现 在 我 们 要 初 始 化 某 个 GPIO 端 口 , 我 们 要 怎 样 快 速 操 作 呢 ? 在 头 文 件

stm32f1xx_hal_gpio.h 头文件中,定义 GPIO 初始化函数为:

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

现在我们想写初始化函数,那么我们在不参考其他代码的前提下,怎么组织代码呢?

首先,我们可以看出,函数的入口参数是 GPIO_TypeDef 类型指针和 GPIO_InitTypeDef 类

型 指 针 , 因 为 GPIO_TypeDef 入 口 参 数 比 较 简 单 , 所 以 我 们 通 过 第 二 个 入 口 参 数

GPIO_InitTypeDef 类型指针来讲解。双击 GPIO_InitTypeDef 后右键选择“Go to definition…”,如

下图 4.7.1:

于是定位到 stm32f1xx_hal_gpio.h 中 GPIO_InitTypeDef 的定义处:

typedef struct

{

uint32_t Pin;

uint32_t Mode;

uint32_t Pull;

uint32_t Speed;

}GPIO_InitTypeDef;

可以看到这个结构体有 4 个成员变量,这也告诉我们一个信息,一个 GPIO 口的状态是由

速度(Speed)和模式(Mode)以及上下拉(Pull)来决定的。我们首先要定义一个结构体变量,下

面我们定义:

GPIO_InitTypeDef GPIO_InitStructure;

接着我们要初始化结构体变量 GPIO_InitStructure。首先我们要初始化成员变量 Pin,这个时

候我们就有点迷糊了,这个变量到底可以设置哪些值呢?这些值的范围有什么规定吗?

这里我们就要找到 HAL_GPIO_Init()函数的声明处,同样,双击 HAL_GPIO_Init(),右键点

击“Go to definition of …”,这样光标定位到 stm32f1xx_hal_gpio.c 文件中的 HAL_GPIO_Init 函数

体开始处,我们可以看到在函数的开始处有如下几行:

void HAL_GPIO_Init(GPIO_TypeDef *GPIOx, GPIO_InitTypeDef *GPIO_Init)

{

……

/* Check the parameters */

assert_param(IS_GPIO_ALL_INSTANCE(GPIOx));

assert_param(IS_GPIO_PIN(GPIO_Init->Pin));

assert_param(IS_GPIO_MODE(GPIO_Init->Mode));

……

assert_param(IS_GPIO_SPEED(GPIO_Init->Speed));

……

}

顾名思义,assert_param 是断言语句,是对函数入口参数的有效性进行判断,所以我们可

以从这个函数入手,确定我们的入口参数的范围。第一行是对第一个参数 GPIOx 进行有效性判

断,双击“IS_GPIO_ALL_PERIPH”右键点击“go to defition of…” 定位到了下面的定义:

#define IS_GPIO_ALL_INSTANCE(INSTANCE) (((INSTANCE) == GPIOA) || \

((INSTANCE) == GPIOB) || \

((INSTANCE) == GPIOC) || \

((INSTANCE) == GPIOD) || \

((INSTANCE) == GPIOE))

很明显可以看出,GPIOx 的取值规定只允许是 GPIOA~GPIOE。

同样的办法,我们双击“IS_GPIO_PIN”右键点击“go to defition of...”,定位到下面的定

义:

#define IS_GPIO_PIN(PIN) ((((PIN) & GPIO_PIN_MASK ) != 0x00U) && (((PIN)

& ~GPIO_PIN_MASK) == 0x00U))

同时,宏定义标识符 GPIO_PIN_MASK 的定义为:

#define GPIO_PIN_MASK 0x0000FFFFU

从上面可以看出,PIN 取值只要低 16 位不为 0 即可。这里需要大家注意,因为一组 IO 口

只有16个IO,实际上PIN的值在这里只有低16位有效,所以PIN的取值范围为0x0001~0xFFFF。

那么是不是我们写代码初始化就是直接给一个 16 位的数字呢?这也是可以的,但是大多数情况

下,我们不会直接在入口参数处设置一个简单的数字,因为这样代码的可读性太差,HAL 库会

将这些数字的含义通过宏定义定义出来,这样可读性大大增强。我们可以看到在

GPIO_PIN_MASK 宏定义的上面还有数行宏定义:

#define GPIO_PIN_0 ((uint16_t)0x0001)

#define GPIO_PIN_1 ((uint16_t)0x0002)

#define GPIO_PIN_2 ((uint16_t)0x0004)

...//此处省略部分定义

#define GPIO_PIN_14 ((uint16_t)0x4000)

#define GPIO_PIN_15 ((uint16_t)0x8000)

#define GPIO_PIN_All ((uint16_t)0xFFFF)

这些宏定义 GPIO_PIN_0~GPIO_PIN_ALL 就是 HAL 库事先定义好的,我们写代码的时候

初始化结构体,成员变量 Pin 的时候入口参数可以是这些宏定义标识符。

同理,对于成员变量 Pull,我们用同样的方法,可以找到其取值范围定义为:

#define IS_GPIO_PULL(PULL) (((PULL) == GPIO_NOPULL) ||

((PULL) == GPIO_PULLUP) || \((PULL) == GPIO_PULLDOWN))

也 就 是 PULL 的 取 值 范 围 只 能 是 标 识 符 GPIO_NOPULL , GPIO_PULLUP 以 及

GPIO_PULLDOWN。

对于其他成员变量 Mode 以及 Speed,方法都是一样的,这里基于篇幅考虑我们就不重复

讲解。讲到这里,我们基本对 HAL_GPIO_Init 的入口参数有比较详细的了解了。于是我们可以

组织起来下面的代码:

GPIO_InitTypeDef GPIO_Initure;

GPIO_Initure.Pin=GPIO_PIN_9;

//PA9

GPIO_Initure.Mode=GPIO_MODE_AF_PP; //复用推挽输出

GPIO_Initure.Pull=GPIO_PULLUP;

//上拉

GPIO_Initure.Speed=GPIO_SPEED_FREQ_HIGH;//高速

HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化 PA9

接着又有一个问题会被提出来,这个初始化函数一次只能初始化一个 IO 吗?我要同时初

始化很多个 IO 口,是不是要复制很多次这样的初始化代码呢?

这里又有一个小技巧了。从上面的 GPIO_PIN_X 的宏定义我们可以看出,这些值是 0,1,2,4 这样

的数字,所以每个 IO 口选定都是对应着一个位,16 位的数据一共对应 16 个 IO 口。这个位为

0 那么这个对应的 IO 口不选定,这个位为 1 对应的 IO 口选定。如果多个 IO 口,他们都是对应

同一个 GPIOx,那么我们可以通过|(或)的方式同时初始化多个 IO 口。这样操作的前提是,

他们的 Mode,Speed,Pull 参数值相同,因为这些参数并不能一次定义多种。所以初始化多个

具有相同配置的 IO 口的方式可以是如下:

GPIO_InitTypeDef GPIO_Initure;

GPIO_Initure.Pin=GPIO_PIN_9|GPIO_PIN_9|GPIO_PIN_9//PA9,PA10,PA11

GPIO_Initure.Mode=GPIO_MODE_AF_PP; //复用推挽输出

GPIO_Initure.Pull=GPIO_PULLUP;

//上拉

GPIO_Initure.Speed=GPIO_SPEED_FREQ_HIGH;//高速

HAL_GPIO_Init(GPIOA,&GPIO_Initure); //初始化 PA9,PA10,PA11

对于那些参数可以通过|(或)的方式连接,这既有章可循,同时也靠大家在开发过程中不

断积累。

大家觉得上面讲解有点麻烦,每次要去查找 assert_param()这个函数去寻找,那么有没有更

好的办法呢?大家可以打开 GPIO_InitTypeDef 结构体定义:

typedef struct

{

uint32_t Pin; /*!< Specifies the GPIO pins to be configured.

This parameter can be any value of @ref GPIO_pins_define */

uint32_t Mode; /*!< Specifies the operating mode for the selected pins.

This parameter can be a value of @ref GPIO_mode_define */

uint32_t Pull; /*!< Specifies the Pull-up or Pull-Down activation for the selected pins.

This parameter can be a value of @ref GPIO_pull_define */

uint32_t Speed; /*!< Specifies the speed for the selected pins.

This parameter can be a value of @ref GPIO_speed_define */

}GPIO_InitTypeDef;

从上图的结构体成员后面的注释我们可以看出 Pin 的意思是

“Specifies the GPIO pins to be configured.

This parameter can be any value of @ref GPIO_pins_define”

从这段注释可以看出 Pin 的取值需要参考注释 GPIO_pins_define,大家可以在 MDKK 中搜

索注释 GPIO_pins_define,就可以找到上面我们提到的 Pin 的取值范围宏定义。如果要确定详

细的信息我们就得去查看手册了。对于去查看手册的那个地方,你可以在函数 HAL_GPIO_Init()

函数体中搜索 Pin 关键字,然后查看库函数设置 Pin 是设置哪个寄存器的哪个位,然后去中文

参考手册查看该寄存器相应位的定义以及前后文的描述。

这一节我们就讲解到这里,希望能对大家的开发有帮助。

4.8 手把手教你入门 STM32CubeMx 图形配置工具

上一章节我们讲解 stm32Cube 的时候提到 stm32Cube 包含 2 个部分:一部分是上一章我们

讲解的嵌入式软件包(包括 HAL 库),另一部分是图形化配置工具 STM32CubeMX。本小节

我们将给大家讲解 STM32CubeMX 相关知识,带领大家入门 STM32CubeMX 图形化配置工具。

之所以我们要把 STM32CubeMX 讲解放在本小节,是因为 STM32CubeMX 最基本也是最重要

的用途是配置时钟系统,所以我们要先讲解 STM32F1 的时钟系统之后,才能教大家学习

STM32CubeMX。这部分内容我们分 3 个小节来讲解:

? 4.8.1 STM32CubeMX 简介

? 4.8.2 STM32CubeMX 运行环境搭建

? 4.8.3 使用 STM32CubeMX 工具配置工程模板

4.8.1 STM32CubeMX 简介

STM32CubeMX 是 ST 意法半导体近几年来大力推荐的 STM32 芯片图形化配置工具,允许用户

使用图形话向导生成 C 初始化代码,可以大大减轻开发工作,时间和费用。STM32CubeMX 几

乎覆盖了 STM32 全系列芯片。它具有如下特性:

① 直观的选择 MCU 型号,可指定系列、封装、外设数量等条件

② 微控制器图形化配置

③ 自动处理引脚冲突

④ 动态设置时钟树,生成系统时钟配置代码

⑤ 可以动态设置外围和中间件模式和初始化

⑥ 功耗预测

⑦ C 代码工程生成器覆盖了 STM32 微控制器初始化编译软件,如 IAR,KEIL,GCC。

⑧ 可以独立使用或者作为 Eclipse 插件使用

对于 STM32CubeMX 和 STM32Cube 的关系这里我们还需要特别说明一下,STM32Cube 包

含 STM32CubeMX 图形工具和 STM32Cube 库两个部分,使用 STM32CubeMX 配置生成的

代码,是基于 STM32Cube 库的。也就是说,我们使用 STM32CubeMX 配置出来的初始化

代码,和 STM32Cube 库兼容,例如硬件抽象层代码就是使用的 STM32 的 HAL 库。不同

的 STM32 系列芯片,会有不同的 STM32Cube 库支持,而 STM32Cube 库即可。它们之间

的关系如下图 4.8.1.1;


4.8.2 STM32CubeMX 运行环境搭建

STM32CubeMX 运行环境搭建包含两个部分。首先是 Java 运行环境安装,其次是

STM32CubeMX 软件安装。对于 Java 运行环境,大家可以到 Java 官网 www.java.com 下载最新

的 Java 软件,也可以直接从我们光盘复制安装包,目录为:\6,软件资料\1,软件\Java 安装包。

这里大家需要注意,STM32CubeMX 的 Java 运行环境版本必须是 V1.7 及以上,如果你的电脑

安装过 V1.7 以下版本,请先删掉后重新安装最新版本。

对于 Java 运行环境安装,我们这里就不做过多讲解,大家直接双击安装包,根据提示安装

即可。安装完成之后提示界面如下图 4.8.2.1:

安装完 Java 运行环境之后,为了检测是否正常安装,我们可以打开 Windows 的命令输入

框,输入:java -version 命令,如果显示 Java 版本信息,则安装成功。提示信息如下图 4.8.2.2:

图 4.8.2.2 查看 Java 版本

在安装完 Java 运行环境之后,接下来我们安装 STM32CubeMX 图形化工具。该软件大家

同样可以直接从光盘复制,目录为:\6,软件资料\1,软件\STM32CubeMX,也可以直接从 ST

官方下载,下载地址为:www.st.com/stm32cube。

接下来我们直接双击 STM32CubeMX 安装包,根据提示信息安装即可。安装完成之后提示信息

如下图 4.8.2.3 所示:

安装完成之后,我们打开软件,如果软件安装成功,打开软件之后的界面如下图 4.8.2.4 所

示:


在安装好 STM32CubeMX 之后,接下来我们要在软件中指定 STM32Cube 软件包。在

STM32CubeMX 操作界面,依次点击 Help->Updater Settings,弹出界面如下图 4.8.2.5 所示:

在上图 4.8.2.5 中,我们只需要点击 Browse 按钮,定位到我们 3.1 小节讲解的 stm32cubefx

存放目录即可。这里大家注意,stm32cubefx 文件夹名字遵循 STM32Cube_FW_Fx_Vm.n 格式,

我们指定的“Repository Folder”下面必须存在一个或者多个 STM32Cube_FW_Fx_Vm.n 格式程

序包,在 STM32CubeMX 生成工程的时候,会根据我们选择的芯片型号,去这个目录加载必要

的库文件。一般情况下,我们会新建一个目录,然后把我们需要使用的各种 stm32cubefx 支持

包解压放到该目录之下,然后把该目录指定为“Repository Folder”即可。操作方法如下图所示:

实际上,我们也可以直接在 STM32CubeMX 中点击 Help->Manage embedded software

packages 下载需要的程序库,但是由于速度比较慢,而且在下载过程中很容易中断,所以我们

不推荐直接在 CubeMX 中下载。

接下来我们将讲解怎么使用 STM32CubeMX 新建一个完整的 STM32F1 工程。

4.8.3 使用 STM32CubeMX 工具配置工程模板

大多数情况下,我们都只使用 STM32CubeMX 来生成工程的时钟系统初始化代码以及外设

的初始化代码,因为用户控制逻辑代码是无法在 STM32CubeMX 中完成的,需要用户自己根据

需求来实现。使用 STM32CubeMX 配置工程的一般步骤为:

1)工程初步建立和保存

2)RCC 设置

3)时钟系统(时钟树)配置

4)GPIO 功能引脚配置

5)生成工程源码

6)编写用户代码

接下来我们将按照上面 6 个步骤,依次教大家使用 STM32CubeMX 工具生成一个完整的工

程。

4.8.3.1 工程初步建立和保存

工程建立的方法有两种方法,第一种方法是打开 STM32CubeMX 之后在主界面点击 New

Project 按钮,第二种方法是在菜单栏依次点击 File->New Project。操作方法如下图 4.8.3.1.1 所

示:

点击新建工程按钮之后,会弹出 MCU 选择窗口。我们依次在选项卡 Core,Serise,Line

和 Package 之下选择与我们使用的芯片 STM32F103RBT6 对应的参数,然后选择对应的芯片型

号,最后双击确定。操作方法如下图 4.8.3.1.2 所示:

为了避免在软件使用过程中出现意外导致工程没有保存,所以我们选择好芯片型号之后,

先对工程进行保存。依次点击菜单栏 File->Save Project,然后保存工程到某个文件夹下面即可。

操作过程如下图 4.8.3.1.3 所示:

保存完成之后,大家进入 Template 目录后发现目录中多了一个 Template.ioc 文件,下次我

们点击这个文件就可以直接打开这个工程。

工程新建好之后会直接进入 Pinout 选项卡,这个时候界面会展示芯片完整引脚图,如下图

4.8.3.1.4 所示:

在引脚图中,我们可以对引脚功能进行配置。图中黄色的引脚主要是一些电源和 GND 引

脚。如果某个引脚被使用,那么会显示为绿色。

4.8.3.2 RCC 设置

对 STM32 芯片而言,RCC 配置的重要性不言而喻。在 STM32CubeMX 中,RCC 相关设置

却非常简单,因为它把系统独立出来配置。在操作界面,依次点击选项卡

Pinout->Peripherals->RCC 便可进入 RCC 配置栏,操作步骤如下图 4.8.3.2.1 所示:

图 4.8.3.2.1 进入 RCC 配置栏

从上图可以看出,RCC 配置栏实际上只有 3 个配置项。选项 High Speed Clock(HSE)用

来配置 HSE,第二个选项 Low Speed Clock(LSE)用来配置 LSE,选项 Master Clock Output

用来选择是否使能 MCO 引脚时钟输出。

本小节我们只使用到 HSE,所以我们设置选项 High Speed Clock(HSE)的值为 Crystal/Ceramic

Resonator(使用晶振/陶瓷振荡器)即可。这里还需要说明一下,值 Bypass Clock Source 的意思

是旁路时钟源,也就是不使用晶振/陶瓷振荡器,直接通过外部提供一个可靠的 4-16Mhz 时钟作

为 HSE。配置好的 RCC 配置选项如下图 4.8.3.2.2 所示:

从上图还可以看出,在我们打开了 HSE 之后,右边的引脚图中,相应的引脚会由灰色变为

绿色,表示该引脚已经被使用。配置完 RCC 之后,接下来我们来看看配置时钟系统树的方法。

4.8.3.3 时钟系统(时钟树)配置

在使用 STM32CubeMX 配置时钟树之前,大家需要充分理解 STM32 时钟系统,这在我们

前面 4.3 小节有非常详细的讲解,只有熟练掌握了 STM32 时钟系统,那么在软件中配置时钟树

才会得心应手。

点击 Clock Configuration 选项卡即可进入时钟系统配置栏,如下图 4.8.3.3.1 所示:

进入 Clock Configuration 配置栏之后可以看到,界面展现一个完整的 STM32F1 时钟系统框

图。这个时钟系统框图跟我们之前时钟系统章节讲解的时钟系统框图实际是一模一样的,只不

过调整了一下显示顺序。从这个时钟树配置图可以看出,配置的主要是外部晶振大小,分频系

数,倍频系数以及选择器。在我们配置的工程中,时钟值会动态更新,如果某个时钟值在配置

过程中超过允许值,那么相应的选项框会红色提示。

这里,我们将配置一个和我们之前讲解的 STM32_Clock_Init 函数实现的一模一样的配置。

Stm32_Clock_Init 函数主要实现的是以 HSE 为时钟源,配置主 PLL 相关参数,然后系统时钟选

择 PLL 为时钟源,最终配置系统时钟为 72Mhz 的过程。同时,还配置了 AHB,APB1,APB2

和 Systick 的相关分频系数。由于图片比较大,我们把主要的配置部分分两部分来讲解,第一部

分是配置系统时钟,第二部分是配置 AHB,APB1 和 APB2 的分频系数。首先我们来看看第一

部分配置如下图 4.8.3.3.2 所示:

我们把系统时钟配置为 4 个步骤,分别用标号①~④表示,详细过程为:

① 时钟源参数设置:HSE 或者 HSI 配置。这里我们选择 HSE 为时钟源,所以我们之前

必须在 RCC 配置工程中我们开启了 HSE。

② 时钟源选择:HSE 还是 HSI。这里我们配置 HSE 的 1 分频即可。

③ PLL 倍频系数 PLLMUL 配置。倍频系数 PLLMUL 我们设置为 9

④ 系统时钟时钟源选择:PLL,HSI 还是 HSE。这里毫无疑问,我们选择了 PLL,选择

器选择 PLLCLK 即可。

经过上面的 4 个步骤,就会生成标准的 72Mhz 系统时钟。接下来我们只需要配置 AHB,

APB1,APB2 和 Systick 的分频系数,就可以完全实现函数 Stm32_Clock_Init 配置的时钟系统。

配置如下图 4.8.3.3.3 所示:

AHB,APB1 和 APB2 总线时钟以及 Systick 时钟的最终来源都是系统时钟 SYSCLK。其中

AHB 总线时钟 HCLK 是由 SYSCLK 经过 AHB 预分频器之后来的,如果我们要设置 HCLK 为

72Mhz,那么我们只需要配置图中标号⑦的地方为 1 即可。得到 HCLK 之后,接下来我们将在

图标好⑧~⑩处同样的方法依次配置 Systick,APB1 以及 APB2 分频系数为 2 和 1 即可。配置完

成之后,那么 HCLK=72Mhz,Systick 时钟为 72/1Mhz=72Mhz,PCLK1=72Mhz/2=36MHz,

PCLK2=72Mhz/1=72Mhz,这和我们使用 Stm32_Clock_Init 函数配置的时钟是一模一样的。

配置完时钟之后,这个时候如果我们直接使用软件生成工程,那么我们就可以从工程中提

取系统时钟初始化配置相关代码。配置时钟系统实际上是 STM32CubeMX 一个很重要的功能。

为了验证我们工程的正确性,下一节我们将手把手教大家进行 IO 口配置,配置一个和我们

NANO STM32F103 开发板跑马灯实验初始化代码一样的效果。

4.8.3.4 GPIO 功能引脚配置

本小节,我们将讲解怎么使用 STM32CubeMX 工具配置 STM32 的 GPIO 口。在 NANO

STM32F103 开发板的 PC0~PC7 引脚有连接八个 LED 灯,本小节将配置 PC0 和 PC1 这两个 IO

口的相关参数。STM32CubeMX 可以直接在芯片引脚图上配置 IO 参数。这里我们回到

STM32CubeMX 的 Pinout 选项,在搜索栏输入 PC0 和 PC1 即可找到 PC0 和 PC1 在引脚图中的

位置如下图 4.8.3.4.1 所示:

接下来,我们在图 4.8.3.4.1 引脚中点击 PC0,在弹出的下拉菜单中,选择 IO 口的功能为

GPIO_Output。操作方法如下图 4.8.3.4.2 所示:

同样的方法,我们配置 PC1 选择功能为 GPIO_Oput 即可。这里我们需要说明一下,如果

我们要配置 IO 口为外部中断引脚或者其他复用功能,我们选择相应的选项即可。配置完 IO 口

功能之后,还要配置 IO 口的速度等参数。这些参数是在 Configuration 选项卡中配置的。配置

步骤如下图 4.8.3.4.3 所示:

依次点击 Configuration->GPIO 即可进入 IO 口详细配置界面,进入 IO 口配置界面之后,界

面会列出所有使用到的 IO 口的参数配置。这里,我们选中 PC0 栏,就会显示框下方显示对应

的 IO 口详细配置信息,然后我们对参数进行配置后点击 Apply 保存即可。配置方法如下图

4.8.3.4.4 所示:

这个界面里的 IO 参数含义这里就不做过多讲解,在大家学习完第一个实验跑马灯实验之

后,对参数的含义理解会更加清晰。配置完成之后,我们点击 OK 后界面回到 Configuration 选

项卡界面。

对于选项卡 Power Consumption Calculator,它的作用是对功耗进行计算,这里我们并没有

使用到,就不详细讲解了。

4.8.3.5 生成工程源码

经过上面 4 个步骤,一个完整的系统已经配置完成。接下来,我们将使用 STM32CubeMX

生成我们需要的工程源码。在 STM32CubeMX 操作界面,依次点击菜单 Project->Generate Code

即可生成源码,操作方法如下图 4.8.3.5.1 所示:

点击之后,弹出的界面会要求配置生成的工程名称,保存目录以及使用的编译软件类型。

我们依次填写名称和目录即可,对于编译软件我们选择 MDK5,还有我们需选择使用的 Cube

库版本。操作过程如下图 4.8.3.5.2 所示:

配置完成后,点击 OK 开始生成源码。源码生成完成之后,就保存在我们 Project Location

选项配置的目录中,同时弹出生成成功提示界面,我们可以点击界面的“Open Folder”按钮打

开工程保存目录,也可以点击界面的“Open Project”按钮直接使用 IDE 打开工程。提示界面如

下图 4.8.3.5.3 所示:

至此,一个完整的 STM32F1 工程就已经生成完成。生成后的工程目录结构如下图 4.8.3.5.4

所示:

Drivers 文件夹存放的是 HAL 库文件和 CMSIS 相关文件。

Inc 文件夹存放的是工程必须的部分头文件。

MDK-ARM 下面存放的是 MDK 工程文件

Src 文件夹下面存放的是工程必须的部分源文件

Template.ioc 是 STM32CubeMX 工程文件,双击该文件工程就会在 STM32CubeMX 中杯打开。

4.8.3.6 编写用户程序

在编写用户程序之前,首先我们打开生成的工程模板进行编译,发现没有任何错误和警告。

工程模板结构如下图 4.8.3.6.1 所示:

该工程模板结构跟我们前面 3.3 小节新建的工程模板实际是类似的,只不过一些分组名称

不一样,同时我们将时钟系统配置源码放在 SYSTEM 分组的 sys.c 中,而该模板直接放在 main.c

源文件中。这里我们对该模板就不做过多讲解。我们直接打开 main.c 源文件可以看到,该文件

定义了两个关键函数 SystemClock_Config 函数用来配置配置时钟系统,和我们模板中的

Stm32_Clock_Init 函数作用一样。MX_GPIO_Init 函数用来初始化 PC0 和 PC1 相关配置,这在

我们的模板中,我们直接放在 main 函数中。接下来我们看看生成的工程模板的 main 函数,这

里我们删掉了源码注释,关键源码如下:

int main(void)

{

HAL_Init();

SystemClock_Config();

MX_GPIO_Init();

While(1)

{

}

}

该函数 while 语句之前实现的功能和我们 3.3 小节的工程模板是一致的。这里,我们直接把

我们 3.3 小节新建的工程模板中 main 函数 while 语句中的源码复制到此处的 while 语句中,然

后复制 Delay 函数声明和定义到 main 函数之前。这里大家需要注意,STM32CubeMX 生成的

main.c 文件中,有很多地方有“/*USER CODE BEGIN X*/”和“/*USER CODE END X*/”格

式的注释,我们在这些注释的 BEGIN 和 END 之间编写代码,那么重新生成工程之后,这些代

码会保留而不会被覆盖。复制完代码之后,main 函数关键源码如下:

/* USER CODE BEGIN 0 */

void Delay(__IO uint32_t nCount);

void Delay(__IO uint32_t nCount)

{

while(nCount--){}

}

/* USER CODE END 0*/

int main(void)

{

HAL_Init();

SystemClock_Config();

MX_GPIO_Init();

/* USER CODE BEGIN WHILE */

while (1)

{

HAL_GPIO_WritePin(GPIOC,GPIO_PIN_0,GPIO_PIN_SET);

//PC0 置 1

HAL_GPIO_WritePin(GPIOC,GPIO_PIN_1,GPIO_PIN_SET);

//PC1 置 1

Delay(0x7FFFFF);

HAL_GPIO_WritePin(GPIOC,GPIO_PIN_0,GPIO_PIN_RESET);

//PC0 置 0

HAL_GPIO_WritePin(GPIOC,GPIO_PIN_1,GPIO_PIN_RESET);

//PC1 置 0

Delay(0x7FFFFF);

/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */

}

/* USER CODE END 3 */

}

这个时候,我们对工程进行编译,发现没有任何警告和错误。同时,我们使用 3.4 小节的方法下载程序到 NANO STM32F103 开发板中(下载是时候,请注意配置 MDK),运行结果和

3.3 小节新建工程运行结果一模一样。

本小节使用 STM32CubeMX 新建的工程模板在我们光盘目录:

“4,程序源码\标准例程-HAL

库函数版本\实验 0-3 Template 工程模板-使用 STM32CubeMX 配置”中有存放,大家在编写用

户代码过程中可以参考该工程的 main.c 文件。

这里我们需要说明一下,大多数情况下,我们使用 STM32CubeMX 主要用来配置时钟系

统和外设初始化代码。这里我们讲解新建一个工程模板,是为了系统全面的讲解 STM32CubeMX

生成工程的步骤。本小节就给大家讲解到这里。通过本章的学习,大家对 STM32CubeMX 的使

用就有了初步的认识和理解,后面我们会在实战篇通过几个实验来巩固这方面的知识。

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表