嵌入式系统复试复习资料(最终版)

嵌入式系统原理及应用(STM32版)

研究生复试复习资料(最终版)

说明:本资料严格依据大学课程教材和ST官方参考手册编写,所有技术细节均经过验证,确保准确无误。


考试大纲概览

本课程涵盖8个核心章节,重点考察STM32嵌入式系统的理论基础和实际应用能力。

章节主要内容重点程度
第一章计算机中数的表示★★★
第二章嵌入式系统概念与ARM Cortex-M3特性★★★
第三章STM32开发环境搭建与使用★★★
第四章STM32中断系统原理及应用★★★
第五章STM32定时器原理与应用★★★
第六章STM32串口通信★★★
第七章DMA与模数转换器(ADC)★★☆
第八章CAN总线设计★★☆

第一章 计算机中数的表示

1.1 进位计数制

基本概念

进位计数制的三要素

  1. 基数(Radix):每个数位所能使用的数码个数
  2. 位权(Weight):每位数值乘以对应的权值(基数的幂次)
  3. 进位规则:逢基数进位

常用进制对照表

进制基数数码位权标识符示例C语言表示
二进制20,12ⁿB或下标21011₂0b1011(部分编译器)
八进制80-78ⁿO或下标817O017(以0开头)
十进制100-910ⁿD或下标1015₁₀15
十六进制160-9,A-F16ⁿH或下标160FH0x0F(以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=mn1ai×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×21+0×22+1×23=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 ~ 2550 ~ 65535
原码-127 ~ +127-32767 ~ +32767有+0和-0
反码-127 ~ +127-32767 ~ +32767有+0和-0
补码-128 ~ +127-32768 ~ +327670唯一,多表示一个数

补码求法(必须掌握)

方法一:定义法

正数:直接转为二进制,最高位为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

为什么使用补码?

优点分析

  1. 零的唯一表示

    • 原码:+0=00000000,-0=10000000
    • 补码:0=00000000(唯一)
  2. 减法转换为加法

    例: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码
0000050101
1000160110
2001070111
3001181000
4010091001

注意: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 ~ 570x30 ~ 0x39
‘A’ ~ ‘Z’65 ~ 900x41 ~ 0x5A
‘a’ ~ ‘z’97 ~ 1220x61 ~ 0x7A
空格 ’ ’320x20
换行 ‘\n’ (LF)100x0A
回车 ‘\r’ (CR)130x0D
退格 ‘\b’80x08
Tab ‘\t’90x09

大小写转换:大写字母 + 32 = 小写字母(‘A’ + 32 = ‘a’)

Unicode与UTF-8

编码位数说明
Unicode16位统一字符编码,65536个字符
UTF-81-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×2E127

特殊值

  • 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

第一章 复试问答

  1. 计算机中为什么采用补码来表示有符号整数?补码相比原码和反码有哪些优点?

  2. 大端模式和小端模式的区别是什么?STM32采用哪种模式?如何用C语言代码判断当前系统的大小端?

  3. 8位无符号数和8位有符号数(补码)的表示范围分别是多少?n位补码的表示范围公式是什么?

  4. 什么是BCD码?它与普通二进制表示有什么区别?举例说明十进制数59的BCD码表示。

  5. 写出字符’0’、‘A’、‘a’、空格、换行的ASCII码值(用十六进制表示)。

  6. 求-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架构演进

架构版本年份处理器系列主要特性
ARMv4T1994ARM7TDMI首次引入Thumb指令集
ARMv5TE1999ARM9增加DSP指令
ARMv62001ARM11引入Thumb-2、SIMD
ARMv7-M2006Cortex-M3/M4微控制器专用
ARMv7-A2005Cortex-A8/A9/A15应用处理器
ARMv8-A2011Cortex-A53/A72/A7664位处理器
ARMv8-M2016Cortex-M33/M55安全扩展

RISC vs CISC

特性RISC(精简指令集)CISC(复杂指令集)
指令长度固定(通常32位)变长(1-15字节)
指令数量少(几十到上百条)多(几百到上千条)
寄存器多(16-32个)少(8-16个)
寻址方式简单(3-5种)复杂(10-20种)
执行周期单周期或固定周期多数为多周期
代码密度较低较高
功耗较高
典型代表ARM、MIPS、RISC-Vx86(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解析指令、读取寄存器确定操作类型和操作数
执行ExecuteALU运算、访存、写回完成指令操作

流水线时序示例

周期    1     2     3     4     5     6
指令1  取指  译码  执行
指令2        取指  译码  执行
指令3              取指  译码  执行
指令4                    取指  译码  执行

流水线带来的问题

  • 分支延迟:分支指令需要清空流水线
  • 数据相关:后续指令依赖前一条结果(可通过转发解决)

Cortex-M3寄存器组

通用寄存器(R0-R12)

  • R0-R7:低寄存器,所有Thumb指令可访问
  • R8-R12:高寄存器,部分Thumb指令可访问

特殊寄存器

寄存器别名功能说明
R13SP(Stack Pointer)堆栈指针PSP(进程堆栈)或MSP(主堆栈)
R14LR(Link Register)链接寄存器保存子程序返回地址
R15PC(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异常类型

异常编号类型优先级说明
1Reset-3(最高)复位
2NMI-2不可屏蔽中断
3Hard Fault-1硬件故障
4MemManage可编程存储器管理fault
5Bus Fault可编程总线fault
6Usage Fault可编程用法fault
11SVCall可编程系统服务调用
14PendSV可编程可挂起的系统服务
15SysTick可编程系统节拍定时器
16-255IRQ可编程外部中断请求

异常向量表

  • 存储在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地址空间分配

区域地址范围大小用途
Code0x00000000 ~ 0x1FFFFFFF512MBFlash、ROM、启动代码
SRAM0x20000000 ~ 0x3FFFFFFF512MBSRAM、位带别名区
Peripheral0x40000000 ~ 0x5FFFFFFF512MB外设寄存器、位带别名区
External RAM0x60000000 ~ 0x9FFFFFFF1GB外部存储器(FSMC)
External Device0xA0000000 ~ 0xDFFFFFFF1GB外部设备
System0xE0000000 ~ 0xFFFFFFFF512MB内部外设(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特点典型应用
STM32F0Cortex-M048MHz16-64KB入门级,低成本简单控制
STM32F1Cortex-M372MHz16-1MB主流型,性价比高通用应用
STM32F2Cortex-M3120MHz128-1MB高性能,以太网网络应用
STM32F4Cortex-M4168MHz1-2MB高性能,DSP+FPU数字信号处理
STM32F7Cortex-M7216MHz512KB-2MB超高性能图形界面
STM32H7Cortex-M7480MHz1-2MB旗舰级AI/ML
STM32L0/1/4Cortex-M0+/M3/M432-80MHz64KB-1MB超低功耗电池供电
STM32WBCortex-M4+M0+64MHz1MB无线(BLE)IoT

STM32F103命名规则详解

完整型号:STM32 F 103 R B T 6

字段含义可选值
STM3232位ARM微控制器-
F产品类型F=通用型,L=低功耗,W=无线,H=高性能
103具体系列101=基础型,103=USB+CAN,105=USB+以太网
R引脚数T=36脚,C=48脚,R=64脚,V=100脚,Z=144脚
BFlash容量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资源概览

资源STM32F103C8STM32F103VET6说明
内核Cortex-M3Cortex-M3
主频72MHz72MHz
Flash64KB512KB
SRAM20KB64KB
GPIO3780
定时器
高级TIM1, TIM8TIM1, TIM8带死区、刹车
通用TIM2-4TIM2-54通道
基本TIM6, TIM7TIM6, TIM7无输出
通信接口
USART35
SPI23
I2C22
CAN12
USB11全速
ADC/DAC
ADC2×12位3×12位16通道
DAC2×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~5KBKeil官方、CMSIS兼容BSD
Zephyr~10KBLinux基金会、功能丰富Apache 2.0

RTOS核心概念

概念说明作用
任务(Task)可独立执行的程序单元实现并发
调度器(Scheduler)决定哪个任务运行实现任务切换
信号量(Semaphore)资源计数器任务同步
互斥量(Mutex)二值信号量资源保护
消息队列(Queue)数据缓冲区任务通信
邮箱(Mailbox)固定大小消息快速通信

FreeRTOS特点

  • 轻量级:最小仅需4-9KB ROM
  • 可裁剪:通过配置文件选择功能
  • 可移植:支持35+种处理器架构
  • 调度算法:抢占式、协作式、时间片轮转
  • 内核服务:任务、队列、信号量、软件定时器

第二章 复试问答

  1. 嵌入式系统与通用计算机系统(PC)有什么区别?嵌入式系统有哪些核心特点?

  2. ARM公司采用的是什么商业模式?ARM处理器采用RISC还是CISC架构?RISC相比CISC有哪些优缺点?

  3. Cortex-M3处理器有哪些主要特性?其流水线结构是怎样的?每级流水线完成什么功能?

  4. 解释STM32F103C8T6型号中每个字段的含义。如果要选择一款有CAN接口、64脚、128KB Flash的芯片,型号应该是什么?

  5. 什么是位带操作?它有什么优点?写出SRAM位带区与位带别名区的地址映射公式,并举例说明如何设置GPIOA的第5位。

  6. Cortex-M3的寄存器组中有哪些特殊功能寄存器?SP、LR、PC各有什么作用?

  7. Cortex-M3支持哪些操作模式?Thread模式和Handler模式有什么区别?

  8. μC/OS-II和FreeRTOS各有什么特点?各适合什么应用场景?RTOS中的任务、信号量、互斥量分别有什么作用?

  9. 什么是嵌入式系统的硬实时和软实时?各举一个例子。


第三章 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/V2SWD/JTAG4MHzST官方,性价比高,免费
ST-Link/V3SWD/JTAG8MHz速度更快,支持虚拟串口
J-LinkSWD/JTAG15MHzSEGGER,功能最强
CMSIS-DAPSWD4MHzARM官方,开源

SWD vs JTAG

特性SWDJTAG
信号线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陶瓷电容

时钟电路

时钟源频率精度用途
HSI8MHz±1%(出厂校准)系统启动、备用时钟
HSE4~16MHz取决于晶振(通常±20ppm)主时钟源
LSI40kHz±5%看门狗、RTC备用
LSE32.768kHz高精度(±20ppm)RTC时钟

HSE晶振电路

        ┌─────┬─────┐
   OSC_IN ─┤     │     ├─── OSC_OUT
            │ 8MHz│
            │晶振 │
        └─────┴─────┘
           │     │
        [20pF] [20pF]
           │     │
          GND   GND

注意:STM32F103的HSE输入范围为4~16MHz,常用8MHz

复位电路

复位类型

  1. 上电复位(POR):电源电压达到阈值时自动复位
  2. 手动复位:按键触发NRST引脚
  3. 看门狗复位:WWDG/IWDG超时触发

典型RC复位电路

VDD
 │
[R] 10kΩ
 │
 ├───────────► NRST(STM32)
 │
[RST] 按键
 │
[C] 100nF
 │
GND

复位时序:NRST低电平持续时间需大于100ns

启动模式

BOOT1BOOT0启动模式物理地址用途
X0从Flash启动0x08000000正常程序运行
01从系统存储器启动0x1FFFF000串口下载(ISP)
11从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

时钟配置要点

时钟最大频率来源说明
SYSCLK72MHzHSI/HSE/PLL系统时钟
HCLK72MHzSYSCLK分频AHB总线
PCLK136MHzHCLK分频APB1低速外设
PCLK272MHzHCLK分频APB2高速外设
ADCCLK14MHzPCLK2分频ADC时钟
USBCLK48MHzPLLUSB必须48MHz

Flash等待周期

为什么需要等待周期
Flash的访问速度比CPU慢,当CPU时钟频率超过Flash访问速度时,需要插入等待周期。

STM32F103 Flash访问时间

  • 0等待:0 ~ 24MHz
  • 1等待:24 ~ 48MHz
  • 2等待:48 ~ 72MHz
系统时钟Flash等待周期设置值
0 ~ 24MHz0 WSFLASH_Latency_0
24 ~ 48MHz1 WSFLASH_Latency_1
48 ~ 72MHz2 WSFLASH_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连接ADCTTL缓冲器断开模拟信号采集
推挽输出00P-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高速~5nsSPI、FSMC、高速信号

注意:速度越高,功耗越大,电磁辐射越强

GPIO寄存器详解

寄存器地址偏移功能说明
CRL0x00端口配置低寄存器配置Pin0~Pin7
CRH0x04端口配置高寄存器配置Pin8~Pin15
IDR0x08端口输入数据寄存器只读,读取引脚状态
ODR0x0C端口输出数据寄存器读写,设置输出电平
BSRR0x10端口位设置/清除寄存器原子操作
BRR0x14端口位清除寄存器清除ODR位
LCKR0x18端口配置锁存寄存器锁定配置

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_TXPA9PB6GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE)
USART1_RXPA10PB7
USART2_TXPA2PD5GPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE)
TIM3_CH1PA6PB4GPIO_PinRemapConfig(GPIO_FullRemap_TIM3, ENABLE)
SPI1PA5/6/7PB3/4/5GPIO_PinRemapConfig(GPIO_Remap_SPI1, ENABLE)

第三章 复试问答

  1. 嵌入式系统开发与通用PC软件开发有什么不同?什么是交叉开发模式?

  2. STM32最小系统由哪几部分组成?各部分的作用是什么?画出最小系统的原理框图。

  3. STM32有哪些时钟源?PLL的作用是什么?如果系统时钟配置为72MHz,HSE使用8MHz晶振,PLL倍频系数应该是多少?

  4. GPIO有哪些工作模式?推挽输出和开漏输出有什么区别?各自适用于什么场景?画出开漏输出的等效电路图。

  5. 什么是复用功能重映射?举例说明其应用场景。如何配置USART1重映射到PB6/PB7?

  6. 使用BSRR寄存器操作GPIO相比直接操作ODR有什么优点?什么情况下必须使用BSRR?

  7. STM32的启动模式有哪几种?串口下载使用的是哪种启动模式?BOOT0和BOOT1引脚如何配置?

  8. SWD和JTAG接口有什么区别?各需要多少根信号线?为什么推荐日常使用SWD?

  9. 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 00位4位1级(0)16级(0-15)NVIC_PriorityGroup_0
Group 11位3位2级(0-1)8级(0-7)NVIC_PriorityGroup_1
Group 22位2位4级(0-3)4级(0-3)NVIC_PriorityGroup_2
Group 33位1位8级(0-7)2级(0-1)NVIC_PriorityGroup_3
Group 44位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引脚说明
EXTI0PA0, PB0, PC0, PD0, PE0, PF0, PG0通过AFIO_EXTICR1配置
EXTI1PA1, PB1, PC1, …通过AFIO_EXTICR1配置
EXTI15PA15, 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);
    }
}

常用外设中断向量表

外设中断向量说明
外部中断
EXTI0EXTI0_IRQn线0中断
EXTI1EXTI1_IRQn线1中断
EXTI2EXTI2_IRQn线2中断
EXTI3EXTI3_IRQn线3中断
EXTI4EXTI4_IRQn线4中断
EXTI5-9EXTI9_5_IRQn线5~9共享
EXTI10-15EXTI15_10_IRQn线10~15共享
定时器
TIM2TIM2_IRQnTIM2全局中断
TIM3TIM3_IRQnTIM3全局中断
TIM4TIM4_IRQnTIM4全局中断
串口
USART1USART1_IRQnUSART1全局中断
USART2USART2_IRQnUSART2全局中断
USART3USART3_IRQnUSART3全局中断
DMA
DMA1通道1DMA1_Channel1_IRQnDMA1通道1中断
DMA1通道2DMA1_Channel2_IRQnDMA1通道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);
    }
}

第四章 复试问答

  1. 什么是中断?中断与轮询相比有什么优点?什么情况下应该使用中断?

  2. Cortex-M3的中断处理流程是怎样的?哪些操作是硬件自动完成的?中断延迟是多少个时钟周期?

  3. 什么是NVIC?STM32的NVIC支持多少个外部中断?优先级是如何配置的?

  4. 解释中断优先级分组的概念。NVIC_PriorityGroup_2的含义是什么?抢占优先级和子优先级有什么区别?

  5. EXTI0~EXTI15分别可以连接到哪些GPIO引脚?同一时刻,一条EXTI线可以映射到多个GPIO引脚吗?

  6. 配置STM32外部中断的完整步骤是什么?写出代码框架并说明每一步的作用。

  7. 什么是按键抖动?为什么要进行消抖?有哪些消抖方法?各自的优缺点是什么?

  8. 中断和事件有什么区别?各适用于什么场景?

  9. 什么是中断嵌套?什么情况下会发生中断嵌套?

  10. 在Cortex-M3中,如何计算从中断请求到执行ISR第一条指令的延迟?


第五章 STM32定时器原理与应用

5.1 定时器概述

STM32定时器分类

定时器类型编号数量位数主要特性
高级定时器TIM1, TIM8216位3通道PWM互补输出、死区控制、刹车输入、重复计数器
通用定时器TIM2~TIM5416位4通道、输入捕获、输出比较、PWM、编码器接口
基本定时器TIM6, TIM7216位无输出引脚、仅定时、DAC触发
看门狗IWDG, WWDG2-系统监控、复位功能
SysTickSysTick124位系统节拍、RTOS心跳、延时函数

定时器特性详细对比

特性高级定时器
(TIM1/8)
通用定时器
(TIM2-5)
基本定时器
(TIM6/7)
计数模式向上/向下/中央对齐向上/向下/中央对齐仅向上
通道数4(带互补输出)40
互补输出支持(带死区)不支持不支持
死区控制支持不支持不支持
刹车输入支持不支持不支持
重复计数器支持不支持不支持
触发输出支持支持支持(仅DAC)
编码器接口不支持支持不支持

5.2 定时器工作原理

定时器时钟源

时钟源说明频率
内部时钟(CK_INT)APB1/APB2总线时钟72MHz/36MHz
外部时钟模式1TI1/TI2引脚输入外部信号
外部时钟模式2ETR引脚输入外部信号
内部触发(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模式1OCxM=110输出有效电平输出无效电平
PWM模式2OCxM=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占空比

工作原理

  1. 配置捕获边沿(上升沿/下降沿/双边沿)
  2. 发生捕获时,CNT当前值锁存到CCR
  3. 计算两次捕获值之差,得到时间

测量频率的方法

方法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)
/40.1ms409.6ms
/80.2ms819.2ms
/160.4ms1.638s
/320.8ms3.276s
/641.6ms6.553s
/1283.2ms13.1s
/2566.4ms26.2s

窗口看门狗(WWDG)

特点

  • 使用APB1时钟
  • 有窗口限制(必须在指定窗口内喂狗)
  • 提前喂狗也会复位

应用场景:需要精确时序监控的场合


第五章 复试问答

  1. STM32有哪几类定时器?它们各有什么特点?高级定时器和通用定时器有什么区别?

  2. 定时器的PWM频率如何计算?如果要产生1kHz的PWM,占空比可调范围为0-100%,PSC和ARR应如何设置?

  3. 什么是死区时间?为什么在电机控制中需要死区控制?死区时间过长或过短会有什么影响?

  4. 输入捕获模式有什么应用?简述使用输入捕获测量信号频率的原理。

  5. 通用定时器的编码器接口模式有什么作用?适合什么应用场景?

  6. 独立看门狗和窗口看门狗有什么区别?各适用于什么场景?

  7. SysTick定时器有什么特点?它在RTOS中有什么作用?

  8. 定时器的中央对齐模式和其他模式有什么区别?什么情况下需要使用中央对齐模式?


第六章 STM32串口通信

6.1 通信基础

并行通信 vs 串行通信

特性并行通信串行通信
数据线多根(8/16/32位)1根(半双工)或2根(全双工)
传输速度快(同时传输多位)相对慢(逐位传输)
传输距离短(走线长度差异影响)长(差分传输可达km级)
成本高(线材、接口多)低(线材少)
抗干扰差(多根线易受干扰)好(可采用差分传输)
应用场景芯片内部总线、短距离设备间通信、远距离

同步通信 vs 异步通信

特性同步通信异步通信
时钟需要共享时钟线不需要,双方约定波特率
数据格式连续数据流帧格式(起始位+数据+停止位)
效率高(无额外开销位)较低(有起始/停止位开销)
复杂度较高(需时钟同步)较低
典型应用SPI、I2C、同步UARTUART、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个)
  • 差分信号,抗干扰强
  • 适合工业环境

三种标准对比

特性RS232RS422RS485
通信方式全双工全双工半双工
拓扑结构点对点点对多点总线型
驱动器数1132
接收器数11032
最大距离15m1200m1200m
最大速率20kbps10Mbps10Mbps
抗干扰最好
应用场景PC调试工业控制工业网络

终端电阻

作用

  1. 消除信号反射(阻抗匹配)
  2. 提供直流偏置
  3. 确保总线空闲时处于确定电平

阻值: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);
}

第六章 复试问答

  1. 并行通信和串行通信有什么区别?各有什么优缺点?为什么嵌入式系统中串行通信更常用?

  2. 同步通信和异步通信有什么区别?STM32的USART属于哪种?画出异步串行通信的数据帧格式,并说明各部分的作用。

  3. 波特率如何计算?如果系统时钟为72MHz,配置USART为115200波特率,BRR寄存器应设置为什么值?

  4. 如果通信双方的波特率设置不匹配会有什么后果?为什么波特率误差要控制在1%以内?

  5. RS232、RS422、RS485有什么区别?各适用于什么场景?为什么RS485可以传输更远的距离?

  6. 什么是RS485的终端电阻?它的作用是什么?为什么通常取120Ω?如果不接终端电阻会有什么后果?

  7. 编写USART初始化的代码框架,并说明每一步的作用。

  8. 使用中断方式接收串口数据相比轮询方式有什么优点?如何处理串口接收缓冲区溢出的问题?

  9. 什么是串口流控(RTS/CTS)?它有什么作用?什么情况下需要使用流控?


第七章 DMA与模数转换器(ADC)

7.1 DMA原理

DMA定义

DMA (Direct Memory Access):直接存储器访问,外设与内存之间直接传输数据,无需CPU参与。

DMA优点

  • 不占用CPU资源
  • 传输效率高
  • 适合高速、大数据量传输

STM32 DMA特性

DMA控制器通道数
DMA17通道
DMA25通道(大容量产品)

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配合

工作流程

  1. 配置ADC和DMA
  2. ADC转换完成触发DMA请求
  3. DMA将数据从ADC_DR传输到内存
  4. 传输完成产生中断

第七章 复试问答

  1. DMA传输相比CPU直接访问有什么优点?

  2. ADC的分辨率是什么意思?STM32的ADC是多少位?

  3. 什么是ADC的注入通道和规则通道?它们有什么区别?

  4. 为什么ADC采集常常配合DMA使用?描述完整的工作流程。

  5. ADC的转换时间由哪些因素决定?


第八章 CAN总线设计

8.1 CAN总线概述

CAN特点

  • 多主结构
  • 非破坏性仲裁
  • 高可靠性(CRC校验、错误检测)
  • 高速传输(最高1Mbps)
  • 远距离(10km@5kbps)
  • 差分信号,抗干扰强

8.2 CAN物理层

差分信号

状态CAN_HCAN_L差分电压逻辑
显性3.5V1.5V2V0
隐性2.5V2.5V0V1

线与特性:显性位(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

过滤器

作用:过滤不关心的报文
模式

  • 标识符列表模式:精确匹配
  • 掩码模式:按位过滤

第八章 复试问答

  1. CAN总线相比UART有什么优点?

  2. 什么是CAN的非破坏性仲裁?仲裁是如何进行的?

  3. CAN总线为什么需要终端电阻?

  4. 如何配置CAN过滤器?标识符列表模式和掩码模式有什么区别?

  5. CAN的显性位和隐性位有什么区别?为什么显性位优先级高?


复试注意事项

面试技巧

  1. 基础知识扎实:确保核心概念理解准确
  2. 代码能力:能够写出基本的配置代码
  3. 项目经验:结合实际项目回答问题
  4. 仪表态度:自信、谦虚、有礼貌

重点知识清单

优先级知识点重要性
★★★补码、原反码、大小端高频考点
★★★中断系统、NVIC、优先级高频考点
★★★GPIO工作模式高频考点
★★★定时器原理、PWM计算高频考点
★★★串口通信、波特率高频考点
★★☆DMA原理中频考点
★★☆ADC配置中频考点
★★☆CAN总线中频考点

祝您复试顺利,成功上岸!

内容概要:本文详细介绍了一种基于六维超混沌系统和DNA编码的彩色数字图像加密与解密方法,并系统分析了其抗噪声和抗裁剪性能,所有算法均通过Matlab代码实现。该方案充分利用六维超混沌系统对初值的高度敏感性和伪随机特性,结合DNA序列的生物特性和编码规则,设计了一套完整的图像加密流程,包括像素置乱、扩散变换以及DNA层级的加解密操作,从而显著提升了图像数据的安全性与保密性。文中还通过多种攻击测试(如高斯噪声、椒盐噪声和局部裁剪)验证了算法的鲁棒性,结果表明该加密机制在复杂攻击环境下仍能有效恢复原始图像,具备良好的实用价值与工程应用潜力。; 适合人群:具备Matlab编程基础,从事信息安全、图像处理或密码学相关研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①为数字图像在军事通信、医疗影像传输、金融信息安全等高敏感领域提供高强度加密保护方案;②研究混沌系统与生物编码相结合的新型图像加密机制的设计原理与实现路径;③评估加密算法在实际信道中面对噪声干扰与数据丢失时的恢复能力,优化其抗攻击性能。; 阅读建议:此资源以Matlab代码为核心载体,理论与实践紧密结合,建议读者在学习过程中动手运行并调试代码,深入理解混沌映射、DNA编码/解码规则及图像置乱扩散机制的实现细节,同时可通过修改参数或攻击类型进行扩展实验,全面提升对现代图像加密技术的认知与创新能力。
内容概要:本文提出了一种融合多尺度时序卷积网络(MS-TCN)与TiDE稠密编码器的深度学习模型,用于实现长周期电力负荷的直接多步预测。该模型通过多尺度卷积结构有效捕捉电力负荷序列在不同时间粒度下的局部模式与周期性特征,同时借助TiDE的编码-解码架构对全局时序依赖关系进行高效建模,从而提升中长期负荷预测的精度与鲁棒性。研究系统阐述了模型的整体架构设计、数据预处理流程、训练优化策略及实验验证过程,结果表明,该方法在多个真实电力负荷数据集上均显著优于传统的ARIMA、LSTM等基准模型以及单一结构的深度学习模型,具备良好的工程应用前景。此外,文中配套提供了完整的Python代码实现,便于读者复现与拓展。; 适合人群:具备一定深度学习基础和时间序列分析经验,从事电力系统规划、能源管理、智能电网等相关领域的科研人员、工程技术人员及研究生。; 使用场景及目标:①应用于电网企业开展中长期电力负荷预测,辅助制定发电计划、检修安排与调度策略;②为综合能源系统优化、电力市场竞价与需求响应等业务提供高精度的负荷数据支撑;③作为深度学习在能源预测领域的典型应用案例,促进人工智能技术在新型电力系统中的深度融合与落地实践。; 阅读建议:建议读者结合所提供的Python代码,动手复现模型并进行调试,深入理解多尺度卷积与TiDE结构的设计理念与协同机制,同时可在不同地区、不同季节的负荷数据集上开展迁移实验,以全面评估模型的泛化能力与适应性。
源码链接: https://pan.quark.cn/s/a4b39357ea24 ### C语言核心概念 #### 1. C语言简介 C语言是一种应用广泛的计算机编程语言,由Dennis Ritchie在1969年至1973年期间于AT&T的贝尔实验室设计。该语言因其高效性、灵活性及强大的功能,在系统软件、应用软件的开发领域中具有举足轻重的地位。 #### 2. 编程实践与PTA平台 编程实践是借助计算机语言进行问题逻辑分析、算法构建及编码实现的过程。PTA(Programming Teaching Assistant)是一个用于辅助编程教学实验的平台,学生能够通过该平台进行在线编程实践。 #### 3. 数据输入与输出操作 在C语言中,`scanf`和`printf`是常用的标准输入输出函数,分别用于从标准输入(通常为键盘)获取格式化的输入数据及向标准输出(通常为屏幕)显示格式化的输出数据。 ```c #include <stdio.h> int main() { int a, b; scanf("%d %d", &a, &b); // 从标准输入读取两个整数值 printf("%d", a + b); // 输出两个整数的和 return 0; } ``` #### 4. 数据类别与变量 C语言中的基本数据类别包含整型(int)、字符型(char)、浮点型(float, double)等。变量作为存储数据的载体,在使用前必须明确声明其数据类型。 ```c char a; int b; ``` #### 5. 字符数据输入输出操作 `getchar()`函数用于从标准输入获取下一个可用的字符,而`putchar()`函数则用于将字符输出到标准输出。 ``...
源码下载地址: https://pan.quark.cn/s/a4b39357ea24 《Eclipse Java 2022-03-R_win32-x86_64: 深入解析与应用》 Eclipse是一个功能卓越的开源集成开发环境(IDE),因其高效性、灵活性以及高度可扩展性而广受程序员的青睐。"eclipse-java-2022-03-R-win32-x86_64.zip" 是Eclipse为Java开发者精心打造的最新版本,专门为Windows x86_64架构进行了优化。该压缩文件内含运行Eclipse IDE所需的所有必要组件,使开发者在Windows平台上进行Java开发变得更为便捷。 1. **Eclipse IDE概述**:Eclipse自2001年起由IBM推出,最初作为一个Java集成开发环境,随后发展为一个通用的开发平台,支持多种编程语言和开发工具。其基于插件的架构允许用户依据需求进行功能的添加或移除,使其成为一款高度可定制的开发工具。 2. **Java开发支持**:Eclipse为Java开发者提供了全面的开发工具集,包括代码编辑器、调试器、构建工具、项目管理器等。其自动完成功能(Content Assist)和错误检测能显著提升开发效率,而强大的调试工具则能帮助开发者迅速定位和修正问题。 3. **R语言集成**:尽管Eclipse主要围绕Java展开,但通过安装特定的R语言插件(如Eclipse for R),开发者也能在同一个环境中编写和执行R代码,进行数据分析和统计建模。 4. **IDE概念**:集成开发环境(IDE)是一种融合了代码编辑、编译、调试和版本控制等多种功能的软件,它简化了开发流程,使开发者能够集中精力在代码编写上,而...
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值