Skip to content

第3章 单片机编程

3 第 3 章 单片机编程

本章系统讲解 STM32 微控制器的硬件基础与编程实践,从芯片选型、最小开发板到 GPIO、时钟、电路基础和外设接口,并通过 CubeMX 开发典型项目巩固所学。

3.1 STM32 芯片简介

STM32 是意法半导体(STMicroelectronics,简称 ST)推出的一系列基于 ARM Cortex-M 内核的 32 位微控制器(MCU)产品线,广泛应用于工业控制、消费电子、机器人及物联网等领域。

3.1.1 命名规则

STM32 的型号名称遵循一套规律性命名约定,以常见型号 STM32F103C8T6 为例:

表 3-1 命名规则

字段 含义 示例说明
STM32 产品系列,32位 ARM 微控制器
F 产品子系列(F=通用型,H=高性能,L=低功耗,G=主流型等) F 通用型
103 具体产品线编号(103为基础型,407为高性能型等) 基础型
C 引脚数量(C=48脚,R=64脚,V=100脚,Z=144脚) 48 引脚
8 Flash 容量(6=32KB,8=64KB,B=128KB,C=256KB,E=512KB) 64 KB
T 封装类型(T=LQFP,H=BGA,U=VFQFPN) LQFP 封装
6 工作温度范围(6=−40~85°C,7=−40~105°C) 工业级

上表对命名规则中各方案的特性进行了横向对比,便于读者根据实际需求选择最合适的技术路线。

3.1.2 主要产品系列

ST 根据性能与应用场景,将 STM32 划分为多个子系列:

表 3-2 主要产品系列

系列 内核 主频 特点 典型型号
STM32F1(基础型) Cortex-M3 72 MHz 性价比高,入门首选 STM32F103C8T6
STM32F4(高性能型) Cortex-M4(带FPU) 168 MHz 浮点运算,适合复杂控制算法 STM32F407ZGT6
STM32F7(高级型) Cortex-M7 216 MHz 双精度FPU,高速外设 STM32F767IGT6
STM32H7(旗舰型) Cortex-M7 480 MHz 最高性能,双核可选 STM32H743IIT6
STM32L(低功耗型) Cortex-M0/M3/M4 32~80 MHz 超低功耗,适合电池供电设备 STM32L476RGT6
STM32G(主流型) Cortex-M0+/M4 64~170 MHz 替代 F1/F3,高集成度 STM32G431CBU6

在机器人控制课程中,以 STM32F103 系列作为核心学习平台,其 72 MHz 主频和丰富外设足以满足绝大多数底层控制任务。

3.1.3 核心特性

STM32 相较于传统 8 位单片机(如 51 系列、AVR)具有显著优势:

  • 32 位处理能力:一条指令可处理 32 位数据,运算效率远高于 8/16 位 MCU,适合执行 PID、矩阵运算等控制算法
  • 丰富的片上外设:集成 GPIO、USART、SPI、I2C、CAN、USB、ADC、DAC、定时器/PWM 等,减少外部芯片需求
  • 中断系统(NVIC):嵌套向量中断控制器支持多达 240 个外部中断,优先级可灵活配置,实现强实时响应
  • DMA 控制器:直接内存访问,无需 CPU 介入即可完成数据搬运,显著降低 CPU 占用率
  • 低功耗模式:支持多种睡眠/待机模式,适用于对功耗敏感的移动机器人应用
  • 完善的生态:官方 HAL/LL 库、STM32CubeMX 图形化配置工具及大量开源例程,学习资源丰富

3.1.4 开发生态

表 3-3 开发生态

工具/资源 说明
STM32CubeMX ST 官方图形化初始化代码生成工具,可自动配置时钟、外设,生成 HAL 初始化代码
STM32CubeIDE 基于 Eclipse 的官方集成开发环境,集成编译、调试、烧录功能
Keil MDK 业界主流的 ARM 开发工具,广泛用于嵌入式项目
HAL 库 硬件抽象层库,屏蔽寄存器细节,提供统一 API,便于跨型号移植
LL 库 底层驱动库,接近寄存器操作,效率更高,适合对性能要求严苛的场景
ST-Link ST 官方调试/烧录器,支持 SWD 和 JTAG 调试接口

3.2 Blue Pill 最小开发板

Blue Pill 是目前最流行的入门级 STM32 开发板之一,因其极低的成本(通常不足 10 元人民币)、完整的最小系统设计和紧凑的体积,成为学习嵌入式开发的首选硬件平台。它将上一节介绍的 STM32 最小系统全部集成在一块约 53 mm × 23 mm 的 PCB 上,开箱即用。

3.2.1 硬件规格

表 3-4 硬件规格

参数 规格
核心芯片 STM32F103C8T6
CPU 内核 ARM Cortex-M3
主频 最高 72 MHz
Flash 64 KB(程序存储)
SRAM 20 KB(运行内存)
工作电压 3.3V(板载 AMS1117-3.3 稳压)
供电输入 Micro-USB 5V 或引脚直接 3.3V/5V
晶振 8 MHz HSE + 32.768 kHz LSE
GPIO 数量 最多 32 个(PA0-PA15, PB0-PB15, PC13-PC15)
调试接口 SWD(4 针排针:GND / SWCLK / SWDIO / 3.3V)
板载 LED PC13 引脚连接一颗用户 LED(低电平点亮)
复位按键 1 个(NRST)
USB 接口 Micro-USB(供电或 USB-FS 通信)
尺寸 53 mm × 23 mm

上述参数配置是硬件规格的典型推荐值,实际工程中可根据硬件条件和性能需求进行适当调整。

3.2.2 引脚布局图

LED PC13 Blue Pill GND GND BOOT1 GND GND GND GND BOOT0 GND GND 3.3V 3.3V PB11 PB11 NRST NRST PB10 PB10 PA0 PA0 ADC PB9 PB9 PA1 PA1 ADC PB8 PB8 STM32F103 PA2 PA3 PA4 PA5 PA6 PA7 PB0 PB1 PB10 PB11 PA2 PA3 PA4 PA5 PA6 PA7 PB0 PB1 PB10 PB11 UART UART SPI SPI SPI SPI ADC ADC I2C I2C C8T6 PB7 PB6 PB5 PB4 PB3 PA15 PA12 PA11 PA10 PA9 PB7 PB6 PB5 PB4 PB3 PA15 PA12 PA11 PA10 PA9 Micro USB SWD PA8 PA8 GND [RESET键 ] PB15 PB15 SWCLK PB14 PB14 SWDIO PB13 PB13 3.3V PB12 PB12 PA0 PA7 ADC USART1 I2C1 I2C2 USB SWD PWM SPI1 SWCLK, PC13 (STM32F103C8T6 最小系统板) (从上到下) (从上到下) / / PA9/PA10 TX/RX(串口1,最常用串口) PB6/PB7 SCL/SDA PB10/PB11 SCL/SDA PA11/PA12 D-/D+ PA13/PA14 / [8MHz晶 ] [32K晶 ] SWDIO LED(

图 3-1 上图以框图形式描绘了引脚布局图的系统架构,清晰呈现了各模块之间的连接关系与信号流向。

3.2.3 板载资源说明

板载 LED(PC13)

Blue Pill 在 PC13 引脚上连接了一颗 LED,是学习 GPIO 控制的第一个实验对象。注意该 LED 为低电平点亮(输出 0 亮、输出 1 灭),与常规逻辑相反,原因是 PC13 的驱动能力较弱(最大 3 mA),电路采用了共阳极接法。

Micro-USB 接口

板上的 Micro-USB 有两个作用: - 供电:通过板载 AMS1117 稳压后为全板提供 3.3V - USB 通信:STM32F103 支持全速 USB(USB-FS 12Mbps),可将 Blue Pill 模拟为 CDC 虚拟串口、HID 设备等,但需要软件配置并在 USB D+ 线上额外接一颗 1.5 kΩ 上拉电阻

SWD 调试口

板子一侧引出了 4 针 SWD 排针(GND / SWCLK / SWDIO / 3.3V),连接 ST-Link 调试器后即可在 Keil 或 STM32CubeIDE 中完成程序烧录与断点调试。

BOOT 跳线

板上有两个跳线帽控制 BOOT0 和 BOOT1:

  • 正常运行:BOOT0 = 0,BOOT1 = 0 → 从 Flash 启动用户程序
  • 串口烧录:BOOT0 = 1,BOOT1 = 0 → 进入系统 Bootloader,可通过 USART1 用串口烧录程序(无需 ST-Link)

3.2.4 Blue Pill 与最小系统的对应关系

Blue Pill MCU AMS1117 3.3 + 8 MHz 32.768 kHz RESET按 BOOT0 BOOT1 4针SWD (HSE) (LSE) / 输入/输出 STM32F103C8T6( MCU旁 供RTC 使 10kΩ 拉+ 100nF电 容+ 线 排20Pin 有GPIO)

图 3-2 该框图展示了 Blue Pill 与最小系统的对应关系的核心结构,读者可以从中把握各功能单元的层次划分与协作方式。

小结:Blue Pill 开发板本质上就是一块 STM32F103 最小系统板,在掌握最小系统的原理后,便能看懂并读懂 Blue Pill 的原理图,进而自行设计定制化的嵌入式硬件。


3.3 STM32CubeMX 介绍

3.3.1 工具概述

STM32CubeMX 是 ST(意法半导体)官方推出的图形化初始化代码生成工具,是 STM32 生态的核心组成部分。它将繁琐的芯片初始化配置(时钟树、引脚模式、外设参数)转变为可视化的鼠标点选操作,并自动生成完整的 HAL 库初始化代码框架,极大地降低了 STM32 开发的入门门槛。

STM32CubeMX CubeMX C ... main.c stm32f1xx_hal.h SystemClock_Config() MX_GPIO_Init() MX_USART_Init()

图 3-3 上图直观呈现了工具概述的组成要素与数据通路,有助于理解系统整体的工作机理。

STM32CubeMX 可独立安装使用,也可作为插件集成进 STM32CubeIDE(ST 官方 IDE)中。两者配合使用是当前 STM32 开发的主流工作流。

3.3.2 关键特色

1.8.2.1 图形化芯片引脚配置

CubeMX 提供直观的芯片俯视图(Pin View),开发者可在图形界面上逐一点击每个引脚,从下拉菜单中选择功能,无需查阅数百页的数据手册。

CubeMX STM32F103C8T6 View) PA0 PA1 ... PC13 PC14 PC15 ] [未 ] ] GPIO mode: Output Push Pull GPIO speed: Low Initial: High User Label: LED [GPIO_Input [GPIO_Output [RCC_OSC32_IN] [RCC_OSC32_OUT] (LED 默认灭) Pin 中PC13

图 3-4 上图以框图形式描绘了图形化芯片引脚配置的系统架构,清晰呈现了各模块之间的连接关系与信号流向。

主要配置项:

表 3-5

配置项 说明
GPIO mode 选择引脚工作模式(推挽输出、开漏输出、上拉输入等8种)
GPIO speed 选择输出速率(Low / Medium / High,影响信号边沿陡峭程度)
GPIO Pull-up/Pull-down 内部上/下拉配置
Initial output level 初始电平(High 或 Low)
User Label 为引脚起别名(如 LED),生成代码时自动替换为宏定义

设置 User Label 后,CubeMX 生成的代码中会自动创建 #define LED_Pin GPIO_PIN_13#define LED_GPIO_Port GPIOC 宏,程序可读性更强。

1.8.2.2 可视化时钟树配置

时钟树配置是 STM32 开发中最容易出错的环节。CubeMX 提供时钟树图形界面(Clock Configuration),开发者直接填写目标主频,工具自动计算 PLL 倍频系数、总线分频比,并实时校验是否超出芯片规格。

CubeMX Blue Pill, 72 MHz) 8 MHz PLL MHz SYSCLK 72 AHB APB1 APB2 72 MHz 36 MHz 72 MHz I2C1 USART2、 HCLK 72 CubeMX (MHz) HSE( 1] 9] ÷ 1 ÷ 2 ÷ 1

图 3-5 该框图展示了可视化时钟树配置的核心结构,读者可以从中把握各功能单元的层次划分与协作方式。

1.8.2.3 外设图形化配置

对于每一个使用的外设(USART、SPI、I2C、ADC、定时器、USB 等),CubeMX 均提供独立的配置面板,以 USART1 为例:

Mode: Asynchronous Baud Rate: 115200 Word Length: 8 Bits Parity: None Stop Bits: 1 Hardware Flow Control: None Bits/s USART1 MX_USART1_UART_Init() HAL_UART_Init()

图 3-6 上图直观呈现了外设图形化配置的组成要素与数据通路,有助于理解系统整体的工作机理。

所有外设配置完成后,引脚冲突检测会自动高亮冲突引脚(红色),帮助开发者在写代码前就发现硬件分配错误。

1.8.2.4 自动生成完整工程框架

点击 "Generate Code" 按钮后,CubeMX 生成一个完整的可编译工程,其结构如下:

CubeMX Inc main.h User Label #define) Src main.c stm32f1xx it.c system stm32f1xx.c * USER CODE BEGIN END * STM32F1xx HAL Driver CMSIS MyProject.ioc HAL ARM CubeMX MyProject/ Core/ stm32f1xx_hal_conf.h Drivers/ ST

图 3-7 上图以框图形式描绘了自动生成完整工程框架的系统架构,清晰呈现了各模块之间的连接关系与信号流向。

关键设计:生成的 main.c 中大量使用 /* USER CODE BEGIN xxx *//* USER CODE END xxx */ 注释对:

int main(void)
{

    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();

    /* USER CODE BEGIN 2 */
    // ← 开发者在此添加初始化后的自定义代码
    /* USER CODE END 2 */

    while (1)
    {
        /* USER CODE BEGIN WHILE */
        HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);   // ← 开发者填写的业务逻辑

        HAL_Delay(500);

        /* USER CODE END WHILE */
    }
}

重要机制:用户代码只能写在 USER CODE BEGIN/END 之间。这样当需要修改硬件配置、重新用 CubeMX 生成代码时,工具会保留注释块内的代码,不会覆盖开发者已写的业务逻辑

1.8.2.5 中间件与 RTOS 支持

CubeMX 的 Middleware(中间件) 配置页面,支持一键集成多种软件组件:

CubeMX Middleware FreeRTOS Azure RTOS USB Device Mbed Host FatFS( SD Flash (ThreadX) TCP/IP) / / RTOS( LwIP( TLS(

图 3-8 该框图展示了中间件与 RTOS 支持的核心结构,读者可以从中把握各功能单元的层次划分与协作方式。

FreeRTOS 集成特别说明:

FreeRTOS 是嵌入式领域使用最广泛的开源实时操作系统(RTOS),支持多任务、信号量、消息队列、软件定时器等特性。在 CubeMX 中启用 FreeRTOS 后:

  1. CubeMX 自动将 FreeRTOS 源码加入工程
  2. 提供图形界面配置任务(Task)、堆栈大小、优先级
  3. 自动生成 freertos.c,包含任务创建代码框架
  4. 自动处理 SysTick 冲突(FreeRTOS 占用 SysTick,HAL 改用 TIM)
/* CubeMX 为 FreeRTOS 生成的任务框架示例 */
void StartDefaultTask(void *argument)
{
    /* USER CODE BEGIN StartDefaultTask */
    for(;;)
    {
        HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
        osDelay(500);   /* FreeRTOS 延时,让出 CPU 给其他任务 */
    }
    /* USER CODE END StartDefaultTask */
}

使用 FreeRTOS 后,LED 闪烁变成了一个独立的任务(Task),系统可同时运行多个任务(如同时控制 LED 和读取传感器),这正是机器人控制系统所必需的多任务能力。

3.3.3 CubeMX 与手写代码的对比

表 3-6 CubeMX 与手写代码的对比

维度 手写 HAL 代码 CubeMX 生成代码
初始化代码 需手动查手册编写 图形配置,自动生成
引脚冲突检测 依赖开发者经验 自动高亮冲突
时钟配置 手动计算 PLL 参数 填目标频率自动计算
重新配置 全部重写 修改 .ioc 文件,重新生成
FreeRTOS 集成 手动移植,步骤复杂 勾选即可,自动配置
适合阶段 深入理解底层原理 快速搭建项目,工程实践

本课程建议:使用 STM32CubeIDE + CubeMX 作为主要开发环境,借助图形化工具快速完成外设初始化,将精力集中在控制算法和机器人业务逻辑的编写上。


3.4 第一个嵌入式程序:LED 闪烁

LED 闪烁(Blink) 是嵌入式开发的 "Hello World",其本质是让连接 LED 的 GPIO 引脚交替输出高低电平,从而驱动 LED 周期性亮灭。

在 Blue Pill 上,LED 连接在 PC13 引脚,采用低电平点亮(输出 0 → 亮,输出 1 → 灭)。

3.4.1 程序基本结构

一个完整的 STM32 HAL 库 LED 闪烁程序由以下几个部分组成:

main.c #include HAL void static void GPIO 化HAL PC13 & SysTick 72 MHz) LED LED PLL MHz GPIO stm32f1xx_hal.h // SystemClock_Config(void); // MX_GPIO_Init(void); // main() HAL_Init() // SystemClock_Config() // MX_GPIO_Init() // while(1) SystemClock_Config() MX_GPIO_Init() 时500ms 时500ms 置HSE 振+ 频→ 频72 使 能GPIOC 置PC13 LED

图 3-9 上图直观呈现了程序基本结构的组成要素与数据通路,有助于理解系统整体的工作机理。

各部分职责说明:

表 3-7

部分 职责
头文件包含 引入 HAL 库定义,使代码能使用 HAL_GPIO_WritePin 等 API
函数声明 C 语言规范要求:函数在调用前需先声明(或定义在调用处之前)
main() 主函数 程序入口,依次完成初始化,然后进入永不退出的主循环
SystemClock_Config() 嵌入式程序必须自行配置时钟,此函数将主频从默认 8 MHz 提升至 72 MHz
MX_GPIO_Init() 专门负责 GPIO 初始化,将外设配置逻辑与业务逻辑分离,结构清晰

嵌入式 C 程序的典型模式:初始化阶段(运行一次)+ while(1) 主循环(永续运行)。所有嵌入式程序几乎都遵循此结构。

3.4.2 从电路图确定 LED 的引脚编号

编写程序之前,必须先从电路图(原理图,Schematic)中查找 LED 连接的是哪一个引脚。以 Blue Pill 为例,查找步骤如下:

第一步:在电路图中找到 LED 符号

电路图中 LED 用如下符号表示(三角形加竖线,旁有箭头表示发光):

阳极(+) 阴极(-) (LED 符号)

图 3-10 上图以框图形式描绘了从电路图确定 LED 的引脚编号的系统架构,清晰呈现了各模块之间的连接关系与信号流向。

第二步:顺着连线追踪到芯片引脚

STM32F103 PC13 LED Pin 2 1kΩ LED VCC 3.3V PC13 (PC13)

图 3-11 该框图展示了从电路图确定 LED 的引脚编号的核心结构,读者可以从中把握各功能单元的层次划分与协作方式。

第三步:读取引脚标号

在 Blue Pill 原理图中,LED 阴极一侧的连线上标有网络标号 PC13,由此确认:

  • LED 连接在 GPIOC 端口的第 13 号引脚

  • 在程序中写作 GPIOC + GPIO_PIN_13

  • 因为 LED 阳极接 VCC、阴极接 PC13,所以 PC13 输出低电平(0)时 LED 亮输出高电平(1)时 LED 灭

HAL PC13 PB5 PA0 P[端 ][引 ] GPIO[字 ] + HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, ...) HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, ...) HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, ...) GPIO_PIN_[编号]

图 3-12 上图直观呈现了从电路图确定 LED 的引脚编号的组成要素与数据通路,有助于理解系统整体的工作机理。

3.5 GPIO 管脚工作模式

确定了引脚编号之后,程序还必须配置该引脚的工作模式(通过 MX_GPIO_Init() 函数中的 GPIO_InitStruct.Mode 参数)。工作模式决定了引脚的电气行为:是作为输入还是输出?内部是什么电路结构?

STM32 GPIO 共有 8 种工作模式,分为输入类和输出类两大类:

STM32 GPIO (Float) 引脚不接上/下拉,电平完全由外部决定 (PU) (PD) (Analog) (PP) (OD) (AF_PP) 由片上外设(UART/SPI等)控制,推挽驱动 (AF_OD) 4种 (40kΩ至VCC) (40kΩ至GND) 通ADC, 4种 I2C 使

图 3-13 上图以框图形式描绘了 GPIO 管脚工作模式的系统架构,清晰呈现了各模块之间的连接关系与信号流向。

推挽输出与开漏输出的内部电路对比:

Pull) VCC Drain) [P管 ] 1 [上 ] [N管 GND 0 ] 0 [N管 ] GND 25mA) Push Open VCC(

图 3-14 该框图展示了 GPIO 管脚工作模式的核心结构,读者可以从中把握各功能单元的层次划分与协作方式。

表 3-8 各模式的典型应用场景:

工作模式 HAL 库常量 典型应用
浮空输入 GPIO_MODE_INPUT + GPIO_NOPULL 外部已有确定电平的信号输入
上拉输入 GPIO_MODE_INPUT + GPIO_PULLUP 按键检测(按下拉低,松开默认高)
下拉输入 GPIO_MODE_INPUT + GPIO_PULLDOWN 需要默认低电平的信号输入
模拟输入 GPIO_MODE_ANALOG ADC 采样、DAC 输出
推挽输出 GPIO_MODE_OUTPUT_PP 驱动 LED、继电器、蜂鸣器
开漏输出 GPIO_MODE_OUTPUT_OD I2C 总线、多设备共享总线
复用推挽输出 GPIO_MODE_AF_PP UART TX、SPI MOSI/SCK
复用开漏输出 GPIO_MODE_AF_OD I2C SDA/SCL

上表对 GPIO 管脚工作模式中各方案的特性进行了横向对比,便于读者根据实际需求选择最合适的技术路线。

3.5.1 浮空输入(Float)、上拉输入(PU)、下拉输入(PD)的使用场景

这三种输入模式的核心区别在于引脚默认电平由谁决定(外部电路/内部上拉/内部下拉),选择的核心原则是:让引脚在“无外部有效输入”时拥有确定的电平,避免电平飘忽不定导致误判。下面结合实际场景讲清楚每种模式的使用时机:

3.5.2 浮空输入(Float):外部有明确电平驱动时用

核心特性:引脚内部无上/下拉电阻,处于高阻态,电平完全由外部电路的驱动信号决定;若外部无驱动,引脚电平会受电磁干扰“飘忽不定”(0/1 乱跳)。

适用场景 & 实际例子: - 场景 1:通信类外设的输入引脚(外部有主动电平驱动) 例:USART 的 RX 引脚、SPI 的 MISO 引脚、I2C 的 SDA/SCL 引脚(I2C 总线通常外接上拉电阻,内部只需浮空)。 ✅ 原因:这些引脚的电平由通信对端(如串口模块、传感器、另一块 MCU)主动输出高/低电平,内部无需上/下拉,避免干扰外部驱动。 - 场景 2:ADC 模拟输入引脚 例:读取电压传感器的 ADC 通道引脚。

✅ 原因:上/下拉电阻会分流,导致 ADC 采样精度下降;浮空输入无内部电阻,能精准采集外部模拟电压。 - 场景 3:外部电路已自带上/下拉的引脚 例:外部电路已经接了 10kΩ上拉到 VCC 的按键引脚(此时内部选浮空,避免双重上拉)。

CubeMX 配置示例: 在「Pinout & Configuration」中选择引脚→「GPIO mode」→「Input Floating」。

3.5.3 上拉输入(PU):外部低电平有效、默认需高电平时用

核心特性:引脚内部通过约 40kΩ电阻上拉到 VCC,无外部输入时默认电平为高;当外部电路将引脚拉到 GND 时,电平变为低。

适用场景 & 实际例子: - 场景 1:轻触按键检测(最典型) 例:按键一端接 GND,另一端接 MCU 引脚(无外部上拉)。 ✅ 原因: - 按键松开:内部上拉使引脚为高电平(无操作); - 按键按下:引脚被拉到 GND,电平变低(检测到按键触发); - 避免浮空时按键松开的电平飘忽,导致误触发。 - 场景 2:外部低电平触发的中断引脚

例:传感器的“报警引脚”(正常时悬空/高,报警时拉到 GND)。 ✅ 原因:默认高电平(无报警),报警时外部拉低,触发中断,逻辑清晰且无误触发。

  • 场景 3:总线类引脚(无外部上拉时) 例:简单的单总线通信(如 DS18B20),内部上拉替代外部电阻(低速率场景)。

CubeMX 配置示例: 引脚→「GPIO mode」→「Input Pull-Up」;代码层面会自动生成:

HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET); // 启用上拉(部分系列需手动置1)

1.10.3.1 下拉输入(PD):外部高电平有效、默认需低电平时用

核心特性:引脚内部通过约 40kΩ电阻下拉到 GND,无外部输入时默认电平为低;当外部电路将引脚拉到 VCC 时,电平变为高。

适用场景 & 实际例子: - 场景 1:高电平触发的外部中断/检测 例:传感器的“数据就绪引脚”(无数据时悬空,有数据时拉到 VCC)。 ✅ 原因:默认低电平(无数据),有数据时外部拉高,触发检测,避免浮空误判。 - 场景 2:工业场景的高电平有效输入 例:外部继电器触点(闭合时接 VCC,断开时悬空),内部下拉使默认低电平,闭合时高电平。 - 场景 3:避免上电瞬间高电平误触发 例:某些外设上电瞬间可能出现电平抖动,下拉使默认低电平,仅当外部主动拉高时才响应。

CubeMX 配置示例: 引脚→「GPIO mode」→「Input Pull-Down」;代码层面:

HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET); // 启用下拉

1.10.3.2 关键注意事项(新手避坑)
  1. 上拉/下拉的电阻是“弱上拉/下拉”(约 40kΩ):
  2. 优点:功耗低,无需外部电阻;
  3. 缺点:驱动能力弱(如 I2C 总线高速场景需外接 1k~4.7kΩ强上拉,仅靠内部不够)。
  4. 浮空输入≠“不用管”
  5. 仅当外部有明确电平驱动时用,无外部驱动时(如悬空按键)绝对不能用,否则电平乱跳。
  6. ADC 输入必须用浮空:上/下拉电阻会分流,导致采样值偏离实际电压。
1.10.3.3 总结
  1. 浮空输入(Float):外部有主动电平驱动(如通信 RX、ADC 采样)时用,依赖外部确定电平;
  2. 上拉输入(PU):外部低电平有效(如接地按键),需要默认高电平时用;
  3. 下拉输入(PD):外部高电平有效,需要默认低电平时用; 核心原则:让引脚在“无有效输入”时拥有确定的默认电平,避免电平飘忽导致误判。

3.5.4 开漏输出(OD)、复用推挽输出(AF_PP)、复用开漏输出(AF_OD)的使用场景

选择的核心逻辑: - 是否需要多设备共享总线(线与逻辑)→ 选开漏(OD/AF_OD); - 是否需要外设复用引脚(非普通 GPIO 控制)→ 选复用(AF_PP/AF_OD); - 是否需要主动拉高电平(无需外部上拉)→ 选推挽(AF_PP)。

1.10.4.1 开漏输出(GPIO_MODE_OUTPUT_OD):普通 GPIO 的开漏模式

核心特性: - 内部仅能“拉低”电平(输出 0 时引脚接 GND),输出 1 时引脚处于高阻态(无法主动拉高),必须外接上拉电阻(1k~10kΩ到 VCC)才能输出高电平; - 支持线与逻辑:多个开漏引脚接同一条总线,只要有一个引脚拉低,总线就为低;全部释放(高阻)时,上拉电阻使总线为高(避免推挽输出短接烧引脚)。

适用场景 & 实际例子: - 场景 1:多 MCU 共享的状态引脚(如“忙”“报警”信号) 例:3 个 MCU 的 GPIO 引脚接同一条总线,任意一个 MCU 拉低引脚表示“忙”,全部释放则为“空闲”。 - 场景 2:普通 GPIO 模拟 I2C(软件 I2C) 例:用 PA0(SDA)、PA1(SCL)模拟 I2C,配置为开漏输出,外接 4.7kΩ上拉电阻(模拟 I2C 的线与逻辑)。 - 场景 3:电平转换(3.3V MCU 驱动 5V 外设) 例:3.3V MCU 的开漏引脚外接上拉到 5V,输出高电平时为 5V,低电平时为 0V,实现 3.3V→5V 电平兼容。

CubeMX & 代码示例: - CubeMX:引脚 → GPIO mode → Output Open Drain; - 代码(控制电平):

// PA0配置为开漏输出,外接上拉电阻
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET);  // 输出1(高阻态,上拉电阻拉高)
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET); // 输出0(拉低到GND)

1.10.4.2 复用推挽输出(GPIO_MODE_AF_PP):外设复用+推挽输出

核心特性: - “复用(AF)”:引脚不再由普通 GPIO 控制器管理,交给片内外设(UART、SPI、TIM 等)控制; - “推挽(PP)”:内部同时有拉高/拉低电路,能主动输出高(接 VCC)/低(接 GND)电平,驱动能力强(mA 级),无需外部上拉; - 不支持线与逻辑(多个推挽引脚短接会导致 VCC→GND 短路)。

适用场景 & 实际例子: - 场景 1:单设备独占的通信外设输出引脚 例:UART 的 TX 引脚(USART1_TX)、SPI 的 MOSI/SCK 引脚、TIM 的 PWM 输出引脚。 ✅ 原因:这些引脚仅由一个外设独占,需要稳定、主动的高/低电平输出,推挽的驱动能力和稳定性更优。 - 场景 2:需要强驱动能力的外设输出 例:SPI 主设备的 SCK 引脚,需带动多个从设备,推挽输出能提供足够的驱动电流。

CubeMX & 代码示例: - CubeMX:引脚 → GPIO mode → Alternate Function Push Pull → 选择对应外设功能(如 USART1_TX); - 代码(由外设驱动,无需手动控制引脚):

// 初始化UART(TX为AF_PP)
HAL_UART_Transmit(&huart1, (uint8_t*)"Hello", 5, 100); // TX引脚由USART1自动输出电平

1.10.4.3 复用开漏输出(GPIO_MODE_AF_OD):外设复用+开漏输出

核心特性: - 结合“外设复用”和“开漏”优点:引脚由外设直接控制,同时保持开漏特性(需外部上拉),支持多设备共享总线; - 比普通 GPIO 开漏更高效(硬件外设驱动,无需软件模拟)。

适用场景 & 实际例子: - 场景 1:硬件 I2C 总线(最典型) 例:STM32 的 I2C1_SDA(PB7)、I2C1_SCL(PB6)配置为 AF_OD,外接 4.7kΩ上拉电阻。 ✅ 原因:I2C 允许多主/从设备共享总线,必须依赖线与逻辑,硬件 I2C 外设直接驱动开漏引脚,比软件模拟更稳定。 - 场景 2:SMBus/CAN 总线 例:SMBus 的 Alert 引脚、CAN 总线的 TX 引脚,配置为 AF_OD,支持多节点共享总线。

CubeMX & 代码示例: - CubeMX:引脚 → GPIO mode → Alternate Function Open Drain → 选择对应外设功能(如 I2C1_SDA); - 代码(硬件 I2C 初始化):

// 初始化I2C(SDA/SCL为AF_OD)
HAL_I2C_Init(&hi2c1);
// 发送数据,引脚由I2C外设自动控制电平
HAL_I2C_Master_Transmit(&hi2c1, 0xA0, (uint8_t*)"Data", 4, 100);

1.10.4.4 新手避坑关键
  1. 开漏输出(OD/AF_OD)必须外接上拉电阻:无外部上拉时,输出 1(高阻)的引脚电平会飘忽不定,外设无法识别;
  2. 推挽输出(AF_PP)禁止多设备共享:多个推挽引脚短接会形成短路,烧毁引脚;
  3. 复用模式引脚不可普通 GPIO 控制:配置为 AF_PP/AF_OD 后,HAL_GPIO_WritePin 无法修改引脚电平,需通过外设驱动。

3.5.5 总结

  1. 开漏输出(OD):普通 GPIO 使用,需多设备共享/电平转换,外接上拉(如软件 I2C、多 MCU 共享引脚);
  2. 复用推挽输出(AF_PP):外设独占引脚,需主动高/低电平输出,无需上拉(如 UART TX、SPI SCK);
  3. 复用开漏输出(AF_OD):外设复用+多设备共享总线,需外接上拉(如硬件 I2C、CAN 总线); 核心选择逻辑:先判断是否需要外设复用(选 AF),再判断是否需要总线共享(选 OD),否则选 PP。

3.6 GPIO 工作模式的命名解释

要让学生通过字面意思+生活比喻秒记复用、推挽、开漏,核心是把专业术语拆成“好懂的名字逻辑”,不用死记硬背,具体拆解如下:

3.6.1 一、复用(Alternate Function,AF)——“引脚换主人,功能能复用”

1.11.1.1 字面拆解

“复用”=重复使用、换着用,不是只能干一件事。

1.11.1.2 通俗比喻

STM32 的引脚就像教室的公用插座: - 默认状态是“GPIO 模式”(当普通照明插座用); - 但还能插 UART、SPI、I2C 等外设(当通信/控制插座用)。

插座没换,只是换了“功能主人”——从 GPIO 换成外设用,这就是复用

1.11.1.3 核心记忆

引脚不换,功能复用:不是只能当 GPIO,还能被外设“借去用”。


3.6.2 二、推挽(Push-Pull,PP)——“能推高、能拉低,主动输出高低”

1.11.2.1 字面拆解

“推”=往上推,“挽”=往下拉,两个动作对应两种电平。

1.11.2.2 通俗比喻

引脚的输出电路里有两个“大力士”: - 一个往上推:直接接电源 VCC,把引脚“顶成高电平”(输出 1); - 一个往下拉:直接接地 GND,把引脚“拽成低电平”(输出 0)。

两个大力士轮流干活,主动把电平定死,不用靠外部帮忙,这就是推挽

1.11.2.3 核心记忆

双大力士,推高拉低:能主动输出高低电平,驱动能力强,不用外接上拉。


3.6.3 三、开漏(Open-Drain,OD)——“只能拉低,拉高靠外接”

1.11.3.1 字面拆解

“开漏”=漏极开路,简化记:“开”=开路(不直接接 VCC),“漏”=只留接 GND 的通路。

1.11.3.2 通俗比喻

引脚的输出电路里只有一个“往下拉的大力士”(接 GND),没有往上推的大力士: - 想输出 0:大力士拉低引脚(接 GND); - 想输出 1:引脚变成“高阻态”(悬空),必须靠外部接一个上拉电阻往上“拉”,才能变高电平。

就像“只有拉低的手,没有拉高的手,拉高得靠别人帮忙”,这就是开漏

1.11.3.3 核心记忆

单拉低,拉高靠外接:只能输出 0,1 必须靠上拉电阻,适合多设备共享总线。


3.6.4 四、终极记忆口诀(学生直接背)

  1. 复用:引脚换功能,外设来复用;
  2. 推挽:双大力士,推高拉低强;
  3. 开漏:单拉低,拉高靠外接。

3.6.5 五、组合场景再强化(秒懂)

  • 复用推挽(AF_PP):外设用+主动高低(比如 UART 的 TX、SPI 的 SCK,独占引脚,不用上拉);
  • 复用开漏(AF_OD):外设用+只能拉低(比如硬件 I2C,多设备共享,必须上拉);
  • 普通开漏(GPIO_OD):GPIO 用+只能拉低(比如软件 I2C,自己模拟,外接上拉)。

本程序使用的工作模式:推挽输出(GPIO_MODE_OUTPUT_PP)

LED 闪烁程序需要引脚主动输出高电平(LED 灭)和低电平(LED 亮),因此选择推挽输出模式。推挽输出具有较强的驱动能力(最大 25 mA),足以点亮 LED。

GPIO_InitStruct.Mode  = GPIO_MODE_OUTPUT_PP;   /* 推挽输出 ← LED 驱动必选 */
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;   /* 低速(2 MHz),驱动 LED 足够 */

为什么不用开漏输出? 开漏模式在引脚输出"1"时只是断开 N 管,引脚电平由外部上拉决定;而 Blue Pill 的 PC13 没有外接上拉电阻,因此无法靠开漏输出高电平,LED 将永远亮着。

1.11.5.1 程序执行流程

1. 72 MHz 2. STM32 使 3. 4. 500ms PC13 0 LED PC13 1 LED / SystemInit() RCC_APB2PeriphClockCmd() GPIO_Init() while(1) (低电平) (高电平) 置PLL, 使 能GPIOC 置PC13 时500ms

图 3-15 上图直观呈现了程序执行流程的组成要素与数据通路,有助于理解系统整体的工作机理。

1.11.5.2 STM32 CubeMX 时钟树最简默认设置方法

时钟树配置的核心是选择时钟源分配分频/倍频系数,对新手来说,无需外部硬件依赖、CubeMX 自动计算参数的方式就是最简单的,具体步骤如下:

1.11.5.3 核心原则

优先使用 HSI(内部高速时钟) 作为系统时钟源,无需外接晶振/时钟元件,CubeMX 自动完成分频/倍频计算,零手动修改成本。

1.11.5.4 具体操作步骤

① 打开 CubeMX,选择目标 STM32 芯片(如 STM32F103C8T6),进入工程配置界面; ② 进入「Pinout & Configuration」→「System Core」→「RCC」: - 「High Speed Clock (HSE)」选择 Disabled(关闭外部高速时钟); - 「High Speed Clock (HSI)」保持 Enabled(默认已开启,内部 8MHz/16MHz 时钟,依芯片型号而定); - 低速时钟(LSI/LSE)默认关闭即可,无需修改; ③ 进入「Clock Configuration」(时钟配置核心页面): - 点击页面左上角「Reset Clock Configuration」(重置时钟配置),CubeMX 会恢复芯片默认时钟树; - 此时系统时钟(SYSCLK)默认由 HSI 提供(如 F103 为 8MHz),HCLK(AHB 总线)、PCLK1(APB1)、PCLK2(APB2)均为默认分频(通常 1:1),无需手动调整; ④ 直接生成代码即可,无需额外修改时钟相关配置。

1.11.5.5 最简配置的效果
  • 硬件上:无需焊接外部晶振、谐振电容,减少硬件接线和故障点;
  • 软件上:CubeMX 自动生成 SystemClock_Config() 函数,无需手动编写时钟初始化代码;
  • 性能上:满足基础外设(GPIO、UART、SPI 等)的低速使用需求(如 UART 波特率 9600/115200)。

1.11.5.6 需要特殊设置时钟树的场景

当最简配置无法满足功能/性能需求时,需针对性调整时钟树,常见场景如下:

1.11.5.7 需求更高的系统主频
  • 原因:HSI 主频上限低(如 F103 最大 8MHz,F407 最大 16MHz),而多数 STM32 支持更高主频(F103 最大 72MHz、F407 最大 168MHz、H7 系列可达 400MHz),需通过 HSE+PLL 倍频实现;
  • 配置要点
  • 启用 HSE(选择「Crystal/Ceramic Resonator」,需硬件焊接 8MHz/12MHz 晶振+2 个 22pF 电容);
  • 在「Clock Configuration」中设置 PLL 倍频系数(如 F103:HSE 8MHz → PLL 倍频 9 → SYSCLK=72MHz);
  • 调整分频系数:确保 AHB/APB 总线时钟不超过芯片手册上限(如 F103 的 PCLK1 最大 36MHz,需分频 2)。
1.11.5.8 外设需要高精度/精准时钟
  • 场景 1:USB 外设 USB 协议要求必须提供 精准 48MHz 时钟,仅靠 HSI 无法满足(精度±1% 以上),需配置:
  • 启用 HSE(8MHz 晶振),通过 PLL 分频/倍频生成 48MHz(如 F103:PLLCLK=72MHz → 分频 1.5 → 48MHz);
  • 场景 2:RTC(实时时钟)

RTC 需低功耗、高精度计时,需启用 LSE(外部低速晶振,32.768kHz)(硬件焊接 32.768kHz 晶振+2 个 12.5pF 电容),而非 LSI(内部低速,精度±5%); - 场景 3:以太网(ETH)/I2S ETH 需 25MHz 外部时钟或 PLL 生成精准时钟,I2S 需音频级精准时钟,均需调整 PLL 分频系数。

1.11.5.9 低功耗应用场景
  • 原因:HSI/HSE 主频高、功耗大,低功耗模式(Stop/Standby)需切换到低速时钟源;

  • 配置要点

  • 启用 LSI(内部低速 128kHz)或 LSE(32.768kHz)作为低功耗时钟源;
  • 关闭 HSE/PLL,降低 SYSCLK 频率(如切换到 32kHz),减少功耗。
1.11.5.10 时钟安全/冗余需求
  • 场景:工业控制、汽车电子等对时钟稳定性要求高的场景,需防止时钟故障导致系统崩溃;
  • 配置要点:启用「Clock Security System (CSS)」(时钟安全系统),当 HSE 故障时,硬件自动切换到 HSI,保证系统持续运行。
1.11.5.11 外部时钟输入(HSE Bypass 模式)
  • 场景:硬件上使用外部设备(如其他 MCU、时钟芯片)提供的时钟信号(而非晶振);
  • 配置要点:RCC 中 HSE 选择「BYPASS Mode」,适配外部时钟输入的硬件接线(仅接时钟信号线,无需晶振)。

1.11.5.12 总结
  1. 最简配置核心:启用 HSI(内部时钟)+ 重置时钟配置,无需外接晶振,CubeMX 自动完成参数计算,满足基础低速需求;
  2. 特殊配置触发条件:需高主频、USB/ETH/RTC 等高精度外设、低功耗、时钟安全或外部时钟输入时,需针对性启用 HSE/LSE + 调整 PLL/分频系数;
  3. 关键注意事项:特殊配置时需确保各总线/外设时钟不超过芯片手册上限(如 APB1 最大 36MHz for F103),外设时钟满足功能刚需(如 USB 必须 48MHz)。

3.7 为什么需要外部时钟源

3.7.1 一、STM32 不同系列 HSI 的最高工作频率

HSI(内部高速时钟)的基础频率最大倍频能力因 STM32 芯片系列不同而差异显著,核心结论先明确:

表 3-9 一、STM32 不同系列 HSI 的最高工作频率

STM32系列 HSI基础频率 理论最大倍频后主频 工程实际可用主频
F1系列(如F103) 8MHz(±1%~±4%温漂) 8MHz × 9(PLL倍频)= 72MHz ≤ 36MHz(稳定性可接受)
F4系列(如F407) 16MHz(±1%温漂) 16MHz × 10.5 = 168MHz ≤ 84MHz(仅低速外设场景)
H7系列(如H743) 64MHz(±0.5%温漂) 64MHz × 6 = 384MHz ≤ 192MHz(极少用)

通过上表的对比可以看出,不同方案在 STM32 系列、HSI 基础频率、理论最大倍频后主频等等方面各有优劣,实际选型时应结合具体应用场景综合权衡。

1.12.1.1 关键补充(以最常用的 F103 为例):

F103 的 HSI 是内部 RC 振荡器,而非精准晶振,存在两个核心问题: 1. 精度差:出厂校准后误差±1%,温漂/电压变化会导致误差扩大到±4%; 2. 稳定性弱:温度、电压波动会让时钟频率漂移,无法保证高频下的时序稳定性。

因此,即便 F103 的 PLL 理论上能将 HSI(8MHz)倍频到 72MHz(8×9),官方手册和工程实践中均不推荐,仅建议 HSI 用于≤36MHz 的低速场景(如仅 GPIO、低波特率 UART)。

3.7.2 二、要工作到 72MHz,是否必须用 HSE?

1.12.2.1 理论层面:可以不用(但完全不推荐)

F103 的时钟树逻辑上支持 HSI(8MHz) → PLL 倍频 9 → SYSCLK=72MHz,CubeMX 中也能手动配置该参数: - 步骤:RCC 中启用 HSI → 时钟配置页面将 PLLMUL 设为 9 → 确认 AHB/APB 分频(HCLK=72MHz,PCLK1=36MHz,PCLK2=72MHz)。

但这种配置仅能让芯片“跑起来”,实际会出现: - 外设不稳定:UART 波特率误差超标、SPI/I2C 通信丢包; - USB 功能失效:USB 要求 48MHz 精准时钟(误差≤0.25%),HSI 的±4% 误差完全不满足; - 长期可靠性差:温度变化会导致主频漂移,甚至程序跑飞。

1.12.2.2 工程实践层面:必须用 HSE

72MHz 是 F103 的额定最高主频,要稳定运行该频率,唯一可靠方案是 HSE(外部高速晶振)+ PLL,原因: - HSE(通常 8MHz 晶振)精度极高:商用晶振误差仅±50ppm(0.005%),温漂可忽略; - 符合官方设计规范:STM32F1 参考手册明确推荐“HSE 作为 PLL 输入源实现最高主频”; - 满足外设时序要求:高频下的 GPIO、定时器、通信外设(UART/SPI/I2C)时序精准,无丢包/错码风险。

3.7.3 HSE 配置 72MHz 的核心步骤(CubeMX):

① 硬件:焊接 8MHz 晶振 + 2 个 22pF 谐振电容到 STM32 的 OSC_IN/OSC_OUT 引脚; ② CubeMX:RCC 中 HSE 选择「Crystal/Ceramic Resonator」(启用外部晶振); ③ 时钟配置:PLLSRC 选择 HSE → PLLMUL 设为 9 → 自动计算得 SYSCLK=8×9=72MHz; ④ 确认分频:PCLK1(APB1)分频 2(36MHz,符合 F103 最大限制),PCLK2(APB2)分频 1(72MHz)。

3.7.4 总结

  1. HSI 最高频率:F1 系列理论 8×9=72MHz(不可用),工程中≤36MHz;F4 系列 HSI 16MHz,理论倍频到 168MHz,实际仅≤84MHz;
  2. 72MHz 的核心结论:理论上可通过 HSI+PLL 实现,但工程中必须用 HSE,否则外设不稳定、可靠性差;
  3. 核心原则:STM32 高主频(如 F1 的 72MHz、F4 的 168MHz)场景,优先选择 HSE 作为时钟源,HSI 仅用于无外部晶振的临时调试/低速场景。
1.12.4.1 方法一:使用 HAL 库(推荐,STM32CubeIDE)

HAL(Hardware Abstraction Layer)是 ST 官方提供的硬件抽象库,屏蔽了底层寄存器细节,代码可读性高、跨型号移植方便,是当前主流开发方式。

/* main.c - Blue Pill LED 闪烁(HAL 库版本)
 * 开发环境: STM32CubeIDE
 * 目标板:   Blue Pill (STM32F103C8T6)
 * LED 引脚: PC13,低电平点亮
 */

#include "stm32f1xx_hal.h"   /* HAL 库头文件 */

/* 函数声明 */
void SystemClock_Config(void);
static void MX_GPIO_Init(void);

int main(void)
{
    /* 1. HAL 库初始化(配置 SysTick 定时器,提供 HAL_Delay 的时间基准) */
    HAL_Init();

    /* 2. 配置系统时钟:外部 8MHz 晶振 → PLL 倍频 → 72MHz */
    SystemClock_Config();

    /* 3. 初始化 GPIO(使能 GPIOC 时钟,配置 PC13 为推挽输出) */
    MX_GPIO_Init();

    /* 4. 主循环:LED 每 500ms 翻转一次状态 */
    while (1)
    {
        /* PC13 输出低电平 → LED 亮 */
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
        HAL_Delay(500);   /* 延时 500 毫秒 */

        /* PC13 输出高电平 → LED 灭 */
        HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
        HAL_Delay(500);   /* 延时 500 毫秒 */

        /* 也可以用一句话实现翻转,效果相同:
         * HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
         * HAL_Delay(500);
         */
    }
}

/* GPIO 初始化函数 */
static void MX_GPIO_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    /* 使能 GPIOC 外设时钟(STM32 外设上电后默认关闭,必须先开启才能使用) */
    __HAL_RCC_GPIOC_CLK_ENABLE();

    /* 配置 PC13 引脚参数 */
    GPIO_InitStruct.Pin   = GPIO_PIN_13;       /* 选择 PC13 引脚 */
    GPIO_InitStruct.Mode  = GPIO_MODE_OUTPUT_PP; /* 推挽输出模式 */
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; /* 低速输出(驱动 LED 足够) */
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

    /* 初始状态:PC13 输出高电平 → LED 默认熄灭 */
    HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);
}

/* 系统时钟配置:HSE 8MHz → PLL × 9 → SYSCLK 72MHz */
void SystemClock_Config(void)
{
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

    /* 启用 HSE 外部高速晶振,并配置 PLL 倍频 */
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    RCC_OscInitStruct.HSEState       = RCC_HSE_ON;
    RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
    RCC_OscInitStruct.PLL.PLLState   = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource  = RCC_PLLSOURCE_HSE;
    RCC_OscInitStruct.PLL.PLLMUL     = RCC_PLL_MUL9;   /* 8MHz × 9 = 72MHz */
    HAL_RCC_OscConfig(&RCC_OscInitStruct);

    /* 配置总线分频:SYSCLK=72MHz, AHB=72MHz, APB1=36MHz, APB2=72MHz */
    RCC_ClkInitStruct.ClockType      = RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK
                                     | RCC_CLOCKTYPE_PCLK1  | RCC_CLOCKTYPE_PCLK2;
    RCC_ClkInitStruct.SYSCLKSource   = RCC_SYSCLKSOURCE_PLLCLK;
    RCC_ClkInitStruct.AHBCLKDivider  = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
    HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);
}
1.12.4.2 方法二:直接操作寄存器(理解底层原理)

直接操作寄存器可以帮助深刻理解 HAL 库背后的硬件机制,也是嵌入式进阶学习的必经之路。

/* main.c - Blue Pill LED 闪烁(寄存器直接操作版本)
 * 不依赖任何库,直接读写硬件寄存器
 * 适合理解 STM32 底层工作原理
 */

#include "stm32f103xb.h"   /* 包含 STM32F103 寄存器地址定义 */

/* 简易软件延时(循环空转,不精确,仅用于教学演示) */
void delay(uint32_t count)
{
    while (count--);
}

int main(void)
{
    /*
     * 步骤 1:使能 GPIOC 时钟
     * RCC_APB2ENR 寄存器第 4 位(IOPCEN)控制 GPIOC 时钟
     * 置 1 → 开启时钟,GPIOC 寄存器才可以正常读写
     */
    RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;

    /*
     * 步骤 2:配置 PC13 为通用推挽输出,最高速率 2MHz
     *
     * GPIOC_CRH 寄存器控制 PC8~PC15 的引脚模式
     * PC13 对应 CRH 中的 CNF13[1:0] 和 MODE13[1:0](第 20~23 位)
     *
     * MODE13 = 10(输出,最大速率 2MHz)
     * CNF13  = 00(通用推挽输出)
     *
     * 先清零再置位,避免影响其他引脚配置
     */
    GPIOC->CRH &= ~(0xF << 20);   /* 清除 PC13 的配置位 */
    GPIOC->CRH |=  (0x2 << 20);   /* MODE13=10, CNF13=00 → 推挽输出 2MHz */

    /* 步骤 3:主循环,交替拉高拉低 PC13 实现 LED 闪烁 */
    while (1)
    {
        /*
         * 使用 BSRR 寄存器原子操作控制引脚电平(推荐方式,无需关中断)
         * BSRR 高 16 位 = BRR(置低),低 16 位 = BSR(置高)
         */

        /* PC13 置低 → LED 亮 */
        GPIOC->BSRR = GPIO_BSRR_BR13;   /* BR13 位置 1 → PC13 输出 0 */
        delay(500000);

        /* PC13 置高 → LED 灭 */
        GPIOC->BSRR = GPIO_BSRR_BS13;   /* BS13 位置 1 → PC13 输出 1 */
        delay(500000);
    }
}
1.12.4.3 关键概念解析

为什么要先开启时钟?

STM32 采用时钟门控技术:每个外设(GPIO、UART、SPI 等)上电后默认关闭时钟,以降低功耗。使用任何外设前,必须先通过 RCC(复位与时钟控制)寄存器将其时钟打开,否则对外设寄存器的读写操作无效甚至死机。

推挽输出 vs 开漏输出

表 3-10 关键概念解析

模式 原理 适用场景
推挽输出(PP) 可主动输出高电平(接 VCC)和低电平(接 GND),驱动能力强 驱动 LED、继电器等负载
开漏输出(OD) 只能主动输出低电平,高电平需外接上拉电阻 I2C 总线、电平转换

LED 闪烁使用推挽输出,因为需要主动驱动 LED 的通断。

HAL 库 vs 寄存器操作对比

表 3-11 关键概念解析

维度 HAL 库 寄存器直接操作
代码量 较多(含初始化结构体) 少而精
可读性 高,接近自然语言 低,需查手册
执行效率 略低(有封装开销) 最高
移植性 好,跨 STM32 型号 差,型号相关
学习曲线 平缓,入门推荐 陡峭,进阶必备

建议:初学阶段使用 HAL 库快速上手,理解程序逻辑后再研读寄存器版本,深入掌握硬件工作机制。


3.8 电路图解读基础

读懂电路图(原理图,Schematic)是嵌入式开发的基本技能。电路图描述的是电气连接关系,而非物理布局,它回答的核心问题是:谁连接了谁,通过什么方式连接

3.8.1 LED 电路局部图

Blue Pill 上的 LED 电路极为简洁,是理解电路图的最佳入口:

Blue Pill STM32F103C8T6 PC13 GPIO Port C Pin 13 R 1kΩ LED D : VCC [P管 OUT [N管 GND ]← 3.3 PC13 ]← VCC PC13 LED LED阳 LED阴 GND LED (PC13) (保护LED, 限制电流) 阳极(+)朝上 (LED阴极接VCC) VCC(3.3V) 限流电阻(1kΩ) PC13(低电平=0V) 载LED

图 3-16 上图以框图形式描绘了 LED 电路局部图的系统架构,清晰呈现了各模块之间的连接关系与信号流向。

读图要点一:追踪网络标号(Net Label)

原理图中,PC13 是一个网络标号(Net Label),凡是标有同一名称的节点,在电气上都是相连的,无论它们在图纸上距离多远。因此: - 芯片引脚旁的 PC13 标号 - LED 电路一端的 PC13 标号

→ 这两个点在 PCB 上通过铜箔走线直接相连。


3.8.2 从芯片引脚到板上针脚的追踪路径

阅读电路图时,需要在三个层次之间建立对应关系:

Blue Pill PC13 Pin 1 Pin 2 Pin 3 Pin 4 Pin 19 Pin 20 GND GND 3.3V NRST PC14 PC15 32kHz 32kHz GPIO) GPIO) STM32F103C8T6 为LQFP 48 2 PC13 GPIO 3 PC14 GPIO 4 PC15 GPIO LED Pill LED PC13 STM32 R3 D2 Pin2 1kΩ VCC Pill PC13 LED, PC13 / / OSC32_IN / OSC32_OUT (LED) (PC13) Datasheet) 共48 Blue Schematic) 2脚 Blue PCB) PC13 载LED, 制LED, 作PC13 ·· ·

图 3-17 该框图展示了从芯片引脚到板上针脚的追踪路径的核心结构,读者可以从中把握各功能单元的层次划分与协作方式。


3.8.3 Blue Pill 完整电路图(简化原理图)

Blue Pill Micr USB STM32F103C8T6 GND AMS1117 3.3V LDO GND LED PC13 32.768kHz 100nF + : 10uF GND PA0 PA15 PB0 PB15 SWDIO SWD NRST BOOT0 BOOT1 LED D2 VCC R3 LED 1kΩ SWCLK SWD PC13 PC13 2脚 8MHz XTAL 20pF 20pF 32.768 kHz 20pF 20pF VCC 10kΩ NRST [按 ] GND 100nF GND GND GND VBUS(5V) VCC_3V3 VCC(×4) VCC_3V3 GND(×4) (见③) OSC_IN OSC_OUT (见④) OSC32_IN OSC32_OUT (见⑤) (见⑥) VCC_3V3 (绿色) (HSE) (LSE) (负载 负载) (负载 负载) 电容) 8MHz 线 线 载LED 8MHz 32.768kHz

图 3-18 上图直观呈现了 Blue Pill 完整电路图(简化原理图)的组成要素与数据通路,有助于理解系统整体的工作机理。


3.8.7 读图基本要点

要点 1:识别符号(元器件)

电路图使用标准符号表示元器件,掌握常见符号是读图的基础:

LED: kHz VCC GND VDD VSS /\/\/ / / / :、 Ω pF、 nF、F μ +竖 线 MHz、 线 线

图 3-19 上图以框图形式描绘了要点 1:识别符号(元器件)的系统架构,清晰呈现了各模块之间的连接关系与信号流向。

要点 2:理解网络标号(Net Label)

U1 PC13 R3 PC13 PC13 U1 PC13 R3 PCB PC13 线

图 3-20 该框图展示了要点 2:理解网络标号(Net Label)的核心结构,读者可以从中把握各功能单元的层次划分与协作方式。

要点 3:区分电源轨与信号线

3.3V GND GND PC13 GPIO SWDIO VCC_3V3 VCC_3V3 USART1_TX Power Rail) 穿 线 Signal Line) 线 线

图 3-21 上图直观呈现了要点 3:区分电源轨与信号线的组成要素与数据通路,有助于理解系统整体的工作机理。

要点 4:读懂芯片引脚图(Pin Diagram)

U1 STM32F103C8T6 VCC PA0 GND NRST PC13 PA1 PA2 PA9/TX OSC_IN PA10/RX I/O、通信接口输出 引脚名称后的斜线(PA9/TX)表示该引脚有复用功能

图 3-22 上图以框图形式描绘了要点 4:读懂芯片引脚图(Pin Diagram)的系统架构,清晰呈现了各模块之间的连接关系与信号流向。

要点 5:电流方向与 LED 极性

LED Blue Pill [LED [LED ] [限 ] LED +) LED LED 10mA 1kΩ VCC(3.3V) PC13(低电平) (VCC - V_LED) / I_LED (3.3V - 2.0V) / 载LED) +] PC13 时LED () R 130Ω

图 3-23 该框图展示了要点 5:电流方向与 LED 极性的核心结构,读者可以从中把握各功能单元的层次划分与协作方式。

要点 6:从原理图到实物排针的对应方法

Datasheet STM32F103C8T6 PC13) LQFP 2 LED, PA0 5针 Blue Pill STM32 GND GND 3.3V 3.3V Pin4 Pin5 Pin6 Pin7 Pin8 Pin9 Pin10 Pin1 NRST PA0 PA1 PA2 PA3 PA4 PA5 PB12 ADC ADC ADC ADC ADC ADC PB13 PB14 PB15 PA8 PA9 PA10 PA11 PA12 PA15 PC13 MCO / TIM2_CH1 / TIM2_CH2 / USART2_TX / USART2_RX / SPI1_NSS / SPI1_SCK SPI2_NSS / I2S_WS SPI2_SCK SPI2_MISO SPI2_MOSI TIM1_CH1 / USART1_TX USART1_RX USB_DM (D-) USB_DP (D+) SPI3_NSS / TIM2_CH1 PC13 48: PC13 与STM32 侧Pin1 侧Pin2 侧Pin3 侧Pin2 侧Pin3 侧Pin4 侧Pin5 侧Pin6 侧Pin7 侧Pin8 侧Pin9 侧Pin10 载LED(

图 3-24 上图直观呈现了要点 6:从原理图到实物排针的对应方法的组成要素与数据通路,有助于理解系统整体的工作机理。

总结:读电路图的核心思路是"追踪信号流"——从信号源(MCU 引脚)出发,沿着同名网络标号,经过元器件符号,找到最终的负载(LED、电机、传感器等)。掌握网络标号、元器件符号和电源轨三个基本规则,就能读懂绝大多数嵌入式开发板的原理图。


在硬件层内部,以 STM32 微控制器(如 STM32F103 系列)的最小系统和一般嵌入式系统为例,其主要组成部分可以详细划分为以下几个核心模块:

  1. 核心微处理器 (CPU/MCU):整个硬件系统的"大脑",负责执行指令和数据处理。例如基于 ARM Cortex-M3 内核的 STM32 单片机,其内部还集成了 DMA 控制器(用于直接内存存取,减轻 CPU 负担)、NVIC(嵌套向量中断控制器)等核心组件。
  2. 基本支撑电路 (最小系统要素):使微处理器能够正常工作的最基本物理条件。
  3. 电源 (Power):为整个系统提供稳定的电压(如 3.3V 稳压电源)。
  4. 时钟电路 (Clock/Oscillator):包括高速外部时钟(HSE,如 8MHz 晶振)和低速外部时钟(LSE),为 CPU 及外设提供工作的心跳节拍。
  5. 复位电路 (Reset):用于在系统上电或出现异常时,将系统状态初始化到默认的安全起点。
  6. 存储器 (Memory):分为非易失性存储器(如用于存放程序代码的 Flash)和易失性存储器(如用于存放运行数据的 SRAM)。
  7. 外部设备与 I/O 接口 (Peripherals & I/O):微处理器与外部世界进行交互的通道。
  8. 通用 I/O 端口 (GPIO):用于最基本的信号输入输出,如驱动 LED 灯、读取按键状态等。
  9. 通信接口:用于数据传输的串口(TTL 电平的 USART/UART)、I2C、SPI、USB、CAN 等通信总线。
  10. 功能外设:包含模拟数字转换器(ADC)、数字模拟转换器(DAC)、各类定时器(用于延时或输出 PWM 信号控制电机等)。

根据上述构成详细分解,硬件层组成图如下:

Flash SRAM (Hardware Layer) (Memory) (VCC/GND) (存储程序代码) (晶体振荡) (存储运行数据) (Reset) (MCU / CPU, 如 STM32) (包含: ARM Cortex 内核、DMA控制器、NVIC中断控制器、总线矩阵) (I/O & Peripherals) 通用输入输出(GPIO):控制LED闪烁、读取按键状态等 串行与总线通信:USART(串口)、I2C、SPI、CAN、USB等 模拟混合信号:ADC(模数转换)、DAC(数模转换) 定时与控制:定时器(Timer)、PWM输出、看门狗(Watchdog)等 线

图 3-25 上图以框图形式描绘了要点 6:从原理图到实物排针的对应方法的系统架构,清晰呈现了各模块之间的连接关系与信号流向。


依据上述层次,嵌入式系统的构成图如下:

(用户应用程序:包含机器学习、PID控制、运动姿态控制等) (操作系统如 uCOS Ⅱ / FreeRTOS、网络系统、文件系统、组件模块) 中间层(BSP/HAL硬件抽象层) (硬件抽象、设备驱动实现) (SOC单片机、微处理器、存储器、外部设备I/O等)

图 3-26 该框图展示了要点 6:从原理图到实物排针的对应方法的核心结构,读者可以从中把握各功能单元的层次划分与协作方式。

3.9 嵌入式电路基础知识

在动手搭建或调试嵌入式系统之前,需要掌握一组核心电气概念。这些知识决定了能不能接、能不能驱动、会不会烧,是实践中避免硬件损坏的安全底线。


3.9.1 芯片工作电压

表 3-12 不同时代的芯片采用不同的工作电压,混接会导致芯片损坏。

芯片类别 典型工作电压 代表芯片
传统 5V 单片机 5V 51 单片机(AT89C51)、ATmega328(Arduino UNO)
现代 3.3V ARM 3.3V STM32 全系列、ESP32、树莓派 GPIO
低功耗 MCU 1.8V ~ 3.3V STM32L 系列、nRF52
高性能 SoC 1.0V ~ 1.8V(内核)+ 3.3V(I/O) 树莓派 CPU 内核、i.MX 系列

STM32 工作电压详情

STM32F103 2.0V 2.4V) VDD( ~3.6V, 值3.3V VDDA( :2.0V ~3.6V( ADC VBAT( :1.8V ~3.6V( 供RTC 的ESD

图 3-27 上图直观呈现了芯片工作电压的组成要素与数据通路,有助于理解系统整体的工作机理。

注意:部分 STM32 引脚标注了 FT(5V Tolerant),表示该引脚可以容忍 5V 输入,但仍需查手册确认,不可一概而论。


3.9.2 电平标准:高电平与低电平

数字电路用电压范围而非精确值来定义"1"和"0",这是因为实际信号总有噪声和波动。

1.15.2.1 TTL 电平(5V 系统,如 Arduino UNO / 51 单片机)

5.0 1 2.4V 2.4 0.8 0 0.8V 0.0 1 2.0V 0 0.8V (V) ░░░░ ░░░░ ░░░░ ▓▓▓▓ ▓▓▓▓ ▓▓▓▓

图 3-28 上图以框图形式描绘了 TTL 电平(5V 系统,如 Arduino UNO / 51 单片机)的系统架构,清晰呈现了各模块之间的连接关系与信号流向。

1.15.2.2 LVTTL / LVCMOS 电平(3.3V 系统,如 STM32)

3.3 1 2.4V 2.4 0.8 0 0.4V 0.0 STM32 GPIO VDD 3.3V( 0 ≥DD 0.4V) (V) ░░░░ ░░░░ ░░░░ ▓▓▓▓ ▓▓▓▓ ▓▓▓▓ 3.3V) 0.4V)

图 3-29 该框图展示了 LVTTL / LVCMOS 电平(3.3V 系统,如 STM32)的核心结构,读者可以从中把握各功能单元的层次划分与协作方式。

1.15.2.3 两种电平直接互连的风险

STM32) TX 5V STM32 RX( 入3.3 3.3V, 5V) Arduino TX STM32 RX GND 5V × 3.3V Arduino TX [TXS0102 74LVC245] STM32 RX Arduino(5V) 2/(1+2) / ✅正 [1kΩ ] [2kΩ ] ✅正

图 3-30 上图直观呈现了两种电平直接互连的风险的组成要素与数据通路,有助于理解系统整体的工作机理。


3.9.3 三、串口电平(UART / RS-232 / RS-485)

串口是嵌入式系统中最常用的通信接口,但"串口"本身只定义了协议,不同场合下使用的电平标准差异极大

TTL UART 3.3V 5V 0V STM32 USART RS 232 3V 15V +3V ~+15V M PLC TTL RS 485 2~6V / (MCU 原生) (注意极性 (逻辑 1 ) (逻辑 0 ) 相反!) (差分信号) (A-B > 0) (A-B < 0) 脑C DB9) 分+2~ +6V 线 远1200m 多32

图 3-31 上图以框图形式描绘了三、串口电平(UART / RS-232 / RS-485)的系统架构,清晰呈现了各模块之间的连接关系与信号流向。

STM32 连接 PC 的正确方式:

STM32 CH340 USB 成TTL CP2102 USB USB PC RS 232 STM32 STM32。 RS 232 COM (3.3V TTL) / TX/RX ± 15V

图 3-32 该框图展示了三、串口电平(UART / RS-232 / RS-485)的核心结构,读者可以从中把握各功能单元的层次划分与协作方式。


3.9.4 四、GPIO 驱动能力与输出功率

STM32 的 GPIO 是弱驱动接口,设计用于数字信号控制,不能直接驱动大功率负载。

1.15.4.1 STM32F103 GPIO 电气参数

STM32F103 GPIO 25 25 150 8 8 mA) mA) VDD 和VSS mA 0.4V PC13 2.9V I/O mA( mA( mA( 150 VOH) VDD VOL) 0.4V 3 mA( 载LED)

图 3-33 上图直观呈现了 STM32F103 GPIO 电气参数的组成要素与数据通路,有助于理解系统整体的工作机理。

1.15.4.2 GPIO 能直接驱动的器件

mA mA mA < 1 mA mA MOSFET 300mA MOSFET + LED( 1W+) ✅可 求≤ 8mA) 光LED( 5~ 10 5~ 20 30 5~ 15 (A μ) ❌不 Servo) 100~ 500mA 用5V 线 50~ 100mA 管+ mA

图 3-34 上图以框图形式描绘了 GPIO 能直接驱动的器件的系统架构,清晰呈现了各模块之间的连接关系与信号流向。

1.15.4.3 三极管驱动电路(GPIO 扩流的基本方法)

GPIO NPN VCC [负 ] GPIO B NPN S8050、 2N2222) E GND GPIO E 2mA) GND( mA) GPIO (5V 或更高电压) 极C [1kΩ ] 3.3V) C 从VCC

图 3-35 该框图展示了三极管驱动电路(GPIO 扩流的基本方法)的核心结构,读者可以从中把握各功能单元的层次划分与协作方式。


3.9.5 五、发光二极管(LED)的电压与电流

LED 是电流驱动器件(不是电压驱动),必须限流使用,否则会因电流过大烧毁。

1.15.5.1 常见 LED 参数

Imax 10 mA 30 mA 10 mA 30 mA 10 mA 30 mA 200 mA 350 mA 1000 mA (导通压降) 见LED Vf 流If 色🔴 1.8~ 2.2V 色🟡 1.8~ 2.2V 绿 色🟢 2.0~ 2.5V 外IR 1.2~ 1.5V 20~ 100mA 3.0~ 3.6V

图 3-36 上图直观呈现了常见 LED 参数的组成要素与数据通路,有助于理解系统整体的工作机理。

1.15.5.2 限流电阻计算公式

R If VCC Vf If LED 5~ 10 LED mA, 寿 10mA R 0.010 130 Ω R 2: 5V 0.010 300 Ω 10mA 330Ω 3: 3.3V LED( Vf 3.2V) If 5mA R 0.005 20 Ω 低If VCC) 线 R] [LED +] [LED ] GND VCC [LED Blue Pill [LED [限 R] (VCC - Vf) / (3.3 - 2.0) / (5.0 - 2.0) / (3.3 - 3.2) / GPIO(高) GPIO(低) 例1: 3.3V 色LED( Vf 2.0V) If 值150Ω 色LED( Vf 2.0V) If 22Ω [限 线 +] 载LED 极]

图 3-37 上图以框图形式描绘了限流电阻计算公式的系统架构,清晰呈现了各模块之间的连接关系与信号流向。


3.9.6 六、上拉电阻与下拉电阻

GPIO 引脚悬空(未连接任何信号)时,电压值不确定,会随机变化,导致程序逻辑错乱。上下拉电阻用于将悬空引脚钳位到确定的电平。

Pull up) VCC VCC [R] 4.7kΩ GND VCC( GND( Pull down) GND VCC [R] GND BOOT0 GND( VCC( Flash [开关/传感器输出] [开关/传感器输出] 100kΩ

图 3-38 该框图展示了六、上拉电阻与下拉电阻的核心结构,读者可以从中把握各功能单元的层次划分与协作方式。

STM32 内部上下拉:STM32 GPIO 在配置为输入模式时,可以通过寄存器开启片内上拉(40kΩ)或下拉(40kΩ),无需外接电阻,简化硬件设计。


3.9.7 七、去耦电容(Bypass Capacitor)

去耦电容是每个数字芯片电源引脚旁必不可少的小电容,功能是滤除电源噪声、稳定瞬态供电

MCU VCC 线 VCC GND VCC VCC [100nF] GND GND VCC 100nF 线 / 低频滤波(电解/钽电容,稍远处) 线 PCB 线 [10μ F] 10μ F

图 3-39 上图直观呈现了七、去耦电容(Bypass Capacitor)的组成要素与数据通路,有助于理解系统整体的工作机理。


3.9.8 八、常用元器件电气参数速查

STM32F103 VDD: SWD, GPIO, USART, I2C, SPI Vf: 3.3V SG90 50Hz, HC SR04 5 5V PWM 30mA TTL MPU 6050 OLED SSD1306) 3~ 5V( LDO) I2C : I2C 400kHz SPI I2C I2C SPI 1A 1.1 : 1A CAN NPN GPS S8050 1N4007 NE 6M) HC 05) GPIO( 25mA/脚,150mA / GPIO(Trig/Echo) 3.3V/5V,接口: Ic_max: 500mA,Vce_sat: 电源/驱动保护 电源/继电器续流 9600/115200bps USB转串口(CH340/CP2102) 2.0~ 3.6V, GPIO : 光LED 1.8~ 3.5 If: 5~ 20mA GPIO( 3~ 12V, : 2~ 4kHz, GPIO( PWM输 5V, GPIO( 3~ 12V, : 200mA~ 2A GPIO( PWM, 4.8~ 6V, PWM 0.5~ 2.5ms GPIO( PWM输 15mA, : : : : 0.3V, Hfe: 100~ 300 : 0.3V, : 40V, Io: : : 1000V, I 3.3V~ 5V, 9600bps, NMEA协 UART( 3.3V~ 6V, UART( : 5V, TTL电 : 2Mbps UART( MCU通 CAN收 TJA1050) 4.5~ 5.5V, CAN速 : 1Mbps, CAN( MCU CAN控 CAN总 线 MCP25625) : 2.7~ 5.5V, CAN控 : 40~ 125℃ 1N5819

图 3-40 上图以框图形式描绘了八、常用元器件电气参数速查的系统架构,清晰呈现了各模块之间的连接关系与信号流向。

安全原则:在嵌入式硬件调试中,遵循"先算后接"的原则——连接任何器件前,先估算工作电流是否在驱动芯片的安全范围内。若电流超出,必须加驱动芯片或三极管,而不是"试试看"。


3.10 STM32 核心接口与外设

STM32 芯片集成了丰富的片上外设,每个引脚除了可作为通用 GPIO 外,大多还具备一种或多种复用功能(Alternate Function)。掌握这些接口的特性与应用场景,是读懂数据手册和芯片引脚图的关键。

3.10.1 一、主要接口总览表

表 3-13 一、主要接口总览表

接口缩写 英文全称 中文名称 主要作用 典型应用
GPIO General Purpose Input/Output 通用输入输出 可软件配置为输入或输出,读取/控制数字信号 LED 控制、按键检测、继电器驱动
USART Universal Synchronous/Asynchronous Receiver/Transmitter 通用同步/异步收发器 串行数据收发,全双工,最常用通信接口 调试输出、GPS 模块、蓝牙模块、串口屏
SPI Serial Peripheral Interface 串行外设接口 高速同步全双工串行总线,主从结构 SD 卡、Flash 存储、LCD 屏幕、ADC/DAC 芯片
I2C Inter-Integrated Circuit 集成电路间总线 两线制同步串行总线,支持多设备挂载 OLED 屏、MPU-6050 陀螺仪、温湿度传感器、EEPROM
CAN Controller Area Network 控制器局域网 差分总线,高抗干扰,多节点广播 汽车电子、工业机器人关节控制、电机驱动器
USB Universal Serial Bus 通用串行总线 高速设备与主机通信 模拟虚拟串口(CDC)、HID 设备、大容量存储
ADC Analog-to-Digital Converter 模数转换器 将模拟电压值转为数字量 电位器读值、电池电压监测、传感器模拟量采集
DAC Digital-to-Analog Converter 数模转换器 将数字量输出为模拟电压 音频输出、模拟信号发生、电机平滑控制
TIM Timer 定时器 精确计时、产生 PWM、捕获输入信号 电机 PWM 调速、舵机控制、超声波测距、编码器读值
IWDG Independent Watchdog 独立看门狗 防止程序跑飞,自动复位系统 所有需要高可靠性的嵌入式产品
WWDG Window Watchdog 窗口看门狗 比 IWDG 更严格,必须在时间窗口内喂狗 实时性要求严格的控制系统
RTC Real-Time Clock 实时时钟 低功耗运行,维持日期和时间 数据记录时间戳、定时唤醒、闹钟功能
DMA Direct Memory Access 直接内存访问 不经过 CPU,外设与内存直接传输数据 ADC 连续采样、串口大数据收发、SPI 批量传输
NVIC Nested Vectored Interrupt Controller 嵌套向量中断控制器 管理所有中断的优先级与响应 所有需要实时响应的事件处理
EXTI External Interrupt/Event Controller 外部中断/事件控制器 检测 GPIO 引脚电平变化并触发中断 按键中断、编码器计数、传感器触发信号
SWD Serial Wire Debug 串行线调试接口 程序烧录与在线调试 Keil/CubeIDE 连接 ST-Link 调试器

3.10.2 二、重点接口详解

1.16.2.1 GPIO — 通用输入输出

GPIO 是最基础也是使用最频繁的外设,每个引脚均可独立配置工作模式:

STM32 GPIO LED、 DAC (PP) 可主动输出高/低,驱动能力强(驱动 (OD) 由片上外设(UART/SPI (40kΩ (40kΩ 接ADC I2C 线 I2C 使

图 3-41 该框图展示了 GPIO — 通用输入输出的核心结构,读者可以从中把握各功能单元的层次划分与协作方式。

1.16.2.2 TIM — 定时器

定时器是 STM32 中功能最强大、使用最复杂的外设之一,远不止"计时"那么简单:

STM32F103 TIM1 16 TIM2 16 路PWM + TIM3 16 路PWM TIM4 TIM5 7 16 16 4 PWM PWM LED PID PWM 75%: LED × 100% 基本/通用定时器 / / / / / 补PWM( 动H 4 4 每1ms 比25%:

图 3-42 上图直观呈现了 TIM — 定时器的组成要素与数据通路,有助于理解系统整体的工作机理。

1.16.2.3 ADC — 模数转换器

STM32F103 ADC 12 PA7 8 PB1 2 ADC读 × ADC读 × ADC 2048 2048 × 3.3 4096 1.65V (VREF / 4096) (3.3 / 4096) / 围0~ 4095, 多18 PA0 高1 MHz( VDDA( 3.3V) 0~ 3.3V) PB0

图 3-43 上图以框图形式描绘了 ADC — 模数转换器的系统架构,清晰呈现了各模块之间的连接关系与信号流向。


3.10.3 三、串行通信接口横向对比

STM32 支持多种串行通信协议,选择哪种取决于距离、速率、节点数和抗干扰要求:

1.16.3.1 接口特性速查

表 3-14 接口特性速查

特性 UART/USART SPI I2C CAN
信号线数 2(TX/RX) 4(MOSI/MISO/SCK/CS) 2(SDA/SCL) 2(CAN_H/CAN_L)
通信方式 全双工异步 全双工同步 半双工同步 半双工差分
最高速率 ~5 Mbps ~45 Mbps 100k/400k/3.4M bps 1 Mbps
最大距离 数十米(TTL) <1 m(板级) <1 m(板级) 40m@1Mbps / 1km@50kbps
节点数 点对点 1主多从(CS片选) 1主多从(地址寻址,最多127) 多主多从(最多110节点)
典型用途 调试、模块通信 高速板级外设 短距离低速传感器 汽车/工业总线

上表对接口特性速查中各方案的特性进行了横向对比,便于读者根据实际需求选择最合适的技术路线。

1.16.3.2 UART / RS-232 / RS-485 / CAN 深度对比

这四种协议在日常工作中最容易混淆,需从物理层协议层两个维度理解它们的本质区别:

UART RS 232 RS 485 CAN UART RS 232 RS 485 CAN 232 0V 高1, 低0 负1, 正0 32 110 5 Mbps 115.2 kbps 10 Mbps 1 Mbps 15m 1200m 1000m STM32 需MAX485 需TJA1050 / / / (TTL) 数据帧格式) / 3.3V(5V) 显性/隐性 (@9600bps) (@50kbps) TX/RX UART 线 端TTL 端RS ± 3V ~± 15V ± 1.5V~6 ±V ± 1V~3 ±V 正1 负0 1:1) 1:1) 线 1:N) 线 N:N) 2( 2( <15m( (1 ±5V压 +错 )性 )奇 )奇 CRC+位 +帧 件USART 块需MAX232 件bxCAN PLC、 器机

图 3-44 该框图展示了 UART / RS-232 / RS-485 / CAN 深度对比的核心结构,读者可以从中把握各功能单元的层次划分与协作方式。

1.16.3.3 关键区别可视化

STM32 [TX] [RX] [RX] [TX] 0V 3.3V [GND] [GND] 232 STM32 MAX232 PC COM [TX] [T1IN T1OUT] [RX [RX] [R1OUT R1IN ] [TX [GND] [GND ] [GND Pin5] Pin3] Pin2] :15V 15V 1 3~15V 0 485 STM32 MA 485 A A MAX485 [TX] [DI DE] [RO DE] [R ] [RX] [RO RE] [DI RE] [T ] B B 2 线 120Ω STM32 TJA1050 [CAN TX] [TXD CANH] [CAN RX] [RXD CANL] CAN H CAN L CAN CAN 0V( 线 (TTL) / (DB9) 发送/接收方向切换(半双工) 显性位(逻辑0):CAN_H CAN_L +2V(CAN_H高,CAN_L低) 隐性位(逻辑1):CAN_H CAN_L UART RS 平± 15V +3~ +15V RS 线 线 A、 B) CAN 线 2 3 ... [120Ω ] 线

图 3-45 上图直观呈现了关键区别可视化的组成要素与数据通路,有助于理解系统整体的工作机理。

1.16.3.4 协议选型指南

MCU STM32 与PC USB <1m) UART UART CH340 USB RS 232 ECU UART + RS 485 CAN SPI I2C MAX232 (TTL) / Flash、 ADC) IMU、 湿

图 3-46 上图以框图形式描绘了协议选型指南的系统架构,清晰呈现了各模块之间的连接关系与信号流向。

3.10.4 CAN 总线编程实战

CAN(Controller Area Network)是机器人系统中最重要的工业通信总线——从汽车 ECU 到工业机器人多关节控制,几乎所有需要可靠多节点通信的场景都使用 CAN。STM32 片上集成了 bxCAN 控制器,原生支持 CAN 2.0A/B 协议。

CAN 2.0 协议基础

CAN 帧结构(标准帧 CAN 2.0A):

表 3-15 CAN 2.0 协议基础

字段 位数 说明
SOF 1 帧起始(显性位)
仲裁段 12 11 位 ID + 1 位 RTR(远程帧标志)
控制段 6 IDE + r0 + 4 位 DLC(数据长度 0-8)
数据段 0-64 0~8 字节有效数据
CRC 段 16 15 位 CRC + 1 位定界符
ACK 段 2 接收方应答
EOF 7 帧结束

通过上表的对比可以看出,不同方案在字段、位数、说明等方面各有优劣,实际选型时应结合具体应用场景综合权衡。

CAN 0x7FF) SOF CRC ACK EOF 1 11+1 bit 6bit 0 8 byte 15+1 2 7 11位 ID( 0x000 ID越

图 3-47 该框图展示了 CAN 2.0 协议基础的核心结构,读者可以从中把握各功能单元的层次划分与协作方式。

CAN 总线仲裁机制

CAN 采用非破坏性位仲裁——多个节点同时发送时,ID 最小(最高优先级)的报文获得总线控制权,其余节点自动退让并在稍后重试:

  • 显性位(Dominant, 逻辑 0):CAN_H - CAN_L ≈ +2V
  • 隐性位(Recessive, 逻辑 1):CAN_H - CAN_L ≈ 0V

  • 当显性位与隐性位同时出现时,总线呈现显性("线与"逻辑)

CAN 0 0 0 线 A B ID 0x100 ID 0x120 0 0 0 0 0 0 1 1 1 0 0 0 0 1 0 0 0 0 0 0 0 B发 性1 线 →测 B停 : 退 ! A获

图 3-48 上图直观呈现了 CAN 2.0 协议基础的组成要素与数据通路,有助于理解系统整体的工作机理。

CAN 错误检测机制(五重保护):

表 3-16

检测方式 原理
CRC 校验 15 位 CRC,检测数据传输错误
位填充 连续 5 个相同位后插入反转位,防止时钟偏移
帧格式检查 检查固定格式字段(SOF/EOF/ACK)是否正确
ACK 检查 发送方检查是否收到接收方的应答
位监控 发送方监控总线状态是否与发送值一致

上表对 CAN 2.0 协议基础的核心信息进行了结构化整理,读者可根据需要快速查阅相关内容。

STM32 bxCAN 外设架构

STM32F103 集成 bxCAN 控制器,支持 CAN 2.0A/B,具有 3 个发送邮箱和 2 个接收 FIFO(各含 3 级深度缓冲):

STM32 bxCAN APB1 bxCAN Mailbox0 Mailbox1 Mailbox2 28 FIFO 0 FIFO 1 TJA1050 MCP2551 CAN CAN H CAN L CAN TX CAN RX 线 : : ID过 3级 3级

图 3-49 上图以框图形式描绘了 STM32 bxCAN 外设架构的系统架构,清晰呈现了各模块之间的连接关系与信号流向。

CubeMX 配置 CAN

在 STM32CubeMX 中配置 CAN 外设的关键步骤:

  1. 启用 CAN:Connectivity → CAN → Mode: Master Mode
  2. 波特率配置

CAN 波特率 = APB1 时钟 / (Prescaler × (BS1 + BS2 + 1))

对于 36MHz APB1 时钟,常用配置:

目标波特率 Prescaler BS1 BS2 采样点位置
1 Mbps 4 6 TQ 2 TQ 77.8%
500 kbps 8 6 TQ 2 TQ 77.8%
250 kbps 16 6 TQ 2 TQ 77.8%
125 kbps 32 6 TQ 2 TQ 77.8%
  1. 引脚分配:CAN_TX → PA12,CAN_RX → PA11(或重映射至 PB9/PB8)
  2. 中断配置:启用 CAN RX0 中断(接收 FIFO 0)
HAL 库 CAN 编程

初始化与滤波器配置

// can_init.c
#include "main.h"

CAN_HandleTypeDef hcan;

void CAN_Init(void) {
    hcan.Instance = CAN1;
    hcan.Init.Prescaler = 4;           // 36MHz / 4 = 9MHz
    hcan.Init.Mode = CAN_MODE_NORMAL;
    hcan.Init.SyncJumpWidth = CAN_SJW_1TQ;
    hcan.Init.TimeSeg1 = CAN_BS1_6TQ;  // BS1 = 6
    hcan.Init.TimeSeg2 = CAN_BS2_2TQ;  // BS2 = 2
    // 波特率 = 9MHz / (1+6+2) = 1 Mbps
    hcan.Init.TimeTriggeredMode = DISABLE;
    hcan.Init.AutoBusOff = ENABLE;    // 自动恢复
    hcan.Init.AutoWakeUp = DISABLE;
    hcan.Init.AutoRetransmission = ENABLE; // 自动重传
    hcan.Init.ReceiveFifoLocked = DISABLE;
    hcan.Init.TransmitFifoPriority = DISABLE;

    if (HAL_CAN_Init(&hcan) != HAL_OK) {
        Error_Handler();
    }

    // 配置接收滤波器(接收所有报文)
    CAN_FilterTypeDef filter;
    filter.FilterBank = 0;
    filter.FilterMode = CAN_FILTERMODE_IDMASK;
    filter.FilterScale = CAN_FILTERSCALE_32BIT;
    filter.FilterIdHigh = 0x0000;      // 不过滤
    filter.FilterIdLow = 0x0000;
    filter.FilterMaskIdHigh = 0x0000;  // 掩码全0=接收所有
    filter.FilterMaskIdLow = 0x0000;
    filter.FilterFIFOAssignment = CAN_RX_FIFO0;
    filter.FilterActivation = ENABLE;

    if (HAL_CAN_ConfigFilter(&hcan, &filter) != HAL_OK) {
        Error_Handler();
    }

    // 启动 CAN 和接收中断
    HAL_CAN_Start(&hcan);
    HAL_CAN_ActivateNotification(&hcan,
        CAN_IT_RX_FIFO0_MSG_PENDING);
}

发送 CAN 报文

// can_tx.c — 发送电机速度指令
void CAN_SendMotorCommand(uint16_t motor_id,
                          int16_t target_rpm) {
    CAN_TxHeaderTypeDef tx_header;
    uint8_t tx_data[4];
    uint32_t tx_mailbox;

    // 设置报文头
    tx_header.StdId = 0x200 + motor_id; // 标准 ID
    tx_header.ExtId = 0;
    tx_header.RTR = CAN_RTR_DATA;       // 数据帧
    tx_header.IDE = CAN_ID_STD;         // 标准帧
    tx_header.DLC = 4;                  // 4 字节数据
    tx_header.TransmitGlobalTime = DISABLE;

    // 数据段:目标转速(大端序)
    tx_data[0] = (target_rpm >> 8) & 0xFF;  // RPM 高字节
    tx_data[1] = target_rpm & 0xFF;         // RPM 低字节
    tx_data[2] = 0x01;                      // 使能标志
    tx_data[3] = 0x00;                      // 保留

    // 发送到空闲邮箱
    if (HAL_CAN_AddTxMessage(&hcan, &tx_header,
                              tx_data, &tx_mailbox) != HAL_OK) {
        // 所有邮箱满,处理拥塞
        Error_Handler();
    }
}

接收 CAN 报文(中断模式)

// can_rx.c — 中断回调接收电机反馈
typedef struct {
    int16_t  rpm;        // 实际转速
    int16_t  current;    // 实际电流 (mA)
    uint16_t position;   // 编码器位置
} MotorFeedback_t;

volatile MotorFeedback_t motor_fb[4];  // 4 个电机的反馈

void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) {
    CAN_RxHeaderTypeDef rx_header;
    uint8_t rx_data[8];

    HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0,
                          &rx_header, rx_data);

    // 根据 ID 解析不同电机的反馈
    if (rx_header.StdId >= 0x201 && rx_header.StdId <= 0x204) {
        uint8_t idx = rx_header.StdId - 0x201;
        motor_fb[idx].rpm =
            (int16_t)((rx_data[0] << 8) | rx_data[1]);
        motor_fb[idx].current =
            (int16_t)((rx_data[2] << 8) | rx_data[3]);
        motor_fb[idx].position =
            (uint16_t)((rx_data[4] << 8) | rx_data[5]);
    }
}
接收滤波器高级配置

在多节点 CAN 网络中,通过滤波器只接收关心的报文,减轻 CPU 负担:

// 滤波器示例:只接收 ID 范围 0x201~0x204 的报文
void CAN_ConfigMotorFilter(void) {
    CAN_FilterTypeDef filter;
    filter.FilterBank = 1;
    filter.FilterMode = CAN_FILTERMODE_IDLIST;  // ID 列表模式
    filter.FilterScale = CAN_FILTERSCALE_16BIT; // 16 位

    // 两个 16 位 ID 列表(每个 Bank 可放 4 个 16 位 ID)
    filter.FilterIdHigh = 0x201 << 5;      // ID 0x201
    filter.FilterIdLow = 0x202 << 5;       // ID 0x202
    filter.FilterMaskIdHigh = 0x203 << 5;  // ID 0x203
    filter.FilterMaskIdLow = 0x204 << 5;   // ID 0x204

    filter.FilterFIFOAssignment = CAN_RX_FIFO0;
    filter.FilterActivation = ENABLE;

    HAL_CAN_ConfigFilter(&hcan, &filter);
}

滤波器模式对比

表 3-17 接收滤波器高级配置

模式 原理 适用场景
掩码模式(IDMASK) ID & Mask == FilterID & Mask 接收一个 ID 范围(如 0x200~0x20F)
列表模式(IDLIST) ID == FilterID1 或 ID == FilterID2 只接收指定的几个 ID

通过上表的对比可以看出,不同方案在模式、原理、适用场景等方面各有优劣,实际选型时应结合具体应用场景综合权衡。

CAN 总线在机器人系统中的应用

ID 0x200 n, DLC 4, [RPM H,RPM L,EN,0] ID 0x200 n, DLC 6, [RPM,Current,Pos] IMU ID 0x300, DLC 8, [GyroXYZ, AccelXYZ] CAN H CAN L 120Ω IMU ID:0x203 ID:0x300 120Ω STM32 ID:0x100 ID:0x201 ID:0x202 节CAN 线 : 控→ 机→ : 3 1 2

图 3-50 该框图展示了 CAN 总线在机器人系统中的应用的核心结构,读者可以从中把握各功能单元的层次划分与协作方式。

CAN 在机器人领域的典型应用

表 3-18

应用场景 CAN ID 分配策略 波特率 说明
多关节机械臂 每关节一个 ID 段 1 Mbps 各关节电机驱动器通过 CAN 接收位置/速度指令
AGV 底盘 功能分组 500 kbps 驱动电机、转向、传感器分组通信
RoboMaster 机器人 C620 电调协议 1 Mbps 大疆 C620 电调使用 CAN 接收电流指令和反馈转速
汽车电子 SAE J1939 250 kbps 发动机、变速箱、仪表盘通过 CAN 互联

3.11 本章小结

表 3-19 本章系统介绍了 STM32 微控制器开发的核心基础:

主题 关键内容
芯片与开发板 STM32F103C8T6 架构、Blue Pill 硬件资源
开发工具 CubeMX 图形化配置、HAL 库编程模型
GPIO 推挽/开漏/浮空/上拉等 8 种工作模式
时钟系统 HSE/HSI/PLL 时钟树、72MHz 主频配置
电路读图 原理图符号、网络标号、去耦电容、上拉电阻
电气基础 工作电压、电平兼容、限流电阻、分压电路
通信接口 UART/SPI/I2C/CAN 特性对比与选型

📌 实践项目请参见 附录 C:STM32 实践项目集,包含 5 个由浅入深的实战项目(流水灯、呼吸灯、电压表、串口控制台、智能温控风扇)。


3.12 本章测验

Quiz results are saved to your browser's local storage and will persist between sessions.

#

1) STM32F103C8T6 的 "C8" 表示什么?

#

2) 在 STM32CubeMX 中配置 GPIO 为推挽输出模式时,以下哪项描述是正确的?

#

3) 以下哪些是 SPI 通信相比 I2C 的优势?

#

4) 当 STM32 的 GPIO 需要连接 5 V 器件时,以下哪项措施是正确的?

Quiz Progress

0 / 0 questions answered (0%)

0 correct