STM32单片机开发:HAL库开发模式
- STM32单片机开发
- 9天前
- 68热度
- 0评论
为什么不建议采用寄存器开发模式?
在前面的STM32实验中,我们采用了直接配置寄存器的方式(也称为寄存器开发模式)。配置的时候,我们需要查阅STM32单片机的技术手册和数据手册,了解寄存器的详情配置方式,看用到哪些配置位,这些都是很琐碎、机械的工作。使用寄存器开发方式直接操作内存单元,效率高,且对于硬件资源的控制更为灵活和精细。然而,在实际开发中,STM32单片机一般不采用纯粹的寄存器开发方式,这主要是由于以下几个原因:
开发速度慢:STM32单片机的外设资源丰富,寄存器数量和复杂度相对较高。如果直接操作寄存器,开发者需要深入了解每个寄存器的功能和地址,以及它们之间的关联和依赖关系。这大大增加了开发难度和时间成本。
程序可读性差:直接操作寄存器通常涉及大量的位操作和地址计算,代码难以阅读和维护。这对于团队协作和长期项目来说是一个巨大的挑战。
维护复杂:随着项目的推进和功能的增加,直接操作寄存器的代码会变得越来越复杂。修改或添加新功能时,很容易引入错误或遗漏。
移植性差:直接操作寄存器是在非常低的抽象层次上进行开发,这限制了代码的可移植性和复用性。当需要在不同型号的STM32单片机之间移植代码时,通常需要大量的修改工作。
什么是HAL库开发模式?
HAL库是ST公司针对STM32提供的函数库,这些库函数将底层寄存器的操作封装成了易于使用的API。开发者可以通过调用这些API来完成对硬件的控制,而无需直接操作寄存器。这种方式大大提高了开发效率、代码可读性和可维护性。
当我们调用库的API时,可以不用考虑库底层的寄存器操作,就像C语言的printf()函数一样,只需要它提供的功能,不需要研究的它的底层逻辑。HAL库是架设在寄存器与用户驱动层之间的代码,向下处理与寄存器直接相关的配置,向上为用户提供配置寄存器的接口。HAL库开发方式与直接配置寄存器开发方式区别见下图。
HAL库结构及层次关系
HAL库由核心组件(包括CMSIS层、HAL驱动文件、系统初始化文件等)、辅助组件(包括板载外设驱动、中间件、示例代码和工程模板等)以及配置文件(包括HAL库配置文件和启动文件等)构成。
HAL驱动包括STM32微控制器片内外设的HAL库驱动文件和初始化文件,提供了与外设进行通信的API,使得开发者可以通过调用这些API来控制外设,而无需直接操作寄存器。初始化文件负责系统的初始化工作,包括时钟配置、中断向量表设置等。
板载外设为特定开发板的外设提供的驱动程序,这些驱动程序使得开发者可以方便地控制开发板上的外设。
中间件为开发者提供了丰富的功能组件,如图形界面、音频处理、USB通信等。
HAL库还提供了针对官方发行demo板的例子和工程模板,以及针对MCU片内外设的简单例程(如GPIO、UART等)、针对单个中间件的应用例程和针对多个中间件的综合应用例程。
配置文件用于裁剪HAL库、配置晶振参数等,使得开发者可以根据实际需求对HAL库进行定制。
CMSIS(Cortex Microcontroller Software Interface Standard)是ARM和一些编译器厂家以及半导体厂家共同遵循的一套标准,由ARM提出,专门针对CORTEX-M系列。它位于硬件层与操作系统或用户层之间,提供了与芯片生产商无关的硬件抽象层,为接口外设、实时操作系统提供简单的处理器软件接口,屏蔽了硬件差异,有利于软件的移植。
CMSIS架构如下图所示。
CMSIS架构由用户代码、CMSIS层、MCU(硬件层)构成。CMSIS层内部包括CMSIS核心层、实时系统内核、设备外设函数、实时系统API、CMSIS-DSP构成。
CMSIS核心层主要定义了访问SIMD指令集的API、核内外设函数、访问内核寄存器名称、地址定义和中断向量。
设备外设函数由芯片厂商实现,负责对硬件寄存器地址及外设接口进行定义。
CMSIS-DSP提供了丰富的数学和信号处理函数,包括基本数学函数、快速数学函数、复数运算、滤波、矩阵操作、变换、电机控制、统计以及支持函数等。这些函数涵盖了大多数工程应用中的算法需求。
CMSIS实时系统API是ARM公司为RTOS内核制定的一套通用接口协议,提供了丰富的API和宏定义,涵盖了线程管理、互斥锁、信号量、消息队列等RTOS核心功能,方便开发者进行RTOS开发。
库目录和文件简介
应用STM32CubeIDE构建的工程并没有包含所有的HAL库文件,仅包含了必要的库文件。完整的HAL库存储在Repository目录,开发者可以配置该目录的存储路径。
设置步骤:
(1)打开STM32CubeIDE软件,依次点击Window→Preference。
(2)依次点击左侧STM32Cube→FirmwareUpdater,点击Browser选择想要存放库文件的文件夹。
HAL库主要目录如下图所示。
Documentation目录主要存储了使用HAL库入门,准备使用HAL库的开发者可以参考该文档。
Drivers目录主要存储与硬件相关的驱动文件,文件夹里面又包含了 3个文件夹,它们的作用如下:
BSP目录
存放开发板板级支持包驱动代码,如各种外设驱动;
CMSIS目录
存放 CMSIS 底层代码和开发文档。目录包含多个文件夹。
Core目录存储与Cortex内核相关的文件,如内核寄存器定义、宏定义、内核操作函数等,这些文件由ARM公司提供,用于访问和操作内核寄存器、滴答定时器、嵌套向量中断控制器等。
Device目录存储与芯片外设相关的文件,如外设寄存器定义、外设操作函数等,这些文件由芯片供应商提供,对应用程序开发人员十分重要。
DSP目录主要存储与ARM Cortex微控制器的数字信号处理(DSP)相关的文件。
STM32F1xx_HAL_Driver目录存放 ST 提供的 F1 系列 HAL 库驱动源码,并提供了API接口文档,便于开发者查询相关API的使用方法。
Projects目录主要存储STM32单片机开发示例。这些示例按ST提供的开发板组织,并为主要支持的工具链提供了预配置项目。
Middlewares目录主要存放的是中间层代码(组件/Lib 等),比如:FATFS、USB、LWIP、FreeRTOS,各种 GUI 等等。
HAL库开发示例
打开《控制LED等仿真实验》一节构建的工程文件,将下面直接操作寄存器的代码,调用HAL库API实现。
void LED_Init(void)
{
RCC->APB2ENR|=1<<3;
GPIOB->CRL&=0X00000000;//清零
GPIOB->CRL|=0X33333333;//50MHz输出模式
GPIOB->ODR=0X00FF; //输出高
}
LED_Init函数初始化一个或多个连接到GPIOB端口的LED,代码使用了STM32标准外设库的直接寄存器访问方式。
RCC->APB2ENR|=1<<3;用于使能GPIOB端口的时钟。
GPIOB->CRL&=0X00000000;将GPIOB的低配置寄存器清零。GPIOB->CRL|=0X33333333;将CRL寄存器设置为0x33333333,这个值配置了GPIOB的前8个引脚为通用推挽输出模式,最大速度为50MHz。
GPIOB->ODR=0X00FF;设置GPIOB的输出数据寄存器(ODR,Output Data Register)为0x00FF。ODR寄存器控制GPIOB端口的输出电平。将ODR设置为0x00FF意味着将GPIOB的前8个引脚(PB0到PB7)全部设置为高电平。
现在需要调用HAL库API来实现上述代码的功能。要找到对应的API接口,可以在HAL库API接口文档内找到这些API。
接口文档存储在STM32F1xx_HAL_Driver目录下:
该目录下存储了F103系列不同存储容量的接口文档,选择一个接口文档打开即可。
接口文档左侧导航窗口定位到目录标签项:
展开Modules/STM32F1xx_HAL_Driver列表项,可以看到类似GPIO、RCC、EXTI等熟悉的项名称,GPIO项列出了GPIO相关操作的API接口,RCC项列出了RCC寄存器组操作的相关API接口,……,以此类推,开发者可以查询到要使用的API接口。
调用HAL库的代码为:
void LED_Init(void)
{
__HAL_RCC_GPIOB_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 配置GPIOB的PB0~PB7
for (uint8_t i = 0; i < 8; i++)
{
GPIO_InitStruct.Pin = 1 << i; // 选择第i个引脚
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出模式
GPIO_InitStruct.Pull = GPIO_NOPULL; // 无上拉/下拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; // 高速(50MHz或更快,取决于具体型号)
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); // 应用配置
// 设置GPIOB引脚输出高电平
HAL_GPIO_WritePin(GPIOB, GPIO_InitStruct.Pin, GPIO_PIN_SET);
}
}
对比直接操作寄存器的代码,HAL库代码不够灵活,代码量相对较多。但HAL库的代码具有可移植性高、可读性强、可维护性强的优点。因此在后续开发中,我们将使用HAL库和直接寄存器操作方式的混合开发模式。