加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

mini2440硬件篇之Nand Flash

发布时间:2020-12-15 17:22:38 所属栏目:百科 来源:网络整理
导读:1. ? 硬件原理 N and ?F lash 在对大容量的数据存储中发挥着重要的作用。相对于 or ,它具有一些优势,但它的一个劣势是很容易产生坏块,因此在使用 时,往往要利用校验算法发现坏块并标注出来,以便以后不再使用该坏块。 没有地址或数据总线,如果是 8 位

1.?硬件原理

Nand?Flash在对大容量的数据存储中发挥着重要的作用。相对于or,它具有一些优势,但它的一个劣势是很容易产生坏块,因此在使用时,往往要利用校验算法发现坏块并标注出来,以便以后不再使用该坏块。没有地址或数据总线,如果是8,那么它只有IO口,这口用于传输命令、地址和数据。主要以page(页)为单位进行读写,以block(块)为单位进行擦除。每一页中又分为main区和spare区,区用于正常数据的存储,区用于存储一些附加信息,如块好坏的标记、块的逻辑地址、页内数据的ECC校验和等。

1.1.?坏块管理

由于NAND?Flash的工艺不能保证NANDMemoryArray在其生命周期中保持性能的可靠,因此,在的生产中及使用过程中会产生坏块。坏块的特性是:当编程/擦除这个块时,不能将某些位拉高,这会造成PageProgramBlockErase操作时的错误,相应地反映到StatusRegister的相应位。

(1)固有坏块,这是生产过程中产生的坏块,一般芯片原厂都会在出厂时都会将坏块第一个area的第6bit标记为不等于0xff的值。(2)使用坏块,这是在使用过程中,如果或者错误,就可以简单地将这个块作为坏块来处理,这个时候需要把坏块标记起来。为了和固有坏块信息保持一致,将新发现的坏块的第一个Bit标记为非(3)坏块管理根据上面的这些叙述,可以了解NAND出厂时在中已经反映出了坏块信息,因此,如果在擦除一个块之前,一定要先check一下512)或第12k是否是0xff,如果是就证明这是一个好块,可以擦除;如果是非,那么就不能擦除。当然,这样处理可能会犯一个错误―――“错杀伪坏块,因为在芯片操作过程中可能由于电压不稳定等偶然因素会造成操作的错误。但是,为了数据的可靠性及软件设计的简单化,我们就要奉行蒋委员长宁可错杀一千,也决不放过一个的宗旨。(4)需要对前面由于错误发现的坏块进行一下特别说明。如果在对一个块的某个进行编程的时候发生了错误就要把这个块标记为坏块,首先就要把其他好的里面的内容备份到另外一个空的好块里面,然后,把这个块标记为坏块。当然,这可能会犯“错杀之误,一个补救的办法,就是在进行完页备份之后,再将这个块擦除一遍,如果Block?Erase发生错误,那就证明这个块是个真正的坏块,那就毫不犹豫地将它打个吧!)可能有人会问,为什么要使用的第六个作为坏块标记。这是生产商的默认约定。

?

2.?芯片手册

K9F2G08U0B

2.1.?特性

容量256MB一页2k+64;一块128k+4k

2.2.?引脚描述

见手册

2.3.?指令集

?

2.4.?

?

2.5.?编程

?

2.6.?擦除

?

2.7.?读ID

?

?

3.?mini2440电路图

4.?S3C2440寄存器

4.1.?控制器特性

、支持读编程?NAND?FLASH内存2、系统复位后nand?flash的前4k代码自动copy到内部sram??成后从启动,此时内部被映射为nGCS0。(当OM[10]=?00时使能NAND?FLASH?启动模式)3、支持硬件校验4、系统启动后内部ram可以用做其他的用途。

4.2.?操作Nand方法

、设置配置寄存器NFCONF、向命令寄存器NFCMD写入操作命令、向地址寄存器NFADDR写入地址、读写数据前要读取状态寄存器NFSTAT来判断是否处于忙状态。

4.3.?ECC奇偶校验

S3C2440在读写操作时,自动生成2048字节的奇偶校验码。的页为2048B。在读写的时候每页会产生大小的校验码。28bit校验码=22bit线校验码+6bit列校验码产生模块执行以下步骤::当MCU写数据到时,产生模块生成码。读数据时,码同时用户程序将它与先前写入时产生的码作比较。在自动引导模式下,不进行ECC检测。因此,4KB应确保不能有位错误(一般厂家都确保)。

nand.h

/*******************************************************************
 * Copyright (C),2011-2012, XXX.
 * FileName: nand.h 
 * Author:HuangYinqing
 * Version:1.0
 * Date::2012-04-22
 * Description:nand flash驱动.
 * Function List:
 * History:
 ******************************************************************/

#ifndef __NAND_H__
#define __NAND_H__

/*nand flash调试等级*/
#define DBG_NAND_LEVEL	  	1

/*nand flash信息*/
#define NAND_PAGE_SIZE		(2*1024)		   		//==1页2k
#define NAND_BLOCK_SIZE		(NAND_PAGE_SIZE*64)	   	//==1块128k
#define NAND_SIZE			(256*1024*1024)			//==容量256M

/*操作命令*/
#define CMD_READ1                 0x00              //页读命令周期1
#define CMD_READ2                 0x30              //页读命令周期2
#define CMD_READID                0x90              //读ID命令
#define CMD_WRITE1                0x80              //页写命令周期1
#define CMD_WRITE2                0x10              //页写命令周期2
#define CMD_ERASE1                0x60              //块擦除命令周期1
#define CMD_ERASE2                0xd0              //块擦除命令周期2
#define CMD_STATUS                0x70              //读状态命令
#define CMD_RESET                 0xff              //复位
#define CMD_RANDOMREAD1           0x05				//随意读命令周期1
#define CMD_RANDOMREAD2           0xE0				//随意读命令周期2
#define CMD_RANDOMWRITE           0x85				//随意写命令

/*NFCONF设置时序*/
#define TACLS	1
#define TWRPH0	2
#define TWRPH1	0

/*NFCONT片选*/
#define NF_nFCE_L()                        {rNFCONT &= ~(1<<1); }	//==打开片选
#define NF_nFCE_H()                        {rNFCONT |= (1<<1); }	//==关闭片选

/*读写数据*/
#define NF_CMD(data)              {rNFCMD  = (data);}        //传输命令
#define NF_ADDR(addr)             {rNFADDR = (addr);}        //传输地址
#define NF_RDDATA()               (rNFDATA)                  //读32位数据
#define NF_RDDATA8()              (rNFDATA8)                 //读8位数据
#define NF_WRDATA(data)           {rNFDATA = (data);}         //写32位数据
#define NF_WRDATA8(data)          {rNFDATA8 = (data);}        //写8位数据

/*NFSTAT的第0位可以用于判断nandflash是否在忙,第2位用于检测RnB引脚信号*/
#define NF_WAITRB()                {while(!(rNFSTAT&(1<<0)));}           //等待nandflash不忙
#define NF_CLEAR_RB()			   {rNFSTAT |= (1<<2); }                 //清除RnB信号
#define NF_DETECT_RB()             {while(!(rNFSTAT&(1<<2)));}           //等待RnB信号变高,即不忙

/*ECC*/
#define NF_RSTECC()                        {rNFCONT |= (1<<4); }         //复位ECC
#define NF_MECC_UnLock()                   {rNFCONT &= ~(1<<5); }		 //解锁main区ECC
#define NF_MECC_Lock()                     {rNFCONT |= (1<<5); }         //锁定main区ECC
#define NF_SECC_UnLock()                   {rNFCONT &= ~(1<<6); }        //解锁spare区ECC
#define NF_SECC_Lock()                     {rNFCONT |= (1<<6); }         //锁定spare区ECC

/*函数*/
void NandTest(void);
void NandInit(void);

#endif 
??nand.c

/*******************************************************************
 * Copyright (C),2011-2012, XXX.
 * FileName: nand.h 
 * Author:HuangYinqing
 * Version:1.0
 * Date::2012-04-22
 * Description:nand flash驱动.
 * Function List:
 * History:
 ******************************************************************/
#include "common.h"
#include "core.h"
#include "nand.h"


/********************************************************************
函数功能:nand flash初始化。
入口参数:无。
返    回:无。
备    注:无。
********************************************************************/ 
void NandInit(void)
{
	rGPACON = (rGPACON &~(0x3f<<17)) | (0x3f<<17);            //配置芯片引脚
	
	/*TACLS=1、TWRPH0=2、TWRPH1=0,8位IO*/
	rNFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4)|(0<<0);
	/*非锁定,屏蔽nandflash中断,初始化ECC及不锁定main区和spare区ECC,使能nandflash片选及控制器*/
	rNFCONT = (0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(0<<6)|(0<<5)|(1<<4)|(1<<1)|(1<<0);
}


/********************************************************************
函数功能:nand flash复位。
入口参数:无。
返    回:无。
备    注:无。
********************************************************************/ 
void NandReset(void)
{
	int i;
	
	NF_nFCE_L();                           	//打开nandflash片选
	NF_CLEAR_RB();							//清除RnB信号
	for ( i=0; i<10; i++);
	NF_CMD(CMD_RESET);						//写入复位命令
	NF_DETECT_RB();							//等待RnB信号变高,即不忙
	NF_nFCE_H();                            //关闭nandflash片选
}


/********************************************************************
函数功能:nand flash读ID。
入口参数:无。
返    回:设备ID。
备    注:无。
********************************************************************/ 
char NandReadID(void)
{
	char pMID;
	char pDID;
	char cyc3,cyc4,cyc5; 
	
	NF_nFCE_L();                          //打开nandflash片选
	// NF_CLEAR_RB();                        //清RnB信号
	NF_CMD(CMD_READID);					 //读ID命令
	NF_ADDR(0x0);                         //写0x00地址
	// for ( i=0; i<10; i++);
	
	//读五个周期的ID
	pMID = NF_RDDATA8();                   //厂商ID:0xEC
	pDID = NF_RDDATA8();                   //设备ID:0xDA
	cyc3 = NF_RDDATA8();                   //0x10
	cyc4 = NF_RDDATA8();                   //0x95
	cyc5 = NF_RDDATA8();                   //0x44
	
	NF_nFCE_H();							  //关闭nandflash片选

	DbgPrintX( DBG_NAND_LEVEL,"rMID=%2x,DID=%2x,3r=%2x,4r=%2x,5r=%2xn",pMID,pDID,cyc3,cyc5);

	return pDID;
}


/********************************************************************
函数功能:nand flash检查坏块。(大页2k)
入口参数:
	ulBlockNum:待检查块索引
返    回:1:坏块;0:好块。
备    注:无。
********************************************************************/ 
int NandIsBadBlockPage2048(U32 ulBlockNum)
{
    U32 ulPageNum;
	U8 	bBadBlockFlag;
	
   	ulPageNum = ulBlockNum << 6;	//==1块=128k=64页
   	
   	NF_nFCE_L();
    NF_CLEAR_RB();
   	NF_CMD(0x00);
	NF_ADDR(0);
	NF_ADDR((2048>>8)&0xff);
    NF_ADDR(ulPageNum&0xff);
    NF_ADDR((ulPageNum>>8)&0xff);
    NF_ADDR((ulPageNum>>16)&0xff);
    NF_CMD(0x30);        
   	NF_DETECT_RB();

   	bBadBlockFlag = NF_RDDATA8();
             
   	NF_nFCE_H();    

    return (bBadBlockFlag != 0xff);
}


/********************************************************************
函数功能:检查坏块。
入口参数:
	ulBlockNum:待检查块索引;
	bIsLargerPage:1:大页;0:小页
返    回:1:坏块;0:好块。
备    注:无。
********************************************************************/ 
int NandIsBadBlock(U32 ulBlockNum,U8 bIsLargePage)
{
	switch(bIsLargePage) 
	{
		case 0:
//			return Nand_IsBadBlockPage512(ulBlockNum);
		case 1:
			return NandIsBadBlockPage2048(ulBlockNum);
	}
	return 0;
}



/********************************************************************
函数功能:读nand flash一页数据。(大页2k)
入口参数:
	ulPageNum:页索引
	pucBuffer:缓冲区
返    回:成功:读取字节数
备    注:无。
********************************************************************/ 
U32 NandReadPage2048(U32 ulPageNum,U8 *pucBuffer)
{
	U32 i;

	NF_nFCE_L();                   //打开nandflash片选
	NF_CLEAR_RB();                 //清RnB信号
	
	NF_CMD(CMD_READ1);             //页读命令周期1

	//写入5个地址周期
	NF_ADDR(0x00);                                        //列地址A0~A7
	NF_ADDR(0x00);                                        //列地址A8~A11
	NF_ADDR((ulPageNum) & 0xff);						 //行地址A12~A19
	NF_ADDR((ulPageNum >> 8) & 0xff);					 //行地址A20~A27
	NF_ADDR((ulPageNum >> 16) & 0xff);					 //行地址A28
	
	NF_CMD(CMD_READ2);             //页读命令周期2
	NF_DETECT_RB();                //等待RnB信号变高,即不忙
	
	//读取一页数据内容
	for (i = 0; i < 2048; i++)
	{
		*pucBuffer++ = NF_RDDATA8();
	} 
	
	NF_nFCE_H();                   //打开nandflash片选   
	return i;
}


/********************************************************************
函数功能:读nand flash一页数据。
入口参数:
	ulPageNum:页索引
	pucBuffer:缓冲区
	bIsLargerPage:1:大页;0:小页
返    回:读取字节数。
备    注:无。
********************************************************************/ 
int NandReadPage(U32 ulPageNum,U8 *pucBuffer,U8 bIsLargePage)
{
	switch(bIsLargePage) 
	{
		case 0:
//			return Nand_ReadSectorPage512(ulPageNum,buffer);
		case 1:
			return NandReadPage2048(ulPageNum,pucBuffer);
	}
	return 0;
}


/********************************************************************
函数功能:读nand flash数据。
入口参数:
	ulAddr:从哪个地址读
	ulSize:要读取的字节数
	pucBuffer:缓冲区
返    回:成功:读取字节数;其他:出错。
备    注:无。
********************************************************************/ 
int NandRead(U32 ulAddr,U32 ulSize,U8 *pucBuffer)
{
	int i,j;

	/*检查参数*/
	if ( (ulAddr & (NAND_PAGE_SIZE-1)) || (ulSize & (NAND_PAGE_SIZE-1)) )
	{
		return ERR_PARAMETER;	/* invalid alignment */
	}

	/*每次读一页*/
	for (i=ulAddr; i < (ulAddr + ulSize);) 
	{
		if (i & (NAND_BLOCK_SIZE-1)== 0)  //==如果是块首,要检查坏块。
		{
			if ( NandIsBadBlock(i/NAND_BLOCK_SIZE,1) ) 
			{
				/* Bad block */
				i += NAND_BLOCK_SIZE;
				ulSize += NAND_BLOCK_SIZE;
				continue;
			}
		}
		
		j = NandReadPage(i/NAND_PAGE_SIZE,pucBuffer,1);
		i += j;
		pucBuffer += j;
	}

	return ERR_SUCCESS;
}

/********************************************************************
函数功能:写nand flash一页数据。(大页)
入口参数:
	ulPageNum:页索引
	pucBuffer:缓冲区
返    回:成功:读取字节数
备    注:无。
********************************************************************/ 
U32 NandWritePage2048(U32 ulPageNum,U8 *pucBuffer)
{
	U32 i;

	NF_nFCE_L();                   //打开nandflash片选
	NF_CLEAR_RB();                 //清RnB信号
	
	NF_CMD(CMD_WRITE1);             //页读命令周期1
								  //写入5个地址周期
	NF_ADDR(0x00);                                        //列地址A0~A7
	NF_ADDR(0x00);                                        //列地址A8~A11
	NF_ADDR((ulPageNum) & 0xff);						 //行地址A12~A19
	NF_ADDR((ulPageNum >> 8) & 0xff);					 //行地址A20~A27
	NF_ADDR((ulPageNum >> 16) & 0xff);					 //行地址A28
	
	//读取一页数据内容
	for (i = 0; i < 2048; i++)
	{
		  NF_WRDATA8(*pucBuffer++);
	} 
	
	NF_CMD(CMD_WRITE2);             //页读命令周期2
	udelay(200);

#if 0
	NF_CMD(CMD_STATUS);                 //读状态命令
	//判断状态值的第6位是否为1,即是否在忙,该语句的作用与NF_DETECT_RB();相同
	do
	{
		stat = NF_RDDATA8();
	}while(!(stat&0x40));
#endif

 	NF_DETECT_RB();                //等待RnB信号变高,即不忙

	NF_nFCE_H();                   //打开nandflash片选  
	 
	return i;
}


/********************************************************************
函数功能:写nand flash一页数据。
入口参数:
	ulPageNum:页索引
	pucBuffer:缓冲区
	bIsLargerPage:1:大页;0:小页
返    回:读取字节数。
备    注:无。
********************************************************************/ 
int NandWritePage(U32 ulPageNum,U8 bIsLargerPage)
{
	switch(bIsLargerPage) 
	{
		case 0:
//			return Nand_ReadSectorPage512(ulPageNum,buffer);
		case 1:
			return NandWritePage2048(ulPageNum,pucBuffer);
	}
	return 0;
}


/********************************************************************
函数功能:写nand flash数据。
入口参数:
	ulAddr:写到哪个地址
	ulSize:要写的字节数
	pucBuffer:缓冲区
返    回:成功:编程字节数;其他:出错。
备    注:无。
********************************************************************/
int NandWrite(U32 ulAddr,j;

	/*检查参数*/
	if ( (ulAddr & (NAND_PAGE_SIZE-1)) || (ulSize & (NAND_PAGE_SIZE-1)) )
	{
		return ERR_PARAMETER;	/* invalid alignment */
	}

	/*每次写一页*/
	for (i=ulAddr; i < (ulAddr + ulSize);) 
	{
		if (i & (NAND_BLOCK_SIZE-1)== 0)  //==如果是块首,要检查坏块。
		{
			if ( NandIsBadBlock(i/NAND_BLOCK_SIZE,1) ) 
			{
				/* Bad block */
				i += NAND_BLOCK_SIZE;
				ulSize += NAND_BLOCK_SIZE;
				continue;
			}
		}
		
		j = NandWritePage(i/NAND_PAGE_SIZE,1);
		i += j;
		pucBuffer += j;
	}

	return ERR_SUCCESS;
}


/********************************************************************
函数功能:擦除nand flash数据。
入口参数:
	ulBlock:块索引
返    回:成功:0;其他:出错。
备    注:无。
********************************************************************/ 
int NandErase(U32 ulBlockNum)
{
	if( NandIsBadBlock(ulBlockNum,1) )
	{
		return ERR_ERASE_NANDFLASH;
	}

	NF_nFCE_L();                   //打开nandflash片选
	NF_CLEAR_RB();                 //清RnB信号
	
	NF_CMD(CMD_ERASE1);             //页读命令周期1

	NF_ADDR((ulBlockNum <<6 ) & 0xff);						 //行地址A12~A19
	NF_ADDR((ulBlockNum >> 2) & 0xff);					 //行地址A20~A27
	NF_ADDR((ulBlockNum >> 10) & 0xff);					 //行地址A28
		
	NF_CMD(CMD_ERASE2);             //页读命令周期2
	udelay(200);

#if 0
	NF_CMD(CMD_STATUS);                 //读状态命令
	//判断状态值的第6位是否为1,即是否在忙,该语句的作用与NF_DETECT_RB();相同
	do
	{
		stat = NF_RDDATA8();
	}while(!(stat&0x40));
#endif

 	NF_DETECT_RB();                //等待RnB信号变高,即不忙

	NF_nFCE_H();                   //打开nandflash片选   
	return ERR_SUCCESS;
}


/********************************************************************
函数功能:测试nand flash。
入口参数:无。
返    回:无。
备    注:无。
********************************************************************/ 
void NandTest(void)
{
	int ret;
	unsigned char data[4][1024];

	memset(data,0x86,4*1024);

	NandInit();
	NandReset();
	NandReadID();

	ret = NandErase(2);
	if(ret != ERR_SUCCESS)
	{
	 	DbgPrintX( DBG_NAND_LEVEL,"rnand erase err=%2xn",ret);
		return;
	}

	ret = NandWrite( 0x50000,4*1024,(unsigned char *)data );
	if(ret != ERR_SUCCESS)
	{
	 	DbgPrintX( DBG_NAND_LEVEL,"rnand write err=%2xn",ret);
		return;
	}

	ret = NandRead(0x50000,(unsigned char *)0x31000000);
	if(ret != ERR_SUCCESS)
	{
	 	DbgPrintX( DBG_NAND_LEVEL,"rnand read err=%2xn",ret);
		return;
	}

}

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读