STM32 最小系统与嵌入式 C 语言
最小系统四要素(电源、时钟、复位、调试下载);AMS1117 降压、HSE/LSE 晶振、上电/手动复位、SWD/JTAG、BOOT 启动模式;嵌入式 C 语言程序结构、STM32 数据类型(uint8_t 等)、IO 限定词(__I/__O/__IO)、const/static/volatile/extern 关键字、指针与回调函数。
🎯学习目标
- 掌握 STM32 最小系统设计(电源/时钟/复位/调试下载);
- 理解和掌握嵌入式 C 语言的程序结构;
- 掌握数据类型、const、volatile、extern、static 等关键字;
- 掌握条件编译、指针的应用;
- 学会分析 HAL 库源码。
①最小系统概述 ⭐
②电源 / 时钟 / 复位 ⭐
电源电路
- STM32F103 工作电压 2.0V~3.6V(常用 3.3V);
- 用 AMS1117-3.3 把 5V 降到 3.3V 供 VDD;
- 输入电容防断电电压倒置,输出滤波电容抑制自激振荡、稳定输出;
- STM32 内部电压调节器把 3.3V 转 1.8V 供 Cortex-M3 内核/内存/外设;
- VBAT:主电源掉电后给 RTC 和备份寄存器供电(1.8~3.6V),不用时接 VDD;
- VREF+/VREF−:ADC 参考电压,常用独立电源。
时钟电路(晶振)
无源晶振(晶体)
2 引脚无极性,需借助外部电路起振,精度差些,价格便宜。
有源晶振(振荡器)
内含晶体管和阻容,是完整谐振振荡器,只需供电,4 引脚(VCC/GND/输出/悬空)。
- HSE 高速外部时钟:一般 8MHz,方便倍频,作系统时钟源;
- LSE 低速外部时钟:32.768kHz,主要作 RTC 时钟源(精准计时/万年历)。
复位电路
STM32 的 NRST 引脚内部已接上拉电阻,持续低电平时复位。建议外接对地电容(必要时再加上拉电阻)。
- 上电复位:上电瞬间电容两端电压不能突变,RESET 出现短暂低电平,自动复位;电容充电后(约 1ms)开始正常工作;
- 手动复位:按键按下 RESET 接地,产生低电平实现复位。
③调试下载与启动模式 ⭐
调试端口 SWJ-DP
STM32F103 内核集成了 SWD/JTAG 调试端口(SWJ-DP),复用引脚:
| 引脚名 | JTAG-DP | SW-DP | 引脚号 |
|---|---|---|---|
| JTMS/SWDIO | 模式选择 | 串行数据输入输出 | PA13 |
| JTCK/SWCLK | 时钟 | 串行时钟 | PA14 |
| JTDI | 数据输入 | — | PA15 |
| JTDO/TRACESWO | 数据输出 | 异步跟踪 | PB3 |
| JNTRST | 模块复位 | — | PB4 |
启动模式(BOOT0/BOOT1)
Cortex-M3 有三种启动方式,通过 BOOT0/BOOT1 电平组合选择,把 0x00000000 映射到不同存储区域:
| BOOT0 | BOOT1 | 启动介质 | 起始地址 | 说明 |
|---|---|---|---|---|
| 0 | x | 用户 Flash | 0x08000000 | 最常用,正常运行用户程序 |
| 1 | 0 | 系统存储器 | 0x1FFFF000 | ISP 串口下载(出厂 Bootloader) |
| 1 | 1 | 内置 SRAM | 0x20000000 | 从 SRAM 启动(调试用) |
④嵌入式 C 程序结构
嵌入式 C 兼具汇编操作底层的优势和高级语言功能性强的特点。程序特点:
- 程序总是从 main 函数开始执行;
- 函数是 C 语言的基本结构,由函数说明部分和函数体组成;
- 一个程序包含若干源文件(.c)和头文件(.h);
- 采用模块化、层次化设计:一个外设功能模块 = 一个 .c + 一个 .h。
⑤数据类型与 IO 限定词 ⭐
STM32 数据类型(C99 标准,v3.5.0)
| C 类型 | STM32 类型 | 说明 |
|---|---|---|
| unsigned char | uint8_t / u8 | 8 位无符号(0~255) |
| unsigned short int | uint16_t / u16 | 16 位无符号(0~65535) |
| unsigned int | uint32_t / u32 | 32 位无符号 |
| unsigned long long | uint64_t | 64 位无符号 |
| signed char | int8_t | 8 位有符号(−128~127) |
| signed short int | int16_t | 16 位有符号 |
| signed int | int32_t | 32 位有符号 |
IO 类型限定词(core_cm3.h 定义)
| 限定词 | 类型 | 说明 |
|---|---|---|
| __I | volatile const | 只读 |
| __O | volatile | 只写 |
| __IO | volatile | 读和写 |
与数据类型结合,用于定义寄存器和结构体变量。
布尔类型枚举(stm32f10x.h)
- FunctionalState:DISABLE=0 / ENABLE=!0
- FlagStatus / ITStatus:RESET=0 / SET=!0
- ErrorStatus:ERROR=0 / SUCCESS=!0
⑥const/static/volatile/extern ⭐
const
定义只读变量,编译时不能被改变。声明时必须初始化。目的:防止误修改,提高安全可靠性。C 标准中 const 变量是全局的。
static
修饰变量或函数为静态。静态全局变量只在定义它的源文件内有效,其他源文件不能引用,避免同名冲突,利于模块化。
volatile
告诉编译器不要优化对该变量的访问,每次都从内存重新读取。用于寄存器、中断、多线程共享变量等可能被外部改变的场景。
extern
声明变量/函数在其他文件中定义,用于跨文件引用全局变量或函数,扩展作用域。
⑦指针与回调
指针
指针是嵌入式 C 的核心。STM32 中通过指针访问寄存器地址来操作外设:
#define GPIOB_BASE (uint32_t)(0x40010C00) // 通过指针读写寄存器 *(volatile uint32_t *)GPIOB_BASE = 0x0001;
外设操作本质 = 对特定内存地址的读写(存储器映射)。
回调函数
把函数指针作为参数传给另一个函数,由后者在合适时机"回调"。HAL 库大量使用回调(如 HAL_GPIO_EXTI_Callback),用户重写回调即可自定义中断处理逻辑。
其他
- typedef:给类型起别名(如 typedef uint32_t uint32_t);
- #define:宏定义(如 #define GPIOB_BASE ...);
- #ifdef / #if 条件编译:按条件选择编译内容,用于跨平台/配置;
- struct 结构体:把寄存器组封装成结构体,方便整体访问;
- enum 枚举:定义命名常量集合。
⭐重点例题
答案
答案
答案
答案
答案
🎯自测(点击展开)
STM32F103 的工作电压范围?
HSE 和 LSE 的频率和用途?
上电复位的原理?
SWD 调试需要几根线?
ISP 下载时 BOOT0/BOOT1 怎么设?
static 修饰全局变量的作用?
const 和 volatile 能否同时用?
📝强化题库
选择题点选即时判分;填空题输入后"检查"或"显示答案"。