S32K148中的FEE实现方式有一定的硬件机制存在,因此在实现上跟之前接触到的FEE多少有一些不同。在此之前接触到的FEE,比如说MPC5644上的FEE,基本上是Flash驱动加纯软件策略来实现的。而S32K148上面则不同,在这个MCU上有专门的硬件实现机制,虽然也用到了Flash但是Flash是表现上其实看上去更像是真实的EEPROM,用户只需要直接写入或者读取RAM即可。
具体的原理分析在《001_Flash例程分析》、《002_S32K148 Flash相关文档解读》、《003_FlexRAM与EEPROM大小关系》这三份笔记中做了详细的整理。
接下来,看一下我自己在软件设计应用中的一部分尝试。
void fee_lld_init(void)
{
status_t ret; /* Store the driver APIs return code */
POWER_SYS_SetMode(RUN, POWER_MANAGER_POLICY_AGREEMENT);
/* Install interrupt for Flash Command Complete event */
INT_SYS_InstallHandler(FTFC_IRQn, CCIF_Handler, (isr_t *)0);
INT_SYS_EnableIRQ(FTFC_IRQn);
/* Always initialize the driver before calling other functions */
ret = FLASH_DRV_Init(&Flash1_InitConfig0, &flashSSDConfig);
DEV_ASSERT(STATUS_SUCCESS == ret);
if (flashSSDConfig.EEESize == 0u)
{
/* Configure FlexRAM as EEPROM and FlexNVM as EEPROM backup region,
* DEFlashPartition will be failed if the IFR region isn't blank.
* Refer to the device document for valid EEPROM Data Size Code
* and FlexNVM Partition Code. For example on S32K148:
* - EEEDataSizeCode = 0x02u: EEPROM size = 4 Kbytes
* - DEPartitionCode = 0x04u: EEPROM backup size = 64 Kbytes */
ret = FLASH_DRV_DEFlashPartition(&flashSSDConfig, 0x02u, 0x04u, 0x0u, false, true);
DEV_ASSERT(STATUS_SUCCESS == ret);
/* Re-initialize the driver to update the new EEPROM configuration */
ret = FLASH_DRV_Init(&Flash1_InitConfig0, &flashSSDConfig);
DEV_ASSERT(STATUS_SUCCESS == ret);
/* Make FlexRAM available for EEPROM */
ret = FLASH_DRV_SetFlexRamFunction(&flashSSDConfig, EEE_ENABLE, 0x00u, NULL);
DEV_ASSERT(STATUS_SUCCESS == ret);
}
else /* FLexRAM is already configured as EEPROM */
{
ret = FLASH_DRV_DEFlashPartition(&flashSSDConfig, 0x02u, 0x04u, 0x0u, false, true);
DEV_ASSERT(STATUS_SUCCESS == ret);
/* Make FlexRAM available for EEPROM, make sure that FlexNVM and FlexRAM
* are already partitioned successfully before */
ret = FLASH_DRV_SetFlexRamFunction(&flashSSDConfig, EEE_ENABLE, 0x00u, NULL);
DEV_ASSERT(STATUS_SUCCESS == ret);
}
/* Set callback function before a long time consuming flash operation
* (ex: erasing) to let the application code do other tasks while flash
* in operation. In this case we use it to enable interrupt for
* Flash Command Complete event */
pCallBack = (flash_callback_t)CCIF_Callback;
flashSSDConfig.CallBack = pCallBack;
/* Disable Callback */
flashSSDConfig.CallBack = NULL_CALLBACK;
POWER_SYS_SetMode(HSRUN, POWER_MANAGER_POLICY_AGREEMENT);
(void)ret;
}
初始化函数的设计其实就是例程中的框架,没有什么太多的变化。这里有一个时钟的修改,至于为什么改后面再说。
void fee_lld_write(uint32_t reletive_addr, uint32_t size, uint8_t *p_data)
{
uint32_t address;
status_t ret; /* Store the driver APIs return code */
if (flashSSDConfig.EEESize != 0u)
{
POWER_SYS_SetMode(RUN, POWER_MANAGER_POLICY_AGREEMENT);
address = flashSSDConfig.EERAMBase + reletive_addr;
ret = FLASH_DRV_EEEWrite(&flashSSDConfig, address, size, p_data);
DEV_ASSERT(STATUS_SUCCESS == ret);
POWER_SYS_SetMode(HSRUN, POWER_MANAGER_POLICY_AGREEMENT);
}
else
{
/* no code */
}
(void)ret;
}
void fee_lld_read(uint32_t reletive_addr, uint32_t size, uint8_t *p_data)
{
uint32_t address;
if (flashSSDConfig.EEESize != 0u)
{
POWER_SYS_SetMode(RUN, POWER_MANAGER_POLICY_AGREEMENT);
address = flashSSDConfig.EERAMBase + reletive_addr;
memcpy(p_data, (uint8_t *)address, size);
POWER_SYS_SetMode(HSRUN, POWER_MANAGER_POLICY_AGREEMENT);
}
else
{
/* no code */
}
}
写入以及读取稍微做了一下处理,因为这个机制的运行其实有一定的先决条件。具体的文档信息前面整理过,这里把这个相关点再拿出来看一下。
在这两种模式下,Flash驱动是不能够操作的。而这个FEE既然用到了flash做记录的保存,其实还是脱离不了flash驱动的使用。很多产品或者系统设计的时候很重要的一个目标其实是性能,因此会直接在HSRUN模式下运行。这样就会导致Flash写入的错误,其实读取倒是没有特别的说明。那么为什么我这里多个地方也加了相应的模式切换呢?这就需要回答前面初始化的时候埋下的问题了:为什么初始化的时候改了时钟?答案很简单,我在调试的时候发现其实HSRUN模式下初始化然后进行EEPROM的操作也会失败!那么,安全起见,自然全都离开这种模式会好一些。
static void fee_lld_fill_eeprom_with_same_bytes(uint8_t byte_data)
{
uint8_t data[512];
memset(data, byte_data, 512);
fee_lld_write(512 * 0, 512, data);
fee_lld_write(512 * 1, 512, data);
fee_lld_write(512 * 2, 512, data);
fee_lld_write(512 * 3, 512, data);
}
void fee_lld_clear_eeprom(void)
{
fee_lld_fill_eeprom_with_same_bytes(0x00U);
}
void fee_lld_format_eeprom(void)
{
fee_lld_fill_eeprom_with_same_bytes(0xFFU);
}
清除以及格式化分了两个函数,而两个函数又调用了另一个共用的静态函数。如果在以前,或许我就是两个函数复制一份就结束了,但是SICP看了一个开篇之后,对这方面开始越来越在意了。计算机的工作的确是应该当成魔法、艺术来对待,不能够做成搬砖类的工作。
整个调试比较简单,测试的信息不再放了。