Flash 以 4KB 为一个 Page 进行擦除/写入操作(如果在一个 Page 需要写入一个字节,需要将整个 Page 擦除),Flash 组成部分及其它们关联的链接器文件如下。
对于协议栈以库文件方式链接的工程( _lirary ):
cc26xx_app.icf
(IAR)和 cc26xx_app.cmd
(CCS)。对于采用两个 FLash 镜像方式编译的工程( Split Image ):
Application Image Code Space (应用程序镜像代码区):独立的应用程序 Flash 镜像,该镜像在应用程序的链接器配置文件中被配置为 cc26xx_app.icf
(IAR)和 cc26xx_app.cmd
(CCS)。
Stack Image Code Space (协议栈镜像代码区):独立的协议栈Flash镜像。该镜像在协议栈的链接器配置文件中被配置: cc26xx_stack.icf
(IAR)和 cc26xx_ stack.cmd
(CCS)。
Simple NV (SNV)Area :非易失性存储器使用,可供应用程序和协议栈(GAP Bond Manager)使用。有关 SNV 的配置请参阅本文后面的使用 Simple NV 进行 Flash 存储。配置时,SNV Flash 存储区域是协议栈镜像的一部分。
本节介绍用于两个拆分镜像项目配置的 Flash 内存映射。如下图1所示,应用程序链接文件存储在用实线箭头指向的内存位置,协议栈链接文件存储在用虚线箭头指向的内存位置。
注意:将协议栈作为库的工程,协议栈以库的形式被应用程序调用。协议栈和应用程序合并放在应用程序镜像代码空间中,不需要与拆分镜像
相关的任何边界要求。
下表总结了图 1 中的 Flash System Map
定义, 并提供了可在各个 IDE 链接器文件中找到的关联链接器的定义或符号。
表1. Flash 系统映射定义
符号/区域 | 意义 | 工程 | CCS定义 | IAR定义 |
---|---|---|---|---|
APP_FLASH_START | flash的起始位置/应用程序镜像的起始位置 | App | FLASH_APP_BASE | FLASH_START |
APP_FLASH_END | 应用程序镜像的结束位置(ICALL_STACK0_ADDR) | App | ADJ_ICALL_STACK0_START_FLASH_APP_BASE | FLASH_END |
STACK_FLASH_START | 协议栈镜像的起始位置(ICALL_STACK0_ADDR) | Stack | FLASH_START | FLASH_START |
STACK_FLASH_END | 协议栈镜像的结束位置,包括SNV | Stack | FLASH_SIZE_RESERVED_SIZE | FLASH_END |
CCA sector | flash最后的扇区,包括CCFG | App | FLASH_LAST_PAGE | FLASH_LAST_PAGE |
CCFG region | 位于CCA中,用来存储本地配置参数 | App | CCA的最后88个字节 | CCA的最后88个字节 |
应用程序和协议栈代码镜像区域是基于预定义符号 ICALL_STACK0_ADDR 和 ICALL_STACK0_START 的,这些值定义协议栈镜像入口函数的硬编码 Flash 地址,它本质上是应用程序和协议栈项目边界字对齐的 Flash 地址。为了确保正确链接,应用程序和协议栈项目都必须使用相同的定义符号。默认情况下,链接器配置会将未使用的 Flash 分配给应用程序项目,但可以通过边界工具手动或自动修改。使用边界工具配置 Flash 边界地址的有关信息,请参阅本文后面的边界工具操作。
Flash 的 Simple NV (SNV)区域用于存储不易变动的数据,例如用于绑定的加密密钥或需要存储的自定义参数。协议栈可以配置成为 SNV 预留最多两个 4kB Flash page ,有效数据仅存储在一个可用的 Flash page 中。为了最小化 Flash 上的擦除周期数,当扇区具有 80% 的无效数据时,SNV 管理器对 Flash扇区(可能是多个扇区)进行压缩。压缩是将有效数据复制到临时区域,然后擦除先前存储数据的扇区。再根据 OSAL_SNV Values
中描述的 OSAL_SNV 值,该有效数据接着会被放回新擦除的扇区中或着保存在新扇区中。可以通过在协议栈项目中设置 OSAL_SNV 预处理器符号的值来配置分配给 SNV 的 Flash 扇区数目。
表 2 列出了可配置的有效值以及相关描述。
表2. OSAL_SNV 值
OSAL_SNV值 | 描述 |
---|---|
0 | SNV 未使能,不能在 NV 中存储绑定密钥。这时应用程序和协议栈工程代码的存储区域最大,GAP Bond Manager 也不能使能。在协议栈工程中需要设置预处理符号 NO_OSAL_SNV 关闭 GAP Bond Manager 功能。关于配置低功耗蓝牙协议栈特征可以查看 Stack Configurations |
1 (默认值) | 分配一个 Flash 扇区给 SNV ,绑定信息存储在 NV 中。Flash 压缩时使用 cache RAM 作为中间存储,因此压缩期间的功率损耗将导致 SNV 数据丢失。由于临时禁用 cache ,在压缩期间可能会导致系统性能的降低。在协议栈工程中设置预处理符号 OSAL_SNV = 1 |
2 | 分配两个 Flash 扇区给 SNV ,绑定信息存储在 NV 区。在压缩期间功率损耗时 SNV 数据会受到保护 |
将 OSAL_SNV 设置成其他值是无效的。设置的值越低,给应用程序或协议栈工程分配的代码空间就越大。可以使用以下 API 读取或写入 SNV 。
uint8 osal_snv_read( osalSnvId_t id, osalSnvLen_t len, void *pBuf)
||从 NV 读取数据|
|形参|id -有效的 NV 条目
len -读取数据的长度
pBuf -存储读取数据的缓冲区指针|
|返回值|SUCCESS:NV 条目读取成功
NV_OPER_FAILED:读取 NV 条目失败|
uint8 osal_snv_write(osalSnvId_t id,osalSnvLen_t len,void * pBuf)
||写数据到 NV |
|形参|id -有效的 NV 条目
len-写入数据的长度
pBuf -包含了需要写入数据的缓冲区指针。所有数据都会被立即更新|
|返回值|SUCCESS:写入 NV 条目成功
NV_OPER_FAILED:写入 NV 条目失败|
由于 SNV 与 BLE5-Stack 中的其他模块(如 GAP Bond Manager)是共享的,所以必须小心管理 NV 条目 ID。默认情况下,客户可用的 ID 在 bcomdef.h 中有定义,如下代码所示。
1 // Device NV Items - Range 0 - 0x1F
2 #define BLE_NVID_IRK 0x02 //!< The Device's IRK
3 #define BLE_NVID_CSRK 0x03 //!< The Device's CSRK
4 #define BLE_NVID_SIGNCOUNTER 0x04 //!< The Device's Sign Counter
5 #define BLE_LRU_BOND_LIST 0x05 //!< The Device's order of bond indexes in least recently used order
6 // Bonding NV Items - Range 0x20 - 0x5F - This allows for 10 bondings
7 #define BLE_NVID_GAP_BOND_START 0x20 //!< Start of the GAP Bond Manager's NV IDs
8 #define BLE_NVID_GAP_BOND_END 0x5f //!< End of the GAP Bond Manager's NV IDs Range
9 // GATT Configuration NV Items - Range 0x70 - 0x79 - This must match the number of Bonding entries
10 #define BLE_NVID_GATT_CFG_START 0x70 //!< Start of the GATT Configuration NV IDs
11 #define BLE_NVID_GATT_CFG_END 0x79 //!< End of the GATT Configuration NV IDs
12 // Customer NV Items - Range 0x80 - 0x8F - This must match the number of Bonding entries
13 #define BLE_NVID_CUST_START 0x80 //!< Start of the Customer's NV IDs
14 #define BLE_NVID_CUST_END 0x8F //!< End of the Customer's NV IDs
15
16
17
如下代码显示了如何从 SNV flash 中读取和写入一个字节数组:
/*********************************************************************
* GLOBAL VARIABLES
*/
#define BUF_LEN 4
#define SNV_ID_APP 0x80
uint8 buf[BUF_LEN] ={0,};
static void SimpleBLEPeripheral_taskFxn(UArg a0, UArg a1)
{
// Initialize application
SimpleBLEPeripheral_init();
uint8 status = SUCCESS;
status = osal_snv_read(SNV_ID_APP, BUF_LEN, (uint8 *)buf);
if(status != SUCCESS)
{
Display_print1(dispHandle, 0, 0, "SNV READ FAIL: %d", status);
//Write first time to initialize SNV ID
osal_snv_write(SNV_ID_APP, BUF_LEN, (uint8 *)buf);
}
//Increment first element of array and write to SNV flash
buf[0]++;
status = osal_snv_write(SNV_ID_APP, BUF_LEN, (uint8 *)buf);
if(status != SUCCESS)
{
Display_print1(dispHandle, 0, 0, "SNV WRITE FAIL: %d", status);
}
else
{
Display_print1(dispHandle, 0, 0, "Num of Resets: %d", buf[0]);
}
// Application main loop
for (;;)
{
//...
}
NV 条目 ID 不需要事先初始化,OSAL SNV 管理器在首次调用 osal_snv_write()成功访问 SNV 时会初始化 NV ID。
当向 SNV 读取或写入大量数据时,TI 建议将读/写数据放置在静态(链接器)分配的数组或从堆中分配的缓冲区中。在本地数组中放置大量数据可能会导致任务堆栈溢出。
默认情况下,osalSnvId_t
和 osalSnvLen_t
被定义为 uint8
类型。如果定义为 uint16
类型,需要在应用程序和协议栈项目中去修改预处理符号 OSAL_SNV_UINT16_ID
的定义。
osal_snv_read
和 osal_snv_write
只允许在任务上下文中使用,在 Swis 或 Hwis 内无法调用此 API。
用户配置区(CCA)占用 Flash 的最后一页,用户可以在用户配置(CCFG)表中配置各种芯片和系统的参数。 ccfg_app_ble.c 中定义了 CCFG 表,你可以在应用程序项目的 Startup
文件夹中找到 ccfg_app_ble.c 文件。CCA 扇区的最后 88 个(sizeof(ccfg_t))字节是系统为 CCFG 表保留的,默认情况下,链接器将 CCA 扇区未使用的 Flash 分配给应用程序镜像来存储代码和数据,也可以修改链接器以保留整个扇区来存储用户参数(例如,板序列号和其他标识参数)。
CCA 区域由应用程序链接器文件中的 FLASH_LAST_PAGE
定义。在 IDE 中展示如下:
CCS 中:
MEMORY
{
...
// CCFG Page, contains .ccfg code section and some application code.
FLASH_LAST_PAGE (RX) : origin = FLASH_LAST_PAGE_START, length = FLASH_PAGE_LEN
...
}
SECTIONS
{
...
.ccfg : > FLASH_LAST_PAGE (HIGH)
...
}
IAR 中:
////////////////////////////////////////////////////////////////////////////////
// Memory Regions
////////////////////////////////////////////////////////////////////////////////
...
define region FLASH_LAST_PAGE = mem:[from(FLASH_SIZE - PAGE_SIZE) to FLASH_SIZE-1];
...
////////////////////////////////////////////////////////////////////////////////
...
// CCFG
place at end of FLASH_LAST_PAGE { readonly section .ccfg };
keep { section .ccfg }
有关 CCFG 区域和相关配置选项的详细信息,以及如何设置 CCFG 来禁止对内部 Flash 内容的访问,请参阅 CC26XX 技术参考手册。
与 Flash 类似,RAM 在应用程序和协议栈工程之间也是共享。RAM 部分在其各自的链接器文件中被配置。
cc26xx_app.icf
(IAR)和 cc26xx_app.cmd
(CCS)。.bss
和 .data
部分的 RAM 空间。此镜像在协议栈的链接器配置文件中被配置:cc26xx_stack.icf
(IAR)和 cc26xx_stack.cmd
(CCS)。下面的图 2 System Memory Map 展示了 simple_peripheral 工程默认的系统内存映射。这个图只是一个概要,你可以在 IAR 中的输出文件夹或 CCS 中的 FlashROM 文件夹中的 simple_peripheral_app.map 和 simple_peripheral_stack.map 文件中找到给定编译的确切内存位置。在利用 map 文件查看系统 flash 和 RAM 使用情况时,应用程序链接器文件包含实心箭头指向的符号,协议栈链接器文件包含虚线箭头指向的符号。
应用程序和协议栈的 RAM 内存映射基于通用的宏定义符号: ICALL_RAM0_START
。该值定义了应用程序的 RAM 空间结束以及协议栈 .BSS
和 .DATA
段镜像开始的硬编码 RAM 边界位置。与 Flash 边界不同,协议栈工程的元素(如任务堆栈和堆)在应用程序项目中分配。为确保正确链接,应用程序和协议栈工程都必须使用相同的 ICALL_RAM0_START
值。默认情况下,边界工具通过配置 ICALL_RAM0_START
将未使用的 RAM 空间分配给应用程序工程。使用边界工具配置 RAM 边界地址的有关信息,请参阅本文后面的边界工具操作。
除了 RTOS 和 ICall 堆,考虑一下内存的其他部分。如 TI-RTOS 概述中的任务所述,每个任务都有自己用于上下文切换的运行时栈。运行时栈由 RTOS用于 main()、HWi 和 SWi 。该系统堆栈在应用程序链接器文件中分配,放置在应用程序 RAM 空间的末尾。
IAR 的 RTOS 系统堆栈由 CSTACK
符号定义:
////////////////////////////////////////////////////////////////////////////////
// Stack
define symbol STACK_SIZE = 0x400;
define symbol STACK_START = RAM_END + 1;
define symbol STACK_END = STACK_START - STACK_SIZE;
//
define symbol STACK_TOP = RAM_END + 1;
export symbol STACK_TOP;
// Runtime Stack
define block CSTACK with alignment = 8, size = STACK_SIZE { section .stack };
place at end of RAM { block CSTACK };
在 IAR 中,更改 CSTACK
的大小需要调整应用程序链接器文件中的 STACK_SIZE
的值。
CCS 的 RTOS 系统堆栈由 RTOS 配置文件 appBLE.cfg 中的 Program.stack 参数定义:
/* main() and Hwi, Swi stack size */
Program.stack = 1024;
并由链接器放置在应用程序的 RAM 空间中:
/* Create global constant that points to top of stack */
/* CCS: Change stack size under Project Properties */
__STACK_TOP = __stack + __STACK_SIZE;
系统使用两个堆进行动态内存分配。应用程序设计人员必须了解每个堆的使用情况,以便最大限度地利用可用内存。
在 RTOS 配置文件 app_ble.cfg 中,RTOS 配置了一个小堆:
var HeapMem = xdc.useModule('xdc.runtime.HeapMem');
BIOS.heapSize = 1668;
该堆( HeapMem )用于初始化 RTOS 对象、分配低功耗蓝牙协议栈的任务运行时堆栈。TI 选择使用此大小的堆来满足系统初始化要求。由于此堆的体积本来就小,所以不建议从 RTOS 堆分配内存以供一般应用程序使用。有关 TI-RTOS 堆配置的更多信息,请参阅 TI-RTOS SYS/BIOS Kernel User’s Guide 中的 Heap Implementations 部分。
应用程序必须使用单独的堆。ICall 模块使用应用程序 RAM 区域的一部分,这部分也可以由各种任务使用。ICALL 堆的大小由应用程序工程中的 HEAPMGR_SIZE
预处理符号定义,可以用非零值将 ICall 堆设定为指定大小。如果 HEAPMGR_SIZE
值为 0 ,系统会自动将堆的大小设置为链接器未分配而可用的空闲 RAM 的大小。默认情况下,如 simple_peripheral
项目使用的是自动大小分配功能。虽然 ICall 堆在应用程序工程中定义,但该堆由低功耗蓝牙协议栈使用。分配内存的 API(如 GATT_bm_alloc())是从 ICall 堆分配内存。
分析 ICALL 堆使用的情况需要在应用程序工程的预处理器符号中定义 HEAPMGR_METRICS
。启用自动堆大小功能后要确定 ICall 堆的大小的有关信息,请参阅 Profiling the ICall Heap Manager ( heapmgr.h )。
注意:自动堆大小功能不能确定和知晓应用程序所需的堆大小,系统设计人员必须确保堆具有应用程序运行时需要的足够内存空间。
以下是使用 ICALL 堆动态分配可变长度(n)数组的示例:
//define pointer
uint8_t *pArray;
// Create dynamic pointer to array.
if (pArray = (uint8_t*)ICall_malloc(n*sizeof(uint8_t)))
{
//fill up array
}
else
{
//not able to allocate
}
以下是释放上一个数组空间的示例:
ICall_free(pMsg->payload);
缓存是为处理器在 RAM 上保留的一个 8KB 空间。缓存模块临时存储从 Flash 读取的数据,以便经常使用的数据不需要每次访问 Flash 来获取。这样可以减少 CPU 等待状态并节省电量,当不使用缓存时就不对它供电,当缓存没有工作时就让它处于待机和空闲的状态。
如果应用程序需要更多内存或者 SRAM 空间,则缓存可以当作 RAM 使用。这样链接器就可以将编译应用程序的一部分存储在这一部分的 RAM 中,这部分 RAM 将被称为通用 RAM( GPRAM )。但这样做会稍微降低程序的运行速度,并且会增加休眠状态下的设备功耗。主要是因为与缓存相反的 GPRAM 即使是在设备休眠时也必须供电。CC2640R2F 数据表中列出了具有和不具有缓存的待机模式下的当前消耗,您可以去查看验证。
上面已经提到将缓存作为 GPRAM 使用会稍微降低运行速度以及增加功耗,如何影响设备的功耗还是取决于应用。对于某些应用增加的功耗会非常小,但是对于处理密集型应用增加的功耗会略高一些。您可以使用 Measuring Bluetooth Low Energy Power Consumption Application Report (SWRA478)中描述的方法来验证您应用程序的电流消耗。
把缓存作为 RAM 使用需要做两件事情。首先,必须在使用这块内存的时候告诉程序是将它作为 Cache 还是 GPRAM 。其次,链接器必须被告知将要用作缓存的内存区域分配给 GPRAM ,以及哪一部分代码要存储在 GPRAM 中,这是在链接器的命令/配置文件中完成。CCS 和 IAR 链接器命令/配置文件的语法略有不同。要了解有关 CCS 链接器命令文件的更多信息,请参阅 wiki 文章Linker Command File Primer。要了解有关 IAR 链接器的更多信息,请参阅 IAR C/C++ Development Guide 。
BLE5-Stack中有一些示例项目会存在一个编译配置,允许将缓存用作 RAM 。对于多角色工程来说是很好的,在这种情况下,选择该编译配置就可以将缓存作为 RAM 使用了。
在 CCS 中:Project -> Build Configurations -> Set Active -> FlashROM-CacheAsRAM
。
在 IAR 中:Project -> Edit Configurations -> FlashROM - CacheAsRAM
。
警告:更改项目的编译配置时,工程属性/选项可能会重置。更改编译配置后,需要检查更改一些工程预定义等 。
如果要想在没有 CacheAsRAM 编译配置的工程中将缓存作为 RAM 使用,请按照下列步骤操作:
注意:CCS 用户和 IAR 用户的步骤会不同。根据您的工程所基于的示例项目不同操作步骤也会不同。对于在 Ble5stack 文件夹中的示例项目,只需要 1-5 步。
app_ble_ccfg.c
或 ccfg.c
)中 #include <startup_files/ccfg.c>
之前添加下面的代码:#ifdef CACHE_AS_RAM
#define SET_CCFG_SIZE_AND_DIS_FLAGS_DIS_GPRAM 0x0 /* Enable GPRAM */
#endif //CACHE_AS_RAM
#include <startup_files/ccfg.c>
清单1. 在休眠时作为缓存使用。
#ifdef CACHE_AS_RAM
// retain cache during standby
Power_setConstraint(PowerCC26XX_SB_VIMS_CACHE_RETAIN);
Power_setConstraint(PowerCC26XX_NEED_FLASH_IN_IDLE);
#else
// Enable iCache pre-fetching
VIMSConfigure(VIMS_BASE, TRUE, TRUE);
// Enable cache
VIMSModeSet(VIMS_BASE, VIMS_MODE_ENABLED);
#endif //CACHE_AS_RAM
警告:请确保您的程序在将缓存用作 RAM 时没有使用 VIMS 。
在同一个文件中,包括以下文件:(Ble5stack 项目中这些文件已经包含在 main.c 中)
/* Power Driver */
#include <ti/drivers/Power.h>
#include <ti/drivers/power/PowerCC26XX.h>
/* Header files required to enable instruction fetch cache */
#include <ti/devices/cc26x0r2/inc/hw_memmap.h>
#include <ti/devices/cc26x0r2/driverlib/vims.h>
3.编译器预定义选项中添加 CACHE_AS_RAM
。对于来自 Ble5stack 文件夹的示例项目,此定义将在以下文件中对执行的代码进行更改:
4.链接器预定义选项中添加 CACHE_AS_RAM=1
。这个定义会对 cc26xx_app.cmd/cc26xx_app.icf 中的执行代码进行更改。
注意:有关如何编辑编译器和链接器符号设置的更多信息,请参阅以下部分:
5.如果您的工程是基于 BLE5-Stack 的工程,这将使 .bss (ll.o 除外)从 SRAM 移动到 GPRAM ,LL.o 需要由 RF 驱动器放置在 SRAM 中。其他对象也可以根据需要移动到 .bss
中。您可以在本文后面的使用 AUX RAM 作为 RAM 中找到示例。
重新编译并更新您的应用程序工程,查看 .map
文件确定设备内存的哪些部分被占用。(或者在 CCS 中可以通过:View -> Memory Allocation
)
6.如果您的项目不是基于 BLE5-Stack 的项目,要将 Cache 用作 GPRAM 还需要进行相关修改。如果您的项目正在使用无线电,在无线电覆盖中添加 0x00018063
。
7.GPRAM 内存区域必须在链接器命令文件中定义。定义语法在 CCS 和 IA R链接器中是不同的。下面分别说明一下:
在 CCS 中,链接器命令文件的尾缀是 .cmd
(如:CC2640R2_LAUNCHXL_TIRTOS.cmd
)。
清单2. 在 Memory Sizes
下,添加 GPRAM 开始地址和长度的定义。
/*******************************************************************************
* Memory Sizes
*/
#define FLASH_BASE 0x00000000
#define GPRAM_BASE 0x11000000
#define RAM_BASE 0x20000000
#define ROM_BASE 0x10000000
#ifdef CC26X0ROM
#define FLASH_SIZE 0x00020000
#define GPRAM_SIZE 0x00002000
#define RAM_SIZE 0x00005000
#define ROM_SIZE 0x0001C000
#endif /* CC26X0ROM */
清单3. 在 Memory Definitions
下添加 GPRAM
/*******************************************************************************
* GPRAM
*/
#ifdef CACHE_AS_RAM
#define GPRAM_START GPRAM_BASE
#define GPRAM_END (GPRAM_START + GPRAM_SIZE - 1)
#endif /* CACHE_AS_RAM */
清单4. 在 MEMORY{}
中为 GPRAM 分配空间
#ifdef CACHE_AS_RAM
GPRAM(RWX) : origin = GPRAM_APP_BASE, length = GPRAM_SIZE
#endif /* CACHE_AS_RAM */
清单5. 在SECTIONS{}
中将.bss
从SRAM移动到GPRAM。
GROUP > SRAM
{
.data
#ifndef CACHE_AS_RAM
.bss
#endif /* CACHE_AS_RAM */
.vtable
.vtable_ram
vtable_ram
.sysmem
.nonretenvar
#ifdef CACHE_AS_RAM
}
#else // !CACHE_AS_RAM
} LOAD_END(heapStart)
#endif //CACHE_AS_RAM
.stack : > SRAM (HIGH) LOAD_START(heapEnd)
#ifdef CACHE_AS_RAM
ll_bss > SRAM
{
--library=cc2640_ll_*.a<ll.o> (.bss)
}LOAD_END(heapStart)
.bss :
{
*(.bss)
} > GPRAM
#endif /* CACHE_AS_RAM */
重新编译你的应用程序会将 .bss
从 SRAM 移动到 GPRAM ,放在自动堆大小开始的后面,其他对象也可以移动。请参阅本文后面的使用 AUX RAM 作为 RAM 查看例程。
.icf
(例如 CC2640R2_LAUNCHXL_TIRTOS.icf
)。清单6. 在 Memory Definitions
下面添加对 GPRAM 起始地址和长度的定义。
//////////////////////////////////////////////////////////////////////////////
// GPRAM
//
if ( isdefinedsymbol(CACHE_AS_RAM) )
{
define symbol GPRAM_START = 0x11000000;
define symbol GPRAM_SIZE = 8096;
define symbol GPRAM_END = GPRAM_START + GPRAM_SIZE;
}
清单7. 在 Memory Regions
下面为 GPRAM 分配空间。
if ( isdefinedsymbol(CACHE_AS_RAM) )
{
define region GPRAM = mem:[from GPRAM_START to GPRAM_END];
}
清单8. 在 Memory Placement
下将 .bss
从 SRAM 移动到 GPRAM。
if ( isdefinedsymbol(CACHE_AS_RAM) )
{
// GPRAM
define block GPDATA { section .bss };
place in GPRAM { block GPDATA } except { module ll.o };
}
重新编译应用程序就会将 .bss
从 SRAM 移动到 GPRAM,其他对象也可以移动。可以在本文后面的使用 AUX RAM 作为 RAM 查看例程。
AUX RAM 是属于传感器控制器中的一个 2KB 的内存区域。如果应用程序不使用传感器控制器,则可以将此内存用作应用程序的 RAM 。但访问该存储器比访问 SRAM 要慢得多,这可能导致功耗增加以及程序执行速度变慢。
要在应用程序中将 AUX RAM 作为 RAM 使用,请按照以下步骤操作(先描述 CCS,再描述 IAR)。
1.在链接器命令文件中添加一个新的定义:
在 CCS 中:Project -> Properties -> ARM Linker->Advanced Options -> Command File Preprocessing
。
在 IAR 中:Options -> Linker -> Config
,添加 AUX_AS_RAM=1
。
2.链接器命令/配置文件 CCS 和 IAR 有些不同。这里先介绍在 CCS 中更改链接器配置文件,再介绍 IAR。
cc26xx_app.cmd
中,定义 AUX_RAM 的内存区域:#ifdef AUX_AS_RAM
#define AUX_RAM_BASE 0x400E0000
#define AUX_RAM_SIZE 0x800
#endif /* AUX_AS_RAM */
MEMORY{}
中创建一个 AUX_RAM 内存区域#ifdef AUX_AS_RAM
AUX_RAM (RWX) : origin = AUX_RAM_BASE, length = AUX_RAM_SIZE
#endif /* AUX_AS_RAM */
SECTIONS{}
中将内存或代码的部分添加到 AUX_RAM 中清单9. 将目标文件移动到 AUX_RAM 中。示例来自 simple_peripheral( cc26xx_app.cmd
)
#ifdef AUX_AS_RAM
reorganized_into_auxram
{
simple_peripheral.obj(.data)
devinfoservice.obj(.data)
simple_gatt_profile.obj(.data)
icall.obj(.data)
board.obj(.bss)
} > AUX_RAM
#endif/* AUX_AS_RAM */
.obj 文件放在应用程序项目的 FlashROM 文件夹中,它们也在 .map 文件中列出了大小。链接器命令文件和内存部分的详细描述在 WiKi 文章 Linker Command File Primer 中给出。
如果你想要更多地控制 AUX_RAM 中存储的内容,你可以使用命令 #pragma DATA_SECTION
将指定的变量存储里面。但请注意,这仅适用于全局变量。
清单10.将全局显示句柄的变量移动到 AUX RAM 中一个叫做 my_section
的新区域。
// Display Interface
#pragma DATA_SECTION(dispHandle, "my_section")
Display_Handle dispHandle = NULL;
清单11. 在链接器命令文件( cc26xx_app.cmd )中,将这部分( my_section
)添加到 AUX_RAM。
#ifdef AUX_AS_RAM
reorganized_into_auxram
{
simple_peripheral.obj(my_section)
} > AUX_RAM
#endif /* AUX_AS_RAM */
警告:仅对链接器命令文件进行更改时请确保要重新编译(
Rebuild
),而不仅仅是编译(Build
)。( CCS 在您重新编译之前,不会认识到您对项>目进行了更改。)
cc26xx_app.icf
,在 Memory Definitions
下添加////////////////////////////////////////////////////////////////////////////////
// AUX_RAM
//
if ( isdefinedsymbol(AUX_AS_RAM) )
{
define symbol AUX_RAM_START = 0x400E0000;
define symbol AUX_RAM_SIZE = 0x800;
define symbol AUX_RAM_END = AUX_RAM_START + AUX_RAM_SIZE;
}
Memory Regions
中添加if ( isdefinedsymbol(AUX_AS_RAM) )
{
define region AUX_RAM = mem:[from AUX_RAM_START to AUX_RAM_END];
}
Memory Placement
下添加清单12. 将目标文件移动到 AUX_RAM 中。来自 simple_peripheral( cc26xx_app.icf
)的示例
if ( isdefinedsymbol(AUX_AS_RAM) )
{
// AUX_RAM
define block AUXDATA { section .data object simple_peripheral.o,
section .data object devinfoservice.o,
section .data object simple_gatt_profile,
section .data object icall.o,
section .data object board.o};
place in AUX_RAM { block AUXDATA };
}
.o 文件是在 .map 文件中列出的。有关链接器配置文件的更多信息,请参阅 IAR C/C++ Development Guide 。
如果你想要更多操控 AUX_RAM 中存储的内容,你可以使用命令 #pragma DATA_SECTION
将指定的变量存储在里面。但请注意,这仅适用于全局变量。
清单13.将全局显示句柄的变量移动到 AUX RAM 中一个叫做 my_section
的新区域
// Display Interface
#pragma location="my_section"
Display_Handle dispHandle = NULL;
清单14. 在链接器配置文件( cc26xx_app.cmd
)中,将下面这部分代码添加到 AUX_RAM
if ( isdefinedsymbol(AUX_AS_RAM) )
{
// AUX_RAM
define block AUXDATA { section my_section object simple_peripheral.o };
place in AUX_RAM { block AUXDATA };
}
在将应用程序和协议栈工程编译为两个镜像文件时,边界工具用来自动调整两个镜像之间共享的各个 RAM 和 Flash 的边界地址符号。应用程序将协议栈作为库使用的工程不需要使用 Frontier Tool 。
边界工具作为在协议栈工程编译后的一个步骤来运行,它基于对协议栈链接器和映射文件的分析来调整相应的 RAM 和 Flash 边界。边界工具不会修改任何项目文件和源代码,不运行任何编译器,也不执行链接器优化,只用于调整和更新位于编译器和链接器配置文件中的各个 Flash 和 RAM 的边界地址。应用程序以及协议栈工程都会使用这些配置文件。
边界工具安装到 SDK 中的以下路径:
<SDK_INSTALL_DIR>\examples\rtos\CC2640R2_LAUNCHXL\ble5stack\tools\frontier\frontier.exe
(< SDK_INSTALL_DIR >是蓝牙协议栈 SDK 的安装路径)
此工具的 python 源代码也包括在其中。
表3显示了边界工具更新的边界地址符号。
表3.边界地址和符号
边界地址符号 | 描述 |
---|---|
ICALL_STACK0_START | 应用程序和协议栈之间的 Flash 边界地址。代表应用程序镜像的结束和协议栈镜像的开始 |
ICALL_STACK0_ADDR | 协议栈实体的地址(Flash) |
ICALL_RAM0_START | 应用程序和协议栈之间的 RAM 边界地址。代表应用程序 RAM 的结束以及协议栈 RAM 的开始 |
默认情况下所有的示例应用程序项目都配置成使用边界工具,不需要进行边界工具的用户配置。当协议栈配置更改时,或者在协议栈工程中一些文件的更新导致协议栈镜像大小发生变化,边界文件可能会更新。因此在每次协议栈工程编译后都必须重新编译应用程序工程。
注意:
frontier tool 替代了早期 SDK 中使用的边界工具。
边界工具( frontier.exe )是协议栈工程在 CCS 或 IAR 集成开发环境中编译后进行调用的。如果需要对 RAM 或 Flash 边界进行调整,则边界工具将更新以下列出的边界链接器配置和 C 定义文件。要并入更新后的配置值,请在应用程序工程上执行Project -> Rebuild All
。在重新编译应用程序之前,协议栈工程必须正确编译和链接。
SDK 中的每个工程都有一组配置文件,IDE 的链接器和编译器利用它们设置或调整相应的 Flash 和 RAM 值。这些配置文件在应用程序和协议栈工作区之间共享,并存储在以下位置:
<SDK_INSTALL_DIR>\examples\rtos\<EVAL_BOARD>\ble5stack\<PROJECT>\tirtos\<IDE>\config
其中 <EVAL_BOARD>
是评估平台,<PROJECT>
是示例应用程序(例如 simple_peripheral
),<IDE>
是 IAR 或 CCS 。
例如,在 CC2640R2F LaunchPad 上运行的 simple_peripheral
示例应用程序,边界配置文件位于以下路径:
CCS: <SDK_INSTALL_DIR>\examples\rtos\CC2640R2_LAUNCHXL\ble5stack\simple_peripheral\tirtos\ccs\config
IAR: <SDK_INSTALL_DIR>\examples\rtos\CC2640R2_LAUNCHXL\ble5stack\simple_peripheral\tirtos\iar\config
以下是边界配置文件:
iar_boundary.xcl
[IAR] 或 ccs_linker_defines.cmd
[CCS],为链接器定义边界地址。该文件位于 TOOLS IDE 文件夹中,并在需要进行调整时由边界工具进行更新。iar_boundary.bdef
[IAR] 或 ccs_compiler_defines.bcfg
[CCS]。定义编译器的边界地址。该文件位于 TOOLS IDE 文件夹中,并在需要进行调整时由边界工具进行更新。注意:边界链接器配置文件和边界 C 定义文件中的值必须匹配。
文章所有代码、工具、文档开源。加入我们QQ群 591679055获取更多支持,共同研究CC2640R2F&BLE5.0。