Files
EmbeddedSoftwareLearn1/02-嵌入式系统基础知识/README.md
T
2025-06-17 14:55:56 +08:00

573 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 第二层:嵌入式系统基础知识
---
## 🔹 嵌入式系统概览
### 📌 嵌入式系统定义与特点
**定义**:
- 专用性:针对特定任务优化,如汽车 ABS 防抱死系统仅负责刹车控制。
- 嵌入性:隐藏于设备内部,用户通常意识不到其存在(如微波炉中的控制系统)。
- 计算系统:包含硬件(处理器、传感器)和软件(固件、驱动)。
**特点**:
- 资源受限(低功耗、内存小)
- 实时性要求高
- 高可靠性与稳定性
- 通常运行裸机或 RTOS
---
### 📌 系统构成(MCU、存储器、传感器、外设)
| 模块 | 功能说明 |
|------------|--------------------------------------|
| MCU | 核心控制器,如 ARM Cortex-M |
| Flash/SRAM | 存储程序代码与运行数据 |
| 外设 | GPIO、UART、SPI、I2C、ADC、PWM 等 |
| 传感器 | 温湿度、光照、姿态等 |
| 通信模块 | WiFi、BLE、LoRa、CAN、NB-IoT 等 |
| 电源管理 | 电池、LDO、DC-DC,支持低功耗模式 |
```
+--------------------------+
| MCU |
| +----------------------+ |
| | Flash / RAM | |
| | GPIO / UART / ADC | |
| +----------------------+ |
+-----------|--------------+
|
+------+------+
| 外设 / 传感器 |
+-------------+
```
嵌入式系统的硬件构成是一个有机整体,各模块协同工作实现特定功能。以下从核心组件到通信架构进行深度解析:
---
### MCU(微控制器):嵌入式系统的心脏
#### 1. **核心功能**
- **运算与控制**:执行程序指令,处理传感器数据,控制外设。
- **典型架构**:
- **ARM Cortex-M**:主流低功耗MCU(如STM32、Nordic nRF系列)。
- **RISC-V**:开源架构,灵活定制(如SiFive、平头哥玄铁系列)。
- **8051/AVR**:传统8位MCU,低成本(如Arduino Uno基于ATmega328P)。
#### 2. **关键参数**
- **主频**:决定运算速度(如STM32F103主频72MHz,STM32H7主频480MHz)。
- **内核位数**:8位(适合简单控制)、32位(主流)、64位(高性能应用)。
- **片上外设**:集成ADC、DAC、PWM等功能模块,减少外部芯片依赖。
### 存储器:程序与数据的载体
#### 1. **Flash存储器**
- **功能**:存储程序代码(固件),掉电不丢失。
- **分类**:
- **NOR Flash**:读取速度快,支持XIP(就地执行),适合代码存储。
- **NAND Flash**:容量大、成本低,适合存储大量数据(如SSD、SD卡)。
- **典型应用**:
- MCU内置Flash(如STM32F407含1MB Flash)。
- 外部SPI Flash(如W25Q系列,用于存储文件系统或配置参数)。
#### 2. **SRAM(静态随机存取存储器)**
- **功能**:运行时数据存储(如变量、堆栈)。
- **特点**:速度快(纳秒级访问),但成本高、容量小。
- **容量配置**:
- 小型MCU:几KB~几十KB(如Arduino Uno含2KB SRAM)。
- 高性能MCU:数百KB~MB级(如STM32H7含1MB SRAM)。
#### 3. **其他存储类型**
- **EEPROM**:可擦写可编程只读存储器,适合存储少量关键参数(如设备ID)。
- **FRAM**:铁电随机存储器,读写速度快、寿命长(10^12次擦写),用于数据记录。
### 外设接口:与外部世界的桥梁
#### 1. **GPIO(通用输入输出)**
- **功能**:数字信号输入/输出(如控制LED、读取按键状态)。
- **特性**:
- 可配置上拉/下拉电阻。
- 支持中断触发(如外部按键按下时唤醒MCU)。
#### 2. **通信接口**
| **接口** | **特点** | **典型应用** |
|----------|--------------------------|----------------------------------|
| **UART** | 全双工,异步,2线(TX/RX) | 调试信息输出、与模块通信(如GPS) |
| **SPI** | 全双工,同步,4线(SCK/MOSI/MISO/CS) | 高速数据传输(如OLED屏幕) |
| **I2C** | 半双工,同步,2线(SCL/SDA) | 多设备通信(如连接多个传感器) |
| **CAN** | 差分信号,抗干扰强,多主模式 | 汽车电子(如ECU间通信) |
#### 3. **模拟接口**
- **ADC(模拟-to-数字转换器)**:
- 将模拟信号(如电压、电流)转换为数字值。
- 精度通常为10~16位(如STM32的12位ADC,分辨率4096级)。
- **DAC(数字-to-模拟转换器)**:
- 将数字值转换为模拟电压输出(如音频信号生成)。
#### 4. **定时控制**
- **PWM(脉冲宽度调制)**:
- 通过占空比控制输出电压平均值,用于电机调速、LED调光。
- 频率范围:几Hz~MHz(如舵机控制需50Hz PWM)。
### 传感器:感知物理世界的窗口
#### 1. **常见类型**
- **环境传感器**:
- **温湿度**:DHT22、SHT30(精度±0.3°C)。
- **光照**:BH1750(测量范围1~65535 lux)。
- **气压**:BMP280(海拔测量误差±1m)。
- **运动传感器**:
- **加速度计**:ADXL345(检测倾斜、振动)。
- **陀螺仪**:MPU6050(测量角速度,用于姿态解算)。
- **其他**:
- **气体传感器**:MQ-2(检测烟雾、液化气)。
- **红外传感器**:HC-SR501(人体感应)。
#### 2. **接口方式**
- **数字接口**:I2C(如SHT30)、SPI(如ADXL345)。
- **模拟接口**:输出电压值,需通过MCU的ADC转换(如模拟光照传感器)。
### 通信模块:连接万物的纽带
#### 1. **短距离通信**
- **WiFi**:
- 标准:802.11b/g/n(如ESP8266、ESP32)。
- 应用:智能家居(如智能插座)、数据上传至云端。
- **BLE(蓝牙低功耗)**:
- 传输距离:10~100m,功耗极低(如Nordic nRF52系列)。
- 应用:可穿戴设备(如心率带)、Beacon定位。
#### 2. **中长距离通信**
- **LoRa**:
- 扩频技术,传输距离5~15km,低功耗(如Semtech SX1278)。
- 应用:物联网广域覆盖(如智能抄表)。
- **NB-IoT**:
- 蜂窝网络,覆盖全国,功耗极低(如Quectel BC660K)。
- 应用:远程监控(如井盖状态监测)。
#### 3. **工业总线**
- **CAN总线**:
- 传输速率:500kbps~1Mbps,抗干扰强。
- 应用:汽车电子(如车身控制模块)、工业自动化。
- **Modbus**:
- 主从协议,支持RS-232/RS-485,广泛用于工业设备通信。
### 电源管理:续航与稳定性的保障
#### 1. **电源转换**
- **LDO(低压差线性稳压器)**:
- 优点:电路简单,输出纹波小(如AMS1117-3.3)。
- 缺点:效率低(输入输出压差越大,效率越低)。
- **DC-DC转换器**:
- 升压/降压,效率高(可达90%以上,如TPS62160)。
- 适合电池供电设备(如充电宝)。
#### 2. **低功耗设计**
- **休眠模式**:
- MCU进入休眠,关闭非必要外设,仅保留唤醒源(如RTC时钟)。
- 典型功耗:STM32L4系列休眠电流低至0.5μA。
- **动态电压调整**:
- 根据工作负载动态降低MCU电压(如ARM Cortex-M4的FlexPowerControl)。
---
### 系统集成与典型架构
#### 1. **最小系统**
- MCU + 晶振 + 复位电路 + 电源。
- 示例:STM32最小系统板(核心板)。
#### 2. **扩展架构**
```
┌─────────────────────────────────────────┐
│ 应用层 │
│ (用户逻辑:如温湿度采集、数据处理) │
├─────────────────────────────────────────┤
│ 驱动层 │
│ (传感器驱动、通信协议栈、外设控制) │
├─────────────────────────────────────────┤
│ 硬件层 │
│ MCU ── 存储器 ── 外设 ── 传感器 ── 通信 │
└─────────────────────────────────────────┘
```
#### 3. **低功耗设计案例**
- **智能手环**:
- 平时MCU处于休眠,加速度计检测运动状态。
- 定时唤醒GPS模块采集位置数据,通过BLE上传手机。
### 开发与调试工具
#### 1. **硬件工具**
- **开发板**:STM32 Nucleo、Arduino、ESP32 DevKit。
- **调试器**:ST-Link、J-Link(用于程序下载和调试)。
- **逻辑分析仪**:Saleae Logic(分析通信协议波形)。
#### 2. **软件工具**
- **IDE**:Keil MDK、STM32CubeIDE、Arduino IDE。
- **驱动配置**:STM32CubeMX(自动生成初始化代码)。
- **调试工具**:OpenOCD(开源调试协议)、GDB(调试器)。
---
### 面试高频问题
1. **SPI与I2C的区别**:
- SPI:高速(可达数十Mbps),4线,点对点;I2C:低速(标准100kbps),2线,支持多设备。
2. **如何选择合适的通信协议**:
- 短距离高速:SPI;多设备低速:I2C;长距离抗干扰:CAN;广域低功耗:LoRa/NB-IoT。
3. **低功耗设计的关键策略**:
- 休眠模式、动态电压调整、关闭非必要外设、使用低功耗通信协议(如BLE)。
---
## 🔹 架构与启动流程
### 📌 Cortex-M 内核结构
- 32 位精简指令集(Thumb 指令集)
- 内建 NVIC(中断控制器)
- 支持两种堆栈:MSP(主栈)和 PSP(进程栈)
- 寄存器组:R0-R15、LR、PC、xPSR
---
### 📌 启动文件 Startup.s
- 用汇编语言书写的启动文件,完成向量表定义、初始化堆栈、调用 `main()`。
```asm
Reset_Handler:
LDR R0, =_estack ; 设置栈顶地址
MOV SP, R0
BL SystemInit ; 时钟初始化
BL main ; 跳转到主函数
```
---
### 📌 启动流程简要
1. MCU 上电 → 执行 `Reset_Handler`
2. 设置 SP(栈顶)
3. 初始化 `.data` 和 `.bss` 段
4. 调用 `SystemInit()`(通常配置系统时钟)
5. 跳转执行用户 `main()` 函数
---
## 🔹 编译器与链接器
### 嵌入式工具链详解
#### 1. **Keil MDK(Microcontroller Development Kit)**
- **特点**:
- 商业软件,支持ARM Cortex-M/R/A全系列处理器。
- 集成μVision IDE、ARM编译器、调试器,界面友好。
- 针对STM32等芯片提供Device Family Pack(DFP),简化外设配置。
- **应用场景**:
- 企业级产品开发(如医疗设备、工业控制)。
- 需高效调试功能(如硬件断点、实时变量监控)。
#### 2. **IAR EWARM(Embedded Workbench for ARM)**
- **特点**:
- 编译效率高,生成代码体积比GCC小10%-20%。
- 调试器支持高级功能(如指令级调试、功耗分析)。
- 跨平台支持(Windows、Linux、Mac)。
- **应用场景**:
- 对代码体积敏感的场景(如MCU Flash空间有限)。
- 汽车电子(符合ISO 26262功能安全标准)。
#### 3. **GCC (arm-none-eabi)**
- **特点**:
- 开源免费,基于GNU工具链(GCC、GDB、Binutils)。
- 跨平台支持,适合Linux开发者。
- 可通过命令行(CLI)集成到自动化构建流程(如Makefile、CMake)。
- **典型工具**:
- `arm-none-eabi-gcc`:编译器。
- `arm-none-eabi-ld`:链接器。
- `arm-none-eabi-objcopy`:格式转换工具(如生成.bin/.hex文件)。
### 链接脚本(.ld)深入解析
#### 1. **核心作用**
- **内存分区**:定义Flash、RAM等存储器区域的起始地址和大小。
- **段分配**:将代码段(.text)、数据段(.data)、BSS段(.bss)等映射到指定内存区域。
- **地址对齐**:确保关键数据(如中断向量表)位于特定地址。
#### 2. **MEMORY 区块解析**
```ld
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K // 只读,可执行
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 64K // 可读可写可执行
}
```
- **属性说明**:
- `r`:可读,`w`:可写,`x`:可执行。
- `ORIGIN`:起始地址,`LENGTH`:大小。
#### 3. **SECTIONS 区块解析**
```ld
SECTIONS
{
.text : { *(.text) } > FLASH // 代码段放入Flash
.data : { *(.data) } > RAM AT > FLASH // 已初始化数据运行时在RAM,加载时在Flash
.bss : { *(.bss) } > RAM // 未初始化数据放入RAM
.stack : { . = . + 0x1000; } > RAM // 栈空间(1KB)
.heap : { . = . + 0x2000; } > RAM AT > RAM // 堆空间(2KB)
}
```
- **关键段说明**:
- `.text`:存储程序代码(如函数体)。
- `.data`:存储已初始化的全局变量(如`int a = 10;`)。
- `.bss`:存储未初始化的全局变量(如`int b;`),运行前自动清零。
- `.stack`:栈空间,用于局部变量和函数调用。
- `.heap`:堆空间,用于动态内存分配(如`malloc`)。
#### 4. **特殊用法**
- **自定义段**:
```ld
.mysection : { KEEP(*(.mysection)) } > RAM // 保留特定段,不被链接器优化
```
- **指定中断向量表位置**:
```ld
.isr_vector : {
. = ALIGN(4);
KEEP(*(.isr_vector)) // 中断向量表必须位于Flash起始地址
. = ALIGN(4);
} > FLASH
```
### STM32存储器布局详解
#### 1. **物理内存映射(以STM32F4为例)**
```
地址范围 大小 描述
0x00000000-0x1FFFFFFF 512MB 代码区(可映射到Flash/SRAM/系统内存)
0x20000000-0x2001FFFF 128KB SRAM(运行时数据)
0x40000000-0x5FFFFFFF 512MB 外设寄存器(APB/AHB总线)
0xE0000000-0xE00FFFFF 1MB 系统控制空间(NVIC、SysTick等)
```
#### 2. **Flash区域详解**
- **起始地址**:0x08000000(实际代码从此处开始)。
- **典型布局**:
```
0x08000000-0x08000100 中断向量表
0x08000100-0x08080000 程序代码(.text)
0x08080000-0x08088000 已初始化数据(.data加载时位置)
```
#### 3. **RAM区域详解**
- **起始地址**:0x20000000。
- **典型布局**:
```
0x20000000-0x20000100 已初始化数据(.data运行时位置)
0x20000100-0x20000200 未初始化数据(.bss)
0x20000200-0x20001200 栈空间(向下增长)
0x20001200-0x20010000 堆空间(向上增长)
```
#### 4. **外设映射区(0x40000000起)**
- **GPIO控制器**:0x40020000-0x40025000(如GPIOA基址0x40020000)。
- **USART1**:0x40011000-0x40011400。
- **定时器(TIM2)**:0x40000000-0x40000400。
- **访问示例**:
```c
#define GPIOA_BASE 0x40020000
#define GPIOA_MODER (*(volatile uint32_t*)(GPIOA_BASE + 0x00)) // 模式寄存器
GPIOA_MODER |= 0x01; // PA0设为输出模式
```
### 编译与链接流程
#### 1. **编译阶段**
```
源代码(.c/.cpp) → 预处理器 → 编译器 → 汇编代码(.s) → 汇编器 → 目标文件(.o)
```
- **关键步骤**:
- 预处理器处理`#include`、`#define`等指令。
- 编译器将代码转换为汇编语言。
- 汇编器生成机器码(目标文件)。
#### 2. **链接阶段**
```
多个目标文件(.o) + 库文件(.a/.lib) → 链接器 → 可执行文件(.elf) → 格式转换器 → 固件文件(.bin/.hex)
```
- **链接器工作**:
1. 合并所有目标文件的段(如.text、.data)。
2. 解析符号引用(如函数调用、全局变量)。
3. 根据链接脚本分配地址。
4. 生成最终可执行文件。
#### 3. **烧录阶段**
- 工具:ST-Link、J-Link、OpenOCD等。
- 流程:将.bin/.hex文件写入MCU的Flash起始地址(如0x08000000)。
### 常见问题与调试技巧
#### 1. **链接错误**
- **符号未定义**:
- 原因:调用未实现的函数或使用未定义的变量。
- 解决:检查函数名拼写,确保目标文件包含该符号。
- **内存溢出**:
- 原因:代码或数据量超过Flash/RAM大小。
- 解决:优化代码,减少全局变量,或更换更大容量的MCU。
#### 2. **调试工具**
- **反汇编工具**:
```bash
arm-none-eabi-objdump -d main.elf # 生成反汇编代码
```
- **查看内存分布**:
```bash
arm-none-eabi-size main.elf # 显示各段大小
```
输出示例:
```
text data bss dec hex filename
1234 56 789 2079 81F main.elf
```
#### 3. **链接脚本调试技巧**
- 添加自定义段:
```ld
.debug_info : { *(.debug_info) } > RAM // 将调试信息放入RAM
```
- 使用`KEEP`防止符号被优化:
```ld
.vectors : { KEEP(*(.vectors)) } > FLASH // 保留中断向量表
```
### 面试高频问题
1. **.data和.bss的区别**:
- `.data`存储已初始化的全局变量,占用Flash和RAM;
- `.bss`存储未初始化的全局变量,仅占用RAM(运行前自动清零)。
2. **如何减小代码体积**:
- 使用IAR等优化能力更强的编译器。
- 移除无用代码(如未使用的函数)。
- 压缩常量数据(如图片、字体)。
3. **链接脚本中AT关键字的作用**:
- `AT`指定段的加载地址,如`.data > RAM AT > FLASH`表示:
- 运行时在RAM(0x20000000),但加载时从Flash(0x08080000)复制到RAM。
---
### 芯片数据手册阅读方法
#### 为什么重要?
芯片数据手册(Datasheet)和参考手册(Reference Manual)是开发嵌入式系统时的核心参考材料,了解外设功能、寄存器地址、时钟结构、中断号、引脚复用等关键信息。
#### 典型结构(以 STM32 为例):
| 部分 | 说明 |
| --------------------------- | -------------------------------- |
| Features | 简要功能描述,例如内核型号、Flash/RAM 容量、外设数量等 |
| Block Diagram | 芯片整体结构图 |
| Pinout / Alternate Function | 引脚定义和复用功能说明 |
| Electrical Characteristics | 电源、电压、电流、温度范围等参数 |
| Memory Map | 内存地址映射(Flash、SRAM、外设地址区间) |
| Peripherals | 每个外设的寄存器结构与配置方法 |
#### 阅读技巧:
* **先看 Block Diagram 和内存映射图**,了解系统架构。
* **按功能模块查阅**:比如用 UART,就查看 USART 章节。
* **关注寄存器描述表格**:查看寄存器地址、每个位的含义、读写属性(R/W)和复位值。
* **善用搜索关键词**:如 `RCC_APB2ENR`,快速定位外设时钟控制相关信息。
---
### 向量表的定义与重定向
#### 什么是向量表?
向量表是处理器在启动时用来获取异常/中断服务函数入口地址的数组,通常放在 Flash 起始地址(如 `0x0800 0000`)或 RAM 中。
每个项为一个 **函数指针**,比如:
```c
typedef void(*ISR_Handler)(void);
const ISR_Handler vector_table[] __attribute__((section(".isr_vector"))) = {
(ISR_Handler)&_estack, // 初始栈顶指针
Reset_Handler, // Reset
NMI_Handler, // NMI
HardFault_Handler, // HardFault
...
};
```
#### 重定向方法(常用于 Bootloader 或自定义中断):
**1. 修改中断处理函数指针:**
```c
__attribute__((section(".vector_table")))
void (*my_vector_table[])(void) = {
... // 自定义中断处理函数
};
```
**2. 使用 `SCB->VTOR`(Vector Table Offset Register)更改向量表地址:**
```c
#include "core_cm4.h"
SCB->VTOR = (uint32_t)my_vector_table;
```
> 注意:新表地址必须对齐 0x100(最低 8 位为 0)
#### 应用场景:
* Bootloader 跳转到 App 时的向量表切换
* 定制中断处理逻辑
* 启动阶段将向量表从 Flash 重定向到 RAM(以支持运行时修改)
---
### ROM 启动 vs RAM 启动的差异
#### 启动方式的定义:
启动方式决定系统复位后,**从哪块内存的地址开始执行程序**,通常与 Boot Mode 管脚或 Boot 配置位有关。
#### ROM 启动(Flash 启动):
* CPU 从 Flash 起始地址(如 `0x0800 0000`)加载向量表与指令
* 适用于生产烧录版本
* 启动速度快,代码执行稳定
#### RAM 启动:
* CPU 从 SRAM 地址(如 `0x2000 0000`)启动
* 适用于调试、自定义 Bootloader 或通过 JTAG 加载代码场景
* 启动前通常需要拷贝一段程序到 RAM(由 Boot ROM、调试器或引导代码完成)
#### 使用差异:
| 项目 | ROM 启动 | RAM 启动 |
| ----- | -------------- | --------------------- |
| 程序位置 | 编译链接到 Flash 区域 | 编译链接到 RAM 区域 |
| 向量表位置 | 默认在 Flash | 需手动配置向量表并设置 SCB->VTOR |
| 应用场景 | 量产版本、正常运行 | Bootloader、自举加载、调试 |
#### 链接脚本修改示例(GCC):
* ROM 启动:
```ld
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K
```
* RAM 启动:
```ld
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 64K
```