样例工程目录结构
WQiang authored at 2021-12-01 10:47:56
9.68 KiB
microCLib

#include "ConfigBase.h"
#include "Flash.h"
#include "Crc.h"

#define DisableIrq() __disable_irq()
#define EnableIrq() __enable_irq()

// 获取 Config
bool GetConfig(Config_t* cfg,uint addr)
{
	// uint addr = GetChipSize(0) - GetFlashBlockSize(0) + GetChipStartAddr(0);
	ConfigBase_t* cf = (ConfigBase_t*)addr;

	// 魔数  默认为 : 'M' 'a' 'g' '&'   0x4D616726
	if (cf->MagicNum != 0x4D616726)return false;

	// 计算校验
	uint crc = CaclcCRC32B((byte*)cf, sizeof(ConfigBase_t) - sizeof(cf->Crc32));
	if (cf->Crc32 != crc)return false;

	memcpy(cfg, (byte*)&cf->Config, cf->Length);

	// 判断是否完全兼容, 不要求强制升级好了。
	// 让用的人自己去区分。
	// if (sizeof(Config_t) != cf->Length)
	// {
	// 	CleanConfig();
	// }

	return true;
}

bool SetConfig(Config_t* cfg2, uint addr)
{
	// uint addr = GetChipSize(0) - GetFlashBlockSize(0) + GetChipStartAddr(0);
	// 硬件要求不能被中断打断  否则会失败
	DisableIrq();

	ConfigBase_t base;
	base.MagicNum = 0x4D616726;
	base.Length = sizeof(Config_t);

	memcpy((byte*)&base.Config, (byte*)cfg2, sizeof(Config_t));

	// 计算校验
	uint crc = CaclcCRC32B((byte*)&base, sizeof(ConfigBase_t) - sizeof(base.Crc32));
	base.Crc32 = crc;

	const int tryCount = 3;
	bool stat = false;
	int count = 0;
	while (true)
	{
		// 超出擦除失败次数
		if (count > tryCount)break;

		if (EraseBlock(addr, false))
		{
			stat = true;
			break;
		}

		count++;
	}
	EnableIrq();

	if (!stat)return stat;

	stat = false;
	count = 0;
	// 硬件要求不能被中断打断  否则会失败
	DisableIrq();
	while (true)
	{
		// 超出写入出错次数
		if (count > tryCount)break;

		if (WriteBlock(addr, (byte*)&base, sizeof(ConfigBase_t), true))
		{
			stat = true;
			break;
		}
		count++;
	}
	EnableIrq();

	return stat;
}

bool CleanConfig(uint addr)
{
	// 硬件要求不能被中断打断  否则会失败
	DisableIrq();

	// uint addr = GetChipSize(0) - GetFlashBlockSize(0) + GetChipStartAddr(0);
	bool rs = EraseBlock(addr, false);

	EnableIrq();

	return rs;
}



#include "Crc.h"
#include "Array.h"

// Config 有效性 取决于 MagicNum SaveIdx Crc32
typedef struct
{
	// 配置实际内容。
	Config_t Config;

	// 魔数  内部使用。 外部不要碰
	// 默认为 : 'M' 'a' 'g' '&'   0x4D616726
	uint MagicNum;
	// 保存号,也是位置号。内部使用。外部不要碰  0-65535
	ushort Index;
	// 校验  内部使用。 外部不要碰
	ushort Crc16;
}FConfigItem_t;

/**
 * @brief 修正内部数据
 * @param cfg 配置
 * @param idx 必要条件
 */
static void FixFCfg(FConfigItem_t* cfg, ushort idx)
{
	cfg->Index = idx;
	cfg->MagicNum = 0x4D616726;
	byte* p = (byte*)cfg;
	int len = offsetof2(FConfigItem_t,Crc16);
	cfg->Crc16 = CaclcCRC16_MODBUS(p, len);
	// cfg->Crc16 = CaclcCRC16_MODBUS(p,sizeof(FConfigItem_t) - 2);
}

/**
 * @brief 检查配置是否有效
 * @param cfg
 */
static bool FCfgIsOK(FConfigItem_t* cfg) 
{
	if(cfg == NULL)return false;
	if(cfg->MagicNum != 0x4D616726)return false;
	
	byte* p = (byte*)cfg;
	int len = offsetof2(FConfigItem_t,Crc16);
 	ushort cl = CaclcCRC16_MODBUS(p, len);
 	// ushort cl = CaclcCRC16_MODBUS(p,sizeof(FConfigItem_t) - 2);
	if(cl != cfg->Crc16)return false;

	return true;
}

/**
 * @brief 所在块的块地址
 * @param ctl
 * @param idx
 */
static uint FCfgSecAddr(FCfgCtrl_t* ctl,int idx)
{
	// 块个数
	int sCnt = 1 << ctl->FSecBit;
	// 所在块
	int sec = idx & (sCnt - 1);
	// 块地址
	uint addr = ctl->FSecAddrs[sec];
	return addr;
}

// #define FSECADDR(ctl,idx)  (ctl->FSecAddrs[(idx & ((1 << ctl->FSecBit) - 1)))])
// FSECADDR FCfgSecAddr

/**
 * @brief 块内偏移量
 * @param ctl
 * @param idx
 */
static uint FSecOffset(FCfgCtrl_t* ctl,int idx)
{
	// 块内编号
	idx >>=  ctl->FSecBit;
	// 块内个数
	int cCnt = 1 << ctl->FSecCfgBit;
	// 取余
	int sidx = idx & (cCnt - 1);
	// 块内偏移
	uint offset = sizeof(FConfigItem_t) * sidx;
	return offset;
}

// #define FSECOFFSET(ctl,idx)  (sizeof(FConfigItem_t) * ((idx >> ctl->FSecBit) & (1 << ctl->FSecCfgBit) - 1))))
// #define FSECOFFSET FSecOffset

/**
 * @brief 初始化
 * @param ctl
 * @param api NOR FLASH 接口
 * @param sad FLASH 块地址数组
 * @param sadlen 块数组长度
 * @param ccnt 块内 存放配置的份数
 * @return 0 无错
 */
int FCfgInit(FCfgCtrl_t* ctl, NorFlash_t* api, uint* sad, byte sadlen, byte ccnt)
{
	int err = 0;
	byte temp = 0;
	if(ctl == NULL){ err = __LINE__; goto reserr; }
	if(api == NULL){ err = __LINE__; goto reserr; }
	if(sad == NULL){ err = __LINE__; goto reserr; }
	if(sadlen < 2) { err = __LINE__; goto reserr; }
	if(ccnt < 1)   { err = __LINE__; goto reserr; }

	memcpy(&ctl->FApi,api,sizeof(NorFlash_t));
	ctl->FSecAddrs = sad;

	// 计算位数
	temp = 0;
	for (int i = 1; ; i++)
	{
		int cnt = 1 << i;
		if(sadlen >= cnt)temp = i;
		else break;
	}	
	ctl->FSecBit = temp;

	temp = 0;
	for (int i = 0; ; i++)
	{
		int cnt = 1 << i;
		if(ccnt >= cnt)temp = i;
		else break;
	}
	ctl->FSecCfgBit = temp;
	return 0;

	reserr:
	DebugPrintf("%s [%d] ERROR\r\n",__func__,__LINE__);
	return err;
}

/**
 * @brief 扫描整个 FLASH 存储区,找到最新且有效的配置取出来
 * @param ctl
 * @param cfg
 */
bool FCfgLoad(FCfgCtrl_t* ctl, Config_t* cfg) 
{
	if(ctl == NULL)return false;
	if(ctl->FSecAddrs == NULL)return false;
	if(ctl->FSecBit < 1)return false;
	if(cfg == NULL)return false;

	// 最大同时存在配置个数
	int limit = (1 << ctl->FSecBit) *  (1 << ctl->FSecCfgBit);
	// 已存在最大IDX
	int max = -1;
	// 小于limit的,已存在最大IDX
	int limitMax = -1;
	
	FConfigItem_t temp;
	FConfigItem_t* fp = &temp;
	// 块个数
	int slen = 1 << ctl->FSecBit;
	// 块内配置个数
	int sCfgCnt = 1 << ctl->FSecCfgBit;

	for (int i = 0; i < slen; i++)
	{
		// 块首地址
		uint base = ctl->FSecAddrs[i];
		for (int j = 0; j < sCfgCnt; j++)
		{			
			// 得到地址
			uint addr = sizeof(FConfigItem_t) * j + base;
			
			if(ctl->FApi.FlashRead != NULL)
			{
				// 读取 配置项
				memset(fp, 0x00, sizeof(FConfigItem_t));
				if(!ctl->FApi.FlashRead(addr,(byte*)fp,sizeof(FConfigItem_t)))continue;				
			}
			else
			{ 
				// 直接指针过去
				fp = (FConfigItem_t*)addr;
			}

			// 配置有效性检查
			if(!FCfgIsOK(fp))continue;

			// 选取最后存入的 idx
			ushort idx = fp->Index;
			if(idx > max)max = idx;
			if(idx < limit)
			{
				if(idx > limitMax)limitMax = idx;
			}			
		}
	}
	
	// 没有任何有效配置
	if(max == -1)
	{
		ctl->CurrIndex = 0;
		memset(&ctl->CurrCfg,0x00,sizeof(FConfigItem_t));		
		return false;
	}

	// 判定最后写入是哪个
	int last = -1;
	if((max > 65535 - limit) && (limitMax != -1))
	{
		// 刚好跨界了。大的被丢弃。使用小的里面最大的。
		last = limitMax;
	}
	else
	{
		last = max;
	}

	// 块地址 + 块内偏移
	uint addr = FCfgSecAddr(ctl,last) + FSecOffset(ctl,last);
	if(ctl->FApi.FlashRead != NULL)
	{
		// 读取 配置项
		memset(fp, 0x00, sizeof(FConfigItem_t));
		ctl->FApi.FlashRead(addr,(byte*)fp,sizeof(FConfigItem_t));				
	}
	else
	{ 
		// 直接指针过去
		fp = (FConfigItem_t*)addr;
	}

	memcpy(&ctl->CurrCfg,&fp->Config,sizeof(Config_t));
	ctl->CurrIndex = fp->Index;

	memcpy(cfg,&ctl->CurrCfg,sizeof(Config_t));
	return true;
}

/**
 * @brief 写配置进去,地址为块首地址的时候擦除块。
 * @param ctl
 * @param cfg
 */
static bool FCfgWrite(FCfgCtrl_t* ctl, FConfigItem_t* cfg)
{
	if(ctl->FApi.FlashWrite == NULL)return false;

	int idx = cfg->Index;
	// 块地址
	uint addr = FCfgSecAddr(ctl,idx);
	// 块内偏移
	uint offset = FSecOffset(ctl,idx);

	if(offset == 0)
	{
		if(ctl->FApi.FlashErase == NULL)return false;
		if(!ctl->FApi.FlashErase(addr))return false;
	}
	
	// 写
	return ctl->FApi.FlashWrite(addr + offset, (byte*)cfg, sizeof(FConfigItem_t));
}

/**
 * @brief 保存配置
 * @param ctl
 * @param cfg
 */
bool FCfgSave(FCfgCtrl_t* ctl, Config_t* cfg)
{
	if(ctl == NULL)return false;
	if(ctl->FSecAddrs == NULL)return false;
	if(ctl->FSecBit < 1)return false;
	if(cfg == NULL)return false;

	// 相同,不存。
	Config_t* curr = &ctl->CurrCfg;
	if(!ArrayEqual((byte*)cfg, (byte*)curr,sizeof(Config_t)))return true;

	// 下一个位置保存
	ushort idx = ctl->CurrIndex + 1;
	FConfigItem_t item;
	memcpy(&item.Config,cfg,sizeof(Config_t));
	FixFCfg(&item,idx);
	bool res = FCfgWrite(ctl, &item);
	// 保存完成,更新本地信息
	if(res)
	{
		memcpy(&ctl->CurrCfg,cfg,sizeof(Config_t));
		ctl->CurrIndex = item.Index;
	}

	return res;
}

/**
 * @brief 删除一个配置
 * @param ctl
 * @param idx
 */
bool FCfgClean(FCfgCtrl_t* ctl, int idx)
{
	if(ctl->FApi.FlashWrite == NULL)return false;

	// 块地址
	uint addr = FCfgSecAddr(ctl,idx);
	// 块内偏移
	uint offset = FSecOffset(ctl,idx);

	// 配置内 magic 位置
	offset += offsetof2(FConfigItem_t,MagicNum);

	// FLASH 擦完是 ff 只能往 00 写
	uint magic = 0;
	return ctl->FApi.FlashWrite(addr+offset, (byte*)&magic, sizeof(magic));
}

/**
 * @brief 擦除所有 FLASH 区域,一个不留
 * @param ctl
 */
bool FCfgCleanAll(FCfgCtrl_t* ctl)
{
	if(ctl == NULL)return false;
	if(ctl->FSecAddrs == NULL)return false;
	if(ctl->FSecBit < 1)return false;
	if(ctl->FApi.FlashErase == NULL)return false;

	int slen = 1 << ctl->FSecBit;
	for (int i = 0; i < slen; i++)
	{
		ctl->FApi.FlashErase(ctl->FSecAddrs[i]);
	}
	ctl->CurrIndex = 0;

	return true;
}