1974|6

327

帖子

1

TA的资源

纯净的硅(初级)

楼主
 

PVD断电写入方式FLASH模拟EEPROM [复制链接]

我们在使用STM32时经常会遇到需要保存少量数据但又不想增加EEPROM芯片,这时一般都会使用芯片内部FLASH来充当这一功能。其实不管是芯片内部FLASH还是EEPROM芯片都存在擦写寿命的,只是FLASH的擦写寿命比专业的EEPROM更短。这对一个强迫癌晚期患者的打击是很大的,每每想起来都寝食难安。尤其是近期遇到一个应用场景不得不考虑一个新方法来治好我的焦虑。事情是这样婶儿滴:有一个累计计数32位变量,开机后实时记录用户的使用计数,数据变化最快17Hz。要求断电后数据不能丢失,直到用户操作清零后重新开始计数。

我的解决方案是这样筛儿滴:

  1. 使用内部flash的最后一个页来保存这4个字节,并且写入地址每次增加4字节直到写满整页后擦除一次从页起始写入。读取数据寻址时从页起始开始读每次增加4字节,直到读到0xffffffff前面4个字节即为目标数据或者读到页末尾为目标数据;
  2. 为了最大限度的减少写入次数,数据写入时机放到芯片掉电瞬间。需要打开PVD检测中断在PVD中断函数中完成数据写入。这里有一个问题:由于掉电时VCC电压已经几近枯竭,而擦除时间又比较长,此外擦除还会增加芯片的电流消耗,这让本就不多的VCC电压更加捉襟见肘,安全起见掉电时不能执行页擦除操作,页擦除应该放到上电瞬间。上电时先读取flash数据,如flash页数据已满则执行页擦除并将最后的数据写回页起始位置;
  3. 由于flash擦除后为0xffffffff,我们的寻址标记也是0xffffffff,计数值从零往上计数到0xffffffff后与寻址标记混淆,所以要将数据取反后存储。

以下是源代码:

#define USER_DATA1_ADDRESS        0x0801FC00

/*******************************************************************************
* Function Name  : Read32DataFromIntFlash
* Description    : Read 32bit data from internal flash
* Input          : None
* Output         : None
* Return         : uint32_t
* Author         : shipeng
*******************************************************************************/
uint32_t Read32DataFromIntFlash(void)
{
        uint32_t i,buf32;
        const uint32_t* flash_ptr = (const uint32_t*)USER_DATA1_ADDRESS;
        for (i = 0;i < 1024/4; i++)
        {
                buf32 = *flash_ptr++;
                if (i!=1024/4-1 && 0xFFFFFFFF==*flash_ptr)break;
        }
        if (i==1024/4 && 0xFFFFFFFF!=buf32)
        {
                FLASH_Unlock();
                FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
                FLASH_ErasePage(USER_DATA1_ADDRESS);
                FLASH_ProgramHalfWord(USER_DATA1_ADDRESS,(uint16_t)(buf32&65535));
                FLASH_ProgramHalfWord(USER_DATA1_ADDRESS+2,(uint16_t)(buf32>>16));
                FLASH_Lock();
                SystemParams.flash_null_addr = USER_DATA1_ADDRESS+4;
        }
        else if (0xFFFFFFFF==buf32)SystemParams.flash_null_addr = (uint32_t)flash_ptr-4;
        else SystemParams.flash_null_addr = (uint32_t)flash_ptr;
        return (~buf32);
}

/*******************************************************************************
* Function Name  : Write32DataToIntFlash
* Description    : Write 32bit data to internal flash
* Input          : uint32_t
* Output         : None
* Return         : void
* Author         : shipeng
*******************************************************************************/
void Write32DataToIntFlash(uint32_t dat32)
{
        uint32_t buf32 = ~dat32,flash_addr = SystemParams.flash_null_addr;
        if (*((uint32_t*)flash_addr-1)==buf32)return;
        FLASH_Unlock();
        FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR);
        FLASH_ProgramHalfWord(flash_addr,(uint16_t)(buf32&65535));
        FLASH_ProgramHalfWord(flash_addr+2,(uint16_t)(buf32>>16));
        FLASH_Lock();
}

PVD检测初始化和PVD中断处理函数:


/*******************************************************************************
* Function Name  : PVD_EXTI_Init
* Description    : Configures PVD INTERRUPT.
* Input          : None
* Output         : None
* Return         : None
* Author         : Shipeng
*******************************************************************************/
void PVD_EXTI_Init(void)
{
    EXTI_InitTypeDef EXTI_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
        /*Power Voltage Down NVIC Configuration*/
        NVIC_InitStructure.NVIC_IRQChannel = PVD_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStructure);
    
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);

    EXTI_DeInit();
        EXTI_StructInit(&EXTI_InitStructure);
        EXTI_InitStructure.EXTI_Line = EXTI_Line16;
        EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
        EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
        EXTI_InitStructure.EXTI_LineCmd = ENABLE;
        EXTI_Init(&EXTI_InitStructure);

        PWR_PVDLevelConfig(PWR_PVDLevel_2V9);
        PWR_PVDCmd(ENABLE);
}

void PVD_IRQHandler(void)
{
    if(EXTI_GetITStatus(EXTI_Line16) != RESET)
    {
        EXTI_ClearITPendingBit(EXTI_Line16);
        if (RESET!=PWR_GetFlagStatus(PWR_FLAG_PVDO))//if (PWR->CSR&PWR_CSR_PVDO)
        {
            PWR->CR &= 0x7F;
            RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC,DISABLE);
            Write32DataToIntFlash(SystemParams.total_counter);
            __set_FAULTMASK(1);NVIC_SystemReset();
        }
        else PWR->CR |= 0x80;
    }
}

 

此帖出自stm32/stm8论坛

最新回复

楼主放心应用吧! 我在我的项目里面已经验证通过了。 实际应用效果相当完美   详情 回复 发表于 2023-7-21 10:12
点赞 关注
个人签名模电临时工
 

回复
举报

1144

帖子

17

TA的资源

纯净的硅(高级)

沙发
 

EEPROM便宜的几毛钱吧?

此帖出自stm32/stm8论坛
 
 

回复

1704

帖子

0

TA的资源

五彩晶圆(初级)

板凳
 

要求断电后数据不能丢失,直到用户操作清零后重新开始计数,楼主的解决方案是刚刚的,不错

此帖出自stm32/stm8论坛

点评

谢谢支持  详情 回复 发表于 2023-7-20 14:14
 
 

回复

327

帖子

1

TA的资源

纯净的硅(初级)

4
 
火辣西米秀 发表于 2023-7-19 22:43 要求断电后数据不能丢失,直到用户操作清零后重新开始计数,楼主的解决方案是刚刚的,不错

谢谢支持

此帖出自stm32/stm8论坛
 
个人签名模电临时工
 
 

回复

29

帖子

0

TA的资源

一粒金砂(中级)

5
 

st不是有个文档讲怎么把flash做成eeproom嘛

此帖出自stm32/stm8论坛
 
 
 

回复

672

帖子

0

TA的资源

纯净的硅(高级)

6
 

楼主放心应用吧!

我在我的项目里面已经验证通过了。

实际应用效果相当完美

此帖出自stm32/stm8论坛

点评

其实我也已经用到项目里面了,谢谢你的回馈,能够帮到大家我很欣慰  详情 回复 发表于 2023-7-21 18:40
 
 
 

回复

327

帖子

1

TA的资源

纯净的硅(初级)

7
 
jobszheng5 发表于 2023-7-21 10:12 楼主放心应用吧! 我在我的项目里面已经验证通过了。 实际应用效果相当完美

其实我也已经用到项目里面了,谢谢你的回馈,能够帮到大家我很欣慰

此帖出自stm32/stm8论坛
 
个人签名模电临时工
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/8 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表