当前位置: 首页 > news >正文

stm32内存知识概览

文章目录

    • 一、宏观视角:STM32 的内存不是一块,而是一张“城市地图”
    • 二、核心内存区域详解
      • 2.1 Flash —— 你的代码和常量的家
      • 2.2 SRAM —— 运行时的变量、栈和堆
      • 2.3 外设寄存器区 —— 软硬件桥梁的“接触点”
      • 2.4 位带区(Bit-banding)—— 对单个比特的直接“原子”操控
        • 问题:直接操作寄存器某一位不够原子
        • 位带区域
        • 映射公式
      • 2.5 系统控制区 —— 内核的“设置面板”
    • 三、内存对齐:为什么不能“乱放”数据
      • 3.1 什么是内存对齐?
      • 3.2 为什么需要对齐?—— 硬件的物理限制
      • 3.3 Cortex-M3/M4/M7 的对齐特性
      • 3.4 结构体的内存对齐
    • 四、内存保护单元(MPU)—— 可选的“内存防火墙”
    • 五、从源码到二进制:链接脚本与内存分配
      • 5.1 程序的各个“段”(Section)
      • 5.2 链接脚本的作用
      • 5.3 启动过程与内存初始化
    • 六、实践中的内存知识要点
      • 6.1 volatile 为何在寄存器访问中必须
      • 6.2 DMA 与缓存一致性(M7 需要注意)
      • 6.3 堆和栈的管理
      • 6.4 内存屏障指令
    • 七、总结:一张小抄助你记住所有
    • 专栏目录

从零开始,把 STM32 的“内存世界”完整拆解一遍。读完你会明白:芯片内的每一块内存,都是怎么来的、如何访问、有什么规矩,以及写代码时怎样避开常见的坑。


一、宏观视角:STM32 的内存不是一块,而是一张“城市地图”

STM32 使用的是统一编址的存储器映射架构。CPU 所能访问的所有东西——真正的内存、外设寄存器、内部 Flash、甚至系统控制块——都被分配到了一个 4GB 的线性地址空间里。

ARM Cortex-M3/M4为例,这 4GB 空间被 ARM 官方划定了大致的“功能区”,STM32 再在里面填充具体的设备。

4GB 地址空间概览(Cortex-M 约定) 0x00000000 ────────── 0x1FFFFFFF : Code 区(Flash 别名、内部 Flash 等) 0x20000000 ────────── 0x3FFFFFFF : SRAM 区(内部 SRAM) 0x40000000 ────────── 0x5FFFFFFF : 外设区(GPIO, USART, SPI...) 0x60000000 ────────── 0x9FFFFFFF : 外部存储器(FSMC/FMC) 0xE0000000 ────────── 0xFFFFFFFF : 系统控制区(NVIC, SCB, MPU...)

你编写的任何 C 代码、访问的任何变量、操作的任何寄存器,最终都会落入这张地图的某个地址。

关键认知:内存 = 存储器映射的地址空间。它不仅仅是“RAM”,还包括 Flash、外设寄存器等一切可寻址的东西。


二、核心内存区域详解

2.1 Flash —— 你的代码和常量的家

  • 地址范围(以 F407 为例):0x0800 0000开始,最大 1MB(具体看芯片)。
  • 特性:非易失性,掉电不丢;只可在 CPU 视角下读取,写入需通过 Flash 控制器进行特殊的“编程/擦除”操作,不能像 RAM 那样随意赋值。
  • 存放了什么:
    • 你的程序机器码(.text段)
    • 常量数据(const全局变量、字符串常量,.rodata段)
    • 中断向量表(默认放在 Flash 开头,0x08000000
    • 有可能有掉电保存的用户数据(如果特意编程写入)
  • 为什么 Flash 也在此列?因为 CPU 是取指和执行代码的,它必须能从某个地址读到指令。Flash 就被映射到 Code 区。

有时你会看到0x00000000也能访问到 Flash,因为 STM32 上电后会根据 BOOT 引脚把 Flash 或系统存储器**别名(重映射)**到地址 0,从而让 CPU 从 0 地址取栈顶和复位向量。

2.2 SRAM —— 运行时的变量、栈和堆

  • 地址范围:0x20000000开始,大小依芯片而定(F407 有 128KB 常规 SRAM,还有 64KB CCM RAM)。
  • 特性:可任意读写,速度快,掉电数据丢失。这是你普通变量存放的地方。
  • 内存细分(以 F4 为例):
    • 常规 SRAM(128KB):位于0x20000000,可以存数据,也可以执行代码(尽管不常用)。
    • CCM(内核耦合内存)RAM(64KB):位于0x10000000,直接挂在 CPU 数据总线上,只能存数据(不能存指令),且DMA 无法访问。适合存放需要 CPU 频繁操作的栈或关键数据,速度更快且无总线竞争。

2.3 外设寄存器区 —— 软硬件桥梁的“接触点”

  • 地址:0x40000000开始,各种外设的寄存器组按总线和编号排布。
  • 实质:这些地址背后不是 RAM 存储单元,而是一组一组的硬件触发器。读这些地址会得到引脚电平或状态标志;写这些地址会驱动硬件动作。这就是你之前学到的寄存器。
  • 访问规则:必须用volatile指针;有些寄存器只读、只写或写1清零,要严格遵循手册。

2.4 位带区(Bit-banding)—— 对单个比特的直接“原子”操控

这是一个超级实用的特性,值得展开。

问题:直接操作寄存器某一位不够原子

假如你想把 GPIOA 的 ODR 第 5 位置 1,经典的“读-修改-写”操作:

GPIOA->ODR|=(1<<5);

实际是三步:读整个 32 位 → 修改第 5 位 → 写回。如果在此中间发生中断也修改了 ODR,就可能覆盖掉中断的改动。所以 STM32 设计了 BSRR 寄存器来原子地置位/复位。

但有些外设没有 BSRR,这时候位带可以解决。位带让你能用一个独立的 32 位地址去代表某个寄存器的某一个比特。你向这个地址写1就是将该位置 1,写0就是清 0,CPU 硬件保证这是一次原子的“读-改-写”,不会被中断干扰。

位带区域

Cortex-M3/M4 支持两个位带区:

  1. SRAM 位带区:0x20000000~0x200FFFFF(共 1MB)
  2. 外设位带区:0x40000000~0x400FFFFF(共 1MB)

它们分别对应一个位带别名区

  • SRAM 别名区:0x22000000~0x23FFFFFF
  • 外设别名区:0x42000000~0x43FFFFFF
映射公式

要找到某个比特的别名地址:

bit_word_addr = alias_base + (byte_offset * 32) + (bit_number * 4)
  • alias_base是别名区起始地址(外设是0x42000000,SRAM 是0x22000000
  • byte_offset是目标字节在 bit-band 区的偏移地址(即目标寄存器地址减去 bit-band 基址)
  • bit_number是比特编号(0~7 对于字节操作,0~31 对于字操作)

例如 GPIOA ODR 是0x40020014,要对 bit 5 操作:

  • bit-band 基址0x40000000
  • 偏移 =0x40020014 - 0x40000000 = 0x20014
  • bit = 5
  • 别名地址 =0x42000000 + (0x20014 * 32) + (5 * 4) = 0x42400294

随后:

*(volatileuint32_t*)0x42400294=1;// 原子置1*(volatileuint32_t*)0x42400294=0;// 原子清0

读别名地址返回的是 0 或 1(非 0 时表示该位为 1)。

实际上,头文件或 CMSIS 提供了宏方便使用(如__BITBAND_PERIPH()),你甚至可以直接用标准外设库或 LL 库,它们底层可能用位带优化。

位带的意义:原子位操作、简化代码、提高性能(省去屏蔽与或运算)。

2.5 系统控制区 —— 内核的“设置面板”

0xE0000000开始,包含:

  • NVIC中断控制器寄存器
  • 系统控制块(SCB):配置优先级分组、系统异常等
  • SysTick 定时器
  • MPU 内存保护单元
    这些也是通过内存映射寄存器访问的。

三、内存对齐:为什么不能“乱放”数据

3.1 什么是内存对齐?

简单说,数据在内存中的起始地址必须是其大小(字节数)的整数倍

  • 2 字节的short必须放在偶数地址(地址是 2 的倍数)
  • 4 字节的int/float必须放在地址是 4 的倍数的位置
  • 8 字节的double(若支持)必须放在地址是 8 的倍数的位置

如果地址不满足,就称为非对齐访问

3.2 为什么需要对齐?—— 硬件的物理限制

CPU 从内存取数据时不是逐字节抓的,而是通过数据总线一次抓一个字(32位)或半个字。总线设计时,地址线的最低几位用来控制字节选通。

假设没有对齐,一个 4 字节的int存放在地址0x20000001

  • CPU 需要读两次:先读地址0x20000000取后 3 个字节,再读0x20000004取前 1 个字节,然后拼接。
  • 这会降低性能,且硬件实现复杂。

所以,很多 CPU 架构(如一些 ARM 的老版本,或某些模式)直接禁止非对齐访问,一旦发生就触发异常。

3.3 Cortex-M3/M4/M7 的对齐特性

Cortex-M3/M4支持硬件级的非对齐访问(仅限于普通的数据加载/存储指令,如LDR/STR),但有几个重要限制:

  • LDM/STM(多寄存器加载/存储)及堆栈操作(PUSH/POP)必须对齐到 4 字节,否则发生用法 fault。
  • 非对齐访问是的,可能需要多个总线周期。
  • 外设寄存器区域、强排序内存区域等可能不支持非对齐访问(通常只支持字访问)。
  • 编译器默认会将数据对齐,所以正常写 C 代码很少遇到非对齐问题,除非你强制转换指针

实践铁律:永远不要让指针指向未对齐的地址,除非你非常清楚自己在做什么。

3.4 结构体的内存对齐

编译器会自动在结构体成员之间插入填充字节来满足对齐要求。这会导致sizeof(结构体)大于各成员大小之和。

例:

structDemo{chara;// 1字节intb;// 4字节shortc;// 2字节};// 实际内存布局(在默认4字节对齐下):// a 占 1 字节,后跟 3 个填充字节,使 b 从 4 的倍数开始// b 占 4 字节// c 占 2 字节,因为结构体总大小需要是最大成员对齐的倍数,所以末尾补 2 字节填充// sizeof(Demo) 很可能是 12

可以通过改变成员顺序减少填充:

structDemoOpt{intb;shortc;chara;};// sizeof 可能是 8

改变对齐方式:

  • __attribute__((aligned(n))):指定变量或结构体的最小对齐。
  • __attribute__((packed)):取消填充,紧凑排列。但会导致非对齐访问,影响性能甚至出错,仅在移植协议或硬件映射时使用。
  • #pragma pack(push, 1)#pragma pack(pop):效果类似packed

编写与硬件寄存器映射相关的结构体时,必须确保没有自动填充,通常用__packed__attribute__((packed)),并用静态断言检查大小。


四、内存保护单元(MPU)—— 可选的“内存防火墙”

MPU 可以让你把内存分成多个区域,分别设定权限(读/写/执行)和属性(缓存、缓冲、共享等)。它在以下场景有用:

  • 防止堆栈溢出破坏数据区。
  • 禁止代码从 SRAM 执行(安全)。
  • 隔离实时任务和非实时任务的数据。

MPU 在启动代码里配置,或者用 RTOS(如 FreeRTOS)的 API 管理。默认它是关闭的,所有内存都可访问。


五、从源码到二进制:链接脚本与内存分配

5.1 程序的各个“段”(Section)

你的 C 程序编译后会被分割成不同的段:

  • .text:代码,放入 Flash。
  • .rodata:只读数据(const 变量、字符串),放入 Flash。
  • .data:已初始化的全局/静态变量,其初始值存在 Flash 里,上电后由启动代码复制到 SRAM。
  • .bss:未初始化或初始化为 0 的全局/静态变量,启动代码在 SRAM 中将其清零。
  • 堆(heap):动态内存分配(malloc)用的区域,从 RAM 中划定。
  • 栈(stack):局部变量、函数调用参数、返回地址等,也是从 RAM 中划定。

5.2 链接脚本的作用

链接脚本(.ld文件)决定这些段放在哪里。例如 STM32F407 的脚本:

MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 128K CCMRAM (rw) : ORIGIN = 0x10000000, LENGTH = 64K } SECTIONS { .text : { *(.text) } > FLASH .rodata : { *(.rodata) } > FLASH .data : { *(.data) } > RAM AT> FLASH .bss : { *(.bss) } > RAM /* 堆栈通常放在 RAM 的最后 */ }

这样链接器就知道哪些东西往哪放,并为全局变量生成正确的地址。

5.3 启动过程与内存初始化

芯片上电后:

  1. 从地址0x00000000(重映射自 Flash 或系统存储器)读取初始堆栈指针放入 MSP。
  2. 读取复位向量(Reset_Handler 的地址)并跳转。
  3. Reset_Handler 里会做:复制.data段从 Flash 到 RAM,清零.bss,调用SystemInit()(配置时钟),然后调用main()

你可以在启动文件(startup_stm32f407xx.s)和系统文件(system_stm32f4xx.c)里看到这些。


六、实践中的内存知识要点

6.1 volatile 为何在寄存器访问中必须

因为外设寄存器值会由硬件改变,编译器如果不加volatile,可能会把多次读取优化成一次,或者打乱写顺序,导致逻辑错误。所以所有外设寄存器定义都带有__IO(即volatile)。

6.2 DMA 与缓存一致性(M7 需要注意)

如果使用 M7 内核(如 H7 系列),CPU 带有数据缓存。当 DMA 修改了 SRAM,CPU 缓存里的副本不会自动更新,导致读到旧数据。需要手动操作 SCB 的缓存维护指令(clean/invalidate)或使用 MPU 将 DMA 缓冲区设为不缓存。

对于 M3/M4(无缓存),通常不需要考虑这个问题。

6.3 堆和栈的管理

  • :由 MSP(主栈指针)管理。每个函数调用会压栈(局部变量、返回地址),递归过深或局部数组太大会栈溢出,通常表现为 HardFault。可以在链接脚本里设置栈大小,并在 startup 文件里分配。
  • :如果用了malloc,需要在链接脚本里指定堆的大小。嵌入式系统一般不建议用动态内存分配,因为容易产生碎片且不确定。通常用静态分配或内存池。

6.4 内存屏障指令

Cortex-M 提供了DMBDSBISB等指令,用于保证内存访问的顺序。在操作外设寄存器时,如果编译器或硬件可能重排指令,可以使用。通常外设寄存器被映射为“设备”内存类型(在内核层面),访问顺序已得到保证,大多数应用不需要显式加屏障。


七、总结:一张小抄助你记住所有

区域地址段里面有什么操作要点
Flash0x08000000代码、常量、向量表只读,不可直接赋值
SRAM0x20000000变量、堆、栈可读可写,掉电丢
CCM RAM0x10000000高速数据缓冲区CPU 专用,DMA 别碰
外设0x40000000寄存器volatile,按位域操作
位带别名0x42…原子位操作超级好用
系统控制0xE000E000NVIC、SCB、MPU内核功能配置

内存对齐:让数据地址是其大小的整数倍,编译器自动做,结构体注意排列,强制指针转换要万分小心。

内存模式:一切皆地址,CPU 只管读写地址,硬件根据地址决定谁来响应。你写 C 代码时操作的一切,最终都在这个统一的 4GB 地图上被精准定位。

掌握了这些,你就能从底层看懂为何程序能跑起来,为何会进 HardFault,以及如何写出更安全高效的嵌入式代码。带着这张“内存地图”去读手册和写程序,你会觉得整个世界通透许多。

专栏目录

Click

http://www.jsqmd.com/news/1093698/

相关文章:

  • 告别SPSSStata繁琐操作!百考通AI搞定经管社科论文量化实证分析
  • py每日spdier案例之某website文字转音频接口加密参数解密(难度一般)
  • epower — 轻量化电网建模与潮流仿真工具
  • STM32 FIR滤波器实战避坑指南:从MATLAB到CMSIS-DSP的高效实现
  • 豆包专业版实测:从对话AI到桌面Agent的能力升级
  • AI大模型时代的内容策略:如何构建品牌专属的AI知识库?
  • 告别SPSS/Stata繁琐操作!百考通AI搞定经管社科论文量化实证分析
  • 【中兴未来领军】助兄弟姐妹们拿下蓝剑/SSP高端offer,开启顶尖职业之路!
  • AI Skills技能系统,让 Agent 自动变强
  • 软件设计师教程第 7 章 面向对象技术
  • 2026 区块链交易平台搭建全指南:打造安全高效合规化数字资产流通系统
  • 降成本+控质量:团队级AI编程多模型协同落地路径
  • 遥感目标检测实战:九大核心数据集特性解析与选型指南
  • 【计算机毕业设计案例】基于 SpringBoot 的教师考勤异常报备管理系统 校园人事教师考勤信息管理系统设计与实现(程序+文档+讲解+定制)
  • 终极硬件信息欺骗工具:EASY-HWID-SPOOFER内核级技术完全指南
  • Kubevirt下载安装
  • 2026百度网盘最新提速技巧:解析设置跑满带宽的指南
  • AI智能眼镜的视频流通路设计
  • Agent Skill 状态机工程:Mode-Step 网格如何拆开工作流边界
  • HTTPS之后如何防御API重放攻击?请求签名原理与JS/Java实现详解
  • 2026年下半年量化工具选择,先按阶段放置 AI
  • 豆包上做广告知名的郑州geo品牌
  • 机器人PCB行业多家头部企业的市场表现对比
  • 法大大Nota Sign通过SOC 2 Type II审计,为出海企业提供国际合规保障
  • 别再死磕单机SLAM了!天花板上的智能照明,才是陪伴机器人的“终极外挂”
  • 工控机为什么不用消费级主板?
  • 【AI 杂谈】Agent的信任壕沟:为什么越强的Agent越没人敢用?
  • 2026年6月亲测,深圳吊装这样选才靠谱!
  • MSPM0 UNICOMM-SPI寄存器级配置与调试实战指南
  • AI营销拓客工具推荐