tags: [source-summary] type: source source: "尚硅谷嵌入式技术之STM32单片机(扩展篇)V1.0.0 — 附录章节" author: "尚硅谷研究院" date: 2026-07-15
用生活理解:HAL 库就像汽车上的自动变速箱——你不用关心底下齿轮怎么换挡(寄存器操作),只管踩油门(调 API 函数)就行。Keil MDK 的编译过程就像做菜——编译(切菜)→ 链接(配菜摆盘)→ 格式转换(装盘)→ 下载(上菜)。
HAL = Hardware Abstraction Layer(硬件抽象层),是 ST 官方提供的跨平台硬件驱动库。与标准库(StdPeriph)不同,HAL 库的 API 风格在所有 STM32 系列(F0/F1/F4/F7/H7)上统一。
HAL 库为每个外设提供三种操作模式:
| 模式 | 特点 | 适用场景 |
|---|---|---|
| 轮询(Polling) | 阻塞式,简单直接 | 调试、低速通信(如 I2C 读 EEPROM) |
| 中断(IT) | 非阻塞,CPU 可做其他事 | 接收不定长数据(如串口 RX) |
| DMA | 非阻塞,硬件自动搬运 | 大批量数据传输(如 ADC 多通道扫描) |
CubeMX 自动生成的项目目录结构:
Project/
├── Core/
│ ├── Inc/ # 头文件
│ │ ├── main.h
│ │ ├── gpio.h
│ │ ├── usart.h
│ │ └── stm32f1xx_it.h
│ └── Src/ # 源文件
│ ├── main.c # 主函数 + USER CODE
│ ├── gpio.c # GPIO 初始化(CubeMX 自动生成)
│ ├── usart.c # USART 初始化
│ ├── stm32f1xx_it.c # 中断服务函数
│ ├── stm32f1xx_hal_msp.c # 外设 MSP 初始化
│ └── system_stm32f1xx.c # 系统时钟配置
├── Drivers/
│ ├── CMSIS/ # 内核定义(core_cm3.h 等)
│ └── STM32F1xx_HAL_Driver/ # HAL 库源码
│ ├── Inc/ # HAL 头文件
│ └── Src/ # HAL 源文件
└── Project.ioc # CubeMX 配置文件
int main(void)
{
/* USER CODE BEGIN 1 */
// 用户预初始化代码(如变量声明)
/* USER CODE END 1 */
// ==== MCU 配置(由 CubeMX 生成,不要修改)====
HAL_Init(); // HAL 库初始化(SysTick 时基、NVIC 优先级分组等)
SystemClock_Config(); // 系统时钟配置(72MHz)
MX_GPIO_Init(); // GPIO 初始化
MX_USART1_UART_Init(); // USART1 初始化
/* 其他外设初始化... */
/* USER CODE BEGIN 2 */
// 用户初始化代码(如自定义变量初始化)
/* USER CODE END 2 */
while (1)
{
/* USER CODE BEGIN WHILE */
// 主循环逻辑
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
重要:CubeMX 生成的代码在
/* USER CODE BEGIN */和/* USER CODE END */之间保留,重新生成时不会被覆盖。
| 宏 | 说明 |
|---|---|
UNUSED(var) |
标记未被使用的变量(避免编译器警告) |
IS_XXX(VAL) |
参数校验宏(如 IS_GPIO_PIN(VAL)) |
__HAL_RCC_GPIOA_CLK_ENABLE() |
开启外设时钟(替代直接操作 RCC) |
__HAL_UART_ENABLE_IT(huart, IT) |
使能中断 |
MODIFY_REG(REG, CLEARMASK, SETMASK) |
安全修改寄存器位域 |
源文件(.c/.s) → 编译(armcc/armasm) → 目标文件(.o)
目标文件(.o) + 库 → 链接(armlink) → 镜像文件(.axf/.elf)
镜像文件(.axf) → 格式转换(fromelf) → 烧录文件(.bin/.hex)
烧录文件(.bin/.hex) → 下载 → 芯片 Flash
各阶段详解:
| 阶段 | 工具 | 输入 | 输出 | 说明 |
|---|---|---|---|---|
| 1. 编译 | armcc(C) armasm(汇编) | main.c, *.s |
*.o |
源文件→机器码,含调试信息 |
| 2. 链接 | armlink | *.o, 库文件 |
*.axf/.elf |
合并所有 .o,分配地址,解析符号 |
| 3. 格式转换 | fromelf | *.axf |
*.bin/.hex |
去掉调试信息,生成烧录用文件 |
| 4. 下载 | ST-Link Utility | *.bin/.hex |
Flash | 烧录到芯片 |
Flash (ROM) RAM (SRAM)
┌──────────────────┐ ┌──────────────────┐
│ .text (Code) │ │ .data (RW-data) │ 已初始化全局变量
│ 程序代码 │ │ (从Flash复制过来) │
├──────────────────┤ ├──────────────────┤
│ .rodata │ │ .bss (ZI-data) │ 零初始化全局变量
│ 只读数据(const) │ │ (全为0) │
├──────────────────┤ ├──────────────────┤
│ .data (初值) │──────→│ 堆(Heap) │
│ 已初始化全局变量 │ 复 │ malloc分配区域 │
│ 的初始值 │ 刻 ├──────────────────┤
│ │ │ 栈(Stack) ⬇⬆ │
│ │ │ 局部变量/函数调用 │
└──────────────────┘ └──────────────────┘
编译输出信息解读:
| 段 | 含义 | 存储位置 | 生成时的内容 |
|---|---|---|---|
| Code | 程序代码(指令) | Flash | 函数体、指令序列 |
| RO-data | 只读数据 | Flash | const 变量、字符串常量 |
| RW-data | 可读写数据 | Flash 存初值→RAM 运行 | 已初始化的全局/静态变量 |
| ZI-data | 零初始化数据 | RAM | 未初始化/初始化为 0 的全局变量 |
编译输出示例:
Program Size: Code=1234 RO-data=56 RW-data=12 ZI-data=200
Flash 占用 = Code + RO-data + RW-data = 1234 + 56 + 12 = 1302 字节
RAM 占用 = RW-data + ZI-data = 12 + 200 = 212 字节
在 Debug 模式下,通过 Peripherals 菜单打开外设寄存器窗口:
Peripherals → General Purpose I/O → GPIOA
可以直接查看和修改寄存器值,非常适合调试硬件配置是否正确。
| 断点类型 | 说明 |
|---|---|
| 普通断点 | F2 或点击行号左侧 |
| 条件断点 | 右键断点 → Condition(如 count > 10) |
| 日志断点 | 右键断点 → Action → "printf x"(不停止,只打印) |
| 访问断点 | 数据地址读写触发(Data Breakpoint) |
| 外设 | 初始化 | 轮询收发 | 中断收发 | DMA 收发 |
|---|---|---|---|---|
| GPIO | HAL_GPIO_Init() |
ReadPin/WritePin |
EXTI_Callback |
— |
| UART | HAL_UART_Init() |
Transmit/Receive |
_IT |
_DMA |
| SPI | HAL_SPI_Init() |
Transmit/Receive |
_IT |
_DMA |
| I2C | HAL_I2C_Init() |
Master_Transmit/Receive |
_IT |
_DMA |
| ADC | HAL_ADC_Init() |
Start/Stop |
_IT |
_DMA |
| TIM | HAL_TIM_Init() |
— | PWM/OC/IC_Start_IT |
— |