NXP芯片Flash模拟E方

本文讲述了在项目开发中,通过发现并利用NXPK1芯片的FlexNVM和FlexRAM资源,避免了对E方芯片的过度依赖,优化了数据存储方案。介绍了如何配置和初始化这些内存区域,以及如何通过模拟E方实现更高效的数据记忆。
摘要由CSDN通过智能技术生成

项目背景

在开发项目的整个过程中,产品同学经常给开发团队传递一类信息:这个数据由MCU来进行掉电记忆。

甲方的开发团队觉得数据记忆就是要写在E方里面的,并且在以往的项目里面已经加了个E方的芯片,用IIC来通讯,E方多大就只能记忆多少数据,于是很多数据都不能由MCU记忆下来。

产品得到这个消息的时候总是很疑惑,为什么这么多数据都不能由MCU记忆下来,沟通几次之后大家都决定由SOC来记忆,导致有些只能由MCU记忆的功能只能被迫砍掉。

突然有天我在查BootLoader代码的时候,发现用到了Flash管理的代码里面有个模拟E方的东东,看了看,里面居然有4K的空间!还有个更大的64K的FlexNVM区域!人家都提醒你这么多次你能记忆得下来了,结果一直浪费这么好的资源不用,真的太可惜了。

PE配置

打开之后有个FlexRam的地方,设定个基地址,我这里使用默认的0x14000000。

原理

规格书里面有写,NXP的K1芯片都有下面这么几块内存区域。

我这个芯片是146的,所以有1M的PFlash/64K的FlexNVM和4K的FlexRAM。

内存映射图

最上面的1M空间PFlash是不会动的,把4K的FlexRAM变为EERAM。

能分区的只有4K的FlexRAM有三种配置方式,分一部分给Dflash,一部分给EFlash。

这个不是本文的重点,我们要用的是4K的FlexRAM,知道大概怎么分就行。

生成代码

生成的代码在Generated_Code文件夹下的Flash.c,里面规定了:

1、Pflash的起始地址为0、因为默认就那1M大小,所以范围就是0-0x0100 0000

2、Dflash的起始地址为0x10000000,然后就看配置多大了,64K里面没有配置到的都是EFlash。

3、EERAM也就是模拟E方的起始地址为0x14000000,大小为4K已经定好了。

/*! @brief Configuration structure flashCfg_0 */
const flash_user_config_t Flash_InitConfig0 = {
    .PFlashBase  = 0x00000000U,                     /* Base address of Program Flash block */
    .PFlashSize  = 0x00100000U,                     /* Size of Program Flash block         */
    .DFlashBase  = 0x10000000U,                     /* Base address of Data Flash block    */
    .EERAMBase   = 0x14000000U,                     /* Base address of FlexRAM block */
    /* If using callback, any code reachable from this function must not be placed in a Flash block targeted for a program/erase operation.*/
    .CallBack    = NULL_CALLBACK
};

初始化代码

大致过程如下,基本都是标准的。

uint8_t FlashEmulateEE_Init(void)
{   
	uint8_t ret = STATUS_SUCCESS;

    ret = POWER_SYS_SetMode(PWR_RUN, POWER_MANAGER_POLICY_AGREEMENT);//电源管理相关

	ret = FLASH_DRV_Init(&Flash_InitConfig0, &flashSSDConfig);//初始化所有内存区域

    if (flashSSDConfig.EEESize == 0u)//没有分出E方的区域
    {
        ret = FLASH_DRV_DEFlashPartition(&flashSSDConfig, 0x02u, 0x08u, 0x03u, false, true);//分出E方区域
        DEV_ASSERT(STATUS_SUCCESS == ret);
    }

    ret = FLASH_DRV_SetFlexRamFunction(&flashSSDConfig, EEE_ENABLE, 0x00u, NULL);//允许使用模拟E方功能
    DEV_ASSERT(STATUS_SUCCESS == ret);
    
    ret = POWER_SYS_SetMode(PWR_HSRUN, POWER_MANAGER_POLICY_AGREEMENT);//电源管理相关
	return ret;
}

下面我们详细分析每个接口

FLASH_DRV_Init

初始化所有Flash内存区域,FEATURE_FLS_HAS_FLEX_NVM的宏定义要开起来,除了完成初始化以外,把使用初始化结构体Flash_InitConfig0执行完的信息放到flashSSDConfig结构体里面。

status_t FLASH_DRV_Init(const flash_user_config_t * const pUserConf,
                        flash_ssd_config_t * const pSSDConfig)
{
    DEV_ASSERT(pUserConf != NULL);
    DEV_ASSERT(pSSDConfig != NULL);
    status_t ret = STATUS_SUCCESS;
#if FEATURE_FLS_HAS_FLEX_NVM
    uint8_t DEPartitionCode;    /* store D/E-Flash Partition Code */
#endif

    pSSDConfig->PFlashBase = pUserConf->PFlashBase;
    pSSDConfig->PFlashSize = pUserConf->PFlashSize;
    pSSDConfig->DFlashBase = pUserConf->DFlashBase;
    pSSDConfig->EERAMBase = pUserConf->EERAMBase;
    pSSDConfig->CallBack = pUserConf->CallBack;

#if FEATURE_FLS_HAS_FLEX_NVM
    /* Temporary solution for FTFC and S32K144 CSEc part */
    /* Get DEPART from Flash Configuration Register 1 */
    DEPartitionCode = (uint8_t)((SIM->FCFG1 & SIM_FCFG1_DEPART_MASK) >> SIM_FCFG1_DEPART_SHIFT);
    /* Get data flash size */
    FLASH_DRV_GetDEPartitionCode(pSSDConfig, DEPartitionCode);
    if (pSSDConfig->DFlashSize < FEATURE_FLS_DF_BLOCK_SIZE)
    {
        pSSDConfig->EEESize = FEATURE_FLS_FLEX_RAM_SIZE;
    }
    else
    {
        pSSDConfig->EEESize = 0U;
    }
#else /* FEATURE_FLS_HAS_FLEX_NVM == 0 */
    /* If size of D/E-Flash = 0 */
    pSSDConfig->DFlashSize = 0U;
    pSSDConfig->EEESize = 0U;
#endif /* End of FEATURE_FLS_HAS_FLEX_NVM */

    return ret;
}

flashSSDConfig结构体不仅有初始化结构体Flash_InitConfig0原来的信息,还多了个EEESize表示划分出来的模拟E方范围和DFlashSize表示DFlash范围大小。

typedef struct
{
    uint32_t PFlashBase;          /*!< The base address of P-Flash memory */
    uint32_t PFlashSize;          /*!< The size in byte of P-Flash memory */
    uint32_t DFlashBase;          /*!< For FlexNVM device, this is the base address of D-Flash memory (FlexNVM memory);
                                   *    For non-FlexNVM device, this field is unused */
    uint32_t DFlashSize;          /*!< For FlexNVM device, this is the size in byte of area
                                   *    which is used as D-Flash from FlexNVM memory;
                                   *    For non-FlexNVM device, this field is unused */
    uint32_t EERAMBase;           /*!< The base address of FlexRAM (for FlexNVM device)
                                   *    or acceleration RAM memory (for non-FlexNVM device) */
    uint32_t EEESize;             /*!< For FlexNVM device, this is the size in byte of EEPROM area which was partitioned
                                   *    from FlexRAM; For non-FlexNVM device, this field is unused */
    flash_callback_t CallBack;    /*!< Call back function to service the time critical events. Any code reachable from this function
                                   *   must not be placed in a Flash block targeted for a program/erase operation */
} flash_ssd_config_t;

FLASH_DRV_DEFlashPartition

重新分区,分区完成之后也是把分区之后的结果给到flashSSDConfig结构体。

status_t FLASH_DRV_DEFlashPartition(const flash_ssd_config_t * pSSDConfig,
                                    uint8_t uEEEDataSizeCode,
                                    uint8_t uDEPartitionCode,
                                    uint8_t uCSEcKeySize,
                                    bool uSFE,
                                    bool flexRamEnableLoadEEEData)
{
    DEV_ASSERT(pSSDConfig != NULL);
    DEV_ASSERT(uCSEcKeySize <= CSE_KEY_SIZE_CODE_MAX);
    status_t ret;    /* Return code variable */

    /* Check CCIF to verify the previous command is completed */
    if (0U == (FTFx_FSTAT & FTFx_FSTAT_CCIF_MASK))
    {
        ret = STATUS_BUSY;
    }
    else
    {
        /* Clear RDCOLERR & ACCERR & FPVIOL & MGSTAT0 flag in flash status register. Write 1 to clear */
        CLEAR_FTFx_FSTAT_ERROR_BITS;

        /* Passing parameter to the command */
        FTFx_FCCOB0 = FTFx_PROGRAM_PARTITION;
        FTFx_FCCOB1 = uCSEcKeySize;
        FTFx_FCCOB2 = (uint8_t)(uSFE ? 1U : 0U);
        FTFx_FCCOB3 = (uint8_t)(flexRamEnableLoadEEEData ? 0U : 1U);
        FTFx_FCCOB4 = uEEEDataSizeCode;
        FTFx_FCCOB5 = uDEPartitionCode;

        /* Calling flash command sequence function to execute the command */
        ret = FLASH_DRV_CommandSequence(pSSDConfig);
    }

    return ret;
}

第二个入参uEEEDataSizeCode是放到FCCOB4寄存器来划分模拟E方大小的,0x0F就是不用模拟E方,2就是4K全部用来做模拟E方。

第三个入参uDEPartitionCode放到FCCOB5里面,有下面几种分法,前提是你的芯片支持你这么分,我们这里用了8,也就是全部分成EFlash。

FLASH_DRV_SetFlexRamFunction

它主要就是看第二个入参flexRamFuncCode,只要写成EEE_ENABLE就能放入FCCOB1寄存器使能模拟E方,后面就都能够用了。

status_t FLASH_DRV_SetFlexRamFunction(const flash_ssd_config_t * pSSDConfig,
                                      flash_flexRam_function_control_code_t flexRamFuncCode,
                                      uint16_t byteOfQuickWrite,
                                      flash_eeprom_status_t * const pEEPROMStatus)
{
    DEV_ASSERT(pSSDConfig != NULL);
    status_t ret;    /* Return code variable */

    /* Check CCIF to verify the previous command is completed */
    if (0U == (FTFx_FSTAT & FTFx_FSTAT_CCIF_MASK))
    {
        ret = STATUS_BUSY;
    }
    else
    {
        /* Clear RDCOLERR & ACCERR & FPVIOL flag in flash status register. Write 1 to clear */
        CLEAR_FTFx_FSTAT_ERROR_BITS;

        /* Passing parameter to the command */
        FTFx_FCCOB0 = FTFx_SET_EERAM;
        FTFx_FCCOB1 = (uint8_t)flexRamFuncCode;

        if (flexRamFuncCode == EEE_QUICK_WRITE)
        {
            FTFx_FCCOB4 = (uint8_t)(byteOfQuickWrite >> 0x8U);
            FTFx_FCCOB5 = (uint8_t)(byteOfQuickWrite & 0xFFU);
        }

        /* Calling flash command sequence function to execute the command */
        ret = FLASH_DRV_CommandSequence(pSSDConfig);

        if ((flexRamFuncCode == EEE_STATUS_QUERY) && (ret == STATUS_SUCCESS))
        {
            if (pEEPROMStatus == NULL)
            {
                ret = STATUS_ERROR;
            }
            else
            {
                pEEPROMStatus->brownOutCode = FTFx_FCCOB5;
                pEEPROMStatus->sectorEraseCount = (uint16_t)((uint16_t)FTFx_FCCOB8 << 8U);
                pEEPROMStatus->sectorEraseCount |= (uint16_t)FTFx_FCCOB9;
                pEEPROMStatus->numOfRecordReqMaintain = (uint16_t)((uint16_t)FTFx_FCCOB6 << 8U);
                pEEPROMStatus->numOfRecordReqMaintain |= (uint16_t)FTFx_FCCOB7;
            }
        }
    }

    return ret;
}

写入E方

status_t FlexRAM_Emulated_EEPROM_Write(uint32_t WriteOffsetAddress,uint32_t WriteDataLength,uint8_t * WriteData){
	status_t ret = STATUS_SUCCESS;
    uint32_t WriteAddress =  flashSSDConfig.EERAMBase + WriteOffsetAddress; 
    
    if (flashSSDConfig.EEESize != 0u)//E方有空间
    {
        ret = POWER_SYS_SetMode(PWR_RUN, POWER_MANAGER_POLICY_AGREEMENT);//通过电源管理将系统频率切换为正常频率,这样才能操作flash
        DEV_ASSERT(STATUS_SUCCESS == ret);

        ret = FLASH_DRV_EEEWrite(&flashSSDConfig,WriteAddress,WriteDataLength,WriteData);///写入E方
        DEV_ASSERT(STATUS_SUCCESS == ret);

        ret = POWER_SYS_SetMode(PWR_HSRUN, POWER_MANAGER_POLICY_AGREEMENT);//把频率切换回高速频率
        DEV_ASSERT(STATUS_SUCCESS == ret);

        for(uint8_t i = 0;i < WriteDataLength;i++){//看看写入是否成功
            if ( (*WriteData) != (*(uint8_t *)WriteAddress) & 0xff)
			{
				ret = STATUS_ERROR;
			}
            WriteData++;
            WriteAddress++;
        }
    }
    else
    {
        ret = STATUS_ERROR;
    }
    return ret;
}

读出E方

读取的话就没有系统频率的问题

status_t FlexRAM_Emulated_EEPROM_Read(uint32_t ReadOffsetAddress,uint32_t ReadDataLength,uint8_t * ReadData){
	status_t ret = STATUS_SUCCESS;
    uint32_t ReadAddress =  flashSSDConfig.EERAMBase + ReadOffsetAddress; 
     
    if (flashSSDConfig.EEESize != 0u)//E方有空间
    {
        memcpy(ReadData,ReadAddress,ReadDataLength);
    }
    else
    {
        ret = STATUS_ERROR;
    }
    return ret;
}

 测试代码

uint32_t OffsetAddress = 0x0100;//在E方区域里面的地址偏移,注意不要写到E方外面
uint32_t DataLength = 0x8;//数据长度
uint8_t WriteData[8] = {0x00,0x55,0xAA,0xBB,0xCC,0xDD,0xEE,0xFF};//要写入的数据
uint8_t ReadData[8] = {0x00};             
FlexRAM_Emulated_EEPROM_Write(OffsetAddress,DataLength,WriteData);
CPU_DealyMs(5);
FlexRAM_Emulated_EEPROM_Read(OffsetAddress,DataLength,ReadData);

重启之后内存的数据正确

  • 28
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不吃鱼的羊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值