一、标准库 vs HAL库区别:
特性 | 标准库(StdPeriph) | HAL库(Hardware Abstraction Layer) |
---|---|---|
来源 | STMicroelectronics 官方早期支持库 | ST 官方后续主推的抽象库 |
发布年代 | 较早(STM32F1/F4 初期) | 较新(STM32F4/F7/L4 开始推广) |
抽象层次 | 较低,贴近寄存器 | 较高,屏蔽底层细节 |
易用性 | 相对复杂,需要熟悉寄存器结构 | 较易用,有统一接口封装 |
灵活性 | 高,自由度大 | 中等,受限于 HAL 封装结构 |
可移植性 | 差,不同芯片系列要大量改代码 | 好,代码迁移更容易 |
编码风格 | 偏裸机控制 | 偏向抽象平台 |
学习成本 | 高,适合深入学习底层 | 低,适合快速上手项目 |
示例与支持 | 较少,社区多,但已不再维护 | 官方维护,CubeMX 配套 |
二、示例:GPIO初始化的区别
1. 使用标准库:
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStruct);
2. 使用HAL库:
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_5;
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库风格更简洁,而标准库更贴近硬件
三、在HAL库中编程
第一步:使用 STM32CubeMX 生成工程框架
-
选择芯片(我使用了 STM32F407VET6 )
-
配置时钟、引脚、串口、I2C、SPI 等外设
-
勾选生成 “HAL” 驱动代码
-
VSCode编辑生成的代码
-
生成代码工程并导入 Keil,编译下载
一些CubeMX(V6_15_0)安装注意事项:
1. 安装路径不要有中文,一路next
2. 安装软件之后,还需要安装固件库
我下载了ST的GitHub上能下载的最新版本(stm32cubef4-v1-28-0),下载后离线安装
但ST的最新版本GitHub上好像没同步,落后了两个版本。
有资源,固件库安装简单,此处无安装教程,可参考其它。
3. CubeMX+固件库的下载链接:
通过网盘分享的文件:00Software
链接: https://pan.baidu.com/s/16UxGhO1v9OvM4HidUSdJwg 提取码: 5mc3
--来自百度网盘超级会员v8的分享
4. Gitee代码仓库分享:
https://gitee.com/jinjing616/HAL_Test.git
第二步:编写 HAL 应用代码
CubeMX 会自动生成 HAL 初始化函数,只需要写核心逻辑
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET); // 点亮LED
HAL_Delay(500);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET); // 熄灭LED
HAL_Delay(500);
第三步:掌握 HAL 外设函数
HAL 库中每个外设都有一套封装函数,例如:
GPIO:HAL_GPIO_Init(), HAL_GPIO_WritePin()
UART:HAL_UART_Transmit(), HAL_UART_Receive_IT()
TIM:HAL_TIM_Base_Start(), HAL_TIM_PWM_Start()
ADC:HAL_ADC_Start(), HAL_ADC_GetValue()
I2C:HAL_I2C_Master_Transmit()
四、标准库和HAL库中必须掌握的外设
必须掌握的 10 大外设:标准库与 HAL 库接口对比:
外设 | 标准库关键函数 | HAL库关键函数 | 功能说明 |
---|---|---|---|
GPIO | GPIO_Init() , GPIO_SetBits() | HAL_GPIO_Init() , HAL_GPIO_WritePin() | 控制 LED、读取按键、IO 接口 |
USART/UART | USART_Init() , USART_SendData() | HAL_UART_Init() , HAL_UART_Transmit() | 串口通信,调试/传输 |
SPI | SPI_Init() , SPI_I2S_SendData() | HAL_SPI_Init() , HAL_SPI_Transmit() | 与 OLED、Flash、传感器通信 |
I2C | I2C_Init() , I2C_SendData() | HAL_I2C_Init() , HAL_I2C_Master_Transmit() | 与 EEPROM、MPU6050 通信 |
TIM | TIM_TimeBaseInit() , TIM_Cmd() | HAL_TIM_Base_Start() , HAL_TIM_PWM_Start() | 生成延时、PWM、控制舵机、电机 |
ADC | ADC_Init() , ADC_GetConversionValue() | HAL_ADC_Init() , HAL_ADC_GetValue() | 读取电压、电位器、温度值 |
EXTI | EXTI_Init() , NVIC_Init() | HAL_GPIO_EXTI_Callback() | 外部中断,如按键唤醒 |
NVIC/SysTick | NVIC_Init() , SysTick_Config() | 自动封装在 HAL_Init() 中 | 中断管理 / 系统节拍(RTOS) |
DMA | DMA_Init() , DMA_Cmd() | HAL_DMA_Start() , HAL_UART_Transmit_DMA() | 高速数据传输,减少CPU负担 |
RCC | RCC_DeInit() , RCC_HSEConfig() | 封装在 SystemClock_Config() 中 | 控制时钟频率,性能设置 |
HAL 库封装了标准库的功能,大多数 HAL 函数实际就是对标准库寄存器操作的封装。
HAL 库适合快速开发,标准库更适合理解底层。
类别 | 外设名称 | 功能用途 | 开发中作用 |
---|---|---|---|
核心 | GPIO | 通用输入输出 | 控制 LED、按键、模块开关等 |
通信 | USART/UART | 串口通信 | 打印调试信息、上位机通信 |
通信 | SPI | 高速通信总线 | 与 Flash、OLED、传感器通信 |
通信 | I2C | 低速通信总线 | 与 EEPROM、MPU6050 等通信 |
计时 | 定时器 TIM | 定时、PWM输出、输入捕获等 | 延时、PWM 控制电机、读取编码器等 |
采集 | ADC | 模拟-数字转换 | 采集电压、电流、温度、电位器等 |
存储 | Flash 读写(内部) | 数据保存 | 保存参数,配合 Bootloader |
内存 | SRAM / DMA / malloc | 内存和传输管理 | 优化效率,减少CPU开销 |
电源 | PWR(低功耗) | 睡眠/待机模式 | 节能控制,电池供电设备常用 |
调试 | NVIC / SysTick | 中断控制 / 系统节拍 | 控制中断响应,运行 FreeRTOS |
外设控制 | EXTI | 外部中断 | 按键/外部信号触发事件 |
系统 | RCC | 时钟配置 | 提升系统性能,配置频率 |
五、示例一 :基础的入门项目模板----LED + UART 打印
功能:
-
使用 GPIO 控制 LED
-
使用 USART1 打印 Hello World
工程结构:
HAL_LedUart/
├── Core/
│ ├── Src/
│ │ ├── main.c <-- 用户逻辑代码
│ │ ├── stm32f4xx_it.c <-- 中断服务函数
│ ├── Inc/
│ │ ├── main.h
│ │ ├── stm32f4xx_it.h
├── Drivers/
│ ├── STM32F4xx_HAL_Driver/ <-- HAL 库
│ └── CMSIS/ <-- Cortex-M 内核
1. 使用STM32CubeMX 生成工程
(基于 STM32F407)
1. 创建工程
打开 STM32CubeMX
New Project → 搜索 STM32F407VET6(或你的具体型号)
2. 配置
1. 外设配置
GPIO
在 Pinout 界面,点击 PA6,选为 GPIO_Output
PA6 接板载 LED //换成相应的板载LED引脚
USART1
“USART1” 设为:Asynchronous Mode
点击 PA9 设置为 USART1_TX
点击 PA10 设置为 USART1_RX
RCC
启用外部晶振:点击“RCC”,启用 High Speed Clock (HSE) → Crystal/Ceramic Resonator
SYS
“Debug” 选为 Serial Wire(用于 STLink 调试,一定不要忘,不设置后续烧写会有问题)
2. 时钟配置
Clock Configuration
点击“Clock Configuration”,将 SYSCLK 设置为 72 MHz 或更高(推荐 168 MHz)
参见文章最后小Tips设置,有图片参考
3. 工程管理配置
命名工程HAL_LedUsart,选择 Toolchain 为 MDK-ARM
Firmware Package Name and Version:选中下载并安装好的固件库stm32cubef4-v1-28-0
CubeMX 勾选 "Generate peripheral initialization as a pair of .c/.h files per IP"
每个外设单独文件管理,便于阅读和维护
3. 生成工程
Generate Code
2. 使用 Keil 打开工程
双击打开 project.uvprojx
结构中会有 Src 和 Inc 目录,包含所有 HAL 初始化代码
所有用户逻辑写在 Core/Src/main.c 的 main() 函数中
3. 编写 main.c 测试代码
#include "main.h"
#include "stdio.h"
UART_HandleTypeDef huart1;
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, HAL_MAX_DELAY);
return ch;
}
int main(void)
{
//HAL库初始化
HAL_Init();
//系统时钟配置(CubeMX自动生成)
SystemClock_Config();
//初始化外设(GPIO和USART1)
MX_GPIO_Init();
MX_USART1_UART_Init();
printf("Hello from STM32 HAL UART!\r\n");
while (1)
{
//翻转PA6状态,实现LED闪烁
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_6);
HAL_Delay(500); //延时 500ms
}
}
补充HAL外设初始化函数(由CubeMX自动生成)
GPIO 初始化(MX_GPIO_Init)
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 1. 使能 GPIOC 时钟
__HAL_RCC_GPIOC_CLK_ENABLE();
// 2. 配置 PA6 为推挽输出,无上拉
GPIO_InitStruct.Pin = GPIO_PIN_6;
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);
}
USART1 初始化(MX_USART1_UART_Init)
UART_HandleTypeDef huart1;
void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
HAL_UART_Init(&huart1);
}
4. 编译下载
使用 Keil 编译
勾选如下图Tips2和Tips3
使用 DAP 调试下载(有什么用什么吧)
用串口助手SSCom(115200 8N1)查看串口输出(PA9, PA10 接 USB 转串口)
还有一些代码实践的小Tips,后续文章不再重复。
Tips1:注意时钟树配置:
Tips2:注意勾选:(生成的时候不勾选,printf跑不动,会阻塞)
Tips3:勾选Reset and Run
5. 现象展示
同时LED闪烁。
今天的HAL库简单示例就到这里啦,明天我们再来一个简单的HAL库小示例。