STM32性能飞跃:函数内存RAM执行提速秘籍!

在嵌入式开发中,优化系统的执行效率是每个工程师都需要关注的核心问题。STM32等基于ARM Cortex-M的微控制器通常将程序代码存储在闪存(Flash)中,从闪存中读取并执行。虽然这种方式在大多数情况下已经足够,但在一些关键应用场景中,程序执行的速度和响应时间可能成为瓶颈。如何突破这个瓶颈,提高系统性能?今天,我们将揭开一个STM32优化性能的秘密武器——将函数放入RAM中执行!

为什么将函数放入RAM中执行?

STM32的闪存读取速度虽不慢,但与RAM相比,速度差距明显。RAM的访问速度要比闪存快得多,尤其是在频繁读取或者执行大量数据时,RAM能够提供更高的带宽和更低的延迟。因此,将一些关键函数或者中断处理函数放入RAM中执行,能够显著提高系统的响应速度和执行效率。

适用场景

  1. 高频中断处理:中断服务程序(ISR)通常对执行时间有着极高的要求。将ISR放入RAM中,可以减少执行延迟,提高系统的实时性。
  2. 实时性要求高的算法:对于需要大量计算或者频繁操作的数据,放入RAM中可以显著提高算法的处理速度,适合处理图像、声音等实时数据。
  3. 安全性和灵活性需求:通过将代码动态加载到RAM中执行,可以实现某些代码的更新或者保护,防止闪存中的程序被篡改。

如何将STM32函数放入RAM中执行?

1. 修改链接脚本 (Linker Script)

将函数放入RAM的第一步是修改链接脚本。链接脚本定义了各个段如何映射到内存中。通过在链接脚本中指定RAM区域,我们可以将特定的函数或数据放入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();
    }
    解释:
  • 该代码演示了如何将存储在闪存中的代码拷贝到RAM中,并通过函数指针跳转到RAM中执行。这样可以动态加载并执行RAM中的代码。
  • 4. 在中断服务程序中使用RAM执行

    对于中断服务函数(ISR),通常对响应速度有严格的要求。将ISR存储在RAM中,能够减少访问延迟,提高响应速度。

    示例代码:
    void MyIRQHandler(void) __attribute__((section(".IRQ_FUNCTIONS")));
    void MyIRQHandler(void)
    {
        // 中断服务处理代码
    }

    通过 __attribute__((section(".IRQ_FUNCTIONS"))),将 MyIRQHandler 函数放入特定的RAM区域中执行,从而提高中断响应速度。

    将函数放入RAM中的优势

    1. 提高执行速度:将函数放入RAM中执行,减少了闪存读取的延迟,尤其是在数据处理频繁的情况下,RAM的访问速度能够显著提升系统的响应时间。
    2. 增强实时性:对于需要高实时响应的应用,比如中断处理、实时信号处理等,放入RAM中的函数能够提供更快速的执行路径。
    3. 灵活的代码更新:将代码放入RAM中执行,能够更方便地进行固件更新和动态加载,实现代码的灵活性和可更新性。
    4. 节省闪存空间:通过将不常用的代码存放在闪存中,只在需要时加载到RAM中,可以有效节省闪存空间。

    作者:小昭debug嵌入式

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32性能飞跃:函数内存RAM执行提速秘籍!

    发表回复