vault backup: 2026-06-10 11:17:10

This commit is contained in:
2026-06-10 11:17:10 +08:00
parent 89e183aaaa
commit 788576ec4b
15 changed files with 3124 additions and 0 deletions
@@ -0,0 +1,201 @@
---
tags: [嵌入式系统, 复习, ARM架构]
create time: 2026-06-10 11:11
---
# ARM处理器架构
## 概述
本文档深入讲解ARM处理器的架构设计,包括命名规则、存储体系结构、流水线、寄存器组织、CPSR位域、工作模式以及FIQ/IRQ中断机制。ARM架构是嵌入式系统考试的重点内容。
## 正文
### 1. ARM概述
ARM(Advanced RISC Machines)是一系列**处理器核心**的名称,不是一家公司的名字。ARM公司采用**IP授权**模式——设计处理器核心,将设计方案授权给芯片厂商(如高通、三星、ST)进行制造。
ARM的核心特点:
- **RISC架构**(精简指令集计算机):指令数量少,指令长度固定,执行效率高
- **低功耗**:适合移动设备和嵌入式场景
- **IP授权模式**:ARM自己不生产芯片
### 2. ARM7TDMI命名规则
以经典的ARM7TDMI为例,其后缀含义如下:
| 后缀 | 全称 | 含义 |
|------|------|------|
| T | Thumb | 支持Thumb指令集(16位) |
| D | Debug | 支持JTAG调试 |
| M | Multiplier | 增强型乘法器 |
| I | ICE | 支持嵌入式ICE(In-Circuit Emulator) |
### 3. 冯·诺依曼 vs 哈佛架构
这是嵌入式系统的经典考点:
**冯·诺依曼架构**(ARM7采用):
- 指令和数据**共享**同一条总线和同一存储空间
- 不能同时取指令和读写数据
- 结构简单,成本低
**哈佛架构**(ARM9及以上采用):
- 指令和数据使用**独立的**总线和存储空间
- 可以同时取指令和读写数据
- 性能更高,但结构更复杂
> [!question] ARM7为什么选择冯·诺依曼架构?
> ARM7面向低成本嵌入式应用,冯·诺依曼架构只需一组总线,可以减少芯片面积和引脚数量,降低功耗和成本。对于ARM7的应用场景,性能已经足够。
### 4. ARM7三级流水线
ARM7处理器使用三级流水线:
| 阶段 | 名称 | 操作 |
|------|------|------|
| 1 | Fetch(取指) | 从存储器取出指令 |
| 2 | Decode(译码) | 解析指令含义,读取寄存器 |
| 3 | Execute(执行) | 执行运算,写回结果 |
在理想情况下,三级流水线使得每个时钟周期都能完成一条指令(吞吐率为1 CPI)。但当发生分支跳转时,流水线需要清空并重新填充,导致性能损失。
> [!tip] 流水线级数与PC值的关系
> 在ARM7三级流水线中,当前执行的指令地址是PC-8(因为PC已经预取了两条指令之后的内容)。这个细节在调试时很重要。
### 5. 寄存器组织
ARM7共有**37个**32位寄存器,但在任意时刻只能访问其中一部分:
- **User/SYS模式**:可访问**17个**(R0-R15 + CPSR)
- **其他特权模式**:可访问各自的SPSR,以及部分私有寄存器
#### 特殊功能寄存器
| 寄存器 | 名称 | 功能 |
|--------|------|------|
| R13 | SP(Stack Pointer) | 栈指针,指向当前栈顶 |
| R14 | LR(Link Register) | 链接寄存器,保存子程序返回地址 |
| R15 | PC(Program Counter) | 程序计数器,指向下一条要取的指令 |
| R12 | IP(Intra-Procedure-call) | 过程调用中间暂存寄存器 |
> [!question] 为什么每个异常模式都有自己独立的R13(SP)?
> 因为每个模式有自己的运行栈。如果所有模式共享一个SP,当中断发生时可能破坏正在使用的栈数据,导致系统崩溃。
### 6. CPSR位域详解
**CPSR**(Current Program Status Register,当前程序状态寄存器)是ARM中最关键的寄存器之一:
```mermaid
graph LR
subgraph CPSR["CPSR Bit Fields"]
direction LR
N["N[31]"] --> Z["Z[30]"] --> C["C[29]"] --> V["V[28]"]
V --> D["D[27]"] --> I["I[7]"] --> F["F[6]"] --> T["T[5]"]
T --> M["M[4:0]"]
end
style N fill:#ef9a9a
style Z fill:#ffcc80
style C fill:#fff176
style V fill:#a5d6a7
style I fill:#90caf9
style F fill:#90caf9
style T fill:#ce93d8
style M fill:#f48fb1
```
**条件标志位(N, Z, C, V)**:
| 标志位 | 位号 | 含义 |
|--------|------|------|
| N | [31] | **负数**标志:运算结果最高位为1时置位 |
| Z | [30] | **零**标志:运算结果为0时置位 |
| C | [29] | **进位**标志:加法产生进位或减法无借位时置位 |
| V | [28] | **溢出**标志:有符号运算溢出时置位 |
**控制位**:
| 标志位 | 位号 | 含义 |
|--------|------|------|
| I | [7] | IRQ禁止:置1时禁止IRQ中断 |
| F | [6] | FIQ禁止:置1时禁止FIQ中断 |
| T | [5] | Thumb状态:置1表示Thumb状态,置0表示ARM状态 |
| M[4:0] | [4:0] | 处理器模式选择位 |
> [!question] 如果CPSR的N=1、Z=0,这意味着什么?
> N=1表示上一次运算结果为负数,Z=0表示结果不为零。如果此时用BMI(Branch Minus)指令,分支将被执行,因为BMI条件是N=1。
### 7. SPSR
**SPSR**(Saved Program Status Register)保存的是进入异常模式**之前**的CPSR值。
当异常发生时,硬件自动将当前CPSR保存到对应异常模式的SPSR中。异常返回时,通过 `MOVS PC, LR` 指令将SPSR恢复回CPSR,从而恢复之前的状态。
> [!warning] User模式没有SPSR
> User模式不是异常模式,所以没有SPSR。如果需要保存User模式的状态,必须通过软件方式实现。
### 8. 七种工作模式
ARM处理器有7种工作模式:
```mermaid
graph TD
subgraph nonPriv["Non-Privileged"]
USR["USR - User Mode"]
end
subgraph priv["Privileged Modes"]
SYS["SYS - System Mode"]
SVC["SVC - Supervisor Mode"]
ABT["ABT - Abort Mode"]
UND["UND - Undefined Mode"]
IRQ["IRQ - IRQ Mode"]
FIQ["FIQ - FIQ Mode"]
end
SYS -.-> |"Same registers as USR"| USR
SVC --> |"Reset enters here"| SVC
style USR fill:#bbdefb
style SYS fill:#c8e6c9
style SVC fill:#ffcc80
style ABT fill:#ffab91
style UND fill:#ef9a9a
style IRQ fill:#ce93d8
style FIQ fill:#f48fb1
```
| 模式 | 编码 | 说明 | 进入方式 |
|------|------|------|----------|
| USR | 10000 | 用户模式,非特权 | 正常程序执行 |
| SYS | 11111 | 系统模式,特权 | 与USR共享寄存器 |
| SVC | 10011 | 管理模式 | **复位**、SWI |
| ABT | 10111 | 中止模式 | 存储器访问异常 |
| UND | 11011 | 未定义模式 | 未定义指令异常 |
| IRQ | 10010 | 中断模式 | IRQ中断 |
| FIQ | 10001 | 快速中断模式 | FIQ中断 |
几个关键点:
- **复位后进入SVC模式**,这是系统上电后的初始状态
- **USR不是异常模式**,因此没有SPSR
- **SYS模式**与USR共享寄存器,但具有特权权限
### 9. FIQ vs IRQ
| 比较维度 | FIQ | IRQ |
|----------|-----|-----|
| 优先级 | **更高** | 较低 |
| 私有寄存器 | R8-R14(7个) | R13-R14(2个) |
| 向量表位置 | 0x0000001C(末尾) | 0x00000018 |
| 响应速度 | 更快(寄存器保留多,切换少) | 较慢 |
| 嵌套 | 支持 | 支持 |
FIQ更快的原因:
1. 有更多的**私有寄存器**(R8-R14),中断处理时不需要保存/恢复通用寄存器
2. 向量表位于地址空间末尾,可以直接放置ISR代码,**无需跳转**
> [!question] 为什么FIQ的向量表放在0x0000001C(最后),而不是和其他异常一样在前面?
> 把FIQ的处理程序直接放在向量表位置0x0000001C开始的地方,可以省去一次跳转指令。因为从0x0000001C到中断向量表结束还有空间,足够放几条关键指令。
## 关联笔记
- [[嵌入式系统/试题册/index|试题册索引]]
@@ -0,0 +1,235 @@
---
tags: [嵌入式系统, 复习, ARM异常]
create time: 2026-06-10 11:11
---
# ARM异常与中断机制
## 概述
本文档详细讲解ARM处理器的异常与中断机制,包括7种异常类型、异常向量表、异常响应流程、FIQ/IRQ中断处理以及中断嵌套。这是理解嵌入式系统实时响应能力的关键。
## 正文
### 1. 什么是异常
**异常(Exception)** 是指处理器在执行正常程序过程中,遇到的需要特殊处理的事件。异常是同步事件,由当前执行的指令触发。
**中断(Interrupt)** 是一种特殊的异常,属于异步事件——由外部硬件信号触发,与当前执行的指令无关。
> [!tip] 异常与中断的关系
> 中断是异常的子集。所有中断都是异常,但不是所有异常都是中断。IRQ和FIQ是中断,而Reset、SWI、Data Abort等是其他类型的异常。
### 2. 七种异常类型
ARM7定义了7种异常类型,每种异常对应一种处理器模式:
| 异常类型 | 触发原因 | 进入模式 | 优先级 |
|----------|----------|----------|--------|
| Reset | 复位信号 | SVC | 最高(1) |
| Undefined Instruction | 无法识别的指令 | UND | 最高(2) |
| SWI(软件中断) | SWI指令执行 | SVC | 最高(3) |
| Prefetch Abort | 指令预取失败 | ABT | 最高(4) |
| Data Abort | 数据访问失败 | ABT | 最高(5) |
| IRQ | 外部IRQ中断请求 | IRQ | 较低(6) |
| FIQ | 外部FIQ中断请求 | FIQ | 最低(7) |
> [!question] 为什么Reset的优先级最高?
> Reset是最高优先级异常,因为它代表系统需要从头开始。无论处理器当前在做什么,复位信号都必须立即响应,将系统拉回到初始状态。
### 3. 异常向量表
异常向量表是位于地址 **0x00000000** 的一张跳转表,每种异常对应一个固定的偏移地址:
```mermaid
graph TD
subgraph vectorTable["Exception Vector Table"]
V0["0x00000000: Reset"] --> V4["0x00000004: Undefined Instruction"]
V4 --> V8["0x00000008: SWI"]
V8 --> VC["0x0000000C: Prefetch Abort"]
VC --> V10["0x00000010: Data Abort"]
V10 --> V14["0x00000014: Reserved"]
V14 --> V18["0x00000018: IRQ"]
V18 --> V1C["0x0000001C: FIQ"]
end
style V0 fill:#ef9a9a
style V4 fill:#ffcc80
style V8 fill:#fff176
style VC fill:#a5d6a7
style V10 fill:#90caf9
style V14 fill:#e0e0e0
style V18 fill:#ce93d8
style V1C fill:#f48fb1
```
每个向量表项只占**4个字节**(一条ARM指令的大小),通常放置一条跳转指令(B或LDR PC)跳转到对应的异常处理程序。
> [!warning] 0x00000014是保留位置
> 0x00000014是Reserved,没有定义任何异常。这是因为每种异常间隔4个字节,而从Data Abort到IRQ之间需要一个间隔。
> [!question] 为什么FIQ的向量地址是0x0000001C,排在最后?
> 把FIQ放在向量表末尾有一个巧妙的好处:FIQ的处理程序可以直接从0x0000001C开始写,而不需要一条跳转指令。因为从0x0000001C往后到向量表结束还有空间,足以放置几条关键指令,从而节省了一个跳转周期。
### 4. 异常响应流程
当异常发生时,ARM处理器自动执行以下步骤:
```mermaid
flowchart TD
A["Exception Occurs"] --> B["Step1: Save CPSR to SPSR of target mode"]
B --> C["Step2: Set CPSR mode bits to target mode"]
C --> D["Step3: Set CPSR T bit to 0 ARM state"]
D --> E["Step4: Set CPSR I/F bits disable interrupts"]
E --> F["Step5: Save return address to LR of target mode"]
F --> G["Step6: Set PC to vector address"]
G --> H["Execute exception handler"]
style A fill:#ef9a9a
style H fill:#a5d6a7
```
具体来说:
**Step 1:保存CPSR**
- 将当前CPSR的值复制到**目标异常模式的SPSR**
- 例如:IRQ发生时,CPSR → SPSR_irq
**Step 2:切换处理器模式**
- 修改CPSR的M[4:0]位,切换到异常对应的模式
**Step 3:切换到ARM状态**
- 清除CPSR的T位(T=0),确保在ARM状态下执行异常处理程序
- 这意味着即使当前在Thumb状态,异常处理也使用ARM指令
**Step 4:禁止中断**
- 根据异常类型,设置I位或F位来禁止中断
- IRQ异常设置I位(禁止IRQ),FIQ异常设置I和F位(禁止所有中断)
**Step 5:保存返回地址**
- 将程序计数器的值保存到**目标模式的LR**
- 不同异常的返回地址偏移不同(见下表)
**Step 6:跳转到向量表**
- 将PC设置为对应异常的向量地址
### 5. 返回地址的偏移
每种异常保存到LR中的返回地址有特定的偏移:
| 异常类型 | LR保存的值 | 说明 |
|----------|------------|------|
| Reset | 未定义 | 无需返回 |
| Undefined | PC + 4 | 指向未定义指令的下一条 |
| SWI | PC + 4 | 指向SWI指令的下一条 |
| Prefetch Abort | PC + 4 | 指向预取失败指令的下一条 |
| Data Abort | PC + 8 | 指向数据访问失败指令的下一条 |
| IRQ | PC + 4 | 指向被中断指令的下一条 |
| FIQ | PC + 4 | 指向被中断指令的下一条 |
> [!question] 为什么Data Abort的偏移是+8而其他是+4?
> 因为ARM7三级流水线中,当Data Abort被检测到时,流水线已经多预取了指令。Data Abort在执行阶段才被发现,此时PC已经前进了8个字节(两条指令之后)。
### 6. 异常返回
异常处理完成后,需要返回到被中断的程序。返回方式取决于异常类型:
**从IRQ/FIQ返回**:
```arm
SUBS PC, LR, #4 ; LR_irq/fiq - 4 = 被中断指令的下一条地址
```
**从SWI/Undefined返回**:
```arm
MOVS PC, LR ; LR_svc/und 直接就是返回地址
```
**从Data Abort返回**:
```arm
SUBS PC, LR, #8 ; LR_abt - 8 = 需要重新执行那条失败的指令
```
> [!warning] 关键点:MOVS和SUBS中的"S"后缀
> 返回指令必须使用带S后缀的版本(MOVS/SUBS),因为这会自动将SPSR的值恢复到CPSR。如果用MOV而不是MOVS,SPSR不会被恢复,处理器状态将不正确。
### 7. 中断嵌套
ARM7的中断**支持嵌套**。所谓嵌套,是指在一个中断处理过程中,更高优先级的中断可以打断当前处理。
```mermaid
sequenceDiagram
participant Main as Main Program
participant IRQ as IRQ Handler
participant FIQ as FIQ Handler
Main->>IRQ: IRQ interrupt occurs
Note over IRQ: CPSR saved, IRQ enabled
IRQ->>FIQ: FIQ interrupt occurs
Note over FIQ: CPSR saved, all IRQ disabled
FIQ-->>IRQ: Return from FIQ
IRQ-->>Main: Return from IRQ
```
实现中断嵌套的关键:
1. 进入中断处理程序后,需要**手动重新使能IRQ**(清除CPSR的I位)
2. 保存必要的寄存器到栈上
3. 使用STMFD/LDMFD来管理栈帧
```arm
; IRQ中断处理程序模板
IRQ_Handler:
SUB LR, LR, #4 ; 修正返回地址
STMFD SP!, {LR} ; 保存返回地址
MRS R14, SPSR ; 保存SPSR
STMFD SP!, {R14} ; 将SPSR压栈
STMFD SP!, {R0-R3, R12} ; 保存可能被破坏的寄存器
MSR CPSR_c, #0x13 ; 切换到SVC模式,使能IRQ
STMFD SP!, {LR} ; 保存SVC模式的LR
; ... 在SVC模式下处理中断 ...
LDMFD SP!, {LR} ; 恢复SVC的LR
MSR CPSR_c, #0x92 ; 切回IRQ模式,禁止IRQ
LDMFD SP!, {R0-R3, R12} ; 恢复寄存器
LDMFD SP!, {R14} ; 恢复SPSR到R14
MSR SPSR_cxsf, R14 ; 写回SPSR
LDMFD SP!, {PC}^ ; 恢复PC,同时恢复CPSR
```
### 8. FIQ vs IRQ 对比总结
```mermaid
graph TD
subgraph comparison["FIQ vs IRQ Comparison"]
direction LR
subgraph fiqProps["FIQ Properties"]
FP1["Higher priority"]
FP2["Private R8-R14"]
FP3["Vector at 0x1C"]
FP4["Fastest response"]
end
subgraph irqProps["IRQ Properties"]
IP1["Lower priority"]
IP2["Private R13-R14 only"]
IP3["Vector at 0x18"]
IP4["Needs register save"]
end
end
style FP1 fill:#f48fb1
style IP1 fill:#ce93d8
```
| 特性 | FIQ | IRQ |
|------|-----|-----|
| 优先级 | 高 | 低 |
| 私有寄存器 | R8-R14(7个) | R13-R14(2个) |
| 向量地址 | 0x0000001C | 0x00000018 |
| 响应速度 | 快(无需保存R8-R12) | 慢(需要保存R0-R12) |
| 嵌套 | 支持 | 支持 |
| 典型用途 | 高速数据传输(DMA) | 一般外部设备中断 |
> [!question] 如果系统中只有一个中断源,应该选择FIQ还是IRQ?
> 如果只有一个中断源且对响应速度有要求,建议使用FIQ。因为FIQ有更多私有寄存器,中断处理程序不需要保存/恢复R8-R12,减少了压栈/出栈的时间开销。
## 关联笔记
- [[嵌入式系统/试题册/index|试题册索引]]
@@ -0,0 +1,310 @@
---
tags: [嵌入式系统, 复习, ARM指令集]
create time: 2026-06-10 11:11
---
# ARM指令集与汇编
## 概述
本文档详细讲解ARM指令集体系,包括ARM/Thumb指令集对比、指令格式、核心指令详解、条件码、寻址方式以及汇编程序设计。这是嵌入式系统课程中需要动手实践的重点部分。
## 正文
### 1. ARM指令集 vs Thumb指令集
| 比较维度 | ARM指令集 | Thumb指令集 |
|----------|-----------|-------------|
| 指令宽度 | **32位** | **16位** |
| 功能 | 完整功能 | ARM的子集 |
| 代码密度 | 低(占用空间大) | 高(占用空间小) |
| 性能 | 高 | 略低 |
| 状态标志 | CPSR.T = 0 | CPSR.T = 1 |
> [!warning] Thumb状态不能执行ARM指令
> 在Thumb状态下,处理器只能执行16位的Thumb指令。如果需要执行32位ARM指令,必须先切换到ARM状态(通过BX指令)。两种状态不能在同一时刻混合执行。
### 2. 指令格式
ARM指令的基本格式为:
```
opcode{condition}{S} Rd, operand1, operand2
```
| 字段 | 说明 |
|------|------|
| opcode | 操作码,如MOV, ADD, LDR |
| {condition} | 条件码,如EQ, NE, GT(可选) |
| {S} | 是否影响CPSR标志位(可选) |
| Rd | 目标寄存器 |
| operand1 | 第一操作数(通常是寄存器) |
| operand2 | 第二操作数(灵活,可带移位) |
```mermaid
graph LR
subgraph format["Instruction Format"]
direction LR
OP["opcode"] --> CON["condition"] --> S["S flag"] --> RD["Rd"] --> OP1["operand1"] --> OP2["operand2"]
end
style OP fill:#4fc3f7
style CON fill:#81d4fa
style S fill:#b3e5fc
style RD fill:#ffcc80
style OP1 fill:#ffab91
style OP2 fill:#a5d6a7
```
### 3. 桶形移位器(Barrel Shifter)
ARM指令的一个强大特性:**第二操作数可以在送入ALU之前先进行移位操作**。这意味着一条指令就能完成"移位+运算",无需额外的移位指令。
移位类型:
| 移位操作 | 全称 | 说明 |
|----------|------|------|
| LSL | Logical Shift Left | 逻辑左移,低位补0 |
| LSR | Logical Shift Right | 逻辑右移,高位补0 |
| ASR | Arithmetic Shift Right | 算术右移,高位补符号位 |
| ROR | Rotate Right | 循环右移 |
```arm
; 示例:利用桶形移位器
ADD R0, R1, R1, LSL #2 ; R0 = R1 + R1 * 4 = R1 * 5
SUB R2, R3, R3, LSR #1 ; R2 = R3 - R3/2 = R3/2
MOV R4, R5, ROR #8 ; R4 = R5循环右移8位
```
> [!tip] 桶形移位器的价值
> 在没有桶形移位器的架构中,`R1 * 5` 需要两条指令:先LSL #2再ADD。ARM用一条指令就完成了,这体现了RISC架构中"简单指令的巧妙组合"的设计哲学。
### 4. 核心指令详解
#### 4.1 数据传送指令 MOV
```arm
MOV R0, #10 ; R0 = 10(立即数传送)
MOV R1, R0 ; R1 = R0(寄存器传送)
MOV R2, R0, LSL #3 ; R2 = R0 << 3(带移位传送)
```
**MOV只能在寄存器之间传送数据,不能访问内存!** 这是与LDR的关键区别。
#### 4.2 加载/存储指令 LDR/STR
```arm
LDR R0, [R1] ; 从R1指向的内存地址加载数据到R0
STR R0, [R1] ; 将R0的值存储到R1指向的内存地址
LDR R0, [R1, #4] ; 从R1+4地址加载(基址+偏移)
STR R0, [R1], #4 ; 先存储,再更新R1 = R1 + 4(后索引)
LDR R0, [R1, #4]! ; 先更新R1 = R1 + 4,再加载(前索引)
```
> [!question] MOV R0, #0xFF 和 LDR R0, =0x12345678 有什么区别?
> `MOV R0, #0xFF` 是真正ARM指令,0xFF是合法的8位立即数(通过循环右移编码)。
> `LDR R0, =0x12345678` 是**伪指令**,汇编器会将0x12345678放入文字池(literal pool),然后用一条LDR从该地址加载。因为0x12345678无法编码为8位立即数。
#### 4.3 算术运算指令
```arm
ADD R0, R1, R2 ; R0 = R1 + R2
ADD R0, R1, #5 ; R0 = R1 + 5
ADDS R0, R1, R2 ; R0 = R1 + R2,并更新CPSR标志位
SUB R0, R1, R2 ; R0 = R1 - R2
SUBS R0, R1, #1 ; R0 = R1 - 1,更新标志位
MUL R0, R1, R2 ; R0 = R1 * R2
```
注意带`S`后缀的指令会更新CPSR的N、Z、C、V标志位。
#### 4.4 比较指令 CMP
```arm
CMP R0, R1 ; 比较R0和R1(计算R0-R1,但不保存结果)
BEQ label ; 如果相等(Z=1),跳转到label
BNE label ; 如果不相等(Z=0),跳转到label
```
CMP本质上是一条**不保存结果的减法指令**,只设置标志位。
#### 4.5 分支指令 B / BL
```arm
B label ; 无条件跳转到label
BL function ; 跳转到function,同时将返回地址保存到LR
```
- **B**:简单跳转,用于循环、条件分支
- **BL**:带链接的跳转,用于**函数调用**(LR自动保存返回地址)
#### 4.6 软中断指令 SWI
```arm
SWI #0x123456 ; 触发软中断,进入SVC模式
```
SWI用于**从用户模式请求操作系统服务**(系统调用)。执行SWI后:
1. 进入SVC模式
2. CPSR保存到SPSR_svc
3. 返回地址保存到LR_svc
4. PC跳转到向量地址0x00000008
#### 4.7 批量加载/存储 STMFD / LDMFD
```arm
STMFD SP!, {R0-R3, LR} ; 将R0-R3和LR入栈(满递减栈)
LDMFD SP!, {R0-R3, PC} ; 从栈中恢复R0-R3,并将PC出栈实现返回
```
这两个指令常用于**函数入口保存现场**和**函数出口恢复现场**。FD表示Full Descending(满递减栈),是ARM的默认栈类型。
#### 4.8 状态寄存器读写 MRS / MSR
```arm
MRS R0, CPSR ; 将CPSR读入R0
MSR CPSR_c, R0 ; 将R0写入CPSR的控制位域
MSR CPSR_f, R0 ; 将R0写入CPSR的标志位域
```
MRS/MSR用于在**特权模式下修改CPSR**,例如开关中断:
```arm
; 关中断
MRS R0, CPSR
ORR R0, R0, #0x80 ; I位(bit7)置1
MSR CPSR_c, R0
; 开中断
MRS R0, R1
BIC R0, R0, #0x80 ; I位(bit7)清0
MSR CPSR_c, R0
```
#### 4.9 测试等价指令 TEQ
```arm
TEQ R0, R1 ; 按位异或(EOR),只设置标志位,不保存结果
```
TEQ用于测试两个值是否相等(或测试某些位),与CMP类似但使用异或运算。
### 5. 条件码
ARM支持15种条件码,基于CPSR的标志位进行判断:
| 条件码 | 含义 | 标志位条件 |
|--------|------|------------|
| EQ | 相等 | Z=1 |
| NE | 不相等 | Z=0 |
| GT | 大于(有符号) | Z=0且N=V |
| LT | 小于(有符号) | N!=V |
| GE | 大于等于(有符号) | N=V |
| LE | 小于等于(有符号) | Z=1或N!=V |
| HI | 无符号大于 | C=1且Z=0 |
| LS | 无符号小于等于 | C=0或Z=1 |
| CS/HS | 无符号大于等于 | C=1 |
| CC/LO | 无符号小于 | C=0 |
| PL | 正数或零 | N=0 |
| MI | 负数 | N=1 |
| VS | 溢出 | V=1 |
| VC | 无溢出 | V=0 |
| AL | 无条件(默认) | 任意 |
> [!question] CMP R0, #0 后接 BMI label,什么时候会跳转?
> BMI是"负数"条件跳转。CMP R0, #0 实际上是R0 - 0,结果就是R0本身。如果R0 < 0(最高位为1),N标志位被置位,BMI条件成立,跳转到label。
### 6. 寻址方式
| 寻址方式 | 示例 | 说明 |
|----------|------|------|
| 立即数寻址 | `MOV R0, #10` | 操作数就在指令中 |
| 寄存器寻址 | `ADD R0, R1, R2` | 操作数在寄存器中 |
| 寄存器间接寻址 | `LDR R0, [R1]` | R1中存放的是内存地址 |
| 基址+偏移寻址 | `LDR R0, [R1, #4]` | R1为基地址,4为偏移量 |
| 基址+索引寻址 | `LDR R0, [R1, R2]` | R2的值作为索引偏移 |
### 7. 立即数编码
ARM指令是32位固定的,立即数编码只占用**12位**(4位旋转量 + 8位立即数值)。规则:
```
实际值 = 8位立即数 循环右移 (旋转量 * 2) 位
```
这意味着只有**特定的值**才能作为立即数。例如:
- `#0xFF`:合法,8位全1,旋转0位
- `#0x100`:合法,`#1`循环右移24位
- `#0x12345678`:**不合法**,无法用8位循环右移表示
> [!tip] 立即数合法性判断
> 判断一个数是否是合法的ARM立即数:将它写成二进制,看是否能通过循环右移偶数位变成8位以内的数。
### 8. 字对齐与大小端
**数据宽度**:
| 类型 | 位数 | 字节数 |
|------|------|--------|
| Word(字) | 32位 | 4字节 |
| Halfword(半字) | 16位 | 2字节 |
| Byte(字节) | 8位 | 1字节 |
**对齐要求**:Word访问的地址必须是4的倍数(地址 mod 4 = 0),Halfword访问地址必须是2的倍数。
**大小端**:
- **Little-Endian(小端)**:低字节存低地址(ARM常用)
- **Big-Endian(大端)**:高字节存低地址
```arm
; 假设内存地址0x1000存放值 0x12345678
; 小端模式:
; 0x1000: 0x78 (最低字节)
; 0x1001: 0x56
; 0x1002: 0x34
; 0x1003: 0x12 (最高字节)
; 大端模式:
; 0x1000: 0x12 (最高字节)
; 0x1001: 0x34
; 0x1002: 0x56
; 0x1003: 0x78 (最低字节)
```
### 9. 完整示例:C if-else 对应的ARM汇编
以下是C语言if-else语句及其对应的ARM汇编实现:
```c
// C代码
int max(int a, int b) {
if (a > b)
return a;
else
return b;
}
```
```arm
; ARM汇编实现
max:
CMP R0, R1 ; 比较a和b(R0-R1,设置标志位)
MOVGT R0, R0 ; 如果a > b(GT条件成立),R0保持不变
MOVLE R0, R1 ; 如果a <= b(LE条件成立),R0 = b
BX LR ; 返回,LR中保存了调用者的返回地址
```
更高效的写法:
```arm
; 优化版本:只用两条核心指令
max:
CMP R0, R1 ; 比较a和b
MOVLT R0, R1 ; 如果a < b,则R0 = b
BX LR ; 返回
```
> [!question] 为什么优化版本只判断了LT一种情况?
> 因为如果a >= b,R0的值本来就是a,不需要任何操作。只有当a < b时才需要将b写入R0。这体现了"条件执行"的强大——ARM的每条指令都可以条件执行,避免了不必要的跳转。
## 关联笔记
- [[嵌入式系统/试题册/index|试题册索引]]
@@ -0,0 +1,336 @@
---
tags: [嵌入式系统, 复习, Android开发]
create time: 2026-06-10 11:11
---
# Android应用开发基础
## 概述
本文档覆盖Android应用开发的核心知识,包括分层架构、四大组件、Activity生命周期、数据存储、UI布局、权限管理和常用设计模式。这是嵌入式系统课程中移动终端开发部分的复习重点。
## 正文
### 1. Android分层架构
Android系统采用分层架构设计,从底层到顶层分为四层:
```mermaid
graph TD
A["Applications"] --> B["Application Framework"]
B --> C["Libraries / Android Runtime"]
C --> D["Linux Kernel"]
style A fill:#4fc3f7
style B fill:#81d4fa
style C fill:#b3e5fc
style D fill:#ffcc80
```
| 层级 | 组成 | 说明 |
|------|------|------|
| **Applications** | 短信、电话、浏览器、第三方App | 用户直接使用的应用 |
| **Application Framework** | Activity Manager, Content Provider, Resource Manager, Notification Manager | 提供构建应用的基础API |
| **Libraries / Runtime** | SQLite, OpenGL, WebKit, Media Framework, Dalvik/ART | 系统库和运行时环境 |
| **Linux Kernel** | 驱动、内存管理、进程管理、网络栈 | 底层硬件抽象和系统服务 |
> [!tip] Dalvik vs ART
> - **Dalvik**:Android 4.4之前使用,基于JIT(Just-In-Time)编译
> - **ART**:Android 5.0开始替代Dalvik,使用AOT(Ahead-Of-Time)编译,运行更快但安装更慢
### 2. 四大组件
Android应用由四大核心组件构成:
#### 2.1 Activity(活动)
Activity代表一个**用户界面屏幕**,是用户与应用交互的主要入口。
```java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
```
#### 2.2 Service(服务)
Service在**后台**运行,没有用户界面。适合执行长时间运行的操作(如音乐播放、文件下载)。
两种启动方式:
- **Started Service**:通过startService()启动,独立运行
- **Bound Service**:通过bindService()绑定,提供客户端-服务器接口
#### 2.3 ContentProvider(内容提供者)
ContentProvider用于**跨应用数据共享**。它封装了数据访问接口,其他应用通过ContentResolver来查询和修改数据。
```java
// 在AndroidManifest.xml中注册
<provider
android:name=".MyContentProvider"
android:authorities="com.example.myprovider"
android:exported="true" />
```
> [!question] 为什么需要ContentProvider而不是直接共享数据库?
> ContentProvider提供了统一的数据访问接口和权限控制机制。直接共享数据库会暴露数据结构细节,且难以控制访问权限。
#### 2.4 BroadcastReceiver(广播接收器)
BroadcastReceiver用于接收和响应**系统或应用广播**,实现组件间的松耦合通信。
配置方式有两种:
**XML静态注册**:
```xml
<receiver android:name=".MyReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
```
**代码动态注册**:
```java
IntentFilter filter = new IntentFilter("com.example.MY_ACTION");
registerReceiver(new MyReceiver(), filter);
```
### 3. Activity生命周期
Activity的生命周期是Android开发的基础知识点:
```mermaid
stateDiagram-v2
[*] --> Created
Created --> Started: onCreate -> onStart
Started --> Running: onResume
Running --> Paused: onPause
Paused --> Running: onResume
Paused --> Stopped: onStop
Stopped --> Started: onRestart -> onStart
Stopped --> Created: onDestroy
Running --> Stopped: onStop
Paused --> Created: onDestroy
Created --> [*]: onDestroy
note right of Created: Activity created
note right of Started: Visible but not focused
note right of Running: Foreground, interactive
note right of Paused: Partially obscured
note right of Stopped: Fully hidden
```
| 生命周期方法 | 调用时机 | 说明 |
|-------------|----------|------|
| onCreate() | Activity首次创建 | 初始化界面、绑定数据 |
| onStart() | Activity变为可见 | 准备显示 |
| onResume() | Activity获得焦点 | 可与用户交互 |
| onPause() | Activity失去焦点 | 保存临时数据(秒级) |
| onStop() | Activity完全不可见 | 释放资源 |
| onDestroy() | Activity被销毁 | 最终清理 |
| onRestart() | 从Stopped重新启动 | 不会直接到onCreate |
> [!warning] onPause()中不要做耗时操作
> onPause()执行完毕后,新的Activity才会启动。如果在onPause()中做网络请求或大量数据处理,会导致界面切换卡顿。耗时操作应该放到onStop()或异步任务中。
### 4. Intent数据传递
Intent是Android中组件间通信的核心机制,用于启动Activity、Service和发送广播。
**传递数据的两种方式**:
**Serializable**(Java标准序列化):
```java
// 发送端
Intent intent = new Intent(this, TargetActivity.class);
intent.putExtra("user", (Serializable) userObj);
startActivity(intent);
// 接收端
User user = (User) getIntent().getSerializableExtra("user");
```
**Parcelable**(Android特有序列化,性能更好):
```java
// 发送端
intent.putExtra("user", (Parcelable) userObj);
// 接收端
User user = getIntent().getParcelableExtra("user");
```
| 比较 | Serializable | Parcelable |
|------|-------------|------------|
| 性能 | 慢(使用反射) | 快(手动实现) |
| 实现 | 简单(实现接口即可) | 复杂(需实现writeToParcel) |
| 用途 | 临时存储、简单对象 | Activity间频繁传递 |
| 开销 | 产生大量临时对象 | 内存开销小 |
### 5. startActivityForResult
当一个Activity需要从另一个Activity**获取返回数据**时,使用startActivityForResult:
```java
// 启动方
Intent intent = new Intent(this, SecondActivity.class);
startActivityForResult(intent, REQUEST_CODE);
// 接收返回数据
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) {
String result = data.getStringExtra("result");
// 处理返回数据
}
}
// 被启动方设置返回数据
Intent resultIntent = new Intent();
resultIntent.putExtra("result", "hello from second");
setResult(RESULT_OK, resultIntent);
finish();
```
### 6. SQLite数据类型
Android内置SQLite数据库,支持以下5种数据类型:
| 数据类型 | 说明 |
|----------|------|
| NULL | 空值 |
| INTEGER | 整数 |
| REAL | 浮点数 |
| TEXT | 文本字符串 |
| BLOB | 二进制大对象 |
> [!warning] SQLite没有VARCHAR类型
> SQLite使用动态类型系统,TEXT类型可以存储任意长度的字符串,不需要指定VARCHAR(n)。但创建表时写VARCHAR也是合法的(会被忽略长度约束)。
### 7. UI布局
Android支持多种布局方式:
| 布局类型 | 特点 | 适用场景 |
|----------|------|----------|
| **LinearLayout** | 线性排列(水平/垂直) | 简单的行列排列 |
| **RelativeLayout** | 相对定位 | 复杂的相对位置关系 |
| **FrameLayout** | 层叠排列,后面的元素覆盖前面 | 重叠界面、帧动画 |
**SimpleAdapter**用于为ListView等列表控件提供数据:
```java
// 数据源:List<Map<String, Object>>
List<Map<String, Object>> data = new ArrayList<>();
Map<String, Object> item = new HashMap<>();
item.put("title", "标题");
item.put("image", R.drawable.icon);
data.add(item);
// 创建适配器
SimpleAdapter adapter = new SimpleAdapter(
this,
data,
R.layout.list_item,
new String[]{"title", "image"}, // 数据键
new int[]{R.id.tvTitle, R.id.ivIcon} // 视图ID
);
listView.setAdapter(adapter);
```
> [!question] SimpleAdapter的数据映射List&lt;Map&gt;中,Map的key和value分别对应什么?
> key是字符串类型的字段名(如"title"),value是该字段的值(如"标题"文本)。适配器通过key从Map中取值,然后绑定到对应的视图控件上。
### 8. 权限管理
Android 7.0将权限分为四种保护级别:
| 权限级别 | 说明 | 安装行为 |
|----------|------|----------|
| **normal** | 低风险权限(如网络访问) | 自动授予 |
| **dangerous** | 高风险权限(如摄像头、位置) | 需要用户确认 |
| **signature** | 只有与应用签名相同的应用才能获得 | 自动授予 |
| **signatureOrSystem** | 签名相同或系统应用才能获得 | 自动授予 |
权限声明方式(AndroidManifest.xml):
```xml
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
```
> [!warning] Android 6.0+运行时权限
> 从Android 6.0开始,dangerous权限需要在运行时动态申请,而不仅仅在Manifest中声明。但signature和normal权限仍然在安装时自动授予。
### 9. SharedPreferences
SharedPreferences是Android提供的轻量级键值对存储机制:
```java
// 保存数据
SharedPreferences prefs = getSharedPreferences("mydata", MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putString("username", "admin");
editor.putInt("age", 25);
editor.apply(); // 异步写入
// 读取数据
String name = prefs.getString("username", "default");
int age = prefs.getInt("age", 0);
```
> [!warning] 默认不能跨应用共享
> MODE_PRIVATE表示该SharedPreferences文件只有当前应用可以访问。如果需要跨应用共享数据,应该使用ContentProvider。
### 10. res vs assets
| 比较 | res | assets |
|------|-----|--------|
| 引用方式 | 通过R.java自动生成的ID | 通过AssetManager按文件名访问 |
| 子目录 | 有结构化子目录(drawable, layout等) | 无结构,可自由组织 |
| 自动优化 | 编译时会被压缩/优化 | 原样保留 |
| 文件名限制 | 只能用小写字母、数字、下划线 | 无限制 |
```java
// res资源引用
setContentView(R.layout.activity_main);
ImageView iv = findViewById(R.id.imageView);
iv.setImageResource(R.drawable.icon);
// assets资源访问
InputStream is = getAssets().open("data/config.json");
```
### 11. 单例模式
单例模式确保一个类只有一个实例:
```java
public class DatabaseHelper {
private static volatile DatabaseHelper instance;
// 私有构造函数,防止外部实例化
private DatabaseHelper() {
// 初始化数据库
}
public static DatabaseHelper getInstance() {
if (instance == null) {
synchronized (DatabaseHelper.class) {
if (instance == null) {
instance = new DatabaseHelper();
}
}
}
return instance;
}
}
```
> [!tip] 双重检查锁定(Double-Checked Locking)
> 单例模式中使用volatile + synchronized实现线程安全的懒加载。volatile防止指令重排序,synchronized保证只创建一次实例。
## 关联笔记
- [[嵌入式系统/试题册/index|试题册索引]]
+39
View File
@@ -0,0 +1,39 @@
---
tags:
- 嵌入式系统
- 复习
- 索引
create time: 2026-06-10 11:11
---
# 嵌入式系统复习文档索引
## 概述
本文档为「嵌入式系统」及「嵌入式系统设计与开发」课程复习材料索引。内容基于历年试题册高频考点梳理,涵盖基础概念、ARM 架构、指令集、异常机制、操作系统及 Android 开发等核心板块。
> [!tip] 使用建议
> 建议按照从基础到进阶的顺序阅读。先掌握 [[嵌入式系统基础]] 和 [[ARM处理器架构]],再深入 [[ARM指令集与汇编]] 与 [[ARM异常与中断机制]],最后学习 [[嵌入式操作系统]] 和 [[Android应用开发基础]]。
## 复习文档目录
### 嵌入式系统(核心)
| 序号 | 文档 | 核心考点 |
|:----:|------|----------|
| 1 | [[嵌入式系统基础]] | 定义、特征、最小系统、OS 对比、应用领域 |
| 2 | [[ARM处理器架构]] | 寄存器组织、CPSR 位域、7 种工作模式、流水线 |
| 3 | [[ARM指令集与汇编]] | ARM vs Thumb、指令格式、桶形移位器、寻址方式 |
| 4 | [[ARM异常与中断机制]] | 7 种异常、向量表、响应流程、中断嵌套 |
| 5 | [[嵌入式操作系统]] | 抢占式/协作式调度、uC/OS-II 任务管理 |
### 嵌入式系统设计与开发(拓展)
| 序号 | 文档 | 核心考点 |
|:----:|------|----------|
| 6 | [[Android应用开发基础]] | 四大组件、Activity 生命周期、Intent、SQLite |
| 7 | [[嵌入式系统开发与前沿趋势]] | 交叉开发环境、RISC-V、AI+嵌入式、5G+IoT |
## 关联笔记
- [[嵌入式系统/试题册/index|试题册索引]]
@@ -0,0 +1,216 @@
---
tags: [嵌入式系统, 复习, RTOS, uC/OS-II]
create time: 2026-06-10 11:11
---
# 嵌入式操作系统
## 概述
本文档讲解嵌入式操作系统的核心概念,重点覆盖RTOS调度策略、uC/OS-II的特性与API、任务管理机制,以及嵌入式Linux和WinCE的对比。RTOS是嵌入式系统考试中与ARM架构并列的重点。
## 正文
### 1. 嵌入式操作系统概述
嵌入式操作系统是运行在嵌入式硬件上的系统软件,负责管理硬件资源、提供任务调度和服务接口。与通用操作系统相比,它更注重**实时性**、**可裁剪性**和**可靠性**。
嵌入式操作系统的分类:
| 分类 | 特点 | 典型代表 |
|------|------|----------|
| 嵌入式RTOS | 严格实时,微内核,可裁剪 | FreeRTOS, uC/OS-II, RT-Thread |
| 嵌入式Linux | 功能丰富,开源,非严格实时 | Android, OpenWrt, Yocto |
| 商用嵌入式OS | 商业授权,完整生态 | WinCE, VxWorks, QNX |
### 2. 抢占式 vs 协作式调度
这是RTOS最核心的概念之一:
**抢占式调度(Preemptive)**:
- 高优先级任务**立即**获得CPU控制权
- 无论当前任务在做什么,都会被中断
- **实时性好**,响应时间可预测
- uC/OS-II、FreeRTOS均采用此方式
**协作式调度(Cooperative)**:
- 任务必须**主动**放弃CPU(调用yield或等待事件)
- 实现简单,但一个任务卡死会影响整个系统
- 早期Windows 3.1、部分简单RTOS使用
```mermaid
graph TD
subgraph preemptive["Preemptive Scheduling"]
direction TB
PA1["Task A running"] --> |"Higher priority Task B becomes ready"| PB1["Task B preempts A"]
PB1 --> |"Task B finishes"| PA2["Task A resumes"]
end
subgraph cooperative["Cooperative Scheduling"]
direction TB
CA1["Task A running"] --> |"Task A calls yield or blocks"| CB1["Task B runs"]
CB1 --> |"Task B yields"| CA2["Task A resumes"]
end
style PA1 fill:#90caf9
style PB1 fill:#ef9a9a
style PA2 fill:#90caf9
style CA1 fill:#a5d6a7
style CB1 fill:#ffcc80
style CA2 fill:#a5d6a7
```
> [!question] 为什么嵌入式系统通常选择抢占式调度?
> 嵌入式系统经常需要处理紧急事件(如传感器报警、安全检测),如果采用协作式调度,低优先级任务长时间不让出CPU会导致高优先级任务无法及时响应,可能造成安全事故。
### 3. RTOS实时性指标
衡量一个RTOS的实时性,主要看两个指标:
1. **调度器执行时间**:调度算法本身运行所需的时间。越短越好。
2. **任务切换时间**:从一个任务切换到另一个任务所需的时间(保存上下文 + 恢复上下文 + 刷新流水线)。
> [!tip] 最坏情况下的响应时间
> 对于硬实时系统,关注的是**最坏情况**下的响应时间,而不是平均响应时间。最坏响应时间 = 最长中断屏蔽时间 + 最高优先级任务执行时间。
### 4. uC/OS-II概述
uC/OS-II是一个经典的**抢占式实时内核**,主要特点:
- **抢占式**基于优先级的调度
- **可裁剪**:通过条件编译去掉不需要的功能
- **可移植**:支持多种处理器架构
- **确定性**:服务时间可预测
- **商业软件**:非开源,需要购买授权
- **支持64个任务**(优先级0-63,0最高,63最低)
> [!warning] uC/OS-II不是免费的
> uC/OS-II是商业软件,与FreeRTOS(免费开源)不同。学习和评估可以使用,但商业应用需要购买许可证。
### 5. 任务管理API
uC/OS-II提供了完善的任务管理接口:
#### 创建与删除
```c
// 创建任务
INT8U OSTaskCreate(
void (*task)(void *), // 任务函数指针
void *pdata, // 传递给任务的参数
OS_STK *ptos, // 任务栈顶指针
INT8U prio // 任务优先级
);
// 删除任务
INT8U OSTaskDel(INT8U prio); // 删除指定优先级的任务
INT8U OSTaskDelReq(INT8U prio); // 请求其他任务删除自己
```
#### 挂起与恢复
```c
// 挂起任务(暂停执行)
INT8U OSTaskSuspend(INT8U prio);
// 恢复被挂起的任务
INT8U OSTaskResume(INT8U prio);
```
#### 延时
```c
// 延时指定时钟节拍数
void OSTimeDly(INT32U ticks);
// 精确延时(时、分、秒)
void OSTimeDlyHMSM(INT8U hours, INT8U minutes,
INT8U seconds, INT16U ms);
```
#### 优先级修改
```c
// 动态修改任务优先级
INT8U OSTaskChangePrio(INT8U oldprio, INT8U newprio);
```
### 6. 任务状态
uC/OS-II中任务有5种状态:
```mermaid
stateDiagram-v2
[*] --> Dormant: Task Created
Dormant --> Ready: OSTaskCreate
Ready --> Running: Scheduler Dispatch
Running --> Ready: Preempted by higher priority
Running --> Waiting: OSTimeDly or Event Wait
Waiting --> Ready: Delay expires or Event occurs
Running --> Suspended: OSTaskSuspend
Suspended --> Ready: OSTaskResume
Running --> Dormant: OSTaskDel
Dormant --> [*]: Deleted
```
| 状态 | 说明 | 进入条件 |
|------|------|----------|
| **Dormant**(休眠) | 任务代码存在内存中但未被调度器管理 | OSTaskDel后 |
| **Ready**(就绪) | 任务准备好运行,等待CPU | 创建后、事件到达后 |
| **Running**(运行) | 任务正在使用CPU执行 | 被调度器选中 |
| **Waiting**(等待) | 任务等待某个事件(延时、信号量等) | 调用延时或等待函数 |
| **Suspended**(挂起) | 任务被显式挂起,不再参与调度 | 调用OSTaskSuspend |
> [!question] Ready和Waiting有什么区别?
> Ready状态的任务随时可以运行,只等调度器分配CPU;Waiting状态的任务即使给它CPU也无法运行,因为它在等待某个外部事件(如延时到期、信号量释放等)。
### 7. 任务优先级分配原则
- **每个任务必须有唯一的优先级**(uC/OS-II中不允许同优先级任务)
- **数值越小,优先级越高**(0最高,63最低)
- **关键任务分配高优先级**:如安全检测、紧急控制
- **不同优先级之间可以抢占**
- **相同优先级的处理**:在uC/OS-II中不支持,但在FreeRTOS中支持时间片轮转
分配策略示例:
| 任务 | 优先级 | 理由 |
|------|--------|------|
| 安全监控 | 0-2 | 最高优先级,必须立即响应 |
| 电机控制 | 3-5 | 实时性要求高 |
| 传感器采集 | 6-10 | 周期性任务 |
| 通信处理 | 11-15 | 重要但允许一定延迟 |
| UI显示 | 20-30 | 实时性要求低 |
### 8. 嵌入式Linux
嵌入式Linux基于标准Linux内核进行裁剪和定制:
- **开源免费**:遵循GPL协议
- **功能丰富**:网络协议栈、文件系统、设备驱动完善
- **非严格意义上的RTOS**:Linux本身不是实时内核,但通过PREEMPT_RT补丁可以实现软实时
- **适合场景**:需要复杂网络功能、多媒体处理的嵌入式设备
与RTOS的对比:
| 比较维度 | 嵌入式Linux | RTOS (uC/OS-II) |
|----------|-------------|------------------|
| 实时性 | 软实时(非严格) | 硬实时 |
| 内核大小 | MB级 | KB级 |
| 开源 | 是 | 否(商业) |
| 功能 | 丰富 | 精简 |
| 适用场景 | 复杂嵌入式设备 | 资源受限的实时系统 |
### 9. WinCE
Windows CE(简称WinCE)是微软推出的嵌入式操作系统:
- **非开源**,商业授权
- 提供完整的GUI和开发工具(Visual Studio集成)
- 适合需要Windows风格界面的手持设备
- 已逐渐被Windows IoT Core取代
> [!question] 在资源极度受限的MCU(如Cortex-M0, 2KB RAM)上,应该选择什么OS?
> 这种情况下应该选择轻量级RTOS如FreeRTOS或直接裸机编程。嵌入式Linux至少需要几MB内存,uC/OS-II虽然裁剪后很小但也需要几十KB,而FreeRTOS最小仅需几百字节RAM。
## 关联笔记
- [[嵌入式系统/试题册/index|试题册索引]]
@@ -0,0 +1,147 @@
---
tags: [嵌入式系统, 复习, 嵌入式基础]
create time: 2026-06-10 11:11
---
# 嵌入式系统基础
## 概述
本文档覆盖嵌入式系统的核心概念,包括定义、特征、硬件组成、操作系统对比以及应用领域。这是整个嵌入式系统课程的基石,理解这些内容是后续学习ARM架构、指令集和操作系统的基础。
## 正文
### 1. 嵌入式系统的定义
**嵌入式系统**(Embedded System)的标准定义为:
> 以**应用为中心**,以**计算机技术**为基础,软硬件**可裁剪**,适应应用系统对功能、可靠性、成本、体积、功耗等严格要求的**专用计算机系统**。
理解这个定义,需要抓住几个关键词:
| 关键词 | 含义 |
|--------|------|
| 以应用为中心 | 不是通用计算,而是为特定任务而设计 |
| 软硬件可裁剪 | 根据需求定制,去掉不需要的功能以节省资源 |
| 专用计算机系统 | 本质上仍是计算机,但专用于特定场景 |
> [!question] 为什么说嵌入式系统"不等于单片机"?
> 单片机只是嵌入式系统的硬件平台之一。嵌入式系统是一个完整的系统概念,包含硬件、驱动、操作系统和应用软件。
### 2. 嵌入式系统的核心特征
1. **专用性** —— 面向特定应用,功能明确
2. **实时性** —— 必须在规定时间内响应外部事件(硬实时/软实时)
3. **可靠性** —— 往往运行在无人值守或关键任务环境
4. **资源受限** —— CPU、内存、存储空间有限
5. **软硬件协同设计** —— 硬件和软件需同步考虑和优化
6. **低功耗** —— 很多场景依赖电池供电
> [!tip] 硬实时 vs 软实时
> - **硬实时**:必须在截止时间前完成,否则造成灾难性后果(如汽车ABS制动系统)
> - **软实时**:允许偶尔超时(如视频播放的轻微卡顿)
### 3. 嵌入式系统的最小系统组成
一个可工作的嵌入式最小系统需要以下组件:
- **处理器(MCU/MPU)**:系统的大脑
- **存储器**:ROM/Flash(存放程序)+ RAM(运行时数据)
- **时钟电路**:为处理器提供工作节拍
- **电源电路**:提供稳定的工作电压
- **复位电路**:确保系统从已知状态启动
- **JTAG调试接口**:用于程序烧写和在线调试
> [!question] 为什么嵌入式系统通常使用Flash而不是硬盘来存储程序?
> Flash是非易失性存储,没有机械部件,抗震性好,功耗低,访问速度快,非常适合嵌入式场景。
### 4. 嵌入式操作系统 vs 通用操作系统
| 比较维度 | 嵌入式OS | 通用OS |
|----------|----------|--------|
| 设计目标 | 面向特定应用 | 面向多种应用 |
| 体积 | 可裁剪,极小 | 功能全面,体积大 |
| 实时性 | 要求高(RTOS) | 不强调实时 |
| 资源需求 | 低(KB~MB级) | 高(GB级) |
| 用户界面 | 简单或无 | 复杂GUI |
| 典型代表 | FreeRTOS, uC/OS-II, RT-Thread | Windows, Linux, macOS |
**嵌入式实时操作系统(RTOS)**的核心指标:
- **调度器执行时间**:调度算法本身的耗时
- **任务切换时间**:从一个任务切换到另一个任务的耗时
### 5. 嵌入式系统的应用领域
嵌入式系统几乎无处不在:
- **消费电子**:智能手机、平板电脑、可穿戴设备
- **汽车电子**:发动机控制、ABS、车载娱乐系统
- **智能家居**:智能音箱、智能门锁、家电控制
- **工业控制**:PLC、工业机器人、自动化产线
- **医疗设备**:心脏起搏器、监护仪、输液泵
- **航空航天**:飞行控制、导航系统
### 6. 智能移动终端带来的变革
智能移动终端(以智能手机为代表)带来了深刻的变革:
- **交互方式**:从键盘鼠标 → 触摸屏、语音、手势
- **计算模式**:从集中式 → 云端+终端协同
- **信息获取**:从固定网络 → 随时随地移动获取
- **生态系统**:从封闭系统 → 开放应用商店生态
### 7. 嵌入式系统架构分层
嵌入式系统的软件架构通常分为以下层次:
```mermaid
graph TD
A["Application Layer"] --> B["Middleware"]
B --> C["RTOS / OS"]
C --> D["BSP / Driver"]
D --> E["Hardware"]
style A fill:#4fc3f7
style B fill:#81d4fa
style C fill:#b3e5fc
style D fill:#e1f5fe
style E fill:#fff9c4
```
- **Hardware**:处理器、存储器、外设
- **BSP/Driver**:板级支持包和设备驱动,屏蔽硬件差异
- **RTOS/OS**:提供任务调度、内存管理、同步机制
- **Middleware**:网络协议栈、文件系统、数据库等通用服务
- **Application**:面向用户的具体应用
### 8. 嵌入式系统的产品形态
```mermaid
graph LR
subgraph forms["Product Forms"]
direction TB
F1["Single Chip MCU"]
F2["SOM Module"]
F3["SBC Board"]
F4["Full System"]
end
F1 --> F2
F2 --> F3
F3 --> F4
style F1 fill:#ffcc80
style F2 fill:#ffab91
style F3 fill:#ef9a9a
style F4 fill:#f48fb1
```
- **单芯片MCU**:所有功能集成在一颗芯片上(如STM32)
- **SOM模块**:核心板模块,含处理器和基础电路
- **SBC单板计算机**:完整的单板系统(如树莓派)
- **完整系统**:包含外壳、电源、外设的完整产品
> [!question] 嵌入式系统的"软硬件协同设计"具体是什么意思?
> 传统的设计流程是先设计硬件再写软件,但嵌入式系统需要在设计初期就同时考虑软硬件。例如,如果软件算法可以补偿硬件精度不足,就可以选用更便宜的传感器,从而降低成本和功耗。
## 关联笔记
- [[嵌入式系统/试题册/index|试题册索引]]
@@ -0,0 +1,285 @@
---
tags: [嵌入式系统, 复习, 开发环境, 前沿技术]
create time: 2026-06-10 11:11
---
# 嵌入式系统开发与前沿趋势
## 概述
本文档涵盖嵌入式系统的开发环境与流程、BootLoader、GPIO配置、存储技术、RISC-V架构、国产替代分析,以及AI+嵌入式、TinyML、5G+IoT等前沿技术趋势。既覆盖传统开发基础,也关注行业发展方向。
## 正文
### 1. 交叉开发环境
嵌入式开发与普通PC开发的最大区别在于:**开发在宿主机上进行,运行在目标板上**。这种开发方式称为交叉开发。
```mermaid
graph LR
subgraph devEnv["Cross-Development Environment"]
direction TB
subgraph hw["Hardware"]
HB["Host PC"]
JTAG["JTAG Debugger"]
TB["Target Board"]
end
subgraph sw["Software"]
TC["Cross-Compiler Toolchain"]
IDE["IDE (Keil/IAR)"]
DBG["Debugger"]
RTOS["OS / RTOS"]
end
end
HB --> |"Compile Code"| TC
TC --> |"Generate Binary"| TB
JTAG --> |"Debug & Flash"| TB
IDE --> TC
IDE --> DBG
style HB fill:#90caf9
style JTAG fill:#ffcc80
style TB fill:#a5d6a7
style TC fill:#ce93d8
```
#### 硬件部分
| 组件 | 作用 |
|------|------|
| **目标板(Target Board)** | 运行最终程序的嵌入式硬件 |
| **JTAG调试器** | 连接宿主机和目标板,用于烧写和在线调试 |
| **宿主机(Host PC)** | 运行开发工具的PC(Windows/Linux) |
#### 软件部分
| 组件 | 作用 |
|------|------|
| **交叉编译工具链** | 在宿主机上编译出目标板可执行的代码(如arm-none-linux-gnueabi-gcc) |
| **IDE** | 集成开发环境(Keil MDK, IAR, Eclipse) |
| **调试器** | GDB + OpenOCD等远程调试工具 |
| **OS/RTOS** | 目标板上运行的操作系统 |
> [!question] 什么是"交叉编译"?
> 交叉编译是指在一个平台上(如x86 PC)编译出另一个平台(如ARM)可执行的代码。因为宿主机和目标板的CPU架构不同,普通编译器生成的代码无法直接在目标板上运行。
### 2. 开发流程
嵌入式系统的典型开发流程:
```mermaid
flowchart TD
A["Write Source Code"] --> B["Cross-Compile"]
B --> C["Generate Binary File"]
C --> D["Flash to Target Board"]
D --> E["Debug & Test"]
E --> F{"Pass?"}
F --> |"No"| A
F --> |"Yes"| G["Deployment"]
style A fill:#4fc3f7
style B fill:#81d4fa
style C fill:#b3e5fc
style D fill:#ffcc80
style E fill:#ffab91
style G fill:#a5d6a7
```
关键步骤说明:
1. **编写代码**:C/C++/汇编,在宿主机上编写
2. **交叉编译**:使用交叉编译器生成目标平台的二进制文件
3. **烧写(Flash)**:通过JTAG/串口/USB将程序写入目标板Flash
4. **调试**:使用GDB远程调试,设置断点、单步执行
5. **验证**:功能测试、性能测试、稳定性测试
### 3. BootLoader
**BootLoader**是嵌入式系统上电后**第一个执行**的程序,负责初始化硬件并加载操作系统。
启动流程:
1. **上电复位** → CPU从固定地址(通常是0x00000000)开始执行
2. **硬件初始化** → 设置时钟、初始化DRAM、配置外设
3. **加载内核** → 从Flash读取OS内核到RAM
4. **跳转执行** → 将控制权交给操作系统内核
常见的BootLoader:
- **U-Boot**:开源,支持多种架构,最广泛使用
- **GRUB**:主要用于x86 Linux系统
- **Blob**:早期ARM Linux使用的BootLoader
> [!warning] BootLoader不是操作系统
> BootLoader的功能非常有限,它的唯一使命是把操作系统"拉起来"。一旦OS开始运行,BootLoader就完成了历史使命。
### 4. GPIO可配置属性
GPIO(General Purpose Input/Output)是嵌入式系统中最基本的外设接口。每个GPIO引脚通常可以配置以下属性:
| 属性 | 说明 |
|------|------|
| **速度(Speed)** | 2MHz / 25MHz / 50MHz / 100MHz |
| **方向(Direction)** | 输入 / 输出 |
| **外设选择(AF)** | 通用GPIO / 特殊功能(如UART、SPI) |
以STM32为例,一个GPIO引脚可以配置为:
- 输入模式:上拉、下拉、浮空
- 输出模式:推挽输出、开漏输出
- 复用功能:UART_TX, SPI_MOSI, I2C_SCL等
### 5. SRAM vs DRAM
存储技术是嵌入式系统的关键:
| 比较维度 | SRAM | DRAM |
|----------|------|------|
| 结构 | 6个晶体管/cell | 1个晶体管 + 1个电容/cell |
| 速度 | **快**(1-10ns) | 较慢(50-70ns) |
| 成本 | **贵** | 便宜 |
| 密度 | 低(面积大) | **高**(面积小) |
| 功耗 | 低 | 需要刷新,功耗较高 |
| 用途 | Cache、嵌入式MCU内部RAM | 主存、大容量存储 |
> [!question] 为什么嵌入式MCU通常使用SRAM而不是DRAM?
> MCU对成本和功耗敏感,且所需RAM容量通常较小(几KB到几百KB)。SRAM不需要刷新电路,集成在芯片内部更简单,且读写速度更快,非常适合MCU的应用场景。
### 6. RISC-V架构
RISC-V是一个**开源的指令集架构(ISA)**,近年来在嵌入式领域受到广泛关注。
核心特点:
- **开源免费**:任何人都可以自由使用、修改和实现
- **模块化设计**:基础指令集 + 可选扩展
- RV32I:32位基础整数指令集
- RV64I:64位基础整数指令集
- M扩展:乘除法
- A扩展:原子操作
- F/D扩展:单/双精度浮点
- C扩展:压缩指令(16位)
### 7. RISC-V vs ARM 国产替代
在国产化替代的大背景下,RISC-V被视为打破ARM垄断的重要路径:
| 比较维度 | RISC-V | ARM |
|----------|--------|-----|
| 授权费用 | **免费** | 需要授权费 |
| 生态成熟度 | **不成熟** | 非常成熟 |
| 软件兼容性 | 需要适配 | 广泛支持 |
| 人才储备 | **不足** | 丰富 |
| 自主可控 | **完全自主** | 受制于ARM公司 |
| 政策支持 | 国家重点扶持 | 已广泛使用 |
| 芯片厂商 | 芯来科技、平头哥 | 高通、三星、ST |
> [!tip] 国产替代的现实挑战
> RISC-V虽然在架构层面实现了自主可控,但生态建设需要时间。编译器、操作系统、驱动、开发工具等软件生态的完善是最大的挑战。短期内ARM仍然是主流选择。
### 8. AI + 嵌入式
AI与嵌入式系统的融合是当前最热门的方向之一。典型的AI嵌入式系统架构:
```mermaid
graph TD
subgraph sensorLayer["Sensor Layer"]
DHT11["DHT11 Temp/Humidity"]
BH1750["BH1750 Light"]
MQ2["MQ-2 Smoke"]
end
subgraph mcuLayer["MCU Layer"]
STM32["STM32 MCU"]
end
subgraph commLayer["Communication Layer"]
ESP8266["ESP8266 WiFi"]
MQTT["MQTT Protocol"]
end
subgraph cloudLayer["Cloud & AI"]
SERVER["Cloud Server"]
AI["AI Classification"]
end
subgraph appLayer["Application Layer"]
APP["Mobile APP"]
end
DHT11 --> STM32
BH1750 --> STM32
MQ2 --> STM32
STM32 --> ESP8266
ESP8266 --> MQTT
MQTT --> SERVER
SERVER --> AI
AI --> APP
SERVER --> APP
style DHT11 fill:#ffcc80
style BH1750 fill:#ffcc80
style MQ2 fill:#ffcc80
style STM32 fill:#a5d6a7
style ESP8266 fill:#90caf9
style MQTT fill:#90caf9
style SERVER fill:#ce93d8
style AI fill:#f48fb1
style APP fill:#4fc3f7
```
#### 传感器数据采集
常用嵌入式传感器:
- **DHT11**:温湿度传感器,单总线协议
- **BH1750**:光照强度传感器,I2C协议
- **MQ-2**:烟雾/可燃气体传感器,模拟输出
#### 数据处理链路
1. **数据滤波**:去除噪声(均值滤波、中值滤波)
2. **阈值检测**:判断是否超过安全范围
3. **AI分类**:使用轻量级模型进行异常识别
4. **MQTT云上传**:通过MQTT协议将数据上传到云平台
5. **手机APP**:实时查看数据和接收告警
#### 关键硬件组件
| 组件 | 角色 | 说明 |
|------|------|------|
| STM32 | 主控MCU | 数据采集和预处理 |
| ESP8266 | WiFi模块 | 无线联网 |
| MQTT | 通信协议 | 轻量级IoT消息协议 |
### 9. 5G + IoT
5G技术为嵌入式IoT系统带来新的可能:
| 5G特性 | 对IoT的影响 |
|--------|-------------|
| **eMBB**(增强移动宽带) | 支持高清视频监控、AR/VR嵌入式设备 |
| **uRLLC**(超可靠低延迟) | 工业控制、自动驾驶等硬实时场景 |
| **mMTC**(海量机器通信) | 支持每平方公里百万级设备连接 |
### 10. TinyML
**TinyML**(Tiny Machine Learning)是指在**微控制器级别**(通常<1mW功耗)运行机器学习推理。
核心挑战:
- **内存限制**:MCU通常只有几KB到几百KB RAM
- **算力限制**:没有GPU,只能用CPU或专用加速器
- **功耗限制**:电池供电设备要求极低功耗
关键技术:
- **模型量化**:将32位浮点模型压缩为8位整数模型
- **模型剪枝**:移除不重要的神经元连接
- **知识蒸馏**:用大模型训练小模型
- **专用框架**:TensorFlow Lite Micro, STM32Cube.AI
> [!question] 为什么TinyML选择在MCU上推理而不是把数据传到云端处理?
> 有三个关键原因:1)**隐私保护**,数据不出本地;2)**低延迟**,本地推理响应更快;3)**省带宽**,不需要持续的网络连接,降低功耗和通信成本。
> [!tip] TinyML的应用场景
> - 关键词检测(如"Hey Siri")
> - 异常声音检测(如玻璃破碎、设备故障)
> - 运动手势识别
> - 工业设备预测性维护
> - 农业病虫害识别
## 关联笔记
- [[嵌入式系统/试题册/index|试题册索引]]