1)实验平台:ALIENTEK NANO STM32F411 V1开发板

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

第三章 MDK5 软件入门

本章将向大家介绍 MDK5 软件和 STM32CubeF4,通过本章的学习,我们最终将建立一个

基于 HAL 库的 MDK5 工程,同时本章还将向大家介绍 MDK5 软件的一些使用技巧,希望大家

在本章之后,能够对 MDK5 这个软件有个比较全面的了解。

本章分为如下个小结:

3.1,STM32CubeF4 简介

3.2,MDK5 简介与安装

3.3,新建基于 HAL 库的工程模板和工程结构讲解;

3.4,程序下载与调试;

3.5,MDK5 使用技巧;

3.1 STM32CubeF4 简介

STM32Cube 是 ST 提供的一套性能强大的免费开发工具和嵌入式软件模块,能够让开发人

员在 STM32 平台上快速、轻松地开发应用。它包含两个关键部分:

1、图形配置工具 STM32CubeMX。允许用户通过图形化向导来生成 C 语言工程。

2、嵌入式软件包(STM32Cube 库)。包含完整的 HAL 库(STM32 硬件抽象层 API),

配套的中间件(包括 RTOS,USB,TCP/IP 和图形),以及一系列完整的例程。

嵌入式软件包完全兼容 STM32CubebMX。对于图形配置工具 STM32CubeMX 入门使用,

由于需要 STM32F4 基础才能入门使用,所以我们安排在后面 4.8 小节给大家讲解。本小节,我

们主要讲解 STM32Cube 的嵌入式软件包部分。在讲解之前,首先我们来看看库函数和寄存器

开发的关系。

3.1.1 库开发与寄存器开发的关系

很多用户都是从学 51 单片机开发转而想进一步学习 STM32 开发,他们习惯了 51 单片机

的寄存器开发方式,突然一个 ST 官方库摆在面前会一头雾水,不知道从何下手。下面我们将

通过一个简单的例子来告诉 STM32 固件库到底是什么,和寄存器开发有什么关系?其实一句

话就可以概括:固件库就是函数的集合,固件库函数的作用是向下负责与寄存器直接打交道,

向上提供用户函数调用的接口(API)。

在 51 的开发中我们常常的作法是直接操作寄存器,比如要控制某些 IO 口的状态,我们直

接操作寄存器:

P0=0x11;

而在 STM32 的开发中,我们同样可以操作寄存器:

GPIOx->BSRR = 0x0011;

这种方法当然可以,但是这种方法的劣势是你需要去掌握每个寄存器的用法,你才能正确

使用 STM32,而对于 STM32 这种级别的 MCU,数百个寄存器记起来又是谈何容易。于是 ST(意

法半导体)推出了官方固件库,固件库将这些寄存器底层操作都封装起来,提供一整套接口(API)

供开发者调用,大多数场合下,你不需要去知道操作的是哪个寄存器,你只需要知道调用哪些

函数即可。

比如上面的控制 BSRR 寄存器实现电平控制,官方 HAL 库封装了一个函数:

void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState

PinState)

{

if(PinState != GPIO_PIN_RESET)

{

GPIOx->BSRR = GPIO_Pin;

}

else

{

GPIOx->BSRR = (uint32_t)GPIO_Pin << 16U;

}

}

这 个 时 候 你 不 需 要 再 直 接 去 操 作 BSRR 寄 存 器 了 , 你 只 需 要 知 道 怎 么 使 用

HAL_GPIO_WritePin 这个函数就可以了。在你对外设的工作原理有一定的了解之后,你再去看

固件库函数,基本上函数名字能告诉你这个函数的功能是什么,该怎么使用,这样是不是开发

会方便很多?

任何处理器,不管它有多么的高级,归根结底都是要对处理器的寄存器进行操作。但是固

件库不是万能的,您如果想要把 STM32 学透,光读 STM32 固件库是远远不够的。你还是要了

解一下 STM32 的原理,而这些原理了解了,你在进行固件库开发过程中才可能得心应手游刃

有余。只有谅解了原理,你才能做到“知其然知其所以然”,所以大家在学习库函数的同时,

别忘了要了解一下寄存器大致配置过程。

3.1.2 STM32CubeF4 固件包介绍

STM32Cube 目前几乎支持 STM32 全系列,本手册,我们讲解的是 STM32F411 的使用,

所以我们主要讲解 STM32F4 相关知识。如果大家使用的是其他系列的 STM32 芯片,请到 ST

官网下载对应 STM32Cube 包即可。完整的 STM32CubeF4 包在我们的开发板配套光盘有提供,

目录为:7,STM32 参考资料1,STM32CubeF4 固件包

接下来我们看看 STM32CubeF4 包目录结构,如下图 3.1.2.1 所示:

图 3.1.2.1 STM32CubeF4 包目录结构

对于 Ducumentation 文件夹,里面是一个 STM32CubeF4 的英文说明文档,这里我们就不做

过多解释。接下来我们通过几个表格依次来介绍下 STM32CubeF4 中几个关键的文件夹。

1)Drivers 文件夹

Drivers 文件夹包含 BSP,CMSIS 和 STM32F4xx_HAL_Driver 三个子文件夹。三个子文件

夹具体说明请参考 3.1.2.2:

表 3.1.2.2 Drivers 文件夹介绍

2)Middlewares 文件夹

该文件夹下面有 ST 和 Third_Party 2 个子文件夹。ST 文件夹下面存放的是 STM32 相关的

一些文件,包括 STemWin 和 USB 库等。Third_Party 文件夹是第三方中间件,这些中间件都是

非常成熟的开源解决方案。具体说明请见下表 3.1.2.3:

表 3.1.2.3 Middlewares 文件夹介绍

3) Project 文件夹

该文件夹存放的是一些可以直接编译的实例工程。每个文件夹对应一个 ST 官方的 Demo

板。这里我们讲解的是 STM32F411RC 开发板,所以我们打开子文件夹 STM32F411RET6-Nucleo

即可(RET6 FLASH 容量会大点)。里面有很多实例,我们都可用来参考。这里大家注意,每

个工程下面都有一个 MDK-ARM 子文件夹,该子文件夹内部都会有名称为 Project.uvprojx 的工

程 文 件 , 我 们 只 需 要 点 击 它 就 可 以 MDK 中 开 打 工 程 , 例 如 我 们 打 开

STM32Cube_FW_F4_V1.24.0ProjectsSTM32F411RE-Nucleo

TemplatesMDK-ARM 文件夹,内容如下图所示 3.1.2.4:

图 3.1.2.4 Templates 工程中 MDK-ARM 文件内容

3)Utilities 文件夹该文件夹下面是一些其他组件,在项目中使用不多。有兴趣的同学可以学习一下,这里我们就不做过多介绍。

3.1.3 HAL 库和标准库选择

ST 先后提供了两套固件库:标准库和 HAL 库。STM32 芯片面市之初只提供了丰富全面的

标准库,大大便利了用户程序开发,为广大开发板所推崇,同时也为 ST 积累了大量标准库的

用户。有过 STM32 基础的同学想必对标准库非常熟悉。我们正点原子目前的所有 STM32F1 开

发板以及探索者 STM32F407 开发板都是采用的标准库。目前网络学习资料和源码,绝大多数

都是采用的标准库。

大约到 2014 年左右,ST 在标准库的基础上又推出了 HAL 库。实际上,HAL 库和标准库

本质上是一样的,都是提供底层硬件操作 API,而且在使用上也是大同小异。有过标准库及基

础的同学对 HAL 库的使用也很容易入手。个人认为 ST 官方之所以这几年大力推广 HAL 库,

是因为 HAL 的结构更加容易整合 STM32Cube,而 STM32CubeMX 是 ST 这几年极力推荐的程

序生成开发工具。所以这两年推出的 STM3232 芯片,ST 直接只提供 HAL 库,在新型的 STM32

芯片中,用 HAL 库逐步淘汰彼标准库。

那有很多同学不禁要问,我么是使用 HAL 库还是标准库好吗?这里我们只想说的是,HAL

库和标准库都非常强大,对于目前标准库支持的芯片采用标准库开发也非常方便实用,而且目

前网络资料和程序大部分都是讲解的标准库。大家不需要纠结自己学的是 HAL 库还是标准库,

无论使用哪种库,只要理解了 STM32 本质,任何库都是一种工具,使用起来都非常方便,学

会了一种库,另外一种库也非常容易上手,程序开发思路转变也非常容易。如果你是一个 STM32

熟手,长期从事 STM32 开发,那么有必要对标准库和 HAL 库都有一定的了解,这样才能在开

发中得心应手游韧有余。

在之前正点原子推出的 STM32F429 和 STM32F7 开发板,我们采用了 HAL 库。同样在

NANO 板我们也采用 HAL 库。对于 STM32 熟手,Cube 确实是个非常强大的工具,也是 ST 这

几年大力推广的工具,为了能教用户使用 Cube,所以我们选择 HAL 库。目前网络 HAL 库和

Cube 使用教程都非常少,我们也希望能在这方面贡献一份力量,协助 ST 带领大家入门 HAL

库和 Cube。

3.2 MDK5 简介

MDK 源自德国的 KEIL 公司,是 RealView MDK 的简称。在全球 MDK 被超过 10 万的嵌

入式开发工程师使用。目前最新版本为:MDK5.27,但该版本存在一些 BUG,教程中我们使用

的是 MDK5.23。该版本使用 uVision5 IDE 集成开发环境,是目前针对 ARM 处理器,尤其是

Cortex M 内核处理器的最佳开发工具。

MDK5 向后兼容 MDK4 和 MDK3 等,以前的项目同样可以在 MDK5 上进行开发(但是头文

件方面得全部自己添加), MDK5 同时加强了针对 Cortex-M 微控制器开发的支持,并且对传统

的开发模式和界面进行升级,MDK5 由两个部分组成:MDK Core 和 Software Packs。其中,

Software Packs 可以独立于工具链进行新芯片支持和中间库的升级。如图 3.1.1 所示:

图 3.2.1 MDK5 组成

从上图可以看出,MDK Core 又分成四个部分:uVision IDE with Editor(编辑器),ARM

C/C++ Compiler(编译器),Pack Installer(包安装器),uVision Debugger with Trace(调试跟

踪器)。uVision IDE 从 MDK4.7 版本开始就加入了代码提示功能和语法动态检测等实用功能,

相对于以往的 IDE 改进很大。Software Packs(包安装器)又分为:Device(芯片支持),CMSIS(ARM Cortex 微控制器软件接口标准)和 Mdidleware(中间库)三个小部分,通过包安装器,我们可以安装最新的组件,从而支持新的器件、提供新的设备驱动库以及最新例程等,加速产品开发进度。

同以往的 MDK 不同,以往的 MDK 把所有组件到包含到了一个安装包里面,显得十分“笨

重”,MDK5 则不一样,MDK Core 是一个独立的安装包,它并不包含器件支持和设备驱动等

组件,但是一般都会包括 CMSIS 组件,大小 350M 左右,相对于 MDK4.70A 的 500 多 M,瘦

身不少,MDK5 安装包可以在:http://www.keil.com/demo/eval/arm.htm 下载到。而器件支持、

设备驱动、CMSIS 等组件,则可以点击 MDK5 的 Build Toolbar 的最后一个图标调出 Pack

Installer,来进行各种组件的安装。也可以在 http://www.keil.com/dd2/pack 这个地址下载,然

后进行安装。

在 MDK5 安装完成后,要让 MDK5 支持 STM32F411 的开发,我们还需要安装 STM32F4

的器件支持包:Keil.STM32F4xx_DFP.2.9.0.pack(STM32F4 的器件包)。这个包以及 MDK5.23

安装软件,我们都已经在开发板光盘提供了,大家自行安装即可。

3.3 新建基于 HAL 库的工程模板和工程结构讲解

在前面的章节我们介绍了 STM32F4xx 官方 HAL 库包的一些知识,这些我们将着重讲解建

立基于 HAL 库的工程模板的详细步骤。实际上,我们可以使用 ST 官方的 STM32CubeMX 图

形工具生成一个工程模板,这里之所以我们还要手把手教大家新建一个模板,是为了让大家对

工程新建和运行过程有一个深入的理解,这样在日后的开发中遇到任何问题都可以得心应手的

解决,STM32CubeMX 工具的使用我们在后面的 4.8 小节会详细讲解。在新建模板之前,首先

我们要准备如下资料:

1)HAL 库开发包:STM32Cube_FW_F4_V1.24.0 这是 ST 官网下载的 STM32CubeF4 包完

整版,我们光盘目录(压缩包):

“7, STM32 参考资料1,STM32CubeF4 固件包en.stm32cubef4.zip”。

我们官方论坛下载地址:http://openedv.com/posts/list/6054.htm

2)MDK5.23 开发环境(我们的板子的开发环境目前使用这个版本)。这在我们光盘的软

件目录下面有包装包:软件资料软件MDK5。

3.3.1 新建基于 HAL 库工程模板

在新建之前,首先我们要说明一下,这一小节我们新建的工程放在光盘目录,路径为:“4,

程序源码标准例程-库函数版本实验 0-1 Template 工程模板-新建工程章节使用”下面,大家在

学习新建工程过程中间遇到一些问题,可以直接打开这个模板,然后对比学习。

1)在建立工程之前,我们建议用户在电脑在某个目录下面建立一个文件夹,后面所建立的

工程都可以放在这个文件夹下面,这里我们建立一个文件夹为 Template。这是工程的根目录文

件夹。然后为了方便我们存放工程需要的一些其他文件,这里我们还新建下面 4 个子文件夹:

CORE,HALLIB,OBJ 和 USER。至于这些文件夹名字,实际上是可以任取的,我们这样取名

只是为了方便识别。对于这些文件夹用来存放什么文件,我们后面的步骤会一一提到。新建好

的目录结构如下图 3.3.1.1:

图 3.3.1.1 新建文件夹

2)接下来,打开 MDK,点击菜单 Project->New Uvision Project,然后将目录定位到刚才建

立的文件夹 Template 之下的 USER 子目录,工程取名为 Template 之后点击保存,工程文件都保

存到 USER 文件夹下面。操作过程如下图 3.3.1.2 和 3.3.1.3 所示:

图 3.3.1.2 新建工程

图 3.3.1.3 定义工程名称

接下来会出现一个选择 Device 的界面,就是选择我们的芯片型号,这里我们定位到

STMicroelectronics 下面的 STMF411RCT6(针对我们的正点原子 NANO STM32F411 板子是这

个型号)。这里我们选择 STMicroelectronics->STM32F4 Series->STM32F411->STM32F411RC(如

果使用的是其他系列的芯片吗,选择相应的型号就可以了,例如我们的战舰 STM32 开发板是

STM32F103ZET6。特别注意:一定要安装对应的器件 pack 才会显示这些内容)。

图 3.3.1.4 选择芯片型号

点击 OK,MDK 会弹出 Manage Run-Time Environment 对话框,如图 3.3.1.5 所示:

图 3.3.1.5 Manage Run-Time Environment 界面

这是 MDK5 新增的一个功能,在这个界面,我们可以添加自己需要的组件,从而方便构建

开发环境,不过这里我们不做介绍。所以在图 3.3.1.5 所示界面,我们直接点击 Cancel,即可,

得到如图 3.3.1.6 所示界面:

图 3.3.1.6 工程初步建立

3)现在我们看看 USER 目录下面内容,如下图 3.3.1.7:

图 3.3.1.7 工程 USER 目录文件

这里我们说明一下,Template.uvprojx 是工程文件,非常关键,不能轻易删除,MDK5.23

生成的工程文件是以.uvprojx 为后缀。DebugConfig,Listing 和 Objects 三个文件夹是 MDK 自

动生成的文件夹。其中 DebugConfig 文件夹用于存储一些调试配置文件,Listings 和 Objects 文

件夹用来存储 MDK 编译过程的一些中间文件。这里,我们把 Listing 和 Objects 文件夹删除,

我们会在下一步骤中新建一个 OBJ 文件夹,用来存放编译中间文件。当然,我们不删除这两个

文件夹也没有关系,只是我们不用它而已。

4)接下来我们将从官方 stm32cubeF4 包里面复制一些我们新建工程需要的关键文件到我

们的工程目录中。首先,我们要将 STM32CubeF4 包里的源码文件复制到我们的工程目录文件

夹下面。打开官方 STM32CubeF4 包,定位到我们之前准备好的 HAL 库包的目录:

STM32Cube_FW_F4_V1.24.0DriversSTM32F4xx_HAL_Driver 下面,将目录下面的 Src,Inc

文件夹复制到我们刚才建立的 HALLIB 文件夹下面。Src 存放的是固件库的.c 文件,Inc 存放的

是对应的.h 文件。操作完成后工程 HALLIB 目录内容如下图 3.3.1.8。

图 3.3.1.8 官方库源码文件夹

5)接下来,我们要将 STM32CubeF4 包里面相关的启动文件以及一些关键头文件复制到我

们 的 工 程 目 录 CORE 只 下 。 打 开 STM32CubeF4 包 , 定 位 到 目 录

STM32Cube_FW_F4_V1.24.0DriversCMSISDeviceSTSTM32F4xxSourceTemplatesarm

下面 , 将 文 件 startup_stm32f411xe.s 复 制 到 CORE 目 录 下 面 。 然 后 定 位 到 目 录

STM32Cube_FW_F4_V1.24.0DriversCMSISInclude,将里面的两个头文件:cmsis_armcc.h,

core_cm4.h,复制到 CORE 目录下面,同样需要将 core_cmFunc.h,

core_cmInstr,

core_cmSimd.h

也复制到 CORE 目录下面(STM32Cube_FW_F4_V1.24.0 版本文件里面不带,以前版本是带的)。

现在看看我们的 CORE 文件夹下面的文件,如下图 3.3.1.9:

图 3.3.1.9 CORE 文件夹文件

6)接下来我们要复制工程模板需要的一些其他头文件和源文件到我们工程。首先定位到目

录:STM32Cube_FW_F1_V1.6.0DriversCMSISDeviceSTSTM32F1xxInclude 将里面的 3 个文

stm32f4xx.h,system_stm32f4xx.h 和 stm32f411xe.h 复制到 USER 目录之下,这个目录下面

有好几个文件夹,如下图 3.3.1.10,我们需要从 Src 和 Inc 文件夹下面复制我们需要的文件到

USER 目录。

图 3.3.1.10 固件库包 Template 目录下面文件一览

首先我们打开 Inc 目录,将目录下面的 3 个头文件 stm32f4xx_it.h,stm32f4xx_hal_conf.h

和 main.h 全部复制到 USER 目录下面,然后我们打开 Src 目录,将下面的四个源文件

system_stm32f4xx.c,stm32f4xx_it.c,stm32f4xx_hal_msp.cmain.c 同样全部复制到 USER

目录下面。相关文件复制到 USER 目录之后,USER 目录文件如下图 3.3.1.11:

图 3.3.1.11 USER 目录文件浏览

7)前面 6 个步骤,我们将需要的文件复制到了我们的工程目录下面了。接下来,我们还需

要复制 ALIENTEK 编写的 SYSTEM 文件夹内容到工程目录中。首先,我们需要解释一下,这

个 SYSTEM 文件夹内容是 ALIENTEK 为开发板用户编写的一套非常实用的函数库,比如系统

时钟初始化,串口打印,延时函数等,这些函数被很多工程师运用到自己的工程项目中。当然,

大家也可以根据自己需求决定是否需要 SYSTEM 文件夹,对于 STM32F411 的工程模板,如果

没有加入 SYSTEM 文件夹,那么大家需要自己定义系统时钟初始化。SYSTEM 文件夹对于库

函数版本程序和寄存器版本程序是有所区别的,这里我们新建的是库函数工程模板,所以大家

从光盘程序源码目录之下的库函数版本的任何一个实验中复制过来即可。这里我们打开光盘的

“4,程序源码标准例程-HAL 库函数版本实验 0-1 Template 工程模板-新建工程章节使用”工

程目录,从里面复制 SYSTEM 文件夹到我们的 Template 工程模板目录即可。操作过程如下图

3.3.1.12 和图 3.3.1.12 所示:

图 3.3.1.12 复制实验 0-1 的 SYSTEM 文件夹到工程根目录

图 3.3.1.13 复制 SYSTEM 文件夹之后的 Template 根目录文件夹结构

到这里,工程模板所需要的所有文件都已经复制进去。接下来,我们将在 MDK 中将这些

文件添加到工程。

8)下面我们将前面复制过来的文件加入我们的工程中。右键点击 Target1,选择 Manage

Project Items,如下图 3.3.1.14 所示:

图 3.3.1.14 点击 Management Project Itmes

9)Project Targets 一栏,我们将 Target 名字修改为 Template,然后在 Groups 一栏删掉一个

Source Group1,建立四个 Groups:USER,SYSTEM,CORE,和 HALLIB。然后点击 OK,可

以看到我们的 Target 名字以及 Groups 情况如下图 3.3.1.15 和图 3.3.1.16 所示:

图 3.3.1.15 新建 Group

图 3.3.1.16 查看工程 Group 情况

10)下面我们往 Group 里面添加我们需要的文件。我们按照步骤 9 的方法,右键点击

Tempate,选择 Manage Project Items 然后选择需要添加文件的 Group,这里第一步我们选择

HALLIB,然后点击右边的 Add Files,定位到我们刚才建立的目录HALLIBSrc 下面,将里面

所有的文件选中(Crtl+A),然后点击 Add,然后 Close,可以看到 Files 列表下面包含我们添

加的文件,如下图 3.3.1.17。这里需要说明一下,对于我们写代码,如果我们只用到了其中的某

个外设,我们就可以不用添加没有用到的外设的库文件。例如我只用 GPIO,我可以只用添加

stm32f4xx_gpio.c 而其他外设相关的可以不用添加。这里我们全部添加进来是为了后面的方便,

不用每次添加,当然这样的坏处是工程太大,编译起来速度慢,用户可以自行选择。

图 3.3.1.17 添加文件到 HALLIB 分组

这 里 有 几 个 文 件 比 较 特 殊 , 例 如 stm32f4xx_hal_msp_template.c 、

stm32f4xx_hal_timebase_rtc_alarm_template.c、stm32f4xx_hal_timebase_rtc_wakeup_template.c 和

stm32f1xx_hal_timebase_tim_template.c 四个文件不需要引入工程,

stm32f4xx_hal_msp_template.c

文 件 内 容 是 一 些 空 函 数 , 而 stm32f4xx_hal_timebase_rtc_alarm_template.c 、

stm32f4xx_hal_timebase_rtc_wakeup_template.c 和 stm32f4xx_hal_timebase_tim_template.c 是一些

例子,一般也不需要引入。删除某个方法如下图 3.3.1.18 所示:

图 3.3.1.18 删掉 HALLIB 分组中不需要的源文件

使 用 同 样 的 方 法 删 除 文 件 stm32f4xx_hal_timebase_rtc_alarm_template.c 、

stm32f4xx_hal_timebase_rtc_wakeup_template.c 和 stm32f4xx_hal_timebase_tim_template.c 即可。

11)用上面同样的方法,将 Group 定位到 CORE,USER 和 SYSTEM 分组之下,添加需要

的文件。CORE 分组下面需要添加的文件为一些头文件以及启动文件 startup_stm32f411xe.s(注

意,默认添加的时候文件类型为.c,添加.h 头文件和 startup_stm32f411xe.s 启动文件的时候,你

需要选择文件类型为 All files 才能看得到这些文件)。USER 分组下面需要添加的文件 USER

目录下面所有的.c 文件:main.c,stm32f4xx_hal.msp,stm32f4xx_it.c 和 system_stm32f4xx.c 四

个文件。SYSTEM 分组下面需要添加 SYSTEM 文件夹下所有子文件夹内的.c 文件,包括 sys.c,

usart.c 和 delay.c 三个源文件。添加完必要的文件到工程之后,最后点击 OK,回到工程主界面。

操作过程如下图 3.3.1.19~3.3.1.22:

图 3.3.1.19 添加文件到 USER 分组

图 3.3.1.20 文件添加到 USER 分组完成

使用同样的方法,选中 CORE 分组,点击 Add Files 按钮,添加需要的文件到 CORE 分组。

图 3.3.1.21 添加.h 头文件和启动文件到 CORE 分组

图 3.3.1.22 添加启动文件和头文件到 CORE 分组完成

最后添加文件到 SYSTEM 分组,这里需要注意,SYSTEM 文件夹包括三个子文件夹 sys,

delay 和 usart。在添加文件的时候,需要分别定为到三个子文件夹内部,依次添加下面的.c 文

件即可。添加完成后如下图 3.3.1.23 所示:

图 3.3.1.23 添加文件到 SYSTEM 分组

添加完所有文件到工程中之后,我们点击 OK 按钮,回到 MDK 工程主界面,如下图 3.3.1.24

所示:

图 3.3.1.24 工程分组情况

12)接下来我们要在 MDK 里面设置头文件夹存放路径。也就是告诉 MDK 到那些目录下

面去寻找包含了的头文件。这一步骤非常重要。如果没有设置头文件路径,那么工程会出现报

错文件路劲找不到。具体操作如下图 3.3.1.25 和 3.3.1.26 所示,5 步之后添加相应的头文件路径。

图 3.3.1.26 添加头文件路径到 PATH

这里大家需要注意,这里添加的路劲必须添加到头文件所在目录的最后一集。比如在

SYSTEM 文件夹下面有三个子文件下面都有.h 头文件,这些头文件在工程中都需要使用到,所

以我们必须将这三个目录都包含进来。这里我们的需要添加的头文件路径包括:CORE,

USER,SYSTEMdelay,SYSTEM甥慳瑲,SYSTEMsys 以及HALLIBInc。这里还需要提醒大

家,HAL 库存放头文件子目录是HALLIBInc,不是 HALLIBSrc,其次很多朋友都是这样弄错

导致报很多奇怪的错误。添加完成之后如下图 3.3.1.27 所示。

图 3.3.1.27 添加头文件路径

13)接下来对于 STM32F411 系列的工程,还需要添加全局宏定义标识符,所谓的全局宏

定义标识符,就是在工程中任何地方都可见。添加方法是点击魔术棒之后,进入 C/C++选项卡,

然后在 Define 输入框连输入:USE_HAL_DRIVER,STM32F411xE。注意这里是三个标识符

USE_HAL_DRIVER、STM32F411xE,他们之间是用逗号隔开的,请大家注意,这个字符串大

家可以直接打开我们光盘的新建好工程模板,从里面复制。另外部分库文件使用了 C99 模式,

在 Misc Controls 栏输入:--C99(注意有两条“-”线),模板存放目录为:4,程序源码标准

例程-HAL 库函数版本实验 0-1Template 工程模板-新建工程章节使用。本步骤操作过程如下图

3.3.1.28 所示:

图 3.3.1.28 添加全局宏定义标识符

14)接下来我们要编译工程,在编译之前我们首先要选择编译中间文件编译后存放目录。

前面我们讲过,MDK 默认编译后的中间文件存放目录为 USER 目录下面的 Listings 和 Objects

子目录,这里为了和我们 ALIENTEK 工程结构保持一致,我们重新选择存放到目录 OBJ 目录

之下。操作方法是点击魔术棒

,然后选择“Output”选项下面的“Select folder for objects...”,

然后选择目录为我们上面新建的 OBJ 目录,然后依次点击 OK 即可。操作过程如下图 3.3.1.29

和 3.3.1.30 所示

图 3.3.1.29 点击按钮“Select Folder for Objects…

图 3.3.1.30 选择 OBJ 目录为中间文件存放目录

选择完 OBJ 目录为编译中间文件存放目录之后,点击 OK 回到 Oueput 选项卡。这里我们

还有勾上“Create HEX File”选项和 Browse Information 选项。Create HEX File 选项选上是要求

编译之后生成 HEX 文件。而 Browse Information 选项选上是方便我们查看工程中的一些函数变

量定义等。具体操作方法如下图 3.3.1.31 所示:

图 3.3.1.31 勾选上 Create HEX file 和 Browse Information 选项

15)接下来在编译之前,我们先把 main.c 文件里面的内容替换为如下内容:

void Delay(__IO uint32_t nCount);

void Delay(__IO uint32_t nCount)

{

while(nCount--){}

}

int main(void)

{

GPIO_InitTypeDef GPIO_Initure;

HAL_Init();

//初始化 HAL 库

Stm32_Clock_Init(96,4,2,4);

//设置时钟,96Mhz

__HAL_RCC_GPIOC_CLK_ENABLE();

//开启 GPIOC 时钟

GPIO_Initure.Pin=GPIO_PIN_0|GPIO_PIN_1;

//PC0、PC1

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

GPIO_Initure.Pull=GPIO_PULLUP;

//上拉

GPIO_Initure.Speed= GPIO_SPEED_HIGH;

//高速

HAL_GPIO_Init(GPIOC,&GPIO_Initure);

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);

}

}

上面这段代码,大家如果不方便自己编写,可以直接打开我们光盘库函数源码目录“4,程

序源码标准例程-库函数版本实验 0-1 Template 工程模块-新建工程章节使用”,找到我们已经

新建好的工程模板 USER 目录下面 main.c 文件,直接复制过来即可。

16)下面我们点击编译按钮

编译工程,编程没错,如图 3.3.1.32 所示:

图 3.3.1.32 编译工程错误

17)到这里,一个基于 HAL 库的工程模板就建立完成。大家可以参考后面我们的 3.4 小节

的内容,将代码下载到开发板,会发现两个 led 灯不停的闪烁现象。

18)这里还一个地方需要大家修改一下,那就是关于系统初始化之后的中断优先级分组组

号的设置。默认情况下调用 HAL 初始化函数 HAL_Init 之后,会设置分组为组 4,这里我们正

点原子所有实验使用的是分组 2,所以我们修改 HAL_Init 函数内部,重新设置分组为组 2 即可。

具体方法是:打开 HALLIB 分组之下的 stm32f1xx_hal.c 文件,搜索函数 HAL_Init,找到函数

体,里面默认有这样一行代码:

HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);

我们将入口参数 NVIC_PRIORITYGROUP_4 修改为 NVIC_PRIORITYGROUP_2 即可。关

于中断优先级分组相关知识请参考本手册 4.5 小节即可。

3.3.2 工程模板解读

上一节,我们新建了一个基于 HAL 库的 STM32F411 工程模板,本节,我们将给大家讲解

工程模板的一些关键文件的作用以及整个工程模板程序运行流程。通过对本节内容的学习,大

家将对 STM32F411 工程有一个比较全面的了解,为后面实验学习打下良好的基础。

3.3.2.1 关键文件介绍

在讲解之前我们需要说明一点,任何一个 MDK 工程,不管它有多复杂,无非就是一些.c

源文件和.h 文件,还有一些类似.s 的驱动文件或者 lib 文件等。在工程中,他们通过各种包含关

系组织在一起,被我们用户代码最终调用或者引用。所以我们必须了解这些文件的作用以及他

们之间的包含关系,从而理解这个工程的运行流程,这样我们才能在项目开发中得心应手。

1) HAL 库关键文件介绍如下表 3.3.2.1 所示:

表 3.3.2.1 HAL 库文件介绍

2)stm32f4xx_it.c/stm32f4xx_it.h 文件

这两个文件非常简单,也非常好理解。stm32f4xx_it.h 中主要是一些中断服务函数的声明。

stm32f4xx_it.h 中是这些中断服务函数定义,而这些函数定义除了 Systick 中断服务函数

SysTick_Handler 外基本都是空函数,没有任何控制逻辑。一般情况下,我们可以去掉这两个文

件,然后把中断服务函数写在工程中的任何一个可见文件中。

3)stm32f4xx.h 头文件

头文件 stm32f4xx.h 内容看似非常少,却非常重要,它是所有 stm32f4 系列的顶层头文件。

使用 STM32F4 任何型号的芯片,都需要包含这个头文件。同时,因为 stm32f4 系列芯片型号非

常多,ST 定义了一个头文件 stm32f411xe.h,然后 stm32f4xx.h 顶层头文件会根据工程芯片型号,

来选择包含对应芯片的片上外设访问层头文件。我们可以打开 stm32f4xx.h 头文件可以看到,

里面有如下几行代码:

#if defined(STM32F405xx)

#include "stm32f405xx.h"

...

#elif defined(STM32F411xE)

#include "stm32f411xe.h"

#else

#error "Please select first the target STM32F4xx device used in your application

(in stm32f4xx.h file)"

#endif

这几行代码非常好理解,我们以 STM32F411RCT6 为例,如果定义了宏定义标识符

STM32F411xE,那么头文件 stm32f4xx.h 将会包含头文件 stm32f411xe.h。实际上,在我们上一

节新建工程的时候,我们在 C/C++选项卡里面输入的全局宏定义标识符中就包含了标识符

STM32F411xE(请参考 3.3.1.28)。所以头文件 stm32f411xe.h 一定会被整个工程所引用。

4)stm32f411xe.h 头文件

根据前面的讲解,stm32f411xe.h 是 STM32F411RCT6 芯片外设访问层头文件,只要我们进

行 STM32F411RCT6 开发,就必然使用到该文件。打开该文件我们可以看到里面主要是一些结

构体和宏定义标识符。这个文件的主要作用是寄存器定义声明以及封装内存操作。在后面寄存

器地址名称映射分析小节我们会给大家详细讲解。

5)system_stm32f4xx.c/system_stm32f4xx.h 文件

头文件 system_stm32f4xx.h 和源文件 system_stm32f4xx.c 主要是声明和定义了系统初始化

函数 SystemInit 以及系统时钟更新函数 SystemCoreClockUpdate。SystemInit 函数的作用是进行

时钟系统的一些初始化操作以及中断向量表偏移地址设置,但它并没有设置具体的时钟值,这

是与标准库的最大区别,在使用标准库的时候,SystemInit 函数会帮我们配置好系统时钟配置

相关的各个寄存器。在启动文件 startup_stm32f411xe.s 中会设置系统复位后,直接调用 SystemInit

函数进行系统初始化。SystemCoreClockUpdate 函数是在系统时钟配置进行修改后,调用这个函

数来更新全局变量 SystemCoreClock 的值,变量 SystemCoreClock 是一个全局变量,开放这个

变量可以方便我们在用户代码中直接使用这个变量来进行一些时钟运算。

6)stm32f4xx_hal_msp.c 文件

MSP,全称为 MCU support package,关于怎么理解 MSP,我们后面在讲解程序运行流程

的时候会给大家举例详细讲解,这里大家只要知道,函数名字中带有 MspInit 函数,他们的作

用是进行 MCU 级别硬件初始化设置,并且它们通常会被上一层的初始化函数所调用,这样做

的目的是为了把 MCU 相关的硬件初始化剥夺出来,方便用户代码在不同型号的 MCU 上移植。

stm32f4xx_hal_msp.c 文件定义了两个函数 HAL_MspInit 和 HAL_MspDeInit。这两个函数分别被

文件 stm32f4xx_hal.c 中的 HAL_Init 和 HAL_DeInit 所调用。HAL_MspInit 函数的主要作用是进

行 MCU 相关的硬件初始化操作。例如我们要初始化某些硬件,我们可以硬件相关的初始化配

置写在 HAL_MspDeinit 函数中。这样的话,在系统启动后调用了 HAL_Init 之后,会自动调用

硬件初始化函数。实际上,我们在工程模板中直接删掉 stm32f4xx_hal_msp.c 文件也不会对程序

运行产生任何影响。对于这个文件存在的意义,我们在后面讲解完程序运行流程之后,大家会

有更加清晰的理解。

7)startup_stm32f411xe.s 启动文件

STM32 系列所有芯片工程都会有一个.s 启动文件。对于不同型号的 stm32 芯片启动文件也

是不一样的。我们的开发板主芯片是 STM32F411RCT6,所以我们需要使用与之对应的启动文

件 startup_stm32f411xe.s。启动文件的作用主要是进行堆栈初始化,中断向量表以及中断函数定

义等。启动文件有一个很重要的作用就是系统复位后引导进入 main 函数。打开启动文件

startup_stm32f411xe.s,可以看到下面几行代码:

; Reset handler

Reset_Handler

PROC

EXPORT Reset_Handler

[WEAK]

IMPORT SystemInit

IMPORT __main

LDR

R0, =SystemInit

BLX

R0

LDR

R0, =__main

BX

R0

ENDP

Reset_Handler 在我们系统启动的时候会执行,这几行代码的作用是在系统启动之后,首先

调用 SystemInit 函数进行系统初始化,然后引导进入 main 函数执行用户代码。

接下来我们看看 HAL 库工程模板中各个文件之前的包含关系,如下图 3.3.2.2 所示

图 3.3.2.2 HAL 库工程文件包含关系

从上面工程文件包括关系图可以看出,顶层头文件 stm32f4xx.h 直接或间接包含了其他所

有工程必要头文件,所以在我们的用户代码中,我们只需要包含顶层头文件 stm32f4xx.h 即可。

这里我们还需要说明一下,在我们 ALIENTEK 提供的 SYSTEM 文件夹内部的 sys.h 头文件中,

我们默认包含了 stm32f4xx.h 头文件,所以在我们用户代码中,只需要包含 sys.h 头文件即可,

当然也可以直接包含顶层头文件 stm32f4xx.h。关于工程模块中关键文件内容我们就给大家介绍

到这里。

3.3.2.2 HAL 库中__weak 修饰符讲解

在 HAL 库中,很多回调函数前面使用__weak 修饰符,这里我们有必要给大家讲解__weak

修饰符的作用。

weak 顾名思义是“弱”的意思,所以如果函数名称前面加上__weak 修饰符,我们一般称

这个函数为“弱函数”。加上__weak 修饰符的函数,用户可以在用户文件中重新定义一个同名

函数,最终编译器编译的时候,会选择用户定义的函数,如果用户没有重新定义这个函数,那

么编译器就会执行__weak 声明的函数,并且编译器不会报错。

这里我给大家举个例子来加深大家的理解。比如我们打开工程模板,找到并打开文件

stm32f4xx_hal.c 文件,里面定义了一个函数 HAL_MspInit,定义如下:

__weak void HAL_MspInit(void)

{

__IO uint32_t tmpreg = 0x00;

UNUSED(tmpreg);

}

大家可以看出,HAL_MspInit 函数前面有加修饰符__weak。同时,在该文件的前面有定义

函数 HAL_Init,并且 HAL_Init 函数中调用了函数 HAL_MspInit。

HAL_StatusTypeDefHAL_Init(void)

{

...//此处省略部分代码

HAL_MspInit();

return HAL_OK;

}

如果我们没有在工程中其他地方重新定义 HAL_MspInit()函数,那么 HAL_Init 初始化函数

执行的时候,会默认执行 stm32f4xx_hal.c 文件中定义的 HAL_MspInit 函数,而这个函数没有任

何控制逻辑。如果用户在工程中重新定义函数 HAL_MspInit,那么调用 HAL_Init 之后,会执行

用户自己的 HAL_MspInit 函数而不会执行 stm32f4xx_hal.c 默认定义的函数。也就是说,表面上

我们看到函数 HAL_MspInit 被定义了两次,但是因为有一次定义是弱函数,使用了__weak 修

饰符,所以编译器不会报错。

__weak 在回调函数的时候经常用到。这样的好处是,系统默认定义了一个空的回调函数,

保证编译器不会报错。同时,如果用户自己要定义用户回调函数,那么只需要重新定义即可,

不需要考虑函数重复定义的问题,使用非常方便,在 HAL 库中__weak 关键字被广泛使用。

韦东山 IMX6ULL和正点原子_「正点原子NANO STM32开发板资料连载」第三章 MDK5 软件入门1...相关推荐

  1. stm32看门狗_「正点原子NANO STM32开发板资料连载」第十一章 独立看门狗实验

    1)实验平台:ALIENTEK NANO STM32F411 V1开发板2)摘自<正点原子STM32F4 开发指南(HAL 库版>关注官方微信号公众号,获取更多资料:正点原子 第十一章 独 ...

  2. 嵌入式实时操作系统ucos-ii_「正点原子NANO STM32开发板资料连载」第三十六章 UCOSII 实验 1任务调度...

    1)实验平台:alientek NANO STM32F411 V1开发板2)摘自<正点原子STM32F4 开发指南(HAL 库版>关注官方微信号公众号,获取更多资料:正点原子 第三十六章 ...

  3. 嵌入式实时操作系统ucos-ii_「正点原子NANO STM32开发板资料连载」第三十八章 UCOSII 实验 3...

    1)实验平台:alientek NANO STM32F411 V1开发板2)摘自<正点原子STM32F4 开发指南(HAL 库版>关注官方微信号公众号,获取更多资料:正点原子 第三十八章 ...

  4. 看门狗寄存器c语言代码_「正点原子NANO STM32F103开发板资料连载」第十一章 看门狗实验...

    1)实验平台:[正点原子] NANO STM32F103 开发板 2)摘自<正点原子STM32 F1 开发指南(NANO 板-HAL 库版)>关注官方微信号公众号,获取更多资料:正点原子 ...

  5. 判断按键值_「正点原子NANO STM32开发板资料连载」第十六章电容触摸按键实验...

    1)实验平台:ALIENTEK NANO STM32F411 V1开发板2)摘自<正点原子STM32F4 开发指南(HAL 库版>关注官方微信号公众号,获取更多资料:正点原子 第十六章电容 ...

  6. hal库开启中断关中断_「正点原子NANO STM32开发板资料连载」第十章 外部中断实验...

    1)实验平台:ALIENTEK NANO STM32F411 V1开发板 2)摘自<正点原子STM32F4 开发指南(HAL 库版>关注官方微信号公众号,获取更多资料:正点原子 第十章 外 ...

  7. dev c++怎么调试_「正点原子NANO STM32开发板资料连载」第十八章 USMART 调试组件...

    1)实验平台:ALIENTEK NANO STM32F411 V1开发板2)摘自<正点原子STM32F4 开发指南(HAL 库版>关注官方微信号公众号,获取更多资料:正点原子 第十八章 U ...

  8. stm32 文件系统dma大小_「正点原子NANO STM32F103开发板资料连载」第二十二章 DMA 实验...

    1)实验平台:[正点原子] NANO STM32F103 开发板 2)摘自<正点原子STM32 F1 开发指南(NANO 板-HAL 库版)>关注官方微信号公众号,获取更多资料:正点原子 ...

  9. stm32 读取sd卡图片显示_「正点原子STM32Mini板资料连载」第三十五章 汉字显示实验...

    1)实验平台:正点原子STM32mini开发板 2)摘自<正点原子STM32 不完全手册(HAL 库版)>关注官方微信号公众号,获取更多资料:正点原子 第三十五章 汉字显示实验 汉字显示在 ...

最新文章

  1. 2019 AI Index 报告出炉:AI 领域取得的进展很多,但结果忧喜参半
  2. Mac和 iOS 下的对称和非对称加密算法的使用
  3. 路由器 VS OSI七层模型
  4. BASE64Encoder及BASE64Decoder的正确用法
  5. JVM内存分配策略原
  6. 用Scala实现简单的Web和API服务器
  7. 【原】无脑操作:ElasticSearch学习笔记(01)
  8. 高通HAL层之Sensor HAL
  9. myeclipse中删除tomcat 的server后,重新添加进来的方法
  10. Java注释以及快捷键
  11. 使用pytorch中预训练模型VGG19获取图像特征,得到图像embedding
  12. 存储数据使用数据库而不用EXCEL
  13. 地理信息安全在线培训考试系统题库-单选题
  14. java操作mysql临时表_MySQL 临时表
  15. webscraper多页爬取_Web Scraper 翻页——抓取「滚动加载」类型网页(Web Scraper 高级用法)| 简易数据分析 10...
  16. 八字算命网站源码技术细节解析:使用PHP和JAVA实现的MVC架构、MySQL数据库设计和功能模块介绍
  17. MySQL怎样通过Adjacency List存储树形结构?
  18. 机器学习之变分推断(三)基于平均场假设变分推断与广义EM
  19. win10 c 语言 全屏,win10所有的视频都不能全屏了,重装了好几次都是这样 ,求助 - Microsoft Community...
  20. 求n阶方阵里所有数的和

热门文章

  1. 在Linux中卸载Refind
  2. C++ 工程实践:避免使用虚函数作为库的接口
  3. 在金融行业中,直播获客应该怎么做呢?
  4. Digital Vision Phoenix 2019(凤凰电影修复软件)官方正式版V2019.1 R2 | 数字电影修复软件下载 | 含Digital Vision Phoenix安装教程
  5. mysql 恢复sql文件
  6. 说说对React refs 的理解?应用场景?
  7. 关闭应用右下角的盾牌图标
  8. 4.13黄金多空博弈,白银TD最新操作
  9. Linux-查询登入用户信息
  10. webstrom主题、字体设置