首页/新闻资讯/正文详情

【STM32】内存地址映射全解析

发布时间:2026/9/24 20:49:13 来源:云帆数科 栏目:资讯中心
【STM32】内存地址映射全解析
STM32 内存地址映射全解析一、为什么要懂内存地址映射STM32 是 32 位 MCU地址总线 32 位理论寻址空间 2³² 4GB。这 4GB 不是随便排的而是 ARM Cortex-M 内核规定好的地图——每一块区域放什么、能干什么都有严格定义。理解这张地图的意义看懂寄存器操作库函数GPIOA-BSRR 0x0001本质是往某个地址写数据懂了映射才知道它在干嘛调试时不慌程序跑飞了看 Fault 地址、看栈指针能立刻定位是 Flash 崩了还是 RAM 爆了做 Bootloader/链接脚本自定义分区、重定向向量表都离不开地址计算面试高频考点嵌入式面试必问STM32 的内存是怎么映射的二、4GB 地址空间总览Cortex-M3/M4 把 4GB 地址空间按用途切成了 8 块每块 512MB高地址 0xFFFFFFFF ┌──────────────────────────┐ │ 系统级空间 │ 512MB → 内核私有NVIC/SCB/SysTick 0xE0000000 ├──────────────────────────┤ │ 外部设备区扩展 │ 512MB → FSMC 外接设备 0xC0000000 ├──────────────────────────┤ │ 外部 RAM 区扩展 │ 512MB → FSMC 外接 SRAM/SDRAM 0xA0000000 ├──────────────────────────┤ │ 片上外设寄存器区 │ 512MB → AHB/APB 外设寄存器 ← 重点 0x80000000 ├──────────────────────────┤ │ 外部 RAM 区扩展 │ 512MB → FSMC 外接 RAM 0x60000000 ├──────────────────────────┤ │ 外部设备区扩展 │ 512MB → FSMC 外接设备 0x40000000 ├──────────────────────────┤ │ 片上外设寄存器区 │ 512MB → GPIOA/USART1/TIM2 ← 重点 0x3FFFFFFF ├──────────────────────────┤ │ 片上 SRAM │ 512MB → 主 SRAM 备份 SRAM ← 重点 0x20000000 ├──────────────────────────┤ │ 片上 Flash │ 512MB → 主 Flash 系统存储器 ← 重点 0x00000000 └──────────────────────────┘ 低地址对 STM32F103 这种中小容量芯片来说上面一半0x80000000 以上基本没用到实际打交道的就是下面这三块Flash、SRAM、片上外设寄存器。三、片上 Flash0x08000000 起3.1 Flash 里存了什么0x08000000 ┌──────────────────────────┐ │ 中断向量表 │ ← 前几百字节 0x080000C0 ├──────────────────────────┤ │ │ │ .text 代码段 │ ← 你写的函数编译后的机器码 │ Reset_Handler │ │ main() │ │ USART1_IRQHandler() │ │ ... │ │ │ 0x0800XXXX ├──────────────────────────┤ │ .rodata 只读数据 │ ← const 常量、字符串字面量 │ const int buf[100]; │ │ printf(hello) 里的 hello │ │ │ 0x0800YYYY ├──────────────────────────┤ │ .data 段的初值 │ ← 已初始化全局变量的初值 │ int a 10; → 这里存 10│ │ 启动时拷贝到 RAM │ │ │ 0x0801FFFF └──────────────────────────┘ ← 128KB Flash 结束3.2 向量表的具体地址向量表从 Flash 最开头 0x08000000 开始每项 4 字节地址偏移内容中断号0x080000000x00初始栈顶 MSP—0x080000040x04Reset_Handler10x080000080x08NMI_Handler20x0800000C0x0CHardFault_Handler30x080000100x10MemManage_Handler40x080000140x14BusFault_Handler50x080000180x18UsageFault_Handler6............0x080000400x40WWDG_IRQHandler160x080000440x44PVD_IRQHandler17............0x080000D40xD4USART1_IRQHandler53计算公式向量表项地址 0x08000000 中断号 × 4验证 USART153 × 4 212 0xD4 → 0x080000D4 ✅3.3 Flash 页大小型号Flash 容量页大小STM32F103C864KB1KBSTM32F103CB128KB1KBSTM32F103ZE512KB2KB擦写 Flash 时必须按页擦不能按字节。所以做 IAP 时要算好页对齐。四、片上 SRAM0x20000000 起4.1 SRAM 里有什么0x2000FFFF ┌──────────────────────────┘ │ │ │ 栈 Stack │ ← 从高地址往下生长 │ ↓ │ │ 函数调用压栈、中断现场保护 │ │ │ │ 栈满了会覆盖堆/全局变量│ │ │ │ 堆 Heap │ ← 从低地址往上生长 │ ↑ │ ← malloc / free │ │ │ .bss 段 │ ← 未初始化全局变量 │ int b; │ 启动时自动清零 │ │ │ .data 段 │ ← 已初始化全局变量 │ int a 10; │ 启动时从 Flash 拷入 │ │ 0x20000000 └──────────────────────────┘4.2 .data 和 .bss 的区别段内容Flash 里有吗启动时做什么例子.data已初始化的全局变量有初值从 Flash 拷贝到 RAMint a 10;.bss未初始化的全局变量没有自动清零int b;为什么要分未初始化的变量默认都是 0不需要在 Flash 里存初值省 Flash 空间。启动代码里有一段专门做这事// 启动文件里 SystemInit 或 __main 做的事 extern uint32_t _sidata; // Flash 里 .data 的起始地址 extern uint32_t _sdata; // RAM 里 .data 的起始地址 extern uint32_t _ebss; // .bss 结束地址 extern uint32_t _sbss; // .bss 起始地址 // 拷贝 .data for (src _sidata, dst _sdata; dst _edata; ) *dst *src; // 清零 .bss for (dst _sbss; dst _ebss; ) *dst 0;4.3 栈和堆的大小在哪定的在启动文件里; 栈大小 1KB Stack_Size EQU 0x00000400 AREA STACK, NOINIT, READWRITE, ALIGN3 Stack_Mem SPACE Stack_Size __initial_sp ; 堆大小 512B Heap_Size EQU 0x00000200 AREA HEAP, NOINIT, READWRITE, ALIGN3 __heap_base __heap_limit栈溢出的典型表现程序跑飞、HardFault、全局变量莫名其妙被改。因为栈往下长堆往上长撞在一起就出事了。五、片上外设寄存器区0x40000000 起这是你写代码最常打交道的区域——直接读写这个地址 读写硬件寄存器。5.1 总线划分0x40000000 ┌──────────────────────────┐ │ │ │ APB1 总线 │ ← 低速外设 │ USART2/3/4/5 │ │ TIM2~TIM7 │ │ I2C1/I2C2 │ │ CAN │ │ PWR/BKP │ │ │ 0x40008000 ├──────────────────────────┤ │ 保留 │ 0x40009000 ├──────────────────────────┤ │ 保留 │ 0x40010000 ├──────────────────────────┤ │ │ │ APB2 总线 │ ← 高速外设 │ GPIOA ~ GPIOG │ │ USART1 │ │ ADC1/ADC2 │ │ EXTI │ │ SPI1 │ │ TIM1 │ │ │ 0x40020000 ├──────────────────────────┤ │ │ │ AHB 总线 │ ← 最高速外设 │ DMA1/DMA2 │ │ RCC时钟控制 │ │ Flash 接口 │ │ CRC 计算单元 │ │ │ 0x50000000 └──────────────────────────┘5.2 外设基地址速查表外设基地址总线DMA10x40020000AHBRCC0x40021000AHBFlash 接口0x40022000AHBCRC0x40023000AHBGPIOA0x40010800APB2GPIOB0x40010C00APB2GPIOC0x40010000APB2USART10x40013800APB2USART20x40004400APB1USART30x40004800APB1SPI10x40013000APB2SPI20x40003800APB1TIM10x40012C00APB2TIM20x40000000APB1ADC10x40012400APB2EXTI0x40010400APB25.3 寄存器地址怎么算每个外设有一个基地址寄存器相对于基地址有偏移量。以 USART1 为例USART1 基地址 0x40013800 寄存器 偏移 实际地址 作用 ────────── ────── ────────────── ────────────── USART_SR 0x00 0x40013800 状态寄存器 USART_DR 0x04 0x40013804 数据寄存器收发 USART_BRR 0x08 0x40013808 波特率 USART_CR1 0x0C 0x4001380C 控制寄存器1 USART_CR2 0x10 0x40013810 控制寄存器2 USART_CR3 0x14 0x40013814 控制寄存器3 USART_GTPR 0x18 0x40013818 保护时间/预分频代码里直接操作// 读 USART1 状态寄存器 uint32_t sr *(volatile uint32_t *)0x40013800; // 往 USART1 写一个字节 *(volatile uint32_t *)0x40013804 0x55;库函数只是帮你封装好了// 头文件里的宏定义 #define USART1 ((USART_TypeDef *)0x40013800) // 使用 USART1-DR 0x55; // 本质还是写 0x40013804volatile 关键字必须加——告诉编译器这个地址可能被硬件改变不要优化掉读取。六、内核外设区0xE0000000 起这块区域是 Cortex-M 内核自己的私有外设和具体芯片无关外设基地址作用SysTick0xE000E010系统滴答定时器NVIC0xE000E100嵌套向量中断控制器SCB0xE000ED00系统控制块VTOR、AIRCR 等SCB 里最常用的寄存器寄存器偏移作用SCB-VTOR0x08向量表偏移寄存器SCB-AIRCR0x0C应用程序中断/复位控制SCB-SHCSR0x24系统异常控制和状态SCB-CPUID0x00内核 ID读出来知道是 M3 还是 M4七、位带区Bit-bandCortex-M3 有个很实用的特性——把一个寄存器的某一位映射到一个单独的地址直接读写那个地址就等于操作那一位不需要读-改-写。7.1 两个位带区区域位带区别名对应区别名区单独位地址SRAM0x20000000 ~ 0x200FFFFF0x22000000 ~ 0x23FFFFFF外设0x40000000 ~ 0x400FFFFF0x42000000 ~ 0x43FFFFFF7.2 地址换算公式别名地址 别名区起始 (字节偏移 × 32) (位号 × 4)为什么是 ×32因为每个字节有 8 位每位映射一个 32 位字4 字节8 × 4 32。7.3 例子操作 PB5GPIOB_BASE 0x40010C00 ODR 偏移 0x0C ODR 地址 0x40010C0C PB5 的位号 5 字节偏移 0x40010C0C - 0x40000000 0x10C0C 别名地址 0x42000000 (0x10C0C × 32) (5 × 4) 0x42000000 0x218180 0x14 0x420218194直接写*(volatile uint32_t *)0x420218194 1; // PB5 输出高 *(volatile uint32_t *)0x420218194 0; // PB5 输出低好处原子操作不会被中断打断不用读-改-写省一条指令多任务/中断安全八、BOOT 启动映射STM32 上电时CPU 总是从 0x00000000 开始取向量表。但这个地址映射到哪里由 BOOT 引脚决定BOOT0BOOT10x00000000 映射到向量表位置用途0X主 Flash0x08000000正常运行最常用10系统存储器0x1FFFF000ISP 串口烧录11SRAM0x20000000调试用少见CPU 上电PC 0x00000000 │ ↓ 芯片内部根据 BOOT 引脚重映射 ┌──────┼──────┐ ↓ ↓ ↓ 0x08000000 0x1FFFF000 0x20000000 主Flash 系统存储 SRAM所以你在芯片手册上看到向量表在 0x08000000但 CPU 实际是从 0x00000000 开始执行的——这是芯片内部做了地址重映射对程序员透明。九、VTOR运行时重定位向量表SCB-VTOR 寄存器可以在程序运行时改变向量表位置// 默认向量表在 Flash 起始 SCB-VTOR 0x08000000; // Bootloader 跳 AppApp 向量表在 Flash 偏移 16KB SCB-VTOR 0x08004000; // 把向量表搬到 SRAM运行时改中断函数 SCB-VTOR 0x20000000;注意VTOR 的地址必须对齐。对齐大小 向量表总大小向上取整到 2 的幂。至少对齐到 128 字节32 个向量 × 4 字节。十、一张图总结高地址 0xFFFFFFFF │ ┌────┴─────────┐ │ 系统级空间 │ ← NVIC(0xE000E100), SCB(0xE000ED00) ├──────────────┤ │ 外部扩展区 │ ← FSMC 外接 ├──────────────┤ │ 外设寄存器 │ ← GPIOA(0x40010800), USART1(0x40013800) │ 0x40000000 │ RCC(0x40021000), TIM2(0x40000000) ├──────────────┤ │ │ │ SRAM 64KB │ ← 栈(0x2000A000), 堆, .data/.bss │ 0x20000000 │ │ │ ├──────────────┤ │ Flash 128KB │ ← 向量表(0x08000000), 代码, .rodata │ 0x08000000 │ │ │ 低地址 0x00000000常用速查用途地址主 Flash 起始0x08000000主 SRAM 起始0x20000000AHB 外设基地址0x40000000APB2 外设基地址0x40010000APB1 外设基地址0x40000000GPIOA 基地址0x40010800GPIOB 基地址0x40010C00USART1 基地址0x40013800RCC 基地址0x40021000NVIC 基地址0xE000E100SCB 基地址0xE000ED00SysTick 基地址0xE000E010

相关推荐

8N8工作流模板2400套实用指南:导入、排查与改造
8N8工作流模板2400套实用指南:导入、排查与改造

你有没有过这种经历:下载了一个号称2400套模板的合集,解压之后盯着满满当当的文件夹,一时间不知道该从哪下手。我拿到这份8N8工作流模板2400套合集的时候,第一反应其实不是“赚到了”,而是“这里面到底有多少东西是我真… · 2026/9/24 20:49:07

AI工程全景地图:从数据到模型落地的完整指南
AI工程全景地图:从数据到模型落地的完整指南

1. AI工程到底是怎么一回事做算法的人经常会说一句话:模型训出来只是开始,真正难的是把它变成一个能稳定跑在生产环境里的系统。这句话其实就是AI工程存在的意义。这两年“AI工程”这个词被反复提起,但它跟传统的软件工程、数据科学都不是一回… · 2026/9/24 20:49:07

PPP协议深度解析:从LCP协商到NCP配置的工程实践指南
PPP协议深度解析:从LCP协商到NCP配置的工程实践指南

1. 为什么今天还要聊PPP协议很多人第一次接触PPP协议,是在《计算机网络》教材的“数据链路层”那一章。翻过去之后,脑子里留下的印象大概就是“拨号上网用的东西”,然后就没有然后了。毕竟现在家里都是光纤入户,公司里跑的是以太网… · 2026/9/24 20:49:07

EasyExcel单元格样式不生效?从POI共享模型到拦截器全解析
EasyExcel单元格样式不生效?从POI共享模型到拦截器全解析

做Java导出功能这些年,EasyExcel用得不算少,群里最常被翻牌的问题除了“大数据量怎么优化”,就是“EasyExcel自定义单元格样式不生效”。这问题看起来简单,实际上牵涉到拦截器机制、POI底层样式对象模型、注解和模板的优先级关系&… · 2026/9/24 21:18:11

Spring Boot自动配置原理与实战:从启动流程到自定义Starter
Spring Boot自动配置原理与实战:从启动流程到自定义Starter

很多用Spring Boot的朋友,上来就是SpringBootApplication一加,再写个Controller就跑起来了。项目跑通当然没问题,但你要是问它:为什么加一个spring-boot-starter-web就能自动起Tomcat?为什么DataSource你不配Bean也能注… · 2026/9/24 21:18:04

企业级AI智能体办公平台数据安全选型指南:六款主流产品评测
企业级AI智能体办公平台数据安全选型指南:六款主流产品评测

站在2026年再看“AI智能体办公平台”这个选题,已经不像前两年那样停留在“我们能不能用”的讨论上,企业真正焦虑的事情变成了“用了之后数据会不会失控”。我过去一年帮几家中型客户做过这类平台的安全评估和选型,从钉钉、飞书到微软Copilot&… · 2026/9/24 21:18:04

代理模式全解析:从静态代理到JDK动态代理、CGLIB与字节码增强
代理模式全解析:从静态代理到JDK动态代理、CGLIB与字节码增强

只要有Java面试经验的读者应该都有感触,代理模式几乎是一个绕不开的考点。从最基础的静态代理,到JDK动态代理、CGLIB,再到直接操作class字节码的ASM、Javassist,这条技术脉络恰好串起了Java开发者从“会用框架”到“看懂框架”的完… · 2026/9/24 21:18:04

统计二叉树好节点:DFS路径最大值与Java递归实现
统计二叉树好节点:DFS路径最大值与Java递归实现

博主经验也不算多,但LeetCode刷了三百来道,Java版二叉树这块踩坑不少。这次拿Lc336-1448这道“统计二叉树中好节点的数目”来聊聊,题目本身不复杂,但背后的DFS思路、Java实现细节,还有面试时怎么答得让面试官眼前一亮&… · 2026/9/24 21:18:04

Java代理模式进阶:从静态代理到动态代理与字节码增强
Java代理模式进阶:从静态代理到动态代理与字节码增强

1. 内容整体设计与思路拆解先抛个场景:你在面试现场,对面面试官不紧不慢地问了一句"讲一下 Java 代理模式",接着补了一句"从静态代理到动态代理再到字节码增强都说说"。这个问题问出来,基本就能把一个人的基础… · 2026/9/24 21:18:04

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程
基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为… · 2026/9/24 0:00:13

1D-CNN时间序列建模实战:从Conv1d原理到工业落地
1D-CNN时间序列建模实战:从Conv1d原理到工业落地

简介:面向时间序列数据建模的一维卷积神经网络完整实现,适合深度学习入门者及需要快速验证时序模型的研究者,能够从音频、文本、传感器或股价等序列中挖掘局部特征与时间依赖。压缩包体积很小,只有3KB,内含3个Python脚… · 2026/9/24 0:00:26

柔软的L:汉语语流中被忽视的舌肌张力控制
柔软的L:汉语语流中被忽视的舌肌张力控制

1. 这个“L”不是字母表里的L,而是舌尖上的L最近在几个方言群和语音教学社群里,反复看到有人发一句:“也说字母L:柔软的长舌”。初看以为是英语发音课笔记,点开才发现全是方言爱好者、播音系学生、语言康复师甚至戏曲演… · 2026/9/24 0:00:44

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码