STM32 基础概念
本教程将带你了解 STM32 微控制器的核心概念,帮助你建立坚实的嵌入式开发基础。我们将以 STM32F103C8T6(Blue Pill 开发板)为例进行讲解。
目录
STM32 系列简介
什么是 STM32?
STM32 是意法半导体(STMicroelectronics)推出的一系列 32 位微控制器,基于 ARM Cortex-M 处理器内核。STM32 系列以其高性能、低功耗和丰富的外设而广受欢迎,是嵌入式开发的首选之一。
STM32 系列分类
STM32 系列按性能和应用场景分为多个系列:
| 系列 | 内核 | 特点 | 典型应用 |
|---|---|---|---|
| STM32F0 | Cortex-M0 | 入门级,低成本 | 简单控制、家电 |
| STM32F1 | Cortex-M3 | 主流型,性价比高 | 工业控制、消费电子 |
| STM32F4 | Cortex-M4 | 高性能,带 DSP | 电机控制、音频处理 |
| STM32F7 | Cortex-M7 | 超高性能 | 图形界面、高级控制 |
| STM32L | 多种内核 | 低功耗 | 电池供电设备 |
STM32F103C8T6 规格详解
我们使用的 STM32F103C8T6 属于 F1 系列,俗称"Blue Pill"(蓝色药丸),是一款非常适合入门学习的开发板。
核心参数
| 参数 | 数值 | 说明 |
|---|---|---|
| 内核 | ARM Cortex-M3 | 32 位 RISC 处理器 |
| 最高主频 | 72 MHz | 本模板配置为 64 MHz |
| Flash 存储 | 64 KB | 存储程序代码 |
| SRAM | 20 KB | 运行时数据存储 |
| GPIO 引脚 | 37 个 | 通用输入/输出 |
| 定时器 | 3 个通用 + 1 个高级 | 用于计时和 PWM |
| 通信接口 | USART×3, SPI×2, I2C×2 | 串行通信 |
| ADC | 2 个 12 位 ADC | 模数转换 |
| 封装 | LQFP48 | 48 引脚封装 |
命名规则解读
STM32F103C8T6
│ │ ││││└─ 温度范围:-40~85°C
│ │ │││└── 封装:LQFP48
│ │ ││└── Flash 容量:64KB (8 = 64KB)
│ │ │└── 引脚数:48 引脚 (C = 48)
│ │ └── 型号:主流型
│ └── 子系列:F103 (增强型)
└── 意法半导体 32 位微控制器ARM Cortex-M3 架构概述
什么是 ARM Cortex-M3?
Cortex-M3 是 ARM 公司专门为微控制器设计的 32 位处理器内核,具有以下特点:
- 32 位架构:数据总线、地址总线均为 32 位
- RISC 设计:精简指令集,指令执行效率高
- Thumb-2 指令集:混合使用 16 位和 32 位指令,代码密度高
Cortex-M3 核心特性
1. 寄存器组
Cortex-M3 有 16 个通用寄存器(R0-R15):
| 寄存器 | 用途 |
|---|---|
| R0-R7 | 通用寄存器,低 8 个 |
| R8-R12 | 通用寄存器,高 5 个 |
| R13 (SP) | 堆栈指针 |
| R14 (LR) | 链接寄存器,保存返回地址 |
| R15 (PC) | 程序计数器 |
2. NVIC(嵌套向量中断控制器)
NVIC 是 Cortex-M3 的中断管理核心:
- 支持 240 个外部中断
- 支持 16 级可编程优先级
- 支持中断嵌套(高优先级中断可打断低优先级)
- 低延迟中断响应(12 个时钟周期)
3. SysTick 定时器
系统定时器,用于产生周期性中断:
// 在模板中,SysTick 用于 HAL 库的延时功能
void SysTick_Handler(void) {
HAL_IncTick(); // 更新系统滴答计数
}为什么选择 Cortex-M3?
对于初学者来说,Cortex-M3 有以下优势:
- 易于学习:指令集简洁,寄存器数量适中
- 实时性好:NVIC 提供确定性的中断响应
- 生态完善:大量开发工具和库支持
- 成本低廉:适合教学和原型开发
存储器映射
什么是存储器映射?
存储器映射是指将物理存储器(Flash、SRAM)和外设寄存器映射到统一的地址空间。Cortex-M3 使用 32 位地址总线,可寻址 4GB 空间。
STM32F103 存储器映射结构
地址范围 | 区域 | 大小 | 用途
---------------------|---------------|---------|------------------
0x0000 0000 - 0x1FFF FFFF | Code | 512 MB | Flash/系统存储器
0x2000 0000 - 0x3FFF FFFF | SRAM | 512 MB | SRAM 区域
0x4000 0000 - 0x5FFF FFFF | Peripheral | 512 MB | 外设寄存器
0x6000 0000 - 0x9FFF FFFF | External | 1 GB | 外部扩展
0xA000 0000 - 0xDFFF FFFF | Reserved | 1 GB | 保留
0xE000 0000 - 0xFFFF FFFF | System | 512 MB | 系统区域Flash 存储器(程序存储区)
Flash 用于存储程序代码和常量数据:
| 区域 | 地址范围 | 大小 | 说明 |
|---|---|---|---|
| 主存储器 | 0x0800 0000 起 | 64 KB | 用户程序 |
| 系统存储器 | 0x1FFF 0000 | 2 KB | Bootloader |
| 选项字节 | 0x1FFF F800 | - | 配置选项 |
重要概念:程序从 Flash 中执行,CPU 直接从 Flash 取指令。
SRAM(数据存储区)
SRAM 用于存储运行时数据:
- 地址范围:0x2000 0000 起
- 大小:20 KB
- 用途:
- 全局变量
- 静态变量
- 堆栈(Stack)
- 堆(Heap)
外设寄存器区域
外设寄存器映射在 0x4000 0000 开始的区域:
外设地址示例:
├── GPIOA: 0x4001 0800 - 0x4001 0BFF
├── GPIOB: 0x4001 0C00 - 0x4001 0FFF
├── GPIOC: 0x4001 1000 - 0x4001 13FF
├── USART1: 0x4001 3800 - 0x4001 3BFF
└── TIM1: 0x4001 2C00 - 0x4001 2FFF时钟系统
为什么时钟如此重要?
时钟是微控制器的"心跳",所有操作都依赖时钟信号。正确的时钟配置是程序正常运行的基础。
STM32F103 时钟源
STM32F103 有多个时钟源:
| 时钟源 | 频率 | 说明 |
|---|---|---|
| HSI | 8 MHz | 内部高速振荡器(出厂校准) |
| HSE | 8-25 MHz | 外部高速振荡器(晶振) |
| LSI | 40 kHz | 内部低速振荡器(看门狗) |
| LSE | 32.768 kHz | 外部低速振荡器(RTC) |
时钟树结构
┌─────────────────────────────────────────┐
│ 时钟源 │
└─────────────────────────────────────────┘
│
┌─────────────┬───────────────┼───────────────┬─────────────┐
▼ ▼ ▼ ▼ │
┌───────┐ ┌───────┐ ┌───────┐ ┌───────┐ │
│ HSI │ │ HSE │ │ LSI │ │ LSE │ │
│ 8MHz │ │外部晶振│ │ 40kHz │ │32.768kHz│ │
└───┬───┘ └───┬───┘ └───────┘ └───────┘ │
│ │ │
│ ┌────────┴────────┐ │
│ │ │ │
▼ ▼ ▼ │
┌───────────────┐ ┌───────────────┐ │
│ PLL │ │ CSS │ │
│ (锁相环) │ │ (时钟安全) │ │
│ ×4/×8/×9...×16│ └───────────────┘ │
└───────┬───────┘ │
│ │
▼ │
┌───────────────┐ │
│ SYSCLK │ ← 系统时钟(最高 72 MHz) │
│ 系统时钟 │ │
└───────┬───────┘ │
│ │
┌───────┴───────┬─────────────────┬─────────────────┐ │
▼ ▼ ▼ ▼ │
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ AHB │ │ APB1 │ │ APB2 │ │ USB │ │
│ 总线 │ │ 总线 │ │ 总线 │ │ 时钟 │ │
│ ≤72MHz │ │ ≤36MHz │ │ ≤72MHz │ │ 48MHz │ │
└────┬────┘ └────┬────┘ └────┬────┘ └─────────┘ │
│ │ │ │
▼ ▼ ▼ │
┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ GPIO │ │ USART │ │ ADC │ │
│ SRAM │ │ TIM2-7 │ │ TIM1 │ │
│ Flash │ │ I2C │ │ SPI1 │ │
│ DMA │ │ CAN │ │ GPIO │ │
└─────────┘ └─────────┘ └─────────┘ │模板中的时钟配置
让我们看看模板项目中的时钟配置(system.c):
void SystemClock_Config(void) {
RCC_OscInitTypeDef osc = {0};
osc.OscillatorType = RCC_OSCILLATORTYPE_HSI; // 使用 HSI 内部振荡器
osc.HSIState = RCC_HSI_ON; // 开启 HSI
osc.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; // 默认校准值
osc.PLL.PLLState = RCC_PLL_ON; // 开启 PLL
osc.PLL.PLLSource = RCC_PLLSOURCE_HSI_DIV2; // HSI/2 作为 PLL 输入
osc.PLL.PLLMUL = RCC_PLL_MUL16; // PLL 倍频 ×16
HAL_RCC_OscConfig(&osc); // 配置振荡器时钟计算:
HSI = 8 MHz
PLL 输入 = HSI / 2 = 4 MHz
PLL 输出 = 4 MHz × 16 = 64 MHz
系统时钟 SYSCLK = 64 MHz继续看总线时钟配置:
RCC_ClkInitTypeDef clk = {0};
clk.ClockType = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK |
RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
clk.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; // 使用 PLL 作为系统时钟
clk.AHBCLKDivider = RCC_SYSCLK_DIV1; // HCLK = 64 MHz
clk.APB1CLKDivider = RCC_HCLK_DIV2; // APB1 = 32 MHz
clk.APB2CLKDivider = RCC_HCLK_DIV1; // APB2 = 64 MHz
HAL_RCC_ClockConfig(&clk, FLASH_LATENCY_2); // 配置时钟
}总线时钟总结:
| 总线 | 时钟源 | 分频 | 最终频率 | 连接的外设 |
|---|---|---|---|---|
| AHB | SYSCLK | 1 | 64 MHz | GPIO, DMA, Flash |
| APB1 | HCLK | 2 | 32 MHz | USART2/3, TIM2-7, I2C |
| APB2 | HCLK | 1 | 64 MHz | USART1, SPI1, ADC, TIM1 |
为什么 APB1 要降频?
APB1 总线最高只能运行在 36 MHz,所以必须分频。这是硬件限制,设计时需要注意。
GPIO 基础
什么是 GPIO?
GPIO(General Purpose Input/Output,通用输入/输出)是微控制器与外部世界交互的基本接口。每个 GPIO 引脚都可以配置为输入或输出模式。
GPIO 的基本功能
- 输出模式:控制 LED、继电器、蜂鸣器等
- 输入模式:读取按键、传感器信号等
- 复用功能:USART、SPI、I2C 等通信接口
GPIO 工作模式
STM32 的 GPIO 支持 8 种工作模式:
输入模式(4 种)
| 模式 | 说明 | 典型应用 |
|---|---|---|
| 浮空输入 | 无上拉/下拉,高阻态 | 外部信号读取 |
| 上拉输入 | 内部上拉电阻,默认高电平 | 按键检测(低电平有效) |
| 下拉输入 | 内部下拉电阻,默认低电平 | 按键检测(高电平有效) |
| 模拟输入 | 模拟信号输入 | ADC 采样 |
输出模式(4 种)
| 模式 | 说明 | 典型应用 |
|---|---|---|
| 推挽输出 | 可输出高/低电平,驱动能力强 | LED 控制 |
| 开漏输出 | 只能拉低,需外部上拉 | I2C 通信 |
| 复用推挽 | 外设控制输出 | USART TX |
| 复用开漏 | 外设控制开漏 | I2C SDA/SCL |
推挽输出 vs 开漏输出
推挽输出(Push-Pull):
VCC
│
┌────┴────┐
│ │
┌┴┐ ┌┴┐
│ │ P-MOS │ │ N-MOS
└┬┘ └┬┘
│ │
└────┬────┘
│
引脚 ────► 可输出高电平或低电平
开漏输出(Open-Drain):
VCC
│
┌┴┐ 外部上拉电阻
│ │
└┬┘
│
┌────┴────┐
│ │
┌┴┐ ┌─┐
│ │ N-MOS │ │ (无 P-MOS)
└┬┘ └─┘
│
└────┬────┘
│
引脚 ────► 只能拉低,释放后由上拉电阻拉高模板中的 GPIO 配置
让我们分析模板项目中的 GPIO 配置(main.c):
// 1. 定义 GPIO 初始化结构体
GPIO_InitTypeDef def;
// 2. 配置 GPIO 模式
def.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出模式
// 3. 选择引脚
def.Pin = GPIO_PIN_13; // PC13 引脚
// 4. 配置速度
def.Speed = GPIO_SPEED_FREQ_LOW; // 低速(省电)
// 5. 配置上拉/下拉
def.Pull = GPIO_PULLDOWN; // 下拉电阻
// 6. 使能 GPIO 时钟
__HAL_RCC_GPIOC_CLK_ENABLE();
// 7. 初始化 GPIO
HAL_GPIO_Init(GPIOC, &def);
// 8. 设置初始状态
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);GPIO 操作函数
HAL 库提供了以下 GPIO 操作函数:
// 写引脚状态
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET); // 输出高电平
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET); // 输出低电平
// 翻转引脚状态
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); // 高→低 或 低→高
// 读取引脚状态
GPIO_PinState state = HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_13);
// 返回 GPIO_PIN_SET 或 GPIO_PIN_RESETLED 闪烁示例
模板中的主循环实现了 LED 闪烁:
while (1) {
HAL_Delay(1000); // 延时 1000ms = 1秒
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); // 翻转 LED 状态
}注意:Blue Pill 开发板上的 LED 连接在 PC13 引脚,低电平点亮(板载 LED 是共阳极接法)。
中断基础
什么是中断?
中断是一种机制,允许外部事件或内部事件"打断"正在执行的程序,转而去执行特定的处理函数(中断服务程序),处理完成后返回原程序继续执行。
中断的作用
- 实时响应:及时处理紧急事件
- 提高效率:避免轮询等待,CPU 可以做其他事情
- 降低功耗:CPU 可以休眠,等待中断唤醒
NVIC(嵌套向量中断控制器)
Cortex-M3 的 NVIC 管理所有中断:
- 优先级:0-15 级,数值越小优先级越高
- 嵌套:高优先级中断可以打断低优先级中断
- 向量表:中断服务程序的地址表
常见中断类型
| 类型 | 说明 | 示例 |
|---|---|---|
| 外部中断 | GPIO 引脚触发 | 按键检测 |
| 定时器中断 | 定时器溢出 | 周期性任务 |
| 通信中断 | USART/SPI/I2C | 数据收发 |
| ADC 中断 | 转换完成 | 模拟量采集 |
| 系统中断 | SysTick | 系统滴答 |
SysTick 中断示例
模板中使用了 SysTick 中断:
// SysTick 中断服务程序
void SysTick_Handler(void) {
HAL_IncTick(); // 更新 HAL 库的系统滴答计数
}这个中断每 1ms 触发一次(由 HAL_Init() 配置),用于:
HAL_Delay()延时功能HAL_GetTick()获取系统运行时间
中断优先级配置
// 设置中断优先级分组
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
// 配置外部中断优先级
HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0); // 抢占优先级0,子优先级0
HAL_NVIC_EnableIRQ(EXTI0_IRQn); // 使能中断HAL 库概念
什么是 HAL 库?
HAL(Hardware Abstraction Layer,硬件抽象层)库是 ST 官方提供的驱动库,它将底层寄存器操作封装成易用的 API 函数。
HAL 库的优势
| 特点 | 说明 |
|---|---|
| 可移植性 | 同一 API 可用于不同 STM32 系列 |
| 易用性 | 无需了解寄存器细节 |
| 安全性 | 内置参数检查 |
| 兼容性 | 支持 RTOS 和裸机开发 |
HAL 库 vs 寄存器操作
寄存器方式(直接操作):
// 直接操作寄存器点亮 LED
GPIOC->BSRR = GPIO_PIN_13; // 置位(输出高电平)
GPIOC->BRR = GPIO_PIN_13; // 复位(输出低电平)HAL 库方式(推荐):
// 使用 HAL 库函数
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);HAL 库模块结构
从 stm32f1xx_hal_conf.h 可以看到 HAL 库的模块化设计:
// HAL 模块启用/禁用配置
#define HAL_MODULE_ENABLED // HAL 核心模块
#define HAL_GPIO_MODULE_ENABLED // GPIO 模块
#define HAL_RCC_MODULE_ENABLED // 时钟控制模块
#define HAL_UART_MODULE_ENABLED // UART 模块
#define HAL_TIM_MODULE_ENABLED // 定时器模块
#define HAL_ADC_MODULE_ENABLED // ADC 模块
// ... 更多模块模块化优势:
- 只编译需要的模块,减少代码体积
- 未使用的模块可以注释掉,节省 Flash 空间
HAL 库常用函数模式
HAL 库遵循一致的命名和调用模式:
// 1. 初始化结构体
XXX_InitTypeDef config;
config.参数1 = 值1;
config.参数2 = 值2;
// 2. 使能时钟
__HAL_RCC_XXX_CLK_ENABLE();
// 3. 调用初始化函数
HAL_XXX_Init(&config);
// 4. 使用功能函数
HAL_XXX_Function(...);HAL 库初始化流程
模板中的初始化顺序:
int main() {
// 1. HAL 库初始化
HAL_Init();
// 2. 系统时钟配置
SystemClock_Config();
// 3. 外设初始化
// ... GPIO, UART, etc.
// 4. 主循环
while (1) {
// 应用程序代码
}
}HAL_Init() 做了什么?
HAL_Init() {
// 1. 设置中断优先级分组
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
// 2. 配置 SysTick 为 1ms 中断
SysTick_Config(SystemCoreClock / 1000);
// 3. 初始化 HAL 库内部状态
// ...
}总结
核心概念回顾
| 概念 | 要点 |
|---|---|
| STM32F103C8T6 | 72MHz Cortex-M3, 64KB Flash, 20KB SRAM |
| Cortex-M3 | 32位 RISC, Thumb-2 指令集, NVIC 中断控制器 |
| 存储器映射 | Flash(程序), SRAM(数据), 外设寄存器 |
| 时钟系统 | HSI/HSE → PLL → SYSCLK → AHB/APB 总线 |
| GPIO | 8 种模式,推挽/开漏输出,上拉/下拉输入 |
| 中断 | NVIC 管理,优先级嵌套,实时响应 |
| HAL 库 | 硬件抽象层,简化外设编程 |
学习路径建议
- 掌握基础:GPIO 输入/输出、延时、LED 控制
- 理解时钟:时钟源、PLL、总线分频
- 学习通信:USART 串口通信
- 深入定时器:定时中断、PWM 输出
- 探索 ADC:模拟信号采集
- 进阶主题:DMA、RTOS、低功耗
下一步学习
- GPIO 深入教程(即将推出)
- 时钟系统详解(即将推出)
- 中断与 NVIC(即将推出)
附录:常用缩写对照
| 缩写 | 英文全称 | 中文含义 |
|---|---|---|
| GPIO | General Purpose Input/Output | 通用输入/输出 |
| HAL | Hardware Abstraction Layer | 硬件抽象层 |
| RCC | Reset and Clock Control | 复位和时钟控制 |
| NVIC | Nested Vectored Interrupt Controller | 嵌套向量中断控制器 |
| PLL | Phase Locked Loop | 锁相环 |
| HSI | High Speed Internal | 内部高速振荡器 |
| HSE | High Speed External | 外部高速振荡器 |
| AHB | Advanced High-performance Bus | 高级高性能总线 |
| APB | Advanced Peripheral Bus | 高级外设总线 |
| Flash | - | 闪存(程序存储器) |
| SRAM | Static Random Access Memory | 静态随机存取存储器 |