单片机内核
什么是内核?这是一个很抽象的问题。有时会听到 ARMv7-M、ARMv7-A 架构,又听过 Cortex-M3、Cortex-M4 内核——它们分别是什么?本文以 M3/M4 内核为切入点,介绍内核与架构的基本概念。
本文以文字为主,尽量简单描述。
M3/M4 内核
先看下面这张M3/M4 内核框图(这里先不用纠结“内核”到底是什么意思)。看到这张图,你可能会产生两个疑问:
- 为什么 M3 和 M4 的内核框图可以共用同一张图?
- 图中为什么没有外设?
问题一:为什么 M3/M4 共用同一张框图?
查阅 M3 和 M4 的内核手册会发现,它们的内容几乎完全一样——无论是内核寄存器(R0~R15),还是特殊功能寄存器(xPSR、CONTROL 等)。这是因为它们都隶属于ARMv7-M 架构,因此寄存器、流水线设计等均相同(流水线细节不了解也没关系)。
有兴趣的话可以对比core_cm3.h和core_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(连接寄存器)的使用可分为三种场景:
- 中断打断了主程序
- 中断嵌套
- 函数调用
前两种属于中断场景:
- 中断发生时会自动将
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 寄存器
typedefstruct{__IMuint32_tCPUID;/*!< Offset: 0x000 (R/ ) CPUID Base Register */__IOMuint32_tICSR;/*!< Offset: 0x004 (R/W) Interrupt Control and State Register */__IOMuint32_tVTOR;/*!< Offset: 0x008 (R/W) Vector Table Offset Register */__IOMuint32_tAIRCR;/*!< Offset: 0x00C (R/W) Application Interrupt and Reset Control Register */__IOMuint32_tSCR;/*!< Offset: 0x010 (R/W) System Control Register */__IOMuint32_tCCR;/*!< Offset: 0x014 (R/W) Configuration Control Register */__IOMuint8_tSHP[12U];/*!< Offset: 0x018 (R/W) System Handlers Priority Registers (4-7, 8-11, 12-15) */__IOMuint32_tSHCSR;/*!< Offset: 0x024 (R/W) System Handler Control and State Register */__IOMuint32_tCFSR;/*!< Offset: 0x028 (R/W) Configurable Fault Status Register */__IOMuint32_tHFSR;/*!< Offset: 0x02C (R/W) HardFault Status Register */__IOMuint32_tDFSR;/*!< Offset: 0x030 (R/W) Debug Fault Status Register */__IOMuint32_tMMFAR;/*!< Offset: 0x034 (R/W) MemManage Fault Address Register */__IOMuint32_tBFAR;/*!< Offset: 0x038 (R/W) BusFault Address Register */__IOMuint32_tAFSR;/*!< Offset: 0x03C (R/W) Auxiliary Fault Status Register */__IMuint32_tPFR[2U];/*!< Offset: 0x040 (R/ ) Processor Feature Register */__IMuint32_tDFR;/*!< Offset: 0x048 (R/ ) Debug Feature Register */__IMuint32_tADR;/*!< Offset: 0x04C (R/ ) Auxiliary Feature Register */__IMuint32_tMMFR[4U];/*!< Offset: 0x050 (R/ ) Memory Model Feature Register */__IMuint32_tISAR[5U];/*!< Offset: 0x060 (R/ ) Instruction Set Attributes Register */uint32_tRESERVED0[5U];__IOMuint32_tCPACR;/*!< Offset: 0x088 (R/W) Coprocessor Access Control Register */}SCB_Type;2. NVIC 单元寄存器
typedefstruct{__IOMuint32_tISER[8U];/*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */uint32_tRESERVED0[24U];__IOMuint32_tICER[8U];/*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */uint32_tRESERVED1[24U];__IOMuint32_tISPR[8U];/*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */uint32_tRESERVED2[24U];__IOMuint32_tICPR[8U];/*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */uint32_tRESERVED3[24U];__IOMuint32_tIABR[8U];/*!< Offset: 0x200 (R/W) Interrupt Active bit Register */uint32_tRESERVED4[56U];__IOMuint8_tIP[240U];/*!< Offset: 0x300 (R/W) Interrupt Priority Register (8Bit wide) */uint32_tRESERVED5[644U];__OMuint32_tSTIR;/*!< Offset: 0xE00 ( /W) Software Trigger Interrupt Register */}NVIC_Type;3. SysTick 单元寄存器
typedefstruct{__IOMuint32_tCTRL;/*!< Offset: 0x000 (R/W) SysTick Control and Status Register */__IOMuint32_tLOAD;/*!< Offset: 0x004 (R/W) SysTick Reload Value Register */__IOMuint32_tVAL;/*!< Offset: 0x008 (R/W) SysTick Current Value Register */__IMuint32_tCALIB;/*!< Offset: 0x00C (R/ ) SysTick Calibration Register */}SysTick_Type;声明:本文内容为作者原创笔记。在写作过程中使用了 AI 辅助工具进行语言润色和排版优化,但所有技术观点、分析及结论均来自作者本人。