LPC54608 之 将部分程序放入外部 Flash (上)

一、概述
我们都知道, MCU 片内的空间通常都有限且非常宝贵,因此,有时会出现片内 Flash 不够用的情况。此时,我们除了尽量节省片内 Flash 的空间外,还有没有其他的办法呢?当然有,我们可以把部分程序或数据放入片外 Flash 中。那么,我们该如何实现呢?接下来将通过两篇博文来介绍,我们怎样试着把部分程序放入外部 Flash 。


1.1
准备工作
我们怎样才能让我们想放入外部 Flash 的那部分程序乖乖听话地存入外部 Flash ,而不是跑来抢片内 Flash 的空间呢?没错,就是通过分散加载文件。本篇博文将介绍分散加载文件以及如何按照我们自己的需求修改它。

当我们通过分散加载文件给那部分程序分配好空间以后,怎样才能将它们按照分配的地址顺利地放入外部 Flash 呢?这里我们将编写与外部 Flash 型号对应的下载算法,然后将程序烧录到分散加载分配的对应空间里去。关于这部分下载算法的内容,将在下一篇博文《 LPC54608 之 将部分程序放入外部 Flash (下) 》中介绍。


二、分散加载介绍
2.1 分散加载概述

我们先来看看官方的描述:

通过使用分散加载机制,可以为链接器指定映像的内存映射。分散加载为您提供了对映像组建分组和位置的全面控制。分散加载可以用于简单映像,但它通常仅用于具有复杂内存映射的映像,即多个区在加载和执行时多个内存区域分散在内存映射中。

呃,说人话,大概就是分散加载可以让我们自己管理代码各部分的内存分布,即我们可以指定怎样分配 RO ( ReadOnly ) 、 RW ( ReadWrite ) 、 ZI ( ZeroInit ) 等数据的存放地址。各段解释如下:

    Code 段:表示程序代码部分

    RO - data 段:程序定义的所有常量以及 const 类型数据

    RW - data 段:已经初始化的所有静态变量

    ZI - data 段:未初始化的静态变量

    RO 段:指 Code 以及 RO-data 的统称

关于这些数据的总大小,我们可以在 Keil 编译后的 Build Output 窗口查看,至于详细各个细节的大小,可以双击工程名称,在 .map 文件查看。


图 1. 查看 Build Output 窗口

图 2. 查看 .map 文件


2.2
分散加载语法

在认识语法之前,让我们先了解两个概念,即 加载地址 & 运行地址。 ARM 映象文件各组成部分在存储系统中的地址有两种:一种是在映象文件位于存储器中时(也就是该映象文件开始运行之前,通俗的说就是下载到 Flash 中的二进制代码)的地址,称为加载地址;一种是在映象文件运行时(通俗的说就是给板子上电,开始运行 Flash 中的程序了)的地址,称为运行时地址。赋初值的全局变量和静态变量在程序还没运行的时候,初值是被放在 Flash 中的,此时它们的地址称为加载地址,当程序运行后,这些初值会从 Flash 中拷贝到 RAM 中,这时的地址就是运行地址。

好了,现在我们来看看分散加载的基本语法。分散文件包含一个或多个加载区域,每个加载区域可以包含一个或多个执行区域。顾名思义,加载域就是程序运行之前代码或数据存储的地方,运行域就是程序运行时,读写变量数据的地方。

下图显示了典型分散文件的组件和组织:


图 3. 分散加载结构

如图,这部分分散加载包含了一个加载域,此加载域包含了两个执行域,第一个执行域是 program1 运行时 RO 数据存放的位置,第二个执行域则是 program1 运行时 RW 和 ZI 数据存放的位置。

关于语法的具体细节,这里不做详细介绍,大家也可以看看这个博客:https://blog.csdn.net/weixin_39118482/article/details/79983692


2.3
分散加载示例

理论知识终究比较抽象,不够过瘾,接下了我们就来看看实际示例,试着去理解一个分散加载文件,下面以 LPC54608 的默认分散加载为例来进行介绍。

首先我们打开一个工程,这里以 LPC54608 的 SDK 里的 GPIO LED 灯例程为例,打开工程后,具体步骤如下图。


图 4. 打开分散加载文件

打开分散加载以后,我们来看看这些代码都代表什么意义。首先,文件最上方有一个“ #! armcc -E ”,它的意思是调用预处理器,这样我们就可以运用宏定义了。


图 5. 分散加载文件内容( 1 )

接着是一段宏定义,定义各个地址,这里和大家平时写的代码一样,比较好理解。


图 6. 分散加载文件内容( 2 )

接下来就是分散加载的主要部分了,我们可以对照 2.2 节的典型组织结构来分析,各部分解释如下图。


图 7. 分散加载文件内容( 3 )

上面的地址大家可以代入宏定义计算,然后对照着 LPC54608 用户手册里的 Memory map 来看,会更容易理解。


图 8. 用户手册 Memory map


三、修改分散加载文件 .scf

以上面看的 LPC54608 的分散加载文件为例,假设现在我们需要将两个只读图片数组放入外部 W25Q128 SPI Flash ( 大小为 16 Mbyte )中,我们应该如何修改分散加载文件,以满足自己的需求呢?接下来就让我们试试吧。


3.1
确定要放入的地址

这里我们查找 LPC54608 的用户手册可以知道, SPI Flash 起始地址为 0x1000 0000 ,由于我们的 SPI Flash 大小为 16 Mbyte ,故其地址范围为: 0x1000 0000 – 0x10FF FFFF 。


图 9. Memory map and peripheral addressing


3.2
将数组放入自己的节

我们想要将两个只读数组放入外部 Flash ,可以先将它们放入一个专门的“节”,然后在分散加载中为此节分配外部 Flash 的地址。因此,现在我们先把数组放入节中,这里我把它们放到了名为“ PictureSection ”的节中,如下图。


图 10. 将只读数组放入节中

图片数组 2 也做同样处理,这样我们就将它们放入专门的节中了。


3.3
通过分散加载为节分配地址

现在,我们需要将专门的节的地址分配到外部 Flash 的地址中去。


图 11. 宏定义外部 Flash 的地址


图 12. 将节分配到外部 Flash


3.4 通过 .map 文件验证分配

现在,看起来似乎完成了,可是我们怎样才能验证我们有没有达到目的呢?没错,就是前面提到的 .map 文件,在里面我们可以看到详细的内存分配情况。


图 13. 配置 Flash 参数

双击工程名打开 .map 文件,查找我们放入的数组名,可以看到,两个数组的地址分别是 0x1000 0000 和 0x1001 544a ,处于 SPI Flash 的地址范围,说明我们已经成功将这两个只读数组分配到了外部 Flash 中。


3.5
修改后的分散加载代码

#! armcc -E	;调用预处理器
/*
** ###################################################################
** Processors: LPC54608J512BD208
** LPC54608J512ET180
**
** Compiler: Keil ARM C/C++ Compiler
** Reference manual: LPC546xx User manual Rev.1.9 5 June 2017
** Version: rev. 1.2, 2017-06-08
** Build: b180801
**
** Abstract:
** Linker file for the Keil ARM C/C++ Compiler
**
** Copyright 2016 Freescale Semiconductor, Inc.
** Copyright 2016-2018 NXP
**
** SPDX-License-Identifier: BSD-3-Clause
**
** http: www.nxp.com
** mail: support@nxp.com
**
** ###################################################################
*/

#define m_interrupts_start 0x00000000
#define m_interrupts_size 0x00000400

#define m_text_start 0x00000400
#define m_text_size 0x0007FC00

;------------------ W25Q128 Flash ------------------
#define m_ex_flash_start 0x10000000
#define m_ex_flash_size 0x00FFFFFF
;-------------------------------------------------------

#define m_data_start 0x20000000
#define m_data_size 0x00028000

#define m_usb_sram_start 0x40100000
#define m_usb_sram_size 0x00002000

/* USB BDT size */
#define usb_bdt_size 0x0
/* Sizes */
#if (defined(__stack_size__))
#define Stack_Size __stack_size__
#else
#define Stack_Size 0x0400
#endif

#if (defined(__heap_size__))
#define Heap_Size __heap_size__
#else
#define Heap_Size 0x0400
#endif


; 语法: RO 只读,RW 可读可写,ZI 初始化为 0,UNINIT 没有初始化,FIXED 自动 size
; 加载区: load 段名字 起始地址 size

LR_m_text m_interrupts_start m_text_start+m_text_size-m_interrupts_start ;(0x00000000, Flash)
{ ; load region size_region
VECTOR_ROM m_interrupts_start m_interrupts_size ; load address = execution address ;(0x00000000, Flash)
{
* (RESET,+FIRST) ;异常,中断向量表 (+FIRST 表示放到首地址)
}
ER_m_text m_text_start FIXED m_text_size ; load address = execution address ;(0x00000400, Flash)
{
* (InRoot$$Sections) ;ARM 相关库
.ANY (+RO) ;其他代码段,主要为用户代码
}

RW_m_data m_data_start m_data_size-Stack_Size-Heap_Size ; RW data ;(0x20000000, SRAM, size: 0x27800)
{
.ANY (+RW +ZI) ;SRAM 上的 RW、ZI 段
}
ARM_LIB_HEAP +0 EMPTY Heap_Size ; Heap region growing up ;(0x20027800, SRAM, size: 0x0400)
{
}
ARM_LIB_STACK m_data_start+m_data_size EMPTY -Stack_Size ; Stack region growing down ;(0x20028000, SRAM)
{
}
}

;--------------------------------------------------------------------------------------------
LR_m_ex_flash m_ex_flash_start m_ex_flash_size
{
ER_PictureSection m_ex_flash_start m_ex_flash_size
{
* (PictureSection) ;节存放 (图片数据存在此节)
}
}
;--------------------------------------------------------------------------------------------

LR_m_usb_bdt m_usb_sram_start usb_bdt_size ;(0x40100000, AHB - USB SRAM, size: 0x0)
{
ER_m_usb_bdt m_usb_sram_start UNINIT usb_bdt_size ;(0x40100000, AHB - USB SRAM)
{
* (m_usb_bdt)
}
}

LR_m_usb_ram (m_usb_sram_start + usb_bdt_size) (m_usb_sram_size - usb_bdt_size)
{
ER_m_usb_ram (m_usb_sram_start + usb_bdt_size) UNINIT (m_usb_sram_size - usb_bdt_size)
{
* (m_usb_global)
}
}

 

 

四、参考资料

(1) LPC546XX 系列相关资料均可在 NXP 官网下载,网址如下:

https://www.nxp.com/products/processors-and-microcontrollers/arm-microcontrollers/general-purpose-mcus/lpc54000-cortex-m4-/power-efficient-microcontrollers-mcus-with-advanced-peripherals-based-on-arm-cortex-m4-core:LPC546XX?&tid=vanLPC546XX

(2) KEIL 官网关于分散加载的概述,网址如下:

http://www.keil.com/support/man/docs/armclang_ref/armclang_ref_pge1406304378956.htm

(3) KEIL 官网关于分散加载的语法,网址如下:

http://www.keil.com/support/man/docs/armclang_ref/armclang_ref_pge1362075656353.htm

★博文内容均由个人提供,与平台无关,如有违法或侵权,请与网站管理员联系。

★文明上网,请理性发言。内容一周内被举报5次,发文人进小黑屋喔~

评论