6. HAL固件库简介以及STM32CubeIDE工程结构

本章参考资料:《dm00327659-stm32mp157-advanced-armbased-32bit-mpus-stmicroelectronics》参考手册、 《ARM Cortex-M3与Cortex-M4权威指南(第3版)》, STM32 HAL库帮助文档。

STM32的外设功能强大,通常有十几个乃至数十个寄存器来配置以及控制一个外设, ST公司提供的HAL软件库,包含了STM32芯片所有寄存器的控制操作, 我们学习如何使用ST的HAL库,会极大地方便控制STM32芯片。

6.1. CMSIS标准及库层次关系

因为基于Cortex-M系列芯片采用的内核都是相同的,区别主要为核外的片上外设的差异,这些差异却导致软件在同内核, 不同外设的芯片上移植困难。为了解决不同的芯片厂商生产的Cortex-M微控制器软件的兼容性问题, ARM与芯片厂商建立了CMSIS标准(Cortex MicroController Software Interface Standard)。

所谓CMSIS标准,实际是新建了一个软件抽象层。见 CMSIS架构

图 5‑1 CMSIS架构

CMSIS标准中最主要的为CMSIS核心层,它包括了:

  • 内核函数层:其中包含用于访问内核寄存器的名称、地址定义,主要由ARM公司提供。

  • 设备外设访问层:提供了片上的核外外设的地址和中断定义,主要由芯片生产商提供。

可见CMSIS层位于硬件层与操作系统或用户层之间,提供了与芯片生产商无关的硬件抽象层, 可以为接口外设、实时操作系统提供简单的处理器软件接口,屏蔽了硬件差异,这对软件的移植是有极大的好处的。 STM32的库,就是按照CMSIS标准建立的。

6.2. 库目录、文件简介

在前面的章节中我们使用STM32CubeIDE生成了一个工程,其目录结构如下所示:

图 5‑2 STM32HAL库

目录:.\00NewProject\

  • CA7 :该文件夹下生成的文件是主要是设备树文件,包括了kernel、tf-a、u-boot可能会用到的文件。

  • CM4 :M4的整个工程文件,对M4内核的开发基本上都是在这个文件中。

  • Common目录 : 这个目录下总的只有一个system_stm32MP1xx.c文件, 含了STM32 M4内核上电后初始化系统时钟、扩展外部存储器用的函数。如SystemInit函数等。

  • Drivers :文件夹下是官方的CMSIS库,HAL库,板载外设驱动。

  • .project :整个STM32CubeIDE工程的入口。

  • 00NewProject.ioc:STM32CubeMX工程的入口

在使用HAL开发的时候,主要就是利用Drivers\STM32MP1xx_HAL_Driver目录下文件提供的HAL库函数接口 实现操作外设相关功能。

6.2.1. Drivers目录

先看看 Drivers\CMSIS 文件夹。

.00NewProject\Drivers\CMSIS文件夹如下。

图 5‑3 CMSIS文件夹内容 目录:Drivers\CMSIS\

图 5‑3 CMSIS文件夹内容 目录:Drivers\CMSIS\

6.2.1.1. Drivers\CMSIS\Include目录

在Include文件夹中包含了的是位于CMSIS标准的核内设备函数层的Cortex-M核通用的头文件, 它们的作用是为那些采用Cortex-M核设计SOC的芯片商设计的芯片外设提供一个进入内核的接口, 定义了一些内核相关的寄存器。 这些文件在其它公司的Cortex-M系列芯片也是相同的。至于这些功能是怎样用源码实现的,可以不用管它, 我们只需要了解个大概即可,有兴趣的朋友可以深究,关于内核的寄存器说明, 需要查阅《cortex_M4_Technical Reference Manual》及《Cortex®-M4内核编程手册》文档, 《STM32MP15xxx参考手册》只包含片上外设说明,不包含内核寄存器。

较重要的是在core_CM4.c文件中包含了“stdint.h” 这个头文件,这是一个ANSI C 文件,是独立于处理器之外的,就像我们熟知的C语言头文件 “stdio.h” 文件一样。主要作用是提供一些类型定义。见 宏定义

代码清单5‑2:stdint.c文件中的类型定义

stdint.c
/* exact-width signed integer types */

typedef   signed          char int8_t;

typedef   signed short     int int16_t;

typedef   signed           int int32_t;

typedef   signed       __int64 int64_t;

/* exact-width unsigned integer types */

typedef unsigned          char uint8_t;

typedef unsigned short     int uint16_t;

typedef unsigned           int uint32_t;

typedef unsigned       __int64 uint64_t;

这些新类型定义屏蔽了在不同芯片平台时,出现的诸如int的大小是16位,还是32位的差异。 所以在我们以后的程序中,都将使用新类型如uint8_t、uint16_t等。

建议在以后的新程序中尽量使用uint8_t 、uint16_t类型的定义。

core_CM4.c跟启动文件一样都是底层文件,都是由ARM公司提供的,遵守CMSIS标准, 即所有CM4芯片的库都带有这个文件,这样软件在不同的CM4芯片的移植工作就得以简化。

6.2.1.2. Drivers\CMSIS\Device目录

在Device文件夹下的是具体芯片直接相关的文件,包含启动文件、芯片外设寄存器定义、 系统时钟初始化功能的一些文件,这是由ST公司提供的。

  • stm32mp1xx.h文件

文件目录:Drivers \CMSIS\Device\ST\stm32MP1xx\Include

stm32mp1xx.h 这个文件非常重要,是一个STM32芯片底层相关的文件。 包含了STM32中所有的外设寄存器地址和结构体类型定义, 在使用到STM32 HAL库的地方都要包含这个头文件。

CMSIS文件夹中的主要内容就是这样,接下来我们看看STM32MP1xx_HAL_Driver文件夹。

6.2.1.3. Drivers\STM32MP1xx_HAL_Driver文件夹

文件目录:Drivers\STM32MP1xx_HAL_Driver

进入Drivers目录下的STM32MP1xx_HAL_Driver文件夹,如下。

图 5‑4 外设驱动

STM32MP1xx_HAL_Driver文件夹下有inc(include的缩写)跟src(source的简写)这两个文件夹, 这里的文件属于CMSIS之外的的、芯片片上外设部分。src里面是每个设备外设的驱动源程序,inc则是相对应的外设头文件。 src及inc文件夹是ST的HAL库的主要内容,甚至不少人直接认为ST的HAL库就是指这些文件,可见其重要性。

在src和inc文件夹里的就是ST公司针对每个STM32外设而编写的库函数文件,每个外设对应一个 .c 和 .h 后缀的文件。 我们把这类外设文件统称为:stm32MP1xx_hal_ppp.c 或stm32MP1xx_hal_ppp.h文件,PPP表示外设名称。 这里并没有包含所以的库函数文件,STM32CubeIDE会根据我们用到什么外设而生成相对应的库函数文件。

图 5‑4驱动的源文件及头文件

6.2.2. CM4目录

图 5‑5CM4目录

Debug目录是编译及debug后生成的文件,其中包含有ELF二进制文件。Drivers目录是一个空目录, 需要重点关注的是Core这个目录,

6.2.2.1. CM4\Core\Inc目录

图 5‑6Inc目录

这个目录与我们工程配置息息相关,在工程中我们勾选了为每个外设生成独立的.c .h 文件,其中GPIO.h就是我们GPIO配置的 头文件,stm32mp1xx_it.h文件则是中断服务函数的声明,

stm32MP1xx_hal_conf.h:ST HAL库支持所有STM32MP1型号的芯片,但有的型号芯片外设功能比较多, 所以使用这个配置文件根据芯片型号增减ST库的外设文件,另外时钟源配置也是在这里进行设置。如下。

代码清单5‑3 stm32MP1xx_hal_conf.h文件配置软件库

stm32MP1xx_hal_conf.h
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/* Includes ---------------------------------*/
/**
* @brief Include module's header file
*/

#ifdef HAL_RCC_MODULE_ENABLED
#include "stm32mp1xx_hal_rcc.h"
#endif /* HAL_RCC_MODULE_ENABLED */

#ifdef HAL_EXTI_MODULE_ENABLED
#include "stm32mp1xx_hal_exti.h"
#endif /* HAL_EXTI_MODULE_ENABLED */

#ifdef HAL_GPIO_MODULE_ENABLED
#include "stm32mp1xx_hal_gpio.h"
#endif /* HAL_GPIO_MODULE_ENABLED */

#ifdef HAL_HSEM_MODULE_ENABLED
#include "stm32mp1xx_hal_hsem.h"
#endif /* HAL_HSEM_MODULE_ENABLED */

#ifdef HAL_DMA_MODULE_ENABLED
#include "stm32mp1xx_hal_dma.h"
#endif /* HAL_DMA_MODULE_ENABLED */

#ifdef HAL_MDMA_MODULE_ENABLED
#include "stm32mp1xx_hal_mdma.h"
#endif /* HAL_MDMA_MODULE_ENABLED */

#ifdef HAL_CORTEX_MODULE_ENABLED
#include "stm32mp1xx_hal_cortex.h"
#endif /* HAL_CORTEX_MODULE_ENABLED */

#ifdef HAL_ADC_MODULE_ENABLED
#include "stm32mp1xx_hal_adc.h"
#endif /* HAL_ADC_MODULE_ENABLED */

stm32MP1xx_hal_conf.h这个文件还可配置是否使用“断言”编译选项,如下。

代码清单 5‑4 断言配置

stm32MP1xx_hal_conf.h
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#ifdef  USE_FULL_ASSERT

/**
* @brief  The assert_param macro is used for  parameters check.
* @param  expr: If expr is false, it calls assert_failed function
*   which reports the name of the source file and the source
*   line number of the call that failed.
*   If expr is true, it returns no value.
* @retval None
*/
   #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__))
/* Exported functions ---------------------------------- */
   void assert_failed(uint8_t* file, uint32_t line);
#else
   #define assert_param(expr) ((void)0)
#endif /* USE_FULL_ASSERT */

在ST的HAL库函数中,一般会包含输入参数检查,即上述代码中的“assert_param”宏, 当参数不符合要求时,会调用“assert_failed”函数,这个函数默认是空的。

实际开发中使用断言时,先通过定义USE_FULL_ASSERT宏来使能断言,然后定义“assert_failed”函数, 通常我们会让它调用printf函数输出错误说明。使能断言后,程序运行时会检查函数的输入参数, 当软件经过测试,可发布时,会取消USE_FULL_ASSERT宏来去掉断言功能,使程序全速运行。

6.2.2.2. CM4\Core\Src目录

图 9‑7 Core\Src目录
  • stm32MP1xx_it.c 这个文件是专门用来编写中断服务函数的,在我们修改前,这个文件已经定义了一些系统异常(特殊中断)的接口, 使用STM32CubeIDE时会根据我们设置的中断生成相对应的中断服务函数入口,而中断服务函数的内容则需要我们自己去实现。

  • gpio.c 在使用STM32CubeIDE生成工程时,对于GPIO的相关初始化代码则都包含在里面。

  • main.c 对于这个文件想必大家都不陌生,我们的主函数就是在这个文件中,里面完成了我们配置工程的整个初始化,同样我们需要往 main.c文件中添加逻辑控制代码,实现相对应的逻辑控制。

6.2.2.3. CM4\Core\Startup目录

  • startup_stm32mp157aacx.s 启动文件,由汇编编写的启动文件,主要完成堆栈的设置,以及中断向量表的配置、跳转到C函数等功能

6.2.3. 库各文件间的关系

前面向大家简单介绍了各个工程目录的作用,使用STM32CubeIDE来配置工程时只需要重点关注CM4下的Core目录即可。

接下来从整体上把握一下各个文件在库工程中的层次或关系,这些文件对应到CMSIS标准架构上。

图 9‑8 库各文件关系

上图描述了STM32库各文件之间的调用关系,这个图省略了DSP核和实时系统层部分的文件关系。 在实际的使用库开发工程的过程中,我们把位于CMSIS层的文件包含进工程,除了特殊系统时钟需要修改system_stm32MP1xx.c, 其它文件丝毫不用修改,也不建议修改。

对于位于用户层的几个文件,就是我们在使用库的时候,针对不同的应用对库文件进行增删(用条件编译的方法增删)和改动的文件。

6.3. 使帮助文档

我坚信,授之以鱼不如授之以渔。官方资料是所有关于STM32知识的源头,所以在本小节介绍如何使用官方资料。 官方的帮助手册,是最好的教程,几乎包含了所有在开发过程中遇到的问题。这些资料已整理到了本书附录资料中。

6.3.1. 常用官方资料

  • 《dm00327659-stm32mp157-advanced-armbased-32bit-mpus-stmicroelectronics》参考手册

这个文件全方位介绍了STM32芯片的各种片上外设,它把STM32的时钟、存储器架构、及各种外设、寄存器都描述得清清楚楚。 当我们对STM32的外设感到困惑时,可查阅这个文档。以直接配置寄存器方式开发的话, 查阅这个文档寄存器部分的频率会相当高,但这样效率太低了。

  • 《ARM Cortex-M3与Cortex-M4权威指南(第3版)》。

详细讲解了Cortex内核的架构和特性,要深入了解Cortex-M内核, 这是首选,经典中的经典,而且还是中文版的,方便学习。