zynq-flash滚动存储日志信息

目录

一、常用FLASH擦写规则

二、字节/页/块转化

三、超过256字节循环写入

四、滚动存储日志信息

五、注意


一、常用FLASH擦写规则

最小擦除单位:扇区
可选择擦除单位:扇区、块、全片
最大编程(写入)单位:页( 256 Byte),大于256 Byte则需要循环写入。
最小编程(写入)单位:1 Byte,即一次可写入 1~256 Byte的任意长度字节。
未写入时FLASH里面的数据为全1,即0xFF。
只能由 1 —> 0 写入,不能由 0 —> 1 写入,即如果已经写入过了,则需要先擦除(擦除后数据变为全1)再写入。
示例:0xF0(1111 0000),即高4位可写入,低4位不可写入。

二、字节/页/块转化

1字节(Byte)=8位(bit)

1页=256字节

1扇区=16页=4K

1块=16扇区

1KB=1024B

1MB=1024KB=256扇区

三、超过256字节循环写入

#define PAGE_SIZE  256
INT32U FLASH_PAGE_WRITE(uint8_t* buf, uint32_t wICAddr, uint16_t numByteToWrite)
{
  //擦除FLASH
  //FlashErase(&QspiInstance, TEST_ADDRESS, MAX_DATA);
  uint8_t numOfPage, numOfSingle, offsetAddr, count;
  //写入整页数
  numOfPage = numByteToWrite / PAGE_SIZE;
  //写入整页后剩余字节数
  numOfSingle = numByteToWrite % PAGE_SIZE;
  //写入地址在页中的偏移量
  offsetAddr = wICAddr % PAGE_SIZE;
  //页中剩余可写入字节
  count = PAGE_SIZE - offsetAddr;

  //写入地址为整
  if (offsetAddr == 0)
  {
    //不足一页
    if (numOfPage == 0)
    {
    	FlashWrite(&QspiInstance,wICAddr,numOfSingle,buf,WRITE_CMD);
    }
    //超过一页
    else
    {
      //整页处理
      while (numOfPage --)
      {
    	FlashWrite(&QspiInstance,wICAddr,PAGE_SIZE,buf,WRITE_CMD);
        wICAddr += PAGE_SIZE;
        buf += PAGE_SIZE;
      }

      //不足一页处理
      if (numOfSingle != 0)
      {
    	  FlashWrite(&QspiInstance,wICAddr,numOfSingle,buf,WRITE_CMD);
      }
    }
  }
  //写入地址不为整
  else
  {
    //不足一页
    if (numOfPage == 0)
    {
      //写入字节数不超过页中剩余可写入字节
      if (numOfSingle <= count)
      {
    	  FlashWrite(&QspiInstance,wICAddr,numOfSingle,buf,WRITE_CMD);
      }
      //写入字节数超过页中剩余可写入字节
      else
      {
    	  FlashWrite(&QspiInstance,wICAddr,count,buf,WRITE_CMD);
        wICAddr += count;
        buf += count;
        FlashWrite(&QspiInstance,wICAddr,numOfSingle - count,buf,WRITE_CMD);
      }
    }
    //超过一页
    else
    {
      FlashWrite(&QspiInstance,wICAddr,count,buf,WRITE_CMD);
      wICAddr += count;
      buf += count;
      numOfPage = (numByteToWrite - count) / PAGE_SIZE;
      numOfSingle = (numByteToWrite - count) % PAGE_SIZE;

      //整页处理
      while (numOfPage --)
      {
    	  FlashWrite(&QspiInstance,wICAddr,PAGE_SIZE,buf,WRITE_CMD);
        wICAddr += PAGE_SIZE;
        buf += PAGE_SIZE;
      }

      //不足一页的处理
      if (numOfSingle != 0)
      {
    	  FlashWrite(&QspiInstance,wICAddr,numOfSingle,buf,WRITE_CMD);
      }
    }
  }
  return 2;
}

四、滚动存储日志信息

1、最少是两个扇区循环存储,flash是4字节对齐,所以写的要是四的倍数,本程序写的是256个字节,也就是一页,这样比较好计算,每个扇区要保留256个字节用来存序号,(256个字节不一定全部用完,我是只用了前8个字节存序号,剩下的保留了)来判断是否到了新扇区。

.h文件

#ifndef __LOGDRIVER_H__
#define __LOGDRIVER_H__

#include "main.h"
#define LOG_SECTOR_RSV_BYTES			256
//日志存储器的扇区保留字节数
#define FLASHPARA_ADDR                  0xA00000      //128K
#define FLASHPARA_SIZE                  0x20000
#define LOGSYSPARA_START_ADDR			FLASHPARA_ADDR
#define LOGSYSPARA_SECTOR_NUM           FLASHPARA_SIZE/0x1000  //32个扇区
#define LOG_SECTOR_SIZE 				0x1000 //4096
//日志存储器的扇区大小
#define PARA_LENGTH						(256-4)//减去的4字节是结构体的类型和校验
#define SSC_LOGDRIVER_CRC16					1

#define LOG_READ_RETCODE_ALL_FF 			0
#define LOG_READ_RETCODE_POS_ERR 			1
#define LOG_READ_RETCODE_CHKSUM_ERR 		2
#define LOG_READ_RETCODE_OK 				3
typedef struct
{
	INT16U    	version;              // 数据结构的版本!!!
	INT8U		paras[PARA_LENGTH];	  //实际参数
	INT8U 		crc[2];				  //校验
} PARACB;
typedef struct tag_LOGCB
{
	INT32U 	flash_start_addr;		//该日志存放在flash中的物理起始地址
	INT16U 	items_per_sector;		//一个扇区可存放的条目数,		(LOG_SECTOR_SIZE-8)/item_size  28条
	INT16U 	total_items;			//该日志总条目数,  < 32000   	 	items_per_sector*sectors
	INT8U 	sectors;				//该日志占用的扇区数
	INT16U 	item_size;				//该日志每条目的大小,为item实际大小 + (chkmode + 1).
									//多加的(chkmode + 1)字节为item的校验,由logdiver内部处理
	INT8U 	chkmode;					//0:chksum	1:crc16
} SSC_log_def;
//定义日志系统的基本信息数据结构,一般放在code段中,减少对ram的占用
typedef struct  //<256BYTE
{
//以下可以为通信或其他在程序中自己实时更改 124
	u8 VOL[4];
	u8 CUR[4];
	u8 POW[4];
//ARC Management
	u8 RATE[8];
	u8 arc_con[2];
	u8 micro[9];
	u8 hard[21];
	u8 arc_burst[5];
	u8 counters;
//Communication
	u8 ACTIVE;
	u8 INITIAL;
	u8 ACT_SOURCE;
	u8 RS_485[2];
	u8 BIT;
	u8 PROFIBUS[7];
	u8 BIG;
	u8 COM[2];
//Configuration
	u8 BIP;
	u8 FR_DU[16];
	u8 PUL;
	u8 BLINK[26];
	u8 PRO[2];
	u8 BL_S;
	u8 POW1;
	u8 PAR;
	u8 COM1;
}FLASHPARA;

#define flash_para		(*(FLASHPARA*)(&pkg_para.paras[0]))
#define SSC_para	 ((INT8U*)(&pkg_para.paras[0]))
void SSC_paralog_save(void);
void SSC_paralog_read(void);
INT16U SSC_logdriver_init(SSC_log_def * plogcb, INT8U * pdlog);
void SSC_paralog_init(void);
void setmem_0(INT8U *buf,INT16U n);
INT8U isFF(INT8U *buf, INT16U n); //最多判断前8字节是否全0xff
INT16U SSC_logdriver_save(SSC_log_def * plogcb, INT8U * pslog, INT16U item_pos);
INT16U SSC_logdriver_read(SSC_log_def * plogcb, INT8U * pdlog, INT16U item_pos);
#endif

INT16U SSC_logdriver_save(SSC_log_def * plogcb, INT8U * pslog, INT16U item_pos)
{
	INT16U total_items = plogcb->total_items,crc;
	INT16U item_size = plogcb->item_size;
	INT32U flash_off;
	INT32U wbuf[2];
    //将改变的结构体数据存放到另一个结构体(写入flash)里
	sdn_memcpy((INT8U *)(&USER_TOTAL),SSC_para,PARA_LENGTH);
	if(plogcb->chkmode == SSC_LOGDRIVER_CRC16)
	{
	  	crc = SSC_logdiver_crc16(pslog, item_size-2);
		pslog[item_size-2] = (crc>>8);
		pslog[item_size-1] = crc;
	}
	else
		pslog[item_size-1] = SSC_logdiver_chksum(pslog, item_size-1);

	if(item_pos >= total_items)
		item_pos = 0;

	flash_off = plogcb->flash_start_addr + LOG_SECTOR_SIZE * (item_pos/plogcb->items_per_sector);

	if((item_pos%plogcb->items_per_sector) == 0)	//new sector 新扇区,该日志一共32个扇区
	{
		SSC_logdiver_eraseflash(flash_off, LOG_SECTOR_SIZE);
		wbuf[0] = log_seq_no;
		wbuf[1] = log_seq_no;//保留的8字节中存放相同的序号,用来校验。
		SSC_logdiver_writeflash(flash_off, 8, (INT8U *)wbuf);
		log_seq_no ++;
	}
	else
		flash_off += ((item_pos%plogcb->items_per_sector) * item_size);

	flash_off += LOG_SECTOR_RSV_BYTES;
	SSC_logdiver_writeflash(flash_off, item_size, pslog);
	item_pos ++;
	if(item_pos >= total_items)
		item_pos = 0;
	return item_pos;
}
void SSC_logdiver_readflash(INT32U addr, INT16U n, INT8U *rbuf)
//读flash函数
{
	FlashRead(&QspiInstance,addr,n,rbuf,QUAD_READ_CMD);  //读数据
}

void SSC_logdiver_eraseflash(INT32U addr, INT32U size)
//擦除flash函数,size指擦除的字节数
{
	FlashErase(&QspiInstance,addr,size);
}

void SSC_logdiver_writeflash(INT32U addr, INT16U n, INT8U *wbuf)
//写flash函数
{
	FLASH_PAGE_WRITE(wbuf,addr,  n);//写数据
}
unsigned char gen_chksum(unsigned char *p, INT32U p_len)
{
	unsigned char chksum = 0;

	while(p_len)
	{
		chksum += (*p);
		p++;
		p_len--;
	}

	return chksum;
}
INT8U SSC_logdiver_chksum(INT8U *buf,INT16U n)
//和校验计算函数
{
	return gen_chksum(buf, n);
}
INT16U SSC_logdiver_crc16(INT8U *buf,INT16U n)
{
	return gen_crc(buf, n, 0);
}
INT16U gen_crc(unsigned char * buf, INT32U len, INT16U precrc)
{
	INT16U crc = precrc;
	INT32U i;

	for (i = 0; i < len; i++)
		crc = crc_table[((crc >> 8) ^ buf[i]) & 0xFF] ^ (crc << 8);
	return crc;
}
void sdn_memcpy(INT8U * src, INT8U * dst, INT32U n)
{
	while(n)
	{
		n--;
		*dst++ = *src++;
	}
}


寻找最新的日志信息是用的二分法,可以提高效率

//参数版本,如果参数发生更新,则版本需要更新
static const FLASHPARA default_para =
{
		{0x00,0x00,0x00,0x00},//电压
		{0x00,0x00,0x00,0x00},//电流
		{0x00,0x00,0x00,0x00},//功率
		{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},//urate arc rate
		{0x06,0x01},//ARC CONTROL
		{0x00,0x00,0x20,0x42,0x14,0x00,0x00,0x48,0x42},//MICRO ARC
		{0x00,0x00,0xDD,0x42,0x00,0x00,0xFA,0x42,0x00,0x00,0xA0,0x42,0x00,0x00,0xC8,0x42,0x00},//HARD ARC
		{0x03,0xE8,0x03,0x00,0x78},//ARC BURST
		0x00,//ARC CNT
		0x9B,
		0x10,
		0x10,
		{0x00,0x01},
		0x01,
		{0x78,0x27,0x10,0x27,0x10,0x27,0x10},
		0x01,
		{0x00,0x1E},
		0x04,
		{0x00,0x00,0x20,0x42,0x00,0x00,0x48,0x42,0x00,0x00,0x20,0x42,0x00,0x00,0x48,0x42},
		0x05,
		{0x01,0x00,0x00,0xC0,0x40,0x00,0x00,0x8C,0x42,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x40,0x00,0x00,0x8C,0x42,0x00,0x00,0x00,0x00},
		{0x00,0x00},
		0x07,
		0x01,
		0x00,
		0x00
};
//参数部分所占的字节数,一般建议保留一些字节,已备扩展
const SSC_log_def SSC_paralog_def =
{
	LOGSYSPARA_START_ADDR,
	(LOG_SECTOR_SIZE - LOG_SECTOR_RSV_BYTES)/256,	//32条
	(LOG_SECTOR_SIZE - LOG_SECTOR_RSV_BYTES)/256 * LOGSYSPARA_SECTOR_NUM,
	LOGSYSPARA_SECTOR_NUM,  //32,该日志占用的扇区数
	256,  //4字节对齐
	1
};
void SSC_paralog_init(void)
/*初始化参数log*/
{
	paralog_next_pos = SSC_logdriver_init(paralogcb, (INT8U *)(&pkg_para));
	if( pkg_para.version!=PKGPARA_VERSION )
	{
  		SSC_default_para();
		paralog_next_pos = 0;
		return;
	}
}
//寻找新的扇区日志信息
INT16U SSC_logdriver_init(SSC_log_def * plogcb, INT8U * pdlog)
{
	INT32U i, flash_off;
	INT32U big_sector=0xffffUL, big_seq = 0;
	INT32U rbuf[2];
	INT16U  item_size;
	INT16U item_start, item_end;

	item_size = plogcb->item_size;//256
	flash_off = plogcb->flash_start_addr;//0xA00000

	for(i=0; i<plogcb->sectors; i++)			//寻找最新sector plogcb->sectors 32扇区
	{
        //判断每个扇区的前8个字节
		SSC_logdiver_readflash(flash_off, 8, (INT8U *)rbuf);

		if(rbuf[0]==rbuf[1] && rbuf[0]!=0xffffffffUL)
		{
			if(rbuf[0] > big_seq)
			{
				big_seq = rbuf[0];
				big_sector = i;
			}
		}

		flash_off += LOG_SECTOR_SIZE;
	}

	if(big_sector == 0xffffUL)					//没找到有效sector
	{
        //初始化结构体参数
		setmem_0(pdlog,item_size);
		return 0;
	}

	if(log_seq_no <= big_seq)
		log_seq_no = big_seq + 1;

	//二分法寻找最新item
	flash_off = plogcb->flash_start_addr + big_sector*LOG_SECTOR_SIZE + LOG_SECTOR_RSV_BYTES;

	item_start 	= 0;
	item_end	= plogcb->items_per_sector - 1;

	while(item_start < item_end)
	{
		if(item_start ==(item_end-1))
			i = item_end;
		else
			i = (item_start + item_end)/2;

		SSC_logdiver_readflash(flash_off + i*item_size, item_size, pdlog);

		if(isFF(pdlog, item_size))
			item_end = i-1;
		else
			item_start = i;
	}

	big_seq = big_sector * plogcb->items_per_sector + item_start;	//item_pos --> free item
	SSC_logdiver_readflash(flash_off + item_start*item_size, item_size, pdlog);
	if(!isFF(pdlog, item_size))
		big_seq++;

	for(i=0; i<8; i++)
	{
		if(SSC_logdriver_read(plogcb, pdlog, big_seq-i-1)==LOG_READ_RETCODE_OK)
			return big_seq;
	}

	setmem_0(pdlog,item_size);
	return 0;//big_seq;
}

INT16U SSC_logdriver_read(SSC_log_def * plogcb, INT8U * pdlog, INT16U item_pos)
//读1个item, return:0 -- all FF,1 -- item_pos error,2 -- chksum error, 3 -- read ok
//		item_pos <= 2*total_items - 1 and item_pos >= 0 - (total_items-1)
{
	INT16U total_items = plogcb->total_items;
	INT16U item_size = plogcb->item_size;
	INT32U flash_off;

	if(item_pos > 0x8000U)//序号为负数?
		item_pos += total_items;
	else if(item_pos >= total_items)
		item_pos -= total_items;

	if(item_pos >= total_items)
		return LOG_READ_RETCODE_POS_ERR;

	flash_off = plogcb->flash_start_addr + LOG_SECTOR_SIZE * (item_pos/plogcb->items_per_sector) + LOG_SECTOR_RSV_BYTES;
	flash_off += ((item_pos%plogcb->items_per_sector) * item_size);

	SSC_logdiver_readflash(flash_off, item_size, pdlog);

	if(isFF(pdlog, item_size))
		return LOG_READ_RETCODE_ALL_FF;

	if(plogcb->chkmode == SSC_LOGDRIVER_CRC16)
	{
	  	if(SSC_logdiver_crc16(pdlog, item_size-2) != (pdlog[item_size-2]*256U+pdlog[item_size-1]))
			return LOG_READ_RETCODE_CHKSUM_ERR;
	}
	else
	{
		if(SSC_logdiver_chksum(pdlog, item_size-1) != pdlog[item_size-1])
			return LOG_READ_RETCODE_CHKSUM_ERR;
	}
	sdn_memcpy(SSC_para,(INT8U *)(&USER_TOTAL),PARA_LENGTH);
	return LOG_READ_RETCODE_OK;
}

五、注意

代码不全,只是提供思路:初始化先寻找扇区序号,如果flash里有日志信息,也就是扇区的前8字节不是ffffffff,之后在二分法寻找这个扇区的最新的日志,保存日志前俩字节是结构体类型,最后俩字节是校验

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值