STM32F767 NAND FLASH MT29F4G08 FTL

参考正点原子例程,主要对bsp_nand.c做小幅修改

/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

#include "delay.h"
#include "bsp_lcd.h"
#include "bsp_printf.h"
#include "bsp_sdram.h"
#include "bsp_malloc.h"
#include "bsp_ftl.h"
#include "bsp_nand.h"
#include "bsp_key.h"

/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

uint16_t FBuffer[LCD_WIDTH*LCD_HEIGHT] __attribute__((at(0xC0000000)));
uint32_t POINT_COLOR;		//画笔颜色
uint32_t BACK_COLOR;  	//背景色 

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

DMA2D_HandleTypeDef hdma2d;

LTDC_HandleTypeDef hltdc;

UART_HandleTypeDef huart1;

NAND_HandleTypeDef hnand1;
SDRAM_HandleTypeDef hsdram1;

/* USER CODE BEGIN PV */


/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_FMC_Init(void);
static void MX_LTDC_Init(void);
static void MX_DMA2D_Init(void);
static void MX_USART1_UART_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */


/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_FMC_Init();
  MX_LTDC_Init();
  MX_DMA2D_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
	
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET);
	delay_init(216);
	delay_ms(5000);
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_SET);//打开LCD背光
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);
	
	Sdram_Init_Sequence(); 
	
	my_mem_init(SRAMIN);            //初始化内部内存池
	my_mem_init(SRAMEX);            //初始化外部SDRAM内存池
	
	POINT_COLOR=RED;
	BACK_COLOR = WHITE;
	LTDC_Clear(WHITE);
	LCD_ShowString(30,50,200,16,16,"Apollo STM32F4/F7"); 
	LCD_ShowString(30,70,200,16,16,"NAND TEST");	
	LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");
	LCD_ShowString(30,110,200,16,16,"2016/7/15");	 	 
	LCD_ShowString(30,130,200,16,16,"KEY0:Read Sector 2");
	LCD_ShowString(30,150,200,16,16,"KEY1:Write Sector 2");
	LCD_ShowString(30,170,200,16,16,"KEY2:Recover Sector 2"); 
	
	while(FTL_Init())			    //检测NAND FLASH,并初始化FTL
	{
		LCD_ShowString(30,190,200,16,16,"NAND Error!");
		delay_ms(500);				 
		LCD_ShowString(30,190,200,16,16,"Please Check");
		delay_ms(500);	
	}
	uint8_t *buf;
	uint8_t *backbuf;	
	uint8_t key, t;
	uint16_t i;
	backbuf=mymalloc(SRAMIN,NAND_ECC_SECTOR_SIZE);	//申请一个扇区的缓存
	buf=mymalloc(SRAMIN,NAND_ECC_SECTOR_SIZE);		//申请一个扇区的缓存
 	POINT_COLOR=BLUE;						//设置字体为蓝色 
	sprintf((char*)buf,"NAND Size:%dMB",(nand_dev.block_totalnum/1024)*(nand_dev.page_mainsize/1024)*nand_dev.block_pagenum);
	LCD_ShowString(30,190,200,16,16,buf);	//显示NAND容量  
	FTL_ReadSectors(backbuf,2,NAND_ECC_SECTOR_SIZE,1);//预先读取扇区0到备份区域,防止乱写导致文件系统损坏.
	while(1)
	{
		key=KEY_Scan(0);
		switch(key)
		{
			case KEY0_PRES://KEY0按下,读取sector
				key=FTL_ReadSectors(buf,2,NAND_ECC_SECTOR_SIZE,1);//读取扇区
				if(key==0)//读取成功
				{
					LCD_ShowString(30,210,200,16,16,"USART1 Sending Data...  ");
					printf("Sector 2 data is:\r\n");
					for(i=0;i<NAND_ECC_SECTOR_SIZE;i++)
					{
						printf("%x ",buf[i]);//输出数据
					}
					printf("\r\ndata end.\r\n");
					LCD_ShowString(30,210,200,16,16,"USART1 Send Data Over!  "); 
				}
			break;
				
			case KEY1_PRES://KEY1按下,写入sector
				for(i=0;i<NAND_ECC_SECTOR_SIZE;i++)buf[i]=i+t;	//填充数据(随机的,根据t的值来确定) 
				LCD_ShowString(30,210,210,16,16,"Writing data to sector..");
				key=FTL_WriteSectors(buf,2,NAND_ECC_SECTOR_SIZE,1);//写入扇区
				if(key==0)LCD_ShowString(30,210,200,16,16,"Write data successed    ");//写入成功
				else LCD_ShowString(30,210,200,16,16,"Write data failed       ");//写入失败
			break;
			
			case KEY2_PRES://KEY2按下,恢复sector的数据
				LCD_ShowString(30,210,210,16,16,"Recovering data...      ");
				key=FTL_WriteSectors(backbuf,2,NAND_ECC_SECTOR_SIZE,1);//写入扇区
				if(key==0)LCD_ShowString(30,210,200,16,16,"Recovering data OK      ");//恢复成功
				else LCD_ShowString(30,210,200,16,16,"Recovering data failed  ");//恢复失败
			break;
		}
		t++;
		delay_ms(10);
	}  
	
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
	
  while (1)
  {
		printf("hello,world!\n");
		delay_ms(1000);	
		
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		
  }
  /* USER CODE END 3 */
}
/* FMC initialization function */
static void MX_FMC_Init(void)
{

  /* USER CODE BEGIN FMC_Init 0 */

  /* USER CODE END FMC_Init 0 */

  FMC_NAND_PCC_TimingTypeDef ComSpaceTiming = {0};
  FMC_NAND_PCC_TimingTypeDef AttSpaceTiming = {0};
  FMC_SDRAM_TimingTypeDef SdramTiming = {0};

  /* USER CODE BEGIN FMC_Init 1 */

  /* USER CODE END FMC_Init 1 */

  /** Perform the NAND1 memory initialization sequence
  */
  hnand1.Instance = FMC_NAND_DEVICE;
  /* hnand1.Init */
  hnand1.Init.NandBank = FMC_NAND_BANK3;
  hnand1.Init.Waitfeature = FMC_NAND_WAIT_FEATURE_DISABLE;
  hnand1.Init.MemoryDataWidth = FMC_NAND_MEM_BUS_WIDTH_8;
  hnand1.Init.EccComputation = FMC_NAND_ECC_DISABLE;
  hnand1.Init.ECCPageSize = FMC_NAND_ECC_PAGE_SIZE_512BYTE;
  hnand1.Init.TCLRSetupTime = 2;
  hnand1.Init.TARSetupTime = 2;
  /* hnand1.Config */
  hnand1.Config.PageSize = 2048;
  hnand1.Config.SpareAreaSize = 64;
  hnand1.Config.BlockSize = 64;
  hnand1.Config.BlockNbr = 4096;
  hnand1.Config.PlaneNbr = 2;
  hnand1.Config.PlaneSize = 2048;
  hnand1.Config.ExtraCommandEnable = DISABLE;
  /* ComSpaceTiming */
  ComSpaceTiming.SetupTime = 2;
  ComSpaceTiming.WaitSetupTime = 3;
  ComSpaceTiming.HoldSetupTime = 3;
  ComSpaceTiming.HiZSetupTime = 2;
  /* AttSpaceTiming */
  AttSpaceTiming.SetupTime = 2;
  AttSpaceTiming.WaitSetupTime = 3;
  AttSpaceTiming.HoldSetupTime = 3;
  AttSpaceTiming.HiZSetupTime = 2;

  if (HAL_NAND_Init(&hnand1, &ComSpaceTiming, &AttSpaceTiming) != HAL_OK)
  {
    Error_Handler( );
  }

  /** Perform the SDRAM1 memory initialization sequence
  */
  hsdram1.Instance = FMC_SDRAM_DEVICE;
  /* hsdram1.Init */
  hsdram1.Init.SDBank = FMC_SDRAM_BANK1;
  hsdram1.Init.ColumnBitsNumber = FMC_SDRAM_COLUMN_BITS_NUM_9;
  hsdram1.Init.RowBitsNumber = FMC_SDRAM_ROW_BITS_NUM_13;
  hsdram1.Init.MemoryDataWidth = FMC_SDRAM_MEM_BUS_WIDTH_16;
  hsdram1.Init.InternalBankNumber = FMC_SDRAM_INTERN_BANKS_NUM_4;
  hsdram1.Init.CASLatency = FMC_SDRAM_CAS_LATENCY_3;
  hsdram1.Init.WriteProtection = FMC_SDRAM_WRITE_PROTECTION_DISABLE;
  hsdram1.Init.SDClockPeriod = FMC_SDRAM_CLOCK_PERIOD_2;
  hsdram1.Init.ReadBurst = FMC_SDRAM_RBURST_ENABLE;
  hsdram1.Init.ReadPipeDelay = FMC_SDRAM_RPIPE_DELAY_0;
  /* SdramTiming */
  SdramTiming.LoadToActiveDelay = 2;
  SdramTiming.ExitSelfRefreshDelay = 7;
  SdramTiming.SelfRefreshTime = 4;
  SdramTiming.RowCycleDelay = 7;
  SdramTiming.WriteRecoveryTime = 4;
  SdramTiming.RPDelay = 2;
  SdramTiming.RCDDelay = 2;

  if (HAL_SDRAM_Init(&hsdram1, &SdramTiming) != HAL_OK)
  {
    Error_Handler( );
  }

  /* USER CODE BEGIN FMC_Init 2 */

  /* USER CODE END FMC_Init 2 */
}
static void HAL_FMC_MspInit(void){
  /* USER CODE BEGIN FMC_MspInit 0 */

  /* USER CODE END FMC_MspInit 0 */
  GPIO_InitTypeDef GPIO_InitStruct ={0};
  if (FMC_Initialized) {
    return;
  }
  FMC_Initialized = 1;

  /* Peripheral clock enable */
  __HAL_RCC_FMC_CLK_ENABLE();

  /** FMC GPIO Configuration
  PF0   ------> FMC_A0
  PF1   ------> FMC_A1
  PF2   ------> FMC_A2
  PF3   ------> FMC_A3
  PF4   ------> FMC_A4
  PF5   ------> FMC_A5
  PC0   ------> FMC_SDNWE
  PC2   ------> FMC_SDNE0
  PC3   ------> FMC_SDCKE0
  PF11   ------> FMC_SDNRAS
  PF12   ------> FMC_A6
  PF13   ------> FMC_A7
  PF14   ------> FMC_A8
  PF15   ------> FMC_A9
  PG0   ------> FMC_A10
  PG1   ------> FMC_A11
  PE7   ------> FMC_D4
  PE8   ------> FMC_D5
  PE9   ------> FMC_D6
  PE10   ------> FMC_D7
  PE11   ------> FMC_D8
  PE12   ------> FMC_D9
  PE13   ------> FMC_D10
  PE14   ------> FMC_D11
  PE15   ------> FMC_D12
  PD8   ------> FMC_D13
  PD9   ------> FMC_D14
  PD10   ------> FMC_D15
  PD11   ------> FMC_CLE
  PD12   ------> FMC_ALE
  PD14   ------> FMC_D0
  PD15   ------> FMC_D1
  PG2   ------> FMC_A12
  PG4   ------> FMC_BA0
  PG5   ------> FMC_BA1
  PG8   ------> FMC_SDCLK
  PD0   ------> FMC_D2
  PD1   ------> FMC_D3
  PD4   ------> FMC_NOE
  PD5   ------> FMC_NWE
  PG9   ------> FMC_NCE
  PG15   ------> FMC_SDNCAS
  PE0   ------> FMC_NBL0
  PE1   ------> FMC_NBL1
  */
  GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3
                          |GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_11|GPIO_PIN_12
                          |GPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.Alternate = GPIO_AF12_FMC;
  HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_2|GPIO_PIN_3;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.Alternate = GPIO_AF12_FMC;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_4
                          |GPIO_PIN_5|GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_15;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.Alternate = GPIO_AF12_FMC;
  HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = GPIO_PIN_7|GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10
                          |GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13|GPIO_PIN_14
                          |GPIO_PIN_15|GPIO_PIN_0|GPIO_PIN_1;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.Alternate = GPIO_AF12_FMC;
  HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11
                          |GPIO_PIN_12|GPIO_PIN_14|GPIO_PIN_15|GPIO_PIN_0
                          |GPIO_PIN_1|GPIO_PIN_4|GPIO_PIN_5;
  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.Alternate = GPIO_AF12_FMC;
  HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);

  /* USER CODE BEGIN FMC_MspInit 1 */

  /* USER CODE END FMC_MspInit 1 */
}
#ifndef __BSP_FTL_H
#define __BSP_FTL_H
#include "main.h"
//	 
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32开发板
//NAND FLASH FTL层算法代码	   
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2016/1/15
//版本:V1.3
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//********************************************************************************
//升级说明
//V1.1 20160124
//修改FTL_CopyAndWriteToBlock和FTL_WriteSectors函数,提高非0XFF时的写入速度.  
//V1.2 20160520
//1,修改FTL_ReadSectors,增加ECC出错判断,检测坏块处理,并增加多块连读,提高速度
//2,新增FTL_BlockCompare和FTL_SearchBadBlock函数,用于搜寻坏块
//3,修改FTL_Format坏块检测方式,增加FTL_USE_BAD_BLOCK_SEARCH宏
//V1.3 20160530
//修改当1bit ECC错误出现时,读取2次,来确认1bit 错误,以防错误的修改数据
// 	

//坏块搜索控制
//如果设置为1,将在FTL_Format的时候,搜寻坏块,耗时久(512M,3分钟以上),且会导致RGB屏乱闪
#define FTL_USE_BAD_BLOCK_SEARCH		0		//定义是否使用坏块搜索



uint8_t FTL_Init(void); 
void FTL_BadBlockMark(uint32_t blocknum);
uint8_t FTL_CheckBadBlock(uint32_t blocknum); 
uint8_t FTL_UsedBlockMark(uint32_t blocknum);
uint32_t FTL_FindUnusedBlock(uint32_t sblock,uint8_t flag);
uint32_t FTL_FindSamePlaneUnusedBlock(uint32_t sblock);
uint8_t FTL_CopyAndWriteToBlock(uint32_t Source_PageNum,uint16_t ColNum,uint8_t *pBuffer,uint32_t NumByteToWrite);
uint16_t FTL_LBNToPBN(uint32_t LBNNum); 
uint8_t FTL_WriteSectors(uint8_t *pBuffer,uint32_t SectorNo,uint16_t SectorSize,uint32_t SectorCount);
uint8_t FTL_ReadSectors(uint8_t *pBuffer,uint32_t SectorNo,uint16_t SectorSize,uint32_t SectorCount);
uint8_t FTL_CreateLUT(uint8_t mode);
uint8_t FTL_BlockCompare(uint32_t blockx,uint32_t cmpval);
uint32_t FTL_SearchBadBlock(void);
uint8_t FTL_Format(void); 
#endif


#include "bsp_ftl.h"
#include "string.h"
#include "bsp_malloc.h"
#include "bsp_nand.h"
#include "bsp_printf.h"
//	 
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32开发板
//NAND FLASH FTL层算法代码	   
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2016/1/15
//版本:V1.3
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//********************************************************************************
//升级说明
//V1.1 20160124
//修改FTL_CopyAndWriteToBlock和FTL_WriteSectors函数,提高非0XFF时的写入速度.  
//V1.2 20160520
//1,修改FTL_ReadSectors,增加ECC出错判断,检测坏块处理,并增加多块连读,提高速度
//2,新增FTL_BlockCompare和FTL_SearchBadBlock函数,用于搜寻坏块
//3,修改FTL_Format坏块检测方式,增加FTL_USE_BAD_BLOCK_SEARCH宏
//V1.3 20160530
//修改当1bit ECC错误出现时,读取2次,来确认1bit 错误,以防错误的修改数据
// 	

//每个块,第一个page的spare区,前四个字节的含义:
//第一个字节,表示该块是否是坏块:0XFF,正常块;其他值,坏块.
//第二个字节,表示该块是否被用过:0XFF,没有写过数据;0XCC,写过数据了.
//第三和第四个字节,表示该块所属的逻辑块编号. 

//每个page,spare区16字节以后的字节含义:
//第十六字节开始,后续每4个字节用于存储一个扇区(大小:NAND_ECC_SECTOR_SIZE)的ECC值,用于ECC校验


//FTL层初始化
//返回值:0,正常
//    其他,失败
uint8_t FTL_Init(void)
{
	uint8_t temp;
	if(NAND_Init())return 1;									//初始化NAND FLASH
	if(nand_dev.lut)myfree(SRAMIN,nand_dev.lut);
	nand_dev.lut=mymalloc(SRAMIN,(nand_dev.block_totalnum)*2); 	//给LUT表申请内存
	memset(nand_dev.lut,0,nand_dev.block_totalnum*2);			//全部清理
	if(!nand_dev.lut)return 1;				//内存申请失败 
	temp=FTL_CreateLUT(1);
	if(temp) 
	{   
		printf("format nand flash...\r\n");
		temp=FTL_Format();     //格式化NAND
		if(temp)
		{
			printf("format failed!\r\n");
			return 2;
		}
	}else 	//创建LUT表成功
	{
		printf("total block num:%d\r\n",nand_dev.block_totalnum);
		printf("good block num:%d\r\n",nand_dev.good_blocknum);
		printf("valid block num:%d\r\n",nand_dev.valid_blocknum);
	}
	return 0;
} 

//标记某一个块为坏块
//blocknum:块编号,范围:0~(block_totalnum-1)
void FTL_BadBlockMark(uint32_t blocknum)
{
	uint32_t temp=0XAAAAAAAA;//坏块标记mark,任意值都OK,只要不是0XFF.这里写前4个字节,方便FTL_FindUnusedBlock函数检查坏块.(不检查备份区,以提高速度)
	NAND_WriteSpare(blocknum*nand_dev.block_pagenum,0,(uint8_t*)&temp,4);	//在第一个page的spare区,第一个字节做坏块标记(前4个字节都写)
	NAND_WriteSpare(blocknum*nand_dev.block_pagenum+1,0,(uint8_t*)&temp,4);	//在第二个page的spare区,第一个字节做坏块标记(备份用,前4个字节都写)
} 
//检查某一块是否是坏块
//blocknum:块编号,范围:0~(block_totalnum-1)
//返回值:0,好块
//	  其他,坏块
uint8_t FTL_CheckBadBlock(uint32_t blocknum)
{
	uint8_t flag=0; 
	NAND_ReadSpare(blocknum*nand_dev.block_pagenum,0,&flag,1);//读取坏块标志
	if(flag==0XFF)//好块?,读取备份区坏块标记
	{
		NAND_ReadSpare(blocknum*nand_dev.block_pagenum+1,0,&flag,1);//读取备份区坏块标志
		if(flag==0XFF)return 0;	//好块
		else return 1;  		//坏块
	}   
	return 2; 
}
//标记某一个块已经使用
//blocknum:块编号,范围:0~(block_totalnum-1)
//返回值:0,成功
//    其他,失败
uint8_t FTL_UsedBlockMark(uint32_t blocknum)
{
	uint8_t Usedflag=0XCC;
	uint8_t temp=0;
	temp=NAND_WriteSpare(blocknum*nand_dev.block_pagenum,1,(uint8_t*)&Usedflag,1);//写入块已经被使用标志
	return temp;
}   
//从给定的块开始找到往前找到一个未被使用的块(指定奇数/偶数)
//sblock:开始块,范围:0~(block_totalnum-1)
//flag:0,偶数快;1,奇数块.
//返回值:0XFFFFFFFF,失败
//           其他值,未使用块号
uint32_t FTL_FindUnusedBlock(uint32_t sblock,uint8_t flag)
{
	uint32_t temp=0;
	uint32_t blocknum=0; 
	for(blocknum=sblock+1;blocknum>0;blocknum--)
	{
		if(((blocknum-1)%2)==flag)//奇偶合格,才检测
		{
			NAND_ReadSpare((blocknum-1)*nand_dev.block_pagenum,0,(uint8_t*)&temp,4);//读块是否被使用标记
			if(temp==0XFFFFFFFF)return(blocknum-1);//找到一个空块,返回块编号
		}
	}
	return 0XFFFFFFFF;	//未找到空余块
    
} 
//查找与给定块在同一个plane内的未使用的块
//sblock:给定块,范围:0~(block_totalnum-1)
//返回值:0XFFFFFFFF,失败
//           其他值,未使用块号
uint32_t FTL_FindSamePlaneUnusedBlock(uint32_t sblock)
{
	static uint32_t curblock=0XFFFFFFFF;
	uint32_t unusedblock=0;  
	if(curblock>(nand_dev.block_totalnum-1))curblock=nand_dev.block_totalnum-1;//超出范围了,强制从最后一个块开始
	unusedblock=FTL_FindUnusedBlock(curblock,sblock%2);					//从当前块,开始,向前查找空余块 
	if(unusedblock==0XFFFFFFFF&&curblock<(nand_dev.block_totalnum-1))	//未找到,且不是从最末尾开始找的
	{
		curblock=nand_dev.block_totalnum-1;								//强制从最后一个块开始
		unusedblock=FTL_FindUnusedBlock(curblock,sblock%2);				//从最末尾开始,重新找一遍  
	}
	if(unusedblock==0XFFFFFFFF)return 0XFFFFFFFF;						//找不到空闲block 
	curblock=unusedblock;												//当前块号等于未使用块编号.下次则从此处开始查找
	return unusedblock;													//返回找到的空闲block
}    
//将一个块的数据拷贝到另一块,并且可以写入数据 
//Source_PageNo:要写入数据的页地址,范围:0~(block_pagenum*block_totalnum-1)
//ColNum:要写入的列开始地址(也就是页内地址),范围:0~(page_totalsize-1)
//pBuffer:要写入的数据 
//NumByteToWrite:要写入的字节数,该值不能超过块内剩余容量大小
//返回值:0,成功
//    其他,失败
uint8_t FTL_CopyAndWriteToBlock(uint32_t Source_PageNum,uint16_t ColNum,uint8_t *pBuffer,uint32_t NumByteToWrite)
{
	uint16_t i=0,temp=0,wrlen;
	uint32_t source_block=0,pageoffset=0;
	uint32_t unusedblock=0; 
	source_block=Source_PageNum/nand_dev.block_pagenum;	//获得页所在的块号
	pageoffset=Source_PageNum%nand_dev.block_pagenum;	//获得页在所在块内的偏移 
	retry:      
	unusedblock=FTL_FindSamePlaneUnusedBlock(source_block);//查找与源块在一个plane的未使用块
	if(unusedblock>nand_dev.block_totalnum)return 1;	//当找到的空余块号大于块总数量的话肯定是出错了
	for(i=0;i<nand_dev.block_pagenum;i++)				//将一个块的数据复制到找到的未使用块中
	{                                                                                                                                                                                                                                                                                                                                                                                                                                                       
		if(i>=pageoffset&&NumByteToWrite)				//数据要写入到当前页
		{ 
			if(NumByteToWrite>(nand_dev.page_mainsize-ColNum))//要写入的数据,超过了当前页的剩余数据
			{
				wrlen=nand_dev.page_mainsize-ColNum;	//写入长度等于当前页剩余数据长度
			}else wrlen=NumByteToWrite;					//写入全部数据 
			temp=NAND_CopyPageWithWrite(source_block*nand_dev.block_pagenum+i,unusedblock*nand_dev.block_pagenum+i,ColNum,pBuffer,wrlen);
			ColNum=0;						//列地址归零
			pBuffer+=wrlen;					//写地址偏移
			NumByteToWrite-=wrlen;			//写入数据减少			
		}else								//无数据写入,直接拷贝即可
		{
			temp=NAND_CopyPageWithoutWrite(source_block*nand_dev.block_pagenum+i,unusedblock*nand_dev.block_pagenum+i);
		}
		if(temp)							//返回值非零,当坏块处理
		{ 
			FTL_BadBlockMark(unusedblock);	//标记为坏块
			FTL_CreateLUT(1);				//重建LUT表
			goto retry;
		}
	} 
	if(i==nand_dev.block_pagenum) 			//拷贝完成
	{
		FTL_UsedBlockMark(unusedblock);		//标记块已经使用	
		NAND_EraseBlock(source_block);		//擦除源块
		//printf("\r\ncopy block %d to block %d\r\n",source_block,unusedblock);//打印调试信息
		for(i=0;i<nand_dev.block_totalnum;i++)	//修正LUT表,用unusedblock替换source_block
		{
			if(nand_dev.lut[i]==source_block)
			{
				nand_dev.lut[i]=unusedblock;
				break;
			}
		}  
	}
	return 0;                               //成功
}   
//逻辑块号转换为物理块号
//LBNNum:逻辑块编号
//返回值:物理块编号
uint16_t FTL_LBNToPBN(uint32_t LBNNum)
{
	uint16_t PBNNo=0;
	//当逻辑块号大于有效块数的时候返回0XFFFF
	if(LBNNum>nand_dev.valid_blocknum)return 0XFFFF;
	PBNNo=nand_dev.lut[LBNNum];
	return PBNNo;
}
//写扇区(支持多扇区写),FATFS文件系统使用
//pBuffer:要写入的数据
//SectorNo:起始扇区号
//SectorSize:扇区大小(不能大于NAND_ECC_SECTOR_SIZE定义的大小,否则会出错!!)
//SectorCount:要写入的扇区数量
//返回值:0,成功
//	  其他,失败
uint8_t FTL_WriteSectors(uint8_t *pBuffer,uint32_t SectorNo,uint16_t SectorSize,uint32_t SectorCount)
{
	uint8_t flag=0;
	uint16_t temp;
	uint32_t i=0;
	uint16_t wsecs;		//写页大小
	uint32_t wlen;		//写入长度
	uint32_t LBNNo;      //逻辑块号
	uint32_t PBNNo;      //物理块号
	uint32_t PhyPageNo;  //物理页号
	uint32_t PageOffset; //页内偏移地址
	uint32_t BlockOffset;//块内偏移地址
	uint32_t markdpbn=0XFFFFFFFF;		//标记了的物理块编号  
	for(i=0;i<SectorCount;i++)
	{
		LBNNo=(SectorNo+i)/(nand_dev.block_pagenum*(nand_dev.page_mainsize/SectorSize));//根据逻辑扇区号和扇区大小计算出逻辑块号
		PBNNo=FTL_LBNToPBN(LBNNo);					//将逻辑块转换为物理块
		if(PBNNo>=nand_dev.block_totalnum)return 1;	//物理块号大于NAND FLASH的总块数,则失败. 
		BlockOffset=((SectorNo+i)%(nand_dev.block_pagenum*(nand_dev.page_mainsize/SectorSize)))*SectorSize;//计算块内偏移
		PhyPageNo=PBNNo*nand_dev.block_pagenum+BlockOffset/nand_dev.page_mainsize;	//计算出物理页号
		PageOffset=BlockOffset%nand_dev.page_mainsize;								//计算出页内偏移地址 
		temp=nand_dev.page_mainsize-PageOffset;	//page内剩余字节数
		temp/=SectorSize;						//可以连续写入的sector数 
		wsecs=SectorCount-i;					//还剩多少个sector要写
		if(wsecs>=temp)wsecs=temp;				//大于可连续写入的sector数,则写入temp个扇区  
		wlen=wsecs*SectorSize;					//每次写wsecs个sector  
		//读出写入大小的内容判断是否全为0XFF
		flag=NAND_ReadPageComp(PhyPageNo,PageOffset,0XFFFFFFFF,wlen/4,&temp);		//读一个wlen/4大小个数据,并与0XFFFFFFFF对比
		if(flag)return 2;						//读写错误,坏块 
		if(temp==(wlen/4)) flag=NAND_WritePage(PhyPageNo,PageOffset,pBuffer,wlen);	//全为0XFF,可以直接写数据
		else flag=1;							//不全是0XFF,则另作处理
		if(flag==0&&(markdpbn!=PBNNo))			//全是0XFF,且写入成功,且标记了的物理块与当前物理块不同
		{
			flag=FTL_UsedBlockMark(PBNNo);		//标记此块已经使用  
			markdpbn=PBNNo;						//标记完成,标记块=当前块,防止重复标记
		}
		if(flag)//不全为0XFF/标记失败,将数据写到另一个块   
		{
			temp=((uint32_t)nand_dev.block_pagenum*nand_dev.page_mainsize-BlockOffset)/SectorSize;//计算整个block还剩下多少个SECTOR可以写入
			wsecs=SectorCount-i;				//还剩多少个sector要写
			if(wsecs>=temp)wsecs=temp;			//大于可连续写入的sector数,则写入temp个扇区 
			wlen=wsecs*SectorSize;				//每次写wsecs个sector   
			flag=FTL_CopyAndWriteToBlock(PhyPageNo,PageOffset,pBuffer,wlen);//拷贝到另外一个block,并写入数据
			if(flag)return 3;//失败 
		} 
		i+=wsecs-1;
		pBuffer+=wlen;//数据缓冲区指针偏移
	}
	return 0;   
} 
//读扇区(支持多扇区读),FATFS文件系统使用
//pBuffer:数据缓存区
//SectorNo:起始扇区号
//SectorSize:扇区大小
//SectorCount:要写入的扇区数量
//返回值:0,成功
//	  其他,失败
uint8_t FTL_ReadSectors(uint8_t *pBuffer,uint32_t SectorNo,uint16_t SectorSize,uint32_t SectorCount)
{
	uint8_t flag=0;
	uint16_t rsecs;		//单次读取页数 
	uint32_t i=0;
	uint32_t LBNNo;      //逻辑块号
	uint32_t PBNNo;      //物理块号
	uint32_t PhyPageNo;  //物理页号
	uint32_t PageOffset; //页内偏移地址
	uint32_t BlockOffset;//块内偏移地址 
	for(i=0;i<SectorCount;i++)
	{
		LBNNo=(SectorNo+i)/(nand_dev.block_pagenum*(nand_dev.page_mainsize/SectorSize));//根据逻辑扇区号和扇区大小计算出逻辑块号
		PBNNo=FTL_LBNToPBN(LBNNo);					//将逻辑块转换为物理块
		if(PBNNo>=nand_dev.block_totalnum)return 1;	//物理块号大于NAND FLASH的总块数,则失败.  
		BlockOffset=((SectorNo+i)%(nand_dev.block_pagenum*(nand_dev.page_mainsize/SectorSize)))*SectorSize;//计算块内偏移
		PhyPageNo=PBNNo*nand_dev.block_pagenum+BlockOffset/nand_dev.page_mainsize;	//计算出物理页号
		PageOffset=BlockOffset%nand_dev.page_mainsize;                     			//计算出页内偏移地址 
		rsecs=(nand_dev.page_mainsize-PageOffset)/SectorSize;						//计算一次最多可以读取多少页
		if(rsecs>(SectorCount-i))rsecs=SectorCount-i;								//最多不能超过SectorCount-i
		flag=NAND_ReadPage(PhyPageNo,PageOffset,pBuffer,rsecs*SectorSize);			//读取数据
		if(flag==NSTA_ECC1BITERR)													//对于1bit ecc错误,可能为坏块
		{	
			flag=NAND_ReadPage(PhyPageNo,PageOffset,pBuffer,rsecs*SectorSize);		//重读数据,再次确认
			if(flag==NSTA_ECC1BITERR)
			{
				FTL_CopyAndWriteToBlock(PhyPageNo,PageOffset,pBuffer,rsecs*SectorSize);	//搬运数据 
				flag=FTL_BlockCompare(PhyPageNo/nand_dev.block_pagenum,0XFFFFFFFF);		//全1检查,确认是否为坏块
				if(flag==0)
				{
					flag=FTL_BlockCompare(PhyPageNo/nand_dev.block_pagenum,0X00);		//全0检查,确认是否为坏块
					NAND_EraseBlock(PhyPageNo/nand_dev.block_pagenum);					//检测完成后,擦除这个块
				}
				if(flag)																//全0/全1检查出错,肯定是坏块了.
				{
					FTL_BadBlockMark(PhyPageNo/nand_dev.block_pagenum);					//标记为坏块
					FTL_CreateLUT(1);													//重建LUT表 
				}
				flag=0;
			}
		}
		if(flag==NSTA_ECC2BITERR)flag=0;	//2bit ecc错误,不处理(可能是初次写入数据导致的)
		if(flag)return 2;					//失败
		pBuffer+=SectorSize*rsecs;			//数据缓冲区指针偏移 
		i+=rsecs-1;
	}
	return 0; 
}
//重新创建LUT表
//mode:0,仅检查第一个坏块标记
//     1,两个坏块标记都要检查(备份区也要检查)
//返回值:0,成功
//    其他,失败
uint8_t FTL_CreateLUT(uint8_t mode)
{
	uint32_t i;
	uint8_t buf[4];
	uint32_t LBNnum=0;								//逻辑块号 
	for(i=0;i<nand_dev.block_totalnum;i++)		//复位LUT表,初始化为无效值,也就是0XFFFF
	{
		nand_dev.lut[i]=0XFFFF;
	} 
	nand_dev.good_blocknum=0;
	for(i=0;i<nand_dev.block_totalnum;i++)
	{
		NAND_ReadSpare(i*nand_dev.block_pagenum,0,buf,4);	//读取4个字节
		if(buf[0]==0XFF&&mode)NAND_ReadSpare(i*nand_dev.block_pagenum+1,0,buf,1);//好块,且需要检查2次坏块标记
		if(buf[0]==0XFF)//是好块 				 
		{ 
			LBNnum=((uint16_t)buf[3]<<8)+buf[2];		//得到逻辑块编号
			if(LBNnum<nand_dev.block_totalnum)	//逻辑块号肯定小于总的块数量
			{
				nand_dev.lut[LBNnum]=i;			//更新LUT表,写LBNnum对应的物理块编号
			}
			nand_dev.good_blocknum++;
		}else printf("bad block index:%d\r\n",i);
	} 
	//LUT表建立完成以后检查有效块个数
	for(i=0;i<nand_dev.block_totalnum;i++)
	{
		if(nand_dev.lut[i]>=nand_dev.block_totalnum)
		{
			nand_dev.valid_blocknum=i;
			break;
		}
	}
	if(nand_dev.valid_blocknum<100)return 2;	//有效块数小于100,有问题.需要重新格式化 
	return 0;	//LUT表创建完成
} 
//FTL整个Block与某个数据对比
//blockx:block编号
//cmpval:要与之对比的值
//返回值:0,检查成功,全部相等
//       1,检查失败,有不相等的情况
uint8_t FTL_BlockCompare(uint32_t blockx,uint32_t cmpval)
{
	uint8_t res;
	uint16_t i,j,k; 
	for(i=0;i<3;i++)//允许3次机会
	{
		for(j=0;j<nand_dev.block_pagenum;j++)
		{
			NAND_ReadPageComp(blockx*nand_dev.block_pagenum,0,cmpval,nand_dev.page_mainsize/4,&k);//检查一个page,并与0XFFFFFFFF对比
			if(k!=(nand_dev.page_mainsize/4))break;
		}
		if(j==nand_dev.block_pagenum)return 0;		//检查合格,直接退出
		res=NAND_EraseBlock(blockx);
		if(res)printf("error erase block:%d\r\n",i);
		else
		{ 
			if(cmpval!=0XFFFFFFFF)//不是判断全1,则需要重写数据
			{
				for(k=0;k<nand_dev.block_pagenum;k++)
				{
					NAND_WritePageConst(blockx*nand_dev.block_pagenum+k,0,0,nand_dev.page_mainsize/4);//写PAGE 
				}
			}
		}
	}
	printf("bad block checked:%d\r\n",blockx);
	return 1;
}
//FTL初始化时,搜寻所有坏块,使用:擦-写-读 方式
//512M的NAND ,需要约3分钟时间,来完成检测
//对于RGB屏,由于频繁读写NAND,会引起屏幕乱闪
//返回值:好块的数量
uint32_t FTL_SearchBadBlock(void)
{
	uint8_t *blktbl;
	uint8_t res;
	uint32_t i,j; 
	uint32_t goodblock=0;
	blktbl=mymalloc(SRAMIN,nand_dev.block_totalnum);//申请block坏块表内存,对应项:0,好块;1,坏块;
	NAND_EraseChip(); 						//全片擦除
	for(i=0;i<nand_dev.block_totalnum;i++)	//第一阶段检查,检查全1
	{
		res=FTL_BlockCompare(i,0XFFFFFFFF);	//全1检查 
		if(res)blktbl[i]=1;					//坏块 
		else
		{ 
			blktbl[i]=0;					//好块 
			for(j=0;j<nand_dev.block_pagenum;j++)//写block为全0,为后面的检查准备
			{
				NAND_WritePageConst(i*nand_dev.block_pagenum+j,0,0,nand_dev.page_mainsize/4);
			} 
		}
	}	
	for(i=0;i<nand_dev.block_totalnum;i++)	//第二阶段检查,检查全0
	{ 
		if(blktbl[i]==0)					//在第一阶段,没有被标记坏块的,才可能是好块
		{
			res=FTL_BlockCompare(i,0);		//全0检查 
			if(res)blktbl[i]=1;				//标记坏块
			else goodblock++; 
		}
	}
	NAND_EraseChip();  	//全片擦除
	for(i=0;i<nand_dev.block_totalnum;i++)	//第三阶段检查,标记坏块
	{ 
		if(blktbl[i])FTL_BadBlockMark(i);	//是坏块
	}
	return goodblock;	//返回好块的数量
}

//格式化NAND 重建LUT表
//返回值:0,成功
//    其他,失败
uint8_t FTL_Format(void)
{
	uint8_t temp;
	uint32_t i,n;
	uint32_t goodblock=0;
	nand_dev.good_blocknum=0;
	#if FTL_USE_BAD_BLOCK_SEARCH==1				//使用擦-写-读的方式,检测坏块
	nand_dev.good_blocknum=FTL_SearchBadBlock();//搜寻坏块.耗时很久
	#else										//直接使用NAND FLASH的出厂坏块标志(其他块,默认是好块)
	for(i=0;i<nand_dev.block_totalnum;i++)	
	{
		temp=FTL_CheckBadBlock(i);			//检查一个块是否为坏块
		if(temp==0)							//好块
		{ 
			temp=NAND_EraseBlock(i);
			if(temp)						//擦除失败,认为坏块
			{
				printf("Bad block:%d\r\n",i);
				FTL_BadBlockMark(i);		//标记是坏块
			}else nand_dev.good_blocknum++;	//好块数量加一 
		}
	} 
	#endif
	printf("good_blocknum:%d\r\n",nand_dev.good_blocknum); 
	if(nand_dev.good_blocknum<100) return 1;	//如果好块的数量少于100,则NAND Flash报废   
	goodblock=(nand_dev.good_blocknum*93)/100;	//%93的好块用于存储数据  
	n=0;										
	for(i=0;i<nand_dev.block_totalnum;i++)		//在好块中标记上逻辑块信息
	{
		temp=FTL_CheckBadBlock(i);  			//检查一个块是否为坏块
		if(temp==0)                  			//好块
		{ 
			NAND_WriteSpare(i*nand_dev.block_pagenum,2,(uint8_t*)&n,2);//写入逻辑块编号
			n++;								//逻辑块编号加1
			if(n==goodblock) break;				//全部标记完了
		}
	} 
	if(FTL_CreateLUT(1))return 2;      			//重建LUT表失败 
	return 0;
}



#ifndef __BSP_NAND_H
#define __BSP_NAND_H

#ifdef __cplusplus
extern "C" {
#endif

#include "main.h"

#define NAND_MAX_PAGE_SIZE			4096		//定义NAND FLASH的最大的PAGE大小(不包括SPARE区),默认4096字节
#define NAND_ECC_SECTOR_SIZE		512			//执行ECC计算的单元大小,默认512字节


//NAND属性结构体
typedef struct
{
	uint16_t page_totalsize;     	//每页总大小,main区和spare区总和
	uint16_t page_mainsize;      	//每页的main区大小
	uint16_t page_sparesize;     	//每页的spare区大小
	uint8_t  block_pagenum;      	//每个块包含的页数量
	uint16_t plane_blocknum;     	//每个plane包含的块数量
	uint16_t block_totalnum;     	//总的块数量
	uint16_t good_blocknum;      	//好块数量    
	uint16_t valid_blocknum;     	//有效块数量(供文件系统使用的好块数量)
	uint32_t id;             		//NAND FLASH ID
	uint16_t *lut;      			   	//LUT表,用作逻辑块-物理块转换
	uint32_t ecc_hard;				//硬件计算出来的ECC值
	uint32_t ecc_hdbuf[NAND_MAX_PAGE_SIZE/NAND_ECC_SECTOR_SIZE];//ECC硬件计算值缓冲区  	
	uint32_t ecc_rdbuf[NAND_MAX_PAGE_SIZE/NAND_ECC_SECTOR_SIZE];//ECC读取的值缓冲区
}nand_attriute;      

extern nand_attriute nand_dev;				//nand重要参数结构体 

//NAND FLASH命令
#define NAND_READID         	0X90    	//读ID指令
#define NAND_FEATURE			0XEF    	//设置特性指令
#define NAND_RESET          	0XFF    	//复位NAND
#define NAND_READSTA        	0X70   	 	//读状态
#define NAND_AREA_A         	0X00   
#define NAND_AREA_TRUE1     	0X30  
#define NAND_WRITE0        	 	0X80
#define NAND_WRITE_TURE1    	0X10
#define NAND_ERASE0        	 	0X60
#define NAND_ERASE1         	0XD0
#define NAND_MOVEDATA_CMD0  	0X00
#define NAND_MOVEDATA_CMD1  	0X35
#define NAND_MOVEDATA_CMD2  	0X85
#define NAND_MOVEDATA_CMD3  	0X10

//NAND FLASH状态
#define NSTA_READY       	   	0X40		//nand已经准备好
#define NSTA_ERROR				0X01		//nand错误
#define NSTA_TIMEOUT        	0X02		//超时
#define NSTA_ECC1BITERR       	0X03		//ECC 1bit错误
#define NSTA_ECC2BITERR       	0X04		//ECC 2bit以上错误


uint8_t NAND_Init(void);
uint8_t NAND_ModeSet(uint8_t mode);
uint8_t NAND_WriteSpare(uint32_t PageNum,uint16_t ColNum,uint8_t *pBuffer,uint16_t NumByteToWrite);
uint8_t NAND_ReadSpare(uint32_t PageNum,uint16_t ColNum,uint8_t *pBuffer,uint16_t NumByteToRead);
uint8_t NAND_CopyPageWithWrite(uint32_t Source_PageNum,uint32_t Dest_PageNum,uint16_t ColNum,uint8_t *pBuffer,uint16_t NumByteToWrite);
uint8_t NAND_CopyPageWithoutWrite(uint32_t Source_PageNum,uint32_t Dest_PageNum);
uint8_t NAND_EraseBlock(uint32_t BlockNum);
uint8_t NAND_ReadPageComp(uint32_t PageNum,uint16_t ColNum,uint32_t CmpVal,uint16_t NumByteToRead,uint16_t *NumByteEqual);
uint8_t NAND_WritePage(uint32_t PageNum,uint16_t ColNum,uint8_t *pBuffer,uint16_t NumByteToWrite);
uint8_t NAND_ReadPage(uint32_t PageNum,uint16_t ColNum,uint8_t *pBuffer,uint16_t NumByteToRead);
uint8_t NAND_ECC_Correction(uint8_t* data_buf,uint32_t eccrd,uint32_t ecccl);
uint16_t NAND_ECC_Get_OE(uint8_t oe,uint32_t eccval);
uint8_t NAND_WritePageConst(uint32_t PageNum,uint16_t ColNum,uint32_t cval,uint16_t NumByteToWrite);
void NAND_EraseChip(void);

#ifdef __cplusplus
}
#endif

#endif /* __BSP_NAND_H */


#include "bsp_nand.h"
#include "delay.h"
#include "bsp_printf.h"
#include "bsp_malloc.h"

nand_attriute nand_dev;

//初始化NAND FLASH
uint8_t NAND_Init(void)
{ 
	HAL_NAND_Reset(&hnand1);       		        //复位NAND
	delay_ms(100);
	//NAND_ModeSet(4);			        //设置为MODE4,高速模式 

	nand_dev.page_totalsize=hnand1.Config.PageSize + hnand1.Config.SpareAreaSize;		//2112
	nand_dev.page_mainsize=hnand1.Config.PageSize;	//2048
	nand_dev.page_sparesize=hnand1.Config.SpareAreaSize;	//64
	nand_dev.block_pagenum=hnand1.Config.BlockSize;	//64
	nand_dev.plane_blocknum=hnand1.Config.PlaneSize;	//2048
	nand_dev.block_totalnum=hnand1.Config.BlockNbr; 	//4096
	
	return 0;
}

//读取NAND FLASH的ID
//返回值:0,成功;
//    其他,失败
uint8_t NAND_ModeSet(uint8_t mode)
{   
//	*(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_FEATURE;//发送设置特性命令
//	*(vu8*)(NAND_ADDRESS|NAND_ADDR)=0X01;		//地址为0X01,设置mode
//	*(vu8*)NAND_ADDRESS=mode;					//P1参数,设置mode
//	*(vu8*)NAND_ADDRESS=0;
//	*(vu8*)NAND_ADDRESS=0;
//	*(vu8*)NAND_ADDRESS=0; 
//	if(NAND_WaitForReady()==NSTA_READY)return 0;//成功
//	else return 1;								//失败
	
	
	uint32_t tickstart;
	__HAL_LOCK(&hnand1);
	if(hnand1.State == HAL_NAND_STATE_BUSY)
	{
		 return HAL_BUSY;
	}
	hnand1.State = HAL_NAND_STATE_BUSY;  
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | CMD_AREA)) = NAND_FEATURE;//发送设置特性命令
	__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = 0X01;
		__DSB();
	delay_us(1);
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE)) = mode;
		__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE)) = 0;
	__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE)) = 0;
	__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE)) = 0; 
	__DSB();
	hnand1.State = HAL_NAND_STATE_READY;
	__HAL_UNLOCK(&hnand1);
//	tickstart = HAL_GetTick();
//	while(HAL_NAND_Read_Status(&hnand1) != NAND_READY)
//	{
//		if((HAL_GetTick() - tickstart ) > 3000)
//		{
//			return HAL_TIMEOUT; 
//		}
//	}
	delay_us(2);
	return 0;//成功
}

//向spare区中写数据
//PageNum:要写入的页地址,范围:0~(block_pagenum*block_totalnum-1)
//ColNum:要写入的spare区地址(spare区中哪个地址),范围:0~(page_sparesize-1)  
//pBuffer:要写入的数据首地址 
//NumByteToWrite:要写入的字节数(不大于page_sparesize)
//返回值:0,成功
//    其他,失败
uint8_t NAND_WriteSpare(uint32_t PageNum,uint16_t ColNum,uint8_t *pBuffer,uint16_t NumByteToWrite)
{
    uint8_t temp=0;
    uint8_t remainbyte=0;
    remainbyte=nand_dev.page_sparesize-ColNum;
    if(NumByteToWrite>remainbyte) NumByteToWrite=remainbyte;  //确保要读取的字节数不大于spare剩余的大小
    temp=NAND_WritePage(PageNum,ColNum+nand_dev.page_mainsize,pBuffer,NumByteToWrite);//读取
    return temp;
} 

//读取spare区中的数据
//PageNum:要写入的页地址,范围:0~(block_pagenum*block_totalnum-1)
//ColNum:要写入的spare区地址(spare区中哪个地址),范围:0~(page_sparesize-1) 
//pBuffer:接收数据缓冲区 
//NumByteToRead:要读取的字节数(不大于page_sparesize)
//返回值:0,成功
//    其他,错误代码
uint8_t NAND_ReadSpare(uint32_t PageNum,uint16_t ColNum,uint8_t *pBuffer,uint16_t NumByteToRead)
{
    uint8_t temp=0;
    uint8_t remainbyte=0;
    remainbyte=nand_dev.page_sparesize-ColNum;
    if(NumByteToRead>remainbyte) NumByteToRead=remainbyte;  //确保要写入的字节数不大于spare剩余的大小
    temp=NAND_ReadPage(PageNum,ColNum+nand_dev.page_mainsize,pBuffer,NumByteToRead);//读取数据
    return temp;
} 


将一页数据拷贝到另一页,并且可以写入数据
注意:源页和目的页要在同一个Plane内!
Source_PageNo:源页地址,范围:0~(block_pagenum*block_totalnum-1)
Dest_PageNo:目的页地址,范围:0~(block_pagenum*block_totalnum-1)  
ColNo:页内列地址,范围:0~(page_totalsize-1)
pBuffer:要写入的数据
NumByteToWrite:要写入的数据个数
返回值:0,成功 
    其他,错误代码
//uint8_t NAND_CopyPageWithWrite(uint32_t Source_PageNum,uint32_t Dest_PageNum,uint16_t ColNum,uint8_t *pBuffer,uint16_t NumByteToWrite)
//{
//	uint8_t res=0;
//  __IO uint16_t i=0;
//	uint16_t source_block=0,dest_block=0;  
//	uint8_t eccnum=0;		//需要计算的ECC个数,每NAND_ECC_SECTOR_SIZE字节计算一个ecc
//	uint8_t eccstart=0;		//第一个ECC值所属的地址范围
//	uint32_t tickstart;
//	
//	//判断源页和目的页是否在同一个plane中
//	source_block=Source_PageNum/nand_dev.block_pagenum;
//	dest_block=Dest_PageNum/nand_dev.block_pagenum;
//	if((source_block%2)!=(dest_block%2))return NSTA_ERROR;//不在同一个plane内

//	__HAL_LOCK(&hnand1);
//	if(hnand1.State == HAL_NAND_STATE_BUSY)
//	{
//		 return HAL_BUSY;
//	}
//	hnand1.State = HAL_NAND_STATE_BUSY;  
//	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | CMD_AREA)) = NAND_MOVEDATA_CMD0;
//	__DSB();
//	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)0;
//		__DSB();
//	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)0;
//		__DSB();
//	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)Source_PageNum;
//	__DSB();
//	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)(Source_PageNum>>8);
//	__DSB();
//	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)(Source_PageNum>>16);
//	__DSB();
//	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | CMD_AREA)) = NAND_MOVEDATA_CMD1;
//	__DSB();
//	hnand1.State = HAL_NAND_STATE_READY;
//	__HAL_UNLOCK(&hnand1);
//    
    //下面两行代码是等待R/B引脚变为低电平,其实主要起延时作用的,等待NAND操作R/B引脚。因为我们是通过
    //将STM32的NWAIT引脚(NAND的R/B引脚)配置为普通IO,代码中通过读取NWAIT引脚的电平来判断NAND是否准备
    //就绪的。这个也就是模拟的方法,所以在速度很快的时候有可能NAND还没来得及操作R/B引脚来表示NAND的忙
    //闲状态,结果我们就读取了R/B引脚,这个时候肯定会出错的,事实上确实是会出错!大家也可以将下面两行
    //代码换成延时函数,只不过这里我们为了效率所以没有用延时函数。
	res=NAND_WaitRB(0);			//等待RB=0 
	if(res)return NSTA_TIMEOUT;	//超时退出
    //下面2行代码是真正判断NAND是否准备好的
	res=NAND_WaitRB(1);			//等待RB=1 
    if(res)return NSTA_TIMEOUT;	//超时退出 
//		
	/* Get tick */
	tickstart = HAL_GetTick();
	/* Read status until NAND is ready */
	while(HAL_NAND_Read_Status(&hnand1) != NAND_READY)
	{
		if((HAL_GetTick() - tickstart ) > 3000)
		{
			return HAL_TIMEOUT; 
		}
	}
//		delay_us(2);
    *(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_MOVEDATA_CMD2;  //发送命令0X85
    //发送目的页地址
    *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)ColNum;
    *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)(ColNum>>8);
    *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)Dest_PageNum;
    *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)(Dest_PageNum>>8);
    *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)(Dest_PageNum>>16); 
    //发送页内列地址
	NAND_Delay(NAND_TADL_DELAY);//等待tADL 
//	
//	__HAL_LOCK(&hnand1);
//	if(hnand1.State == HAL_NAND_STATE_BUSY)
//	{
//		 return HAL_BUSY;
//	}
//	hnand1.State = HAL_NAND_STATE_BUSY;  
//	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | CMD_AREA)) = NAND_MOVEDATA_CMD2;//发送命令0X85
//	__DSB();
//	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)ColNum;
//		__DSB();
//	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)(ColNum>>8);
//		__DSB();
//	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)Dest_PageNum;
//	__DSB();
//	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)(Dest_PageNum>>8);
//	__DSB();
//	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)(Dest_PageNum>>16); 
//	__DSB();
//	hnand1.State = HAL_NAND_STATE_READY;
//	__HAL_UNLOCK(&hnand1);
	tickstart = HAL_GetTick();
	while(HAL_NAND_Read_Status(&hnand1) != NAND_READY)
	{
		if((HAL_GetTick() - tickstart ) > 3000)
		{
			return HAL_TIMEOUT; 
		}
	}
//	delay_us(2);
//	
//	if(NumByteToWrite%NAND_ECC_SECTOR_SIZE)//不是NAND_ECC_SECTOR_SIZE的整数倍,不进行ECC校验
//	{  
//		for(i=0;i<NumByteToWrite;i++)		//写入数据
//		{
//			*(__IO uint8_t *)NAND_DEVICE=*(__IO uint8_t *)pBuffer++;
//		}
//	}else
//	{
//		eccnum=NumByteToWrite/NAND_ECC_SECTOR_SIZE;			//得到ecc计算次数
//		eccstart=ColNum/NAND_ECC_SECTOR_SIZE; 
// 		for(res=0;res<eccnum;res++)
//		{
//			SCB_CleanInvalidateDCache();					//清除无效的D-Cache
//			//FMC_Bank3->PCR|=1<<6;							//使能ECC校验 
//			HAL_NAND_ECC_Enable(&hnand1);
//			for(i=0;i<NAND_ECC_SECTOR_SIZE;i++)				//写入NAND_ECC_SECTOR_SIZE个数据
//			{
//				*(__IO uint8_t *)NAND_DEVICE=*(__IO uint8_t *)pBuffer++;
//			}		
//			//while(!(FMC_Bank3->SR&(1<<6)));					//等待FIFO空	 
//			//nand_dev.ecc_hdbuf[res+eccstart]=FMC_Bank3->ECCR;	//读取硬件计算后的ECC值
// 			//FMC_Bank3->PCR&=~(1<<6);						//禁止ECC校验
//			HAL_NAND_GetECC(&hnand1, &nand_dev.ecc_hdbuf[res+eccstart], 2000);
//			HAL_NAND_ECC_Disable(&hnand1);
//		}  
//		i=nand_dev.page_mainsize+0X10+eccstart*4;			//计算写入ECC的spare区地址
//		//NAND_Delay(NAND_TADL_DELAY);//等待 
		tickstart = HAL_GetTick();
		while(HAL_NAND_Read_Status(&hnand1) != NAND_READY)
		{
			if((HAL_GetTick() - tickstart ) > 3000)
			{
				return HAL_TIMEOUT; 
			}
		}
//		delay_us(2);
//		
		*(vu8*)(NAND_ADDRESS|NAND_CMD)=0X85;				//随机写指令
		//发送地址
		*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)i;
		*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)(i>>8);
		NAND_Delay(NAND_TADL_DELAY);//等待tADL 
//		
//		__HAL_LOCK(&hnand1);
//		if(hnand1.State == HAL_NAND_STATE_BUSY)
//		{
//			 return HAL_BUSY;
//		}
//		hnand1.State = HAL_NAND_STATE_BUSY;  
//		*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | CMD_AREA)) = 0X85;//发送命令0X85
//		__DSB();
//		*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)i;
//		__DSB();
//		*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)(i>>8);
//		__DSB();
//		hnand1.State = HAL_NAND_STATE_READY;
//		__HAL_UNLOCK(&hnand1);
		tickstart = HAL_GetTick();
		while(HAL_NAND_Read_Status(&hnand1) != NAND_READY)
		{
			if((HAL_GetTick() - tickstart ) > 3000)
			{
				return HAL_TIMEOUT; 
			}
		}
//		delay_us(2);
//		
//		
//		pBuffer=(uint8_t*)&nand_dev.ecc_hdbuf[eccstart];
//		for(i=0;i<eccnum;i++)					//写入ECC
//		{ 
//			for(res=0;res<4;res++)				 
//			{
//				*(__IO uint8_t *)NAND_DEVICE=*(__IO uint8_t *)pBuffer++;
//			}
//		} 		
//	}
//	
//	
//	
    *(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_MOVEDATA_CMD3;		//发送命令0X10 
 	delay_us(NAND_TPROG_DELAY);	//等待tPROG
	if(NAND_WaitForReady()!=NSTA_READY)return NSTA_ERROR;	//失败
    return 0;	//成功   
//	
//	__HAL_LOCK(&hnand1);
//	if(hnand1.State == HAL_NAND_STATE_BUSY)
//	{
//		 return HAL_BUSY;
//	}
//	hnand1.State = HAL_NAND_STATE_BUSY;  
//	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | CMD_AREA)) = NAND_MOVEDATA_CMD3;//发送命令0X10
//	__DSB();
//	hnand1.State = HAL_NAND_STATE_READY;
//	__HAL_UNLOCK(&hnand1);
//	
//	delay_us(200);
//	
//	tickstart = HAL_GetTick();
//	while(HAL_NAND_Read_Status(&hnand1) != NAND_READY)
//	{
//		if((HAL_GetTick() - tickstart ) > 3000)
//		{
//			return HAL_TIMEOUT; 
//		}
//	}
//	return 0;	//成功   
//} 

//将一页数据拷贝到另一页,并且可以写入数据
//注意:源页和目的页要在同一个Plane内!
//Source_PageNo:源页地址,范围:0~(block_pagenum*block_totalnum-1)
//Dest_PageNo:目的页地址,范围:0~(block_pagenum*block_totalnum-1)  
//ColNo:页内列地址,范围:0~(page_totalsize-1)
//pBuffer:要写入的数据
//NumByteToWrite:要写入的数据个数
//返回值:0,成功 
//    其他,错误代码
uint8_t NAND_CopyPageWithWrite(uint32_t Source_PageNum,uint32_t Dest_PageNum,uint16_t ColNum,uint8_t *pBuffer,uint16_t NumByteToWrite)
{
	uint8_t res=0;
  __IO uint16_t i=0;
	uint16_t source_block=0,dest_block=0;  
	uint8_t eccnum=0;		//需要计算的ECC个数,每NAND_ECC_SECTOR_SIZE字节计算一个ecc
	uint8_t eccstart=0;		//第一个ECC值所属的地址范围
	uint32_t tickstart;
	
	//判断源页和目的页是否在同一个plane中
	source_block=Source_PageNum/nand_dev.block_pagenum;
	dest_block=Dest_PageNum/nand_dev.block_pagenum;
	if((source_block%2)!=(dest_block%2))return NSTA_ERROR;//不在同一个plane内
	
	__HAL_LOCK(&hnand1);
	if(hnand1.State == HAL_NAND_STATE_BUSY)
	{
		 return HAL_BUSY;
	}
	hnand1.State = HAL_NAND_STATE_BUSY;  
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | CMD_AREA)) = NAND_MOVEDATA_CMD0;
	__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)0;
		__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)0;
		__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)Source_PageNum;
	__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)(Source_PageNum>>8);
	__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)(Source_PageNum>>16);
	__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | CMD_AREA)) = NAND_MOVEDATA_CMD1;
	__DSB();
	hnand1.State = HAL_NAND_STATE_READY;
	__HAL_UNLOCK(&hnand1);
    
//    //下面两行代码是等待R/B引脚变为低电平,其实主要起延时作用的,等待NAND操作R/B引脚。因为我们是通过
//    //将STM32的NWAIT引脚(NAND的R/B引脚)配置为普通IO,代码中通过读取NWAIT引脚的电平来判断NAND是否准备
//    //就绪的。这个也就是模拟的方法,所以在速度很快的时候有可能NAND还没来得及操作R/B引脚来表示NAND的忙
//    //闲状态,结果我们就读取了R/B引脚,这个时候肯定会出错的,事实上确实是会出错!大家也可以将下面两行
//    //代码换成延时函数,只不过这里我们为了效率所以没有用延时函数。
//	res=NAND_WaitRB(0);			//等待RB=0 
//	if(res)return NSTA_TIMEOUT;	//超时退出
//    //下面2行代码是真正判断NAND是否准备好的
//	res=NAND_WaitRB(1);			//等待RB=1 
//    if(res)return NSTA_TIMEOUT;	//超时退出 
		
//	/* Get tick */
//	tickstart = HAL_GetTick();
//	/* Read status until NAND is ready */
//	while(HAL_NAND_Read_Status(&hnand1) != NAND_READY)
//	{
//		if((HAL_GetTick() - tickstart ) > 3000)
//		{
//			return HAL_TIMEOUT; 
//		}
//	}
		delay_us(30);
//    *(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_MOVEDATA_CMD2;  //发送命令0X85
//    //发送目的页地址
//    *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)ColNum;
//    *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)(ColNum>>8);
//    *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)Dest_PageNum;
//    *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)(Dest_PageNum>>8);
//    *(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)(Dest_PageNum>>16); 
//    //发送页内列地址
//	NAND_Delay(NAND_TADL_DELAY);//等待tADL 
	
	__HAL_LOCK(&hnand1);
	if(hnand1.State == HAL_NAND_STATE_BUSY)
	{
		 return HAL_BUSY;
	}
	hnand1.State = HAL_NAND_STATE_BUSY;  
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | CMD_AREA)) = NAND_MOVEDATA_CMD2;//发送命令0X85
	__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)ColNum;
		__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)(ColNum>>8);
		__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)Dest_PageNum;
	__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)(Dest_PageNum>>8);
	__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)(Dest_PageNum>>16); 
	__DSB();
	hnand1.State = HAL_NAND_STATE_READY;
	__HAL_UNLOCK(&hnand1);
//	tickstart = HAL_GetTick();
//	while(HAL_NAND_Read_Status(&hnand1) != NAND_READY)
//	{
//		if((HAL_GetTick() - tickstart ) > 3000)
//		{
//			return HAL_TIMEOUT; 
//		}
//	}
	delay_us(1);
	
	if(NumByteToWrite%NAND_ECC_SECTOR_SIZE)//不是NAND_ECC_SECTOR_SIZE的整数倍,不进行ECC校验
	{  
		for(i=0;i<NumByteToWrite;i++)		//写入数据
		{
			*(__IO uint8_t *)NAND_DEVICE=*(__IO uint8_t *)pBuffer++;
		}
	}else
	{
		eccnum=NumByteToWrite/NAND_ECC_SECTOR_SIZE;			//得到ecc计算次数
		eccstart=ColNum/NAND_ECC_SECTOR_SIZE; 
 		for(res=0;res<eccnum;res++)
		{
//			SCB_CleanInvalidateDCache();					//清除无效的D-Cache
			//FMC_Bank3->PCR|=1<<6;							//使能ECC校验 
			HAL_NAND_ECC_Enable(&hnand1);
			for(i=0;i<NAND_ECC_SECTOR_SIZE;i++)				//写入NAND_ECC_SECTOR_SIZE个数据
			{
				*(__IO uint8_t *)NAND_DEVICE=*(__IO uint8_t *)pBuffer++;
			}		
			//while(!(FMC_Bank3->SR&(1<<6)));					//等待FIFO空	 
			//nand_dev.ecc_hdbuf[res+eccstart]=FMC_Bank3->ECCR;	//读取硬件计算后的ECC值
 			//FMC_Bank3->PCR&=~(1<<6);						//禁止ECC校验
			HAL_NAND_GetECC(&hnand1, &nand_dev.ecc_hdbuf[res+eccstart], 2000);
			HAL_NAND_ECC_Disable(&hnand1);
		}  
		i=nand_dev.page_mainsize+0X10+eccstart*4;			//计算写入ECC的spare区地址
		//NAND_Delay(NAND_TADL_DELAY);//等待 
//		tickstart = HAL_GetTick();
//		while(HAL_NAND_Read_Status(&hnand1) != NAND_READY)
//		{
//			if((HAL_GetTick() - tickstart ) > 3000)
//			{
//				return HAL_TIMEOUT; 
//			}
//		}
		delay_us(1);
		
//		*(vu8*)(NAND_ADDRESS|NAND_CMD)=0X85;				//随机写指令
//		//发送地址
//		*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)i;
//		*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)(i>>8);
//		NAND_Delay(NAND_TADL_DELAY);//等待tADL 
		
		__HAL_LOCK(&hnand1);
		if(hnand1.State == HAL_NAND_STATE_BUSY)
		{
			 return HAL_BUSY;
		}
		hnand1.State = HAL_NAND_STATE_BUSY;  
		*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | CMD_AREA)) = 0X85;//发送命令0X85
		__DSB();
		*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)i;
		__DSB();
		*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)(i>>8);
		__DSB();
		hnand1.State = HAL_NAND_STATE_READY;
		__HAL_UNLOCK(&hnand1);
//		tickstart = HAL_GetTick();
//		while(HAL_NAND_Read_Status(&hnand1) != NAND_READY)
//		{
//			if((HAL_GetTick() - tickstart ) > 3000)
//			{
//				return HAL_TIMEOUT; 
//			}
//		}
		delay_us(1);
		
		
		pBuffer=(uint8_t*)&nand_dev.ecc_hdbuf[eccstart];
		for(i=0;i<eccnum;i++)					//写入ECC
		{ 
			for(res=0;res<4;res++)				 
			{
				*(__IO uint8_t *)NAND_DEVICE=*(__IO uint8_t *)pBuffer++;
			}
		} 		
	}
	
	
	
//    *(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_MOVEDATA_CMD3;		//发送命令0X10 
// 	delay_us(NAND_TPROG_DELAY);	//等待tPROG
//	if(NAND_WaitForReady()!=NSTA_READY)return NSTA_ERROR;	//失败
//    return 0;	//成功   
	
	__HAL_LOCK(&hnand1);
	if(hnand1.State == HAL_NAND_STATE_BUSY)
	{
		 return HAL_BUSY;
	}
	hnand1.State = HAL_NAND_STATE_BUSY;  
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | CMD_AREA)) = NAND_MOVEDATA_CMD3;//发送命令0X10
	__DSB();
	hnand1.State = HAL_NAND_STATE_READY;
	__HAL_UNLOCK(&hnand1);
	
	delay_ms(1);
	
//	tickstart = HAL_GetTick();
//	while(HAL_NAND_Read_Status(&hnand1) != NAND_READY)
//	{
//		if((HAL_GetTick() - tickstart ) > 3000)
//		{
//			return HAL_TIMEOUT; 
//		}
//	}
	return 0;	//成功   
} 


//将一页数据拷贝到另一页,不写入新数据
//注意:源页和目的页要在同一个Plane内!
//Source_PageNo:源页地址,范围:0~(block_pagenum*block_totalnum-1)
//Dest_PageNo:目的页地址,范围:0~(block_pagenum*block_totalnum-1)  
//返回值:0,成功
//    其他,错误代码
uint8_t NAND_CopyPageWithoutWrite(uint32_t Source_PageNum,uint32_t Dest_PageNum)
{
	//uint8_t res=0;
	uint16_t source_block=0,dest_block=0;  
	uint32_t tickstart;
	//判断源页和目的页是否在同一个plane中
	source_block=Source_PageNum/nand_dev.block_pagenum;
	dest_block=Dest_PageNum/nand_dev.block_pagenum;
	if((source_block%2)!=(dest_block%2))return NSTA_ERROR;	//不在同一个plane内 
//	*(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_MOVEDATA_CMD0;	//发送命令0X00
//	//发送源页地址
//	*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)0;
//	*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)0;
//	*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)Source_PageNum;
//	*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)(Source_PageNum>>8);
//	*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)(Source_PageNum>>16);
//	*(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_MOVEDATA_CMD1;//发送命令0X35 
	
	__HAL_LOCK(&hnand1);
	if(hnand1.State == HAL_NAND_STATE_BUSY)
	{
		 return HAL_BUSY;
	}
	hnand1.State = HAL_NAND_STATE_BUSY;  
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | CMD_AREA)) = NAND_MOVEDATA_CMD0;//发送命令0X00
	__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)0;
		__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)0;
		__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)Source_PageNum;
	__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)(Source_PageNum>>8);
	__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)(Source_PageNum>>16);
	__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | CMD_AREA)) = NAND_MOVEDATA_CMD1;//发送命令0X35 
	__DSB();
	hnand1.State = HAL_NAND_STATE_READY;
	__HAL_UNLOCK(&hnand1);
//	tickstart = HAL_GetTick();
//	while(HAL_NAND_Read_Status(&hnand1) != NAND_READY)
//	{
//		if((HAL_GetTick() - tickstart ) > 3000)
//		{
//			return HAL_TIMEOUT; 
//		}
//	}
	delay_us(30);
	
//	//下面两行代码是等待R/B引脚变为低电平,其实主要起延时作用的,等待NAND操作R/B引脚。因为我们是通过
//	//将STM32的NWAIT引脚(NAND的R/B引脚)配置为普通IO,代码中通过读取NWAIT引脚的电平来判断NAND是否准备
//	//就绪的。这个也就是模拟的方法,所以在速度很快的时候有可能NAND还没来得及操作R/B引脚来表示NAND的忙
//	//闲状态,结果我们就读取了R/B引脚,这个时候肯定会出错的,事实上确实是会出错!大家也可以将下面两行
//	//代码换成延时函数,只不过这里我们为了效率所以没有用延时函数。
//	res=NAND_WaitRB(0);			//等待RB=0 
//	if(res)return NSTA_TIMEOUT;	//超时退出
//  //下面2行代码是真正判断NAND是否准备好的
//	res=NAND_WaitRB(1);			//等待RB=1 
//	if(res)return NSTA_TIMEOUT;	//超时退出 
//	*(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_MOVEDATA_CMD2;  //发送命令0X85
//	//发送目的页地址
//	*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)0;
//	*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)0;
//	*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)Dest_PageNum;
//	*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)(Dest_PageNum>>8);
//	*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)(Dest_PageNum>>16); 
//	*(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_MOVEDATA_CMD3;		//发送命令0X10 
//	delay_us(NAND_TPROG_DELAY);	//等待tPROG
//	if(NAND_WaitForReady()!=NSTA_READY)return NSTA_ERROR;	//NAND未准备好 
//	return 0;//成功  

	__HAL_LOCK(&hnand1);
	if(hnand1.State == HAL_NAND_STATE_BUSY)
	{
		 return HAL_BUSY;
	}
	hnand1.State = HAL_NAND_STATE_BUSY;  
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | CMD_AREA)) = NAND_MOVEDATA_CMD2;//发送命令0X85
	__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)0;
		__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)0;
		__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)Source_PageNum;
	__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)(Source_PageNum>>8);
	__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)(Source_PageNum>>16);
	__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | CMD_AREA)) = NAND_MOVEDATA_CMD3;//发送命令0X10 
	__DSB();
	hnand1.State = HAL_NAND_STATE_READY;
	__HAL_UNLOCK(&hnand1);
	delay_ms(1);
//	tickstart = HAL_GetTick();
//	while(HAL_NAND_Read_Status(&hnand1) != NAND_READY)
//	{
//		if((HAL_GetTick() - tickstart ) > 3000)
//		{
//			return HAL_TIMEOUT; 
//		}
//	}
	return 0;//成功  
}

//擦除一个块
//BlockNum:要擦除的BLOCK编号,范围:0-(block_totalnum-1)
//返回值:0,擦除成功
//    其他,擦除失败
uint8_t NAND_EraseBlock(uint32_t BlockNum)
{
	uint32_t tickstart;
	BlockNum<<=6;
	
//	*(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_ERASE0;
//	//发送块地址
//	*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)BlockNum;
//	*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)(BlockNum>>8);
//	*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)(BlockNum>>16);
//	*(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_ERASE1;
//	delay_ms(NAND_TBERS_DELAY);		//等待擦除成功
//	if(NAND_WaitForReady()!=NSTA_READY)return NSTA_ERROR;//失败
	
	__HAL_LOCK(&hnand1);
	if(hnand1.State == HAL_NAND_STATE_BUSY)
	{
		 return HAL_BUSY;
	}
	hnand1.State = HAL_NAND_STATE_BUSY;  
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | CMD_AREA)) = NAND_ERASE0;
	__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)BlockNum;
		__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)(BlockNum>>8);
		__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)(BlockNum>>16);
	__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | CMD_AREA)) = NAND_ERASE1;
	__DSB();
	hnand1.State = HAL_NAND_STATE_READY;
	__HAL_UNLOCK(&hnand1);
	delay_ms(4);
//	tickstart = HAL_GetTick();
//	while(HAL_NAND_Read_Status(&hnand1) != NAND_READY)
//	{
//		if((HAL_GetTick() - tickstart ) > 3000)
//		{
//			return HAL_TIMEOUT; 
//		}
//	}
	
	return 0;	//成功   
} 

//读取NAND Flash的指定页指定列的数据(main区和spare区都可以使用此函数),并对比(FTL管理时需要)
//PageNum:要读取的页地址,范围:0~(block_pagenum*block_totalnum-1)
//ColNum:要读取的列开始地址(也就是页内地址),范围:0~(page_totalsize-1)
//CmpVal:要对比的值,以uint32_t为单位
//NumByteToRead:读取字数(以4字节为单位,不能跨页读)
//NumByteEqual:从初始位置持续与CmpVal值相同的数据个数
//返回值:0,成功
//    其他,错误代码
uint8_t NAND_ReadPageComp(uint32_t PageNum,uint16_t ColNum,uint32_t CmpVal,uint16_t NumByteToRead,uint16_t *NumByteEqual)
{
	uint16_t i=0;
	//uint8_t res=0;
	uint32_t tickstart;
	
//	*(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_AREA_A;
//	//发送地址
//	*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)ColNum;
//	*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)(ColNum>>8);
//	*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)PageNum;
//	*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)(PageNum>>8);
//	*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)(PageNum>>16);
//	*(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_AREA_TRUE1;
	
	__HAL_LOCK(&hnand1);
	if(hnand1.State == HAL_NAND_STATE_BUSY)
	{
		 return HAL_BUSY;
	}
	hnand1.State = HAL_NAND_STATE_BUSY;  
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | CMD_AREA)) = NAND_AREA_A;
	__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)ColNum;
		__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)(ColNum>>8);
		__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)PageNum;
	__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)(PageNum>>8);
	__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)(PageNum>>16);
	__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | CMD_AREA)) = NAND_AREA_TRUE1;
	__DSB();
	hnand1.State = HAL_NAND_STATE_READY;
	__HAL_UNLOCK(&hnand1);
//	tickstart = HAL_GetTick();
//	while(HAL_NAND_Read_Status(&hnand1) != NAND_READY)
//	{
//		if((HAL_GetTick() - tickstart ) > 3000)
//		{
//			return HAL_TIMEOUT; 
//		}
//	}
	
	
	
//	//下面两行代码是等待R/B引脚变为低电平,其实主要起延时作用的,等待NAND操作R/B引脚。因为我们是通过
//	//将STM32的NWAIT引脚(NAND的R/B引脚)配置为普通IO,代码中通过读取NWAIT引脚的电平来判断NAND是否准备
//	//就绪的。这个也就是模拟的方法,所以在速度很快的时候有可能NAND还没来得及操作R/B引脚来表示NAND的忙
//	//闲状态,结果我们就读取了R/B引脚,这个时候肯定会出错的,事实上确实是会出错!大家也可以将下面两行
//	//代码换成延时函数,只不过这里我们为了效率所以没有用延时函数。
//	res=NAND_WaitRB(0);			//等待RB=0 
//	if(res)return NSTA_TIMEOUT;	//超时退出
//	//下面2行代码是真正判断NAND是否准备好的
//	res=NAND_WaitRB(1);			//等待RB=1 
//	if(res)return NSTA_TIMEOUT;	//超时退出  
	delay_us(30);
	
	for(i=0;i<NumByteToRead;i++)//读取数据,每次读4字节
	{
		if(*(__IO uint32_t *)NAND_DEVICE!=CmpVal)break;	//如果有任何一个值,与CmpVal不相等,则退出.
	}
	*NumByteEqual=i;					//与CmpVal值相同的个数
//	if(NAND_WaitForReady()!=NSTA_READY)return NSTA_ERROR;//失败
//	return 0;	//成功   
	
//	tickstart = HAL_GetTick();
//	while(HAL_NAND_Read_Status(&hnand1) != NAND_READY)
//	{
//		if((HAL_GetTick() - tickstart ) > 3000)
//		{
//			return HAL_TIMEOUT; 
//		}
//	}
	return 0;	//成功  
} 

//在NAND一页中写入指定个字节的数据(main区和spare区都可以使用此函数)
//PageNum:要写入的页地址,范围:0~(block_pagenum*block_totalnum-1)
//ColNum:要写入的列开始地址(也就是页内地址),范围:0~(page_totalsize-1)
//pBbuffer:指向数据存储区
//NumByteToWrite:要写入的字节数,该值不能超过该页剩余字节数!!!
//返回值:0,成功 
//    其他,错误代码
uint8_t NAND_WritePage(uint32_t PageNum,uint16_t ColNum,uint8_t *pBuffer,uint16_t NumByteToWrite)
{
  __IO uint16_t i=0;  
	uint8_t res=0;
	uint8_t eccnum=0;		//需要计算的ECC个数,每NAND_ECC_SECTOR_SIZE字节计算一个ecc
	uint8_t eccstart=0;		//第一个ECC值所属的地址范围
	uint32_t tickstart;
	
//	*(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_WRITE0;
//	//发送地址
//	*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)ColNum;
//	*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)(ColNum>>8);
//	*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)PageNum;
//	*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)(PageNum>>8);
//	*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)(PageNum>>16);
//	NAND_Delay(NAND_TADL_DELAY);//等待tADL 
	
	__HAL_LOCK(&hnand1);
	if(hnand1.State == HAL_NAND_STATE_BUSY)
	{
		 return HAL_BUSY;
	}
	hnand1.State = HAL_NAND_STATE_BUSY;  
//	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | CMD_AREA)) = NAND_CMD_AREA_A;
//    __DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | CMD_AREA)) = NAND_WRITE0;
	__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)ColNum;
		__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)(ColNum>>8);
		__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)PageNum;
	__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)(PageNum>>8);
	__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)(PageNum>>16);
	__DSB();
	hnand1.State = HAL_NAND_STATE_READY;
	__HAL_UNLOCK(&hnand1);
	delay_us(1);
//	tickstart = HAL_GetTick();
//	while(HAL_NAND_Read_Status(&hnand1) != NAND_READY)
//	{
//		if((HAL_GetTick() - tickstart ) > 3000)
//		{
//			return HAL_TIMEOUT; 
//		}
//	}
	
	
	if(NumByteToWrite%NAND_ECC_SECTOR_SIZE)//不是NAND_ECC_SECTOR_SIZE的整数倍,不进行ECC校验
	{  
		for(i=0;i<NumByteToWrite;i++)		//写入数据
		{
			*(__IO uint8_t *)NAND_DEVICE=*(__IO uint8_t *)pBuffer++;
			__DSB();
		}
	}else
	{
		eccnum=NumByteToWrite/NAND_ECC_SECTOR_SIZE;			//得到ecc计算次数
		eccstart=ColNum/NAND_ECC_SECTOR_SIZE; 
 		for(res=0;res<eccnum;res++)
		{
//			SCB_CleanInvalidateDCache();					//清除无效的D-Cache
			//FMC_Bank3->PCR|=1<<6;							//使能ECC校验 
			HAL_NAND_ECC_Enable(&hnand1);
			for(i=0;i<NAND_ECC_SECTOR_SIZE;i++)				//写入NAND_ECC_SECTOR_SIZE个数据
			{
				*(__IO uint8_t *)NAND_DEVICE=*(__IO uint8_t *)pBuffer++;
			}		
//			while(!(FMC_Bank3->SR&(1<<6)));					//等待FIFO空	
//			nand_dev.ecc_hdbuf[res+eccstart]=FMC_Bank3->ECCR;	//读取硬件计算后的ECC值
//  		FMC_Bank3->PCR&=~(1<<6);						//禁止ECC校验
			HAL_NAND_GetECC(&hnand1, &nand_dev.ecc_hdbuf[res+eccstart], 2000);
			HAL_NAND_ECC_Disable(&hnand1);
		}  
		i=nand_dev.page_mainsize+0X10+eccstart*4;			//计算写入ECC的spare区地址
		//NAND_Delay(NAND_TADL_DELAY);//等待 
		delay_us(2);
//		tickstart = HAL_GetTick();
//		while(HAL_NAND_Read_Status(&hnand1) != NAND_READY)
//		{
//			if((HAL_GetTick() - tickstart ) > 3000)
//			{
//				return HAL_TIMEOUT; 
//			}
//		}
		
//		*(vu8*)(NAND_ADDRESS|NAND_CMD)=0X85;				//随机写指令
//		//发送地址
//		*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)i;
//		*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)(i>>8);
//		NAND_Delay(NAND_TADL_DELAY);//等待tADL 
		
		__HAL_LOCK(&hnand1);
		if(hnand1.State == HAL_NAND_STATE_BUSY)
		{
			 return HAL_BUSY;
		}
		hnand1.State = HAL_NAND_STATE_BUSY;  
		*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | CMD_AREA)) = 0X85;//随机写指令
		__DSB();
		*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)i;
		__DSB();
		*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)(i>>8);
		__DSB();
		hnand1.State = HAL_NAND_STATE_READY;
		__HAL_UNLOCK(&hnand1);
//		tickstart = HAL_GetTick();
//		while(HAL_NAND_Read_Status(&hnand1) != NAND_READY)
//		{
//			if((HAL_GetTick() - tickstart ) > 3000)
//			{
//				return HAL_TIMEOUT; 
//			}
//		}
		delay_us(2);
		
		pBuffer=(uint8_t*)&nand_dev.ecc_hdbuf[eccstart];
		for(i=0;i<eccnum;i++)					//写入ECC
		{ 
			for(res=0;res<4;res++)				 
			{
				*(__IO uint8_t *)NAND_DEVICE=*(__IO uint8_t *)pBuffer++;
			}
		} 		
	}
//	*(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_WRITE_TURE1; 
//	delay_us(NAND_TPROG_DELAY);	//等待tPROG
//	if(NAND_WaitForReady()!=NSTA_READY)return NSTA_ERROR;//失败
//	return 0;//成功   
	
	
	__HAL_LOCK(&hnand1);
	if(hnand1.State == HAL_NAND_STATE_BUSY)
	{
		 return HAL_BUSY;
	}
	hnand1.State = HAL_NAND_STATE_BUSY;  
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | CMD_AREA)) = NAND_WRITE_TURE1;
	__DSB();
	hnand1.State = HAL_NAND_STATE_READY;
	__HAL_UNLOCK(&hnand1);
	delay_ms(1);
	tickstart = HAL_GetTick();
	while(HAL_NAND_Read_Status(&hnand1) != NAND_READY)
	{
		if((HAL_GetTick() - tickstart ) > 3000)
		{
			return HAL_TIMEOUT; 
		}
	}
	return 0;//成功   
}

//读取NAND Flash的指定页指定列的数据(main区和spare区都可以使用此函数)
//PageNum:要读取的页地址,范围:0~(block_pagenum*block_totalnum-1)
//ColNum:要读取的列开始地址(也就是页内地址),范围:0~(page_totalsize-1)
//*pBuffer:指向数据存储区
//NumByteToRead:读取字节数(不能跨页读)
//返回值:0,成功 
//    其他,错误代码
uint8_t NAND_ReadPage(uint32_t PageNum,uint16_t ColNum,uint8_t *pBuffer,uint16_t NumByteToRead)
{
  __IO uint16_t i=0;
	uint8_t res=0;
	uint8_t eccnum=0;		//需要计算的ECC个数,每NAND_ECC_SECTOR_SIZE字节计算一个ecc
	uint8_t eccstart=0;		//第一个ECC值所属的地址范围
	uint8_t errsta=0;
	uint8_t *p;
	uint32_t tickstart;
	
//	*(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_AREA_A;
//	//发送地址
//	*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)ColNum;
//	*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)(ColNum>>8);
//	*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)PageNum;
//	*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)(PageNum>>8);
//	*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)(PageNum>>16);
//	*(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_AREA_TRUE1;
	
	
	__HAL_LOCK(&hnand1);
	if(hnand1.State == HAL_NAND_STATE_BUSY)
	{
		 return HAL_BUSY;
	}
	hnand1.State = HAL_NAND_STATE_BUSY;  
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | CMD_AREA)) = NAND_AREA_A;
	__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)ColNum;
		__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)(ColNum>>8);
		__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)PageNum;
	__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)(PageNum>>8);
	__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)(PageNum>>16);
	__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | CMD_AREA)) = NAND_AREA_TRUE1;
	__DSB();
	hnand1.State = HAL_NAND_STATE_READY;
	__HAL_UNLOCK(&hnand1);
//	tickstart = HAL_GetTick();
//	while(HAL_NAND_Read_Status(&hnand1) != NAND_READY)
//	{
//		if((HAL_GetTick() - tickstart ) > 3000)
//		{
//			return HAL_TIMEOUT; 
//		}
//	}
	
	delay_us(30);
//	if(hnand1.Config.ExtraCommandEnable == ENABLE)
//	{
//		/* Get tick */
//		tickstart = HAL_GetTick();
//		
//		/* Read status until NAND is ready */
//		while(HAL_NAND_Read_Status(&hnand1) != NAND_READY)
//		{
//			if((HAL_GetTick() - tickstart ) > NAND_WRITE_TIMEOUT)
//			{
//				return HAL_TIMEOUT; 
//			}
//		}
//		
//		/* Go back to read mode */
//		*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | CMD_AREA)) = ((uint8_t)0x00U);
//		__DSB();
//	}
	
	
//	//下面两行代码是等待R/B引脚变为低电平,其实主要起延时作用的,等待NAND操作R/B引脚。因为我们是通过
//	//将STM32的NWAIT引脚(NAND的R/B引脚)配置为普通IO,代码中通过读取NWAIT引脚的电平来判断NAND是否准备
//	//就绪的。这个也就是模拟的方法,所以在速度很快的时候有可能NAND还没来得及操作R/B引脚来表示NAND的忙
//	//闲状态,结果我们就读取了R/B引脚,这个时候肯定会出错的,事实上确实是会出错!大家也可以将下面两行
//	//代码换成延时函数,只不过这里我们为了效率所以没有用延时函数。
//	res=NAND_WaitRB(0);			//等待RB=0 
//	if(res)return NSTA_TIMEOUT;	//超时退出
//	//下面2行代码是真正判断NAND是否准备好的
//	res=NAND_WaitRB(1);			//等待RB=1 
//	if(res)return NSTA_TIMEOUT;	//超时退出
	
	if(NumByteToRead%NAND_ECC_SECTOR_SIZE)//不是NAND_ECC_SECTOR_SIZE的整数倍,不进行ECC校验
	{ 
		//读取NAND FLASH中的值
		for(i=0;i<NumByteToRead;i++)
		{
			*(__IO uint8_t *)pBuffer++ = *(__IO uint8_t *)NAND_DEVICE;
		}
	}else
	{
		eccnum=NumByteToRead/NAND_ECC_SECTOR_SIZE;			//得到ecc计算次数
		eccstart=ColNum/NAND_ECC_SECTOR_SIZE;
		p=pBuffer;
		for(res=0;res<eccnum;res++)
		{
//			SCB_CleanInvalidateDCache();					//清除无效的D-Cache
			//FMC_Bank3->PCR|=1<<6;							//使能ECC校验 
			HAL_NAND_ECC_Enable(&hnand1);
			for(i=0;i<NAND_ECC_SECTOR_SIZE;i++)				//读取NAND_ECC_SECTOR_SIZE个数据
			{
				*(__IO uint8_t *)pBuffer++ = *(__IO uint8_t *)NAND_DEVICE;
			}		
//			while(!(FMC_Bank3->SR&(1<<6)));					//等待FIFO空	
//			nand_dev.ecc_hdbuf[res+eccstart]=FMC_Bank3->ECCR;//读取硬件计算后的ECC值
//			FMC_Bank3->PCR&=~(1<<6);						//禁止ECC校验
			HAL_NAND_GetECC(&hnand1, &nand_dev.ecc_hdbuf[res+eccstart], 2000);
			HAL_NAND_ECC_Disable(&hnand1);
		}  
		i=nand_dev.page_mainsize+0X10+eccstart*4;			//从spare区的0X10位置开始读取之前存储的ecc值
		
		//NAND_Delay(NAND_TRHW_DELAY);//等待tRHW 
		delay_us(2);
//		tickstart = HAL_GetTick();
//		while(HAL_NAND_Read_Status(&hnand1) != NAND_READY)
//		{
//			if((HAL_GetTick() - tickstart ) > 3000)
//			{
//				return HAL_TIMEOUT; 
//			}
//		}
		
		
//		*(vu8*)(NAND_ADDRESS|NAND_CMD)=0X05;				//随机读指令
//		//发送地址
//		*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)i;
//		*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)(i>>8);
//		*(vu8*)(NAND_ADDRESS|NAND_CMD)=0XE0;				//开始读数据
//		NAND_Delay(NAND_TWHR_DELAY);//等待tWHR 
		
		
		__HAL_LOCK(&hnand1);
		if(hnand1.State == HAL_NAND_STATE_BUSY)
		{
			 return HAL_BUSY;
		}
		hnand1.State = HAL_NAND_STATE_BUSY;  
		*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | CMD_AREA)) = 0X05;//随机读指令
		__DSB();
		*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)i;
		__DSB();
		*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)(i>>8);
		__DSB();
		*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | CMD_AREA)) = 0XE0;//开始读数据
		__DSB();
		hnand1.State = HAL_NAND_STATE_READY;
		__HAL_UNLOCK(&hnand1);
//		tickstart = HAL_GetTick();
//		while(HAL_NAND_Read_Status(&hnand1) != NAND_READY)
//		{
//			if((HAL_GetTick() - tickstart ) > 3000)
//			{
//				return HAL_TIMEOUT; 
//			}
//		}
		delay_us(2);
		
		pBuffer=(uint8_t*)&nand_dev.ecc_rdbuf[eccstart];
		for(i=0;i<4*eccnum;i++)								//读取保存的ECC值
		{
			*(__IO uint8_t *)pBuffer++= *(__IO uint8_t *)NAND_DEVICE;
		}			
		for(i=0;i<eccnum;i++)								//检验ECC
		{
			if(nand_dev.ecc_rdbuf[i+eccstart]!=nand_dev.ecc_hdbuf[i+eccstart])//不相等,需要校正
			{
				printf("err hd,rd:0x%x,0x%x\r\n",nand_dev.ecc_hdbuf[i+eccstart],nand_dev.ecc_rdbuf[i+eccstart]); 
 				printf("eccnum,eccstart:%d,%d\r\n",eccnum,eccstart);	
				printf("PageNum,ColNum:%d,%d\r\n",PageNum,ColNum);	
				res=NAND_ECC_Correction(p+NAND_ECC_SECTOR_SIZE*i,nand_dev.ecc_rdbuf[i+eccstart],nand_dev.ecc_hdbuf[i+eccstart]);//ECC校验
				if(res)errsta=NSTA_ECC2BITERR;				//标记2BIT及以上ECC错误
				else errsta=NSTA_ECC1BITERR;				//标记1BIT ECC错误
			} 
		} 		
	}
//	if(NAND_WaitForReady()!=NSTA_READY)errsta=NSTA_ERROR;	//失败
//	return errsta;	//成功   
	
//	tickstart = HAL_GetTick();
//	while(HAL_NAND_Read_Status(&hnand1) != NAND_READY)
//	{
//		if((HAL_GetTick() - tickstart ) > 3000)
//		{
//			return HAL_TIMEOUT; 
//		}
//	}
	return errsta;//成功  
} 

读取NAND Flash的指定页指定列的数据(main区和spare区都可以使用此函数)
PageNum:要读取的页地址,范围:0~(block_pagenum*block_totalnum-1)
ColNum:要读取的列开始地址(也就是页内地址),范围:0~(page_totalsize-1)
*pBuffer:指向数据存储区
NumByteToRead:读取字节数(不能跨页读)
返回值:0,成功 
    其他,错误代码
//#define NAND_ADDRESS			0X80000000	//nand flash的访问地址,接NCE3,地址为:0X8000 0000
//#define NAND_CMD				1<<16		//发送命令
//#define NAND_ADDR				1<<17		//发送地址

//uint8_t NAND_ReadPage(uint32_t PageNum,uint16_t ColNum,uint8_t *pBuffer,uint16_t NumByteToRead)
//{
//    __IO uint16_t  i=0;
//	uint8_t res=0;
//	uint8_t eccnum=0;		//需要计算的ECC个数,每NAND_ECC_SECTOR_SIZE字节计算一个ecc
//	uint8_t eccstart=0;		//第一个ECC值所属的地址范围
//	uint8_t errsta=0;
//	uint8_t *p;
//     *(__IO uint8_t *)(NAND_ADDRESS|NAND_CMD)=NAND_AREA_A;
//    //发送地址
//    *(__IO uint8_t *)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)ColNum;
//    *(__IO uint8_t *)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)(ColNum>>8);
//    *(__IO uint8_t *)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)PageNum;
//    *(__IO uint8_t *)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)(PageNum>>8);
//    *(__IO uint8_t *)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)(PageNum>>16);
//    *(__IO uint8_t *)(NAND_ADDRESS|NAND_CMD)=NAND_AREA_TRUE1;
//    //下面两行代码是等待R/B引脚变为低电平,其实主要起延时作用的,等待NAND操作R/B引脚。因为我们是通过
//    //将STM32的NWAIT引脚(NAND的R/B引脚)配置为普通IO,代码中通过读取NWAIT引脚的电平来判断NAND是否准备
//    //就绪的。这个也就是模拟的方法,所以在速度很快的时候有可能NAND还没来得及操作R/B引脚来表示NAND的忙
//    //闲状态,结果我们就读取了R/B引脚,这个时候肯定会出错的,事实上确实是会出错!大家也可以将下面两行
//    //代码换成延时函数,只不过这里我们为了效率所以没有用延时函数。
	res=NAND_WaitRB(0);			//等待RB=0 
    if(res)return NSTA_TIMEOUT;	//超时退出
    //下面2行代码是真正判断NAND是否准备好的
	res=NAND_WaitRB(1);			//等待RB=1 
    if(res)return NSTA_TIMEOUT;	//超时退出

//delay_us(2);

//	if(NumByteToRead%NAND_ECC_SECTOR_SIZE)//不是NAND_ECC_SECTOR_SIZE的整数倍,不进行ECC校验
//	{ 
//		//读取NAND FLASH中的值
//		for(i=0;i<NumByteToRead;i++)
//		{
//			*(__IO uint8_t *)pBuffer++ = *(__IO uint8_t *)NAND_ADDRESS;
//		}
//	}else
//	{
//		eccnum=NumByteToRead/NAND_ECC_SECTOR_SIZE;			//得到ecc计算次数
//		eccstart=ColNum/NAND_ECC_SECTOR_SIZE;
//		p=pBuffer;
//		for(res=0;res<eccnum;res++)
//		{
//			SCB_CleanInvalidateDCache();					//清除无效的D-Cache
//			FMC_Bank3->PCR|=1<<6;							//使能ECC校验 
//			for(i=0;i<NAND_ECC_SECTOR_SIZE;i++)				//读取NAND_ECC_SECTOR_SIZE个数据
//			{
//				*(__IO uint8_t *)pBuffer++ = *(__IO uint8_t *)NAND_ADDRESS;
//			}		
//			while(!(FMC_Bank3->SR&(1<<6)));					//等待FIFO空	
//			nand_dev.ecc_hdbuf[res+eccstart]=FMC_Bank3->ECCR;//读取硬件计算后的ECC值
//			FMC_Bank3->PCR&=~(1<<6);						//禁止ECC校验
//		}  
//		i=nand_dev.page_mainsize+0X10+eccstart*4;			//从spare区的0X10位置开始读取之前存储的ecc值
//		//NAND_Delay(NAND_TRHW_DELAY);//等待tRHW 
//		delay_us(2);
//		*(__IO uint8_t *)(NAND_ADDRESS|NAND_CMD)=0X05;				//随机读指令
//		//发送地址
//		*(__IO uint8_t *)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)i;
//		*(__IO uint8_t *)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)(i>>8);
//		*(__IO uint8_t *)(NAND_ADDRESS|NAND_CMD)=0XE0;				//开始读数据
//		//NAND_Delay(NAND_TWHR_DELAY);//等待tWHR 
//		delay_us(2);
//		pBuffer=(uint8_t*)&nand_dev.ecc_rdbuf[eccstart];
//		for(i=0;i<4*eccnum;i++)								//读取保存的ECC值
//		{
//			*(__IO uint8_t *)pBuffer++= *(__IO uint8_t *)NAND_ADDRESS;
//		}			
//		for(i=0;i<eccnum;i++)								//检验ECC
//		{
//			if(nand_dev.ecc_rdbuf[i+eccstart]!=nand_dev.ecc_hdbuf[i+eccstart])//不相等,需要校正
//			{
//				printf("err hd,rd:0x%x,0x%x\r\n",nand_dev.ecc_hdbuf[i+eccstart],nand_dev.ecc_rdbuf[i+eccstart]); 
// 				printf("eccnum,eccstart:%d,%d\r\n",eccnum,eccstart);	
//				printf("PageNum,ColNum:%d,%d\r\n",PageNum,ColNum);	
//				res=NAND_ECC_Correction(p+NAND_ECC_SECTOR_SIZE*i,nand_dev.ecc_rdbuf[i+eccstart],nand_dev.ecc_hdbuf[i+eccstart]);//ECC校验
//				if(res)errsta=NSTA_ECC2BITERR;				//标记2BIT及以上ECC错误
//				else errsta=NSTA_ECC1BITERR;				//标记1BIT ECC错误
//			} 
//		} 		
//	}
//    //if(NAND_WaitForReady()!=NSTA_READY)errsta=NSTA_ERROR;	//失败
//    return errsta;	//成功   
//} 

//ECC校正函数
//eccrd:读取出来,原来保存的ECC值
//ecccl:读取数据时,硬件计算的ECC只
//返回值:0,错误已修正
//    其他,ECC错误(有大于2个bit的错误,无法恢复)
uint8_t NAND_ECC_Correction(uint8_t* data_buf,uint32_t eccrd,uint32_t ecccl)
{
	uint16_t eccrdo,eccrde,eccclo,ecccle;
	uint16_t eccchk=0;
	uint16_t errorpos=0; 
	uint32_t bytepos=0;  
	eccrdo=NAND_ECC_Get_OE(1,eccrd);	//获取eccrd的奇数位
	eccrde=NAND_ECC_Get_OE(0,eccrd);	//获取eccrd的偶数位
	eccclo=NAND_ECC_Get_OE(1,ecccl);	//获取ecccl的奇数位
	ecccle=NAND_ECC_Get_OE(0,ecccl); 	//获取ecccl的偶数位
	eccchk=eccrdo^eccrde^eccclo^ecccle;
	if(eccchk==0XFFF)	//全1,说明只有1bit ECC错误
	{
		errorpos=eccrdo^eccclo; 
		printf("errorpos:%d\r\n",errorpos); 
		bytepos=errorpos/8; 
		data_buf[bytepos]^=1<<(errorpos%8);
	}else				//不是全1,说明至少有2bit ECC错误,无法修复
	{
		printf("2bit ecc error or more\r\n");
		return 1;
	} 
	return 0;
}

//获取ECC的奇数位/偶数位
//oe:0,偶数位
//   1,奇数位
//eccval:输入的ecc值
//返回值:计算后的ecc值(最多16位)
uint16_t NAND_ECC_Get_OE(uint8_t oe,uint32_t eccval)
{
	uint8_t i;
	uint16_t ecctemp=0;
	for(i=0;i<24;i++)
	{
		if((i%2)==oe)
		{
			if((eccval>>i)&0X01)ecctemp+=1<<(i>>1); 
		}
	}
	return ecctemp;
} 

//在NAND一页中的指定地址开始,写入指定长度的恒定数字
//PageNum:要写入的页地址,范围:0~(block_pagenum*block_totalnum-1)
//ColNum:要写入的列开始地址(也就是页内地址),范围:0~(page_totalsize-1)
//cval:要写入的指定常数
//NumByteToWrite:要写入的字数(以4字节为单位)
//返回值:0,成功 
//    其他,错误代码
uint8_t NAND_WritePageConst(uint32_t PageNum,uint16_t ColNum,uint32_t cval,uint16_t NumByteToWrite)
{
	uint16_t i=0;  
	uint32_t tickstart;
	
	
//	*(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_WRITE0;
//	//发送地址
//	*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)ColNum;
//	*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)(ColNum>>8);
//	*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)PageNum;
//	*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)(PageNum>>8);
//	*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(uint8_t)(PageNum>>16);
//	NAND_Delay(NAND_TADL_DELAY);//等待tADL 
	
	__HAL_LOCK(&hnand1);
	if(hnand1.State == HAL_NAND_STATE_BUSY)
	{
		 return HAL_BUSY;
	}
	hnand1.State = HAL_NAND_STATE_BUSY;  
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | CMD_AREA)) = NAND_WRITE0;
	__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)ColNum;
	__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)(ColNum>>8);
	__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)PageNum;
	__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)(PageNum>>8);
	__DSB();
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | ADDR_AREA)) = (uint8_t)(PageNum>>16);
	__DSB();
	hnand1.State = HAL_NAND_STATE_READY;
	__HAL_UNLOCK(&hnand1);
//	tickstart = HAL_GetTick();
//	while(HAL_NAND_Read_Status(&hnand1) != NAND_READY)
//	{
//		if((HAL_GetTick() - tickstart ) > 3000)
//		{
//			return HAL_TIMEOUT; 
//		}
//	}
	delay_us(1);
	
	for(i=0;i<NumByteToWrite;i++)		//写入数据,每次写4字节
	{
		*(__IO uint32_t *)NAND_DEVICE=cval;
	} 
	
	
//	*(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_WRITE_TURE1; 
//	delay_us(NAND_TPROG_DELAY);	//等待tPROG
//	if(NAND_WaitForReady()!=NSTA_READY)return NSTA_ERROR;//失败
//	return 0;//成功  

	__HAL_LOCK(&hnand1);
	if(hnand1.State == HAL_NAND_STATE_BUSY)
	{
		 return HAL_BUSY;
	}
	hnand1.State = HAL_NAND_STATE_BUSY;  
	*(__IO uint8_t *)((uint32_t)(NAND_DEVICE | CMD_AREA)) = NAND_WRITE_TURE1;
	__DSB();
	hnand1.State = HAL_NAND_STATE_READY;
	__HAL_UNLOCK(&hnand1);
	delay_ms(1);
	tickstart = HAL_GetTick();
	while(HAL_NAND_Read_Status(&hnand1) != NAND_READY)
	{
		if((HAL_GetTick() - tickstart ) > 3000)
		{
			return HAL_TIMEOUT; 
		}
	}
	return 0;//成功  
}

//全片擦除NAND FLASH
void NAND_EraseChip(void)
{
	uint8_t status;
	uint16_t i=0;
	for(i=0;i<nand_dev.block_totalnum;i++) //循环擦除所有的块
	{
		status=NAND_EraseBlock(i);
		if(status)printf("Erase %d block fail!!,错误码为%d\r\n",i,status);//擦除失败
	}
}



总结:
1、
在这里插入图片描述
2、
在这里插入图片描述
3、
在这里插入图片描述
4、
在这里插入图片描述
5、NAND FLASH Ready or busy设置为Disable,FMC的NWAIT控制方式不太理解,程序中直接使用延时替代,延时多久查数据手册
6、程序中的读写控制参考数据手册,例如读数据:
在这里插入图片描述
7、Extra comand enable设置为Disabled,此设置与NAND FLASH芯片Internal ECC Enabled设置有关,FLASH默认为disable
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值