You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于SAMD51芯片的嵌入式系统:C++实现函数驻留RAM的技术咨询

针对你在SAMD51上开发可自覆盖应用、需要将特定函数部署到RAM运行的需求,我结合GCC工具链(SAMD51开发常用的编译环境)整理了几种实用方案,帮你实现完全在RAM中执行目标函数的需求:

核心原理

SAMD51的内存映射中,RAM区域(通常起始地址为0x20000000)属于易失性内存,不会被Flash擦除/编程操作影响。把函数代码部署到RAM后,无论是通过函数指针调用还是JMP指令跳转,都能让代码完全在RAM中运行,避免被自覆盖的Flash写入操作破坏。唯一需要注意的是:RAM内容断电即失,所以每次上电后都需要确保函数代码被正确加载到RAM中(默认是从Flash拷贝)。

方案1:使用编译器属性+链接脚本(最简单)

GCC编译器支持通过__attribute__((section))属性指定函数存放的段,配合链接脚本将该段映射到RAM区域,是最省心的实现方式:

步骤1:标记目标函数

给需要放到RAM的函数添加段属性:

// 将函数指定到.ramfunc段,后续由链接脚本映射到RAM
__attribute__((section(".ramfunc"))) void my_ram_only_function(void) {
    // 这里编写你的函数逻辑,执行时完全在RAM中运行
    // 比如Flash擦除、自覆盖相关的操作代码
}

步骤2:修改链接脚本

找到你的SAMD51项目链接脚本(通常是.ld文件),在SECTIONS块中添加.ramfunc段的定义,将其加载到RAM,同时指定代码初始存储在Flash(上电后自动拷贝):

SECTIONS {
    // ... 保留原有其他段定义(如.text、.data等)
    
    .ramfunc : {
        PROVIDE(__ramfunc_start__ = .);  // 定义RAM中函数段的起始地址符号
        *(.ramfunc)                      // 匹配所有.ramfunc段的内容
        *(.ramfunc.*)                    // 匹配所有.ramfunc开头的子段
        PROVIDE(__ramfunc_end__ = .);    // 定义RAM中函数段的结束地址符号
    } > RAM AT > FLASH                   // >RAM表示运行地址在RAM,AT>FLASH表示加载地址在Flash
}

步骤3:确保代码被拷贝到RAM

大部分SAMD51的启动文件(如startup_samd51.c)会自动处理.data段从Flash到RAM的拷贝,但可能不会处理.ramfunc段。如果你的启动文件没有自动处理,需要手动添加拷贝逻辑:

// 声明链接器生成的符号
extern uint32_t __ramfunc_start__;
extern uint32_t __ramfunc_end__;
extern uint32_t __ramfunc_load_start__;  // 指向Flash中.ramfunc段的起始地址

void copy_ram_functions(void) {
    uint32_t *src = &__ramfunc_load_start__;
    uint32_t *dst = &__ramfunc_start__;
    
    // 逐字拷贝函数代码到RAM
    while (dst < &__ramfunc_end__) {
        *dst++ = *src++;
    }
}

然后在main()函数执行前调用copy_ram_functions(),比如在启动文件的Reset_Handler中添加调用。

方案2:手动指定RAM地址+拷贝(更灵活)

如果需要更精细的内存控制(比如指定特定RAM地址),可以手动将Flash中的函数代码拷贝到RAM的指定位置:

步骤1:定义Flash中的模板函数

先把函数放到Flash的自定义段中:

// 函数先存储在Flash的.flashfunc段
__attribute__((section(".flashfunc"))) void my_flash_template(void) {
    // 你的核心逻辑代码
}

步骤2:指定RAM目标地址并定义函数指针

选择一个未被栈、堆或其他变量占用的RAM地址(参考SAMD51 datasheet的内存映射,比如0x20001000,需2字节对齐,Thumb-2指令要求):

#define RAM_FUNC_RUN_ADDR (0x20001000)  // 自定义RAM运行地址
typedef void (*RamFunctionPtr)(void);
RamFunctionPtr my_ram_exec_func = (RamFunctionPtr)RAM_FUNC_RUN_ADDR;

步骤3:添加拷贝逻辑

在初始化阶段将Flash中的函数代码拷贝到RAM:

// 声明链接器生成的.flashfunc段结束符号
extern uint32_t __flashfunc_end__;

void init_ram_exec_function(void) {
    // 计算函数大小:结束地址 - 起始地址
    uint32_t func_total_size = (uint32_t)&__flashfunc_end__ - (uint32_t)&my_flash_template;
    uint32_t *src_ptr = (uint32_t*)&my_flash_template;
    uint32_t *dst_ptr = (uint32_t*)RAM_FUNC_RUN_ADDR;
    
    // 逐32位拷贝(SAMD51是32位架构,对齐拷贝效率更高)
    for (uint32_t i = 0; i < func_total_size / sizeof(uint32_t); i++) {
        dst_ptr[i] = src_ptr[i];
    }
}

步骤4:修改链接脚本

在链接脚本中添加.flashfunc段的定义:

.flashfunc : {
    PROVIDE(__flashfunc_start__ = .);
    *(.flashfunc)
    *(.flashfunc.*)
    PROVIDE(__flashfunc_end__ = .);
} > FLASH

完成初始化后,你就可以通过my_ram_exec_func()调用RAM中的函数,或者直接用JMP指令跳转到RAM_FUNC_RUN_ADDR地址执行。

关键注意事项
  • 内存对齐:SAMD51使用Thumb-2指令集,函数运行地址必须对齐到2字节(建议4字节对齐),否则会触发HardFault异常。
  • RAM区域冲突:选择RAM地址时,要确保该区域未被栈、堆、全局变量或其他外设占用,可通过查看链接脚本中的STACK、HEAP定义或使用调试工具确认。
  • 中断处理:如果你的自覆盖操作会执行Flash擦除,建议将相关中断服务程序也部署到RAM,或者在擦除期间关闭中断,避免Flash操作被中断打断导致故障。
  • 断电恢复:RAM内容易失,每次上电后必须重新执行拷贝逻辑,确保函数代码存在于RAM中才能调用。

内容的提问来源于stack exchange,提问作者James B. Reese

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 15:47:47