咖啡图片
正在将巧克力泡入咖啡
ntainer" style="display: none">
文章

嵌入式系统

知识储备

嵌入式系统

这篇笔记从第一性原理的角度出发,以 STM32 为例,介绍嵌入式系统的架构和工作原理。

处理器内核

处理器内核是嵌入式芯片中最核心的硬件单元,承担了计算机系统中 CPU 的任务,负责指令的执行。

内核构成

以自底向上的逻辑介绍最简单的处理器内核如何构成:由组合逻辑电路构成加法器、减法器(加法器配合补码)、布尔运算器和移位运算器这些最基本的运算单元,再加上多路选择器 Mux,就构成了算数逻辑单元 ALU。由译码器和一些控制逻辑(如 FSM)的实现构成控制单元 CU,最后再加上寄存器组,包含用于追踪程序运行位置的指令地址寄存器 PC 和存放当前指令的指令寄存器 IR;这样就构成了最简单的处理器内核。

指令的执行过程

将操作编码为一个操作码,一条完整的指令由操作码和操作数构成。 内核执行指令的工作流程为:PC 送地址、对应地址的指令传输到 IR 中、控制单元译码、将相应的操作数送入 ALU 并执行运算,执行结束 PC 自增 1。操作码的集合称为指令集。修改 PC 存储的值,可实现程序的顺序执行、跳转、循环、过程调用和中断服务等功能。处理器访问内存时候需要规定高字节和低字节在内存中的排列顺序,也就是所谓的大小端。其中小端模式指高字节放高地址、低字节放低地址,大端模式反之。大小端由处理器内核架构而非内存决定,内存本身是没有顺序的字节数组,但处理器规定的大小端模式最终会体现在内存的数据布局上。

由于处理器的执行速度远快于存取指令和数据的速度,为了提高计算机系统的工作效率,处理器在设计时都会使用流水线技术,将一条指令的执行过程拆分成多个阶段,不同阶段并行处理不同的指令,提高处理器的吞吐量;具体实现上,处理器中存在指令预取队列 IPQ,处理器从逻辑上划分为执行单元 EU 和总线接口单元 BIU,总线接口单元专门负责与内存或 IO 接口之间的数据传送,不断取得指令送入指令预取队列,并保证指令预取队列尽可能处于满载状态;而执行单元只负责从指令预取队列中获取指令并执行。流水线技术既提高了处理器的效率,也变相降低了对存储器存取速度的要求。

内核架构和指令集架构

实际上,应该是先有人为规定的指令集架构(Instruction Set Architecture)作为处理器的软件抽象,再有了不同的处理器内核架构,芯片设计前,需要选择定义好的指令集架构(ISA)。指令集架构包含了指令集这个操作集合、寄存器布局、寻址模式、数据类型、异常/中断等一整套完整的对外规范。指令集架构按指令长度和复杂度分为复杂指令集(CISC)和精简指令集(RISC)。典型的指令集架构有 x86、ARM(AArch32、AArch64)、RISC-V 等。规定好指令集架构之后,有不同处理器内核架构(又称为微架构)来实现它们,如流水线、乱序执行、分支预测、缓存层次结构等。典型的内核架构有 Intel Core、AMD Zen 系列、ARM Cortex 系列。市面上最主流的芯片架构一是大部分主流电脑 CPU 使用的 X86,突出高性能;二是手机 CPU 和嵌入式 MCU 中使用的 ARM,突出低功耗。

存储架构

除了执行指令的处理器之外,用于存放程序和数据的硬件结构是构成嵌入式系统的另一个核心部件。为了更完整地构建存储系统的知识网络,此处在嵌入式系统的存储上稍加扩展。另外,STM32 使用哈佛结构,即程序和数据分开存储。

存储器结构

存储单元在内存中的位置称为地址,选中存储单元的过程称为寻址,狭义的存储器指地址总线可寻址访问的内存空间,不包括处理器和外设中的寄存器。一般来说,存储容量指存储单元的数量,而内存空间/存储空间/寻址范围与地址总线宽度有关,地址总线的宽度决定了处理器能直接访问的内存单元的数量。

时序逻辑电路提供了存储能力,锁存器是最简单的存储电路,可存储一位数据;并排使用多个锁存器就构成了寄存器,这个寄存器存储信息的位数称为位宽。对于更大的存储需求,使用矩阵存储结构来降低使用的线数,要启用某个锁存器,就打开相应的行线和列线。将这个结构抽象成一个内存块,其输入为行列地址,输出为一位数据,再加上允许读写的控制信号。多个这样的内存块并排使用即可选中一个寄存器,实现一个或多个字节的读写。通过不断把内存打包扩大到更大规模,同时增加地址长度,就构成了 SRAM 内存结构。除了锁存器外,还可以用电容器,电荷捕获或忆阻器等基本单元实现内存结构,它们分别对应着 DRAM,闪存和 NVRAM。

存储器芯片内部的存储矩阵有字节结构和位结构两种,字节结构即每个存储单元存放一个字节,每个字节 8 位,用 N\(\times\)8 来描述;位结构即每个存储单元放一个或几个位,用 N\(\times\)位数来描述。存储器的扩展方式有位扩展和字扩展两种,当存储芯片的字长小于需要时进行位扩展,即增加数据线数量,提高访问速度;当存储芯片容量小于需要时进行字扩展,即提升存储容量。

存储器层次

存储器按照功能分为易失性存储器(Volatile Memory)和非易失性存储器(Non-Volatile Memory)两类。它们在逻辑上的分类和这些类别下的典型实现标准如下,其中浅绿色是抽象的逻辑分类,浅紫色是具体的实现标准。在这个分类体系之外,SPI Flash 或 QSPI Flash 是 NOR Flash 的一种接口封装方式,而非一种具体的存储结构。SD 卡(SD 协议)、U 盘(USB 协议)、SSD(SATA/NVMe 协议)、eMMC(eMMC 协议)本质上都是 NAND Flash + 控制器 + 接口协议的组合。

不同的存储器有不同的特点,其中双极型晶体管 BJT 工艺制作的 RAM 速度快价格高功耗大,一般用作高速缓存 Cache;半导体场效应管 MOSFET 工艺制作的 RAM 反之,一般用作 SRAM 和 DRAM,SRAM 由双稳态触发器 FF 构成,不需要定时充电刷新;DRAM 用电容存储信息,需要定时充电刷新,集成度高,可用作内存条。缓存 Cache 是 CPU 与主存之间的高速小容量存储器,用于提高 CPU 的工作效率,其工作原理是数据访问的局部性原理,即在较短的时间间隔内,CPU 访问的内存地址往往集中在存储器很小的范围内。将这部分内存批量读取到缓存中,CPU 需要时先在缓存中找,找到称为命中,未找到则更新缓存以提高命中率。命中率与缓存容量、使用的算法和运行的程序特点都有关,使用多级缓存结构可以提高命中率。

ROM 分 Mask ROM、PROM、EPROM、EEPROM,目前市面上都是 EEPROM。Flash 的归属方式比较模糊,既可认为是一种特殊的 EEPROM,也可认为是与 ROM 并列的一种非易失性存储器。NOR Flash 的特点包括:每个存储单元直接挂在位线,支持随机访问,因此可以直接运行代码(但速度不如 RAM),容量在几 MB~百 MB 级,一般用于存放 BootLoader、固件、配置数据。NAND Flash 的特点包括:一组存储单元串在一起,按块/页访问,容量在 MB~TB 级。NAND Flash 按单元存储位数分为:SLC(1 bit/单元,快,寿命长,贵)、MLC(2 bit/单元,容量大,寿命一般)、TLC(3 bit/单元,消费级 SSD 常见)、QLC(4 bit/单元,超大容量,寿命低)。

实际计算机系统的存储架构会综合使用多种存储器,将两个及以上的速度、容量和价格各不相同的存储器组成一个存储器系统,从软件抽象上看,这个系统的速度、容量和价格都接近最好的那个。对于 STM32 来说,片上存储只有寄存器、SRAM、Mask ROM、NOR Flash,此外可以外挂 SD 卡等外部存储。代码存储在 Nor Flash 上,临时变量和栈运行在 SRAM 上。MCU 的运行频率在几十 MHz 到一两百 MHz,Nor Flash 的读写速度能够满足 CPU 的读取需求,但写入速度较慢,因此引入了 SRAM 作为临时变量和栈的运行空间。

存储器映射

地址有物理地址和逻辑地址之分,CPU 送到地址总线的地址称为物理地址,是硬件存储单元在存储器中的真实物理位置,存储器的操作完全基于物理地址;编程时使用的称为逻辑地址或虚拟地址,对存储器分配逻辑地址的过程称为存储器映射,由内存管理单元(MMU)这一电路一定的内存映射方式完成。之所以需要进行映射,一方面是为了让 CPU 以统一的方式访问不同的外设,在内存映射下,所有外设都被视为一块内存,操作这块内存即可操作相应的外设;另一方面是为了实现进程隔离、内存保护和虚拟内存的扩展等操作系统层面的管理方式。

STM32 有 32 根地址线,寻址范围为 0x0000 0000~0xFFFF FFFF,共计 4G 个地址的二进制数,一个地址对应一个字节的存储单元(注意是按 byte 而不是 bit 编制),故 32 位的地址线可以寻址 4GB 的逻辑内存。(注意:STM32 的 32 不是指有 32 根地址线,而是指 MCU 内核在处理数据时每次可以处理的数据位宽为 32bit,也由于这个原因,STM32 内部寄存器都是 32 位的)

寄存器映射

STM32 使用统一编址方式,外设和存储器被统一映射到逻辑内存上。在软件上统一用指针操作内存的方式即可访问外设对应的寄存器和相应的硬件电路,给寄存器分配逻辑地址并对寄存器命名的操作称为寄存器映射。片上外设以四个字节 32bit 为一个单元,每个单元对应不同的功能。

STM32 中的寄存器分类如下

输入输出系统

输入输出系统可以按层次分为硬件物理层、外设驱动层、通讯协议层、软件应用层。

GPIO

GPIO(General Purpose Input Output,通用输入输出端口),负责采集外部器件的信息或者控制外部器件工作,是最基础的输入输出外设,也是其它外设实现的物理基础,对外表现为 MCU 封装的芯片引脚。

输入输出系统需要解决速度匹配 CPU、信号电平和驱动能力、信号形式匹配(模拟/数字)、信号格式(串行/并行)、时需配合等一系列问题;数据输入接口要求具有三态输出能力,数据输出接口要求具有数据锁存能力。逻辑电平到物理电平的转化就是由 GPIO 和相应的驱动电路实现的。GPIO 的电路结构设计如下

image-20250525102552762

这样的 GPIO 电路可以通过内部寄存器配置为八种模式,分别是: 输入浮空,输入上拉,输入下拉,模拟,上拉或下拉的开漏输出(用于共用总线的信号),上拉或下拉的推挽输出(为增强驱动能力可以上拉),上拉或下拉的复用功能推挽,上拉或下拉的复用功能开漏。IO 端口的输入输出由 GPIO 外设控制时称为通用;IO 端口的输入输出由其它非 GPIO 外设控制时称为复用。

STM32 中的 GPIO 引脚以组的形式存在,组数视芯片而定,每组包含 16 个 IO 引脚和 4 个 32 位寄存器。各个寄存器的具体配置可查阅手册,此处略去。

外设系统

在 STM32 中,外设通常指芯片内部用于实现特定功能的硬件模块,实际上指的是芯片内部的外设控制器。完整的外设系统由外设控制器实现控制逻辑,通过 GPIO 输出来驱动如传感器、电机舵机等执行器、按键显示器等人机交互设备等外部设备。

外设控制器以晶体管硬件固化了实现具体操作的功能模块,这是外设驱动的硬件部分,在软件上一般通过硬件抽象层(Hardware Abstract Layer,简称 HAL)以配置寄存器的方式使用。所有外设在 ST 提供的参考手册中,都会给出对应的工作原理框图和寄存器配置说明(即每一位代表什么)。实际开发中一般不会直接跟寄存器打交道,而是使用 HAL 库和 CubeMX 这样的工具来配置外设,隐藏硬件细节而专注于应用层开发。

外设接口通过引脚复用挂接在 GPIO 上,大致可分为串行通信接口(USART、UART、SPI、I2C、CAN、USB)、并行接口(FSMC、FMC、LCD)、模拟接口(ADC、DAC)、定时器接口(TIMx、输入捕获、输出比较)、控制接口(EXTI、PWM 控制、电机驱动等)。每个外设都会从 AHB 上分出来的 APB1 或 APB2 上经过分频器拿到自己的时钟,且基本上所有的外设都配备了相应的中断。

传输方式

基本的输入输出方式,即主机与外设之间数据传送的控制方式有无条件传送、查询传送、中断传送和 DMA 传送四种。无条件传送往往只用于不需要数据准备的外设,软硬件结构简单;查询式传送用于对传送速率要求不高的场合,CPU 与外设交换数据前需要进行查询确认,软件设计简单。外设需要提供设备状态,接口需要提供状态端口。CPU 效率低,速度慢,实时性差。中断式传送只在需要进行数据传送时才中断 CPU 正在进行的工作,CPU 效率高,实时性好,程序相对复杂。前三种方式均需要 CPU 介入,程序的执行限定了传送的最大速度,IO 操作相对较慢。而 DMA 传送则无需 CPU 介入,CPU 暂时出让总线控制权,由专门的 DMA 控制器 DMAC 接管,用于高速大批量数据采集系统。

image-20250401105850977

通讯协议

嵌入式硬件主要关注通信协议的物理层规范和数据链路层协议。信号的比特流转化为物理信号的过程称为调制与编码。

  • 物理层规定物理连接特性,关注协议的硬件实现方式,具体来说包括电压电平标准、信号类型是数字信号/模拟信号、引脚连接为差分信号/单端信号、连接器和线缆类型。
  • 数据链路层负责数据帧组织,将原始比特流打包成一定格式的数据帧,并提供错误检测机制,还会通过握手或阻塞等方式实现流量控制,保证接收方处理的及时性。

通信协议按数据在通信通道上的方向和同时性分为单工、半双工和全双工;按同步方式分为同步和异步;连接方式分点对点连接和总线连接。单工指只能单向传输;半双工指可发送或接收,但不能同时进行;全双工指可同时发送和接收。同步方式指用同步字符完成同步,异步方式用一帧中的起始位和停止位完成同步。

通信速率用比特率和波特率描述。比特率指每秒钟传送的比特数,单位为 bit/s(bps);波特率指每秒钟传送的码元数,单位为 Baud。码元是通信系统中传输的最小信号单位,每个码元可以承载一个或多个比特的信息。比特率 = 波特率 * log2 M ,M 表示每个码元承载的信息量。在二进制系统中,波特率在数值上等于比特率。带宽表征了通信链路的最大数据传输能力,是比特率的理论上限,单位为 bps。

所有串行方式都需要进行数据校验,以解决信号的干扰、衰减以及信号畸变的问题。常见的数据校验的方法有奇偶校验、CRC 校验等。奇偶校验以字节为单位,规定字节中 1 的个数为奇数个或偶数个,可以查出奇数个的错误,不能定位错误位置且不能自动纠错,发现错误则重新传输。CRC 校验以数据块或帧为单位,将数据看作多项式,产生校验码并和数据部分拼接一起传输。

嵌入式中常见的串行通讯协议包括串口通信、I2C、SPI、CAN、USB 等。一般来说串口通信特指 UART 通信 (即常说的 COM 口),是串行通讯的异步形式,而 I2C 和 SPI 可以看作是串行通讯的同步形式。UART、I2C 和 SPI 属于标准的串行通信外设,而 USB 是一种高速串行总线协议,不是简单的串口,而是包含复杂的协议栈(有分层结构、包格式、端点管理等),有专属的寄存器、中断、DMA 等,两者的硬件实现机制完全不同。USB 的其中一种应用是通过 CH340、CP2102 等芯片以虚拟串口的方式模拟串口。

UART 串口通信只需发送、接收和地线三根线,是全双工异步通信,采用 TTL 电平,为点对点通信方式。一帧串口数据包含起始位、数据位、校验位(可选择不校验)和停止位。通信双方约定好采用的波特率,常见波特率有 4800、9600、115200 等。硬件连接时 TX 和 RX 需反接,其输出方式为推挽输出。为了增加传输距离串口通信,又衍生处 RS232 和 RS485 三种不同的电平标准来实现串口通信,RS232 通过电平转换芯片提升抗干扰能力和传输距离,RS485 则将信号转化为差分信号,抗干扰能力更强,可实现一主多从组网通讯。

I2C 用于芯片间通讯,采用一主多从模式,通过时钟线和数据线进行数据传输,是通信线路较少的一对多通信方式,节约了 PCB 布线成本。数据传输时,起始位和停止位有特定的电平跳变要求,时钟线用于同步信号,发送端在时钟高电平时发出数据,接收端在时钟高电平接收数据。设备地址码为七位,可表示 128 种结果,读写数据时需发送相应的控制位和应答信号。I²C 使用开漏输出,避免总线短路问题,通过上拉电阻实现高电平,常见速率有 100KB 比特、400K 比特,受上拉电阻影响,更高速率较难实现。

SPI 用于芯片间通讯,采用一主多从模式,需要片选信号线 CS、时钟信号线 SCK、发送信号线 MOSI 和接收信号线 MISO,共四条信号线。通过片选信号线确定通信从机,数据在时钟信号的上升沿或下降沿采样,具体取决于芯片设定。SPI 是全双工同步通信,通讯速率高,可从几百 BPS 到几兆 BPS 甚至更高,其高速通信得益于时钟线同步和推挽输出的设计。

CAN 总线用于汽车中 ECU 之间的通讯,减少布线复杂度。CAN 协议使用差分信号,抗干扰能力强,传输距离可达 1000 米。CAN 通信的一帧数据包含起始位、识别码、控制位、数据长度码、CRC 校验码等,通过识别码确定接收设备,采用仲裁机制解决总线冲突问题,隐性电平表示逻辑一,显性电平表示逻辑零,总线空闲时为隐性电平。CAN 通信为半双工异步通信,无主机,所有设备逻辑平等,波特率由最小时间片 TQ 和每一位的三个段组成,常见最大波特率为一兆比特每秒。

总线系统

总线系统是连接芯片内 CPU、存储和外设或电路板上各模块的数据通路的信号线,以及相应控制协议的集合。总线上可以同时挂接多个设备,主设备通过总线进行数据传输,从设备按主设备要求工作或接收数据。

总线按信号类型分为数据总线(双向)、地址总线(单向)和控制总线(双向);按层次结构分为前端总线(又称 CPU 总线,负责 CPU 与存储器、IO、控制芯片组之间的信息传输)、系统总线(又称 IO 通道总线,主要表现为扩展插槽,如 PCI 总线、ISA 总线等)、外设总线(与外设接口的总线,如 USB、SATA、RS485 等),其中系统总线和外设总线有统一标准。

image-20250401105300428

STM32 使用 AMBA 标准总线架构(Advanced Microcontroller Bus Architecture),在此基础上,各系列又根据性能分不同的总线矩阵。AMBA 总线是芯片内部总线(SoC 内部总线),用于连接 CPU、内部 SRAM/Flash、DMA、外设控制器等。注意与外设通信总线区分开,外设通信总线是 MCU 与外部设备或内部多外设模块之间的总线。AMBA 架构的总线体系一般包括

  • AHB(高速系统总线,用于 CPU、DMA、存储器、外设之间的高速访问)
  • APB(低速外设总线,用于连接定时器、串口、I2C 等低速外设)
  • AXI(高性能总线,支持并行事务、突发访问,用于 Cortex-M7 / H7 等高端系列,连接 SDRAM、DTCM、外设矩阵)
  • DTCM/ITCM(CPU 的快速专用存储访问通道(紧耦合存储器接口),连接内部高速 SRAM)。

F1 系列使用最简单的两级总线结构,AHB 作为主系统总线,APB1 和 APB2 通过桥接器连接到 AHB。APB1 连接低速外设(TIMx、USARTx、I2Cx、ADC),APB2 连接高速外设(SRAM、DMA、Ethernet、USB、FMC)。

F4 和 F7 系列引入总线矩阵的概念,性能更高。引入 I-Code-Bus(CPU 取指令,连接程序代码区)、D-Code-Bus(CPU 取常量/数据访问,连接 SRAM 和常量区)、System-Bus(连接 DMA、APB、外设),支持并行访问 Flash 和 SRAM。

H7 系列使用双总线域+AXI 总线矩阵,分成三个电源/总线域,支持多主机访问(CPU、DMA、MDMA、USB、ETH 等可同时访问存储器),通过 AXI 总线矩阵实现高带宽、低延迟;支持 Cache、Prefetch、并发访问。

时钟系统

嵌入式系统中的处理器、外设、总线等各个部件都是以时钟信号作为基准工作的。时钟信号的作用,一方面是由于门电路运行有延迟,需要由统一的信号等待门电路稳定之后再触发;另一方面从数据传输的角度也需要用时钟来规定最小数据单位对应的物理信号。嵌入式系统中用晶振提供相对准确的时钟信号,并配合分频器倍频器产生不同频率的时钟域以满足不同设备的运行需求,再由计数器对脉冲计数就构成了定时器。

STM32 的时钟系统构成树形结构,也称时钟树。时钟树的分布路径可以归纳为时钟源->锁相环倍频->SYSCLK->分频器->AHB 和 APB 总线时钟->分频器->各个外设时钟。时钟源包括 HSI、HSE、LSI、LSE,分别为内部和外部的高速和低速时钟。SYSCLK 为主系统时钟,经分频后分别提供给 AHB 总线(HCLK)、APB1 总线(PCLK1)和 APB2(PCLK2),各外设从总线上再经过分频器取得满足频率要求的时钟信号。另外,处理器内核中有一个称为 SysTick(系统滴答)的定时器,通过分频器与 HCLK 连接。

中断系统

中断是由于某种随机事件引起处理器暂时中断主程序,转去处理中断服务子程序,处理完后返回的机制;是为了避免处理器不断检测外设状态,提高处理器利用率,同时实现对特殊任务的实时响应而引入的。引发中断的随机事件称为中断源,分为外部中断源和内部中断源,其中外部中断源由处理器以外的设备引起;两者优先级不同而响应机理相同。中断按是否受 IF 标志位影响分为可屏蔽中断和不可屏蔽中断。中断按来源分为硬中断和软中断,硬中断由外部或片上外设的硬件信号引起,通过中断控制器(NVIC / GIC)进入处理器,如 GPIO 外部中断、定时器溢出中断、串口接受中断等;软中断由处理器执行专门的指令主动触发,常用于系统调用,如 ARM 的 SVC 指令触发 SVC_Handler 用于 RTOS 的系统调用、x86 的 int 0x80 进入 linux 内核等。

中断的处理流程包括:

  • 中断请求:中断源向处理器发起中断请求信号,有边沿请求和电平请求两种,中断信号应保持直到中断被处理为止,且中断得到响应后需及时撤销中断请求信号。

  • 中断判优:又称仲裁,若中断同时产生则按照优先级处理,同级则先来先处理;若中断非同时产生则高优先级中断可以打断低优先级中断,即允许中断嵌套。

  • 中断响应:处理器在每条指令的最后一个时钟周期检测中断信号,若同时满足当前指令执行完、当前无 RESET 或 HOLD 信号、可屏蔽中断未被屏蔽(对可屏蔽中断而言需要满足),则处理器响应中断。响应过程中需要完成:向外设发出信号表示中断得到响应、断点保护(相应寄存器入栈)、获得中断服务程序首地址

  • 中断处理:需要保护的数据入栈,打开可屏蔽中断以允许中断嵌套,进行相关处理,关闭可屏蔽中断以防止在恢复现场前被打断。

  • 中断返回:将栈中的被保护数据和相应寄存器值出栈,恢复现场、调用 IRET 返回主程序

STM32 中使用嵌套向量中断控制器(NVIC)来管理中断,NVIC 接收来自各个外设(包括 EXTI)的中断请求,并根据优先级调度执行相应的中断服务函数(ISR)。中断来源可以分为三类。一是系统中断,即来自内核本身的中断(系统异常),如 NMI、HardFault、SysTick、SVC、PendSV 等;二是外部中断,即由 GPIO 或特定信号线触发的中断,如 EXTI0~EXTI15 中断信号线引发的中断;三是外设中断,即来自各种片上外设(定时器、串口、ADC、DMA 等)的中断,如 TIMx、USARTx、SPIx、DMAx、I2Cx、USB、CAN、ADC 等。EXTI 是外部中断/事件控制器,负责监听来自外部引脚(如 GPIO)或内部信号(如 RTC、USB 等)的变化,产生中断请求(IRQ)或事件信号(Event),并向 NVIC 发出中断请求信号。

事件是与中断类似的处理机制,指的是特定条件满足时,硬件内部产生信号去触发某个外设动作。两者区别在于中断会打断正在执行的程序并进入中断服务函数(ISR),需要 CPU 的参与;而事件不会打断 CPU,也不执行中断服务函数,外设之间直接协作。

启动方式

STM32 提供三种上电后的引导方式:从 flash 引导启动,从 system memory 引导启动 和 从 SRAM 引导启动,根据 BOOT 引脚的电平值选择启动方式,具体来说是将起始地址重映射到不同的存储器上。

  • 从主 Flash 启动:将主 Flash 地址 0x0800 0000 映射到 0x0000 0000,代码启动后相当于从 0x0800 0000 开始运行,使用 JTAG 或者 SWD 模式下载程序时会下载到 Flash 并从中启动。
  • 从系统存储器启动:将系统存储器地址 0x1FFF F000 映射到 0x0000 0000。系统存储器是芯片内部的 ROM 区域,芯片出厂时在其中预置了 Bootloader,称为 ISP 程序。Bootloader 由厂家设置,一般无法修改。
  • 从内置 SRAM 启动:将 SRAM 地址 0x2000 0000 映射到 0x0000 0000,代码启动后相当于从 0x2000 0000 开始运行。由于 SRAM 是易失性存储器,因此这个模式一般仅用于程序调试。

补充说明

HAL 库

HAL 是硬件抽象层的缩写,HAL 库的设计初衷是为了隐藏复杂的寄存器配置细节,让开发人员专注于软件逻辑的编写。因此有必要了解一些 HAL 库的架构逻辑。

HAL 库几大特点

  1. 句柄 Handle:句柄中包含了一个外设在整个项目流程中都要设置的各个成员变量,使用时调用初始化时定义的句柄就好。

  2. MSP 函数:MCU Specific Package,是指和 MCU 相关的初始化,可以配合句柄达到很强的移植性。

  3. Callback 函数:类似于 MSP 函数,用于帮助用户进行应用层的代码编写。以中断为例,HAL 库中断服务程序接管对中断的判断、读出数据到缓冲区,清除中断标志位等等,用户在 Callback 回调函数中编写处理逻辑即可。

HAL 库提供的 API 可以分为四类

  1. 初始化/反初始化:HAL_PPP_Init(), HAL_PPP_DeInit()
  2. IO 操作:HAL_PPP_Read(),HAL_PPP_Write(),HAL_PPP_Transmit(), HAL_PPP_Receive()
  3. 控制:HAL_PPP_Set(),HAL_PPP_Get()
  4. 状态和错误:HAL_PPP_GetState (), HAL_PPP_GetError ()

在此结构下,用户代码的处理主要分为:处理句柄外设(实现用户功能)、处理 MSP、处理各种回调函数三部分

  1. 外设句柄定义:每个外设抽象成了一个称为 ppp_HandleTypeDef 的结构体,其中 ppp 就是每个外设的名字。所有的函数都是工作在 ppp_HandleTypeDef 指针之下。外设句柄支持多实例,即每个外设/模块实例都有自己的句柄。因此,实例资源是独立的下面,以 ADC 为例,外围进程相互通信:该句柄用于管理进程例程之间的共享数据资源。
  2. 三种编程模式:HAL 库将所有的函数模型统一为三种模式:轮询模式、中断模式、DMA 模式(如果外设支持)。其分别对应三种类型的函数。此外,新的 HAL 库架构下统一采用宏的形式对各种中断等进行配置。
  3. 三大回调函数:HAL 库负责整个处理和 MCU 外设的处理逻辑,并将必要部分以回调函数的形式给出到用户,用户只需要在对应的回调函数中做修改即可。

中间件

emWin、LVGL、lwIP、uC/OS、FreeRTOS 都是嵌入式开发中非常重要的中间件或操作系统组件。emWin 全称 SEGGER emWin,是用于嵌入式系统的图形用户界面(GUI)库,需要商业授权;LVGL 是开源嵌入式图形库,适用于资源有限的嵌入式设备,完全开源;lwlP 为一个轻量级的 TCP/IP 协议栈,适用于嵌入式系统,完全开源;uC/OS-II / uC/OS-III 为硬实时内核(RTOS),需要商业授权;FreeRTOS 全称 Free Real-Time Operating System,为轻量级硬实时内核。

位操作

位操作在嵌入式开发中很常见,嵌入式外设一般通过内存映射寄存器控制,每个寄存器通常 16 或 32 位,为了节省内存,一个或几个位代表一个功能开关。另外,位操作在 CPU 上通常翻译为单条指令,没有函数调用开销。在硬件结构上,寄存器本身也是按位寻址的。

为了提高位操作的效率,一些单片机引入了位带映射技术。该技术通过将每个位(bit)与一个单独的内存地址进行映射,使得对该位的操作可以像对内存变量一样进行,从而可以大大简化位操作的流程。它是单片机的一种 “内存地址映射技术”—— 给原始内存(位带区)的每 1 个 bit,都分配一个专属的 “别名地址”(位带别名区),让 “操作 1 个 bit” 和 “操作 1 个普通变量” 一样简单,从而解决传统位操作 “代码复杂、效率低、易出错” 的问题。单片机的 CPU 本身不直接支持 “只修改 1 个 bit”——CPU 最小的操作单位是 “字节(8bit)” 或 “字(16/32bit)”。

参考资料

嵌入式系统设计基础

计算机科学通识

CPU 工作原理

图灵完备

第一性原理构建计算机

计算机硬件基础

STM32 内存分配机制

内存映射和启动方式

STM32 CubeMX 开发

keysking STM32

GPIO 模式

三大通信总线协议

各个通信协议优缺点

嵌入式通信底层逻辑

CRC 校验

HAL 库底层逻辑

本文由作者按照 CC BY 4.0 进行授权
/body>