说明:该方法为固定大小的数据包方式进行记录,写满一页后再擦除设定页从新记录,增加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();
}
}