/******************************************************************************
版权所有：	
文件名称：	flash_at45db321.c
文件版本：	01.01
创建作者:	   sunxi
创建日期：	2008-08-19
功能说明：	at45db321驱动程序。
其它说明：	目前通过页擦除整个FLASH需要64.8秒，读整个FLASH需要6.3秒，写整个FLASH
			需要121秒（首版测试结果）。
修改记录：
	2010-07-16 sunxi	_at45db321_page_write函数以前是由OP_PROGRAM_VIA_BUF1命令
						完成，现改为由OP_WRITE_BUFFER1和OP_MWRITE_BUFFER1命令组合
						完成。这样做的原因OP_PROGRAM_VIA_BUF1实际由BUF写、页擦除、
						页编程三个命令组合而成，由于有页擦除命令的存在，导致页写的
						时间速度比较慢，要15ms左右，改进后只需3.5ms左右。使用本修改
						后的程序必须确保写之前已经对相应的页进行了擦除。
						新版本代码的测试结果如下：
						flash_test begin!
						block_size=512,blocks=8192.
						at45db321d_Identify ok!(us=9)!
						at45db321_sector_protection:ret=0!
						at45DB321Erase (us=77945334,ret=0)!
						at45DB321Erase verify ok(us=6230836)!
						at45DB321Write ok(us=28132804)!
						at45DB321Write verify ok(us=6217728)!
						at45DB321Erase (us=69492882,ret=0)!
						at45DB321Erase verify ok(us=6243943)!
						at45DB321Write ok(us=28350756)!
						at45DB321Write verify ok(us=6322586)!
						flash_test end!
						
*/

/*------------------------------- 头文件 --------------------------------------
*/
#include <string.h>

#include "bspconfig.h"
#include "ustimer.h"
#include "dspi.h"
#include "flash.h"
#include "gpio.h"

#include "rt.h"
/*------------------------------- 宏定义 --------------------------------------
*/

#define _DEBUG_FLASH
#if CFG_BSP_DEBUG

#define _DEBUG_FLASH

#endif


//操作命令宏定义
#define OP_READ_CONTINUOUS      0xE8
#define OP_READ_PAGE            0xD2
#define OP_READ_STATUS          0xD7          
#define OP_READ_BUFFER1         0xD4         
#define OP_READ_BUFFER2         0xD6             
#define OP_WRITE_BUFFER1        0x84           
#define OP_WRITE_BUFFER2        0x87          
                             
/* erasing flash */                             
#define OP_ERASE_PAGE           0x81                             
#define OP_ERASE_BLOCK          0x50                             
                             
/* move data between buffer and flash */                         
#define OP_TRANSFER_BUF1        0x53                             
#define OP_TRANSFER_BUF2        0x55                             
#define OP_MREAD_BUFFER1        0xD4                             
#define OP_MREAD_BUFFER2        0xD6                             
#define OP_MWERASE_BUFFER1      0x83                             
#define OP_MWERASE_BUFFER2      0x86                             
#define OP_MWRITE_BUFFER1       0x88    
#define OP_MWRITE_BUFFER2       0x89    
                             
/* write to buffer, then write-erase to flash */                 
#define OP_PROGRAM_VIA_BUF1     0x82                             
#define OP_PROGRAM_VIA_BUF2     0x85                             
                             
/* compare buffer to flash */                             
#define OP_COMPARE_BUF1         0x60                             
#define OP_COMPARE_BUF2         0x61                             
                             
/* read flash to buffer, then write-erase to flash */            
#define OP_REWRITE_VIA_BUF1     0x58                             
#define OP_REWRITE_VIA_BUF2     0x59    

/* newer chips report JEDEC manufacturer and device IDs; chip
 * serial number and OTP bits; and per-sector writeprotect.  
 */                                                          
#define OP_READ_ID              0x9F                         
#define OP_READ_SECURITY        0x77                         
#define OP_WRITE_SECURITY       0x9A   


/*------------------------------ 全局变量 -------------------------------------
*/
int g_flash_is_ok;

/*------------------------------ 函数声明 -------------------------------------
*/
int _at45db321_identify(void);
int _at45db321_page_read(uint32_t offset,unsigned char * buffer,uint32_t length);
int _at45db321_page_write(uint32_t offset,unsigned char * buffer,uint32_t length);
int _at45db321_page_erase(uint32_t offset);
int _at45db321_wait_ready(unsigned char *p_status_register);

/*------------------------------ 外部函数 -------------------------------------
*/
/******************************************************************************
函数名称：	flash_init
函数版本：	01.01
创建作者:	   sunxi
创建日期：	2008-08-19
函数说明：	flash初始化。
参数说明：	无
返回值：  	成功返回0.
修改记录：	
*/
int flash_init(void)
{
//	GPIO_WP_FLASH_INIT();
	
	if(_at45db321_identify() == 0)
	{
		
		g_flash_is_ok = 1;
	}
	else
	{
		
		g_flash_is_ok = 0;
	}
	
	return 0;
}


int flash_exit(void)
{
	return 0;
}


/******************************************************************************
函数名称：	flash_read
函数版本：	01.01
创建作者:	   sunxi
创建日期：	2008-08-21
函数说明：	从FLASH中读出数据。
参数说明：	
	offset(in)：需要读的数据在FLASH中的偏移量，和length之和不能大于
				CFG_FLASH_SIZE.
	buffer(out):读出数据的buffer。
	length(in):	需要读出数据的长度，和offset之和不能大于CFG_FLASH_SIZE。
返回值：  	成功返回0.
修改记录：	
*/
int flash_read(uint32_t offset,unsigned char * buffer,uint32_t length)
{
	int ret;
	uint32_t length_once,length_spare;
	
	//检查FLASH是否OK
	if(g_flash_is_ok == 0)
	{
		
		return -1;
	}
	
	//检查参数
	if(buffer == 0 || (offset + length) > CFG_FLASH_SIZE)
	{
		
		return -1;
	}
	if(length == 0)
	{
		
		return 0;
	}
	
	//初始化length_spare	
	length_spare = length;
	
	//读起始不完整页
	length_once = CFG_FLASH_PAGE_SIZE - offset%CFG_FLASH_PAGE_SIZE;
	if(length_once > length_spare)
	{
		
		length_once = length_spare;
	}
	if(length_once)
	{
		ret = _at45db321_page_read(offset,buffer,length_once);
		if(ret != 0)
		{
			
			return ret;
		}
		
		offset += length_once;
		buffer += length_once;
		length_spare -= length_once;
	}
	
	//循环读完整页。
	length_once = CFG_FLASH_PAGE_SIZE;
	while(length_spare > CFG_FLASH_PAGE_SIZE)
	{
		ret = _at45db321_page_read(offset,buffer,length_once);
		if(ret != 0)
		{
			
			return ret;
		}
		
		offset += length_once;
		buffer += length_once;
		length_spare -= length_once;
	}
	
	//读末尾不完整页。
	length_once = length_spare;
	if(length_once)
	{
		ret = _at45db321_page_read(offset,buffer,length_once);
		if(ret != 0)
		{
			
			return ret;
		}
	}
			
	return 0;
}


/******************************************************************************
函数名称：	flash_write
函数版本：	01.01
创建作者:	   sunxi
创建日期：	2008-08-21
函数说明：	将数据写入FLASH中。
参数说明：	
	offset(in)：需要写入的数据在FLASH中的偏移量，和length之和不能大于
				CFG_FLASH_SIZE.
	buffer(in):写入数据的buffer。
	length(in):	需要写入数据的长度，和offset之和不能大于CFG_FLASH_SIZE。
返回值：  	成功返回0.
修改记录：	
*/
int flash_write(uint32_t offset,unsigned char * buffer,uint32_t length)
{
	int ret;
	uint32_t length_once,length_spare;
	
	//检查FLASH是否OK
	if(g_flash_is_ok == 0)
	{
		
		return -1;
	}
	
	//检查参数
	if(buffer == 0 || (offset + length) > CFG_FLASH_SIZE)
	{
		
		return -1;
	}
	
	if(length == 0)
	{
		
		return 0;
	}
	
	//初始化length_spare	
	length_spare = length;
	
	//写起始不完整页
	length_once = CFG_FLASH_PAGE_SIZE - offset%CFG_FLASH_PAGE_SIZE;
	if(length_once > length_spare)
	{
		
		length_once = length_spare;
	}
	if(length_once)
	{
		ret = _at45db321_page_write(offset,buffer,length_once);
		if(ret != 0)
		{
			
			return ret;
		}
		
		offset += length_once;
		buffer += length_once;
		length_spare -= length_once;
	}
	
	//循环写完整页。
	length_once = CFG_FLASH_PAGE_SIZE;
	while(length_spare > CFG_FLASH_PAGE_SIZE)
	{
		ret = _at45db321_page_write(offset,buffer,length_once);
		if(ret != 0)
		{
			
			return ret;
		}
		
		offset += length_once;
		buffer += length_once;
		length_spare -= length_once;
	}
	
	//写末尾不完整页。
	length_once = length_spare;
	if(length_once)
	{
		ret = _at45db321_page_write(offset,buffer,length_once);
		if(ret != 0)
		{
			
			return ret;
		}
	}
			
	return 0;
}


/******************************************************************************
函数名称：	flash_erase
函数版本：	01.01
创建作者:	   sunxi
创建日期：	2008-08-21
函数说明：	擦除FLASH中的指定区域。注意擦除是以页（CFG_FLASH_PAGE_SIZE）为单位
			擦除的，为了保证用户指定的区域一定被擦除，实际擦除的长度可能大于指定
			的长度，用户应小心处理这个问题。
参数说明：	
	offset(in)：需要擦除的页在FLASH中的偏移量，和length之和不能大于
				CFG_FLASH_SIZE.
	length(in):	需要擦除的长度，和offset之和不能大于CFG_FLASH_SIZE。
返回值：  	成功返回0.
修改记录：	
*/
int flash_erase(uint32_t offset,uint32_t length)
{
	uint32_t offset_last;
	
	//检查FLASH是否OK
	if(g_flash_is_ok == 0)
	{
		
		return -1;
	}
	
	//检查参数
	if((offset + length) > CFG_FLASH_SIZE)
	{
		
		return -1;
	}
	
	if(length == 0)
	{
		
		return 0;
	}
	
	//循环擦除指定空间
	offset_last = offset + length;
	offset -= offset%CFG_FLASH_PAGE_SIZE;
	while(offset < offset_last)
	{
		if(_at45db321_page_erase(offset) != 0)
		{
			
			return -2;
		}
		
		offset += CFG_FLASH_PAGE_SIZE;
	}
	
	return 0;
}


/******************************************************************************
函数名称：	at45db321_sector_protection
函数版本：	01.01
创建作者:	   sunxi
创建日期：	2008-10-10
函数说明：	启用FLASH的扇区保护功能。
参数说明：	无。
返回值：  	成功返回0.
修改记录：	
*/
int at45db321_sector_protection(void)
{
	int h_qspi,i,ret;
	unsigned char cmd[4];
	
	//获取总线
	h_qspi = dspi_open(DSPI_ID_FLASH);
	if(h_qspi < 0)	
	{
		
		return -1;
	}
	
	//解除写保护
//	GPIO_WP_FLASH_HIGH();
	ustimer_delay(1*USTIMER_US);
	
	//写命令和地址
	//擦除扇区保护寄存器命令(0x3d,0x2a,0x7f,0xcf)
	cmd[0] = 0x3d;
	cmd[1] = 0x2a;
	cmd[2] = 0x7f;
	cmd[3] = 0xcf;
	dspi_write(h_qspi,cmd,4);
	
	//关闭总线
	dspi_close(h_qspi);

	//等待flash操作完成
	ret = _at45db321_wait_ready(0);
	
	//写保护
//	GPIO_WP_FLASH_LOW();
	
	//开始确认保护是否完整
	//获取总线
	h_qspi = dspi_open(DSPI_ID_FLASH);
	if(h_qspi < 0)	
	{
		
		return -1;
	}
	
	//写命令和地址
	//读扇区保护寄存器命令(0x32,0xxx,0xxx,0xxx)
	cmd[0] = 0x32;
	cmd[1] = 0x0;
	cmd[2] = 0x0;
	cmd[3] = 0x0;
	dspi_write(h_qspi,cmd,4);
	
	//读出数据比较
	for(i=0; i<64; i++)
	{
		dspi_read(h_qspi,cmd,1);
		if(cmd[0] != 0xff)
		{
			
			ret = -2;
		}
	}
	
	//关闭总线
	dspi_close(h_qspi);
	
	if(ret != 0)
	{
		
		return -2;
	}
	
	return 0;						
}


/*------------------------------ 内部函数 -------------------------------------
*/
/******************************************************************************
函数名称：	_at45db321_wait_ready
函数版本：	01.01
创建作者:	   sunxi
创建日期：	2008-08-21
函数说明：	等待FLASH完成操作，如果需要返回状态寄存器的值
参数说明：	
	p_status_register(out)：如果不为0，返回状态寄存器的值
返回值：  	成功返回0.
修改记录：	
*/
int _at45db321_wait_ready(unsigned char *p_status_register)
{
	int h_qspi,ret;
	uint32_t us0,us;
	unsigned char c;			

	//获取总线
	h_qspi = dspi_open(DSPI_ID_FLASH);
	if(h_qspi < 0)	
	{
		
		return -1;
	}
	
	//写命令
	c = OP_READ_STATUS;
	dspi_write(h_qspi,&c,1);
	
	//超时等待就绪
	us0= ustimer_get_origin();
	while(1)
	{
		// 得到超时值
		us = ustimer_get_duration(us0);
		
		//读状态寄存器
		dspi_read(h_qspi,&c,1);
		if(c & 0x80)
		{
			ret = 0;
			break;
			
		}
		
		//最大BLOCK擦除时间为100ms
		if(us > 100*USTIMER_MS)
		{
			ret = -1;
			break;
		}
	}
	
	//需要的话，返回状态寄存器的值
	if(ret == 0 && p_status_register != 0)
	{
		
		dspi_read(h_qspi,p_status_register,1);
	}
		
	//关闭总线
	dspi_close(h_qspi);

	return ret;
}

/******************************************************************************
函数名称：	_at45db321_identify
函数版本：	01.01
创建作者:	   sunxi
创建日期：	2008-08-21
函数说明：	检查AT45DB321是否OK
参数说明：	无
返回值：  	成功返回0.
修改记录：	
*/
int _at45db321_identify(void)
{
	int h_qspi,ret;
	unsigned char  buffer[2];
//	unsigned char  dev_info[2] = {0x1F, 0x27};	
	unsigned char  dev_info[2] = {0x20, 0xba};	 //n25q
	
	h_qspi = dspi_open(DSPI_ID_FLASH);
	if(h_qspi < 0)	
	{
		
		return -1;
	}
	
	buffer[0] = OP_READ_ID;
	dspi_write(h_qspi,buffer,1);
	dspi_read(h_qspi,buffer,2);

	rt_printf("FLASH:ID0=%02x,ID1=%02x.\r\n",buffer[0],buffer[1]);
	
	if(memcmp(buffer,dev_info,2) != 0)
	{
		
		ret = -2;
	}
	else	
	{
		
		ret = 0;
	}
	
	dspi_close(h_qspi);
	
	return ret;					
}


/******************************************************************************
函数名称：	_at45db321_page_read
函数版本：	01.01
创建作者:	   sunxi
创建日期：	2008-08-21
函数说明：	从FLASH的一页中读出数据。
参数说明：	
	offset(in)：需要读的数据在FLASH中的偏移量。
	buffer(out):读出数据的buffer。
	length(in):	需要读出数据的长度。
返回值：  	成功返回0.
修改记录：	
*/
int _at45db321_page_read(uint32_t offset,unsigned char * buffer,uint32_t length)
{
	int h_qspi;
	uint32_t pa,ba;
	unsigned char cmd[4];
	
	//获取总线
	h_qspi = dspi_open(DSPI_ID_FLASH);
	if(h_qspi < 0)	
	{
		
		return -1;
	}
	
	//得到页地址和页内偏移
	pa = offset/CFG_FLASH_PAGE_SIZE;
	ba = offset%CFG_FLASH_PAGE_SIZE;
	
	//1 reserved bit + 13位的页面地址 + 10位的页内地址
	//R P12 P11 P10 P9 P8 P7 P6  |  P5 P4 P3 P2 P1 P0 B9 B8  |  B7 B6 B5 B4 B3 B2 B1 B0 	
	cmd[1] = (unsigned char)(pa>>6);
	cmd[2] = (unsigned char)(unsigned char)(pa<<2 | (ba>>8 & 0x3));
	cmd[3] = (unsigned char)ba;
	
	//写命令和地址
	cmd[0] = OP_READ_PAGE;
	dspi_write(h_qspi,cmd,4);
	
	//等待32个时钟周期
	dspi_dummy_byte(h_qspi,4);
	
	//读出数据
	dspi_read(h_qspi,buffer,length);
	
	//关闭总线
	dspi_close(h_qspi);
	
	return 0;					
}


/******************************************************************************
函数名称：	_at45db321_page_write
函数版本：	01.01
创建作者:	   sunxi
创建日期：	2008-08-21
函数说明：	将数据写入FLASH的一页中。
参数说明：	
	offset(in)：需要写入的数据在FLASH中的偏移量.
	buffer(in):写入数据的buffer。
	length(in):	需要写入数据的长度。
返回值：  	成功返回0.
修改记录：	
2010-07-16  sunxi	_at45db321_page_write函数以前是由OP_PROGRAM_VIA_BUF1命令
					完成，现改为由OP_WRITE_BUFFER1和OP_MWRITE_BUFFER1命令组合
					完成。见文件头说明
*/
int _at45db321_page_write(uint32_t offset,unsigned char * buffer,uint32_t length)
{
	int h_qspi,ret;
	uint32_t pa,ba;
	unsigned char cmd[4],status;
	
	//得到页地址和页内偏移
	pa = offset/CFG_FLASH_PAGE_SIZE;
	ba = offset%CFG_FLASH_PAGE_SIZE;
	
	//1 reserved bit + 13位的页面地址 + 10位的页内地址
	//R P12 P11 P10 P9 P8 P7 P6  |  P5 P4 P3 P2 P1 P0 B9 B8  |  B7 B6 B5 B4 B3 B2 B1 B0 	
	cmd[1] = (unsigned char)(pa>>6);
	cmd[2] = (unsigned char)(unsigned char)(pa<<2 | (ba>>8 & 0x3));
	cmd[3] = (unsigned char)ba;
	
//1.先将数据从FLASH中读出到BUFFER1
	//获取总线
	h_qspi = dspi_open(DSPI_ID_FLASH);
	if(h_qspi < 0)	
	{
		
		return -1;
	}
	
	//解除写保护
//	GPIO_WP_FLASH_HIGH();
	ustimer_delay(USTIMER_US);
	
	//写命令和地址
	cmd[0] = OP_TRANSFER_BUF1;
	dspi_write(h_qspi,cmd,4);
	
	//关闭总线
	dspi_close(h_qspi);

	//等待flash操作完成
	if(_at45db321_wait_ready(0) != 0)
	{
		
		return -2;
	}
	
//2. 将数据写入BUF
	//获取总线
	h_qspi = dspi_open(DSPI_ID_FLASH);
	if(h_qspi < 0)	
	{
		
		return -3;
	}
	
	//写命令和地址
	cmd[0] = OP_WRITE_BUFFER1;
	dspi_write(h_qspi,cmd,4);
	
	//写入数据
	dspi_write(h_qspi,buffer,length);
	
	//关闭总线
	dspi_close(h_qspi);
	
//3. 将数据从BUF写入FLASH
	//获取总线
	h_qspi = dspi_open(DSPI_ID_FLASH);
	if(h_qspi < 0)	
	{
		
		return -33;
	}
	
	//写命令和地址
	cmd[0] = OP_MWRITE_BUFFER1;
	dspi_write(h_qspi,cmd,4);
	
	//关闭总线
	dspi_close(h_qspi);
	
	//等待flash操作完成
	ret = _at45db321_wait_ready(0);
	
	//写保护
//	GPIO_WP_FLASH_LOW();
	
	if(ret != 0)
	{
		
		return -4;
	}

	
//4. 检查写入的数据是否正确
	//获取总线
	h_qspi = dspi_open(DSPI_ID_FLASH);
	if(h_qspi < 0)	
	{
		
		return -5;
	}
	
	//写命令和地址
	cmd[0] = OP_COMPARE_BUF1;
	dspi_write(h_qspi,cmd,4);
	
	//关闭总线
	dspi_close(h_qspi);
	
	//等待flash操作完成
	if(_at45db321_wait_ready(&status) != 0)
	{
		
		return -6;
	}
	
	//比较不正确的话，返回出错。
	if(status & 0x40)
	{
		
		return -7;
	}

	return 0;						
}


/******************************************************************************
函数名称：	_at45db321_page_erase
函数版本：	01.01
创建作者:	   sunxi
创建日期：	2008-08-21
函数说明：	擦除FLASH中的一页。
参数说明：	
	offset(in)：需要擦除的页在FLASH中的偏移量.
返回值：  	成功返回0.
修改记录：	
*/
int _at45db321_page_erase(uint32_t offset)
{
	int h_qspi,ret;
	uint32_t pa,ba;
	unsigned char cmd[4];
	
	//获取总线
	h_qspi = dspi_open(DSPI_ID_FLASH);
	if(h_qspi < 0)	
	{
		
		return -1;
	}
	
	//解除写保护
//	GPIO_WP_FLASH_HIGH();
	ustimer_delay(USTIMER_US);
	

	//得到页地址和页内偏移
	pa = offset/CFG_FLASH_PAGE_SIZE;
	ba = offset%CFG_FLASH_PAGE_SIZE;
	
	//1 reserved bit + 13位的页面地址 + 10位的页内地址
	//R P12 P11 P10 P9 P8 P7 P6  |  P5 P4 P3 P2 P1 P0 B9 B8  |  B7 B6 B5 B4 B3 B2 B1 B0 	
	cmd[1] = (unsigned char)(pa>>6);
	cmd[2] = (unsigned char)(unsigned char)(pa<<2 | (ba>>8 & 0x3));
	cmd[3] = (unsigned char)ba;
	
	//写命令和地址
	cmd[0] = OP_ERASE_PAGE;
	dspi_write(h_qspi,cmd,4);
	
	//关闭总线
	dspi_close(h_qspi);

	//等待flash操作完成
	ret = _at45db321_wait_ready(0);
	
	//写保护
//	GPIO_WP_FLASH_LOW();
	
	if(ret != 0)
	{
		
		return -2;
	}
	
	return 0;						
}


/*------------------------------ 测试函数 -------------------------------------
*/
#ifdef _DEBUG_FLASH

#define FLASH_BLOCK_SIZE CFG_FLASH_PAGE_SIZE
int flash_test(void)
{
	int ret;
	u32 i, j;
	uint32_t block_size,blocks;
	uint32_t us0,us;
	unsigned int gTestBuf[FLASH_BLOCK_SIZE/sizeof(int)];
	
	block_size = FLASH_BLOCK_SIZE;	
	blocks = CFG_FLASH_SIZE/FLASH_BLOCK_SIZE;
	//	blocks = 1;
	
	rt_printf("flash_test begin!\r\n");
	rt_printf("block_size=%lu,blocks=%lu.\r\n",block_size,blocks);
	
//	dspi_init();
	
	us0 = ustimer_get_origin();
	//while(1)
	ret = _at45db321_identify();
	us = ustimer_get_duration(us0);
	if(ret != 0)
	{
		rt_printf("at45db321d_Identify error!(ret=%d)!\r\n",ret);
		return -1;
	}
	rt_printf("at45db321d_Identify ok!(us=%lu)!\r\n",us);

	//启用FLASH的扇区保护功能
	ret = at45db321_sector_protection();
	rt_printf("at45db321_sector_protection:ret=%d!\r\n",ret);

	//擦除整个flash。
	us0 = ustimer_get_origin();
	ret = flash_erase(0,blocks*block_size);
	us = ustimer_get_duration(us0);
	rt_printf("at45DB321Erase (us=%lu,ret=%d)!\r\n",us,ret);

	//确认擦除OK
	us0 = ustimer_get_origin();
	for(i=0;i<blocks;i++)
	{
		ret = flash_read(i*block_size,(unsigned char *)gTestBuf,block_size);
		if(ret != 0)
		{
			rt_printf("at45db321Read failure(i=%lu,ret=%d)!\r\n",i,ret);
			return -1;
		}
		for(j=0;j<block_size/sizeof(int);j++)
		{
			
			if(gTestBuf[j] != 0xffffffff)
			{
					rt_printf("at45DB321Erase verify failure(i=%lu,j=%lu,data=0x%08x)!\r\n",i,j,gTestBuf[j]);
					return -1;
			}
		}
	}
	us = ustimer_get_duration(us0);
	rt_printf("at45DB321Erase verify ok(us=%lu)!\r\n",us);

	//整个flash写0
	memset(gTestBuf,0,sizeof(gTestBuf));
	us0 = ustimer_get_origin();
	for(i=0;i<blocks;i++)
	{
		ret = flash_write(i*block_size,(unsigned char *)gTestBuf,block_size);
		if(ret != 0)
		{
			rt_printf("at45DB321Write failure(i=%lu,ret=%d)!\r\n",i,ret);
			return -1;
		}
	}
	us = ustimer_get_duration(us0);
	rt_printf("at45DB321Write ok(us=%lu)!\r\n",us);

	//检查整个flash是否全为0
	us0 = ustimer_get_origin();
	for(i=0;i<blocks;i++)
	{
		ret = flash_read(i*block_size,(unsigned char *)gTestBuf,block_size);
		if(ret != 0)
		{
			rt_printf("at45db321Read failure(i=%lu,ret=%d)!\r\n",i,ret);
			return -1;
		}
		
		for(j=0;j<block_size/sizeof(int);j++)
		{
			
			if(gTestBuf[j] != 0)
			{
					rt_printf("at45DB321Write verify failure(i=%lu,j=%lu,data=0x%08x)!\r\n",i,j,gTestBuf[j]);
					return -1;
			}
		}
	}
	us = ustimer_get_duration(us0);
	rt_printf("at45DB321Write verify ok(us=%lu)!\r\n",us);

	//擦除整个flash
	us0 = ustimer_get_origin();
	ret = flash_erase(0,blocks*block_size);
	us = ustimer_get_duration(us0);
	rt_printf("at45DB321Erase (us=%lu,ret=%d)!\r\n",us,ret);

	//确认擦除OK
	us0 = ustimer_get_origin();
	for(i=0;i<blocks;i++)
	{
		ret = flash_read(i*block_size,(unsigned char *)gTestBuf,block_size);
		if(ret != 0)
		{
			rt_printf("at45db321Read failure(i=%lu,ret=%d)!\r\n",i,ret);
			return -1;
		}
		
		for(j=0;j<block_size/sizeof(int);j++)
		{
			
			if(gTestBuf[j] != 0xffffffff)
			{
					rt_printf("at45DB321Erase verify failure(i=%lu,j=%lu,data=0x%08x)!\r\n",i,j,gTestBuf[j]);
					return -1;
			}
		}
	}
	us = ustimer_get_duration(us0);
	rt_printf("at45DB321Erase verify ok(us=%lu)!\r\n",us);

	//整个flash写测试数据
	us0 = ustimer_get_origin();
	for(i=0;i<blocks;i++)
	{
		//初始化写数据
		for(j=0;j<block_size/sizeof(int);j++)
		{
			gTestBuf[j] = i*block_size/sizeof(int) + j;
		}

		//写
		ret = flash_write(i*block_size,(unsigned char *)gTestBuf,block_size);
		if(ret != 0)
		{
			rt_printf("at45DB321Write failure(i=%lu,ret=%d)!\r\n",i,ret);
			return -1;
		}
	}
	us = ustimer_get_duration(us0);
	rt_printf("at45DB321Write ok(us=%lu)!\r\n",us);

	//读出并校验数据
	us0 = ustimer_get_origin();
	for(i=0;i<blocks;i++)
	{
		ret = flash_read(i*block_size,(unsigned char *)gTestBuf,block_size);
		if(ret != 0)
		{
			rt_printf("at45db321Read failure(i=%lu,ret=%d)!\r\n",i,ret);
			return -1;
		}
		
		for(j=0;j<block_size/sizeof(int);j++)
			if(gTestBuf[j] != i*block_size/sizeof(int) + j)
			{
					rt_printf("at45DB321Write verify failure(i=%lu,j=%lu,data=0x%08x)!\r\n",i,j,gTestBuf[j]);
					return -1;
			}
	}
	us = ustimer_get_duration(us0);
	rt_printf("at45DB321Write verify ok(us=%lu)!\r\n",us);
		
	rt_printf("flash_test end!\r\n\n");
	
	return 0;
}

#endif

