ART-PI 示例代码使用 HAL 库过程

前面有一篇文章《刚到的 ART-Pi 开发板点灯总结》 已经使用 ART-PI 开发板跑起了第一个示例程序,对程序的执行过程也做了简单分析,但是对于我这样的入门新手还是有很多让人迷惑的问题:

  • 这些库和系统是怎么结合并运行的?
  • 这些文件是从哪里来的?
  • 那些是可以使用 CubeMX 直接生成的?
  • 那些是需要后续开发中进行配置的?

要搞清楚这些问题,我觉得还是继续使用点灯程序 sdk-bsp-stm32h750-realthread-artpi\projects\art_pi_blink_led 来寻找答案。

首先,打开工程目录下的 art_pi_blink_led\board\CubeMX_Config\CubeMX_Config.ioc 此项目的 CubeMX 工程(请先安装 CubeMX)。

什么也别做,直接点击 GENERATE CODE 按钮,生成 HAL 库代码和相关驱动和示例代码。

上面绿色的文件夹是新生成的,而橙色的文件夹是有过修改,那么我们就来逐个分析一下有什么变化,而这些文件在此工程中被放到了哪里。

展开此目录中的文件,如下图:

此目录中的 stm32h7xx_hal_conf.h 文件是 HAL 库的一个配置文件,这个文件尤为重要,要弄清楚这些配置和文件之间的关系,我们需要了解系统的启动过程。

启动过程

首先启动的是 startup_stm32h750xx.s 文件,此文件位于 board\CubeMX_Config\MDK-ARM\startup_stm32h750xx.s 是刚才新生成的,事实上我们工程中已经有了此文件,只不过将该文件放到了 libraries 目录而已。

startup_stm32h750xx.s

此文件中做了一些对 栈和堆 内存的配置,定义了一些 中断向量。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
Stack_Size		EQU     0x400

                AREA    STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem       SPACE   Stack_Size
__initial_sp


; <h> Heap Configuration
;   <o>  Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>

Heap_Size      EQU     0x200

                AREA    HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem        SPACE   Heap_Size
__heap_limit

我们目前只关心和启动过程有关的配置,在此文件中还启动了两个函数,一个是 SystemInit 另一个是 _main.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
; Reset handler
Reset_Handler    PROC
                 EXPORT  Reset_Handler                    [WEAK]
        IMPORT  SystemInit
        IMPORT  __main

                 LDR     R0, =SystemInit
                 BLX     R0
                 LDR     R0, =__main
                 BX      R0
                 ENDP

system_stm32h7xx.c

而上面的 SystemInit 则是在 stm32h7xx.h 中定义在 system_stm32h7xx.c 实现的,这个函数则是做了最基本的处理器的系统初始化工作,FPU (浮点运算处理器)设置和向量表的配置。

system_stm32h7xx.c 中提供了两个函数和一个全局变量。

  • SystemInit(): 此函数在 main 函数之前调用,做一些硬件抽象层初始化工作。

  • SystemCoreClock variable:包含核心时钟,用户应用程序可使用它设置 SysTick 计时器或配置其他参数。

  • SystemCoreClockUpdate():更新系统核心时钟变量,只在程序执行期间,当核心时钟更改时调用该变量。

stm32h7xx.h

在该头文件中引入了两个头文件,分别是 stm32h7xx_hal.hstm32h750xx.h.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
/** @addtogroup Device_Included
  * @{
  */

#if defined(STM32H743xx)
  #include "stm32h743xx.h"
#elif defined(STM32H753xx)
  #include "stm32h753xx.h"
#elif defined(STM32H750xx)
  #include "stm32h750xx.h"
#elif defined(STM32H742xx)
  #include "stm32h742xx.h"
#elif defined(STM32H745xx)
//....
1
2
3
#if defined (USE_HAL_DRIVER)
 #include "stm32h7xx_hal.h"
#endif /* USE_HAL_DRIVER */

而宏定义 STM32H743xxUSE_HAL_DRIVER 已经有所定义在配置文件 rtconfig_preinc.h 中:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13

#ifndef RTCONFIG_PREINC_H__
#define RTCONFIG_PREINC_H__

/* Automatically generated file; DO NOT EDIT. */
/* RT-Thread pre-include file */

#define HAVE_CCONFIG_H
#define RT_USING_NEWLIB
#define STM32H750xx
#define USE_HAL_DRIVER

#endif /*RTCONFIG_PREINC_H__*/

stm32h750xx.h

此文件中包含如下内容:

  • 此系列外设的数据结构和地址映射。
  • 此系列外围设备的寄存器声明和位定义。
  • 访问外设寄存器硬件的宏。

另外此文件内还引入了头文件 system_stm32h7xx.h. 此文件正是 system_stm32h7xx.c 中提供的两个函数和一个全局变量的定义头文件。

stm32h7xx_hal.h

此头文件位于 board\CubeMX_Config\Drivers\STM32H7xx_HAL_Driver 内,这个文件包含HAL的所有硬件抽象层函数原型,并按照模块分开。这里尤其要注意的是配置文件 stm32h7xx_hal_conf.h 就是在此文件中引入的。

1
2
/* Includes ------------------------------------------------------------------*/
#include "stm32h7xx_hal_conf.h"

最后我梳理了一个流程图简单总结一下这个过程吧:

STM32 HAL 库在启动流程中的关系 STM32 HAL 库在启动流程中的关系

这个图可能部分地方绘制的有些不足,但是从我的角度已经可以将 CubeMX 生成的文件串到启动过程中,足以说明每个 HAL 文件在何时被调用。

驱动接管

HAL 和 RT-Thread 内核之间还有一个桥梁就是 BSP (板级支持包)和 libcpu 如下图所示:

其中 libcpu 部分已经在 RT-Thread 系统中涵盖, 而 bsp 部分就是需要针对不同的开发板的硬件电路进行修改的地方,官方也会为不同系列提供一套默认驱动。

这样一来我们只需要修改 CubeMX 的配置和部分配置文件而已。

总结

对于整个过程一个粗略的分析后得出一个结论,基于 RT-Thread 开发的开发人员只需要了解 RT-Thread 的相关内核函数的使用和软件包的使用就可以完成很多复杂的开发,这就是所谓站在别人的肩膀上,我们只需要关心业务逻辑本身而不是重复造轮子。

RT-Thread 开发框架结构 RT-Thread 开发框架结构

从理论上来说作为应用开发的开发者只需要关系官方的内核文档和会使用 menuconfig 去使用提供的 package 包即可,剩下的就是基于这些库的业务逻辑的实现了。

RT-Thread 内核 RT-Thread 内核

RT-Thread online packages RT-Thread online packages