调试数据存储到nandflash的那些事儿(一):关于sizeof()的一些坑
调试数据存储到nandflash的那些事儿(二): 存入nandflash的方式比较
调试数据存储到nandflash的那些事儿(三): 关于移位存入,强制类型转换读出的讨论
调试数据存储到nandflash的那些事儿(四):nandflash底层的简介
调试数据存储到nandflash的那些事儿(五):对nandflash的寻址说明
作为用户,操作nandflash大部分操作无非就是 读、写、格式化。其中读写都一定得需要目标地址。如何根据需求,设定寻址方法就显得不可缺少了。
比如说,需求是nandflash存储一年的分钟数据。请问如何存2019年8月21日00:00的分钟数据?一包分钟数据64字节
/*********************************************************************************************************
nandflsh共有1024块,1块=64页,1页=2048bytes
实时数据:
存储一包64字节,一页可存 2048/64 = 32包;
一共有 366*24*60 = 527040包 则共需要 527040/32 = 16470页;
一共需要 16470/64 ~ 258 块
0 1024块
=========================================================================================
0 258
{ 实时数据 }{ 实时数据 }{ 实时数据 }
*****************************************************************************************/
//nandflash的块数
#define NAND_BLOCKS 1024
//nandflash一块中的页数
#define NAND_PAGES_PER_BLOCK 64
//nandflash一页中的字节数
#define NAND_BYTES_PER_PAGE 2048
//一年的分钟数
#define MINUTES_PER_YEAR (366*24*60)
//一个实时数据包大小
#define RT_PACKAGE_SIZE 64
//一页中可以存多少实时数据包
#define RT_PACKAGES_PER_PAGE (NAND_BYTES_PER_PAGE/RT_PACKAGE_SIZE)
//一年数据所需要的页数
#define RT_PAGES_USED_ONE_YEAR (MINUTES_PER_YEAR/RT_PACKAGES_PER_PAGE)
//实时数据初始页
#define RT_INITIAL_OFFSET 0
步骤:
1.我需要知道我一包64字节,如果存一年的数据,nandflash够不够。
366(一年最大天数)*24(小时)*60(分钟)= 527040
总共需要527040*64(包大小)= 33730560字节
而nandflash一共128MB = 128*1024*1024 = 134217728字节
不仅够存,还能存134217728 / 33730560 ≈ 3.98年
当然上面的方法是很极限的存储方式,需要依次存满每一页,也就是说第一页存不完,往第二页接着存,这导致存储时很麻烦,甚至一包还会跨块。不是不可行,而是没必要使用如此紧凑,增加了底层的难度。
如果不让一包跨页,底层实现会更简单。
那么一页最多能存 2048%64 = 32包
一年最大需要存527040包,所以需要527040 /32 = 16470页
一共需要16470/64 = 258块。满足nandflash的存储条件。
2.分配空间的同时其实也想好了寻址方式。
从第1块开始存,每一页存32包分钟数据,一共能存258块。
回到开头提到的问题,如何存2019年8月21日00:00的分钟数据?
思路是根据时间计算分钟数,根据分钟数计算该存到nandflash的第几个字节。
这个时间是全年,所以从1月1日00:00到 8月21日00:00 一共是31+28+31+30+31+30+31+21 = 233天。也就是233*24*60 = 335520(分钟数)。
一页最多能存32包,所以一共需要335520/32 = 10485页 335520%32 = 0。所以这说明了这个时间是要存在第10485页的0页内偏移位置。
所以在nandflash中的位置是第10485*2048+0 = 21473280(字节)
3.将想法转换成函数
一共需要两个函数,一个是根据时间计算分钟数,所以入口参数是时间,返回分钟数
第二个是根据分钟数计算nandflash地址,入口参数为分钟数,返回地址
//月份天数
#define JAN_DAYS 31
#define FEB_DAYS 28
#define MAR_DAYS 31
#define APR_DAYS 30
#define MAY_DAYS 31
#define JUN_DAYS 30
#define JUL_DAYS 31
#define AUG_DAYS 31
#define SEP_DAYS 30
#define OCT_DAYS 31
#define NOV_DAYS 30
#define DEC_DAYS 31
#define LEAP_YEAR 1
#define JAN_OFFSET 0
#define FEB_OFFSET (JAN_OFFSET + JAN_DAYS)
#define MAR_OFFSET (FEB_OFFSET + FEB_DAYS)
#define APR_OFFSET (MAR_OFFSET + MAR_DAYS)
#define MAY_OFFSET (APR_OFFSET + APR_DAYS)
#define JUN_OFFSET (MAY_OFFSET + MAY_DAYS)
#define JUL_OFFSET (JUN_OFFSET + JUN_DAYS)
#define AUG_OFFSET (JUL_OFFSET + JUL_DAYS)
#define SEP_OFFSET (AUG_OFFSET + AUG_DAYS)
#define OCT_OFFSET (SEP_OFFSET + SEP_DAYS)
#define NOV_OFFSET (OCT_OFFSET + OCT_DAYS)
#define DEC_OFFSET (NOV_OFFSET + NOV_DAYS)
/*
*********************************************************************************************************
* Compute_CurrentMinutes
*
* Description : 由时间计算分钟数
* Arguments : time:时间结构体指针
*
* Returns : 分钟数
*********************************************************************************************************
*/
uint32_t Compute_CurrentMinutes(HexTime_Typedef* time)
{
uint8_t leap_year = 0;
uint16_t days = 0;
if((time->year % 4) == 0)
{
leap_year = 1;
}
switch(time->month)
{
case JAN :
days = JAN_OFFSET + (time->day );
break;
case FEB :
days = FEB_OFFSET + (time->day );
break;
case MAR :
days = MAR_OFFSET + (time->day ) + leap_year;
break;
case APR :
days = APR_OFFSET + (time->day ) + leap_year;
break;
case MAY :
days = MAY_OFFSET + (time->day ) + leap_year;
break;
case JUN :
days = JUN_OFFSET + (time->day ) + leap_year;
break;
case JUL :
days = JUL_OFFSET + (time->day ) + leap_year;
break;
case AUG :
days = AUG_OFFSET + (time->day ) + leap_year;
break;
case SEP :
days = SEP_OFFSET + (time->day ) + leap_year;
break;
case OCT :
days = OCT_OFFSET + (time->day ) + leap_year;
break;
case NOV :
days = NOV_OFFSET + (time->day ) + leap_year;
break;
case DEC :
days = DEC_OFFSET + (time->day ) + leap_year;
break;
default: break;
}
return (((days-1) * 1440) + (time->hour*60) + time->minute);
}
//nandflash的块数
#define NAND_BLOCKS 1024
//nandflash一块中的页数
#define NAND_PAGES_PER_BLOCK 64
//nandflash一页中的字节数
#define NAND_BYTES_PER_PAGE 2048
//一年的分钟数
#define MINUTES_PER_YEAR (366*24*60)
//一个实时数据包大小
#define RT_PACKAGE_SIZE 64
//一页中可以存多少实时数据包
#define RT_PACKAGES_PER_PAGE (NAND_BYTES_PER_PAGE/RT_PACKAGE_SIZE)
//一年数据所需要的页数
#define RT_PAGES_USED_ONE_YEAR (MINUTES_PER_YEAR/RT_PACKAGES_PER_PAGE)
//实时数据初始页
#define RT_INITIAL_OFFSET 0
/*
*********************************************************************************************************
*
* Description : 整数分钟数计算存储地址
* Arguments : minute :当前分钟数
* Returns : 存储器地址(字节)
* Note :
*********************************************************************************************************
*/
uint32_t Air_CalculateAddr(HexTime_Typedef* tTime)
{
uint32_t PageNum; //第几页
uint32_t Inner_Offset; //页内偏移
uint32_t _cMinute = Compute_CurrentMinutes(tTime);
//算所需数据所在页数 = 第几页 + nand初始页号
PageNum = _cMinute / RT_PACKAGES_PER_PAGE + RT_INITIAL_OFFSET;
Inner_Offset = (_cMinute % RT_PACKAGES_PER_PAGE) * RT_PACKAGE_SIZE;
return (PageNum * 2048 + Inner_Offset);
}
地址找的对,包内数据的排放顺序又是已知的,存入和读出应该就不难了。