# 存储架构 # ## Flash ## Flash 以 4KB 为一个 Page 进行擦除/写入操作(如果在一个 Page 需要写入一个字节,需要将整个 Page 擦除),Flash 组成部分及其它们关联的链接器文件如下。 对于协议栈以库文件方式链接的工程( _lirary ): * ** Application Image Code Space **(应用程序镜像代码区):应用程序和协议栈分为两个工程来管理,协议栈编译生成库文件供应用程序调用。应用程序会生成一个镜像文件,该镜像文件在应用程序的链接器配置文件中被配置为 `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 存储区域是协议栈镜像的一部分。 * ** Customer Configuration Area(CCA)**:Flash 的最后一个 Page 用于存储客户特定芯片配置(CCFG)参数。CCA 扇区未使用的空间被分配给应用程序项目,详情请参阅本文后面的**用户配置区**。 ### Flash 内存映射 ### 本节介绍用于两个拆分镜像项目配置的 Flash 内存映射。如下图1所示,应用程序链接文件存储在用实线箭头指向的内存位置,协议栈链接文件存储在用虚线箭头指向的内存位置。 ![图1. 系统闪存映射](http://www.leconiot.com/md_res/cc2640r2f/cc2640r2f_architecture/memroy_map/images/flashmemorymap.png) >**注意**:将协议栈作为库的工程,协议栈以库的形式被应用程序调用。协议栈和应用程序合并放在应用程序镜像代码空间中,不需要与拆分镜像 >相关的任何边界要求。 下表总结了图 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个字节| ### 应用程序和协议栈的 Flash 边界 ### 应用程序和协议栈代码镜像区域是基于预定义符号 ICALL_STACK0_ADDR 和 ICALL_STACK0_START 的,这些值定义协议栈镜像入口函数的硬编码 Flash 地址,它本质上是应用程序和协议栈项目边界字对齐的 Flash 地址。为了确保正确链接,应用程序和协议栈项目都必须使用相同的定义符号。默认情况下,链接器配置会将未使用的 Flash 分配给应用程序项目,但可以通过边界工具手动或自动修改。使用边界工具配置 Flash 边界地址的有关信息,请参阅本文后面的**边界工具操作**。 ### 使用 Simple NV 进行 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 中有定义,如下代码所示。 ```C 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 中读取和写入一个字节数组: ```C /********************************************************************* * 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 中: ```C 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 中: ```C //////////////////////////////////////////////////////////////////////////////// // 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 技术参考手册](http://www.ti.com/lit/ug/swcu117g/swcu117g.pdf)。 ## RAM ## 与 Flash 类似,RAM 在应用程序和协议栈工程之间也是共享。RAM 部分在其各自的链接器文件中被配置。 * 应用程序镜像:应用程序和共享堆的 RAM 空间。此镜像在应用程序的链接器配置文件中被配置: `cc26xx_app.icf`(IAR)和 `cc26xx_app.cmd`(CCS)。 * 堆栈镜像:协议栈的 `.bss` 和 `.data` 部分的 RAM 空间。此镜像在协议栈的链接器配置文件中被配置:`cc26xx_stack.icf`(IAR)和 `cc26xx_stack.cmd`(CCS)。 ### RAM 内存映射 ### 下面的图 2 ** System Memory Map **展示了 simple_peripheral 工程默认的系统内存映射。这个图只是一个概要,你可以在 IAR 中的输出文件夹或 CCS 中的 FlashROM 文件夹中的 simple_peripheral_app.map 和 simple_peripheral_stack.map 文件中找到给定编译的确切内存位置。在利用 map 文件查看系统 flash 和 RAM 使用情况时,应用程序链接器文件包含实心箭头指向的符号,协议栈链接器文件包含虚线箭头指向的符号。 ![图2. 系统内存映射](http://www.leconiot.com/md_res/cc2640r2f/cc2640r2f_architecture/memroy_map/images/image184.jpeg) ### 应用程序和协议栈的 RAM 边界 ### 应用程序和协议栈的 RAM 内存映射基于通用的宏定义符号: `ICALL_RAM0_START` 。该值定义了应用程序的 RAM 空间结束以及协议栈 `.BSS` 和 `.DATA` 段镜像开始的硬编码 RAM 边界位置。与 Flash 边界不同,协议栈工程的元素(如任务堆栈和堆)在应用程序项目中分配。为确保正确链接,应用程序和协议栈工程都必须使用相同的 `ICALL_RAM0_START` 值。默认情况下,边界工具通过配置 `ICALL_RAM0_START` 将未使用的 RAM 空间分配给应用程序工程。使用边界工具配置 RAM 边界地址的有关信息,请参阅本文后面的**边界工具操作**。 ### 系统堆栈 ### 除了 RTOS 和 ICall 堆,考虑一下内存的其他部分。如[ TI-RTOS 概述](http://docs.leconiot.com/doku.php?id=cc2640r2f:cc2640r2f_architecture:ti_rtos:tirtos_overivew)中的**任务**所述,每个任务都有自己用于上下文切换的运行时栈。运行时栈由 RTOS用于 main()、HWi 和 SWi 。该系统堆栈在应用程序链接器文件中分配,放置在应用程序 RAM 空间的末尾。 IAR 的 RTOS 系统堆栈由 `CSTACK` 符号定义: ```C //////////////////////////////////////////////////////////////////////////////// // 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 参数定义: ```C /* main() and Hwi, Swi stack size */ Program.stack = 1024; ``` 并由链接器放置在应用程序的 RAM 空间中: ```C /* 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 配置了一个小堆: ```C 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)数组的示例: ```C //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 } ``` 以下是释放上一个数组空间的示例: ```C ICall_free(pMsg->payload); ``` ## 缓存( Cache )/ GPRAM ## 缓存是为处理器在 RAM 上保留的一个 8KB 空间。缓存模块临时存储从 Flash 读取的数据,以便经常使用的数据不需要每次访问 Flash 来获取。这样可以减少 CPU 等待状态并节省电量,当不使用缓存时就不对它供电,当缓存没有工作时就让它处于待机和空闲的状态。 ### 把缓存作为 RAM 使用 ### 如果应用程序需要更多内存或者 SRAM 空间,则缓存可以当作 RAM 使用。这样链接器就可以将编译应用程序的一部分存储在这一部分的 RAM 中,这部分 RAM 将被称为通用 RAM( GPRAM )。但这样做会稍微降低程序的运行速度,并且会增加休眠状态下的设备功耗。主要是因为与缓存相反的 GPRAM 即使是在设备休眠时也必须供电。CC2640R2F 数据表中列出了具有和不具有缓存的待机模式下的当前消耗,您可以去查看验证。 上面已经提到将缓存作为 GPRAM 使用会稍微降低运行速度以及增加功耗,如何影响设备的功耗还是取决于应用。对于某些应用增加的功耗会非常小,但是对于处理密集型应用增加的功耗会略高一些。您可以使用[ Measuring Bluetooth Low Energy Power Consumption Application Report (SWRA478)](http://www.ti.com/lit/an/swra478c/swra478c.pdf)中描述的方法来验证您应用程序的电流消耗。 把缓存作为 RAM 使用需要做两件事情。首先,必须在使用这块内存的时候告诉程序是将它作为 Cache 还是 GPRAM 。其次,链接器必须被告知将要用作缓存的内存区域分配给 GPRAM ,以及哪一部分代码要存储在 GPRAM 中,这是在链接器的命令/配置文件中完成。CCS 和 IAR 链接器命令/配置文件的语法略有不同。要了解有关 CCS 链接器命令文件的更多信息,请参阅 wiki 文章[Linker Command File Primer](http://processors.wiki.ti.com/index.php/Linker_Command_File_Primer)。要了解有关 IAR 链接器的更多信息,请参阅[ IAR C/C++ Development Guide ](http://ftp.iar.se/WWWfiles/arm/webic/doc/EWARM_DevelopmentGuide.ENU.pdf)。 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 步。 1. 在CCFG 文件(`app_ble_ccfg.c` 或 `ccfg.c` )中 `#include ` 之前添加下面的代码: ```C #ifdef CACHE_AS_RAM #define SET_CCFG_SIZE_AND_DIS_FLAGS_DIS_GPRAM 0x0 /* Enable GPRAM */ #endif //CACHE_AS_RAM #include ``` 2. main() 中添加以下代码: 清单1. 在休眠时作为缓存使用。 ```C #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 中) ```C /* Power Driver */ #include #include /* Header files required to enable instruction fetch cache */ #include #include ``` 3.编译器预定义选项中添加 `CACHE_AS_RAM` 。对于来自 Ble5stack 文件夹的示例项目,此定义将在以下文件中对执行的代码进行更改: * ble_user_config.c * main.c * ble_user_config.h 4.链接器预定义选项中添加 `CACHE_AS_RAM=1` 。这个定义会对 cc26xx_app.cmd/cc26xx_app.icf 中的执行代码进行更改。 >**注意**:有关如何编辑编译器和链接器符号设置的更多信息,请参阅以下部分: >CCS: >[访问预处理器符号](http://docs.leconiot.com/doku.php?id=cc2640r2f:cc2640r2f_architecture:ccs:development_with_ccs) >[访问链接器符号](http://docs.leconiot.com/doku.php?id=cc2640r2f:cc2640r2f_architecture:ccs:development_with_ccs) >IAR: >[访问预处理器符号](http://docs.leconiot.com/doku.php?id=cc2640r2f:cc2640r2f_architecture:iar:development_with_iar) >[访问链接器符号](http://docs.leconiot.com/doku.php?id=cc2640r2f:cc2640r2f_architecture:iar:development_with_iar) 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 开始地址和长度的定义。 ```C /******************************************************************************* * 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 ```C /******************************************************************************* * 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 分配空间 ```C #ifdef CACHE_AS_RAM GPRAM(RWX) : origin = GPRAM_APP_BASE, length = GPRAM_SIZE #endif /* CACHE_AS_RAM */ ``` 清单5. 在`SECTIONS{}`中将`.bss`从SRAM移动到GPRAM。 ```C 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 (.bss) }LOAD_END(heapStart) .bss : { *(.bss) } > GPRAM #endif /* CACHE_AS_RAM */ ``` 重新编译你的应用程序会将 `.bss` 从 SRAM 移动到 GPRAM ,放在自动堆大小开始的后面,其他对象也可以移动。请参阅本文后面的**使用 AUX RAM 作为 RAM **查看例程。 8. 在 IAR 中,链接器配置文件的后缀是 `.icf`(例如 `CC2640R2_LAUNCHXL_TIRTOS.icf` )。 清单6. 在 `Memory Definitions` 下面添加对 GPRAM 起始地址和长度的定义。 ```C ////////////////////////////////////////////////////////////////////////////// // 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 分配空间。 ```C if ( isdefinedsymbol(CACHE_AS_RAM) ) { define region GPRAM = mem:[from GPRAM_START to GPRAM_END]; } ``` 清单8. 在 `Memory Placement` 下将 `.bss` 从 SRAM 移动到 GPRAM。 ```C 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 ## AUX RAM 是属于[传感器控制器](http://docs.leconiot.com/doku.php?id=cc2640r2f:tool_kits:sensor_controller:sensor_controller)中的一个 2KB 的内存区域。如果应用程序不使用传感器控制器,则可以将此内存用作应用程序的 RAM 。但访问该存储器比访问 SRAM 要慢得多,这可能导致功耗增加以及程序执行速度变慢。 ### 使用 AUX RAM 作为 RAM ### 要在应用程序中将 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 的内存区域: ```C #ifdef AUX_AS_RAM #define AUX_RAM_BASE 0x400E0000 #define AUX_RAM_SIZE 0x800 #endif /* AUX_AS_RAM */ ``` * 在 `MEMORY{}` 中创建一个 AUX_RAM 内存区域 ```C #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` ) ```C #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 ](http://processors.wiki.ti.com/index.php/Linker_Command_File_Primer)中给出。 如果你想要更多地控制 AUX_RAM 中存储的内容,你可以使用命令 `#pragma DATA_SECTION` 将指定的变量存储里面。但请注意,这仅适用于全局变量。 清单10.将全局显示句柄的变量移动到 AUX RAM 中一个叫做 `my_section` 的新区域。 ```C // Display Interface #pragma DATA_SECTION(dispHandle, "my_section") Display_Handle dispHandle = NULL; ``` 清单11. 在链接器命令文件( cc26xx_app.cmd )中,将这部分( `my_section` )添加到 AUX_RAM。 ```C #ifdef AUX_AS_RAM reorganized_into_auxram { simple_peripheral.obj(my_section) } > AUX_RAM #endif /* AUX_AS_RAM */ ``` >**警告**:仅对链接器命令文件进行更改时请确保要重新编译(` Rebuild`),而不仅仅是编译(`Build`)。( CCS 在您重新编译之前,不会认识到您对项>目进行了更改。) 3. 对于 IAR ,打开链接器配置文件 `cc26xx_app.icf` ,在 `Memory Definitions` 下添加 ```C //////////////////////////////////////////////////////////////////////////////// // 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` 中添加 ```C 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` )的示例 ```C 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 ](http://ftp.iar.se/WWWfiles/arm/webic/doc/EWARM_DevelopmentGuide.ENU.pdf)。 如果你想要更多操控 AUX_RAM 中存储的内容,你可以使用命令 `#pragma DATA_SECTION` 将指定的变量存储在里面。但请注意,这仅适用于全局变量。 清单13.将全局显示句柄的变量移动到 AUX RAM 中一个叫做 `my_section` 的新区域 ```C // Display Interface #pragma location="my_section" Display_Handle dispHandle = NULL; ``` 清单14. 在链接器配置文件( `cc26xx_app.cmd` )中,将下面这部分代码添加到 AUX_RAM ```C 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 中的以下路径: `\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 值。这些配置文件在应用程序和协议栈工作区之间共享,并存储在以下位置: `\examples\rtos\\ble5stack\\tirtos\\config` 其中 `` 是评估平台,`` 是示例应用程序(例如 `simple_peripheral` ),`` 是 IAR 或 CCS 。 例如,在 CC2640R2F LaunchPad 上运行的 `simple_peripheral` 示例应用程序,边界配置文件位于以下路径: `CCS: \examples\rtos\CC2640R2_LAUNCHXL\ble5stack\simple_peripheral\tirtos\ccs\config` `IAR: \examples\rtos\CC2640R2_LAUNCHXL\ble5stack\simple_peripheral\tirtos\iar\config` 以下是边界配置文件: * 边界链接器配置文件:`iar_boundary.xcl` [IAR] 或 `ccs_linker_defines.cmd` [CCS],为链接器定义边界地址。该文件位于 TOOLS IDE 文件夹中,并在需要进行调整时由边界工具进行更新。 * 边界 C 定义文件:`iar_boundary.bdef` [IAR] 或 `ccs_compiler_defines.bcfg` [CCS]。定义编译器的边界地址。该文件位于 TOOLS IDE 文件夹中,并在需要进行调整时由边界工具进行更新。 >**注意**:边界链接器配置文件和边界 C 定义文件中的值必须匹配。 ## 加入我们 ## 文章所有代码、工具、文档开源。加入我们[**QQ群 591679055**](http://shang.qq.com/wpa/qunwpa?idkey=d94f12d37c3b37892af4b757c6dc34bea140f3f3128a8d68e556a3d728148e85)获取更多支持,共同研究CC2640R2F&BLE5.0。

CC2640R2F&BLE5.0-乐控畅联 © Copyright 2017, 成都乐控畅联科技有限公司.