1. STM32 概述与开发环境搭建

做嵌入式开发这么多年,我接触过不少 MCU 平台。但说实话,STM32 是我用得最顺手、也是项目里出现频率最高的一个系列。为什么?因为它的产品线实在太丰富了,从几块钱的 F0 到性能炸裂的 H7,几乎覆盖了你能想到的所有应用场景。

这一章,我们先从选型开始聊,然后快速过一遍 Cortex-M 内核的架构特点,最后把开发环境搭起来。嗯,环境搭建这块我踩过不少坑,后面会给你一些实用建议。

1.1 STM32 系列选型指南

STM32 目前有十几个系列,但常用的其实就那几个。我整理了一张表,方便你快速对比:

系列 内核 主频 典型应用 个人评价
STM32F0 Cortex-M0 48 MHz 简单控制、替代8位机 性价比之王,入门首选
STM32F1 Cortex-M3 72 MHz 工业控制、电机驱动 经典款,资料最多
STM32F4 Cortex-M4F 168 MHz 音频处理、图形界面 带 FPU,算力够用
STM32L4 Cortex-M4F 80 MHz 低功耗传感器、可穿戴 低功耗表现非常出色
STM32H7 Cortex-M7 480 MHz 高性能计算、AI边缘 性能怪兽,但功耗也高
STM32G0 Cortex-M0+ 64 MHz 成本敏感型产品 F0 的升级版,更省电
选型小建议: 我个人习惯先看外设需求,再算算力。比如项目需要 3 路 UART + 2 路 SPI,那就先筛出带这些外设的型号,再对比价格和功耗。千万别一上来就盯着主频看,容易选过头。

1.2 Cortex-M 内核架构简介

Cortex-M 系列是 ARM 专门为微控制器设计的内核。你想想看,它跟手机里用的 Cortex-A 系列最大的区别是什么?

说白了,Cortex-M 追求的是实时性和低功耗,而不是绝对性能。它没有 MMU(内存管理单元),跑不了 Linux,但中断响应速度极快——从收到中断信号到进入中断服务函数,通常只需要 12 个时钟周期。

这里有个关键概念叫中断向量表。Cortex-M 内核规定,地址 0x00000000 存放栈顶指针,0x00000004 存放复位向量。芯片上电后,CPU 自动从这两个地址加载 SP 和 PC,然后开始执行代码。我在项目中遇到过一个问题:有一次我把中断向量表放错了位置,结果芯片死活不进中断。排查了半天才发现,原来是向量表偏移寄存器没配置对。

Cortex-M 内核的三大核心特性:

  • Thumb-2 指令集:混合 16 位和 32 位指令,代码密度高
  • 嵌套向量中断控制器 (NVIC):支持 240 个中断,优先级可配置
  • 系统节拍定时器 (SysTick):专为操作系统设计的 24 位递减计数器

不同内核版本之间的差异,我列了个对比表:

内核 流水线 硬件除法 FPU DSP 指令 典型代表
Cortex-M0 3 级 无 无 无 STM32F0
Cortex-M3 3 级 有 无 无 STM32F1
Cortex-M4 3 级 有 单精度 有 STM32F4
Cortex-M7 6 级 有 双精度 有 STM32H7

下面这张图展示了 Cortex-M 内核的内部结构,以及它和 STM32 外设之间的关系:

Cortex-M 内核与 STM32 系统架构 Cortex-M 内核 CPU 核心 NVIC SysTick FPU (M4/M7) AHB / APB 总线矩阵 GPIO / 定时器 UART / SPI / I2C ADC / DAC DMA / 定时器 存储系统 Flash (程序) SRAM (数据) SWD / JTAG 说明 • 内核通过总线矩阵 访问外设和存储 • NVIC 管理所有中断 • 调试接口独立于 内核运行 • 支持断点和单步

1.3 开发环境搭建

开发 STM32 主要有三种 IDE 可选:Keil MDK、IAR EWARM 和 STM32CubeIDE。我三个都用过,说说我的感受。

Keil MDK

Keil 是 ARM 公司自家的 IDE,编译效率高,调试器好用。但有个问题——它收费,而且不便宜。不过对于初学者,Keil 提供了代码大小限制在 32KB 的免费版,学基础够用了。

避坑提醒: 我曾经在 Keil 里遇到过一个问题:工程路径里带了中文,结果编译报错说找不到文件。从那以后,我所有工程路径都只用英文和数字,空格都不加。

IAR EWARM

IAR 的编译器优化做得非常好,生成的代码体积通常比 Keil 小 10%-15%。如果你做的是 Flash 容量紧张的项目,IAR 是个好选择。不过它的界面风格比较老派,快捷键跟主流 IDE 不太一样,刚上手需要适应一下。

STM32CubeIDE

这个是 ST 官方基于 Eclipse 开发的免费 IDE。我个人现在主要用它,原因有三:

  • 免费:没有任何代码大小限制
  • 集成 CubeMX:图形化配置外设,自动生成初始化代码
  • GCC 编译器:跨平台,Windows/Linux/macOS 都能用

安装步骤其实很简单,去 ST 官网下载 STM32CubeIDE 安装包,一路 Next 就行。但要注意一点:安装路径不要有空格和中文。

1.4 STM32CubeMX 工具入门

CubeMX 是我认为 ST 做的最好的工具之一。它让你用图形化的方式配置芯片引脚、时钟、外设,然后自动生成初始化代码。说白了,以前要手写半天的时钟树配置,现在点几下鼠标就搞定了。

我第一次用 CubeMX 的时候,其实有点怀疑——自动生成的代码能靠谱吗?后来用了一段时间发现,它生成的 HAL 库代码质量很高,而且结构清晰。当然,性能敏感的部分我还是会自己手写寄存器操作,但日常的外设初始化,CubeMX 完全够用。

我的使用习惯:

  1. 先用 CubeMX 配置好所有外设和引脚
  2. 生成工程后,在 main.c 的用户代码区写业务逻辑
  3. 如果后续需要修改配置,回到 CubeMX 改,重新生成时用户代码不会丢失

注意:CubeMX 用 /* USER CODE BEGIN */ 和 /* USER CODE END */ 标记用户代码区域,你写的代码要放在这两个标记之间,否则重新生成时会被覆盖。

下面是一个用 CubeMX 生成的 GPIO 初始化代码示例:

// 这部分是 CubeMX 自动生成的
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  __HAL_RCC_GPIOA_CLK_ENABLE();   // 使能 GPIOA 时钟
  __HAL_RCC_GPIOC_CLK_ENABLE();   // 使能 GPIOC 时钟

  // 配置 PA5 为推挽输出,用于控制 LED
  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);

  // 配置 PC13 为输入,用于读取按键
  GPIO_InitStruct.Pin = GPIO_PIN_13;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
}

// 用户代码区:我在这里写业务逻辑
/* USER CODE BEGIN 0 */
void toggle_led(void)
{
  HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
}
/* USER CODE END 0 */

看到没?CubeMX 把时钟使能、引脚模式、上下拉这些细节都帮你处理好了。你只需要在用户代码区写自己的逻辑就行。

本章小结:

  • 选型先看外设,再算算力,最后对比价格和功耗
  • Cortex-M 内核以实时性和低功耗为核心,NVIC 和 SysTick 是重点
  • 开发环境推荐 STM32CubeIDE,免费且集成 CubeMX
  • CubeMX 能大幅提升开发效率,但要注意保护用户代码区