认识HAL库

一、为什么需要 HAL:从寄存器到 CMSIS 到 HAL

问题起点:C 代码本来就能直接读写硬件寄存器(靠内存映射),但写法五花八门、无法移植。

ARM 的解法 ------ CMSIS 标准(Cortex Microcontroller Software Interface Standard),把代码分成三层:

层 谁负责 内容
内核层 ARM 统一的寄存器定义、内核操作函数(NVIC、SysTick、__WFI 等)
外设层 各芯片厂商(ST / GD / NXP...) 按统一格式定义自家外设寄存器和中断号
高级库层 ST 在 CMSIS 之上再封一层:HAL 库

收益:同一套内核代码可跨厂商复用,外设代码换芯片时改动量大幅下降。

二、HAL / 标准库 / LL 三者对比

特点 代价
HAL 统一 API、带参数检查、回调机制、DMA 封装 代码量约为标准库的 1.5~2 倍,多几层函数调用,稍慢
标准库(SPL) 紧贴寄存器,精简、快 绑死单个系列;已停止维护,新芯片不再提供
LL(Low-Layer) 代码量接近标准库,命名风格与 HAL 一致 覆盖的外设不如 HAL 全
  • LL 可以和 HAL 混用 (例如 GPIO 用 LL、USART 用 HAL),但同一个外设不要混用。

  • 选 HAL 的现实理由不只是"可移植":ST 新芯片只出 HAL/CubeMX,标准库已是历史。

三、内核级 API(CMSIS 提供,HAL 不封装的那一层)

3.1 关 / 开总中断

复制代码
 __disable_irq();   /* PRIMASK = 1 */
 ​
 /* 临界区:这几行必须一口气执行完,不能被中断打断 */
 ​
 __enable_irq();    /* PRIMASK = 0 */
  • "总中断"= Cortex-M 内核的 PRIMASK 寄存器 。它为 1 时,除 NMI 和 HardFault 外,所有可屏蔽中断全部关闭。

  • 用途:进入临界区。

__enable_irq() 是"无条件开门"。如果临界区里又嵌了一层临界区,内层的 __enable_irq() 会在外层还没执行完时就提前开门。正确写法是保存并恢复:

复制代码
 uint32_t pm = __get_PRIMASK();
 ​
 __disable_irq();
 ​
 ...
 ​
 __set_PRIMASK(pm);   /* 恢复到进入前的状态,而不是无条件开 */

RTOS 里另外用 BASEPRI(__set_BASEPRI)只屏蔽低于某优先级的中断,让高优先级中断仍能响应。

3.2 NVIC:使能与优先级

复制代码
 HAL_NVIC_SetPriority(USART1_IRQn, 2, 0);   /* 抢占优先级 2,子优先级 0 */
 ​
 HAL_NVIC_EnableIRQ(USART1_IRQn);           /* 内部即 CMSIS 的 NVIC_EnableIRQ */

配置完中断触发条件后,必须调 NVIC_EnableIRQ 开这个门,NVIC 才会真的响应。

函数 说明
NVIC_EnableIRQ(IRQn) CMSIS 原版,按中断号使能
NVIC_SetPriority(IRQn, priority) CMSIS 原版,只收一个拼好的数值(需自己算分组),不直观
HAL_NVIC_SetPriority(IRQn, 抢占, 子) 实际用的是它,两个参数分开传,内部做分组换算
HAL_NVIC_SetPriorityGrouping(group) 设优先级分组

优先级规则:

  • 抢占优先级:数字越小越优先,高的能打断低的(中断嵌套)

  • 子优先级 :抢占相同时决定谁先响应,不能互相打断

  • STM32F4 只实现 4 位优先级 (__NVIC_PRIO_BITS = 4),所以每档取值 0~15

3.3 SysTick

复制代码
 SysTick_Config(ticks);

HAL_Delay() 和 HAL_GetTick() 背后就是它。

  • 链路:HAL_Init() → HAL_InitTick() 配置 SysTick 为 1ms → SysTick_Handler()(在 stm32f4xx_it.c)调 HAL_IncTick() → 累加 uwTick → HAL_GetTick() 读它。
  1. SysTick 优先级默认是最低的 (TICK_INT_PRIORITY = 0x0F),所以 在中断服务函数里调 HAL_Delay() 会死等 ------SysTick 抢不进来,uwTick 不增加。

  2. 自己调 SysTick_Config() 会覆盖 HAL 的配置,导致 HAL_Delay() 失效。要用就确认没冲突。

3.4 低功耗与空指令

复制代码
 while (1) {
 ​
     /* 处理事件 */
 ​
     __WFI();   /* 没事就睡觉,等按键/串口中断唤醒 */
 ​
 }
  • __WFI():进入睡眠,低功耗产品(尤其电池供电)的标配。

  • __NOP():一个时钟周期什么都不做。不跟 HAL_Delay() 竞争 (一个是 ns 级,一个是 ms 级)。用途:模拟 I2C / OLED / SD 卡这类软件时序,用 NOP 凑周期最精确。

四、CubeMX 标准工程的文件地图

4.1 目录结构

复制代码
 工程根/
 ​
 ├── .ioc                          ← 配置真源,必须进 git
 ​
 ├── Core/
 ​
 │   ├── Inc/     main.h  stm32f4xx_hal_conf.h  stm32f4xx_it.h
 ​
 │   └── Src/     main.c  stm32f4xx_hal_msp.c  stm32f4xx_it.c  system_stm32f4xx.c
 ​
 ├── Drivers/
 ​
 │   ├── CMSIS/                    ← 内核 + 器件头文件(ARM + ST)
 ​
 │   └── STM32F4xx_HAL_Driver/     ← HAL 源码(Inc + Src)
 ​
 └── MDK-ARM/                      ← .uvprojx + startup_stm32f4xxxx.s

4.2 各文件职责

文件 位置 职责
main.c / main.h Core main()、SystemClock_Config()、MX_xxx_Init();引脚宏定义
stm32f4xx_hal_conf.h Core/Inc HAL 配置:外设模块开关 、HSE_VALUE(晶振值)、TICK_INT_PRIORITY
stm32f4xx_it.h Core/Inc 中断服务函数的声明
stm32f4xx_it.c Core/Src 中断服务函数实现 :SysTick_Handler、USART1_IRQHandler 等
stm32f4xx_hal_msp.c Core/Src 外设底层初始化 :GPIO 复用、时钟使能、NVIC 优先级配置------CubeMX 自动生成骨架
system_stm32f4xx.c Core/Src SystemInit(),上电时初始化系统时钟、向量表;一般不用改
stm32f4xx_hal.c Drivers HAL_Init、HAL_Delay、HAL_GetTick、弱定义的 HAL_MspInit

外设中断的优先级和使能,是在 stm32f4xx_hal_msp.c 里生成的

4.3 HAL 头文件索引

功能 头文件
HAL 总入口(HAL_Init / HAL_Delay) stm32f4xx_hal.h
GPIO(点灯、读引脚) stm32f4xx_hal_gpio.h
时钟配置 stm32f4xx_hal_rcc.h
串口 stm32f4xx_hal_uart.h / stm32f4xx_hal_usart.h
外部中断 stm32f4xx_hal_exti.h
定时器 stm32f4xx_hal_tim.h
SPI stm32f4xx_hal_spi.h
I2C stm32f4xx_hal_i2c.h
DMA stm32f4xx_hal_dma.h
NVIC / SysTick 的 HAL 封装 stm32f4xx_hal_cortex.h
ADC stm32f4xx_hal_adc.h
低功耗 stm32f4xx_hal_pwr.h
Flash 读写 stm32f4xx_hal_flash.h··
相关推荐
莫小墨1 小时前
数字地、模拟地、功率地
嵌入式硬件
BSD_HY1 小时前
薄膜开关矩阵按键的GPIO扩展芯片选型与中断响应延迟
单片机·线性代数·矩阵
xunyan62341 小时前
51 单片机LED闪烁(SDCC + Notepad++ + STC-ISP/普中ISP)
单片机·notepad++·接口隔离原则
桀人2 小时前
USART帧格式
网络·stm32·单片机·嵌入式硬件
qq_448011162 小时前
如何烧录TAU804的固件
单片机·嵌入式硬件
深圳老胡2 小时前
STM32F4 OTA 升级双 App 方案设计与实现
笔记·stm32·单片机·代码规范
HRTOS2 小时前
HRTOS 4.0 内核资源配置优化:减少资源对象与消息队列数量
经验分享·单片机·系统架构·51单片机
Jason_zhao_MR2 小时前
新一代电能数据采集终端方案
linux·人工智能·嵌入式硬件·fpga开发·嵌入式
Cerman2 小时前
商用处理器多点开花,边缘计算快速增长
嵌入式硬件·智能硬件