GD32现在越来越火,应用也越来越广泛。我们在开发项目的时候,总会有需要掉电存储一些配置信息的时候,但是使用外挂flash、或者EEPROM,或多或少都会占用一些外围接口或增加一定的成本。于是,直接将配置参数存储到片内flash上还是会更香一些。一般flash的擦写次数能够达到10万次,若擦写操作不太频繁,完全是够用的。
刚好最近做了个项目,用到了这个功能,掉电存储一些必要的参数配置。在GD32中,Flash模块的名称为FMC。
其实要实现的主要功能就只有三个:读取存在flash中的参数信息、报存参数信息到flash中、获取读出来的参数信息。
而参数信息可以是自行定义的任意结构体,但是值得注意的是Flash是按页(1Kb)擦除的。需要自行修改FLASH写入的开始地址和结束地址。我这款单片机Flash大小是用的64K的,数据存储在最后1KB的位置。
由于数据写入以四个直接为单位,所以创建了一个联合体,方便通过指针来将参数数据写入。
注意:以下程序只支持大小小于1KB的参数读写,如果超过1KB,需要加入对页的判断,自行修改数据读、写过程。
程序源码示例:
params_manage.h
#ifndef __PARAMS_MANAGE_H__
#define __PARAMS_MANAGE_H__
#include <stdint.h>
#ifdef __cplusplus
extern "C"{
#endif /* __cplusplus */
#define FMC_PAGE_SIZE ((uint16_t)0x400U)
#define FMC_WRITE_START_ADDR ((uint32_t)0x0800FC00U)
#define FMC_WRITE_END_ADDR ((uint32_t)0x0800FFFFU)
/* 需要保存的参数 */
typedef struct _CONFIG_PARAMS
{
unsigned char data_valid_flag; //数据有效标志 0xF1:有效 其它:无效
unsigned char sleep_flag; //是否进入休眠标志 0:不进入 1:进入
unsigned char open_direction; //开门方向 0:左开门 1:右开门
int16_t x_value; //地磁X轴方向值
int16_t y_value; //地磁Y轴方向值
int16_t z_value; //地磁Z轴方向值
uint32_t tmp_password_flag; //临时密码使用标志
unsigned char user_flag[50]; //当前有效用户标记
unsigned char password_table[50][7]; //用户密码数组
}CONFIG_PARAMS;
/* 系统状态信息 */
typedef struct _SYS_STATUS
{
unsigned char door_state; //门磁状态 0:关门 1:开门
unsigned char auto_anti_lock; //自动反锁配置 0:不自动反锁 1:自动反锁
unsigned char quiet_flag; //自动反锁配置 0:音频正常播放 1:部分音频静音
unsigned char opto1_state; //光耦1状态
unsigned char opto2_state; //光耦2状态
unsigned char opto3_state; //光耦3状态
float battery_volatage; //电池电压 0-5V
}SYS_STATUS;
typedef union _CONFIG_PARAMS_UNION
{
CONFIG_PARAMS config_params; // 360字节
uint32_t data[104]; //用于通过联合体字节对齐,方便数据存储 104*4=416字节
}CONFIG_PARAMS_UNION;
extern int load_config_params(void);
extern CONFIG_PARAMS *get_config_params(void);
extern int is_config_params_avaliable(void);
extern int save_config_params(CONFIG_PARAMS *params);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif
params_manage.c
#include <stdio.h>
#include <string.h>
#include "gd32e230.h"
#include "params_manage.h"
static CONFIG_PARAMS g_config_params = {0};
static void fmc_erase_pages(void)
{
/* unlock the flash program/erase controller */
fmc_unlock();
/* clear all pending flags */
fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_WPERR | FMC_FLAG_PGERR);
/* erase the flash pages */
fmc_page_erase(FMC_WRITE_START_ADDR);
fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_WPERR | FMC_FLAG_PGERR);
/* lock the main FMC after the erase operation */
fmc_lock();
}
static void fmc_program(uint32_t *data, int data_len)
{
/* unlock the flash program/erase controller */
fmc_unlock();
uint32_t address = FMC_WRITE_START_ADDR;
/* program flash */
while(address <= FMC_WRITE_END_ADDR) {
if(data_len <= 0)
break;
fmc_word_program(address, *data);
address += 4U;
data++;
fmc_flag_clear(FMC_FLAG_END | FMC_FLAG_WPERR | FMC_FLAG_PGERR);
data_len-=4;
}
/* lock the main FMC after the program operation */
fmc_lock();
}
//参数是否有效
int is_config_params_avaliable()
{
return (g_config_params.data_valid_flag == 0xF1)?1:0;
}
//加载配置参数
int load_config_params()
{
CONFIG_PARAMS_UNION config_params_union;
unsigned char *ptr = (unsigned char *)FMC_WRITE_START_ADDR; //将指针指向存储区首地址
if(*ptr == 0xF1)
{
//从Flash中读取到有效的配置数据
memcpy(&config_params_union, ptr, sizeof(CONFIG_PARAMS_UNION));
memcpy(&g_config_params, &config_params_union.config_params, sizeof(CONFIG_PARAMS)); //拷贝数据
return 0;
}
return -1;
}
//获取参数配置
CONFIG_PARAMS *get_config_params()
{
return &g_config_params;
}
//保存配置参数
int save_config_params(CONFIG_PARAMS *params)
{
if(params == NULL)
return -1;
CONFIG_PARAMS_UNION config_params_union;
memset(&config_params_union, 0, sizeof(CONFIG_PARAMS_UNION)); //清空数据
memcpy(&config_params_union.config_params, params, sizeof(CONFIG_PARAMS)); //拷贝数据
//擦除FLASH
fmc_erase_pages();
//保存配置数据到Flash中
fmc_program((uint32_t *)&config_params_union, sizeof(CONFIG_PARAMS_UNION));
return 0;
}