MCU的FLASH与SRAM中存了什么?

目录

[1. 静态布局](#1. 静态布局)

[1.1 FLASH里存了什么?](#1.1 FLASH里存了什么?)

[1.1.1 中断向量表 (Vector Table)](#1.1.1 中断向量表 (Vector Table))

[1.1.2 代码段 (.text)](#1.1.2 代码段 (.text))

[1.1.3 只读数据段 (.rodata)](#1.1.3 只读数据段 (.rodata))

[1.1.4 读写数据段 (.rwdata)](#1.1.4 读写数据段 (.rwdata))

总结:

[1.2 SRAM里存了什么?](#1.2 SRAM里存了什么?)

[1.2.1 已初始化数据段 (.data)](#1.2.1 已初始化数据段 (.data))

[1.2.2 未初始化数据段 (.bss)](#1.2.2 未初始化数据段 (.bss))

[1.2.3 堆区 (Heap)](#1.2.3 堆区 (Heap))

[1.2.4 栈区 (Stack)](#1.2.4 栈区 (Stack))

总结:

[2. 时间维度上内存的变化过程](#2. 时间维度上内存的变化过程)

[2.1 硬件复位 - 读取向量表](#2.1 硬件复位 - 读取向量表)

[2.2 Reset_Handler 开始执行](#2.2 Reset_Handler 开始执行)

[2.3 .data 段初始化 - 从 FLASH 拷贝到 SRAM](#2.3 .data 段初始化 - 从 FLASH 拷贝到 SRAM)

[2.4 .bss 段清零](#2.4 .bss 段清零)

[2.5 进入 main() → RT-Thread 初始化](#2.5 进入 main() → RT-Thread 初始化)

[2.6 创建线程 → 调度器启动](#2.6 创建线程 → 调度器启动)

总结:


1. 静态布局

图1.1-FLASH与SRAM内容总览

1.1 FLASH里存了什么?

  • 向量表(MSP初始值 + Reset_Handler地址 + 中断处理函数地址)
  • .text 段(所有函数的机器码)
  • .rodata 段(const 常量、字符串)
  • .rwdata段 (全局变量的初始值副本)

1.1.1 中断向量表 (Vector Table)

中断向量表是系统最先读取的地方,决定了程序从哪里开始执行。前两个最重要的字: 0x08000000: 初始 MSP 值(主栈指针)、 0x08000004: Reset_Handler 地址(复位后第一条指令的位置)。**后续是各种中断处理函数地址:**NMI_Handler, HardFault_Handler, SysTick_Handler, USART1_IRQHandler...

1.1.2 代码段 (.text)

代码段存的是所有函数编译后的机器指令,CPU 从这里取指令执行。

包含内容:

  • 启动代码 (Reset_Handler, SystemInit)
  • main() 函数及你写的所有函数
  • RT-Thread 内核代码 (rt_thread_create, rt_schedule...)【ps:有用到RT-Thread的情况下】
  • 库函数代码 (HAL_GPIO_WritePin, printf...)

1.1.3 只读数据段 (.rodata)

只读数据段存的是程序运行期间不会改变的数据。

典型内容:

  • const char *msg = "Hello"; → 字符串 "Hello" 存这里
  • const int table[] = {1,2,3}; → 常量数组
  • switch-case 跳转表
  • 浮点常量、查找表

1.1.4 读写数据段 (.rwdata)

读写数据段存的是已初始化全局变量的初始值,启动时会被拷贝到 SRAM。

为什么需要这个?

  • int counter = 100; → 100 这个初始值存在 FLASH
  • 因为 SRAM 掉电丢失,每次上电需要从 FLASH 恢复初始值
  • 启动代码负责把这块数据拷贝到 SRAM 的 .data 段

总结:

FLASH 是"只读"的程序存储器,存放不变的东西:代码、常量、以及变量的初始值模板。CPU 执行代码时从 FLASH 取指令。

1.2 SRAM里存了什么?

  • .data 段(已初始化全局变量,从 FLASH 拷贝来)
  • .bss 段(未初始化全局变量,启动时清零)
  • 堆(rt_malloc 动态分配区)
  • 栈(局部变量、函数调用)

1.2.1 已初始化数据段 (.data)

.data段存的是有初始值的全局变量和静态变量,值从 FLASH 拷贝而来。

哪些变量在这里:

  • int g_counter = 100; → 全局变量,初始值 100
  • static int s_flag = 1; → 静态变量,初始值 1
  • char g_name[] = "LED"; → 初始化的数组

**特点:**可读可写,程序运行时可以修改这些值

1.2.2 未初始化数据段 (.bss)

.bss段存的是未初始化或初始化为 0 的全局/静态变量,启动时被清零。

哪些变量在这里:

  • int g_value; → 未初始化,默认 0
  • static char buffer[256]; → 未初始化的数组
  • int g_zero = 0; → 显式初始化为 0

**为什么单独分出来?**不需要在 FLASH 中存储初始值,节省 FLASH 空间

1.2.3 堆区 (Heap)

动态内存分配区域,如果使用了RT-Thread,则由 RT-Thread 内存管理器管理。裸机开发则在.s启动文件中进行配置堆区的空间大小。

谁使用堆:

  • malloc()
  • rt_malloc() / rt_free()
  • rt_thread_create() → 分配 TCB + 线程栈
  • rt_mq_create() → 消息队列缓冲区
  • rt_sem_create() → 信号量控制块

RT-Thread 堆初始化:
rt_system_heap_init(heap_begin, heap_end);

1.2.4 栈区 (Stack)

函数调用的工作区:局部变量、返回地址、寄存器保存

主栈 (MSP) 用于:

  • 启动代码执行 (Reset_Handler → main)
  • 所有中断处理函数

线程栈 (PSP) 用于:

  • 每个 RT-Thread 线程有独立的栈
  • 线程的局部变量、函数调用链

栈上存什么:

  • int local_var = 5; → 局部变量
  • 函数参数、返回地址、保存的寄存器

总结:

SRAM 是"可读写"的运行时存储器,存放会变化的东西:全局变量、动态分配的内存、函数调用栈。掉电后内容全部丢失。

2. 时间维度上内存的变化过程

图2.1-时间维度上内存的变化过程

2.1 硬件复位 - 读取向量表

  1. CPU 复位,所有寄存器清零
  2. 硬件自动从 0x08000000 读取 4 字节 → 加载到 MSP
  3. 硬件自动从 0x08000004 读取 4 字节 → 加载到 PC
  4. CPU 开始从 PC 指向的地址取指令执行

2.2 Reset_Handler 开始执行

  1. 调用 SystemInit() 配置时钟、Flash等待周期
  2. 准备进行内存初始化
  3. 此时还不能使用全局变量!(因为还没初始化)

2.3 .data 段初始化 - 从 FLASH 拷贝到 SRAM

  1. 找到 FLASH 中 .data 初始值的位置
  2. 找到 SRAM 中 .data 段的位置 (_sdata)
  3. 逐字节拷贝,直到 _edata
  4. 所有带初始值的全局变量现在有了正确的值

2.4 .bss 段清零

  1. 找到 .bss 段的起始 (_sbss) 和结束 (_ebss)
  2. 将整个区域填充为 0
  3. 所有未初始化的全局变量现在是 0

2.5 进入 main() → RT-Thread 初始化

  1. 调用 main()
  2. RT-Thread 初始化内核
  3. rt_system_heap_init() 初始化堆内存管理器
  4. 现在可以使用 rt_malloc() 了

2.6 创建线程 → 调度器启动

  1. rt_thread_create() 从堆中分配 TCB + 栈
  2. 初始化线程栈帧
  3. rt_thread_startup() 将线程加入就绪队列
  4. rt_system_scheduler_start() 启动调度
  5. 切换到 PSP,第一个线程开始运行

总结:

上电后:硬件设置 MSP/PC → 启动代码拷贝 .data → 清零 .bss → 初始化堆 → 创建线程 → 运行

相关推荐
m0_553210043 小时前
stm32f407移植modbus协议
stm32·单片机·嵌入式硬件
@good_good_study3 小时前
STM32 static void、extern void、void
stm32·单片机
三佛科技-187366133973 小时前
LP3524B高耐压200V隔离型LLC同步整流控制芯片典型电路
单片机·嵌入式硬件
llilian_163 小时前
精准时序赋能千行百业——IEEE1588PTP授时主时钟应用解析 PTP授时服务器 IEEE1588主时钟
运维·服务器·网络·嵌入式硬件·其他
国科安芯3 小时前
RISC-V怎么实现核间中断?核心本地中断控制器(CLINT)深度解析
网络·stm32·单片机·嵌入式硬件·架构·risc-v·安全性测试
bai5459363 小时前
STM32 CubeIDE(1.18.0) LED闪烁
stm32·单片机·嵌入式硬件
@good_good_study4 小时前
STM32 高级定时器中断实验
stm32·单片机
小李做物联网4 小时前
【单片机毕设】136.1基于单片机stm32排队叫号系统-三窗口物联网嵌入式项目程序
stm32·单片机·嵌入式硬件·物联网
d111111111d4 小时前
什么是野指针,在STM32中如何避免野指针问题
笔记·stm32·单片机·嵌入式硬件·学习