STM32F767 NAND FLASH MT29F4G08 FTL

news/2025/1/9 5:23:39/

参考正点原子例程,主要对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按下,读取sectorkey=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按下,写入sectorfor(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 ConfigurationPF0   ------> FMC_A0PF1   ------> FMC_A1PF2   ------> FMC_A2PF3   ------> FMC_A3PF4   ------> FMC_A4PF5   ------> FMC_A5PC0   ------> FMC_SDNWEPC2   ------> FMC_SDNE0PC3   ------> FMC_SDCKE0PF11   ------> FMC_SDNRASPF12   ------> FMC_A6PF13   ------> FMC_A7PF14   ------> FMC_A8PF15   ------> FMC_A9PG0   ------> FMC_A10PG1   ------> FMC_A11PE7   ------> FMC_D4PE8   ------> FMC_D5PE9   ------> FMC_D6PE10   ------> FMC_D7PE11   ------> FMC_D8PE12   ------> FMC_D9PE13   ------> FMC_D10PE14   ------> FMC_D11PE15   ------> FMC_D12PD8   ------> FMC_D13PD9   ------> FMC_D14PD10   ------> FMC_D15PD11   ------> FMC_CLEPD12   ------> FMC_ALEPD14   ------> FMC_D0PD15   ------> FMC_D1PG2   ------> FMC_A12PG4   ------> FMC_BA0PG5   ------> FMC_BA1PG8   ------> FMC_SDCLKPD0   ------> FMC_D2PD1   ------> FMC_D3PD4   ------> FMC_NOEPD5   ------> FMC_NWEPG9   ------> FMC_NCEPG15   ------> FMC_SDNCASPE0   ------> FMC_NBL0PE1   ------> 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 FLASHif(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();     //格式化NANDif(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;//当逻辑块号大于有效块数的时候返回0XFFFFif(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  //读出写入大小的内容判断是否全为0XFFflag=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-iflag=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++;	//好块数量加一 }} #endifprintf("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++;								//逻辑块编号加1if(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 IDuint16_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);       		        //复位NANDdelay_ms(100);//NAND_ModeSet(4);			        //设置为MODE4,高速模式 nand_dev.page_totalsize=hnand1.Config.PageSize + hnand1.Config.SpareAreaSize;		//2112nand_dev.page_mainsize=hnand1.Config.PageSize;	//2048nand_dev.page_sparesize=hnand1.Config.SpareAreaSize;	//64nand_dev.block_pagenum=hnand1.Config.BlockSize;	//64nand_dev.plane_blocknum=hnand1.Config.PlaneSize;	//2048nand_dev.block_totalnum=hnand1.Config.BlockNbr; 	//4096return 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字节计算一个eccuint8_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字节计算一个eccuint8_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字节计算一个eccuint8_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
在这里插入图片描述


http://www.ppmy.cn/news/166982.html

相关文章

BM37 BM38

import java.util.*;/** public class TreeNode {* int val 0;* TreeNode left null;* TreeNode right null;* public TreeNode(int val) {* this.val val;* }* }*/public class Solution {/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&a…

74系列相关芯片说明

首先先介绍一下74系列下面的子系列及其分别吧&#xff0c;74系列分为LS、ALS&#xff0c;HC和HCT和四个子系列。 74系列不同对比 名称中文输入电平输入电平供电典型输出延迟74LS低功耗肖特基系列TTLTTL5V18ns74ALS先进低功耗肖特基系列TTLTTL5V10ns74HC高速低功耗系列CMOS CMOS…

NTTFS4C10NTAG功率MOSFET 30V 44A 7.4 mOhm单N沟道u8FL

描述 NTTFS4C10NTAG N沟道30V 8.2A&#xff08;Ta&#xff09; 44A&#xff08;Tc&#xff09;790mW&#xff08;Ta&#xff09; 23.6W&#xff08;Tc&#xff09;表面贴装8-WDFN&#xff08;3.3x3.3&#xff09; MOSFET N-CH 30V 44A U8FL 小信号场效应晶体管 ​WDFN8 3.3x3.3…

MFT和USN

MFT和USN 缩写前言类型定义属性类型定义MFT条目索引MFT条目头属性头FILE_NAME值内容ATTRIBUTE_LIST属性值中记录的属性简要信息STANDARD_INFORMATION值内容其它定义 MFTMFT条目属性篇USN篇 缩写 MFT : 主文件表(Master File Table) USN : 更新序列号&#xff08;Update Sequen…

【转载】NXP i.MX8系列处理器介绍

NXP i.MX系列应用处理器是基于32和64位ARM技术&#xff0c;提供多核解决方案&#xff0c;适用于多媒体和显示应用&#xff0c;具有高性能和低功耗、可扩展、安全可靠等特点。发展经历了早期的i.MX28系列&#xff0c;到大获成功的i.MX 6系列&#xff0c;再到更高性能的i.MX 7和i…

74系列芯片

74系列集成电路大致可分为6大类&#xff1a; 74&times;&times;&#xff08;标准型&#xff09;&#xff1b; 74LS&times;&times;&#xff08;低功耗肖特基&#xff09;&#xff1b; 74S&times;&times;&#xff08;肖特基&#xff09;&#xff1b; 74AL…

AM,NM,Container

Application Master &#xff08;AM &#xff09;  管理 YARN 内运行的应用程序的每个实例。  完成数据切分&#xff0c;并为应用程序申请资源并进一步分配给内部任务。  负责协调来自 resource manager的资源&#xff0c;并通过 node manager 监视容易的 执行和资源使用…

CF 780 D. Maximum Product Strikes Back

Problem - D - Codeforces 题目大意&#xff1a;找到一个连续的区间&#xff0c;此区间乘积最大&#xff0c;区间长度0时答案为1&#xff0c;输出区间左边删除数量和右边删除的数量。直接输出区间不好吗&#xff1f;&#xff1f;&#xff1f; 解题思路&#xff1a;查询序列最…