文章目录
在 SRAM 中运行 STM32 程序
背景
听说 STM32 的 FLASH 只能擦写 10000 次,一个开发板就算 1 天擦写 10 次 10000/10/365=2.73972602739726,那岂不是一个开发板用两年之后芯片就废了,好方啊,有没有 (其实如果你能做到 10000 次擦写,估计这个开发板你已经玩透了,把芯片刷过 flash 寿命,菜鸟也变成老鸟了)。如果你实在担心擦写 10000 次,那就可以试试在 RAM 中调试代码。
STM32 的自举配置

- 主 flash:即从内部 flash 启动,keil 下载程序默认就是下载到这里的,10000 次擦写指的也是这里,发布版本的程序也是这种启动模式
- SRAM:即从 RAM 中启动程序,调试的时候可以从 RAM 中启动
- 系统存储器:一般用于 ISP(in system program)
理论知识
一般情况下,我们在 MDK 中编写工程应用后,调试时都是把程序下载到芯片的内部 FLASH 运行测试的,代码的 CODE,RO-data 及 RW-data 的内容被写入到内部 FLASH 中存储。
但在某些应用场合下却不希望或不能修改内部 FLASH 的内容,这时就可以使用 RAM 调试功能了,它的本质是把原来存储在内部 FLASH 的代码 (CODE ,RO-data 及 RW-data 的内容) 改为存储到 SRAM 中 (内部 SRAM 或外部 SDRAM 均可),芯片复位后从 SRAM 中加载代码并运行。
优点
把代码下载到 RAM 中调试有如下优点:
- 下载程序速度非常快。 RAM 存储器的写入速度比在内部 FLASH 中要快得多,且没有擦除过程,因此在 RAM 上调试程序时程序几乎是秒下的,对于需要频繁改动代码的调试过程,能节约很多时间,省去了烦人的擦除与写入 FLASH 过程。
- 不改写内部 FLASH 的原有程序。
- 对于内部 FLASH 被锁定的芯片,可以把解锁程序下载到 RAM 上,进行解锁。
缺点
把代码下载到 RAM 中调试有如下缺点:
- 存储在 RAM 上的程序掉电后会丢失,不能像 FLASH 那样保存。
- 若使用 STM32 的内部 SRAM 存储程序,程序的执行速度与在 FLASH 上执行速度无异,但 SRAM 空间较小。
- 若使用外部扩展的 SDRAM 存储程序,程序空间非常大,但 STM32 读取 SDRAM 的速度比读取内部 FLASH 慢,这会导致程序总执行时间增加,因此在 SDRAM 中调试的程序无法完美仿真在内部 FLASH 运行时的环境。另外,由于 STM32 无法直接从 SDRAM 中启动且应用程序复制到 SDRAM 的过程比较复杂 (下载程序前需要使 STM32 能正常控制 SDRAM),所以在很少会在 STM32 的 SDRAM 中调试程序
在内部 SRAM 中调试代码
创建工程的调试版本
由于在 SRAM 中运行的代码一般只是用于调试,调试完毕后,在实际生产环境中仍然使用在内部 FLASH 中运行的代码,因此我们希望能够便捷地在调试版和发布版代码之间切换。 MDK 的 Manage Project Items 可实现这样的功能,使用它可管理多个不同配置的工程,点击 Manage Project Items 按钮,在弹出对话框左侧的 Project Target 一栏包含了原工程的名字,如图中的原工程名为 tinyusb,右侧是该工程包含的文件。
为了便于调试,我们在左侧的 Project Target 一栏添加一个工程名,如图中输入 ram_code,输入后点击 OK 即可,这个 ram_code 版本的工程会复制原 tinyusb 工程的配置,后面我们再进行修改。

当需要切换工程版本时,点击 MDK 工程名的下拉菜单可选择目标工程,在不同的工程中,所有配置都是独立的,例如芯片型号、下载配置等等,
但如果两个工程共用了同一个文件,对该文件的修改会同时影响两个工程,例如这两个工程都使用同一个 main.c 文件,我们在 main.c 文件修改代码,两个工程都会被修改。
配置分散加载文件
在原来工程的 .sct 文件的工程目录下新建一个 ram.sct 文件,在 Options for Target->linker 的选项中取消勾选第一个 √,自己把刚刚新建的分散加载文件添加进来并 edit
这里注意修改 R/O Base 和 R/W Base

在 ram.sct 文件中添加如下内容
LR_IROM1 0x20000000 0x00010000 { ; load region size_region 虚拟的 flash
ER_IROM1 0x20000000 0x00010000 { ; load address = execution address 虚拟的 flash 地址和大小
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
}
RW_IRAM1 0x20010000 0x00020000 { ; RW data 虚拟 SRAM 地址和大小
.ANY (+RW +ZI)
}
}
在这个分散加载文件配置中,把原本分配到内部 FLASH 空间的加载域和执行域改到了以地址 0x20000000 开始的 64KB(0x00010000) 空间,
而 RW data 空间改到了以地址 0x20010000 开始的 128KB 空间 (0x00020000)。
也就是说,它把 STM32 的内部 SRAM 分成了虚拟 ROM 区域以及 RW data 数据区域,链接器会根据它的配置给工程中的各种内容分配到 SRAM 地址。
配置中断向量表
在 system_stm32f4xx.c 文件中 SystemInit() 函数中有如下定义:
void SystemInit(void)
{
/* FPU settings ------------------------------------------------------------*/
#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */
#endif
#if defined (DATA_IN_ExtSRAM) || defined (DATA_IN_ExtSDRAM)
SystemInit_ExtMemCtl();
#endif /* DATA_IN_ExtSRAM || DATA_IN_ExtSDRAM */
/* Configure the Vector Table location -------------------------------------*/
#if defined(USER_VECT_TAB_ADDRESS)
SCB->VTOR = VECT_TAB_BASE_ADDRESS | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
#endif /* USER_VECT_TAB_ADDRESS */
}
代码中根据是否存储宏定义 USER_VECT_TAB_ADDRESS 来决定 VTOR 的配置,默认情况下代码中没有定义宏 USER_VECT_TAB_ADDRESS,VTOR 默认情况下指示向量表是存储在内部 FLASH 空间的。由于本工程的分散加载文件配置,在启动文件中定义的中断向量表会被分配到 SRAM 空间,所以我们要定义这个宏,使得 SystemInit 函数修改 VTOR 寄存器,向内核指示向量表被存储到内部 SRAM 空间了。
在该宏中可以选择 VECT_TAB_SRAM,这样就将 VTOR 配置到 SRAM_BASE(0x20000000) 了
#if defined(USER_VECT_TAB_ADDRESS)
/*!< Uncomment the following line if you need to relocate your vector Table
in Sram else user remap will be done in Flash. */
/* #define VECT_TAB_SRAM */
#if defined(VECT_TAB_SRAM)
#define VECT_TAB_BASE_ADDRESS SRAM_BASE /*!< Vector Table base address field.
This value must be a multiple of 0x200. */
#define VECT_TAB_OFFSET 0x00000000U /*!< Vector Table base offset field.
This value must be a multiple of 0x200. */
#else
#define VECT_TAB_BASE_ADDRESS FLASH_BASE /*!< Vector Table base address field.
This value must be a multiple of 0x200. */
#define VECT_TAB_OFFSET 0x00000000U /*!< Vector Table base offset field.
This value must be a multiple of 0x200. */
#endif /* VECT_TAB_SRAM */
#endif /* USER_VECT_TAB_ADDRESS */
Options for Target-> c/c++ ->Define 框中输入宏 USER_VECT_TAB_ADDRESS 和 VECT_TAB_SRAM,注意它与其它宏之间要使用英文逗号分隔开。注意不要在在 system_stm32f4xx.c 文件中定义这个宏,因为在 system_stm32f4xx.c 文件修改后下载到 flash 的代码也是使用这个宏了,所以在工程选项卡中定义
修改 flash 的下载配置
得到 SRAM 版本的代码指令后,为了把它下载到芯片的 SRAM 中,还需要修改下载器的配置
Options for Target->Utilities->Settings
- do not erase
- RAM for Algorithm 即程序的 RAM 空间 指
编程算法(Programming Algorithm) 可使用的 RAM 空间,下载程序到 FLASH 时运行的编程算法需要使用 RAM 空间,在默认配置中它的首地址为 0x20000000,即内部 SRAM 的首地址,但由于我们的分散加载文件配置, 0x20000000 地址开始的 64KB 实际为虚拟 ROM 空间,实际的 RAM 空间是从地址 0x20010000 开始的,所以这里把算法 RAM 首地址更改为本工程中实际作为 RAM 使用的地址。若编程算法使用的 RAM 地址与虚拟 ROM 空间地址重合的话,会导致下载出错。 - Programming Algorithm 即 flash 的地址和大小 设置内部 FLASH 的编程算法,编程算法主要描述了 FLASH 的地址、大小以及扇区等信息, MDK 根据这些信息把程序下载到芯片的 FLASH 中,不同的控制器芯片一般会有不同的编程算法。由于 MDK 没有内置 SRAM 的编程算法,所以我们直接在原来的基础上修改它的基地址和空间大小,把它改成虚拟 ROM 的空间信息。

下载程序
BOOT0 和 BOOT1 设置为 SRAM 启动,芯片掉电后这个存储在 SRAM 的程序会丢失,想恢复的话必须要重新下载程序
工程地址
参考资料:零死角玩转 STM32
文章介绍了如何在STM32的SRAM中运行程序,以减少对FLASH的擦写次数,延长芯片寿命。这种方法在调试阶段尤其有用,可以快速下载程序,但程序不会在断电后保留。文章详细阐述了创建调试版本工程、配置分散加载文件、修改中断向量表以及调整下载配置的步骤,并提到了其优缺点。
2302

被折叠的 条评论
为什么被折叠?



