🎓 总站 🏠 本课目录 01 概述 02 ARM与STM32 03 开发环境 04 最小系统与C 05 GPIO 06 中断 07 串口通信 08 DMA 09 定时器 10 ADC 11 从模块到项目 12 嵌入式OS
智能嵌入式系统设计 · 第4章

STM32 最小系统与嵌入式 C 语言

最小系统四要素(电源、时钟、复位、调试下载);AMS1117 降压、HSE/LSE 晶振、上电/手动复位、SWD/JTAG、BOOT 启动模式;嵌入式 C 语言程序结构、STM32 数据类型(uint8_t 等)、IO 限定词(__I/__O/__IO)、const/static/volatile/extern 关键字、指针与回调函数。

📚 学习进度
0%

🎯学习目标

  • 掌握 STM32 最小系统设计(电源/时钟/复位/调试下载);
  • 理解和掌握嵌入式 C 语言的程序结构;
  • 掌握数据类型、const、volatile、extern、static 等关键字;
  • 掌握条件编译、指针的应用;
  • 学会分析 HAL 库源码。

最小系统概述 ⭐

最小系统:用最少的元件组成单片机可以工作的系统。STM32 最小系统包含四部分
① 电源电路:提供稳定可靠的供电
② 时钟电路(晶振):产生稳定脉冲信号
③ 复位电路:将系统复位
④ 调试和下载电路:下载程序、在线调试

电源 / 时钟 / 复位 ⭐

电源电路

  • 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-DPSW-DP引脚号
JTMS/SWDIO模式选择串行数据输入输出PA13
JTCK/SWCLK时钟串行时钟PA14
JTDI数据输入PA15
JTDO/TRACESWO数据输出异步跟踪PB3
JNTRST模块复位PB4
SWD只需 2 根线(SWDIO+SWCLK),比 JTAG(5 根)更省引脚,是最常用的调试方式。

启动模式(BOOT0/BOOT1)

Cortex-M3 有三种启动方式,通过 BOOT0/BOOT1 电平组合选择,把 0x00000000 映射到不同存储区域:

BOOT0BOOT1启动介质起始地址说明
0x用户 Flash0x08000000最常用,正常运行用户程序
10系统存储器0x1FFFF000ISP 串口下载(出厂 Bootloader)
11内置 SRAM0x20000000从 SRAM 启动(调试用)
最常用:BOOT0=0 从用户 Flash 启动运行代码。ISP 下载时设 BOOT0=1、BOOT1=0 进入系统存储器 Bootloader。

嵌入式 C 程序结构

嵌入式 C 兼具汇编操作底层的优势高级语言功能性强的特点。程序特点:

  • 程序总是从 main 函数开始执行;
  • 函数是 C 语言的基本结构,由函数说明部分和函数体组成;
  • 一个程序包含若干源文件(.c)和头文件(.h)
  • 采用模块化、层次化设计:一个外设功能模块 = 一个 .c + 一个 .h。

数据类型与 IO 限定词 ⭐

STM32 数据类型(C99 标准,v3.5.0)

C 类型STM32 类型说明
unsigned charuint8_t / u88 位无符号(0~255)
unsigned short intuint16_t / u1616 位无符号(0~65535)
unsigned intuint32_t / u3232 位无符号
unsigned long longuint64_t64 位无符号
signed charint8_t8 位有符号(−128~127)
signed short intint16_t16 位有符号
signed intint32_t32 位有符号
注意:v3.5.0 采用 C99 标准(uint8_t);旧版用 u8/u16/u32,stm32f10x.h 做了兼容说明。同一类型有多种表示(如 uint8_t = u8 = unsigned char)。

IO 类型限定词(core_cm3.h 定义)

限定词类型说明
__Ivolatile const只读
__Ovolatile只写
__IOvolatile读和写

与数据类型结合,用于定义寄存器和结构体变量。

布尔类型枚举(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

声明变量/函数在其他文件中定义,用于跨文件引用全局变量或函数,扩展作用域。

volatile 核心场景:① 硬件寄存器(值可能被硬件改变)② 中断服务程序修改的变量 ③ 多线程共享变量。STM32 中寄存器都用 volatile 修饰(__IO),防止编译器优化掉读写操作。

指针与回调

指针

指针是嵌入式 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 枚举:定义命名常量集合。

重点例题

例1 STM32 最小系统包含哪四部分?
答案
① 电源电路 ② 时钟电路(晶振)③ 复位电路 ④ 调试和下载电路。用最少的元件让单片机能工作。
例2 BOOT0=0、BOOT1=x 时从哪里启动?
答案
从用户 Flash(起始地址 0x08000000)启动,这是 STM32 最常用的正常运行模式。
例3 __IO 限定词等价于什么?为什么寄存器要用它?
答案
__IO = volatile(读和写)。寄存器值可能被硬件改变,volatile 告诉编译器不要优化访问,每次都从内存重新读写,保证操作真实生效。
例4 uint16_t 对应什么 C 类型?范围?
答案
uint16_t = unsigned short int,16 位无符号,范围 0~65535。
例5 volatile 的三个典型应用场景?
答案
① 硬件寄存器(被硬件改变)② 中断服务程序修改的变量 ③ 多线程共享变量。防止编译器优化掉必要的访问。

🎯自测(点击展开)

STM32F103 的工作电压范围?
2.0V~3.6V,常用 3.3V(由 AMS1117-3.3 从 5V 降压)。
HSE 和 LSE 的频率和用途?
HSE=8MHz 高速外部时钟,作系统时钟源;LSE=32.768kHz 低速外部时钟,作 RTC 时钟源。
上电复位的原理?
上电瞬间电容两端电压不能突变,RESET 出现短暂低电平触发复位;电容充电后(约1ms)正常工作。
SWD 调试需要几根线?
2 根:SWDIO(PA13)+ SWCLK(PA14),比 JTAG 的 5 根更省引脚。
ISP 下载时 BOOT0/BOOT1 怎么设?
BOOT0=1、BOOT1=0,从系统存储器(0x1FFFF000)启动出厂 Bootloader 进行串口下载。
static 修饰全局变量的作用?
限制作用域在定义它的源文件内,其他源文件不能引用,避免同名冲突,利于模块化。
const 和 volatile 能否同时用?
可以,__I 就是 volatile const(只读但每次都重新读)。适用于只读状态寄存器(硬件可能改变但程序不能写)。

📝强化题库

选择题点选即时判分;填空题输入后"检查"或"显示答案"。

已答 0/0答对 0正确率
已答 0/0答对 0