嵌入式系统原理及应用(STM32版)
研究生复试复习资料(最终版)
说明:本资料严格依据大学课程教材和ST官方参考手册编写,所有技术细节均经过验证,确保准确无误。
考试大纲概览
本课程涵盖8个核心章节,重点考察STM32嵌入式系统的理论基础和实际应用能力。
| 章节 | 主要内容 | 重点程度 |
|---|---|---|
| 第一章 | 计算机中数的表示 | ★★★ |
| 第二章 | 嵌入式系统概念与ARM Cortex-M3特性 | ★★★ |
| 第三章 | STM32开发环境搭建与使用 | ★★★ |
| 第四章 | STM32中断系统原理及应用 | ★★★ |
| 第五章 | STM32定时器原理与应用 | ★★★ |
| 第六章 | STM32串口通信 | ★★★ |
| 第七章 | DMA与模数转换器(ADC) | ★★☆ |
| 第八章 | CAN总线设计 | ★★☆ |
第一章 计算机中数的表示
1.1 进位计数制
基本概念
进位计数制的三要素:
- 基数(Radix):每个数位所能使用的数码个数
- 位权(Weight):每位数值乘以对应的权值(基数的幂次)
- 进位规则:逢基数进位
常用进制对照表
| 进制 | 基数 | 数码 | 位权 | 标识符 | 示例 | C语言表示 |
|---|---|---|---|---|---|---|
| 二进制 | 2 | 0,1 | 2ⁿ | B或下标2 | 1011₂ | 0b1011(部分编译器) |
| 八进制 | 8 | 0-7 | 8ⁿ | O或下标8 | 17O | 017(以0开头) |
| 十进制 | 10 | 0-9 | 10ⁿ | D或下标10 | 15₁₀ | 15 |
| 十六进制 | 16 | 0-9,A-F | 16ⁿ | H或下标16 | 0FH | 0x0F(以0x开头) |
注意:在C语言中,十六进制数以0x开头,如0xFF表示255;八进制数以0开头,如017表示15。
1.2 进制转换(必须掌握)
二进制 ↔ 十进制转换
二进制转十进制:按权展开求和
N10=∑i=−mn−1ai×2iN_{10} = \sum_{i=-m}^{n-1} a_i \times 2^iN10=i=−m∑n−1ai×2i
例题1:将二进制数 1011.101₂ 转换为十进制
1011.1012=1×23+0×22+1×21+1×20+1×2−1+0×2−2+1×2−3=8+0+2+1+0.5+0+0.125=11.62510 \begin{align} 1011.101_2 &= 1\times2^3 + 0\times2^2 + 1\times2^1 + 1\times2^0 + 1\times2^{-1} + 0\times2^{-2} + 1\times2^{-3} \\ &= 8 + 0 + 2 + 1 + 0.5 + 0 + 0.125 \\ &= 11.625_{10} \end{align} 1011.1012=1×23+0×22+1×21+1×20+1×2−1+0×2−2+1×2−3=8+0+2+1+0.5+0+0.125=11.62510
十进制整数转二进制:除2取余,逆序排列
例题2:将十进制数 237 转换为二进制
237 ÷ 2 = 118 ... 1 ← 最低位 (LSB)
118 ÷ 2 = 59 ... 0
59 ÷ 2 = 29 ... 1
29 ÷ 2 = 14 ... 1
14 ÷ 2 = 7 ... 0
7 ÷ 2 = 3 ... 1
3 ÷ 2 = 1 ... 1
1 ÷ 2 = 0 ... 1 ← 最高位 (MSB)
结果:11101101₂
十进制小数转二进制:乘2取整,顺序排列
例题3:将十进制小数 0.625 转换为二进制
0.625 × 2 = 1.25 → 取整数部分 1
0.25 × 2 = 0.5 → 取整数部分 0
0.5 × 2 = 1.0 → 取整数部分 1
结果:0.101₂
注意:不是所有十进制小数都能精确转换为二进制,如0.1₁₀ ≈ 0.0001100110011…₂
二进制 ↔ 十六进制转换
转换规则:每4位二进制对应1位十六进制
例题4:
二进制 → 十六进制:
1010 1100.1111 0001₂ = A C . F 1₁₆
十六进制 → 二进制:
7 E 5 A . 3 B₁₆ = 0111 1110 0101 1010.0011 1011₂
二进制 ↔ 八进制转换
转换规则:每3位二进制对应1位八进制
例题5:
二进制 → 八进制:
10 110 011.101 100₂ = 2 6 3 . 5 4₈
八进制 → 二进制:
7 5 2 . 6₁₆ = 111 101 010 . 110₂
十进制 ↔ 八进制/十六进制(通过二进制中转)
例题6:将十进制 237 转换为八进制和十六进制
先转二进制:237 = 11101101₂
转八进制(3位一组):
011 101 101 = 3 5 5₈
转十六进制(4位一组):
1110 1101 = E D₁₆
1.3 有符号数的表示
原码、反码、补码详解
定义(以8位为例):
| 编码方式 | 正数 | 负数 | 0的表示 | 特点 |
|---|---|---|---|---|
| 原码 | 符号位0,数值位不变 | 符号位1,数值位不变 | +0=00000000 -0=10000000 | 表示直观,但0有两种表示 |
| 反码 | 与原码相同 | 符号位不变,数值位按位取反 | +0=00000000 -0=11111111 | 运算复杂,0有两种表示 |
| 补码 | 与原码相同 | 反码+1(符号位参与运算) | 0=00000000(唯一) | 运算统一,0表示唯一 |
表示范围对比
| 编码方式 | 8位范围 | 16位范围 | 说明 |
|---|---|---|---|
| 无符号数 | 0 ~ 255 | 0 ~ 65535 | |
| 原码 | -127 ~ +127 | -32767 ~ +32767 | 有+0和-0 |
| 反码 | -127 ~ +127 | -32767 ~ +32767 | 有+0和-0 |
| 补码 | -128 ~ +127 | -32768 ~ +32767 | 0唯一,多表示一个数 |
补码求法(必须掌握)
方法一:定义法
正数:直接转为二进制,最高位为0
+5 → 00000101
负数:
① 求绝对值的二进制
② 按位取反(包括符号位)
③ 末位加1
例:-5
① +5 = 00000101
② 取反 = 11111010
③ 加1 = 11111011
结果:11111011(补码)
方法二:快捷法(适用于负数)
从右向左,第一个1及其右侧不变,左侧全部取反
例:-5
+5 = 00000101
↑
第一个1
右侧(包含该1):101 不变
左侧:00000 取反 → 11111
结果:11111011 ✓
例题5:求-128的8位补码
方法一:
+128的二进制需要9位:1 00000000
在8位补码中,-128的表示是 10000000(特殊规定)
方法二:
根据补码范围公式:n位补码范围 = -2^(n-1) ~ +2^(n-1)-1
8位补码最小值 = -128,对应编码 10000000
验证:
10000000取反 = 01111111
01111111 + 1 = 10000000
所以 -128 的补码确实是 10000000
补码还原(补码→十进制)
方法一:逆运算
若最高位为0:直接转为十进制
00000101 → 5
若最高位为1:
① 减1
② 取反
③ 转为十进制(加负号)
例:11111011
① 减1:11111010
② 取反:00000101
③ 转十进制:5
结果:-5
方法二:按权展开(适用于负数)
11111011 = -128 + 64 + 32 + 16 + 8 + 0 + 2 + 1
= -128 + 123
= -5
为什么使用补码?
优点分析:
-
零的唯一表示
- 原码:+0=00000000,-0=10000000
- 补码:0=00000000(唯一)
-
减法转换为加法
例:5 - 3 = 5 + (-3) 5 = 00000101 -3 = 11111101 00000101
- 11111101
1 00000010 ← 最高位溢出丢弃
结果:00000010 = 2 ✓
3. **多表示一个数**
- 原码/反码:-127~+127(256个数)
- 补码:-128~+127(256个数)
4. **符号位参与运算**
- 运算时无需考虑符号,结果自动正确
---
## 1.4 大端模式与小端模式
### 定义与区别
**大端模式(Big-Endian)**:高字节存放在低地址,低字节存放在高地址
**小端模式(Little-Endian)**:低字节存放在低地址,高字节存放在高地址
### 存储示例
对于32位数据 0x12345678(假设存储起始地址为0x20000000):
| 内存地址 | 大端模式存储 | 小端模式存储 |
|---------|-------------|-------------|
| 0x20000000 | **12**(最高字节) | **78**(最低字节) |
| 0x20000001 | 34 | 56 |
| 0x20000002 | 56 | 34 |
| 0x20000003 | 78(最低字节) | 12(最高字节) |
**记忆口诀**:
- 大端:高位在低地址(符合人类阅读从左到右的习惯)
- 小端:低位在低地址(符合计算机从低地址开始处理的习惯)
### 典型应用
| 模式 | 典型处理器/系统 | 特点 |
|-----|----------------|-----|
| **小端模式** | x86、ARM Cortex-M系列、STM32 | 便于数据类型转换,CPU处理方便 |
| **大端模式** | PowerPC、Motorola 68000、网络协议(TCP/IP) | 符合人类阅读习惯 |
**STM32采用小端模式**
### 大小端判断代码
```c
// 方法1:使用联合体
union EndianTest {
int a;
char b;
};
union EndianTest test;
test.a = 1;
if (test.b == 1) {
// 小端模式(低字节在低地址)
} else {
// 大端模式
}
// 方法2:使用指针
int a = 0x12345678;
char *p = (char *)&a;
if (*p == 0x78) {
// 小端模式
}
1.5 常用编码
BCD码(Binary-Coded Decimal)
定义:用4位二进制数表示1位十进制数字(0~9)
8421码对照表:
| 十进制 | BCD码 | 十进制 | BCD码 |
|---|---|---|---|
| 0 | 0000 | 5 | 0101 |
| 1 | 0001 | 6 | 0110 |
| 2 | 0010 | 7 | 0111 |
| 3 | 0011 | 8 | 1000 |
| 4 | 0100 | 9 | 1001 |
注意:10101111(1015)在BCD码中是非法码
例题6:
十进制数 59 的BCD码:0101 1001
十进制数 123 的BCD码:0001 0010 0011
BCD码 0111 0100 0010 表示的十进制数:7 4 2 → 742
应用场景:
- 数字钟、电子表显示
- 金融系统(精确表示金额)
- 仪器仪表显示
ASCII码(American Standard Code for Information Interchange)
定义:美国标准信息交换代码,用7位二进制表示128个字符
标准ASCII表(0-127):
| 范围 | 内容 | 说明 |
|---|---|---|
| 0x00~0x1F (0-31) | 控制字符 | 不可打印 |
| 0x20~0x7E (32-126) | 可显示字符 | 字母、数字、符号 |
| 0x7F (127) | DEL | 删除字符 |
常用ASCII值(必须记忆):
| 字符 | ASCII值(十进制) | ASCII值(十六进制) |
|---|---|---|
| ‘0’ ~ ‘9’ | 48 ~ 57 | 0x30 ~ 0x39 |
| ‘A’ ~ ‘Z’ | 65 ~ 90 | 0x41 ~ 0x5A |
| ‘a’ ~ ‘z’ | 97 ~ 122 | 0x61 ~ 0x7A |
| 空格 ’ ’ | 32 | 0x20 |
| 换行 ‘\n’ (LF) | 10 | 0x0A |
| 回车 ‘\r’ (CR) | 13 | 0x0D |
| 退格 ‘\b’ | 8 | 0x08 |
| Tab ‘\t’ | 9 | 0x09 |
大小写转换:大写字母 + 32 = 小写字母(‘A’ + 32 = ‘a’)
Unicode与UTF-8
| 编码 | 位数 | 说明 |
|---|---|---|
| Unicode | 16位 | 统一字符编码,65536个字符 |
| UTF-8 | 1-4字节 | 变长编码,与ASCII兼容,Web标准 |
UTF-8编码规则:
- 0xxxxxxx:单字节(ASCII)
- 110xxxxx 10xxxxxx:双字节
- 1110xxxx 10xxxxxx 10xxxxxx:三字节
- 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx:四字节
1.6 定点数与浮点数基础
定点数(Fixed-Point)
定义:小数点位置固定的数表示方法
Q格式:Q15表示有符号16位数,1位符号,15位小数
应用:DSP处理器、低成本嵌入式系统
IEEE 754单精度浮点数(32位)
格式:
┌──────┬──────────┬───────────────────────────┐
│ 1位 │ 8位 │ 23位 │
│ 符号 │ 指数 │ 尾数 │
│ S │ E │ M │
└──────┴──────────┴───────────────────────────┘
计算公式:V=(−1)S×1.M×2E−127V = (-1)^S \times 1.M \times 2^{E-127}V=(−1)S×1.M×2E−127
特殊值:
- E=0, M=0:±0
- E=255, M=0:±∞
- E=255, M≠0:NaN(非数)
第一章 典型例题详解
例题7:将十进制数 -43 表示为8位补码
解:
① 求+43的二进制:
43 ÷ 2 = 21 ... 1
21 ÷ 2 = 10 ... 1
10 ÷ 2 = 5 ... 0
5 ÷ 2 = 2 ... 1
2 ÷ 2 = 1 ... 0
1 ÷ 2 = 0 ... 1
+43 = 00101011
② 取反:11010100
③ 加1:11010101
结果:-43的8位补码是 11010101
例题8:补码 10010010 对应的十进制数是多少?
解:
方法一:
① 最高位为1,是负数
② 减1:10010001
③ 取反:01101110
④ 转十进制:64+32+8+4+2 = 110
⑤ 结果:-110
方法二(按权展开):
10010010 = -128 + 0 + 0 + 16 + 0 + 0 + 2 + 0
= -128 + 18
= -110
第一章 复试问答
-
计算机中为什么采用补码来表示有符号整数?补码相比原码和反码有哪些优点?
-
大端模式和小端模式的区别是什么?STM32采用哪种模式?如何用C语言代码判断当前系统的大小端?
-
8位无符号数和8位有符号数(补码)的表示范围分别是多少?n位补码的表示范围公式是什么?
-
什么是BCD码?它与普通二进制表示有什么区别?举例说明十进制数59的BCD码表示。
-
写出字符’0’、‘A’、‘a’、空格、换行的ASCII码值(用十六进制表示)。
-
求-128的8位补码表示,并验证。
第二章 嵌入式系统概念与ARM Cortex-M3特性
2.1 嵌入式系统基础
嵌入式系统的定义
IEEE定义:嵌入式系统是"用于控制、监视或者辅助操作机器和设备的装置"。
国内通用定义:以应用为中心、以计算机技术为基础、软硬件可裁剪,适应应用系统对功能、可靠性、成本、体积、功耗等严格要求的专用计算机系统。
嵌入式系统的组成
┌─────────────────────────────────────────────────────┐
│ 嵌入式应用系统 │
├─────────────────────────────────────────────────────┤
│ 应用层 │ 应用程序、GUI │
├─────────────────────────────────────────────────────┤
│ 中间件 │ 文件系统、网络协议栈、数据库 │
├─────────────────────────────────────────────────────┤
│ 操作系统 │ RTOS / Linux / 裸机程序 │
├─────────────────────────────────────────────────────┤
│ BSP层 │ 板级支持包(Bootloader、驱动程序) │
├─────────────────────────────────────────────────────┤
│ 硬件层 │ MCU/MPU + 存储器 + 外设 + 电源 │
└─────────────────────────────────────────────────────┘
嵌入式系统的核心特点
| 特点 | 说明 | 示例 |
|---|---|---|
| 专用性强 | 针对特定应用定制 | 手机基带处理器、汽车ECU |
| 实时性要求高 | 必须及时响应外部事件 | 工业控制、航空航天 |
| 低功耗 | 常使用电池供电 | 可穿戴设备、传感器节点 |
| 高可靠性 | 长时间稳定运行 | 医疗设备、核电站控制系统 |
| 资源受限 | 存储空间、处理能力有限 | 8位MCU只有几KB RAM |
| 软硬件协同设计 | 需综合考虑软硬件 | |
| 生命周期长 | 一旦定型不易更改 | 汽车电子通常10年以上 |
嵌入式系统的分类
按实时性分类:
- 硬实时系统:超时造成灾难性后果(如汽车安全气囊)
- 软实时系统:超时仅造成功能下降(如视频播放器)
按复杂度分类:
- 小型系统:单芯片MCU,无操作系统
- 中型系统:RTOS,如FreeRTOS
- 复杂系统:嵌入式Linux/Android,如智能手机
2.2 ARM处理器概述
ARM公司商业模式
ARM (Advanced RISC Machines):
- 成立于1990年(前身为Acorn公司)
- 不生产芯片,只设计处理器IP核
- 授权IP核给芯片厂商(ST、TI、NXP、Qualcomm等)
- 全球超过95%的智能手机使用ARM处理器
ARM架构演进
| 架构版本 | 年份 | 处理器系列 | 主要特性 |
|---|---|---|---|
| ARMv4T | 1994 | ARM7TDMI | 首次引入Thumb指令集 |
| ARMv5TE | 1999 | ARM9 | 增加DSP指令 |
| ARMv6 | 2001 | ARM11 | 引入Thumb-2、SIMD |
| ARMv7-M | 2006 | Cortex-M3/M4 | 微控制器专用 |
| ARMv7-A | 2005 | Cortex-A8/A9/A15 | 应用处理器 |
| ARMv8-A | 2011 | Cortex-A53/A72/A76 | 64位处理器 |
| ARMv8-M | 2016 | Cortex-M33/M55 | 安全扩展 |
RISC vs CISC
| 特性 | RISC(精简指令集) | CISC(复杂指令集) |
|---|---|---|
| 指令长度 | 固定(通常32位) | 变长(1-15字节) |
| 指令数量 | 少(几十到上百条) | 多(几百到上千条) |
| 寄存器 | 多(16-32个) | 少(8-16个) |
| 寻址方式 | 简单(3-5种) | 复杂(10-20种) |
| 执行周期 | 单周期或固定周期 | 多数为多周期 |
| 代码密度 | 较低 | 较高 |
| 功耗 | 低 | 较高 |
| 典型代表 | ARM、MIPS、RISC-V | x86(Intel/AMD) |
ARM采用RISC架构,但通过Thumb-2技术(16/32位混合指令)兼顾了代码密度和性能。
2.3 Cortex-M3处理器详解
基本特性
| 特性 | 说明 |
|---|---|
| 架构 | ARMv7-M |
| 指令集 | Thumb-2(16/32位混合指令) |
| 流水线 | 3级流水线(取指→译码→执行) |
| 总线架构 | 哈佛架构(指令和数据分离) |
| 工作模式 | Thread模式和Handler模式 |
| 寄存器 | 16个32位寄存器(R0-R15) |
| 中断 | NVIC,最多240个外部中断 |
| 低功耗 | 内置睡眠模式(Sleep/Deep Sleep) |
| 调试 | JTAG(5线)和SWD(2线) |
3级流水线详解
| 阶段 | 英文 | 功能 | 说明 |
|---|---|---|---|
| 取指 | Fetch | 从存储器读取指令 | PC指向当前取指地址 |
| 译码 | Decode | 解析指令、读取寄存器 | 确定操作类型和操作数 |
| 执行 | Execute | ALU运算、访存、写回 | 完成指令操作 |
流水线时序示例:
周期 1 2 3 4 5 6
指令1 取指 译码 执行
指令2 取指 译码 执行
指令3 取指 译码 执行
指令4 取指 译码 执行
流水线带来的问题:
- 分支延迟:分支指令需要清空流水线
- 数据相关:后续指令依赖前一条结果(可通过转发解决)
Cortex-M3寄存器组
通用寄存器(R0-R12):
- R0-R7:低寄存器,所有Thumb指令可访问
- R8-R12:高寄存器,部分Thumb指令可访问
特殊寄存器:
| 寄存器 | 别名 | 功能 | 说明 |
|---|---|---|---|
| R13 | SP(Stack Pointer) | 堆栈指针 | PSP(进程堆栈)或MSP(主堆栈) |
| R14 | LR(Link Register) | 链接寄存器 | 保存子程序返回地址 |
| R15 | PC(Program Counter) | 程序计数器 | 当前指令地址+4(Thumb状态) |
xPSR(Program Status Register):
┌─────────────────────────────────────────────────────┐
│ 31 │ 30 │ 29 │ 28 │ 27-25 │ 24 │ 23-16 │ 15-10 │ 9 │ 8-0 │
│ N │ Z │ C │ V │ Q │ │ │ GE │ E │ ISR│
└─────────────────────────────────────────────────────┘
- N:负数标志(Negative)
- Z:零标志(Zero)
- C:进位标志(Carry)
- V:溢出标志(oVerflow)
- Q:饱和标志(用于DSP指令)
操作模式与特权级
| 操作模式 | 特权级 | 说明 | 进入方式 |
|---|---|---|---|
| Thread模式 | 非特权或特权 | 执行应用程序 | 复位后进入 |
| Handler模式 | 特权 | 执行异常处理 | 异常发生时进入 |
特权级:
- 特权级:可访问所有资源
- 非特权级:受限访问(用于隔离用户程序)
异常与中断
Cortex-M3异常类型:
| 异常编号 | 类型 | 优先级 | 说明 |
|---|---|---|---|
| 1 | Reset | -3(最高) | 复位 |
| 2 | NMI | -2 | 不可屏蔽中断 |
| 3 | Hard Fault | -1 | 硬件故障 |
| 4 | MemManage | 可编程 | 存储器管理fault |
| 5 | Bus Fault | 可编程 | 总线fault |
| 6 | Usage Fault | 可编程 | 用法fault |
| 11 | SVCall | 可编程 | 系统服务调用 |
| 14 | PendSV | 可编程 | 可挂起的系统服务 |
| 15 | SysTick | 可编程 | 系统节拍定时器 |
| 16-255 | IRQ | 可编程 | 外部中断请求 |
异常向量表:
- 存储在Flash起始地址(0x00000000或0x08000000)
- 每个异常占4字节(32位地址)
- 复位向量位于偏移0x04
位带操作(Bit-Banding)
原理:将位带区的每一位映射到位带别名区的一个32位字
地址映射公式:
别名区地址 = BitBandBase + (ByteOffset × 32) + (BitNumber × 4)
地址范围:
- SRAM位带区:0x20000000 ~ 0x200FFFFF(1MB)
- SRAM位带别名区:0x22000000 ~ 0x23FFFFFF(32MB)
- 外设位带区:0x40000000 ~ 0x400FFFFF(1MB)
- 外设位带别名区:0x42000000 ~ 0x43FFFFFF(32MB)
优点:
- 原子操作,无需关闭中断
- 简化位操作代码
示例:设置GPIOA->ODR的第5位
// 传统方法
GPIOA->ODR |= (1 << 5); // 读-改-写,非原子
// 位带操作
#define BITBAND_ADDR(addr, bitnum) (((addr) & 0xF0000000) + 0x2000000 + (((addr) & 0xFFFFF) << 5) + ((bitnum) << 2))
volatile uint32_t *pa5_bit = (volatile uint32_t *)BITBAND_ADDR((uint32_t)&GPIOA->ODR, 5);
*pa5_bit = 1; // 原子操作
Cortex-M3存储器映射
4GB地址空间分配:
| 区域 | 地址范围 | 大小 | 用途 |
|---|---|---|---|
| Code | 0x00000000 ~ 0x1FFFFFFF | 512MB | Flash、ROM、启动代码 |
| SRAM | 0x20000000 ~ 0x3FFFFFFF | 512MB | SRAM、位带别名区 |
| Peripheral | 0x40000000 ~ 0x5FFFFFFF | 512MB | 外设寄存器、位带别名区 |
| External RAM | 0x60000000 ~ 0x9FFFFFFF | 1GB | 外部存储器(FSMC) |
| External Device | 0xA0000000 ~ 0xDFFFFFFF | 1GB | 外部设备 |
| System | 0xE0000000 ~ 0xFFFFFFFF | 512MB | 内部外设(NVIC、调试等) |
STM32F103具体映射:
0x00000000 - 0x07FFFFFF:启动区域(根据BOOT配置映射)
0x08000000 - 0x0801FFFF:Flash(128KB)
0x20000000 - 0x20004FFF:SRAM(20KB)
0x40000000 - 0x40023400:外设寄存器
0xE0000000 - 0xE00FFFFF:Cortex-M3内部外设
MPU(内存保护单元)
作用:可选的内存保护单元,用于:
- 防止用户程序访问特权资源
- 定义内存访问权限(读/写/执行)
- 检测非法内存访问
STM32F103:大部分型号没有MPU,F2/F4系列才有
2.4 STM32微控制器
STM32产品线
| 系列 | 内核 | 主频 | Flash | 特点 | 典型应用 |
|---|---|---|---|---|---|
| STM32F0 | Cortex-M0 | 48MHz | 16-64KB | 入门级,低成本 | 简单控制 |
| STM32F1 | Cortex-M3 | 72MHz | 16-1MB | 主流型,性价比高 | 通用应用 |
| STM32F2 | Cortex-M3 | 120MHz | 128-1MB | 高性能,以太网 | 网络应用 |
| STM32F4 | Cortex-M4 | 168MHz | 1-2MB | 高性能,DSP+FPU | 数字信号处理 |
| STM32F7 | Cortex-M7 | 216MHz | 512KB-2MB | 超高性能 | 图形界面 |
| STM32H7 | Cortex-M7 | 480MHz | 1-2MB | 旗舰级 | AI/ML |
| STM32L0/1/4 | Cortex-M0+/M3/M4 | 32-80MHz | 64KB-1MB | 超低功耗 | 电池供电 |
| STM32WB | Cortex-M4+M0+ | 64MHz | 1MB | 无线(BLE) | IoT |
STM32F103命名规则详解
完整型号:STM32 F 103 R B T 6
| 字段 | 含义 | 可选值 |
|---|---|---|
| STM32 | 32位ARM微控制器 | - |
| F | 产品类型 | F=通用型,L=低功耗,W=无线,H=高性能 |
| 103 | 具体系列 | 101=基础型,103=USB+CAN,105=USB+以太网 |
| R | 引脚数 | T=36脚,C=48脚,R=64脚,V=100脚,Z=144脚 |
| B | Flash容量 | 6=32KB,8=64KB,B=128KB,C=256KB,D=384KB,E=512KB |
| T | 封装类型 | T=LQFP,H=BGA,U=VFQFPN |
| 6 | 温度范围 | 6=-4085°C,7=-40105°C |
示例:STM32F103C8T6
- 通用型、103系列、48脚、64KB Flash、LQFP封装、-40~85°C
STM32F103资源概览
| 资源 | STM32F103C8 | STM32F103VET6 | 说明 |
|---|---|---|---|
| 内核 | Cortex-M3 | Cortex-M3 | |
| 主频 | 72MHz | 72MHz | |
| Flash | 64KB | 512KB | |
| SRAM | 20KB | 64KB | |
| GPIO | 37 | 80 | |
| 定时器 | |||
| 高级 | TIM1, TIM8 | TIM1, TIM8 | 带死区、刹车 |
| 通用 | TIM2-4 | TIM2-5 | 4通道 |
| 基本 | TIM6, TIM7 | TIM6, TIM7 | 无输出 |
| 通信接口 | |||
| USART | 3 | 5 | |
| SPI | 2 | 3 | |
| I2C | 2 | 2 | |
| CAN | 1 | 2 | |
| USB | 1 | 1 | 全速 |
| ADC/DAC | |||
| ADC | 2×12位 | 3×12位 | 16通道 |
| DAC | 2×12位 | 2×12位 |
2.5 典型嵌入式操作系统
实时操作系统(RTOS)
实时性定义:任务必须在确定的时间内完成响应
| RTOS | 开源 | 代码大小 | 特点 | 许可证 |
|---|---|---|---|---|
| FreeRTOS | 是 | ~5KB | 轻量级、可裁剪、文档丰富 | MIT |
| μC/OS-II | 是(部分) | ~6KB | 可剥夺、可认证、经典教材 | 商业 |
| μC/OS-III | 商业 | ~10KB | 功能更强、多核支持 | 商业 |
| RT-Thread | 是 | ~3KB | 国产、组件丰富、社区活跃 | Apache 2.0 |
| RTX | 是 | ~5KB | Keil官方、CMSIS兼容 | BSD |
| Zephyr | 是 | ~10KB | Linux基金会、功能丰富 | Apache 2.0 |
RTOS核心概念
| 概念 | 说明 | 作用 |
|---|---|---|
| 任务(Task) | 可独立执行的程序单元 | 实现并发 |
| 调度器(Scheduler) | 决定哪个任务运行 | 实现任务切换 |
| 信号量(Semaphore) | 资源计数器 | 任务同步 |
| 互斥量(Mutex) | 二值信号量 | 资源保护 |
| 消息队列(Queue) | 数据缓冲区 | 任务通信 |
| 邮箱(Mailbox) | 固定大小消息 | 快速通信 |
FreeRTOS特点
- 轻量级:最小仅需4-9KB ROM
- 可裁剪:通过配置文件选择功能
- 可移植:支持35+种处理器架构
- 调度算法:抢占式、协作式、时间片轮转
- 内核服务:任务、队列、信号量、软件定时器
第二章 复试问答
-
嵌入式系统与通用计算机系统(PC)有什么区别?嵌入式系统有哪些核心特点?
-
ARM公司采用的是什么商业模式?ARM处理器采用RISC还是CISC架构?RISC相比CISC有哪些优缺点?
-
Cortex-M3处理器有哪些主要特性?其流水线结构是怎样的?每级流水线完成什么功能?
-
解释STM32F103C8T6型号中每个字段的含义。如果要选择一款有CAN接口、64脚、128KB Flash的芯片,型号应该是什么?
-
什么是位带操作?它有什么优点?写出SRAM位带区与位带别名区的地址映射公式,并举例说明如何设置GPIOA的第5位。
-
Cortex-M3的寄存器组中有哪些特殊功能寄存器?SP、LR、PC各有什么作用?
-
Cortex-M3支持哪些操作模式?Thread模式和Handler模式有什么区别?
-
μC/OS-II和FreeRTOS各有什么特点?各适合什么应用场景?RTOS中的任务、信号量、互斥量分别有什么作用?
-
什么是嵌入式系统的硬实时和软实时?各举一个例子。
第三章 STM32开发环境搭建与使用
3.1 嵌入式系统开发流程
典型开发流程
需求分析 → 系统设计 → 硬件设计 → 软件设计 → 编码实现 →
调试测试 → 系统集成 → 维护升级
交叉开发模式
特点:在PC(宿主机)上开发,在目标板上运行
┌───────────────────────────────────────────────────────────┐
│ 宿主机(PC) │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
│ │ Keil │ │ 编译器 │ │ 调试器 │ │
│ │ IDE │───►│ (ARMCC) │───►│ (J-Link) │ │
│ └────────────┘ └────────────┘ └─────┬──────┘ │
│ │ │
└─────────────────────────────────────────────┼─────────────┘
│ JTAG/SWD
▼
┌───────────────────────────────────────────────────────────┐
│ 目标机(开发板) │
│ ┌──────────────┐ │
│ │ STM32 │ │
│ │ 目标程序 │ │
│ └──────────────┘ │
└───────────────────────────────────────────────────────────┘
3.2 开发环境
MDK-ARM (Keil)
组件:
- μVision IDE:编辑器、项目管理
- ARM Compiler:编译器(ARMCC 5/6)
- Debugger:调试器(Simulator/硬件调试)
- Pack Installer:芯片支持包管理
版本:
- MDK-Lite:免费,32KB代码限制
- MDK-Essential/Professional:付费,无限制
STM32固件库
标准外设库(StdPeriph_Lib)结构:
STM32F10x_StdPeriph_Lib/
├── Libraries/
│ ├── CMSIS/ # Cortex微控制器软件接口标准
│ │ ├── Core/ # 内核定义、系统配置
│ │ └── STM32F10x/ # 启动文件、系统初始化
│ └── STM32F10x_StdPeriph_Driver/
│ ├── inc/ # 头文件(stm32f10x_gpio.h等)
│ └── src/ # 源文件(stm32f10x_gpio.c等)
├── Project/
│ └── STM32F10x_StdPeriph_Examples/ # 示例代码
└── Utilities/
HAL库(新推荐):
- Hardware Abstraction Layer
- 抽象层次更高,便于移植
- STM32CubeMX代码生成工具
调试器对比
| 调试器 | 接口 | 最大速度 | 特点 |
|---|---|---|---|
| ST-Link/V2 | SWD/JTAG | 4MHz | ST官方,性价比高,免费 |
| ST-Link/V3 | SWD/JTAG | 8MHz | 速度更快,支持虚拟串口 |
| J-Link | SWD/JTAG | 15MHz | SEGGER,功能最强 |
| CMSIS-DAP | SWD | 4MHz | ARM官方,开源 |
SWD vs JTAG:
| 特性 | SWD | JTAG |
|---|---|---|
| 信号线 | 2根(SWDIO/SWCLK) | 5根(TCK/TMS/TDI/TDO/TRST) |
| 调试功能 | 基本调试 | 完整调试+边界扫描 |
| 资源占用 | 少 | 多 |
| 推荐使用 | 日常开发 | 量产测试 |
3.3 STM32最小系统
最小系统组成
定义:使MCU能正常工作的最少硬件电路
| 部分 | 作用 | 典型参数 |
|---|---|---|
| 电源 | 提供工作电压 | 3.3V±5%,去耦电容100nF |
| 时钟 | 提供系统时钟 | HSE 8MHz + LSE 32.768kHz |
| 复位 | 上电复位、手动复位 | 低电平复位,RC电路 |
| 调试接口 | 程序下载和调试 | SWD(PA13/PA14) |
| 启动配置 | 选择启动模式 | BOOT0/BOOT1引脚 |
电源电路
STM32F103供电要求:
- VDD = 2.0V ~ 3.6V(通常3.3V)
- VDDA(模拟电源)= VDD
- VSS = GND
典型电路:
外部5V ──► AMS1117-3.3 ──► VDD (3.3V)
│
[10μF] [100nF] [100nF]
│ │ │
GND VDD VDD
│ │
STM32
去耦电容:每个VDD引脚附近放置100nF陶瓷电容
时钟电路
| 时钟源 | 频率 | 精度 | 用途 |
|---|---|---|---|
| HSI | 8MHz | ±1%(出厂校准) | 系统启动、备用时钟 |
| HSE | 4~16MHz | 取决于晶振(通常±20ppm) | 主时钟源 |
| LSI | 40kHz | ±5% | 看门狗、RTC备用 |
| LSE | 32.768kHz | 高精度(±20ppm) | RTC时钟 |
HSE晶振电路:
┌─────┬─────┐
OSC_IN ─┤ │ ├─── OSC_OUT
│ 8MHz│
│晶振 │
└─────┴─────┘
│ │
[20pF] [20pF]
│ │
GND GND
注意:STM32F103的HSE输入范围为4~16MHz,常用8MHz
复位电路
复位类型:
- 上电复位(POR):电源电压达到阈值时自动复位
- 手动复位:按键触发NRST引脚
- 看门狗复位:WWDG/IWDG超时触发
典型RC复位电路:
VDD
│
[R] 10kΩ
│
├───────────► NRST(STM32)
│
[RST] 按键
│
[C] 100nF
│
GND
复位时序:NRST低电平持续时间需大于100ns
启动模式
| BOOT1 | BOOT0 | 启动模式 | 物理地址 | 用途 |
|---|---|---|---|---|
| X | 0 | 从Flash启动 | 0x08000000 | 正常程序运行 |
| 0 | 1 | 从系统存储器启动 | 0x1FFFF000 | 串口下载(ISP) |
| 1 | 1 | 从SRAM启动 | 0x20000000 | 调试 |
串口下载(ISP模式):
- 使用USART1(PA9/PA10)
- 配合ST官方ISP软件(如FlyMcu)
- 无需调试器即可下载程序
3.4 时钟系统
时钟树结构
┌───────────┐
┌─────────►│ HSI │ 8MHz(内部RC)
│ └─────┬─────┘
│ │
│ ┌─────┴─────┐
│ │ 2分频 │
│ └───────────┘
│ │
┌────┴────┐ ┌───────────┐
│ PLL │◄───│ 预分频器 │
│ 锁相环 │ └───────────┘
└────┬────┘ │
│ │
│ ┌────┴────┐
│ ┌──►│ HSE │ 4~16MHz(外部晶振)
│ │ └─────────┘
│ │
│ │ ┌───────────┐
│ └──►│ 预分频器 │
│ └───────────┘
│ │
▼ ▼
┌──────────┐ ┌──────────┐
│ SYSCLK │ │ SYSCLK │
│(系统时钟)│ │(系统时钟)│
└────┬─────┘ └──────────┘
│
│◄────────────────────────── USB时钟(48MHz,必须)
│
├────────► AHB Prescaler ──► HCLK(最高72MHz)
│ ──► Cortex-M3内核
│ ──► DMA
│
├────────► APB1 Prescaler ──► APB1总线(最高36MHz)
│ ──► TIM2-7、USART2-3、SPI2、I2C、CAN、USB
│
└────────► APB2 Prescaler ──► APB2总线(最高72MHz)
──► GPIO、USART1、SPI1、ADC、TIM1、TIM8
时钟配置要点
| 时钟 | 最大频率 | 来源 | 说明 |
|---|---|---|---|
| SYSCLK | 72MHz | HSI/HSE/PLL | 系统时钟 |
| HCLK | 72MHz | SYSCLK分频 | AHB总线 |
| PCLK1 | 36MHz | HCLK分频 | APB1低速外设 |
| PCLK2 | 72MHz | HCLK分频 | APB2高速外设 |
| ADCCLK | 14MHz | PCLK2分频 | ADC时钟 |
| USBCLK | 48MHz | PLL | USB必须48MHz |
Flash等待周期
为什么需要等待周期:
Flash的访问速度比CPU慢,当CPU时钟频率超过Flash访问速度时,需要插入等待周期。
STM32F103 Flash访问时间:
- 0等待:0 ~ 24MHz
- 1等待:24 ~ 48MHz
- 2等待:48 ~ 72MHz
| 系统时钟 | Flash等待周期 | 设置值 |
|---|---|---|
| 0 ~ 24MHz | 0 WS | FLASH_Latency_0 |
| 24 ~ 48MHz | 1 WS | FLASH_Latency_1 |
| 48 ~ 72MHz | 2 WS | FLASH_Latency_2 |
重要:配置不当会导致程序运行错误!
典型配置(72MHz系统时钟)
void SystemClock_Config(void)
{
// 1. 使能HSE
RCC_HSEConfig(RCC_HSE_ON);
while (RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET);
// 2. 配置Flash等待周期(72MHz需要2个等待周期)
FLASH_SetLatency(FLASH_Latency_2);
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); // 使能预取指
// 3. 配置PLL:HSE × 9 = 8MHz × 9 = 72MHz
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
RCC_PLLCmd(ENABLE);
while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
// 4. 配置AHB、APB1、APB2分频
RCC_HCLKConfig(RCC_SYSCLK_Div1); // HCLK = 72MHz
RCC_PCLK1Config(RCC_HCLK_Div2); // PCLK1 = 36MHz
RCC_PCLK2Config(RCC_HCLK_Div1); // PCLK2 = 72MHz
// 5. 切换系统时钟到PLL
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
while (RCC_GetSYSCLKSource() != 0x08); // 等待切换完成
}
3.5 GPIO原理与应用
GPIO基本结构
┌─────────────────────────────────────────────────────────┐
│ GPIO内部结构 │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ 输入路径: │
│ 引脚 ──► 保护二极管 ──► 输入驱动器 ──► TTL施密特触发器 ──► 输入数据寄存器 │
│ │
│ 输出路径: │
│ 输出数据寄存器 ──► 输出控制器 ──► P-MOS/N-MOS ──► 引脚 │
│ │
│ ┌─────────┐ │
│ │ 上拉 │◄── 配置开关 │
│ │ 电阻 │ │
│ └────┬────┘ │
│ │ │
│ 引脚──┼───────────────────────────────────────► 外部电路 │
│ │ │
│ ┌────┴────┐ │
│ │ 下拉 │◄── 配置开关 │
│ │ 电阻 │ │
│ └─────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────┘
GPIO工作模式详解
| 模式 | 编号 | 说明 | 内部电路 | 典型应用 |
|---|---|---|---|---|
| 输入浮空 | 00 | 高阻态输入 | 上下拉断开 | 外部上拉/下拉电路 |
| 输入上拉 | 01 | 内部30-50kΩ上拉 | 上拉导通 | 按键检测(按键另一端接地) |
| 输入下拉 | 10 | 内部30-50kΩ下拉 | 下拉导通 | 信号检测 |
| 模拟输入 | 11 | 连接ADC | TTL缓冲器断开 | 模拟信号采集 |
| 推挽输出 | 00 | P-MOS+N-MOS同时工作 | 可输出高/低 | LED驱动、数字输出 |
| 开漏输出 | 01 | 仅N-MOS工作 | 仅下拉 | I2C总线、线与功能 |
| 复用推挽 | 10 | 外设控制推挽 | 外设控制 | UART TX、SPI、PWM |
| 复用开漏 | 11 | 外设控制开漏 | 外设控制 | I2C(SDA/SCL) |
推挽输出 vs 开漏输出
推挽输出(Push-Pull):
VDD
│
[P-MOS] ──► 输出高电平(强驱动)
│
引脚
│
[N-MOS] ──► 输出低电平(强驱动)
│
GND
特点:
- 可输出高电平或低电平
- 驱动能力强(最大±8mA,总电流150mA)
- 不能多个输出并联(会短路)
开漏输出(Open-Drain):
VDD
│
[上拉电阻] ◄── 外部必需
│
引脚
│
[N-MOS] ──► 输出低电平
│
GND
特点:
- 只能输出低电平或高阻态
- 高电平需要外部上拉(或内部上拉)
- 支持多个开漏输出并联(线与)
- I2C总线必须使用开漏输出
GPIO速度配置
| 配置值 | 速度 | 上升/下降时间 | 适用场景 |
|---|---|---|---|
| 2MHz | 低速 | ~50ns | 静态IO、按键输入 |
| 10MHz | 中速 | ~10ns | 普通外设如UART |
| 50MHz | 高速 | ~5ns | SPI、FSMC、高速信号 |
注意:速度越高,功耗越大,电磁辐射越强
GPIO寄存器详解
| 寄存器 | 地址偏移 | 功能 | 说明 |
|---|---|---|---|
| CRL | 0x00 | 端口配置低寄存器 | 配置Pin0~Pin7 |
| CRH | 0x04 | 端口配置高寄存器 | 配置Pin8~Pin15 |
| IDR | 0x08 | 端口输入数据寄存器 | 只读,读取引脚状态 |
| ODR | 0x0C | 端口输出数据寄存器 | 读写,设置输出电平 |
| BSRR | 0x10 | 端口位设置/清除寄存器 | 原子操作 |
| BRR | 0x14 | 端口位清除寄存器 | 清除ODR位 |
| LCKR | 0x18 | 端口配置锁存寄存器 | 锁定配置 |
CRL/CRH寄存器格式:
每4位控制一个引脚(CNF[1:0] + MODE[1:0])
CRL(控制Pin0~Pin7):
┌─────────┬─────────┬─────────┬─────────┐
│ Pin7 │ Pin6 │ ... │ Pin0 │
│ CNF MODE│ CNF MODE│ │ CNF MODE│
└─────────┴─────────┴─────────┴─────────┘
4位 4位 4位 4位
BSRR原子操作
BSRR寄存器:
- Bit0~Bit15:设置ODR对应位(写1置位)
- Bit16~Bit31:清除ODR对应位(写1清零)
代码对比:
// 方法1:读-改-写(非原子,可能产生竞争)
GPIOA->ODR |= (1 << 5); // 设置PA5
// 过程:读ODR → 或运算 → 写ODR
// 方法2:使用BSRR(原子操作,推荐)
GPIOA->BSRR = (1 << 5); // 设置PA5
GPIOA->BSRR = (1 << 21); // 清除PA5(16+5=21)
原子操作的重要性:在多任务环境或中断中,避免数据竞争
复用功能重映射
概念:部分外设引脚可通过AFIO重映射到其他GPIO引脚
常用重映射:
| 外设 | 默认引脚 | 重映射引脚 | 重映射函数 |
|---|---|---|---|
| USART1_TX | PA9 | PB6 | GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE) |
| USART1_RX | PA10 | PB7 | |
| USART2_TX | PA2 | PD5 | GPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE) |
| TIM3_CH1 | PA6 | PB4 | GPIO_PinRemapConfig(GPIO_FullRemap_TIM3, ENABLE) |
| SPI1 | PA5/6/7 | PB3/4/5 | GPIO_PinRemapConfig(GPIO_Remap_SPI1, ENABLE) |
第三章 复试问答
-
嵌入式系统开发与通用PC软件开发有什么不同?什么是交叉开发模式?
-
STM32最小系统由哪几部分组成?各部分的作用是什么?画出最小系统的原理框图。
-
STM32有哪些时钟源?PLL的作用是什么?如果系统时钟配置为72MHz,HSE使用8MHz晶振,PLL倍频系数应该是多少?
-
GPIO有哪些工作模式?推挽输出和开漏输出有什么区别?各自适用于什么场景?画出开漏输出的等效电路图。
-
什么是复用功能重映射?举例说明其应用场景。如何配置USART1重映射到PB6/PB7?
-
使用BSRR寄存器操作GPIO相比直接操作ODR有什么优点?什么情况下必须使用BSRR?
-
STM32的启动模式有哪几种?串口下载使用的是哪种启动模式?BOOT0和BOOT1引脚如何配置?
-
SWD和JTAG接口有什么区别?各需要多少根信号线?为什么推荐日常使用SWD?
-
GPIO的速度配置有哪几种?速度越高有什么优缺点?
第四章 STM32中断系统原理及应用
4.1 中断基础
中断的基本概念
中断定义:CPU在执行程序过程中,遇到内部或外部紧急事件,暂时中止当前程序的执行,转去执行相应的事件处理程序(中断服务程序,ISR),处理完毕后返回原程序继续执行。
中断相关术语
| 术语 | 英文 | 说明 |
|---|---|---|
| 中断源 | Interrupt Source | 引起中断的事件或外设 |
| 中断请求 | Interrupt Request (IRQ) | 中断源向CPU发出的请求信号 |
| 中断向量 | Interrupt Vector | 中断服务程序的入口地址 |
| 中断向量表 | IVT | 存放所有中断向量的数组 |
| 中断服务程序 | ISR | 处理中断的函数 |
| 中断延迟 | Interrupt Latency | 从请求到执行ISR的时间 |
| 中断嵌套 | Nested Interrupt | 高优先级中断打断低优先级中断 |
中断处理流程
主程序执行
│
▼ 中断请求发生
保存现场(硬件自动)
│ - xPSR → 堆栈
│ - PC → 堆栈
│ - LR → 堆栈
│ - R12 → 堆栈
│ - R3-R0 → 堆栈
▼
获取中断向量
│
▼
执行ISR
│
▼
恢复现场(硬件自动)
│ - R0-R3 ← 堆栈
│ - R12 ← 堆栈
│ - LR ← 堆栈
│ - PC ← 堆栈
│ - xPSR ← 堆栈
▼
返回主程序(执行BX LR或专用返回指令)
Cortex-M3中断延迟
固定中断延迟:12个时钟周期(确定性)
延迟组成详解:
| 步骤 | 时钟周期 | 说明 |
|---|---|---|
| 请求同步 | 2 | 将异步中断请求同步到CPU时钟 |
| 压栈 | 6 | 保存xPSR、PC、LR、R12、R3-R0(8个寄存器) |
| 取向量 | 2 | 从向量表读取ISR入口地址 |
| 更新寄存器 | 2 | 更新SP、LR、PC等寄存器 |
| 总计 | 12 |
计算示例:
系统时钟72MHz时:
中断延迟 = 12 / 72MHz = 12 / 72,000,000 = 0.167μs = 167ns
系统时钟8MHz时:
中断延迟 = 12 / 8MHz = 1.5μs
尾链优化(Tail Chaining):
- 如果中断返回时恰好有新的中断请求
- 跳过出栈(6周期)和入栈(6周期),直接执行新ISR
- 延迟仅需6周期
迟到中断(Late Arrival):
- 如果在压栈过程中发生更高优先级中断
- 立即切换到高优先级中断
- 不增加延迟
4.2 STM32中断系统
NVIC(嵌套向量中断控制器)
NVIC特性:
| 特性 | 参数 |
|---|---|
| 中断数量 | 最多240个外部中断(IRQ0~IRQ239) |
| 优先级位数 | 8位(实际使用高4位,16级) |
| 优先级分组 | 5种分组方式 |
| 尾链优化 | 支持 |
| 迟到中断 | 支持(Late Arrival) |
| 退出咬尾 | 支持(POP pre-emption) |
优先级系统
优先级寄存器(8位):
7 6 5 4 3 2 1 0
┌───────┬───────┬───────┬───────┬───────┬───────┬───────┬───────┐
│ IP[7]│ IP[6]│ IP[5]│ IP[4]│ 0 │ 0 │ 0 │ 0 │
└───────┴───────┴───────┴───────┴───────┴───────┴───────┴───────┘
─────────────────────────────────────
实际有效的4位
STM32F103只使用高4位,因此实际优先级为0~15(共16级)。
优先级分组
分组方式(通过SCB->AIRCR寄存器的PRIGROUP字段配置):
| 分组 | 抢占优先级位数 | 子优先级位数 | 抢占级数 | 子优先级级数 | 宏定义 |
|---|---|---|---|---|---|
| Group 0 | 0位 | 4位 | 1级(0) | 16级(0-15) | NVIC_PriorityGroup_0 |
| Group 1 | 1位 | 3位 | 2级(0-1) | 8级(0-7) | NVIC_PriorityGroup_1 |
| Group 2 | 2位 | 2位 | 4级(0-3) | 4级(0-3) | NVIC_PriorityGroup_2 |
| Group 3 | 3位 | 1位 | 8级(0-7) | 2级(0-1) | NVIC_PriorityGroup_3 |
| Group 4 | 4位 | 0位 | 16级(0-15) | 1级(0) | NVIC_PriorityGroup_4 |
最常用的是NVIC_PriorityGroup_2
抢占优先级与子优先级的区别
| 特性 | 抢占优先级(Preemption Priority) | 子优先级(Sub Priority) |
|---|---|---|
| 打断能力 | 高优先级可打断低优先级 | 不能打断,只能排队 |
| 相同优先级响应顺序 | 按子优先级 | 按硬件顺序(IRQ编号) |
| 中断嵌套 | 支持 | 不支持 |
| 作用时机 | 决定能否抢占 | 相同抢占级内决定响应顺序 |
示例(NVIC_PriorityGroup_2):
中断A(抢占=2,子=1)
中断B(抢占=2,子=0)
中断C(抢占=1,子=3)
- C的抢占优先级最高(1<2),可以打断A或B
- A和B的抢占优先级相同(都是2),不能相互打断
- 如果A和B同时发生,B先响应(子优先级0<1)
4.3 外部中断/事件控制器(EXTI)
EXTI结构
┌─────────────────────────────────────────────────────────────┐
│ 外部中断/事件控制器(EXTI) │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 16个外部中断线(EXTI0~EXTI15) │ │
│ │ │ │
│ │ EXTI0 ← PA0/PB0/PC0/PD0/PE0/PF0/PG0 任选其一 │ │
│ │ EXTI1 ← PA1/PB1/PC1/... │ │
│ │ ... │ │
│ │ EXTI15 ← PA15/PB15/PC15/... │ │
│ └──────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────┐ │
│ │ 边沿检测器 │ │
│ │ (上升/下降) │ │
│ └───────┬───────┘ │
│ │ │
│ ┌─────────────┼─────────────┐ │
│ ▼ ▼ ▼ │
│ ┌────────┐ ┌────────┐ ┌────────┐ │
│ │ 中断 │ │ 事件 │ │ 软件 │ │
│ │ 请求 │ │ 请求 │ │ 触发 │ │
│ └───┬────┘ └───┬────┘ └───┬────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ NVIC 其他外设 EXTI->SWIER │
│ │
└─────────────────────────────────────────────────────────────┘
EXTI与GPIO映射关系
| EXTI线 | 可映射的GPIO引脚 | 说明 |
|---|---|---|
| EXTI0 | PA0, PB0, PC0, PD0, PE0, PF0, PG0 | 通过AFIO_EXTICR1配置 |
| EXTI1 | PA1, PB1, PC1, … | 通过AFIO_EXTICR1配置 |
| … | … | … |
| EXTI15 | PA15, PB15, PC15, … | 通过AFIO_EXTICR4配置 |
重要:同一时刻,每条EXTI线只能映射到一个GPIO引脚
EXTI触发方式
| 触发方式 | 配置寄存器 | 适用场景 |
|---|---|---|
| 上升沿触发 | EXTI_RTSR | 信号从低到高变化时触发 |
| 下降沿触发 | EXTI_FTSR | 信号从高到低变化时触发 |
| 双边沿触发 | RTSR+FTSR | 任何电平变化都触发 |
| 软件触发 | EXTI_SWIER | 通过软件强制触发 |
中断 vs 事件
| 特性 | 中断(Interrupt) | 事件(Event) |
|---|---|---|
| 触发对象 | NVIC,CPU响应 | 其他外设,无需CPU |
| CPU参与 | 需要 | 不需要 |
| 用途 | 需要CPU处理的任务 | 唤醒CPU、触发DMA等 |
| 示例 | 按键中断处理 | 从低功耗模式唤醒 |
4.4 中断配置步骤
完整配置流程
1. 使能GPIO时钟(RCC_APB2PeriphClockCmd)
│
2. 配置GPIO为输入模式(GPIO_Mode_IPU/IPD/IN_FLOATING)
│
3. 使能AFIO时钟(RCC_APB2Periph_AFIO)
│
4. 连接EXTI线到GPIO(GPIO_EXTILineConfig)
│
5. 配置EXTI(EXTI_Init:触发方式、使能)
│
6. 配置NVIC(NVIC_Init:优先级、使能中断)
│
7. 编写中断服务程序(函数名必须正确)
│
8. 在ISR中清除中断标志(EXTI_ClearITPendingBit)
代码示例(按键外部中断)
// 全局变量
volatile uint8_t key_flag = 0;
// 1. GPIO和EXTI配置
void EXTI_Key_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
EXTI_InitTypeDef EXTI_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
// 使能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_AFIO, ENABLE);
// GPIO配置(PC13,内部上拉)
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; // 输入上拉
GPIO_Init(GPIOC, &GPIO_InitStruct);
// 连接EXTI13到PC13
GPIO_EXTILineConfig(GPIO_PortSourceGPIOC, GPIO_PinSource13);
// EXTI配置(下降沿触发)
EXTI_InitStruct.EXTI_Line = EXTI_Line13;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt; // 中断模式
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling; // 下降沿
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStruct);
// NVIC配置
NVIC_InitStruct.NVIC_IRQChannel = EXTI15_10_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2; // 抢占优先级2
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2; // 子优先级2
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
}
// 2. 中断服务程序(函数名必须与启动文件中的向量表一致)
void EXTI15_10_IRQHandler(void)
{
// 检查是否是EXTI13触发
if (EXTI_GetITStatus(EXTI_Line13) != RESET)
{
// 延时消抖(简单示例,实际应用应使用定时器)
for (volatile int i = 0; i < 100000; i++);
// 确认按键确实按下
if (GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_13) == 0)
{
key_flag = 1; // 设置标志
}
// 必须清除中断标志!
EXTI_ClearITPendingBit(EXTI_Line13);
}
}
常用外设中断向量表
| 外设 | 中断向量 | 说明 |
|---|---|---|
| 外部中断 | ||
| EXTI0 | EXTI0_IRQn | 线0中断 |
| EXTI1 | EXTI1_IRQn | 线1中断 |
| EXTI2 | EXTI2_IRQn | 线2中断 |
| EXTI3 | EXTI3_IRQn | 线3中断 |
| EXTI4 | EXTI4_IRQn | 线4中断 |
| EXTI5-9 | EXTI9_5_IRQn | 线5~9共享 |
| EXTI10-15 | EXTI15_10_IRQn | 线10~15共享 |
| 定时器 | ||
| TIM2 | TIM2_IRQn | TIM2全局中断 |
| TIM3 | TIM3_IRQn | TIM3全局中断 |
| TIM4 | TIM4_IRQn | TIM4全局中断 |
| 串口 | ||
| USART1 | USART1_IRQn | USART1全局中断 |
| USART2 | USART2_IRQn | USART2全局中断 |
| USART3 | USART3_IRQn | USART3全局中断 |
| DMA | ||
| DMA1通道1 | DMA1_Channel1_IRQn | DMA1通道1中断 |
| DMA1通道2 | DMA1_Channel2_IRQn | DMA1通道2中断 |
4.5 按键消抖
消抖原理
机械按键在按下和松开时会产生抖动,持续时间约5~20ms。
抖动波形:
理想波形: 实际波形:
高电平 ─┐ 高电平 ─┐ ┌─┐ ┌──────┐
│ │ │ │ │
│ │ └─┐ │ │
▼ ▼ │ │ ▼
低电平 └──────► 低电平 └──┘ └──────┘──►
按下 抖动区 稳定
消抖方法
方法1:延时消抖(软件延时)
uint8_t Key_Scan(void)
{
if (GPIO_ReadInputDataBit(KEY_GPIO, KEY_PIN) == 0) // 检测到按下
{
delay_ms(20); // 延时20ms消抖
if (GPIO_ReadInputDataBit(KEY_GPIO, KEY_PIN) == 0) // 再次确认
{
while (GPIO_ReadInputDataBit(KEY_GPIO, KEY_PIN) == 0); // 等待释放
return 1; // 确认按下
}
}
return 0;
}
// 缺点:阻塞CPU,不适用于实时系统
方法2:定时器消抖(推荐)
// 状态机方式
#define KEY_STATE_IDLE 0
#define KEY_STATE_PRESS 1
#define KEY_STATE_RELEASE 2
volatile uint8_t key_state = KEY_STATE_IDLE;
volatile uint16_t key_cnt = 0;
// 10ms定时器中断
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
{
switch (key_state)
{
case KEY_STATE_IDLE:
if (Key_Read() == 0) // 检测到按下
{
key_state = KEY_STATE_PRESS;
key_cnt = 0;
}
break;
case KEY_STATE_PRESS:
if (Key_Read() == 0) // 确认按下
{
if (++key_cnt >= 2) // 20ms确认
{
key_flag = 1; // 确认有效按键
key_state = KEY_STATE_RELEASE;
}
}
else // 抖动,返回IDLE
{
key_state = KEY_STATE_IDLE;
}
break;
case KEY_STATE_RELEASE:
if (Key_Read() == 1) // 检测到释放
{
key_state = KEY_STATE_IDLE;
}
break;
}
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
第四章 复试问答
-
什么是中断?中断与轮询相比有什么优点?什么情况下应该使用中断?
-
Cortex-M3的中断处理流程是怎样的?哪些操作是硬件自动完成的?中断延迟是多少个时钟周期?
-
什么是NVIC?STM32的NVIC支持多少个外部中断?优先级是如何配置的?
-
解释中断优先级分组的概念。NVIC_PriorityGroup_2的含义是什么?抢占优先级和子优先级有什么区别?
-
EXTI0~EXTI15分别可以连接到哪些GPIO引脚?同一时刻,一条EXTI线可以映射到多个GPIO引脚吗?
-
配置STM32外部中断的完整步骤是什么?写出代码框架并说明每一步的作用。
-
什么是按键抖动?为什么要进行消抖?有哪些消抖方法?各自的优缺点是什么?
-
中断和事件有什么区别?各适用于什么场景?
-
什么是中断嵌套?什么情况下会发生中断嵌套?
-
在Cortex-M3中,如何计算从中断请求到执行ISR第一条指令的延迟?
第五章 STM32定时器原理与应用
5.1 定时器概述
STM32定时器分类
| 定时器类型 | 编号 | 数量 | 位数 | 主要特性 |
|---|---|---|---|---|
| 高级定时器 | TIM1, TIM8 | 2 | 16位 | 3通道PWM互补输出、死区控制、刹车输入、重复计数器 |
| 通用定时器 | TIM2~TIM5 | 4 | 16位 | 4通道、输入捕获、输出比较、PWM、编码器接口 |
| 基本定时器 | TIM6, TIM7 | 2 | 16位 | 无输出引脚、仅定时、DAC触发 |
| 看门狗 | IWDG, WWDG | 2 | - | 系统监控、复位功能 |
| SysTick | SysTick | 1 | 24位 | 系统节拍、RTOS心跳、延时函数 |
定时器特性详细对比
| 特性 | 高级定时器 (TIM1/8) | 通用定时器 (TIM2-5) | 基本定时器 (TIM6/7) |
|---|---|---|---|
| 计数模式 | 向上/向下/中央对齐 | 向上/向下/中央对齐 | 仅向上 |
| 通道数 | 4(带互补输出) | 4 | 0 |
| 互补输出 | 支持(带死区) | 不支持 | 不支持 |
| 死区控制 | 支持 | 不支持 | 不支持 |
| 刹车输入 | 支持 | 不支持 | 不支持 |
| 重复计数器 | 支持 | 不支持 | 不支持 |
| 触发输出 | 支持 | 支持 | 支持(仅DAC) |
| 编码器接口 | 不支持 | 支持 | 不支持 |
5.2 定时器工作原理
定时器时钟源
| 时钟源 | 说明 | 频率 |
|---|---|---|
| 内部时钟(CK_INT) | APB1/APB2总线时钟 | 72MHz/36MHz |
| 外部时钟模式1 | TI1/TI2引脚输入 | 外部信号 |
| 外部时钟模式2 | ETR引脚输入 | 外部信号 |
| 内部触发(ITR) | 其他定时器触发 | 级联使用 |
注意:TIM2-7在APB1(最高36MHz),TIM1/8在APB2(最高72MHz)
定时器基本结构
┌───────────┐
┌─────────►│ PSC │ 预分频器
│ │ (16位) │
│ └─────┬─────┘
│ │
┌────┴────┐ ┌───────────┐
│ 时钟源 │───►│ CNT │ 计数器
│ │ │ (16位) │
└─────────┘ └─────┬─────┘
│
┌────┴────┐
│ ARR │ 自动重装载寄存器
│ (16位) │
└────┬────┘
│
┌──────────┼──────────┐
▼ ▼ ▼
┌────────┐ ┌────────┐ ┌────────┐
│ CCR1 │ │ CCR2 │ │ CCR3 │ 捕获/比较寄存器
│ 通道1 │ │ 通道2 │ │ 通道3 │
└───┬────┘ └───┬────┘ └───┬────┘
│ │ │
▼ ▼ ▼
CH1 CH2 CH3 输出引脚
定时时间计算
公式:
T定时=(PSC+1)×(ARR+1)fCKT_{定时} = \frac{(PSC + 1) \times (ARR + 1)}{f_{CK}}T定时=fCK(PSC+1)×(ARR+1)
参数说明:
- PSCPSCPSC:预分频器值(0~65535)
- ARRARRARR:自动重装载值(0~65535)
- fCKf_{CK}fCK:定时器时钟频率
例题1:系统时钟72MHz,定时1秒
方案1:PSC=7199, ARR=9999
计数器频率 = 72MHz / (7199+1) = 10kHz
定时时间 = (9999+1) / 10kHz = 1秒
方案2:PSC=999, ARR=71999
计数器频率 = 72MHz / 1000 = 72kHz
定时时间 = 72000 / 72kHz = 1秒
例题2:定时100ms(TIM2,APB1=36MHz)
PSC = 3599(分频3600)
计数器频率 = 36MHz / 3600 = 10kHz
ARR = 999(1000个计数周期)
定时 = 1000 / 10kHz = 100ms
5.3 PWM输出
PWM原理
PWM (Pulse Width Modulation):脉冲宽度调制
┌────────┐ ┌────────┐
│ │ │ │
─────┘ └──────────┘ └────
◄─T_on─►
◄──────T_period──────►
占空比 = T_on / T_period × 100%
PWM频率和占空比计算
频率公式:
FPWM=fCK(PSC+1)×(ARR+1)F_{PWM} = \frac{f_{CK}}{(PSC + 1) \times (ARR + 1)}FPWM=(PSC+1)×(ARR+1)fCK
占空比公式:
Duty=CCRARR+1×100%Duty = \frac{CCR}{ARR + 1} \times 100\%Duty=ARR+1CCR×100%
例题3:产生1kHz PWM,占空比50%(系统时钟72MHz)
目标频率:1kHz
取PSC = 71(分频72),计数器频率 = 1MHz
ARR = 999(1000个计数周期)
PWM频率 = 1MHz / 1000 = 1kHz ✓
占空比50%:CCR = 500
占空比25%:CCR = 250
占空比75%:CCR = 750
PWM模式
| 模式 | 配置 | CNT < CCR时 | CNT ≥ CCR时 |
|---|---|---|---|
| PWM模式1 | OCxM=110 | 输出有效电平 | 输出无效电平 |
| PWM模式2 | OCxM=111 | 输出无效电平 | 输出有效电平 |
有效电平:由CCER寄存器的CCxP位决定(0=高电平有效,1=低电平有效)
PWM代码示例
void PWM_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct;
TIM_OCInitTypeDef TIM_OCInitStruct;
// 1. 使能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 2. GPIO配置(PA8=TIM1_CH1)
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
// 3. 时基单元配置
TIM_TimeBaseStruct.TIM_Prescaler = 71; // 72MHz/72=1MHz
TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStruct.TIM_Period = 999; // 1kHz
TIM_TimeBaseStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStruct);
// 4. PWM模式配置
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStruct.TIM_Pulse = 500; // 占空比50%
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM1, &TIM_OCInitStruct);
// 5. 使能定时器
TIM_Cmd(TIM1, ENABLE);
TIM_CtrlPWMOutputs(TIM1, ENABLE); // 高级定时器需要使能PWM输出
}
// 动态修改占空比
void PWM_SetDuty(uint16_t duty)
{
TIM_SetCompare1(TIM1, duty); // duty范围:0~ARR
}
死区控制详解
死区时间定义:在PWM互补输出中,上下桥臂切换时的延迟时间
为什么需要死区:
无死区(危险): 有死区(安全):
上桥臂 ─┐ 上桥臂 ─┐ ┌───┐
│ ← 同时导通!短路 │ │ │
▼ ▼ │ ▼
下桥臂 ─┘ 下桥臂 ─┘ └───┘
↑
死区时间
死区时间计算:
死区时间 = DTG[7:0] × Tdtg
其中:
- DTG[7:5]=0xx时:Tdtg = TDTS,死区 = DTG[7:0] × TDTS
- DTG[7:5]=10x时:Tdtg = 2×TDTS,死区 = (64+DTG[5:0]) × 2×TDTS
- DTG[7:5]=110时:Tdtg = 8×TDTS,死区 = (32+DTG[4:0]) × 8×TDTS
- DTG[7:5]=111时:Tdtg = 16×TDTS,死区 = (32+DTG[4:0]) × 16×TDTS
TDTS = 1 / (fCK / 2^CKD)
典型值:1-2μs(根据MOSFET开关速度)
5.4 输入捕获
输入捕获原理
用途:
- 测量脉冲宽度
- 测量信号频率
- 测量PWM占空比
工作原理:
- 配置捕获边沿(上升沿/下降沿/双边沿)
- 发生捕获时,CNT当前值锁存到CCR
- 计算两次捕获值之差,得到时间
测量频率的方法
方法1:周期测量法(适合低频)
测量一个周期的时间T
频率 = 1 / T
方法2:频率测量法(适合高频)
固定时间窗口内计数脉冲数N
频率 = N / T窗口
输入捕获代码示例
// 测量PWM频率和占空比
volatile uint16_t IC1Value = 0; // 周期
volatile uint16_t IC2Value = 0; // 高电平时间
volatile float Frequency = 0;
volatile float DutyCycle = 0;
void TIM_IC_Init(void)
{
TIM_ICInitTypeDef TIM_ICInitStruct;
// TIM3 CH1配置为输入捕获
TIM_ICInitStruct.TIM_Channel = TIM_Channel_1;
TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising; // 上升沿触发
TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStruct.TIM_ICFilter = 0x0;
TIM_ICInit(TIM3, &TIM_ICInitStruct);
// 使能捕获中断
TIM_ITConfig(TIM3, TIM_IT_CC1, ENABLE);
}
void TIM3_IRQHandler(void)
{
static uint16_t lastCapture = 0;
uint16_t currentCapture;
if (TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET)
{
currentCapture = TIM_GetCapture1(TIM3);
if (lastCapture != 0)
{
IC1Value = currentCapture - lastCapture;
Frequency = (float)SystemCoreClock / IC1Value;
}
lastCapture = currentCapture;
TIM_ClearITPendingBit(TIM3, TIM_IT_CC1);
}
}
5.5 看门狗定时器
独立看门狗(IWDG)
特点:
- 独立时钟(LSI 40kHz)
- 即使主时钟失效也能工作
- 用于检测程序跑飞
工作原理:
计数器从重装值递减到0时复位MCU
必须在计数器减到0前"喂狗"(写入0xAAAA)
超时时间计算:
TIWDG=重装值imes预分频系数LSI频率(40kHz)T_{IWDG} = \frac{重装值 imes 预分频系数}{LSI频率(40kHz)}TIWDG=LSI频率(40kHz)重装值imes预分频系数
| 预分频 | 最小超时 | 最大超时(重装值=0xFFF) |
|---|---|---|
| /4 | 0.1ms | 409.6ms |
| /8 | 0.2ms | 819.2ms |
| /16 | 0.4ms | 1.638s |
| /32 | 0.8ms | 3.276s |
| /64 | 1.6ms | 6.553s |
| /128 | 3.2ms | 13.1s |
| /256 | 6.4ms | 26.2s |
窗口看门狗(WWDG)
特点:
- 使用APB1时钟
- 有窗口限制(必须在指定窗口内喂狗)
- 提前喂狗也会复位
应用场景:需要精确时序监控的场合
第五章 复试问答
-
STM32有哪几类定时器?它们各有什么特点?高级定时器和通用定时器有什么区别?
-
定时器的PWM频率如何计算?如果要产生1kHz的PWM,占空比可调范围为0-100%,PSC和ARR应如何设置?
-
什么是死区时间?为什么在电机控制中需要死区控制?死区时间过长或过短会有什么影响?
-
输入捕获模式有什么应用?简述使用输入捕获测量信号频率的原理。
-
通用定时器的编码器接口模式有什么作用?适合什么应用场景?
-
独立看门狗和窗口看门狗有什么区别?各适用于什么场景?
-
SysTick定时器有什么特点?它在RTOS中有什么作用?
-
定时器的中央对齐模式和其他模式有什么区别?什么情况下需要使用中央对齐模式?
第六章 STM32串口通信
6.1 通信基础
并行通信 vs 串行通信
| 特性 | 并行通信 | 串行通信 |
|---|---|---|
| 数据线 | 多根(8/16/32位) | 1根(半双工)或2根(全双工) |
| 传输速度 | 快(同时传输多位) | 相对慢(逐位传输) |
| 传输距离 | 短(走线长度差异影响) | 长(差分传输可达km级) |
| 成本 | 高(线材、接口多) | 低(线材少) |
| 抗干扰 | 差(多根线易受干扰) | 好(可采用差分传输) |
| 应用场景 | 芯片内部总线、短距离 | 设备间通信、远距离 |
同步通信 vs 异步通信
| 特性 | 同步通信 | 异步通信 |
|---|---|---|
| 时钟 | 需要共享时钟线 | 不需要,双方约定波特率 |
| 数据格式 | 连续数据流 | 帧格式(起始位+数据+停止位) |
| 效率 | 高(无额外开销位) | 较低(有起始/停止位开销) |
| 复杂度 | 较高(需时钟同步) | 较低 |
| 典型应用 | SPI、I2C、同步UART | UART、RS232、RS485 |
STM32的USART:支持同步和异步模式,通常使用异步模式
6.2 USART原理
USART数据帧格式
┌───────┬─────────┬───────┬─────────┬───────┐
│ 起始位 │ 数据位 │ 校验位 │ 停止位 │ 空闲 │
│ (1位) │(7/8/9位)│(0/1位) │(1/2位) │ │
└───────┴─────────┴───────┴─────────┴───────┘
| 字段 | 长度 | 电平 | 说明 |
|---|---|---|---|
| 起始位 | 1位 | 低电平(0) | 标志帧开始,提供同步 |
| 数据位 | 7/8/9位 | - | 实际数据,LSB先传 |
| 校验位 | 0或1位 | - | 奇校验/偶校验/无校验 |
| 停止位 | 1或2位 | 高电平(1) | 标志帧结束 |
| 空闲位 | 任意 | 高电平(1) | 帧间间隔 |
典型配置:1位起始 + 8位数据 + 无校验 + 1位停止 = 10位/帧
波特率计算
波特率定义:每秒传输的码元个数(单位:bps)
计算公式:
USARTDIV=fPCLK16×波特率USARTDIV = \frac{f_{PCLK}}{16 \times 波特率}USARTDIV=16×波特率fPCLK
BRR寄存器设置:
- 整数部分:USARTDIV的整数部分 → BRR[15:4]
- 小数部分:USARTDIV的小数部分×16 → BRR[3:0]
例题1:系统时钟72MHz,波特率115200
USARTDIV = 72000000 / (16 × 115200) = 39.0625
整数部分:39 = 0x27
小数部分:0.0625 × 16 = 1 = 0x1
BRR = 0x271
例题2:系统时钟36MHz(APB1),波特率9600
USARTDIV = 36000000 / (16 × 9600) = 234.375
整数部分:234 = 0xEA
小数部分:0.375 × 16 = 6 = 0x6
BRR = 0xEA6
波特率误差
误差计算:
误差=∣实际波特率−目标波特率∣目标波特率×100%误差 = \frac{|实际波特率 - 目标波特率|}{目标波特率} \times 100\%误差=目标波特率∣实际波特率−目标波特率∣×100%
允许误差:
- 单设备通信:< 2%
- 多设备通信:< 1%
- 最佳:< 0.5%
例题3:计算误差
目标波特率:115200
实际配置:BRR=0x271 → USARTDIV=39.0625
实际波特率 = 72MHz / (16 × 39.0625) = 115200
误差 = 0%(完美匹配)
6.3 RS232/RS422/RS485
RS232标准
| 参数 | 规格 | 说明 |
|---|---|---|
| 信号电平 | ±3V ~ ±15V | 逻辑1=-3V-15V,逻辑0=+3V+15V |
| 传输距离 | 最大15m | 受电容负载限制 |
| 传输速率 | 最大20kbps | 距离越远,速率越低 |
| 连接方式 | 点对点(全双工) | TX→RX,RX→TX |
| 接口类型 | DB9或DB25 | 常用DB9 |
电平转换:STM32的TTL电平(0-3.3V)需通过MAX3232转换为RS232电平
RS422标准
| 参数 | 规格 |
|---|---|
| 信号方式 | 差分传输(4线制) |
| 传输距离 | 1200m(100kbps时) |
| 传输速率 | 最大10Mbps |
| 连接方式 | 1对10(1个驱动器,10个接收器) |
| 通信方式 | 全双工 |
RS485标准
| 参数 | 规格 |
|---|---|
| 信号方式 | 差分传输(2线制) |
| 传输距离 | 1200m(100kbps时) |
| 传输速率 | 最大10Mbps |
| 节点数 | 最多32个(标准),128个(扩展) |
| 连接方式 | 总线型拓扑 |
| 通信方式 | 半双工 |
RS485优势:
- 远距离传输(1200m)
- 多节点通信(32个)
- 差分信号,抗干扰强
- 适合工业环境
三种标准对比
| 特性 | RS232 | RS422 | RS485 |
|---|---|---|---|
| 通信方式 | 全双工 | 全双工 | 半双工 |
| 拓扑结构 | 点对点 | 点对多点 | 总线型 |
| 驱动器数 | 1 | 1 | 32 |
| 接收器数 | 1 | 10 | 32 |
| 最大距离 | 15m | 1200m | 1200m |
| 最大速率 | 20kbps | 10Mbps | 10Mbps |
| 抗干扰 | 差 | 好 | 最好 |
| 应用场景 | PC调试 | 工业控制 | 工业网络 |
终端电阻
作用:
- 消除信号反射(阻抗匹配)
- 提供直流偏置
- 确保总线空闲时处于确定电平
阻值:120Ω(与双绞线特性阻抗匹配)
接法:总线两端各接一个120Ω电阻
120Ω 120Ω
┌────//\/\────┐ ┌────//\/\────┐
│ │ │ │
┌───┴───┐ ┌───┴───┐ │ ┌───┴───┐
│ 节点1 │─────│ 节点2 │──┘ │ 节点3 │
└───────┘ └───────┘ └───────┘
6.4 USART代码实现
初始化代码
void USART1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
USART_InitTypeDef USART_InitStruct;
// 1. 使能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
// 2. GPIO配置
// PA9=TX(复用推挽输出)
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
// PA10=RX(浮空输入)
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStruct);
// 3. USART参数配置
USART_InitStruct.USART_BaudRate = 115200;
USART_InitStruct.USART_WordLength = USART_WordLength_8b; // 8位数据
USART_InitStruct.USART_StopBits = USART_StopBits_1; // 1位停止
USART_InitStruct.USART_Parity = USART_Parity_No; // 无校验
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // 全双工
USART_Init(USART1, &USART_InitStruct);
// 4. 使能USART
USART_Cmd(USART1, ENABLE);
}
数据发送
// 发送一个字节
void USART_SendByte(uint8_t byte)
{
USART_SendData(USART1, byte);
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); // 等待发送完成
}
// 发送字符串
void USART_SendString(char *str)
{
while (*str)
{
USART_SendByte(*str++);
}
}
// 发送数组
void USART_SendArray(uint8_t *arr, uint16_t len)
{
for (uint16_t i = 0; i < len; i++)
{
USART_SendByte(arr[i]);
}
}
中断接收
#define RX_BUFFER_SIZE 256
volatile uint8_t rx_buffer[RX_BUFFER_SIZE];
volatile uint16_t rx_write_index = 0;
volatile uint16_t rx_read_index = 0;
void USART1_IRQHandler(void)
{
uint8_t data;
// 接收中断
if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
data = USART_ReceiveData(USART1);
// 存入环形缓冲区
uint16_t next_index = (rx_write_index + 1) % RX_BUFFER_SIZE;
if (next_index != rx_read_index) // 缓冲区未满
{
rx_buffer[rx_write_index] = data;
rx_write_index = next_index;
}
// 清除中断标志(读DR自动清除)
}
// 处理其他中断(如溢出错误)
if (USART_GetITStatus(USART1, USART_IT_ORE) != RESET)
{
USART_ReceiveData(USART1); // 清除溢出错误
}
}
// 读取一个字节(非阻塞)
int16_t USART_ReadByte(void)
{
if (rx_read_index != rx_write_index)
{
uint8_t data = rx_buffer[rx_read_index];
rx_read_index = (rx_read_index + 1) % RX_BUFFER_SIZE;
return data;
}
return -1; // 无数据
}
DMA收发
// DMA发送配置
void USART_DMA_Tx_Init(void)
{
DMA_InitTypeDef DMA_InitStruct;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_InitStruct.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)tx_buffer;
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralDST; // 内存到外设
DMA_InitStruct.DMA_BufferSize = 0; // 动态设置
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStruct.DMA_Mode = DMA_Mode_Normal;
DMA_InitStruct.DMA_Priority = DMA_Priority_Medium;
DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel4, &DMA_InitStruct);
USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
}
// DMA发送数据
void USART_DMA_Send(uint8_t *data, uint16_t len)
{
memcpy(tx_buffer, data, len);
DMA_Cmd(DMA1_Channel4, DISABLE);
DMA_SetCurrDataCounter(DMA1_Channel4, len);
DMA_Cmd(DMA1_Channel4, ENABLE);
}
第六章 复试问答
-
并行通信和串行通信有什么区别?各有什么优缺点?为什么嵌入式系统中串行通信更常用?
-
同步通信和异步通信有什么区别?STM32的USART属于哪种?画出异步串行通信的数据帧格式,并说明各部分的作用。
-
波特率如何计算?如果系统时钟为72MHz,配置USART为115200波特率,BRR寄存器应设置为什么值?
-
如果通信双方的波特率设置不匹配会有什么后果?为什么波特率误差要控制在1%以内?
-
RS232、RS422、RS485有什么区别?各适用于什么场景?为什么RS485可以传输更远的距离?
-
什么是RS485的终端电阻?它的作用是什么?为什么通常取120Ω?如果不接终端电阻会有什么后果?
-
编写USART初始化的代码框架,并说明每一步的作用。
-
使用中断方式接收串口数据相比轮询方式有什么优点?如何处理串口接收缓冲区溢出的问题?
-
什么是串口流控(RTS/CTS)?它有什么作用?什么情况下需要使用流控?
第七章 DMA与模数转换器(ADC)
7.1 DMA原理
DMA定义
DMA (Direct Memory Access):直接存储器访问,外设与内存之间直接传输数据,无需CPU参与。
DMA优点
- 不占用CPU资源
- 传输效率高
- 适合高速、大数据量传输
STM32 DMA特性
| DMA控制器 | 通道数 |
|---|---|
| DMA1 | 7通道 |
| DMA2 | 5通道(大容量产品) |
7.2 ADC原理
ADC主要特性
| 特性 | STM32F103 |
|---|---|
| 分辨率 | 12位 |
| 通道数 | 16外部 + 2内部 |
| 转换时间 | 1μs(14周期@14MHz) |
转换时间计算
公式:转换时间 = 采样时间 + 12.5个ADC周期
| 采样时间 | 转换时间 |
|---|---|
| 1.5周期 | 1μs |
| 13.5周期 | 1.07μs(推荐) |
| 28.5周期 | 2.21μs |
ADC通道类型
| 类型 | 说明 |
|---|---|
| 规则通道 | 常规转换,最多16个 |
| 注入通道 | 紧急插入转换,最多4个 |
7.3 ADC+DMA配合
工作流程:
- 配置ADC和DMA
- ADC转换完成触发DMA请求
- DMA将数据从ADC_DR传输到内存
- 传输完成产生中断
第七章 复试问答
-
DMA传输相比CPU直接访问有什么优点?
-
ADC的分辨率是什么意思?STM32的ADC是多少位?
-
什么是ADC的注入通道和规则通道?它们有什么区别?
-
为什么ADC采集常常配合DMA使用?描述完整的工作流程。
-
ADC的转换时间由哪些因素决定?
第八章 CAN总线设计
8.1 CAN总线概述
CAN特点
- 多主结构
- 非破坏性仲裁
- 高可靠性(CRC校验、错误检测)
- 高速传输(最高1Mbps)
- 远距离(10km@5kbps)
- 差分信号,抗干扰强
8.2 CAN物理层
差分信号
| 状态 | CAN_H | CAN_L | 差分电压 | 逻辑 |
|---|---|---|---|---|
| 显性 | 3.5V | 1.5V | 2V | 0 |
| 隐性 | 2.5V | 2.5V | 0V | 1 |
线与特性:显性位(0)可覆盖隐性位(1)
终端电阻
作用:消除信号反射、提供直流偏置
阻值:120Ω(总线两端各一个)
8.3 CAN协议
数据帧格式(标准帧)
[SOF][ID(11位)][RTR][IDE][DLC][DATA(0-8B)][CRC][ACK][EOF]
非破坏性仲裁
- 多个节点同时发送时,通过ID逐位仲裁
- ID越小,优先级越高
- 仲裁失败的节点自动退出,不破坏总线数据
8.4 STM32 CAN配置
波特率计算
波特率=fCAN(BRP+1)×(TS1+TS2+1)波特率 = \frac{f_{CAN}}{(BRP + 1) \times (TS1 + TS2 + 1)}波特率=(BRP+1)×(TS1+TS2+1)fCAN
过滤器
作用:过滤不关心的报文
模式:
- 标识符列表模式:精确匹配
- 掩码模式:按位过滤
第八章 复试问答
-
CAN总线相比UART有什么优点?
-
什么是CAN的非破坏性仲裁?仲裁是如何进行的?
-
CAN总线为什么需要终端电阻?
-
如何配置CAN过滤器?标识符列表模式和掩码模式有什么区别?
-
CAN的显性位和隐性位有什么区别?为什么显性位优先级高?
复试注意事项
面试技巧
- 基础知识扎实:确保核心概念理解准确
- 代码能力:能够写出基本的配置代码
- 项目经验:结合实际项目回答问题
- 仪表态度:自信、谦虚、有礼貌
重点知识清单
| 优先级 | 知识点 | 重要性 |
|---|---|---|
| ★★★ | 补码、原反码、大小端 | 高频考点 |
| ★★★ | 中断系统、NVIC、优先级 | 高频考点 |
| ★★★ | GPIO工作模式 | 高频考点 |
| ★★★ | 定时器原理、PWM计算 | 高频考点 |
| ★★★ | 串口通信、波特率 | 高频考点 |
| ★★☆ | DMA原理 | 中频考点 |
| ★★☆ | ADC配置 | 中频考点 |
| ★★☆ | CAN总线 | 中频考点 |
祝您复试顺利,成功上岸!
&spm=1001.2101.3001.5002&articleId=159473013&d=1&t=3&u=0bc9d404bf634fb09840b7efeebdb866)
1731

被折叠的 条评论
为什么被折叠?



