[STM32 - 野火] - - - 固件库学习笔记 - - - 十六.在SRAM中调试代码
一、简介
在RAM中调试代码是一种常见的嵌入式开发技术,尤其适用于STM32等微控制器。它的核心思想是将程序代码和数据加载到微控制器的内部RAM(SRAM)中运行,而不是运行在Flash存储器中。这种方法在开发过程中具有显著的优势,但也有一些限制。
1.1 为什么要在RAM中调试代码?
-
1、保护Flash存储器:
-
Flash存储器的寿命有限:Flash存储器的擦写次数是有限的(例如,STM32的Flash通常支持10,000次擦写)。频繁的调试和代码烧录可能会加速Flash的磨损。
-
减少擦写次数:将代码运行在RAM中可以避免对Flash的频繁擦写,从而延长Flash的使用寿命。
-
-
2、提高调试效率:
-
快速修改和测试:在RAM中运行代码时,可以快速修改代码并重新加载,而无需擦除和重新烧录Flash。这大大加快了调试速度。
-
动态调试:RAM中的代码可以动态修改,适合进行复杂的调试和测试,例如实时修改变量或函数逻辑。
-
-
3、支持高级调试功能:
-
断点和单步调试:在RAM中运行代码时,调试器可以更灵活地设置断点和进行单步调试,而不会受到Flash存储器的限制。
-
动态内存分配:某些调试功能(如动态内存分配和堆栈跟踪)在RAM中更容易实现。
-
1.2 在RAM中调试代码的优势
-
1、在RAM上调试程序时,下载速度非常快:
与内部FLASH相比,RAM存储器的写入速度要快得多,且无需擦除过程。因此,程序几乎是秒下,这为需要频繁修改代码的调试过程节省了大量时间,省去了烦人的擦除与写入FLASH的步骤。此外,虽然STM32的内部FLASH可擦除次数通常为1万次,一般的调试过程不太可能达到这个次数导致FLASH失效,但这也确实是一个考虑使用RAM的因素。
-
2、在RAM上调试程序时,不会改写内部FLASH的原有程序。
-
3、对于内部FLASH被锁定的芯片,还可以将解锁程序下载到RAM上,进行解锁操作。
1.3 在RAM中调试代码的缺陷
-
1、存储在RAM中的程序在掉电后会丢失,无法像存储在FLASH中那样持久保存。
-
2、如果使用STM32的内部SRAM存储程序,程序的执行速度与在FLASH上执行时基本相同,但内部SRAM的空间相对较小,可能会限制程序的大小。
-
3、如果使用外部扩展的SRAM存储程序,虽然程序空间可以非常大,但STM32读取外部SRAM的速度比读取内部FLASH慢,这会导致程序的总执行时间增加。因此,在外部SRAM中调试的程序无法完全模拟在内部FLASH中运行时的真实环境。
- 此外,STM32无法直接从外部SRAM启动,且将应用程序复制到外部SRAM的过程较为复杂(在下载程序之前,需要确保STM32能够正常控制外部SRAM)。因此,实际开发中很少会在STM32的外部SRAM中调试程序。
二、STM32的启动方式
CM-3 内核在离开复位状态后的工作过程如下图:

-
1、从地址 0x00000000 处取出栈指针 MSP 的初始值,该值就是栈顶的地址。
- 程序局部变量存储在栈空间中,MSP指向栈顶,防止栈溢出。
-
2、从地址 0x00000004 处取出程序指针 PC 的初始值,该值指向复位后应执行的第一条指令。
上述过程由内核自动设置运行环境并执行主体程序,因此它被称为自举过程。

2.1 MSP指针
MSP(Main Stack Pointer,主堆栈指针)是用于管理堆栈的一个重要寄存器,系统复位后,MSP的值会被设置为向量表的第一个值(通常是堆栈的初始地址),用于初始化堆栈。

Stack_Size EQU 0x00000400 // 定义了堆栈的大小为 0x00000400 字节(1024 字节)
AREA STACK, NOINIT, READWRITE, ALIGN=3 // 定义了一个名为 STACK 的内存区域,该区域未初始化(NOINIT),可读写(READWRITE),并且对齐到 2^3(即 8 字节)边界。
Stack_Mem SPACE Stack_Size // 在堆栈区域中分配了 Stack_Size 大小的空间。
__initial_sp // 表示堆栈指针的初始值将被设置为 Stack_Mem 的地址加上 Stack_Size 的大小,即堆栈空间的顶部。
2.2 PC指针
PC指针(程序计数器)是一个寄存器,用于存储下一条指令的地址。

-
1、系统复位后,首先调用 SystemInit 函数来初始化硬件;
-
2、然后跳转到 __main 函数,由它完成C运行时环境的初始化(如全局变量的初始化);
-
3、最终,程序会跳转到 main() 函数(由 __main 调用),开始执行用户程序。
PC指向Reset_Handler,跳转到Reset_Handler函数执行初始化时钟、调用main函数,最终进入main函数中执行程序。
2.3 STM32的三种启动方式
虽然内核默认访问的地址是 0x00000000 和 0x00000004,但这些地址实际上可以被重映射到其他地址空间。以 STM32F103 为例,根据芯片引脚 BOOT0 和 BOOT1 的电平状态,这两个地址可以被映射到内部 FLASH、内部 SRAM 或系统存储器。具体的映射配置取决于 BOOT 引脚的不同设置,具体映射关系见表 BOOT 引脚设置对 0 地址的映射。

当内核离开复位状态后,会从映射的地址中获取初始值,分别赋给主堆栈指针(MSP)和程序计数器(PC),然后开始执行指令。通常,我们会根据这些地址所映射到的存储器类型(如内部FLASH、SRAM或系统存储器)来区分不同的自举过程。
2.3.1 内部 FLASH 启动方式
当芯片上电后,若采样到 BOOT0 引脚为低电平,则 0x00000000 和 0x00000004 地址会被映射到内部 FLASH 的首地址 0x08000000 和 0x08000004。因此,内核在离开复位状态后,会从内部 FLASH 的 0x08000000 地址读取内容并赋值给主堆栈指针(MSP),作为栈顶地址;再从内部 FLASH 的 0x08000004 地址读取内容并赋值给程序计数器(PC),作为第一条指令的地址。具备这两个条件后,内核便开始从 PC 指向的地址中读取并执行指令。
2.3.2 内部 SRAM 启动方式
当芯片上电后,若采样到 BOOT0 和 BOOT1 引脚均为高电平,则 0x00000000 和 0x00000004 地址会被映射到内部 SRAM 的首地址 0x20000000 和 0x20000004。此时,内核会从 SRAM 空间获取内容以完成自举过程。
在实际应用中,0x00000000 和 0x00000004 地址存储的内容由启动文件(如 startup_stm32f10x.s)定义。而在链接阶段,分散加载文件(.sct 文件)会决定这些内容的最终存储位置,即它们会被分配到内部 FLASH 还是内部 SRAM。
2.3.3 内部 SRAM 启动方式
当芯片上电后,若采样到 BOOT0 引脚为高电平且 BOOT1 引脚为低电平时,内核将从系统存储器的 0x1FFFF000 和 0x1FFFF004 地址获取主堆栈指针(MSP)和程序计数器(PC)的值进行自举。
系统存储器是一段特殊的存储空间,用户无法直接访问。ST公司在芯片出厂前在系统存储器中固化了一段代码。当使用系统存储器启动时,内核会执行这段代码,该代码运行时会为 ISP(In System Program,系统内编程) 提供支持。具体来说,它会检测通过 USART1/2、CAN2 或 USB 通讯接口传输过来的信息,并根据这些信息更新内部 FLASH 的内容,从而实现产品应用程序的升级。因此,这种启动方式也被称为 ISP 启动方式。
三、内部FLASH的启动过程
在启动代码的中间部分,通过汇编指令 DCD(Define Constant Data),将 __initial_sp 和 Reset_Handler 的地址定义在了代码段的最前面,从而确保它们被放置在指定的地址空间。

在启动文件中,将栈顶地址和首条指令地址(__initial_sp 和 Reset_Handler)放置在代码的最前面,但这并不直接指定它们的绝对地址。这些内容的绝对地址是由链接器根据分散加载文件**(*.sct)**分配的。

上图为 STM32F103 的默认分散加载文件配置。
-
LR_IROM1:表示一个加载区域(Load Region),名称为 LR_IROM1。
- 0x08000000:加载区域的起始地址(这里是内部FLASH的起始地址);0x00080000:加载区域的大小(这里是512KB)。
-
ER_IROM1(内部FLASH):表示一个执行区域(Execution Region),名称为 ER_IROM1。
- 0x08000000:执行区域的起始地址(与加载地址相同,表示代码加载后直接在该地址执行);0x00080000:执行区域的大小(这里是 512KB)。
-
代码段分配:
*.o (RESET, +First) // 将所有对象文件中定义的 RESET 段(通常是中断向量表和复位处理程序)放在最前面。 *(InRoot$$Sections) // 将所有对象文件中定义的 InRoot 段(通常是启动代码和初始化代码)放在后面。 .ANY (+RO) // 将所有只读(Read-Only)段分配到该区域。 .ANY (+XO) // 将所有可执行但不读取的段分配到该区域。如果把*.o (RESET, +First)放到RW_IRAM1中,那么MSP指针跟PC指针就会指向0x20000000跟0x20000004的地址。
-
RW_IRAM1(RAM空间):表示一个读写区域(Read-Write Region),名称为 RW_IRAM1。
-
0x20000000:读写区域的起始地址(这里是内部 SRAM 的起始地址);0x00010000:读写区域的大小(这里是 64KB)。
-
.ANY (+RW +ZI):将所有读写(Read-Write)和零初始化(Zero-Initialized)的段分配到该区域。
-
在分散加载文件中,加载区和执行区的首地址都被设置为 0x08000000,这恰好是内部 FLASH 的起始地址。因此,汇编文件中定义的栈顶地址和首条指令地址会被存储到 0x08000000 和 0x08000004 的地址空间中。
类似地,如果修改分散加载文件,将加载区和执行区的首地址设置为内部 SRAM 的起始地址 0x20000000,那么栈顶地址和首条指令地址将会被存储到 0x20000000 和 0x20000004 的地址空间中。
四、将代码修改为RAM自举
- 1、设置一个RAM调试的工程:

- 2、在C/C++中添加宏VECT_TAB_SRAM:

此宏在SystemInit函数中。
如果定义了 VECT_TAB_SRAM,则将中断向量表重定向到内部 SRAM 的指定位置;
如果未定义 VECT_TAB_SRAM,则将中断向量表重定向到内部 FLASH 的指定位置。
注意,两个宏之间用","分开。
-
3、打开.sct文件:

-
4、修改.sct文件,把程序分配到SRAM:

修改前的空间大小。

修改后的空间大小:原本的RAM空间为64K,现分一半用来做FLASH,一半用来做RAM
-
5、修改下载配置,把程序下载到SRAM:


-
选择Do not Erase是因为程序下载到RAM中,不需要修改FLASH,勾选Erase Full Chip或Erase Sectors的话会修改FLASH,导致程序下载失败。
-
RAM for Algorithm:烧录算法(Flash Programming Algorithm)预留的RAM空间。
烧录过程中的临时存储:在将程序烧录到Flash时,烧录算法需要运行,而这个算法需要一定的RAM空间来存储其运行时的数据和代码。
仅在烧录时使用:一旦烧录完成,这段RAM空间会被释放,可供应用程序(APP代码)使用。
-
-
6、修改调试器配置,初始化SP和PC指针:

/******************************************************************************/
/* Debug_RAM.ini: Initialization File for Debugging from Internal RAM */
/******************************************************************************/
/* This file is part of the uVision/ARM development tools. */
/* Copyright (c) 2005-2014 Keil Software. All rights reserved. */
/* This software may only be used under the terms of a valid, current, */
/* end user licence from KEIL for a compatible version of KEIL software */
/* development tools. Nothing else gives you the right to use this software. */
/******************************************************************************/FUNC void Setup (void) {SP = _RDWORD(0x20000000); // 设置栈指针SP,把0x20000000地址中的内容赋值到SP。PC = _RDWORD(0x20000004); // 设置程序指针PC,把0x20000000地址中的内容赋值到PC。_WDWORD(0xE000ED08, 0x20000000); // Setup Vector Table Offset Register
}LOAD %L INCREMENTAL // 下载axf文件到RAM
Setup(); //调用上面定义的setup函数设置运行环境 //g, main //跳转到main函数,本示例调试时不需要从main函数执行,注释掉了,程序从启动代码开始执行
这里是强制将SP、PC指针强制指向了0x20000000与0x20000000。
- 7、将工程更改为RAM调试的工程:

调试的时候不能点DOWNLOAD,要点DEBUG。
在DEBUG的时候,想要复位,不能点RST按键,要退出DEBUG后再重新点DEBUG。
相关文章:
[STM32 - 野火] - - - 固件库学习笔记 - - - 十六.在SRAM中调试代码
一、简介 在RAM中调试代码是一种常见的嵌入式开发技术,尤其适用于STM32等微控制器。它的核心思想是将程序代码和数据加载到微控制器的内部RAM(SRAM)中运行,而不是运行在Flash存储器中。这种方法在开发过程中具有显著的优势&#…...
雷军推荐:WPS 与 Pastemate 联用,效率飞升新高度
在当今快节奏的工作与学习环境中,效率提升成为了每个人都在追求的目标。而雷军,这位科技界的领军人物,凭借其敏锐的洞察力,为我们推荐了一组强大的工具组合 ——WPS 与 Pastemate,它们携手合作,能够为我们的…...
css主题色修改后会多出一个css吗?css怎么定义变量?
在 CSS 中修改主题色时,通常不会直接生成一个新的 CSS 文件,而是通过 CSS 变量(Custom Properties) 或 预处理器变量(如 Sass、Less) 来动态修改样式。以下是详细说明: 1. CSS 变量(…...
ubuntu22.4搭建单节点es8.1
下载对应的包 elasticsearch-8.1.1-linux-x86_64.tar.gz 创建es租户 groupadd elasticsearc useradd elasticsearch -g elasticsearch -p elasticsearch chmod uw /etc/sudoers chmod -R elasticsearch:elasticsearch elasticsearch 修改配置文件 vim /etc/sysctl.conf vm…...
轴承故障特征—SHAP 模型 3D 可视化
往期精彩内容: Python-凯斯西储大学(CWRU)轴承数据解读与分类处理 基于FFT CNN - BiGRU-Attention 时域、频域特征注意力融合的轴承故障识别模型-CSDN博客 基于FFT CNN - Transformer 时域、频域特征融合的轴承故障识别模型-CSDN博客 P…...
斐波那契数列模型:在动态规划的丝绸之路上追寻斐波那契的足迹(上)
文章目录 引言递归与动态规划的对比递归解法的初探动态规划的优雅与高效自顶向下的记忆化搜索自底向上的迭代法 性能分析与比较小结 引言 斐波那契数列,这一数列如同一条无形的丝线,穿越千年时光,悄然延续其魅力。其定义简单而优美ÿ…...
智能选路+NAT实验
1.实验拓扑: 二.实验配置 1、防火墙ip配置和信任区域配置: 2.导入地址库:先下载模板--->进入模板修改地址信息--->导入地址: 3配置链路接口: 4.配置真实DNS服务器信息 5.创建虚拟服务,虚拟DNS服务…...
电商API接口数据与市场趋势分析的深度融合
一、电商API接口数据的价值 电商API接口是连接电商平台与外部系统(如数据分析工具、ERP系统等)的桥梁。通过API接口,企业可以获取海量的交易数据、用户行为数据、商品信息等。这些数据具有以下价值: 数据实时性:API接…...
SMOJ 种植玉米/铺地砖 题解
最近练了轮廓线dp的题目 1.种植玉米 题意 农夫有一个被划分成 m m m行 n n n列的农田。 每个格子的数字如果是 1 1 1则表示该格子的土地是肥沃的,可以种植玉米;如果该格子的数字是 0 0 0则表示该格子不能种植玉米。 但是还有一个条件:不…...
沃丰科技大模型标杆案例 | 索尼大模型智能营销机器人建设实践
AI大模型发展日新月异,国内外主流大模型每月必会升级。海外AI大模型市场由美国主导, 各模型已形成“多强竞合”的局面。中国积极响应全球大模型技术的发展趋势,高校、研究院所等科研机构、互联网企业,人工智能企业均不同程度地投入…...
【pytest】编写自动化测试用例命名规范README
API_autoTest 项目介绍 1. pytest命名规范 测试文件: 文件名需要以 test_ 开头或者以 _test.py 结尾。例如,test_login.py、user_management_test.py 这样的命名方式,pytest 能够自动识别并将其作为测试文件来执行其中的测试用例。 测试类…...
双亲委派机制介绍
双亲委派机制(Parent Delegation Model)是Java类加载器(Class Loader)的一种机制,用于确保Java应用程序的安全性和稳定性。 在Java中,类加载器负责将类的字节码文件加载到Java虚拟机(JV…...
fps僵尸:8.丧尸死亡
文章目录 思路死亡时关闭碰撞死亡时开启物理模拟 实现胶囊体关闭碰撞网格体开启物理模拟(两个前提)网格体开启物理碰撞网格体绑定物理资产 注解胶囊体关闭碰撞,则整个蓝图关闭碰撞 思路 死亡时关闭碰撞 死亡时开启物理模拟 实现 胶囊体关闭碰撞 网格体开启物理…...
内存泄漏是什么?
内存泄漏 概述: 程序在运行过程中,动态分配的内存未被及时释放,导致这些内存无法再次使用,最终导致系统内存耗尽,影响程序性能,甚至导致程序崩溃 原因: 未释放已分配的内存:在使用…...
Zipkin 和 SkyWalking 区别
Zipkin 和 SkyWalking 都是分布式追踪和监控工具,但它们在架构设计、功能、扩展性以及适用场景上有所不同。下面是它们的主要区别: 1. 架构和设计 Zipkin: Zipkin 是一个轻量级的分布式追踪系统,通常与 Spring Cloud Sleuth 配合…...
hive如何导出csv格式文件
方法一:使用 Hive 自带功能结合脚本处理 步骤 1:使用 hive -e 命令导出数据到文件 可以通过在命令行中使用 hive -e 执行查询语句,并将结果重定向到本地文件,不过默认是不带字段头的。 hive -e "SELECT column1, column2,…...
【Java项目】基于SpringBoot的【休闲娱乐代理售票系统】
【Java项目】基于SpringBoot的【休闲娱乐代理售票系统】 技术简介:系统软件架构选择B/S模式、SpringBoot框架、java技术和MySQL数据库等,总体功能模块运用自顶向下的分层思想。 系统简介:休闲娱乐代理售票系统,在系统首页可以查看…...
MMLU论文简介
评测语言模型的“全能性”:MMLU基准测试解析 加州大学伯克利分校、哥伦比亚大学等机构的研究团队提出一项全新的评测基准——MMLU(Massive Multitask Language Understanding)。这项测试覆盖57个学科,从基础数学到专业法律&#…...
EasyRTC:开启智能硬件与全平台互动新时代
在当今数字化时代,实时音视频互动已成为企业与用户沟通、协作和娱乐的关键技术。无论是在线教育、视频会议、远程医疗还是互动直播,流畅、高效的互动体验都是成功的关键。然而,实现跨平台、低延迟且功能丰富的音视频互动并非易事——直到 Eas…...
【数据分析】2.数据分析业务全流程
业务流程方法论:3阶段6步骤 一、课程核心内容结构 1. 方法论概述 目标:系统性地解决商业中的关键问题框架:分为三个阶段,每个阶段包含两个步骤适用场景:适用于数据分析师、业务经理等需要通过数据分析支持决策的从业…...
禁止WPS强制打开PDF文件
原文网址:禁止WPS强制打开PDF文件_IT利刃出鞘的博客-CSDN博客 简介 本文介绍如何避免WPS强制打开PDF文件。 方法 1.删除注册表里.pdf的WPS绑定 WinR,输入:regedit,回车。找到:HKEY_CLASSES_ROOT\.pdf删除KWPS.PDF…...
树莓百度百科新动态:宜宾项目的深远影响与意义
在树莓集团的百度百科词条中,宜宾项目的新动态备受关注,其深远影响与意义不容忽视。 从产业发展角度来看,宜宾项目带动了当地数字产业的集聚。树莓集团在宜宾建设的多个数字产业园区,吸引了众多上下游企业入驻。形成了从芯片研发…...
mysql索引为什么用B+树不用,B树或者红黑树
MySQL 选择 B 树作为索引结构,而不是 B 树或红黑树,主要原因如下: 1. 磁盘 I/O 优化 B 树:节点存储更多键值,树的高度较低,减少了磁盘 I/O 次数,适合处理大规模数据。 B 树:虽然也…...
DeepSeek 云原生分布式部署的深度实践与疑难解析—— 从零到生产级落地的全链路避坑指南
一、云原生环境下的部署架构设计 1.1 典型架构拓扑 关键点:Master 节点需保证强一致性,Worker 节点需支持异构硬件调度。 1.2 配置模板陷阱 问题现象: 直接使用官方 Helm Chart 部署后出现 Pod 频繁重启 日志报错 ResourceQuota exceeded…...
【笑着写算法系列】位运算
前言 位运算可以说是一个算法里面比较神奇的算法,利用这个算法可以用极少的资源来完成一些运算,主要得力于位运算的一些特殊的性质。 在进行题目练习之前我们先了解一下有关位运算的一些主要作用: 确定一个数n的第x位二进制位是0还是1,我们可以使用(&a…...
【CCF CSP-J 2020】优秀的拆分
前言 请勿抄袭。 思路 二进制操作题。 首先,根据题意,如果给定的 n n n 是奇数那么直接输出 -1。 然后,可以发现题目是要求我们把 n n n 拆成 2 a 1 2 a 2 . . . 2 a x 2^{a_1}2^{a_2}...2^{a_x} 2a12a2...2ax 这种形式。 看…...
chrome V3插件开发,调用 chrome.action.setIcon,提示路径找不到
问题描述: chrome V3插件开发,调用 chrome.action.setIcon,提示路径找不到。 解决问题过程: chrome插件v2版本中设置插件图标接口是:chrome.browserAction.setIcon。v3 版本种接口是 chrome.action.setIcon。同样的…...
大数据组件(四)快速入门实时数据湖存储系统Apache Paimon(2)
大数据组件(四)快速入门实时数据湖存储系统Apache Paimon(2) 我们上次已经了解了Paimon的下载及安装,并且了解了主键表的引擎以及changelog-producer的含义 大数据组件(四)快速入门实时数据湖存储系统Apache Paimon(1) 今天,我们继续快速了解下最近比…...
多模态机器学习火热idea汇总!
想发论文,却完全没头绪?那我非常推荐你关注这个潜力方向:多模态机器学习! 它能够把不同模态的数据,映射到统一的高维向量空间,实现模态间的语义对齐,从而促进模态间的相互理解,提高…...
【MySQL】简单掌握数据类型与表操作,让数据库性能飞跃
个人主页:♡喜欢做梦 欢迎 👍点赞 ➕关注 ❤️收藏 💬评论 目录 🌳一、数据类型 🍃1.数值类型 🍂整型类型 🍂浮点型类型 🍂定点数类型 🍃2.字符串类型 3.&am…...

