单片机内核

什么是内核?这是一个很抽象的问题。有时会听到 ARMv7-M、ARMv7-A 架构,又听过 Cortex-M3、Cortex-M4 内核——它们分别是什么?本文以 M3/M4 内核为切入点,介绍内核与架构的基本概念。

本文以文字为主,尽量简单描述。

M3/M4 内核

在这里插入图片描述

先看下面这张M3/M4 内核框图(这里先不用纠结“内核”到底是什么意思)。看到这张图,你可能会产生两个疑问:

  1. 为什么 M3 和 M4 的内核框图可以共用同一张图?
  2. 图中为什么没有外设?

问题一:为什么 M3/M4 共用同一张框图?

查阅 M3 和 M4 的内核手册会发现,它们的内容几乎完全一样——无论是内核寄存器(R0~R15),还是特殊功能寄存器(xPSR、CONTROL 等)。这是因为它们都隶属于 ARMv7-M 架构,因此寄存器、流水线设计等均相同(流水线细节不了解也没关系)。

有兴趣的话可以对比 core_cm3.hcore_cm4.h 两个文件,你会发现它们基本一致。甚至内核中最核心的“内核外设”(区别于芯片厂商添加的片上外设)——如 NVIC、SysTick、MPU、SCB 以及调试相关的 ITM、DWT、IPI——也完全相同,连地址都一样。

这种一致性正是“同属 ARMv7-M 架构”的体现。ARMv7-M 架构规定了内核的流水线设计,并强制包含 NVIC、SysTick 等单元(图中已标出),同时包含可选的 MPU 和调试单元(M3/M4 都实现了)。这些单元的统一地址也是 ARMv7-M 规范的一部分。
在这里插入图片描述

那么,不同之处在哪里?M4 包含浮点运算单元(FPU),而 M3 没有。这是两者最大的差异。为什么一个有 FPU、一个没有,却都属于 ARMv7-M?因为 ARMv7-M 架构允许在系统控制空间(SCS)的地址范围内添加不同的内核单元来实现功能扩展。FPU 的存在与否并未跳出 ARMv7-M 的框架,但有了 FPU 就可以执行浮点运算指令。

至此,第一个问题已解答:M3/M4 共用同一框图,因为它们差异不大且同属 ARMv7-M。但需要注意,ARMv7-M 并不只包含 M3/M4——例如 M7 内核还增加了 Cache。更准确的理解是:M3/M4/M7 都是 ARMv7-M 架构的具体实现子集

问题二:为什么框图中没有外设?

如果对 STM32 有较深入的理解,不难知道:所谓外设,对内核而言就是挂载在地址总线上的一些寄存器。内核通过地址总线访问它们,再通过数据总线读写数据。外设这部分完全由芯片厂商自由设计——这就是为什么即使都是 M3/M4 内核,不同芯片的外设差异却很大。

尽管外设不同,但内核访问它们的方式完全一样(如 LDR、STR 指令),只是寄存器的值设置不同,导致外设的工作模式不同。因此,外设不属于内核的一部分,但地址/数据总线属于内核设计的一部分。

小结:什么是内核?什么是架构?

基于以上分析,可以这样理解:

  • 内核:执行指令所需的硬件支持。
  • 架构:一种内核设计蓝图。

因此:

  • ARMv7-M 提供整体设计框架;
  • Cortex-M3 / M4 / M7 是该框架的具体实现;
  • STM32F407VET6 则是这个具体实现再加上厂商配套的外围设施。

内核寄存器和内核外设

上一小节说到内核是一种具体实现,其中这个具体实现中相同的部分主要是内核寄存器和内核外设,所以我们这里主要介绍以下M4内核,相信读者到这里为止对内核,架构,具体芯片应该有了一定的了解了。

在地址划分中有一块称为“私有外设总线”(PPB)的区域,与内核框图中底部的 PPB 对应,通过专用总线访问。这部分与普通外设不同,包含的单元与内核工作模式紧密相关,我称之为内核外设

本文重点介绍三个核心部分:NVIC、SCB、SysTick。其他部分触类旁通,可自行探索。在此之前,先介绍内核寄存器。

推荐参考《M3/M4 内核权威指南》,讲解非常详细,大多数问题都能从中找到答案。

内核通用寄存器

  • R0~R12:通用目的寄存器,主要用于数据计算和缓存。
  • R13(SP):堆栈指针寄存器。程序运行时自动切换主栈指针(MSP)和进程栈指针(PSP)。
  • R14(LR):连接寄存器,通常保存程序返回地址。
  • R15(PC):程序计数器,始终指向下一条要执行的指令。

在这里插入图片描述

R0~R12 没有太多特别之处,主要用于缓存临时变量。函数调用时部分寄存器用于传递参数。

SP(栈指针) 在物理上只有一个寄存器,但对应两个逻辑指针(MSP 和 PSP),具体使用哪个由特殊寄存器 CONTROL 决定。中断中使用 MSP,裸机程序中也使用 MSP,而在 RTOS 环境下任务运行时使用 PSP。掌握这些基本够用。

LR(连接寄存器) 的使用可分为三种场景:

  1. 中断打断了主程序
  2. 中断嵌套
  3. 函数调用

前两种属于中断场景:

  • 中断发生时会自动将 xPSR, PC, LR, R12, R3, R2, R1, R0 压入当前栈。
  • 中断函数执行结束后,LR 通常保存一个特殊值(称为特殊值1),执行 BX LR 即可自动恢复现场,继续执行主程序。
  • 若发生中断嵌套,硬件同样自动压栈,LR 保存另一个特殊值(特殊值2),执行 BX LR 恢复上一层中断现场。

在这两种中断场景中,LR 存放的是特殊值,真正的返回地址来自压栈的 PC 值。

函数调用场景:

  • 硬件不会自动压栈,需要软件手动压栈(通常保存 R4~R11,或根据需求额外保存)。
  • 此时 LR 保存的是真正的返回地址(上一级函数中下一条指令的地址),最后通过 BX LR 跳转回去。

PC 始终指向下一条要执行的指令。

内核特殊寄存器

1. xPSR(组合程序状态寄存器)

32 位寄存器,反映 CPU 的运算状态和当前异常编号,由三个子寄存器组成:

  • APSR(位 [31:27]):包含 N(负)、Z(零)、C(进位/借位)、V(溢出)、Q(饱和标志,仅 M4/M7 等支持 DSP 的内核有)。
  • EPSR(位 [24]):T(Thumb 位),必须为 1。若清零,执行指令会触发 HardFault(Cortex-M 只支持 Thumb 指令集)。
  • IPSR(位 [8:0]):Exception Number,记录当前正在执行的异常/中断编号(0 表示主程序,16 以上为外部中断)。
2. PRIMASK(优先级屏蔽寄存器)
  • 位数:1 位
  • 含义:“全局中断开关”
  • 作用:置 1 时屏蔽所有可配置优先级的中断,只有 NMI 和 HardFault 能抢占。
  • 常用场景:进入临界区(Critical Section)时调用 __disable_irq() 将其置 1。
3. FAULTMASK(异常屏蔽寄存器)
  • 位数:1 位
  • 含义:“更高级别的屏蔽”
  • 作用:比 PRIMASK 更强。置 1 时甚至能屏蔽 HardFault,只有 NMI 可以抢占。
  • 常用场景:极少数极其关键的系统错误处理代码。
4. BASEPRI(基本优先级屏蔽寄存器)
  • 位数:8 位(实际有效位数由芯片厂商决定,通常为高 3 或 4 位)
  • 含义:“优先级阈值过滤”
  • 作用:设定一个阈值。例如设为 0x50,所有优先级数值大于等于 0x50(即优先级较低)的中断都会被屏蔽,而优先级高于 0x50 的中断仍可触发。
  • 常用场景:RTOS(如 FreeRTOS)用于中断嵌套管理,既能保护内核任务,又不像 PRIMASK 那样一刀切。
5. CONTROL(控制寄存器)

决定 CPU 的“身份”和“装备”:

  • nPRIV(位 [0]):0 = 特权级,1 = 用户级(非特权,限制访问某些特殊寄存器)。
  • SPSEL(位 [1]):0 = 使用主栈指针(MSP),1 = 使用进程栈指针(PSP)。
  • FPCA(位 [2])(仅带 FPU 的内核):表示当前是否使用浮点单元。若为 1,中断入栈时会额外压入浮点寄存器。

这些寄存器没有内存地址,只能通过 MRS(读)和 MSR(写)指令访问。

内核外设

寄存器部分较为枯燥,此处仅列出各内核外设的寄存器结构(直接从 core_cm4.h 复制)。如需深入了解,可借助 AI 工具或查阅手册。(其实这个代码在使用cubemx创建的工程中就会生成的,也可直接在评论区@我。这个手册网上还是比较好找的)

1. SCB 寄存器
typedef struct
{
  __IM  uint32_t CPUID;                  /*!< Offset: 0x000 (R/ )  CPUID Base Register */
  __IOM uint32_t ICSR;                   /*!< Offset: 0x004 (R/W)  Interrupt Control and State Register */
  __IOM uint32_t VTOR;                   /*!< Offset: 0x008 (R/W)  Vector Table Offset Register */
  __IOM uint32_t AIRCR;                  /*!< Offset: 0x00C (R/W)  Application Interrupt and Reset Control Register */
  __IOM uint32_t SCR;                    /*!< Offset: 0x010 (R/W)  System Control Register */
  __IOM uint32_t CCR;                    /*!< Offset: 0x014 (R/W)  Configuration Control Register */
  __IOM uint8_t  SHP[12U];               /*!< Offset: 0x018 (R/W)  System Handlers Priority Registers (4-7, 8-11, 12-15) */
  __IOM uint32_t SHCSR;                  /*!< Offset: 0x024 (R/W)  System Handler Control and State Register */
  __IOM uint32_t CFSR;                   /*!< Offset: 0x028 (R/W)  Configurable Fault Status Register */
  __IOM uint32_t HFSR;                   /*!< Offset: 0x02C (R/W)  HardFault Status Register */
  __IOM uint32_t DFSR;                   /*!< Offset: 0x030 (R/W)  Debug Fault Status Register */
  __IOM uint32_t MMFAR;                  /*!< Offset: 0x034 (R/W)  MemManage Fault Address Register */
  __IOM uint32_t BFAR;                   /*!< Offset: 0x038 (R/W)  BusFault Address Register */
  __IOM uint32_t AFSR;                   /*!< Offset: 0x03C (R/W)  Auxiliary Fault Status Register */
  __IM  uint32_t PFR[2U];                /*!< Offset: 0x040 (R/ )  Processor Feature Register */
  __IM  uint32_t DFR;                    /*!< Offset: 0x048 (R/ )  Debug Feature Register */
  __IM  uint32_t ADR;                    /*!< Offset: 0x04C (R/ )  Auxiliary Feature Register */
  __IM  uint32_t MMFR[4U];               /*!< Offset: 0x050 (R/ )  Memory Model Feature Register */
  __IM  uint32_t ISAR[5U];               /*!< Offset: 0x060 (R/ )  Instruction Set Attributes Register */
        uint32_t RESERVED0[5U];
  __IOM uint32_t CPACR;                  /*!< Offset: 0x088 (R/W)  Coprocessor Access Control Register */
} SCB_Type;
2. NVIC 单元寄存器
typedef struct
{
  __IOM uint32_t ISER[8U];               /*!< Offset: 0x000 (R/W)  Interrupt Set Enable Register */
        uint32_t RESERVED0[24U];
  __IOM uint32_t ICER[8U];               /*!< Offset: 0x080 (R/W)  Interrupt Clear Enable Register */
        uint32_t RESERVED1[24U];
  __IOM uint32_t ISPR[8U];               /*!< Offset: 0x100 (R/W)  Interrupt Set Pending Register */
        uint32_t RESERVED2[24U];
  __IOM uint32_t ICPR[8U];               /*!< Offset: 0x180 (R/W)  Interrupt Clear Pending Register */
        uint32_t RESERVED3[24U];
  __IOM uint32_t IABR[8U];               /*!< Offset: 0x200 (R/W)  Interrupt Active bit Register */
        uint32_t RESERVED4[56U];
  __IOM uint8_t  IP[240U];               /*!< Offset: 0x300 (R/W)  Interrupt Priority Register (8Bit wide) */
        uint32_t RESERVED5[644U];
  __OM  uint32_t STIR;                   /*!< Offset: 0xE00 ( /W)  Software Trigger Interrupt Register */
} NVIC_Type;
3. SysTick 单元寄存器
typedef struct
{
  __IOM uint32_t CTRL;                   /*!< Offset: 0x000 (R/W)  SysTick Control and Status Register */
  __IOM uint32_t LOAD;                   /*!< Offset: 0x004 (R/W)  SysTick Reload Value Register */
  __IOM uint32_t VAL;                    /*!< Offset: 0x008 (R/W)  SysTick Current Value Register */
  __IM  uint32_t CALIB;                  /*!< Offset: 0x00C (R/ )  SysTick Calibration Register */
} SysTick_Type;

声明:本文内容为作者原创笔记。在写作过程中使用了 AI 辅助工具进行语言润色和排版优化,但所有技术观点、分析及结论均来自作者本人。

Logo

免费领 150 小时云算力,进群参与显卡、AI PC 幸运抽奖

更多推荐