STM32性能飞跃:函数内存RAM执行提速秘籍!
在嵌入式开发中,优化系统的执行效率是每个工程师都需要关注的核心问题。STM32等基于ARM Cortex-M的微控制器通常将程序代码存储在闪存(Flash)中,从闪存中读取并执行。虽然这种方式在大多数情况下已经足够,但在一些关键应用场景中,程序执行的速度和响应时间可能成为瓶颈。如何突破这个瓶颈,提高系统性能?今天,我们将揭开一个STM32优化性能的秘密武器——将函数放入RAM中执行!
为什么将函数放入RAM中执行?
STM32的闪存读取速度虽不慢,但与RAM相比,速度差距明显。RAM的访问速度要比闪存快得多,尤其是在频繁读取或者执行大量数据时,RAM能够提供更高的带宽和更低的延迟。因此,将一些关键函数或者中断处理函数放入RAM中执行,能够显著提高系统的响应速度和执行效率。
适用场景
-
高频中断处理:中断服务程序(ISR)通常对执行时间有着极高的要求。将ISR放入RAM中,可以减少执行延迟,提高系统的实时性。
-
实时性要求高的算法:对于需要大量计算或者频繁操作的数据,放入RAM中可以显著提高算法的处理速度,适合处理图像、声音等实时数据。
-
安全性和灵活性需求:通过将代码动态加载到RAM中执行,可以实现某些代码的更新或者保护,防止闪存中的程序被篡改。
如何将STM32函数放入RAM中执行?
1. 修改链接脚本 (Linker Script)
将函数放入RAM的第一步是修改链接脚本。链接脚本定义了各个段如何映射到内存中。通过在链接脚本中指定RAM区域,我们可以将特定的函数或数据放入RAM中。


步骤:
/* 修改链接脚本 */ LR_IROM1 0x08000000 0x00100000 { ; 注释说明:这是一个加载区域,包含程序代码等信息ER_IROM1 0x08000000 0x00100000 { ; 注释说明:加载地址即为执行地址 ; *.o 对象文件中的 RESET 段和 +First 段会被加载到这个区域 *.o (RESET, +First) ; 将 InRoot$$Sections 段加载到此区域 *(InRoot$$Sections) ; 将所有只读段 (RO) 放入此区域 .ANY (+RO) ; 将所有外部符号段 (XO) 放入此区域 .ANY (+XO) } RW_IRAM1 0x20000000 0x0001C000 { ; 注释说明:这是一个用于存储读写数据的RAM区域 .ANY (+RW +ZI) ; 将所有读写数据 (RW) 和未初始化数据 (ZI) 放入此区域 } RW_IRAM2 0x2001C000 0x00004000 { .ANY (+RW +ZI) ; 将所有读写数据 (RW) 和未初始化数据 (ZI) 放入此区域 }
}
解释:
LR_IROM1:定义了一个加载区域,起始地址为 0x08000000,大小为 1MB,用于存储程序代码和只读数据。
ER_IROM1:执行区域的定义,加载地址即为执行地址,指明将程序的代码从闪存加载到该区域并执行。
*.o (RESET, +First):将目标文件中的RESET 和+First 段加载到此区域。*(InRoot$$Sections):将指定的InRoot$$Sections 段加载到执行区域。.ANY (+RO):将所有只读段(如代码段)放入此区域。.ANY (+XO):将外部符号段加载到此区域。RW_IRAM1:定义了一个读写数据区域,起始地址为 0x20000000,大小为 112KB,用于存储可读写数据和未初始化数据。
.ANY (+RW +ZI):将所有读写数据(RW)和未初始化数据(ZI)放入该区域。RW_IRAM2:另一个读写数据区域,起始地址为 0x2001C000,大小为 16KB,同样用于存储读写数据和未初始化数据。
2. 使用GCC属性将函数放入RAM
GCC提供了 __attribute__((section(".RAM_FUNCTIONS"))) 属性,可以将特定的函数放入RAM中的特定段。
示例代码:
void MyFunction(void) __attribute__((section(".RAM_FUNCTIONS"))); // 指定放入 RAM 区域
void MyFunction(void)
{
// 函数体
}
解释:
__attribute__((section(".RAM_FUNCTIONS"))),我们将MyFunction 函数放入.RAM_FUNCTIONS 区域,确保它存储在RAM中。3. 启动时拷贝代码到RAM并执行
有时候,我们可能需要在程序启动时将代码从闪存拷贝到RAM,并执行。通常,这种方法用于Bootloader或者自定义固件更新方案。
示例代码:
#define CODE_IN_FLASH ((uint32_t)0x08008000) // 假设代码存储在闪存的 0x08008000 地址处
#define CODE_IN_RAM ((uint32_t)0x20000000) // 假设 RAM 的起始地址是 0x20000000
void (*ram_function)(void);
void CopyToRAM(void)
{
uint32_t i;
uint32_t *flash_ptr = (uint32_t *)CODE_IN_FLASH;
uint32_t *ram_ptr = (uint32_t *)CODE_IN_RAM;
// 假设代码的长度是 1KB
for (i = 0; i < 256; i++) {
ram_ptr[i] = flash_ptr[i];
}
// 设置函数指针指向 RAM 中的函数
ram_function = (void (*)(void)) CODE_IN_RAM;
// 跳转到 RAM 中执行函数
ram_function();
}
解释:
4. 在中断服务程序中使用RAM执行
对于中断服务函数(ISR),通常对响应速度有严格的要求。将ISR存储在RAM中,能够减少访问延迟,提高响应速度。
示例代码:
void MyIRQHandler(void) __attribute__((section(".IRQ_FUNCTIONS")));
void MyIRQHandler(void)
{
// 中断服务处理代码
}
通过 __attribute__((section(".IRQ_FUNCTIONS"))),将 MyIRQHandler 函数放入特定的RAM区域中执行,从而提高中断响应速度。
将函数放入RAM中的优势
-
提高执行速度:将函数放入RAM中执行,减少了闪存读取的延迟,尤其是在数据处理频繁的情况下,RAM的访问速度能够显著提升系统的响应时间。
-
增强实时性:对于需要高实时响应的应用,比如中断处理、实时信号处理等,放入RAM中的函数能够提供更快速的执行路径。
-
灵活的代码更新:将代码放入RAM中执行,能够更方便地进行固件更新和动态加载,实现代码的灵活性和可更新性。
-
节省闪存空间:通过将不常用的代码存放在闪存中,只在需要时加载到RAM中,可以有效节省闪存空间。
作者:小昭debug嵌入式