没有匹配的章节,请换一个关键词。
01 · 考前速记
变量读入
ADR + LDR:先找地址,再从内存读进寄存器。结果写回
ADR + STR:先找变量地址,再把寄存器写回内存。函数调用
BL FUNCTION 调用,BX LR 返回。SPI 读取发送读命令后持续写 dummy byte,才能产生时钟并读回数据。
I2C 读取先写 register pointer,再 repeated start 读取数据。
外部中断ISR 只设置
volatile flag,复杂任务放回 main loop。
核心记忆:低级语言更快、更靠近硬件、能直接控制寄存器和内存;高级语言更容易编写、阅读、调试和维护。
02 · ARM 汇编
| 指令 | 含义 | 快速理解 |
|---|---|---|
LDR | Load Register | 从内存读取到寄存器 |
STR | Store Register | 把寄存器数据写入内存 |
ADR | Address | 把变量或标签地址放进寄存器 |
AND | 按位与 | 两个 bit 都为 1 才为 1 |
ORR | 按位或 | 有一个 bit 为 1 就为 1 |
EOR | 按位异或 | 不同为 1,相同为 0 |
BIC | Bit Clear | Rn AND NOT Operand2,用 1 清除对应 bit |
MVN | Move Not | 按位取反后写入目标寄存器 |
LSL / LSR | 逻辑左移 / 右移 | 按指定 bit 数移动 |
CMP | Compare | 执行减法但不保存结果,只更新 N/Z/C/V |
B / BL | Branch / Branch Link | 跳转 / 保存返回地址后调用函数 |
条件判断:C 转汇编
// C
if (i == j)
f = g + h;
f = f - i;
; ARM
CMP R3, R4
BNE L1
ADD R0, R1, R2
L1:
SUB R0, R0, R3
; 使用条件执行的版本
CMP R3, R4
ADDEQ R0, R1, R2
SUB R0, R0, R3
函数调用
BL FUNCTION ; LR 保存返回地址
ADD R0, R1, R2 ; 函数返回后继续
FUNCTION:
SUB R0, R0, R1
BX LR ; 返回调用点
循环题:找 1 到 9 中的 4 的倍数
unsigned R0 = 0;
unsigned R1 = 1;
unsigned R2 = 10;
unsigned R3 = 3; // 二进制 0011,用作 mask
unsigned R4 = 1;
unsigned R5 = 0;
unsigned R6;
while (R1 != R2) {
R6 = R1 & R3;
if (R6 == R5) {
R0 = R0 + R1;
}
R1 = R1 + R4;
}
最终结果:
R0 = 12,因为 4 + 8 = 12;循环结束时 R1 = 10。03 · 数据流图
Data Flow Graph 不表达普通程序流程,而是表达数据依赖:数据从哪里进入、经过哪些运算、最后形成什么结果。
a→
+→
t1→
×→
z
口诀:变量进圆圈,结果出圆圈;圆圈是运算,箭头是依赖;谁先算出来,谁才能往下走。
04 · 嵌入式系统基础
开发生命周期
- Requirement analysis:明确精度、采样周期、通信、功耗和成本约束。
- System design:选择 MCU、传感器、无线模块和接口。
- Prototyping:搭建硬件原型并编写 firmware。
- Testing and verification:验证精度、时序、可靠性和通信。
- Deployment and maintenance:部署到现场,持续更新和修复。
- Feedback and iteration:根据实际使用继续改进。
Embedded firmware
Firmware 是存储在嵌入式系统程序存储器中的内置控制软件,包含控制算法和配置设置,用于启动硬件、控制外设并响应输入。
英文答题句:Embedded firmware is the built-in control software stored in the memory of an embedded system. It provides the instructions and configuration required to control hardware, peripherals and inputs.
05 · 质量属性
Operational Quality Attributes
| 英文 | 中文 | 记忆 |
|---|---|---|
| Response time | 响应时间 | 快不快 |
| Throughput | 吞吐量 | 单位时间处理得多不多 |
| Reliability | 可靠性 | 稳不稳定 |
| Maintainability | 可维护性 | 好不好修 |
| Security | 信息安全 | 会不会被攻击 |
| Safety | 功能安全 | 会不会伤人或造成危险 |
Non-operational Quality Attributes
| 英文 | 中文 | 记忆 |
|---|---|---|
| Testability / Debuggability | 可测试 / 可调试性 | 好不好测和 debug |
| Scalability | 可扩展性 | 以后能不能增加功能 |
| Time to market | 上市时间 | 能不能快速做出来 |
| Portability | 可移植性 | 能否迁移到其他平台 |
| Per-unit cost and revenue | 单位成本和收益 | 成本和盈利能力 |
06 · 调度与 RTOS
| 方式 | 优点 | 缺点 |
|---|---|---|
| Super loop | 无需 OS、结构简单、内存占用低 | 实时性不稳定;一个任务阻塞会影响整个系统 |
| Embedded OS / RTOS | 多任务、优先级、可预测时序、扩展性强 | 需要更多内存和处理器资源,设计复杂 |
Scheduler 决定下一个获得 CPU 的任务;dispatcher 实际完成上下文切换并把处理器控制权交给该任务。
刹车任务题:如果低优先级或非抢占任务阻塞了紧急刹车,应该使用 preemptive priority scheduling / preemptive HPF,把 braking task 设置为最高优先级,并保证响应时间满足 1 秒要求。
07 · 处理器与存储
RISC 与 CISC
| 对比 | RISC | CISC |
|---|---|---|
| 指令 | 数量少、格式较固定、单条简单 | 数量多、格式复杂、单条功能强 |
| 执行 | 适合流水线,通常执行周期更可预测 | 单条指令可能需要多个周期 |
| 硬件 | 控制逻辑相对简单 | 控制逻辑更复杂 |
| 例子 | ARM、MIPS | x86 |
Memory-mapped I/O
代码、SRAM、外设寄存器、外部存储器和系统控制区域占据不同地址范围。处理器可以用普通 load/store 指令访问外设寄存器,因此程序不需要单独的 I/O 指令。
08 · SPI 与 I2C
| 项目 | SPI | I2C |
|---|---|---|
| 信号线 | MOSI、MISO、SCLK、每个 slave 的 CS | SDA、SCL |
| 寻址 | 使用独立 CS 片选 | 使用 7-bit / 10-bit 地址 |
| 传输 | 全双工,速度高 | 半双工,两线节省引脚 |
| 多设备 | 设备越多,CS 引脚越多 | 多个设备可共享总线 |
| 典型选择 | 高速传感器、显示、存储器 | 低速传感器、配置设备 |
SPI 写寄存器模板
cs = 0; // 选中 slave
spi.write(register_address);
spi.write(value);
cs = 1; // 结束通信SPI 连续读取模板
cs = 0;
spi.write(READ_BIT | MULTI_BIT | start_register);
for (int i = 0; i < length; i++) {
buffer[i] = spi.write(0x00); // dummy byte 产生时钟
}
cs = 1;I2C 读写寄存器模板
// Read
char reg = start_register;
i2c.write(address, ®, 1, true); // repeated start
i2c.read(address, buffer, length);
// Write
char data[2];
data[0] = register_address;
data[1] = value;
i2c.write(address, data, 2);09 · 传感器代码
ADXL345:SPI 初始化
SPI adxl_spi(A6, A5, A4); // MOSI, MISO, SCLK
DigitalOut adxl_cs(D12);
void write_register(uint8_t reg, uint8_t value) {
adxl_cs = 0;
adxl_spi.write(reg);
adxl_spi.write(value);
adxl_cs = 1;
}
void initialise() {
adxl_cs = 1;
adxl_spi.format(8, 3); // 8-bit, mode 3
adxl_spi.frequency(2000000); // 2 MHz
write_register(0x31, 0x0B); // FULL_RES, +/-16g
write_register(0x2D, 0x08); // measurement mode
}
TMP102:读取温度
float read_temperature() {
char reg = 0x00;
char data[2];
if (i2c.write(0x90, ®, 1, true) != 0) return 999.0f;
if (i2c.read(0x90, data, 2) != 0) return 999.0f;
uint8_t msb = (uint8_t)data[0];
uint8_t lsb = (uint8_t)data[1];
int16_t raw = (int16_t)((msb << 4) | (lsb >> 4));
if (raw & 0x0800) raw |= 0xF000; // 负温度符号扩展
return raw * 0.0625f;
}
TMP102 重点:读 temperature register 前先写 pointer;12-bit 数据合并后,1 LSB = 0.0625°C;负数需要 sign extension。
10 · 外部中断模板
InterruptIn button(A6);
volatile bool request_flag = false;
void button_isr() {
request_flag = true; // ISR 只设置 flag
}
int main() {
button.rise(&button_isr);
while (1) {
if (request_flag) {
request_flag = false;
// 在 main 中执行 I2C、printf 等复杂任务
}
ThisThread::sleep_for(20ms);
}
}
volatile 告诉编译器:这个变量可能被正常程序流程之外的中断突然修改,每次使用时都必须重新从内存读取。
不要在 ISR 中:执行 I2C 读写、长时间循环、阻塞等待或复杂的
printf。11 · 模块化编程
头文件负责声明模块对外提供的接口,.cpp 文件负责真正实现。其他文件只需要知道函数原型,不需要了解内部细节,这对应 interface、abstraction 和 encapsulation。
| 文件 | 应该放 | 避免放 |
|---|---|---|
.h | 函数声明、类声明、宏、类型定义、extern 声明 | 普通全局变量定义、普通函数体 |
.cpp | 函数实现、变量定义、模块内部逻辑 | 把所有接口隐藏而不提供头文件 |
Header guard
#ifndef LED_H
#define LED_H
void LED_Init();
void LED_On();
void LED_Off();
#endif
#include 本质上会把头文件内容插入当前位置。Header guard 可防止同一头文件被间接包含多次,避免 redefinition error。
标准答题句:Header guards prevent a header file from being included multiple times in the same translation unit, avoiding redefinition errors caused by repeated inclusion.