MCUXprosso 中 ,将 KW36 flash 烧写程序放在 RAM 运行

1. 目的

         本文主要探讨在 MCUXprosso IDE 中 ,KW36 的工程中 ,如何将 Flash 烧写程序放在 RAM 中并运行 。

 

2. 存储规格

                                                                   
                                                                                                图 1  KW36 存储分布

         如图 1 ,对于 Flash ,分为 PFlash 和 FlexNVM 两个块,FlexNVM 可做为仿真 EEPROM 的备份区 ,Flash 每个扇区为 2KB ,每次擦除最少一个扇区 ,Flash 每页可为 8B ,所以每次写入要 8 字节写入 ,地址 8 对齐 。

         FlexRAM 可配置为仿真 EEPROM (8KB),对齐可配置为 1、2、4字节对齐 。

3. 代码

         首选选择一个 led 灯例程 ,右击工程->属性->MCU setting->Memory details 中 ,看 Flash 和 RAM 的分区情况 ,如图 2 ,其中 RAM3~5 是不可动的 ,所以只能看 RAM 和 RAM2 情况 ,在此我选择的是RAM2 ; 然后在相对应的工程下->Debug(没有的先编译一下)->xxx_Debug.ld(xxx 表示工程名) 中看分段情况 ,如图 3 ,选择 .ramfunc.$SRAM1 段 。

                                         
                                                                                               图 2 分区情况
                                                                    

                                                                            图 3 RAM2/SRAM1 分段情况

         当然 ,如果自己想要去分配 RAM 也可以 ,但记得在 startup->startup_mkw36z4.c->ResetISR 中 ,将代码从 Flash 复制到 Ram ,如下代码 :

    SectionTableAddr = &__data_section_table;

while (SectionTableAddr < &__data_section_table_end) {

LoadAddr = *SectionTableAddr++;

ExeAddr = *SectionTableAddr++;

SectionLen = *SectionTableAddr++;

data_init(LoadAddr, ExeAddr, SectionLen);

}

       其中 __data_section_table  和 __data_section_table_end 在刚才打开的 ld 文件中 ,可以看到载入的 .data_RAM2 与图 3 一致 ,说明在 ResetISR 中 ,已经将代码复制到 RAM 中 。

        __data_section_table = .;

LONG(LOADADDR(.data));

LONG( ADDR(.data));

LONG( SIZEOF(.data));

LONG(LOADADDR(.data_RAM2));

LONG( ADDR(.data_RAM2));

LONG( SIZEOF(.data_RAM2));

LONG(LOADADDR(.data_RAM3));

LONG( ADDR(.data_RAM3));

LONG( SIZEOF(.data_RAM3));

LONG(LOADADDR(.data_RAM4));

LONG( ADDR(.data_RAM4));

LONG( SIZEOF(.data_RAM4));

LONG(LOADADDR(.data_RAM5));

LONG( ADDR(.data_RAM5));

LONG( SIZEOF(.data_RAM5));

__data_section_table_end = .;

         确定好这些前提条件之后 ,就可以开始写代码 ,首先需要确定擦写代码里面所调用的函数也是需要在 RAM 里面的 ,不然一调用就需要读取 Flash 里面的指令 ,跟我们的期望不符合 。基于此 ,就写一个最简单的烧写程序 。

         在 source 下面创建 flash_driver.h 和 flash_driver.c 文件 ,flash_driver.h 文件内容如下 :

#include "fsl_device_registers.h"

#define FLASH_ALIGN 8 // 64bit , 对齐字节
#define FLASH_SECTOR 2048 // 2KB , 扇区大小
#define FLASH_MAX_RANGE 0x80000 // 512KB , Flash 最大范围

void __attribute__((section(".ramfunc.$SRAM1"))) kw36_flash_init(void);

/*
* 擦除 flash
* start_add : flash 要擦除的起始地址 ,要 2KB(2048字节) 对齐
* lengthInSectors : 要擦除的扇区个数
* */
void __attribute__((section(".ramfunc.$SRAM1"))) kw36_flash_erase(uint32_t start_addr, uint32_t lengthInSectors);

/*
* 向 flash 中写入数据
* start_add : flash 要 program 的起始地址 ,要 8字节对齐
* src : 要写入数据的指针 ,元素有效个数是 2 的倍数
* lengthInBytes : 要写入的数据长度 ,要是 8 的倍数

* */
void __attribute__((section(".ramfunc.$SRAM1"))) kw36_flash_program(uint32_t start_addr, uint32_t *src, uint32_t lengthInBytes);


    其中 __attribute__((section(".data.$SRAM1"))) 将此函数制定到 .ramfunc.$SRAM1 段中 。

flash_driver.c 代码如下 :

static volatile uint32_t *const kFCCOBx = (volatile uint32_t *)&(FTFE->FCCOB3);

/*

* 擦除 flash

* start_add : flash 要擦除的起始地址 ,要 2KB(2048字节) 对齐

* lengthInSectors : 要擦除的扇区个数

* */

void __attribute__((section(".ramfunc.$SRAM1"))) kw36_flash_erase(uint32_t start_addr, uint32_t lengthInSectors){

uint8_t flash_status=0; // flash 状态



if( 0==lengthInSectors ){ // 无擦除

return ;

}

// 超过最大范围

if( (start_addr+lengthInSectors*FLASH_SECTOR) > FLASH_MAX_RANGE ){

return ;

}



if( 0!=(start_addr%FLASH_SECTOR) ){ // 地址不是 2KB 对齐 ,不可 erase

return ;

}



while(lengthInSectors>0){

kFCCOBx[0]= (uint32_t)( ((uint32_t)(0x09<<24)) | ((uint32_t)(start_addr&0xFFFFFF)) ) ; // 写入命令



FTFE->FSTAT=0x70; // 清除错误标志



start_addr+=FLASH_SECTOR; //

lengthInSectors--; //



FTFE->FSTAT=0x80; // 执行命令



flash_status=0;

while( !(0xF0&flash_status) ){ // 是否执行完毕

flash_status=FTFE->FSTAT;

}



if(0x20&flash_status){ // 判断返回的状态值

return ;

FTFE->FSTAT=0x80; // 清除错误标志

}else if(0x10&flash_status){

return ;

FTFE->FSTAT=0x80; // 清除错误标志

}else if(0x1&flash_status){

return ;

}else{



}

}

}

/*

* Kai Chen

* 向 flash 中写入数据

* start_add : flash 要 program 的起始地址 ,要 8字节对齐

* src : 要写入数据的指针 ,元素有效个数是 2 的倍数

* lengthInBytes : 要写入的数据长度 ,要是 8 的倍数

* */

void __attribute__((section(".ramfunc.$SRAM1"))) kw36_flash_program(uint32_t start_addr, uint32_t *src, uint32_t lengthInBytes){

uint8_t flash_status=0; // flash 状态



if( (NULL==src) || (0==lengthInBytes) ){ // 无数据写入 ,不可 program

return ;

}



if( (start_addr+lengthInBytes) > FLASH_MAX_RANGE ){ // 超过最大范围

return ;

}



if( (0!=(start_addr%FLASH_ALIGN)) || (0!=(lengthInBytes%FLASH_ALIGN)) ){ // 不是64位对齐,不可 program

return ;

}



while(lengthInBytes>0){

kFCCOBx[0]= (uint32_t)( ((uint32_t)(0x07<<24)) | ((uint32_t)(start_addr&0xFFFFFF)) ) ; // 写入命令

kFCCOBx[1]= (*src); // 写入数据

#if (FLASH_ALIGN==8)

src++;

kFCCOBx[2]= (*src); // 写入数据

#endif



FTFE->FSTAT=0x70; // 清除错误标志



start_addr+=FLASH_ALIGN; //

src++; //

lengthInBytes-=FLASH_ALIGN; //



FTFE->FSTAT=0x80; // 执行命令



flash_status=0;

while( !(0xF0&flash_status) ){ // 是否执行完毕

flash_status=FTFE->FSTAT;

}



if(0x20&flash_status){ // 判断返回的状态值



FTFE->FSTAT=0x80; // 清除错误标志

}else if(0x10&flash_status){



FTFE->FSTAT=0x80; // 清除错误标志

}else if(0x1&flash_status){



}else{



}

}

}

         在上面 ,之所以每次执行完的状态检查中,一检查到错误情况就写入 FTFE->FSTAT=0x80 是因为一旦出错 ,FSTAT->CCIF 必须写第一次清除错误情况 ,第二次写入才会清零启动命令 。

3. 参考资料

(1) NXP :《MKW36A512RM.pdf》

         下载链接 :

https://www.nxp.com/products/wireless/bluetooth-low-energy/kw36-35-34-arm-cortex-m0-pluskinetis-kw36-35-34-bluetooth-low-energy-32-bit-mcus-nxp:KW36-35?tab=Documentation_Tab

 

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

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

评论