一、核心区别
先通过核心特征对比,帮你建立 "一眼识别" 的基础,这也是区分两者的核心依据:
| 核心维度 | 标准外设库 (SPL) | HAL 库 (Hardware Abstraction Layer) |
|---|---|---|
| 命名风格 | 函数名短,无统一前缀(如GPIO_SetBits、USART_SendData) |
所有函数以HAL_开头,命名冗长且格式统一(如HAL_GPIO_WritePin、HAL_UART_Transmit) |
| 芯片支持 | 仅支持 F1/F4 等老系列,无官方更新 | 支持全系列 STM32(F0/F1/F4/L4/H7 等),持续更新 |
| 核心设计 | 寄存器级封装,直接操作寄存器 | 抽象层封装,屏蔽寄存器差异,跨系列兼容 |
| 配套工具 | 无官方图形化工具,纯手动写代码 | 配套 STM32CubeMX 图形化配置,自动生成代码 |
| 状态 / 返回值 | 函数无统一返回值(多为 void) | 几乎所有函数返回HAL_StatusTypeDef枚举(如HAL_OK、HAL_ERROR) |
| 中断处理 | 手动编写中断服务函数,无回调机制 | 内置回调函数(如HAL_UART_RxCpltCallback),中断逻辑更规范 |
二、使用方法(极简实操版)
1. 标准外设库 (SPL) 的使用(仅老项目 / 学习底层)
SPL 已被 ST 废弃,仅适合理解寄存器原理,核心是 "手动配置寄存器 + 调用外设函数":
核心步骤:
- 下载对应芯片的 SPL 包(如 F103 的
STM32F10x_StdPeriph_Lib_V3.5.0); - 引入核心头文件(
stm32f10x.h)和外设文件(如stm32f10x_gpio.c); - 手动配置时钟→初始化外设→调用外设函数。
极简示例(GPIO 翻转):
#include "stm32f10x.h"
void GPIO_Init_PA0(void) {
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 使能时钟
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStruct);
}
int main(void) {
GPIO_Init_PA0();
while(1) {
GPIO_SetBits(GPIOA, GPIO_Pin_0); // 无HAL前缀,短函数名
for(int i=0; i<1000000; i++);
GPIO_ResetBits(GPIOA, GPIO_Pin_0);
for(int i=0; i<1000000; i++);
}
}
2. HAL 库的使用(当前主流)
HAL 库的核心是 "图形化配置 + 自动生成代码 + 调用 HAL 前缀函数",效率远高于 SPL:
核心步骤:
- 安装 STM32CubeMX(图形化工具),选择对应芯片;
- 图形化配置外设(如 GPIO、UART),设置时钟、引脚功能;
- 生成工程代码(支持 Keil/STM32CubeIDE 等);
- 在生成的代码中调用
HAL_前缀函数。
极简示例(同功能 GPIO 翻转):
#include "main.h"
GPIO_InitTypeDef GPIO_InitStruct;
int main(void) {
// 自动生成的初始化代码(CubeMX生成)
HAL_Init(); // HAL库初始化
SystemClock_Config(); // 时钟配置(CubeMX生成)
__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA时钟(HAL封装)
// GPIO初始化
GPIO_InitStruct.Pin = GPIO_PIN_0; // 注意:PIN大写,带下划线
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // HAL前缀函数
while(1) {
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET); // HAL前缀
HAL_Delay(500); // HAL自带延时
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET);
HAL_Delay(500);
}
}
三、如何快速区分两者?(实操技巧)
拿到一段 STM32 代码,按以下步骤 10 秒内就能区分:
- 看函数前缀 :有
HAL_(如HAL_GPIO_WritePin)→ HAL 库;无统一前缀(如GPIO_SetBits)→ SPL; - 看引脚定义 :用
GPIO_PIN_0(大写 + 下划线)→ HAL 库;用GPIO_Pin_0(小写 Pin)→ SPL; - 看返回值 / 状态 :函数返回
HAL_StatusTypeDef(如HAL_OK)→ HAL 库;函数多为 void→ SPL; - 看工程文件 :包含
stm32xxx_hal.c/h(如stm32f1xx_hal_gpio.c)→ HAL 库;包含stm32xxx_stdperiph.c/h→ SPL; - 看配套工具痕迹 :工程中有
CubeMX生成的注释(如/* USER CODE BEGIN */)→ HAL 库;纯手动编写无此类注释→ SPL。
四、总结
- 核心区分点 :HAL 库所有函数带
HAL_前缀,SPL 无统一前缀;HAL 库用GPIO_PIN_0,SPL 用GPIO_Pin_0; - 使用选择:新项目优先用 HAL 库(CubeMX + 自动生成代码,效率高),仅学习底层时用 SPL;
- 关键特征:HAL 库跨系列兼容、有回调函数、配套图形化工具;SPL 轻量高效但仅支持老芯片、无官方更新。