当前位置: 首页 > article >正文

Keil5 MDK在Cortex M系列关于分散加载文件说明指导

类别内容摘要本文结合 SRAM 示例工程说明如何在Cortex M LCM32F067 工程中使用 Keil 分散加载文件将部分函数固定到指定 Flash 地址运行并将部分函数搬运到指定 RAM 地址运行。源代码路径链接: https://pan.baidu.com/s/1De7GAP6OVmd0leHCKNNtag?pwddesm 提取码: desm目 录适用范围基础知识分散加载文件概述分散加载文件语法分散加载应用实例常见问题与验证方法1. 适用范围有时候用户希望在单片机工程中实现如下需求某些函数固定在某个 Flash 地址作为固定接口或服务入口某些函数在上电后搬运到 RAM 中执行以提升速度或避免 Flash 操作时取指冲突普通代码和普通变量仍然按照默认方式分别放在 Flash 和 SRAM 中在 Keil MDK 工程中这类需求通常通过分散加载文件来实现。本文结合Project/Examples/SRAM示例工程重点讲解如何通过section(name)给函数打标签如何在.sct文件中把这些 section 分配到不同地址如何理解加载域、执行域、FIXED、__scatterload如何通过 map 验证最终结果2. 基础知识2.1 基本概念在理解分散加载前需要先清楚下面几类数据的含义Code表示程序代码。RO-Data表示只读数据例如const常量。RW-Data表示已初始化的全局变量或静态变量。ZI-Data表示未初始化的全局变量或静态变量。在嵌入式工程中通常有如下结论Code和RO-Data一般放在 Flash 中RW-Data运行时在 RAM 中但其初值镜像也要占用 FlashZI-Data只占用 RAM不占用 Flash 初值空间因此一个工程的空间占用通常可理解为Flash 占用 Code RO-Data RW-DataRAM 占用 RW-Data ZI-Data2.2 启动阶段谁在搬运代码和数据很多学习者第一次接触分散加载时最容易产生的疑问是“既然某个函数要在 RAM 中运行它是怎么从 Flash 到 RAM 的”答案在启动代码中。在 Cortex-M Keil 的典型启动流程里Reset_Handler进入运行库启动过程__main调用__scatterload__scatterload根据 scatter 文件描述把需要搬运的代码或数据从装载地址复制到执行地址RW数据完成初始化ZI数据完成清零最后才跳入main()因此只要 scatter 文件描述正确那么main()运行之前所有要求“在 RAM 中执行”的函数就已经被搬运到目标 RAM 地址了2.3 本工程中的 4 个目标函数本工程中专门设计了 4 个函数用于演示RAM_FillPatternWindow()RAM_MixAndAccumulate()FLASH_FixedSignature()FLASH_FixedParity()它们分别用于演示两个不同 RAM 地址运行两个不同 Flash 固定地址运行最终链接结果为函数名最终符号地址说明RAM_FillPatternWindow0x20000001在 SRAM 执行区 A 运行RAM_MixAndAccumulate0x20000101在 SRAM 执行区 B 运行FLASH_FixedSignature0x08000801固定在 Flash 窗口 AFLASH_FixedParity0x08000901固定在 Flash 窗口 B说明地址最低位为1是 Thumb 状态位实际区域基地址仍然分别是0x20000000、0x20000100、0x08000800、0x080009003. 分散加载文件概述分散加载文件是一个文本文件用于描述链接器如何组织最终映像并决定程序镜像装载到哪里代码和数据执行时位于哪里不同 section 应该进入哪个执行区如果不使用 scatter 文件链接器会按默认规则放置代码和数据。而在下面这些场景中通常就需要显式编写 scatter 文件代码必须放在多个不同内存区域部分函数必须在 RAM 中执行部分函数必须固定在某个 Flash 地址多块 RAM 或多块 Flash 需要分别利用需要精确控制向量表、启动区、应用区、固定接口区的地址布局对本工程而言使用分散加载的直接目的就是固定两段 Flash 代码区划出两段 RAM 执行区普通代码和数据继续走默认的应用区与数据区4. 分散加载文件语法4.1 加载时域的描述一个典型的加载域写法如下LR_IROM1 0x08000000 0x00010000 { ... }这里LR_IROM1是加载域名称0x08000000是加载域起始地址0x00010000是该加载域大小在本工程中这表示整个映像的主装载空间仍然是片上 Flash其组织起点是0x080000004.2 运行时域的描述一个典型的执行域写法如下ER_FLASH_FIXED_A 0x08000800 FIXED 0x00000100 { *.o (FLASH_FIXED_A) }这里ER_FLASH_FIXED_A是执行域名称0x08000800是执行地址FIXED表示装载地址与执行地址相同0x00000100表示执行域大小如果某个执行域不在 Flash而是在 SRAM 中例如ER_RAM_EXEC_A 0x20000000 0x00000100 { *.o (RAM_EXEC_A) }那么它的含义是这段代码最终在0x20000000执行但初始镜像仍由主加载域统一组织在 Flash 中上电后由__scatterload拷贝到该 SRAM 地址4.3 输入段描述scatter 文件中的输入段匹配规则用来决定“哪些内容放进这个执行域”。例如*.o (RAM_EXEC_A)它表示匹配所有目标文件中的RAM_EXEC_A输入段并把这些输入段放到当前执行域中本工程中在 C 代码里写了__attribute__((section(RAM_EXEC_A)))这就会让该函数进入名为RAM_EXEC_A的输入段。scatter 文件再用*.o (RAM_EXEC_A)将其准确分配到ER_RAM_EXEC_A。类似地FLASH_FIXED_A对应固定 Flash 区 AFLASH_FIXED_B对应固定 Flash 区 BRAM_EXEC_B对应第二个 RAM 执行区4.4.ANY的意义.ANY可以理解成“兜底规则”。例如.ANY (RO)表示所有还没有被前面规则收走的只读代码、只读数据都放到当前执行域因此在写 scatter 文件时通常建议顺序为先写专用 section 的匹配规则最后再写.ANY否则很容易出现你想单独分配的函数先被默认.ANY区域收走后面的专用区域反而匹配不到目标5. 分散加载应用实例5.1 本工程的地址规划本工程使用的关键地址如下区域起始地址用途ER_ROOT0x08000000向量表、启动代码、运行库根区ER_FLASH_FIXED_A0x08000800固定 Flash 函数 AER_FLASH_FIXED_B0x08000900固定 Flash 函数 BER_IROM10x08000A00普通应用代码区ER_RAM_EXEC_A0x20000000RAM 执行函数 AER_RAM_EXEC_B0x20000100RAM 执行函数 BRW_IRAM10x20000200普通数据区用图示表示如下flowchart TB subgraph FLASH[Flash 地址空间] F0[0x08000000\nER_ROOT\n向量表 / 启动代码 / __main / __scatterload] F1[0x08000800\nER_FLASH_FIXED_A\nFLASH_FixedSignature()] F2[0x08000900\nER_FLASH_FIXED_B\nFLASH_FixedParity()] F3[0x08000A00\nER_IROM1\n普通应用代码区] end subgraph SRAM[SRAM 地址空间] R0[0x20000000\nER_RAM_EXEC_A\nRAM_FillPatternWindow()] R1[0x20000100\nER_RAM_EXEC_B\nRAM_MixAndAccumulate()] R2[0x20000200\nRW_IRAM1\nRW / ZI 数据] end5.2 本工程的 scatter 文件配置本工程实际采用的 scatter 文件结构如下LR_IROM1 0x08000000 0x00010000 { ER_ROOT 0x08000000 0x00000800 { *.o (RESET, First) *(InRoot$$Sections) } ER_FLASH_FIXED_A 0x08000800 FIXED 0x00000100 { *.o (FLASH_FIXED_A) } ER_FLASH_FIXED_B 0x08000900 FIXED 0x00000100 { *.o (FLASH_FIXED_B) } ER_IROM1 0x08000A00 FIXED 0x0000F600 { .ANY (RO) .ANY (XO) } ER_RAM_EXEC_A 0x20000000 0x00000100 { *.o (RAM_EXEC_A) } ER_RAM_EXEC_B 0x20000100 0x00000100 { *.o (RAM_EXEC_B) } RW_IRAM1 0x20000200 0x00002600 { .ANY (RW ZI) } }其含义可概括为ER_ROOT放根区内容ER_FLASH_FIXED_A和ER_FLASH_FIXED_B放两个固定地址函数ER_IROM1放其余普通应用代码ER_RAM_EXEC_A和ER_RAM_EXEC_B放两段不同地址的 RAM 执行代码RW_IRAM1放普通全局变量和静态变量5.3 在 C 文件中定义自定义 section本工程在main.c中使用了下面的写法#define RAM_EXEC_A_ATTR __attribute__((noinline, section(RAM_EXEC_A))) #define RAM_EXEC_B_ATTR __attribute__((noinline, section(RAM_EXEC_B))) #define FLASH_FIXED_A_ATTR __attribute__((noinline, section(FLASH_FIXED_A))) #define FLASH_FIXED_B_ATTR __attribute__((noinline, section(FLASH_FIXED_B)))然后分别将 4 个函数放入这些 sectionRAM_EXEC_A_ATTR uint32_t RAM_FillPatternWindow(uint32_t seed); RAM_EXEC_B_ATTR uint32_t RAM_MixAndAccumulate(uint32_t value); FLASH_FIXED_A_ATTR uint32_t FLASH_FixedSignature(void); FLASH_FIXED_B_ATTR uint32_t FLASH_FixedParity(uint32_t value);其中noinline的作用是防止编译器把函数直接内联到调用者中便于在链接报告中清楚看到每个函数的独立落点5.4 双 RAM 运行函数配置实例本工程中两个运行在 RAM 的函数分别是RAM_FillPatternWindow()RAM_MixAndAccumulate()它们的功能并不复杂目的是让学习者把注意力集中在“地址分配”上而不是业务逻辑本身。对应关系如下函数section执行区执行地址RAM_FillPatternWindow()RAM_EXEC_AER_RAM_EXEC_A0x20000000RAM_MixAndAccumulate()RAM_EXEC_BER_RAM_EXEC_B0x20000100程序启动时运行库进入__scatterload将RAM_EXEC_A对应的镜像从 Flash 拷贝到0x20000000将RAM_EXEC_B对应的镜像从 Flash 拷贝到0x20000100main()才开始执行因此在main()中调用这两个函数时它们已经在 RAM 中。5.5 固定 Flash 地址函数配置实例本工程中两个固定在 Flash 中的函数分别是FLASH_FixedSignature()FLASH_FixedParity()对应关系如下函数section执行区固定地址FLASH_FixedSignature()FLASH_FIXED_AER_FLASH_FIXED_A0x08000800FLASH_FixedParity()FLASH_FIXED_BER_FLASH_FIXED_B0x08000900由于这两个执行区都带FIXED因此装载地址 执行地址函数直接在 Flash 中原地执行不需要启动时拷贝这类配置很适合实现固定服务入口版本接口固件标识函数固定跳转入口5.6 普通应用区为什么也要使用 FIXED本工程里普通应用区写成了ER_IROM1 0x08000A00 FIXED 0x0000F600这里的FIXED很重要。原因是前面已经人为切出了两个固定 Flash 小窗普通应用区从0x08000A00开始执行如果这里不加FIXED链接器可能会把装载镜像往前压缩造成 Flash 中装载布局和执行布局发生交叉覆盖风险本工程第一次修改 scatter 文件时就出现了这类错误。最终修复办法就是给普通应用区也加上FIXED这是一条很重要的实践经验只要 Flash 代码区需要精确切片而且这些区域本身就在 Flash 原地执行就优先考虑FIXED5.7 本工程中的 section、执行域与函数对应关系总表名称类型位置作用RAM_EXEC_A自定义输入段C 文件定义作为 RAM 函数 A 的标签RAM_EXEC_B自定义输入段C 文件定义作为 RAM 函数 B 的标签FLASH_FIXED_A自定义输入段C 文件定义作为固定 Flash 函数 A 的标签FLASH_FIXED_B自定义输入段C 文件定义作为固定 Flash 函数 B 的标签ER_RAM_EXEC_A执行域SRAM接收RAM_EXEC_AER_RAM_EXEC_B执行域SRAM接收RAM_EXEC_BER_FLASH_FIXED_A执行域Flash接收FLASH_FIXED_AER_FLASH_FIXED_B执行域Flash接收FLASH_FIXED_B6. 常见问题与验证方法6.1 如何看 map 文件map文件更适合检查整体空间分布与是否重叠。重点检查各执行区是否超出分配大小例如ER_RAM_EXEC_A 0x00000100是否真的足够。SRAM 代码区和普通数据区是否重叠本工程中ER_RAM_EXEC_A从0x20000000ER_RAM_EXEC_B从0x20000100RW_IRAM1从0x20000200固定 Flash 区和普通应用区是否冲突本工程中0x080008000x080009000x08000A00这些边界都应保持清晰独立。6.2 初学者常见错误常见错误主要有以下几类只把顶层函数放到 RAM却忽略其子函数仍在 Flash把.ANY (RO)写在专用 section 规则前面忘记给演示函数加noinline没有给 RAM 代码区与普通数据区留出边界在多块 Flash 切片时忘记使用FIXED结束语对分散加载最实用的理解方式可以归纳为四句话先在 C 文件中用section(name)给函数打标签再在.sct文件中把这个标签映射到指定执行域如果执行地址在 RAM启动时由__scatterload搬运过去如果执行地址在固定 Flash使用FIXED让其原地执行掌握了这套方法后就可以在单片机Cortex M系列工程中灵活实现固定地址函数接口RAM 中运行的关键函数多执行区的代码布局设计更复杂的 Boot / App 分区结构

相关文章:

Keil5 MDK在Cortex M系列关于分散加载文件说明指导

类别内容摘要本文结合 SRAM 示例工程,说明如何在Cortex M( LCM32F067 )工程中使用 Keil 分散加载文件,将部分函数固定到指定 Flash 地址运行,并将部分函数搬运到指定 RAM 地址运行。源代码路径链接: https://pan.baidu…...

ESP32音频开发指南:如何用ESP-ADF的I2S Stream实现高质量音频输出

ESP32音频开发实战:基于I2S Stream的高保真音频输出全解析 在物联网和智能硬件快速发展的今天,音频处理能力已成为嵌入式设备的重要功能之一。ESP32作为一款高性价比的Wi-Fi/蓝牙双模芯片,凭借其强大的处理能力和丰富的外设接口,在…...

火山方舟 Coding Plan 服务变更公告

亲爱的火山方舟 Coding Plan 用户朋友们:大家好!首先由衷感谢大家对火山方舟 Coding Plan 的喜爱与支持!自产品上线以来,我们收到了远超预期的用户热情,无数订阅者通过 Coding Plan 享受到了高效、便捷的 AI 提效服务&…...

python+Ai技术框架的基于与.的个人健康档案管理系统django flask

目录技术选型与框架对比系统模块设计实现步骤部署与扩展关键注意事项项目技术支持可定制开发之功能创新亮点源码获取详细视频演示 :文章底部获取博主联系方式!同行可合作技术选型与框架对比 Django:适合快速构建全功能应用,自带O…...

PostgreSQL时间戳插入踩坑实录:为什么to_date函数会丢失时分秒?

PostgreSQL时间戳处理深度解析:从to_date陷阱到跨数据库兼容实践 在数据库开发中,时间戳处理看似简单却暗藏玄机。许多从Oracle转向PostgreSQL的开发者都会惊讶地发现,原本在Oracle中运行良好的日期处理代码,到了PostgreSQL环境下…...

三菱FX5U与欧姆龙E5CC温控器的通讯实现

三菱FX5U与3台欧姆龙E5CC温控器通讯程序(SL5U-1) 功能:通过三菱FX5U本体485口,结合触摸屏网口,实现对3台欧姆龙E5CC温控器设定温度,读取温度。 反应灵敏,通讯稳定可靠。 器件:三菱FX5UPLC,3台欧…...

手把手教你用AppleScript和Launchd定时重启Mac TouchBar(含日志记录)

深度解析:如何通过自动化脚本优化Mac TouchBar的稳定性 TouchBar作为MacBook Pro的标志性功能,虽然提升了交互体验,但长期使用后容易出现闪烁、卡顿等问题。本文将系统性地介绍如何利用AppleScript和Launchd构建一套完整的TouchBar维护方案&a…...

积木报表达梦数据库适配:零代码可视化工具无缝集成方案

积木报表达梦数据库适配:零代码可视化工具无缝集成方案 【免费下载链接】jimureport 「数据可视化工具:报表、大屏、仪表盘」积木报表是一款类Excel操作风格,在线拖拽设计的报表工具和和数据可视化产品。功能涵盖: 报表设计、大屏设计、打印设…...

二叉树展开链表

https://leetcode.cn/problems/flatten-binary-tree-to-linked-list/description/?envTypestudy-plan-v2&envIdtop-100-liked 题目 思路 先序遍历:跟 -> 左 -> 右 。最后我们要拼接成的序列为 1 -> 2 -> 3 -> 4 -> 5 -> 6 如果正着做&…...

Docker版OnlyOffice中文字体配置全攻略:从字号调整到字体添加

Docker版OnlyOffice中文字体配置全攻略:从字号调整到字体添加 在企业文档协作和教育课件制作中,中文排版规范直接影响内容的专业性和可读性。对于使用Docker部署OnlyOffice的用户来说,默认配置往往无法满足中文排版需求。本文将深入探讨如何通…...

3D 点云处理(PCL)

PCL(点云库)是我们从2D视觉迈向3D空间理解的“三维感知核心”。在掌握OpenCV的基础上,PCL将帮助把内窥镜的2D图像信息,扩展到完整的三维手术空间理解——这正是精准手术导航的基础。 基于之前已经接触过的PCL内容,提供…...

为什么说学编程不如直接去学网络安全?

一、先看一组扎心对比:市场真的不一样 程序员 :2024 智联招聘数据显示,Java 开发岗平均 1 岗 38 人竞争,应届生起薪中位数仅 7800 元;某大厂 2024 校招开发岗简历通过率不足 5%,且明确要求 “211/985 或顶…...

掌握ConfuserEx:从入门到精通的.NET程序混淆保护实战指南

掌握ConfuserEx:从入门到精通的.NET程序混淆保护实战指南 【免费下载链接】ConfuserEx An open-source, free protector for .NET applications 项目地址: https://gitcode.com/gh_mirrors/co/ConfuserEx ConfuserEx是一款开源免费的.NET程序保护工具&#x…...

手把手教你设计反激式开关电源:从PWM控制到高频变压器选型(附完整电路图)

手把手教你设计反激式开关电源:从PWM控制到高频变压器选型(附完整电路图) 在电子设备小型化与高效化的趋势下,反激式开关电源凭借其结构简单、成本低廉的优势,成为100W以内功率场景的首选方案。无论是家用电器辅助电源…...

Keil MDK-ARM避坑指南:STM32开发环境搭建中的5个常见错误及解决方法

Keil MDK-ARM避坑指南:STM32开发环境搭建中的5个常见错误及解决方法 当你第一次打开Keil MDK-ARM准备开始STM32开发之旅时,可能会被各种报错信息、许可证问题和Pack包加载失败搞得一头雾水。作为一款在嵌入式开发领域广泛使用的IDE,Keil MDK-…...

Qt5离线安装包下载终极指南:绕过IP限制的3种实用方法(含迅雷链接)

Qt5离线安装包高效获取方案:开发者必备的三种技术路径 对于国内开发者而言,获取Qt5离线安装包常常会遇到"Download from your IP address is not allowed"的提示。这并非技术障碍,而是网络环境导致的资源访问限制。本文将系统性地介…...

乡合农服土壤改良:给土地“治病”,让丰收“生根”

在什邡市洛水镇银池村的蒜田里,种植大户黎昌勇抓起一把泥土,眼角笑意满满:“这地真的‘活’过来了!”三年前,这片田土壤酸化严重,种下的大蒜不是瘦小枯黄,就是中途坏死,收成远不及以…...

避开这5个坑!Grafana饼图面板使用中的常见错误及解决方案

避开这5个坑!Grafana饼图面板使用中的常见错误及解决方案 在数据可视化领域,Grafana的Piechart panel(饼图面板)因其直观的表现形式而广受欢迎。然而,许多用户在从入门到精通的路上,常常会遇到一些看似简单…...

Python 中的并发 —— 多进程

摘要:本文介绍了Python中的多进程编程方法,重点对比了多进程与多线程的差异。多进程通过利用多个CPU核心实现真正并行,能有效规避Python的GIL限制。文章详细讲解了三种启动进程的方式(Fork/Spawn/Forkserver)&#xff…...

AI 智能体(AI Agent)的开发费

在 2026 年的国内市场,AI 智能体(AI Agent)的开发费用已经告别了早期的“盲目喊价”,形成了以复杂度和落地场景为核心的定价逻辑。以下是目前国内主流的费用构成和估算参考:1. 基础型:知识库与简单问答 (RA…...

避坑指南:Win11+Docker部署Spark集群时你一定会遇到的5个问题(附解决方案)

Win11Docker部署Spark集群实战避坑手册:从端口冲突到资源优化的全链路解决方案 当你在Windows 11上尝试用Docker搭建Spark集群时,可能会遇到一系列令人抓狂的问题——端口被占用、目录权限报错、Hadoop集成失败、Web UI无法访问,甚至资源分配…...

FPGA车牌识别demo 软件用的Vivado2020.2 板子用的正点原子达芬奇ProA7-...

FPGA车牌识别demo 软件用的Vivado2020.2 板子用的正点原子达芬奇ProA7-100T,下载到板子插好摄像头显示屏即可用。 功能包括:基于阈值的车牌识别,字符分割及HDMI显示,特征识别与字符库进行匹配,将捕捉到的车牌打印到串口显示。实验…...

百度网盘不限速下载方案对比:2026年主流工具实测与选择指南

百度网盘作为国内使用最广泛的云存储服务之一,资源丰富、分享便捷,但非会员下载速度常常成为用户痛点。近年来,随着技术迭代和用户需求变化,各种辅助下载方案层出不穷,从经典的Pandownload到如今主流的多线程组合工具&…...

Docker+MinIO实战:用Nginx反向代理解决外网访问认证问题(附完整配置)

DockerMinIO实战:Nginx反向代理实现安全外网访问的完整指南 MinIO作为高性能的对象存储解决方案,在企业内部数据管理中扮演着重要角色。但当我们需要从外网访问内网部署的MinIO服务时,直接暴露端口不仅存在安全隐患,还常常遇到认证…...

Java 循环:for vs for-each —— 何时用哪个?

简洁、安全、高效——这是每个 Java 开发者对循环的追求。 但 for 和 for-each 到底有什么区别?什么时候该用哪一个? 这篇笔记帮你彻底搞懂!🔍 一、基础语法对比 ✅ 传统 for 循环 for (初始化; 条件判断; 更新) {// 循环体 }示例…...

Job for mysqld.service failed because the control process exited with error code. See “systemctl sta

关于mysql启用报错: 错误信息: Job for mysqld.service failed because the control process exited with error code. See "systemctl status mysqld.service" and "journalctl -xe" for details.问题描述 服务器系统盘空间已满导致…...

金属矿山安全智能AI视觉识别

金属矿山安全智能AI视觉识别,是利用AI视觉技术,将摄像头升级为“24小时不眨眼的安全哨兵”,自动识别风险并预警,实现从“人盯人”到“技防智控”的转变。🎯 核心应用场景人员安全行为识别着装合规:自动检测…...

Spring Boot前端请求时的参数绑定

Spring Boot前端请求时的参数绑定 在 Spring Boot 中处理前端请求时,有多个关键的参数绑定注解。来聊聊这些注解的用法和区别。 核心注解对比表注解作用位置获取来源适用场景示例RequestBody方法参数请求体(JSON/XML)接收复杂对象创建/更新操…...

fastjson-EnumDeserializer类及源码分析

本文以fastjson-1.2.83版本中 EnumDeserializer 类的源码,来解释其工作原理和实现细节。 🎯 类结构概览 EnumDeserializer 是 FastJSON 用于将 JSON 反序列化为枚举类型的核心类。它支持: 通过枚举名称(Enum.name())反序列化通过枚举序号(Enu…...

sse哈工大C语言编程练习45

2026 年 3 月 17 日 收获: 判断直角三角形时,两边的平方和减第三边的平方和小于 0.1 即可认为是直角三角形,主要看题目给的测试用例,确定精度。取余和除法第二个数都不能为 0,若遇到则输出错误提示信息,直接…...