在大部分的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寄存器定义如下:
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 (数据)。
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运行模式下的编程,需要注意的事项
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
欢迎关注公众号: