一、开发库选择1.1 概述 STM32开发相关的库有很多,它们都是为了方便开发者使用STM32微控制器而提供的软件工具。根据不同的功能和层次,可以将它们分为以下几类: CMSIS库(Cortex Microcontroller Software Interface Standard)是ARM公司推出的一种标准化的微控制器软件接口,它定义了一些通用的数据类型、寄存器访问、中断处理、内核功能等,方便开发者使用Cortex-M内核的各种功能。CMSIS库还包括了一些中间件组件,如RTOS、DSP、Driver、Pack、SVD、DAP和NN等,提供了丰富的软件功能。CMSIS库不是HAL库,也不是标准库,它是一种与厂商(比如ST公司)无关的软件层,可以在不同的微控制器上使用。 HAL库(Hardware Abstraction Layer,硬件抽象层)是ST公司推出的一种硬件抽象层库,它提供了一套统一、简洁、易用的API函数接口,方便开发者使用STM32的各种外设功能。HAL库支持STM32全系列产品,具有可移植性、易用性和可靠性等优点。HAL库还提供了一些中间件组件,如RTOS,USB,TCP/IP和图形等,可以快速实现复杂的功能。 标准库(Standard Peripheral Libraries)是ST公司为STM32微控制器提供的一种固件函数包,它封装了STM32所有外设的寄存器操作和中断处理,提供了一套统一、简洁、易用的API函数接口,方便开发者使用STM32的各种外设功能。标准库支持STM32全系列产品,具有可移植性、易用性和可靠性等优点。不过,ST官方已经不再更新STM32标准固件库,而是力推新的固件库:HAL库。 LL库(Low-Layer,底层)是ST公司最近(也不是最近,六七年了)新增的一种底层库,它与HAL库捆绑发布,文档也是和HAL文档在一起的。 LL库更接近硬件层,对需要复杂上层协议栈的外设不适用,直接操作寄存器。LL库可以独立使用,也可以和HAL库结合使用。 其他第三方或开源库:除了ST公司提供的官方库外,还有许多第三方或开源的软件库可以用于STM32开发,如FreeRTOS、uCOS、FatFs、LwIP等。这些软件库通常提供了一些特定领域或功能的解决方案,如实时操作系统、文件系统、网络协议等。
很显然,CMSISI库是老大,HAL、SPL、LL都是基于CMSIS进一步进行设计和开发的,旨在降低开发难度。(当然了寄存器开发就不用CMSIS库了哈,反过来,CMSIS库要使用寄存器) 我们通常的、简单的开发形式有:HAL库开发、LL库开发、SPL库开发、第三方库开发。
插一嘴: 一般,如果你使用正点原子的开发板进行学习或开发,就会看到他们的相关资料。你会看到:《xxx开发指南HAL库版本Vx.x》、《xxx开发指南固件库版本Vx.x》、《xxx开发指南寄存器版本Vx.x》。
这里说明一下:正点原子一些资料里面的“固件库”、“标准库”、“库函数”,这些名词一般都指的是:STM32 的 Standard Peripheral Library (SPL):标准外设库(或SPL库中的函数)。 (具体的看资料内部描述即可)
实际上,固件库不单单指的是SPL库,固件库是指由芯片厂商提供的针对特定芯片或系列芯片的软件包,它包含了对芯片外设的驱动函数、示例代码、中间件和实用工具等。固件库的目的是为了简化开发者对底层硬件的访问和操作,提供一个统一的、易于使用的编程接口。所以:LL库、HAL库、SPL库都是固件库。所以,当您看到HAL固件库这样类似的名词时应当理解其就是HAL库。
总之,注意一下,正点原子的一些资料可能把SPL库叫做固件库或标准库。
1.2 CMSIS库CMSIS (Cortex Microcontroller Software Interface Standard) 是 ARM 公司为其 Cortex-M 系列微控制器定义的一套软件接口标准。这套标准旨在简化微控制器的软件开发,使得开发者可以更容易地开发跨不同 Cortex-M 处理器系列和供应商的嵌入式应用程序。 提示: 阅读本专栏其他文章,学习相关知识点。
CMSIS库主要由以下几部分组成: CMSIS-CORE:提供了Cortex-M处理器的核心功能接口,包括NVIC、SysTick等。它还定义了处理器的寄存器访问、中断向量和中断函数名称。 CMSIS-DSP:一个为Cortex-M处理器优化的数字信号处理库,提供了丰富的DSP函数,如FFT、滤波器、矩阵运算等。 CMSIS-RTOS:为实时操作系统提供了一个标准的API接口,使得开发者可以轻松地在不同的RTOS之间切换。 CMSIS-Driver:定义了一套标准的外设驱动接口,如UART、SPI、I2C等。 CMSIS-SVD:系统查看描述,提供了一个描述微控制器外设的XML格式。 CMSIS-Pack:定义了一个用于软件组件、设备和板卡的描述、交付和安装的标准。
1 CMSIS库的优势 跨平台:由于CMSIS是为Cortex-M系列微控制器定义的标准,因此开发者可以轻松地将代码从一个Cortex-M处理器迁移到另一个处理器,而无需进行大量的代码修改。 高效性:CMSIS库中的许多函数都经过了优化,以充分利用Cortex-M处理器的特性,如SIMD指令。 易于使用:CMSIS提供了清晰、一致的API,使得开发者可以快速上手并开始开发。 强大的生态系统:许多第三方供应商和RTOS提供了对CMSIS的支持,这使得开发者可以轻松地集成各种软件组件和工具。
通常在进行STM32开发时,不会直接使用CMSIS库,而是使用基于CMSISI的HAL库或者SPL库,它们提供了更高级的抽象、更易用的API、更丰富的文档、更好的IDE支持等等。 使用CMSIS库可以获得更精细粒度的控制、更强大额性能,在以下场景中,可能会直接使用CMISIS库: - DSP开发,CMSIS-DSP已经为Cortex-M系列微控制器进行了优化。对于需要高性能计算的应用,直接使用这些函数可能会更有效。
- 如果开发者**他们的代码能够在不同的Cortex-M微控制器之间移植,那么直接使用CMSIS库会更有意义,因为它提供了一个标准化的接口。
- 使用RTOS。
1.3 SPL库STM32的SPL(Standard Peripheral Libraries),即标准外设库,是STMicroelectronics为其STM32系列Cortex-M微控制器提供的软件开发库。它为STM32微控制器上的各种外设提供了C语言的函数API,帮助开发者简化硬件配置和使用。 STM32的SPL主要由以下几部分组成: 外设驱动:为STM32微控制器上的各种外设(如GPIO、UART、SPI、I2C、ADC、TIM等)提供了驱动函数。 CMSIS支持:SPL包括对CMSIS库的支持,特别是CMSIS-CORE部分,为开发者提供了对Cortex-M核心功能的访问。 系统配置:包括系统时钟、中断和其他基础配置的函数。 工具链文件:如启动文件、链接脚本等,这些文件为特定的工具链(如GCC、Keil、IAR等)提供了支持。 示例代码:STMicroelectronics通常会提供一些示例代码,展示如何使用SPL来开发应用程序。
STM32 SPL 应用程序的文件描述: 文件名描述
STM32 SPL
stm32yyxx_conf.h外设驱动程序的配置文件。用户可以通过使用模板启用或禁用外设头文件包含。此文件还可用于在编译固件库驱动程序之前启用或禁用库运行时失败,通过预处理器定义USE_FULL_ASSERT
stm32yyxx_ppp.hPPP外设的头文件
stm32yyxx_ppp.c用C语言编写的PPP外设的驱动源代码文件
stm32yyxx_it.h包含所有中断处理程序原型的头文件
stm32yyxx_it.c包含Cortex-Mx异常的中断服务例程(ISR)的模板源文件。用户可以为使用的外设添加额外的ISRs(有关可用的外设中断处理程序的名称,请参考startup_stm32yyxx.s)
CMSIS
stm32yyxx.hCMSIS Cortex-Mx STM32yyxx 设备外设访问层头文件。这是应用程序员在源代码中使用的唯一包含文件
system_stm32yyxx.hCMSIS Cortex-Mx STM32yyxx 设备外设访问层系统头文件
system_stm32yyxx.cCMSIS Cortex-Mx STM32yyxx 设备外设访问层系统源文件2 SPL的优势: 简化开发:STM32的SPL为微控制器的外设提供了高级的抽象,使得开发者不需要深入了解硬件细节就可以配置和使用外设。 跨设备兼容性:由于SPL为STM32系列的微控制器提供了统一的API,因此开发者可以更容易地在同一系列的不同微控制器之间移植代码。 供应商支持:使用SPL意味着可以获得STMicroelectronics的支持,包括文档、示例代码和可能的固件更新。
SPL的局限性: 性能:由于SPL提供了高级的抽象,某些操作可能不如直接访问硬件寄存器那么快。 灵活性:虽然SPL为大多数常见的用例提供了支持,但对于一些特定的、非标准的需求,直接使用CMSIS或其他方法可能会更合适。 更新和维护:STMicroelectronics已经开始推广其HAL(硬件抽象层)库,并逐渐减少对SPL的支持。因此,新的STM32产品可能不再支持SPL,或者SPL可能不再获得更新。
1.4 HAL 库HAL(Hardware Abstraction Layer),即硬件抽象层,是STMicroelectronics为其STM32系列微控制器提供的一个新的软件开发框架。与SPL(标准外设库)相比,HAL提供了更高级的抽象和更多的功能,旨在简化和加速STM32微控制器的开发过程。 STM32的HAL库主要由以下几部分组成: 外设驱动:为STM32微控制器上的各种外设(如GPIO、UART、SPI、I2C、ADC、TIM等)提供了驱动函数。 中间件支持:HAL库包括对各种中间件的支持,如USB、TCP/IP、文件系统等。 系统配置:提供了系统时钟、电源模式、中断和其他基础配置的函数。 回调机制:HAL库使用了回调函数的机制,允许开发者在特定的事件(如数据接收完成)发生时执行自定义的代码。 错误处理:HAL库提供了错误处理和检查机制,帮助开发者识别和处理可能的错误。
HAL库的优势: 模块化和可重用性:HAL库的设计使得开发者可以轻松地重用代码,特别是在不同的STM32微控制器之间。 跨设备兼容性:由于HAL为STM32系列的微控制器提供了统一的API,因此开发者可以更容易地在同一系列的不同微控制器之间移植代码。 集成开发环境(IDE)支持:STMicroelectronics提供了STM32CubeMX工具,该工具可以自动生成HAL库的初始化代码,进一步简化了开发过程。 持续更新和维护:与SPL相比,HAL库获得了更多的更新和维护,以支持新的STM32产品和功能。
HAL库的局限性: 1.5 LL库STM32的LL库((Low Layer))是STM32Cube库的一部分,旨在为STM32外设提供一个简化的API集,同时保持最大的灵活性。LL库是一个更接近硬件的编程接口,与HAL(硬件抽象层)库相比,它提供了更低的抽象级别。 主要特点: 性能:由于LL库提供了更接近硬件的API,因此它通常比HAL库更快,尤其是在中断处理程序中。 简单性:LL库的API设计得更简单,更直接,使得代码更容易阅读和维护。 灵活性:LL库提供了对STM32外设的完全控制,允许开发人员更好地优化其应用程序。 与HAL的互操作性:LL库可以与HAL库一起使用,这意味着开发人员可以在同一个项目中混合使用两者,根据需要选择最佳的库。
LL库的组成: 外设初始化:LL库提供了初始化外设的功能,这些功能通常比HAL库提供的功能更简单,更直接。 外设控制:LL库提供了一组函数,允许开发人员直接控制STM32外设,而无需经过任何中间抽象。 中断处理:与HAL库相比,LL库提供了更简单、更直接的中断处理函数。
使用LL库的优点: 代码大小:由于LL库提供了更少的抽象,因此生成的代码通常比使用HAL库的代码更小。 执行速度:LL库的函数通常比HAL库的函数执行得更快,因为它们提供了更少的抽象。 更好的控制:LL库允许开发人员更直接地控制STM32外设,这可以提供更好的性能和更低的功耗。
STM32Cube库的组成如下:(STMicroelectronics为其STM32微控制器系列提供的一个全面的软件开发工具集) HAL (Hardware Abstraction Layer):这是STM32Cube的核心部分,提供了一套完整的外设驱动程序,用于STM32系列的微控制器。这些驱动程序提供了一个高级的API,使开发人员能够轻松地访问STM32的各种功能。 LL (Low Layer):这是一个更接近硬件的驱动程序集,为那些需要更高性能或更低级访问的应用程序提供了一个选择。 Middleware:STM32Cube还包含了一些中间件组件,如USB库、TCP/IP堆栈、文件系统、RTOS接口等。 CMSIS (Cortex Microcontroller Software Interface Standard):这是ARM定义的一个标准,用于开发Cortex-M微控制器的应用程序。STM32Cube包含了CMSIS核心和设备头文件。 Utilities:这些是一些实用程序和工具,如数据结构、数学函数、图形界面库等。 Examples and Templates:STM32Cube为STM32的各种开发板提供了大量的示例代码和模板,帮助开发人员快速入门。 Configuration Tool:STM32CubeMX是一个图形工具,允许用户图形化地配置STM32微控制器的外设,并自动生成初始化代码。
1.6 寄存器开发STM32寄存器开发是一种直接操作STM32微控制器内部的寄存器来控制外设的开发方式,它可以充分利用STM32的性能和灵活性,但也需要对寄存器的功能和地址有深入的了解。 STM32寄存器开发的优点是: - 可以实现最高效的代码执行,无需调用库函数或中间件。
- 可以实现最灵活的外设配置,无需受限于库函数或中间件的参数。
- 可以实现最低层次的硬件控制,无需考虑库函数或中间件的兼容性。
STM32寄存器开发的缺点是: - 需要对寄存器的功能和地址有深入的了解,否则容易出错或造成冲突。
- 需要编写大量的代码,否则难以实现复杂的功能或逻辑。
- 需要手动管理内存和堆栈,否则容易出现溢出或泄漏。
有时候,直接使用寄存器进行开发也称作“裸机开发”。但裸机开发有时候也指不使用操作系统的开发。 二、代码对比一个简单功能的实现:点亮一个 LED。以下是使用不同 STM32 开发库实现此功能的代码示例(主要部分): 2.1 使用寄存器直接操作STM32的硬件寄存器来配置和控制GPIO。 #define GPIOB_BASE 0x40020400#define GPIOB_MODER ( *(volatile uint32_t*)(GPIOB_BASE) )#define GPIOB_ODR ( *(volatile uint32_t*)(GPIOB_BASE + 0x14) )int main(void) { // 设置PB0为输出模式 GPIOB_MODER |= (1 << 0); while(1) { GPIOB_ODR |= (1 << 0); // 点亮LED }}2.2 使用CMSIS库使用CMSIS定义的结构和宏来操作寄存器。 #include "stm32f4xx.h"int main(void) { RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN; // 使能GPIOB时钟 GPIOB->MODER |= GPIO_MODER_MODER0_0; // 设置PB0为输出模式 while(1) { GPIOB->ODR |= GPIO_ODR_OD0; // 点亮LED }}2.3 使用SPL库使用STM32标准外设库函数。 #include "stm32f4xx_gpio.h"#include "stm32f4xx_rcc.h"int main(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); while(1) { GPIO_SetBits(GPIOB, GPIO_Pin_0); // 点亮LED }}2.4 使用HAL库使用STM32硬件抽象层函数。 #include "stm32f4xx_hal.h"GPIO_InitTypeDef GPIO_InitStructure;int main(void) { __HAL_RCC_GPIOB_CLK_ENABLE(); GPIO_InitStructure.Pin = GPIO_PIN_0; GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStructure.Pull = GPIO_NOPULL; GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOB, &GPIO_InitStructure); while(1) { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET); // 点亮LED }}2.5 使用LL库使用STM32低层库函数。 #include "stm32f4xx_ll_gpio.h"#include "stm32f4xx_ll_bus.h"int main(void) { LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOB); LL_GPIO_SetPinMode(GPIOB, LL_GPIO_PIN_0, LL_GPIO_MODE_OUTPUT); while(1) { LL_GPIO_SetOutputPin(GPIOB, LL_GPIO_PIN_0); // 点亮LED }}2.6 使用RTOS使用实时操作系统(如FreeRTOS)来控制LED。 #include "FreeRTOS.h"#include "task.h"#include "stm32f4xx_hal.h"void LedTask(void *pvParameters) { GPIO_InitTypeDef GPIO_InitStructure; __HAL_RCC_GPIOB_CLK_ENABLE(); GPIO_InitStructure.Pin = GPIO_PIN_0; GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStructure.Pull = GPIO_NOPULL; GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOB, &GPIO_InitStructure); while(1) { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET); // 点亮LED vTaskDelay(1000); // 延时1秒 }}int main(void) { xTaskCreate(LedTask, "LED", 128, NULL, 1, NULL); vTaskStartScheduler();}
|