error : flash system error_Kinetis的Flash擦/写/读/操作

    在大部分的MCU里面,只有一个Flash闪存集成在芯片中,当执行 Flash 内部编程操作的时候,需要拷贝Flash编程相关的函数到RAM里面运行,主要原因是当写MCU的Flash时,Flash不能并行的执行读和写。代码放在RAM里面执行可以避免Flash读/写冲突。

1. ESFC控制位

    Cotex M0+内核的 kinetis中,使用控制寄存器 (MCM_PLACR) 。MCM寄存器为给个外设选择仲裁策略并配置闪存控制器。有一个有用的位是第16位ESFC:启用/禁止Flash 闪存控制器。当此位启用时,它可以在闪存忙时暂停闪存控制器。设置ESFC位可以很好地平衡Flash读写的时序,写Flash时,读Flash指令可以等待,反之亦然。使用ESFC位可以使Flash编程更容易。因此,一个Flash可以自己写。有了此位不需要在做中断处理的时候,关中断再开中断,比如插拔USB或者网口通信的时候,有USB断连或者是网口丢包的情况发生。

MCM_PLACRMCM_PLACR寄存器定义如下:

5b15f798814c15a8cb54cf3f6be4da5e.png    MCM_PLACR ESFC位是使能Flash Stalling控制器,当Flash处于忙状态的时候,0是关闭Stalling控制器,1是使能Stalling控制器。

#define MCM_PLACR_ESFC_MASK 0x10000u

void Flash_init(void)

{

MCM_PLACR|=MCM_PLACR_ESFC_MASK;

}

    当前的 MCM_PLACR位,在如下的 Cotex M0+ 内核有支持:MKE02/04/06( MKE06Z 文档里面有 "ESFC 使能暂停机制,这个位必须在Flahs操作执行前先置位,在操作完成后清除这个位)

SKEA;KE;MKL;MKM;MKV10;MKW

2. 源目标地址对齐访问

    如下代码源目标地址不对齐会导致hardfault。

uint8_t pData[] = {1,1,1,1,1,1,1,1,1,1,1,1,1,};


flash_config_t s_flashDriver;

memset(&s_flashDriver, 0, sizeof(flash_config_t));

FLASH_Init(&s_flashDriver);

FLASH_Program(&s_flashDriver, 0x55000, (uint32_t*)(pData+1), 4);‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

    在调用上述函数FLASH_Program() (fsl_flash.c) 会出现错误,会出现Hardfault 错误的代码行如下:

kFCCOBx[1] = *src++;‍

    其中src是指向要写入flash的数据的指针(即pData+1)。但是,如果将pData或pData+4传递给FLASH_Program(),则不会给出硬件Hardfault 错误,并且程序的行为符合预期。所以,看起来必须有一个单字对齐的源地址(而不是目标地址,那么为什么它必须是单字对齐的!)。

    Cortex-M4内核可以从指针中读取未对齐的长字,但Cortex-M0+内核不能并且会出现HardFault。上述flash驱动程序可以在Cortex-M4上工作,而用户不需要注意这一点,但在Cortex-M0+上可能会失败,这意味着它不可移植。如果更改代码,将输入指针(在函数中)作为字节指针读取,并将4个字节复制到本地的长字变量中,然后再将其写入闪存寄存器,将使它(更)可移植(并且更易于使用)。

3.  MK26FN(Cortex-M4内核) driver_examples\flash\pflash SDK例程包中的疑问。

a.  BUFFER_LEN 是否必须除以 2,同时是字对齐的?如果不是的字对齐的,执行FLASH_Erase 或者 FLASH_Program编程的时候会进入如下的error_trap。

void error_trap(void)
{
PRINTF("\r\n\r\n\r\n\t---- HALTED DUE TO FLASH ERROR! ----");
while (1)
{
}
}

b. 而且目的地址必须是偶数地址,否则设置其他地址也会进入 error_trap。

参考数据手册,Flash program phrase command 是 8 bytes (数据)。

501cd985e7cf28ef143f0c381b92eb6c.png

4. MKE14F512VLH16(Cortex-M4内核)  VTOR 偏移地址对齐的问题

    测试如下的三种情况,当应用程序地址是0x40010,当BOOT跳转到 0x40010后,运行应用程序,程序会复位,为什么呢?

  • 如果应用程序地址是0x10000, 在BOOT起来以后,跳转到APP地址0x10000, 程序运行正常。

  • 如果应用程序地址是0x40000, 在BOOT起来以后,跳转到APP地址0x40000, 程序运行正常。

  • 如果应用程序地址是0x40010, 在BOOT起来以后,跳转到APP地址0x40010, 程序将会复位。

    根据上述测试结果,实际上便宜地址是0xnnnn0400的整数倍就可以,ARM Information Center:http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0552a/Ciheijba.html

    当使用 VTOR, 新向量表的基地址必须与器件向量表大小的2次幂对齐。针对 KE1xF 器件,向量大小是 1 KB, 例如0x400,因此新的向量表的基地址必须是0x000, 0x400, 0x800, 或者 0xC00。

5. 针对HSRUN运行模式下的编程,需要注意的事项

a9a4864834fb2b9168f82cce853bb4df.png

    Flash的操作编程需要在运行模式下进行。

6. Flash编程的文档和参考例程

    关于Flash编程的文档可以参考如下链接:

https://www.nxp.com.cn/docs/en/application-note/AN4695.pdf

    需要注意以下关键点:

* All the flash protection registers are 0xFF

* The flash configuration is set to 0xFFFFFFFE

* Watchdog is disabled

* Interrupts are disabled

* The code is running in RAM

* fails whether or not debugger is attached

    Flash launch command 函数需要放在RAM里面执行,在操作Flash前先关闭中断。在官方的SDK目录下面都有完整的FLASH操作例程\driver_examples\flash\pflash\。

    在 SDK 驱动中函数的使用如下:

result = FLASH_Program(&s_flashDriver, destAdrss, (uint8_t *)s_buffer, sizeof(s_buffer));

    FLASH_Program函数定义如下::

status_t FLASH_Program(flash_config_t *config, uint32_t start, uint8_t *src, uint32_t lengthInBytes)

s_buffer的定义是 uint8.

    Flash操作的完整例程如下:

/*!

 * @brief Use Standard Software Drivers (SSD) to modify pflash.

 *

 * @details This function uses SSD to demonstrate flash mode:

 *            + Check pflash information.

 *            + Erase a sector and verify.

 *            + Program a sector and verify.

 */

int main(void)

{

    ftfx_security_state_t securityStatus = kFTFx_SecurityStateNotSecure; /* Return protection status */

    status_t result;    /* Return code from each flash driver function */

    uint32_t destAdrss; /* Address of the target location */

    uint32_t i, failAddr, failDat;

    uint32_t pflashBlockBase  = 0;

    uint32_t pflashTotalSize  = 0;

    uint32_t pflashSectorSize = 0;

    /* Init hardware */

    BOARD_InitPins();

    BOARD_BootClockRUN();

    BOARD_InitDebugConsole();

    /* Clean up Flash, Cache driver Structure*/

    memset(&s_flashDriver, 0, sizeof(flash_config_t));

    memset(&s_cacheDriver, 0, sizeof(ftfx_cache_config_t));

    /* Setup flash driver structure for device and initialize variables. */

    result = FLASH_Init(&s_flashDriver);

    if (kStatus_FTFx_Success != result)

    {

        error_trap();

    }

    /* Setup flash cache driver structure for device and initialize variables. */

    result = FTFx_CACHE_Init(&s_cacheDriver);

    if (kStatus_FTFx_Success != result)

    {

        error_trap();

    }

    /* Get flash properties*/

    FLASH_GetProperty(&s_flashDriver, kFLASH_PropertyPflash0BlockBaseAddr, &pflashBlockBase);

    FLASH_GetProperty(&s_flashDriver, kFLASH_PropertyPflash0TotalSize, &pflashTotalSize);

    FLASH_GetProperty(&s_flashDriver, kFLASH_PropertyPflash0SectorSize, &pflashSectorSize);

    /* print welcome message */

    PRINTF("\r\n PFlash Example Start \r\n");

    /* Print flash information - PFlash. */

    PRINTF("\r\n PFlash Information: ");

    PRINTF("\r\n Total Program Flash Size:\t%d KB, Hex: (0x%x)", (pflashTotalSize / 1024), pflashTotalSize);

    PRINTF("\r\n Program Flash Sector Size:\t%d KB, Hex: (0x%x) ", (pflashSectorSize / 1024), pflashSectorSize);

    /* Check security status. */

    result = FLASH_GetSecurityState(&s_flashDriver, &securityStatus);

    if (kStatus_FTFx_Success != result)

    {

        error_trap();

    }

    /* Print security status. */

    switch (securityStatus)

    {

        case kFTFx_SecurityStateNotSecure:

            PRINTF("\r\n Flash is UNSECURE!");

            break;

        case kFTFx_SecurityStateBackdoorEnabled:

            PRINTF("\r\n Flash is SECURE, BACKDOOR is ENABLED!");

            break;

        case kFTFx_SecurityStateBackdoorDisabled:

            PRINTF("\r\n Flash is SECURE, BACKDOOR is DISABLED!");

            break;

        default:

            break;

    }

    PRINTF("\r\n");

    /* Test pflash basic opeation only if flash is unsecure. */

    if (kFTFx_SecurityStateNotSecure == securityStatus)

    {

        /* Pre-preparation work about flash Cache/Prefetch/Speculation. */

        FTFx_CACHE_ClearCachePrefetchSpeculation(&s_cacheDriver, true);

        /* Debug message for user. */

        /* Erase several sectors on upper pflash block where there is no code */

        PRINTF("\r\n Erase a sector of flash");

/* In case of the protected sectors at the end of the pFlash just select

the block from the end of pFlash to be used for operations

SECTOR_INDEX_FROM_END = 1 means the last sector,

SECTOR_INDEX_FROM_END = 2 means (the last sector - 1) ...

in case of FSL_FEATURE_FLASH_HAS_PFLASH_BLOCK_SWAP it is

SECTOR_INDEX_FROM_END = 1 means the last 2 sectors with width of 2 sectors,

SECTOR_INDEX_FROM_END = 2 means the last 4 sectors back

with width of 2 sectors ...

*/

#ifndef SECTOR_INDEX_FROM_END

#define SECTOR_INDEX_FROM_END 1U

#endif

/* Erase a sector from destAdrss. */

#if defined(FSL_FEATURE_FLASH_HAS_PFLASH_BLOCK_SWAP) && FSL_FEATURE_FLASH_HAS_PFLASH_BLOCK_SWAP

        /* Note: we should make sure that the sector shouldn't be swap indicator sector*/

        destAdrss = pflashBlockBase + (pflashTotalSize - (SECTOR_INDEX_FROM_END * pflashSectorSize * 2));

#else

        destAdrss = pflashBlockBase + (pflashTotalSize - (SECTOR_INDEX_FROM_END * pflashSectorSize));

#endif

        result = FLASH_Erase(&s_flashDriver, destAdrss, pflashSectorSize, kFTFx_ApiEraseKey);

        if (kStatus_FTFx_Success != result)

        {

            error_trap();

        }

        /* Verify sector if it's been erased. */

        result = FLASH_VerifyErase(&s_flashDriver, destAdrss, pflashSectorSize, kFTFx_MarginValueUser);

        if (kStatus_FTFx_Success != result)

        {

            error_trap();

        }

        /* Print message for user. */

        PRINTF("\r\n Successfully Erased Sector 0x%x -> 0x%x\r\n", destAdrss, (destAdrss + pflashSectorSize));

        /* Print message for user. */

        PRINTF("\r\n Program a buffer to a sector of flash ");

        /* Prepare user buffer. */

        for (i = 0; i < BUFFER_LEN; i++)

        {

            s_buffer[i] = i;

        }

        /* Program user buffer into flash*/

        result = FLASH_Program(&s_flashDriver, destAdrss, (uint8_t *)s_buffer, sizeof(s_buffer));

        if (kStatus_FTFx_Success != result)

        {

            error_trap();

        }

        /* Verify programming by Program Check command with user margin levels */

        result = FLASH_VerifyProgram(&s_flashDriver, destAdrss, sizeof(s_buffer), (const uint8_t *)s_buffer,

                                     kFTFx_MarginValueUser, &failAddr, &failDat);

        if (kStatus_FTFx_Success != result)

        {

            error_trap();

        }

        /* Post-preparation work about flash Cache/Prefetch/Speculation. */

        FTFx_CACHE_ClearCachePrefetchSpeculation(&s_cacheDriver, false);

#if defined(FSL_FEATURE_HAS_L1CACHE) && FSL_FEATURE_HAS_L1CACHE

        L1CACHE_InvalidateCodeCache();

#endif /* FSL_FEATURE_HAS_L1CACHE */

#if defined(__DCACHE_PRESENT) && __DCACHE_PRESENT

        /* Clean the D-Cache before reading the flash data*/

        SCB_CleanInvalidateDCache();

#endif

        /* Verify programming by reading back from flash directly*/

        for (uint32_t i = 0; i < BUFFER_LEN; i++)

        {

            s_buffer_rbc[i] = *(volatile uint32_t *)(destAdrss + i * 4);

            if (s_buffer_rbc[i] != s_buffer[i])

            {

                error_trap();

            }

        }

        PRINTF("\r\n Successfully Programmed and Verified Location 0x%x -> 0x%x \r\n", destAdrss,

               (destAdrss + sizeof(s_buffer)));

        /* Erase the context we have progeammed before*/

        /* Note: we should make sure that the sector which will be set as swap indicator should be blank*/

        FLASH_Erase(&s_flashDriver, destAdrss, pflashSectorSize, kFTFx_ApiEraseKey);

    }

    else

    {

        PRINTF("\r\n Erase/Program opeation will not be executed, as Flash is SECURE!");

    }

    app_finalize();

    return 0;

}

7. Kinetis Bootloader

    Bootloader是一种面向用户应用程序的引导代码,可以在没有烧写器的情况下烧录用户程序,也可以用于在线更新程序。飞思卡尔提供了三种实现bootloader的方式,分别为:

  • 预烧写在ROM中的bootloader

    这种方式是MCU中内置了专用的ROM来存放bootloader,目前支持ROM型bootloader的Kinetis系列MCU包括KL03,KL17,KL27和KL43等。其中KL03和KL17的ROM bootloader包含SPI/UART/IIC三种接收方式,KL27和KL43还增加了USB的方式。

  • 预烧写在FLASH中一次性bootloader

    这种类型的bootloader在芯片出厂前预写在FLASH中,因此可以像ROM型bootloader一样直接使用。但与ROM型不同的是,上电后bootloader会从FLASH搬移到RAM 中运行,再将FLASH整片擦除并烧写用户程序,因此这种bootloader是一次性的。其优点是不需要片内ROM且方便量产烧写,缺点是无法支持以后的程序更新。目前支持预烧写在FLASH中一次性bootloader的Kinetis系列MCU包括K22、K24和KV3x等。

  • 开放源码的bootloader

    这种方式将FLASH空间分为两个部分,一部分用于存储bootloader代码;另一部分用于存储用户应用程序代码。这种方式的bootloader方便客户定制自己的代码。

    目前开放源代码的mcuboot,支持UART/SPI/IIC/USB HID 几种接口方式,可以用SDK的下载包中的 mcuboot目录下找到相应的程序。

    除了mcuboot,还有以下独立版本的bootloader,包括:

    1)AN2295(开发人员的串行引导加载程序)

        文档下载地址为:https://www.nxp.com/docs/zh/application-note/AN2295.pdf

    2)AN4767 (Kinetis E 系列上的UART Boot Loader 设计)

        文档下载地址为:https://www.nxp.com/docs/zh/application-note/AN4767.pdf

    3)AN4775 (Kinetis E 系列上的IIC Boot Loader设计)

        文档下载地址为:https://www.nxp.com/docs/zh/application-note/AN4775.pdf

    4)AN4368 (USB 大容量存储设备主机引导加载程序)

         文档下载地址为:https://www.nxp.com/docs/zh/application-note/AN4368.pdf

    5)AN4379  (Freescale USB大容量存储设备引导加载程序)

         文档下载地址为:https://www.nxp.com/docs/zh/application-note/AN4379.pdf

    6)AN4764   (USB Human Interface Device Boot Loader for ColdFire Plus, Kinetis K, and Kinetis L MCUs)

         文档下载地址为:https://www.nxp.com/docs/en/application-note/AN4764.pdf

    7)AN4370   (用于 MCU 的 USB DFU 引导加载程序)

        文档下载地址为:https://www.nxp.com/docs/zh/application-note/AN4370.pdf

    8)AN4367   (用于 MCU 的 以太网引导加载程序)

        文档下载地址为:https://www.nxp.com/docs/en/application-note/AN4367.pdf

        代码下载地址为:https://www.nxp.com/docs/en/application-note-software/AN4367SW.zip

        最新的代码请到FNET官网下载:http://fnet.sourceforge.net/

   9)Kinetis Bootloader to Update Multiple Devices in a Network - for Cortex-M0+

        代码及文档下载地址为:https://www.nxp.com/docs/en/application-note/AN5204.pdf

https://www.nxp.com/docs/en/application-note-software/AN5204SW.zip

开放源代码的boot也可以参考如下的链接:

https://github.com/Kinetis/AN2295_Bootloader/

https://github.com/yandld/nxp_easy_mcuboot

欢迎关注公众号:

5c94766b8dd26498b7b717a07c523b11.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值