参考正点原子例程,主要对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