单片机-Flash动态自保存

         说明:该方法为固定大小的数据包方式进行记录,写满一页后再擦除设定页从新记录,增加Flash使用寿命。

        环境需求:Flash需要 可程序读写。

        以STM32,大容量为例(HAL库方式)。

        注意事项:避开程序空间,注意页的大小有的为1K,有的为2K按需分配。

         简单说明:       

                以下表为例,主存储块 的 页255,0x0807 F800为地址的开始,0x0807 FFFF为地址的结束,大小为2K,2K的地址为0x800(0x0808 0000-0x0807 F800 = 0x800)区间。 

程序流程图:

 flash.h 文件内容:

#ifndef __FLASH_H
#define __FLASH_H

#include "main.h"

//flash存储地址
#define StartFlashAddress    ((u32)0x0801f000)  //读写起始地址

#define FLASH_DATA_GROUP_SIZE  28    //所有需要FLASH的数据字节大小,尽量凑双,
//地址结构体
typedef struct{
    uint32_t Current_Write_Address;
    uint32_t AddressTail_Sta;
}Flash_Def;

//Flash记录集,用户自己定义,这是我需要Flash的变量集
typedef struct{
    uint16_t Set_Place_Point;
    uint16_t Set_function;
    uint16_t Set_Distance_Left; 
    uint16_t Set_Distance_Right;
    uint16_t Set_BackLimit;
    uint16_t Set_TurnLimit;
    
    uint32_t WorkdayAlarmStartPoint;
    uint32_t WorkdayAlarmStopPoint;
    uint32_t DayOffAlarmStartPoint;
    uint32_t DayOffAlarmStopPoint;
}Flash_Data;
//初始化 执行函数
void First_Launches_Flash_Process(void);
//大循环执行函数
uint16_t Auto_Write_Flash(void);
#endif /* __FLASH_H */

 flash.c 文件内容:

#include "flash.h"
//用于传递所需的全局变量,用户自行更改
#include "main_combine.h"
#include "bsp_rtc.h"
//地址头
#define MEMORY_FLASH_START_ADDRESS 0x0803F800
//获取未地址 FLASH_PAGE_SIZE 库文件带有,如果没有就按照文章开头的计算公式计算
//FLASH_PAGE_SIZE / FLASH_DATA_GROUP_SIZE * FLASH_DATA_GROUP_SIZE取整运算计算,不能消消乐
#define MEMORY_FLASH_STOP_ADDRESS 0x0803F800 + FLASH_PAGE_SIZE / FLASH_DATA_GROUP_SIZE * FLASH_DATA_GROUP_SIZE

//Flash用户地址变量
Flash_Def Flash_SoftWare_REG;
//擦除出错地址记录
uint32_t ErasePageError_Addr;
//数据结构体,和用户数据一样大小,侦测变量用
Flash_Data OldFlashData;

/**
  * @功能		    上电初始化数据记录。刷新所有事件
  * @输入变量	    
  * @输出变量	    
  * @参数			
  */
void OldFlashData_Init(void){
//我个人需要Flash的数据,用户按照自己的添加
    OldFlashData.Set_Place_Point = Set_Place_Point;
    OldFlashData.Set_function = Set_function;
    OldFlashData.Set_Distance_Left = Set_Distance_Left;
    OldFlashData.Set_Distance_Right = Set_Distance_Right;
    OldFlashData.Set_BackLimit = Set_BackLimit;
    OldFlashData.Set_TurnLimit = Set_TurnLimit;
    
    OldFlashData.WorkdayAlarmStartPoint = WorkdayAlarm.StartTimePoint;
    OldFlashData.WorkdayAlarmStopPoint = WorkdayAlarm.StopTimePoint;
    OldFlashData.DayOffAlarmStartPoint = DayOffAlarm.StartTimePoint;
    OldFlashData.DayOffAlarmStopPoint = DayOffAlarm.StopTimePoint;
}

/**
  * @功能		    内存无记录时(第一次启动Flash为空),默认数据方式。
  * @输入变量	    
  * @输出变量	    
  * @参数			
  */
void FlashData_Normal(void){
//我个人需要Flash的数据,用户按照自己的添加
    Set_Place_Point = 0;                        //清标定位置,位置1-left 2-right
    Set_function = 1;                           //默认开启蜂鸣器报警但不开启温度限制与迎宾
    Fan_Seat &= 0xfd;                           //清风扇
    WorkdayAlarm.StartTimePoint = 21600;        //工作闹钟起始点
    WorkdayAlarm.StopTimePoint = 68400;         //工作闹钟结束点
    DayOffAlarm.StartTimePoint = 21600;         //非工作闹钟起始点
    DayOffAlarm.StopTimePoint = 68400;          //非工作闹钟结束点
    Set_Distance_Left = 4000;                   //激光距离
    Set_Distance_Right = 4000;                  //激光距离
    Set_BackLimit = 1800;                       //靠背限流
    Set_TurnLimit= 2100;                        //旋转限流
}

/**
  * @功能		    扇区内首个无数据的地址.
  * @输入变量	    头地址
  * @输出变量	    返回数据地址位置
  * @参数			
  */
static uint32_t check_flash_data_addr(uint32_t chip_addr){
    uint8_t i;
    uint16_t offset_addr;
    uint16_t flash_data_group_counter = FLASH_PAGE_SIZE / FLASH_DATA_GROUP_SIZE;
    for(i = 0;i <= flash_data_group_counter;i++) {
        offset_addr = *((volatile uint16_t*)(chip_addr + i * FLASH_DATA_GROUP_SIZE));
        if(offset_addr == 0xffff){
             break;  
        }
    }
    return chip_addr + i * FLASH_DATA_GROUP_SIZE;
}

/**
  * @功能		    擦除扇区数据.
  * @输入变量	    扇区地址,随机数据
  * @输出变量	    
  * @参数			
  */
static void memory_erase(uint32_t sector_addr) {
    FLASH_EraseInitTypeDef pEraseInit;
    HAL_FLASH_Unlock();
    pEraseInit.NbPages = 1;
    pEraseInit.PageAddress = sector_addr;
    pEraseInit.TypeErase = FLASH_TYPEERASE_PAGES;
    HAL_FLASHEx_Erase(&pEraseInit, &ErasePageError_Addr);
    HAL_FLASH_Lock();
}

/**
  * @功能		    读取内存数据,读Flash数据。
  * @输入变量	    数据地址位置
  * @输出变量	    
  * @参数			
  */
void FlasReadData(uint32_t data_addr){
//我个人需要Flash的数据,用户按照自己的添加
    Set_Place_Point = *((volatile uint16_t*)(data_addr));
    Set_function = *((volatile uint16_t*)(data_addr + 2));
    Set_Distance_Left = *((volatile uint16_t*)(data_addr + 4));
    Set_Distance_Right = *((volatile uint16_t*)(data_addr + 6));
    Set_BackLimit = *((volatile uint16_t*)(data_addr + 8));
    Set_TurnLimit = *((volatile uint16_t*)(data_addr + 10));
    
    WorkdayAlarm.StartTimePoint = *((volatile uint32_t*)(data_addr + 12));
    WorkdayAlarm.StopTimePoint = *((volatile uint32_t*)(data_addr + 16));
    DayOffAlarm.StartTimePoint = *((volatile uint32_t*)(data_addr + 20));
    DayOffAlarm.StopTimePoint = *((volatile uint32_t*)(data_addr + 24));
//读取Flash数据出错 对变量数据进行限制,下面用户根据自己的变量及环境进行配置
    if((Set_Place_Point&0x03) > 2){
        Set_Place_Point = 0;                             //数据错误,清标定位置,位置1-left 2-right
    }
    if(Set_function == 0xff){
        Set_function = 1;                                       //默认开启蜂鸣器报警但不开启温度限制与迎宾
    }
    
    Set_function &= 0xfd;                                                           //清风扇温度控制位
    
    if((Set_function&0x08)==0){
        Set_Place_Point &= 0x0;                                //如果没有标定,清位置信息
    }
    Fan_Seat &= 0xfd;                                                               //清风扇

    if(WorkdayAlarm.StartTimePoint >= 86400 || WorkdayAlarm.StopTimePoint >= 86400 || (WorkdayAlarm.StartTimePoint == WorkdayAlarm.StopTimePoint)){
       WorkdayAlarm.StartTimePoint = 21600;
       WorkdayAlarm.StopTimePoint = 68400;
    }
    if(DayOffAlarm.StartTimePoint >= 86400 || DayOffAlarm.StopTimePoint >= 86400 || (DayOffAlarm.StartTimePoint == DayOffAlarm.StopTimePoint)){
       DayOffAlarm.StartTimePoint = 21600;
       DayOffAlarm.StopTimePoint = 68400;
    }
    if((Set_Distance_Left > 4000)||(Set_Distance_Left < 50))  {
        Set_function &= 0xf1;                                                       //清标定与自动
        Set_Distance_Left = 4000;
    }
    if((Set_Distance_Right > 4000)||(Set_Distance_Right < 50)) {
        Set_function &= 0xf1;                                                       //清标定与自动
        Set_Distance_Right = 4000;
    }
    if((Set_BackLimit<500)||(Set_BackLimit>3000)){
        Set_BackLimit=1800;                //超限赋默认倿
    }
    if((Set_TurnLimit<800)||(Set_TurnLimit>4000)){
        Set_TurnLimit= 2200;              //超限赋默认倿
    }
}

/**
  * @功能		    写数据。
  * @输入变量	    数据地址位置
  * @输出变量	    
  * @参数			
  */
void FlashWriteData(uint32_t data_addr){
    HAL_FLASH_Unlock();
    HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, data_addr    , OldFlashData.Set_Place_Point);
    HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, data_addr + 2, OldFlashData.Set_function);
    HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, data_addr + 4, OldFlashData.Set_Distance_Left);
    HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, data_addr + 6, OldFlashData.Set_Distance_Right);
    HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, data_addr + 8, OldFlashData.Set_BackLimit);
    HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, data_addr + 10, OldFlashData.Set_TurnLimit);
    
    HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, data_addr + 12, OldFlashData.WorkdayAlarmStartPoint);
    HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, data_addr + 16, OldFlashData.WorkdayAlarmStopPoint);
    HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, data_addr + 20, OldFlashData.DayOffAlarmStartPoint);
    HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, data_addr + 24, OldFlashData.DayOffAlarmStopPoint);
    HAL_FLASH_Lock();
}

/**
  * @功能		    数据变动检测。
  * @输入变量	    
  * @输出变量	    变量有变化则 返回1,无变化则 返回0
  * @参数			
  */
uint16_t Data_Altered_Event(void){
    uint16_t evant_sta = 0;
    if((OldFlashData.Set_Place_Point != Set_Place_Point) 
    || (OldFlashData.Set_function != Set_function) 
    || (OldFlashData.Set_Distance_Left != Set_Distance_Left) 
    || (OldFlashData.Set_Distance_Right != Set_Distance_Right) 
    || (OldFlashData.Set_BackLimit != Set_BackLimit) 
    || (OldFlashData.Set_TurnLimit != Set_TurnLimit) 
    || (OldFlashData.WorkdayAlarmStartPoint != WorkdayAlarm.StartTimePoint) 
    || (OldFlashData.WorkdayAlarmStopPoint != WorkdayAlarm.StopTimePoint) 
    || (OldFlashData.DayOffAlarmStartPoint != DayOffAlarm.StartTimePoint) 
    || (OldFlashData.DayOffAlarmStopPoint != DayOffAlarm.StopTimePoint)){

//刷新结构体内容
        OldFlashData.Set_Place_Point = Set_Place_Point;
        OldFlashData.Set_function = Set_function;
        OldFlashData.Set_Distance_Left = Set_Distance_Left;
        OldFlashData.Set_Distance_Right = Set_Distance_Right;
        OldFlashData.Set_BackLimit = Set_BackLimit;
        OldFlashData.Set_TurnLimit = Set_TurnLimit;
        OldFlashData.WorkdayAlarmStartPoint = WorkdayAlarm.StartTimePoint;
        OldFlashData.WorkdayAlarmStopPoint = WorkdayAlarm.StopTimePoint;
        OldFlashData.DayOffAlarmStartPoint = DayOffAlarm.StartTimePoint;
        OldFlashData.DayOffAlarmStopPoint = DayOffAlarm.StopTimePoint;
                 
        evant_sta = 1;
    }
    return evant_sta;
}

/**
  * @功能		    获取开机首数据地址及数据。
  * @输入变量	    
  * @输出变量	    
  * @参数			
  */
void First_Launches_Flash_Process(void){
    uint32_t data_addr;
//获取扇区内首个无数据的地址
    data_addr = check_flash_data_addr(MEMORY_FLASH_START_ADDRESS);
    //如果第一次开机,未录入任何数据
    if(data_addr == MEMORY_FLASH_START_ADDRESS){
        //获取当前可写地址
        Flash_SoftWare_REG.Current_Write_Address = MEMORY_FLASH_START_ADDRESS;
        //地址尾部标志位
        Flash_SoftWare_REG.AddressTail_Sta = 0;
        //默认出厂设置(用户变量默认值)
        FlashData_Normal();
    }
    //如果已录入数据
    else{
        //获取内存数据
        FlasReadData(data_addr - FLASH_DATA_GROUP_SIZE);
        //如果在最后一个地址
        if(data_addr == MEMORY_FLASH_STOP_ADDRESS){
            //记录已经写满内存
            Flash_SoftWare_REG.AddressTail_Sta = 1;
            //写地址从头开始
            Flash_SoftWare_REG.Current_Write_Address = MEMORY_FLASH_START_ADDRESS;
        }else{//如果不在最后一个地址
            //清尾部标志位
            Flash_SoftWare_REG.AddressTail_Sta = 0;
            //写地址在空内容上
            Flash_SoftWare_REG.Current_Write_Address = data_addr;
        }
    }
    //对数据进行记录 并刷新写刷新
    OldFlashData_Init();
}

/**
  * @功能		    获取开机数据。
  * @输入变量	    
  * @输出变量	    
  * @参数			
  */
uint16_t Auto_Write_Flash(void){
    uint16_t evant_sta = 0;
    //如果数据有变动,则进行数据记录
    if(Data_Altered_Event() == 1){
        //如果在扇区内
        if((Flash_SoftWare_REG.Current_Write_Address>= MEMORY_FLASH_START_ADDRESS) && (Flash_SoftWare_REG.Current_Write_Address < MEMORY_FLASH_STOP_ADDRESS)){
            //如果Flash 2K内存已满
            if(Flash_SoftWare_REG.AddressTail_Sta){
                Flash_SoftWare_REG.AddressTail_Sta = 0;
                Flash_SoftWare_REG.Current_Write_Address = MEMORY_FLASH_START_ADDRESS;
                //擦除这个扇区
                memory_erase(MEMORY_FLASH_START_ADDRESS);
            }
        }else{
            //擦除这个扇区
            memory_erase(MEMORY_FLASH_START_ADDRESS);

            Flash_SoftWare_REG.AddressTail_Sta = 0;
            //复位擦写地址
            Flash_SoftWare_REG.Current_Write_Address = MEMORY_FLASH_START_ADDRESS;
        }
        
        FlashWriteData(Flash_SoftWare_REG.Current_Write_Address);
        Flash_SoftWare_REG.Current_Write_Address += FLASH_DATA_GROUP_SIZE;
        evant_sta = 1;
    }
    return evant_sta;
}

 main应用:

#include "main.h" 
#include "flash.h" 

int main(void){
    //用户代码

    //Flash初始化代码
    First_Launches_Flash_Process();

    while(1){
        //用户代码
            
        //Flash自动记录代码
        Auto_Write_Flash();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

嵌入式小学

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

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

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

打赏作者

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

抵扣说明:

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

余额充值