Skip to content

第二层:嵌入式系统基础知识

这一章用于建立嵌入式系统的整体认知。它回答的是“嵌入式系统由什么组成、如何启动、如何调试、如何理解芯片手册与工具链”这些根问题。

建议学习目标:

  • 建立 MCU、存储器、外设、传感器、通信模块之间的系统视角。
  • 理解从上电、启动到应用运行的基本链路。
  • 能读懂基础链接脚本、启动文件和芯片数据手册中的关键字段。
  • 对后续驱动开发、RTOS 和 Embedded Linux 学习形成背景认知。

阅读建议:先看系统构成与架构,再看启动流程、工具链和数据手册阅读方法。


嵌入式系统概览

嵌入式系统定义与特点

嵌入式系统是为特定任务设计的专用计算系统,通常集成在设备内部,直接服务于控制、采集、通信、显示或执行等功能。

典型特征:

  • 专用性强:面向单一或有限场景优化,而不是做通用计算。
  • 资源受限:CPU 主频、内存、存储空间和功耗通常受到严格限制。
  • 强调实时性:很多系统要求在确定时间内完成响应。
  • 高可靠性:工业、汽车、医疗等场景对稳定性要求极高。
  • 软硬件耦合紧密:代码常直接与寄存器、外设和总线交互。

嵌入式系统并不总是“小而简单”。从 8 位 MCU 到运行 Linux 的多核 SoC,都属于嵌入式系统范畴,只是复杂度和工程方法不同。

系统构成(MCU、存储器、传感器、外设)

一个典型嵌入式系统通常由以下模块组成:

模块作用
MCU / MPU / SoC运算、控制和调度核心
Flash / EEPROM / NAND / NOR程序和持久化数据存储
SRAM / DDR运行时数据和缓冲区
GPIO / UART / SPI / I2C / ADC / PWM与外部设备交互
传感器采集环境、姿态、位置、电流等数据
通信模块Wi-Fi、BLE、LoRa、CAN、以太网、4G/5G 等
电源管理稳压、充电、低功耗切换和电池管理

可以把它理解为三层结构:

  1. 计算与控制核心
  2. 数据采集与通信接口
  3. 电源与系统支撑部分

MCU(微控制器):嵌入式系统的心脏

MCU 负责执行程序、处理中断、访问外设和调度系统行为。

常见架构:

  • ARM Cortex-M:当前 MCU 主流,常见于 STM32、GD32、nRF、NXP 等系列。
  • RISC-V:开源指令集,近年来快速增长。
  • AVR / 8051:传统 8 位 MCU,适合低成本或教学场景。

选择 MCU 时常看的参数:

  • 主频和内核类型
  • Flash / SRAM 容量
  • 外设数量和种类
  • 功耗水平
  • 封装、成本和供货情况
  • 工具链和社区支持

对于初学者,最重要的不是记住某个型号,而是理解:MCU 的差异主要体现在“算力、存储、外设和功耗”四个维度。

存储器:程序与数据的载体

嵌入式系统中常见的存储器包括以下几类:

1. Flash

  • 通常用于存放程序代码和常量数据
  • 掉电不丢失
  • 可分为片内 Flash 和外部 Flash

2. SRAM

  • 用于运行时变量、栈、堆和缓冲区
  • 速度快,但容量通常有限
  • 掉电后数据丢失

3. EEPROM / FRAM

  • 适合存放参数、校准值、设备 ID 等少量关键数据
  • 擦写寿命通常比 Flash 更适合做参数存储

4. 外部存储

  • NAND Flash、eMMC、SD 卡等,适合较大容量场景
  • 常见于 Embedded Linux 或带文件系统的设备

工程上要重点理解三件事:

  • 程序通常放在哪
  • 变量和栈通常放在哪
  • 参数和日志适合放在哪

外设接口:与外部世界的桥梁

外设接口决定了 MCU 如何连接传感器、执行器和其他芯片。

常见接口:

接口特点典型用途
GPIO最基础的数字输入输出按键、LED、中断输入
UART / USART点对点异步串口调试输出、模块通信
SPI全双工、速度快显示屏、Flash、高速传感器
I2C两线总线、便于挂多个设备温湿度、IMU、RTC
ADC模拟量采样电压、电流、传感器输出
PWM占空比控制电机、蜂鸣器、LED 调光
CAN / RS-485 / Ethernet工业与网络通信车载、工控、联网设备

学习外设时建议始终带着这几个问题:

  • 时钟从哪里来
  • 引脚如何复用
  • 数据如何收发
  • 中断或 DMA 怎样配合
  • 出错时如何定位

传感器:感知物理世界的窗口

传感器把外部物理量转换成可处理的电信号或数字数据。

常见类型:

  • 环境类:温湿度、气压、光照、空气质量
  • 运动类:加速度计、陀螺仪、磁力计
  • 电气类:电压、电流、功率、霍尔传感器
  • 人机类:触摸、红外、人体存在检测

接入方式一般分为两类:

  • 数字接口:I2C、SPI、UART
  • 模拟接口:通过 ADC 采样

工程重点不只是“接上能读”,还包括:

  • 供电与噪声
  • 采样频率
  • 校准
  • 滤波和异常值处理

通信模块:连接万物的纽带

通信模块决定设备如何与外部系统交换数据。

按距离和用途可粗略分为:

  • 短距离:UART、SPI、I2C、USB
  • 局域无线:Wi-Fi、BLE
  • 广域低功耗:LoRa、NB-IoT
  • 工业网络:CAN、RS-485、Modbus
  • 高带宽网络:以太网、4G/5G

选择通信方式时主要看:

  • 数据量大小
  • 实时性要求
  • 传输距离
  • 功耗预算
  • 成本和部署环境

电源管理:续航与稳定性的保障

电源管理不仅关系到续航,还直接影响系统稳定性。

典型关注点:

  • 电源输入范围
  • 稳压方式:LDO 还是 DC-DC
  • 峰值电流和压降
  • 唤醒源和低功耗模式
  • 充电管理和电池保护

很多“软件问题”本质上是电源问题,例如:

  • 上电不稳定
  • 通信时随机复位
  • 电机启动导致掉电
  • 休眠功耗异常

系统集成与典型架构

一个常见嵌入式系统通常可抽象为三层:

text
应用层     -> 业务逻辑、状态机、控制策略
驱动层     -> 外设访问、协议封装、中断处理
硬件层     -> MCU、存储器、传感器、执行器、电源

在复杂项目中,还会加入:

  • BSP(板级支持包)
  • 中间件(文件系统、网络协议栈、GUI、RTOS)
  • 生产诊断和升级机制

开发与调试工具

常见工具可以分为四类:

  1. 开发环境:Keil、STM32CubeIDE、VS Code、CLion
  2. 配置工具:STM32CubeMX、PinMux、设备树工具
  3. 下载调试:ST-Link、J-Link、OpenOCD、GDB
  4. 硬件测量:逻辑分析仪、示波器、电流表

学习时不要把工具当成知识本身,而要关注它们分别解决什么问题。


架构与启动流程

Cortex-M 内核结构

Cortex-M 是最常见的 MCU 内核系列之一。理解其核心结构有助于理解中断、异常和启动流程。

关键组成:

  • 通用寄存器:R0-R12
  • 栈指针:MSP / PSP
  • 链接寄存器:LR
  • 程序计数器:PC
  • 程序状态寄存器:xPSR
  • NVIC:中断控制器
  • SysTick:系统节拍定时器

对应用开发最重要的是:

  • 中断进入时栈如何保存现场
  • 异常向量如何跳转
  • 主栈和进程栈的差异

启动文件 Startup.s

启动文件通常由汇编编写,负责完成上电后的最早期初始化。

典型职责:

  • 设置初始栈顶
  • 定义中断向量表
  • 调用系统初始化函数
  • 清零 .bss
  • 拷贝 .data
  • 最终跳转到 main

示例:

asm
Reset_Handler:
    LDR   R0, =_estack
    MOV   SP, R0
    BL    SystemInit
    BL    main

启动流程简要

一个 MCU 上电后的典型启动顺序如下:

  1. 上电或复位
  2. CPU 从固定启动地址取指
  3. 读取向量表中的栈顶与复位入口
  4. 执行 Reset_Handler
  5. 初始化内存段和系统时钟
  6. 调用 main

如果没有理解这条链路,后续学习 Bootloader、RAM 启动、中断重定向时会很吃力。


编译器与链接器

常用嵌入式工具链

嵌入式开发常用的是交叉编译工具链,即“在 PC 上编译,给目标芯片运行”。

常见工具链:

  • arm-none-eabi-gcc
  • Keil ARM Compiler
  • IAR EWARM
  • RISC-V GCC Toolchain

工具链通常包含:

  • 编译器
  • 汇编器
  • 链接器
  • 头文件和运行时库
  • 调试工具

链接脚本(ld)示例

链接脚本用于告诉链接器:程序的代码段、数据段、栈和堆应放到哪段存储器里。

典型片段:

ld
MEMORY
{
  FLASH (rx)  : ORIGIN = 0x08000000, LENGTH = 512K
  RAM   (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
}

它直接决定了:

  • 程序是否能放进 Flash
  • .data / .bss 是否落在正确 RAM
  • 栈和堆是否有足够空间

存储器布局图(STM32 示例)

一个常见 STM32 工程中的布局可以粗略理解为:

text
Flash:
  [向量表][代码段 .text][只读数据 .rodata]

RAM:
  [.data][.bss][heap][stack]

开发时经常遇到的错误:

  • Flash 溢出
  • RAM 溢出
  • 栈过小导致 HardFault
  • 链接地址与实际启动地址不匹配

芯片数据手册阅读方法

典型结构(以 STM32 为例)

一套完整芯片资料通常包括:

  • Datasheet:电气参数、封装、引脚定义、工作范围
  • Reference Manual:寄存器、外设详细说明
  • Programming Manual:内核和编程模型
  • Errata:已知问题
  • Application Note:应用建议

阅读技巧

建议按问题来读,而不是从头到尾顺序翻。

例如:

  • 查引脚复用:看 Datasheet 的 Pinout / Alternate Function 表
  • 查 UART 如何初始化:看 Reference Manual 的 USART 章节
  • 查中断号:看向量表或 NVIC 对应章节
  • 查低功耗模式:看电源管理与功耗章节

高效阅读习惯:

  • 先定位章节结构
  • 再查寄存器位定义
  • 最后对照库函数或初始化代码验证

向量表的定义与重定向

什么是向量表

向量表本质上是一个函数地址表。它记录了复位处理函数和各类异常、中断服务函数的入口地址。

在 Cortex-M 中,向量表开头通常包括:

  1. 初始栈顶地址
  2. Reset_Handler
  3. NMI_Handler
  4. HardFault_Handler
  5. 各种外设中断入口

重定向方法(常用于 Bootloader 或自定义中断)

当使用 Bootloader 或多应用布局时,应用程序的向量表往往不在默认地址,需要通过 SCB->VTOR 重定向。

示例:

c
SCB->VTOR = 0x08010000;

常见场景:

  • Bootloader 跳转到 App
  • 多固件分区
  • RAM 启动调试

ROM 启动 vs RAM 启动的差异

ROM 启动(Flash 启动)

这是量产环境下最常见的启动方式。

特点:

  • 程序固化在 Flash 中
  • 上电即可执行
  • 可靠性高
  • 适合正式发布版本

RAM 启动

RAM 启动常用于调试、下载器或特定快速加载场景。

特点:

  • 程序先被加载到 RAM
  • 执行速度可能更高
  • 断电即丢失
  • 常用于调试或特定自举流程

使用差异

项目ROM 启动RAM 启动
存放位置FlashRAM
掉电保留
常见用途正式固件调试、Bootloader、特殊加载
向量表位置默认在 Flash常需手动重定向

链接脚本示例:

ld
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K
RAM   (rwx): ORIGIN = 0x20000000, LENGTH = 64K

本章小结

这一章的重点不是背概念,而是建立“系统是如何被组织起来的”这条主线。只要把系统构成、启动流程、向量表、链接脚本和工具链之间的关系理清,后续做驱动、RTOS 和 Bootloader 时会更顺畅。

以 GitHub Pages 发布,使用 VitePress 构建。