UART_HandleTypeDef)+ 回调 + HAL_ 前缀 = HAL;轻量直读寄存器、无句柄 = LL一句话:"开发快、要移植选 HAL;资源紧、要极致选 LL/寄存器;认风格看有没有句柄和回调。"
写 STM32 有三档"档位"——寄存器 = 手动挡(直接拨寄存器)、HAL = 自动挡(全封装,好用但沉)、LL = 半自动挡(封装很薄,贴着寄存器,快但轻)。面试核心就一件事:什么情况该开哪一档。
| 档位 | 长啥样 | 优点 | 缺点 | 何时用 |
|---|---|---|---|---|
| 寄存器 | RCC->APB2ENR \|= ... |
最透、最快、最省 | 开发慢、移植全重写 | 学底层、性能极致 |
| HAL | HAL_UART_Transmit(&huart1,...) |
开发快、跨型号可移植、CubeMX 生成 | 代码大、慢、结构重 | 快速开发、复杂外设、要移植 |
| LL | LL_USART_TransmitData8(USART1, x) |
比 HAL 轻、快、省资源 | 要自己管细节、可移植不如 HAL | 资源紧张、性能敏感、驱动层 |
| 模式 | 行为 | 适用 |
|---|---|---|
| 轮询 | 阻塞死等 | 调试、低速(I2C 读 EEPROM) |
| 中断(IT) | 非阻塞,到点回来处理 | 不定长串口接收 |
| DMA | 硬件搬运,CPU 零参与 | 大批量(ADC 多通道) |
IS_XXX 宏)HAL_XXX_Callback)多这几层 = 代码大、调用慢,但换来"你只管调 API,底层差异我罩着"。
不是"寄存器定义不一致所以不能移植"——恰恰相反:HAL 把芯片差异藏在库内部(F1、F4 各有自己的 stm32f1xx_hal_uart.c / stm32f4xx_hal_uart.c,对外提供同一个 HAL_UART_Transmit)。你的代码只调 API、从不碰寄存器 → 换芯片只换库 + CubeMX 重生成,业务代码不动。
| 特征 | 寄存器 | LL | HAL |
|---|---|---|---|
| 句柄结构体 | 无 | 无/极少 | 有(每个外设一个) |
| 回调函数 | 无 | 无 | 有 |
| 函数前缀 | 无(直接寄存器) | LL_ |
HAL_ |
| 代码量 | 最小 | 小 | 大 |
| 可移植性 | 差 | 中 | 好 |
HAL_UART_Receive_IT(&huart1, buf, 10) 是 HAL(句柄 + IT 模式 + 回调);LL_USART_ReceiveData8(USART1) 是 LL(直接读寄存器、无句柄)| 相邻概念 | 关系 | 孤立理解会犯的错 |
|---|---|---|
| 中断优先级 | HAL 的 IT 模式本质是中断,也要配 NVIC | 只调 HAL_XX_IT 不配 NVIC,中断不来 |
| DMA | HAL 的 DMA 模式就是主题 33 那套 | 以为 DMA 是 HAL 独有,寄存器也有 |
| 时钟树 | CubeMX 的 SystemClock_Config 就是主题 34 那套 |
改时钟忘配 Flash 等待,HAL 也死机 |
| 元学习法 | 每个主题背后都是"选型判别规则" | 背 API 名不背"什么时候用哪个" |
2026-08-18
每主题一页,复习时只翻本目录。