K9F2G08U0A nand flash 的容量为256M byte,其内部有2048块,每块有64页,每页有2K+64字节,其中每页会分为main区(主域)和spare区(备用域),main区一般用来存入主要数据,spare一般用来存放ECC校验码。
下面几点是编程时需要注意的:
1.NAND FLASH芯片手册里说的column是指页内地址,row是指页地址,page也是指页;
2.删除时是以块为单位的,但是删除块时写的是row地址,自动会删除row所在的块;
3.读写方式有页读写,或随机读写,所谓的随机读写就是可以在页内的任一地方读写一个字节;
4.ECC校验码分为main区的ECC和spare区的ECC,它们一般都会存放在64字节的spare区内,下面是翻译2440手册的关于ECC编程的内容:
ECC 编程向导
1) 在软件模式, ECC 模块会为全部读 / 写数据产生 ECC 检验码。所以你需要在读或者写数据前给 InitECC(NFCONT[4]) 位写 1 和给 MainECCLock(NFCONT[5]) 位写 0(Unlock) 来复位 ECC 值。
MainECCLock(NFCONT[5]) 和 SpareECCLock(NFCONT[6] 控制 ECC 校验码是否产生。
2) 任何时候读或者写数据时, ECC 模块在 NFMECC0/1 上产生 ECC 校验码。
3) 在你完成读或者写一个页后(不包含备用数据域),给 MainECCLock 位置 1(lock) 。 ECC 校验码被锁上, ECC 状态寄存器的值将不会被改变。
4) 清 0(Unlock) SpareECCLock(NFCONT[6]) 位来产生备用域的 ECC 校验码。
5) 任何时候读或者写数据时,备用域 ECC 模块在寄存器 NFSECC 上产生 ECC 校验码。
6) 在完成读或者写备用域后,给 SpareECCLock 位置 1(lock) 。 ECC 校验码被锁上, ECC 状态寄存器的值将不会被改变。
7) 一旦完成你就可以使用这些值来记录到备用域或者检测位错误。
接下来是代码:
NAND-FLASH.H内容:
/***********
128K Pages
2048 Blocks
1 device = 2048 blocks =128k pages
1 block = 64 pages
1 page = (2k +64)bytes
***********/
#ifndef __NAND_FLASH_H__
#define __NAND_FLASH_H__
#include "lhg_def.h" //U8,U32相关的宏
#define MAX_NAND_BLOCK 2048 /*定义nand最大块数*/
#define NAND_PAGE_SIZE 2048 /*定义一页的容量*/
typedef struct nand_id_info //芯片的ID信息
{
U8 IDm; //marker code
U8 IDd; //device code
U8 ID3rd;
U8 ID4th;
U8 ID5th;
} nand_id_info;
typedef struct bad_block_info //登记坏块用的
{
U8 area[MAX_NAND_BLOCK];//0表示非坏块,1表示坏块
U32 sum;//坏块的总数
} bad_block_info;
//
//NAND 操作指令
#define NAND_CMD_READ_1st 0x00
#define NAND_CMD_READ_2st 0x30
#define NAND_CMD_RANDOM_WRITE 0x85
#define NAND_CMD_RANDOM_READ_1st 0x05
#define NAND_CMD_RANDOM_READ_2st 0xe0
#define NAND_CMD_READ_CB_1st 0x00
#define NAND_CMD_READ_CB_2st 0x35
#define NAND_CMD_READ_ID 0x90
#define NAND_CMD_RES 0xff
#define NAND_CMD_WRITE_PAGE_1st 0x80
#define NAND_CMD_WRITE_PAGE_2st 0x10
#define NAND_CMD_BLOCK_ERASE_1st 0x60
#define NAND_CMD_BLOCK_ERASE_2st 0xd0
#define NAND_CMD_READ_STATUS 0x70
//NAND 中断向量
#define INT_NFCON (24)
//NFCONF HCLK=100MHZ
#define S3C2440_NFCONF_TACLS_init (1<<12)
#define S3C2440_NFCONF_TWRPH0_init (4<<8)
#define S3C2440_NFCONF_TWRPH1_init (0<<4)
#define S3C2440_NFCONF_BusWidth_init (0) /*8-bit bus 这里会根据GPG15硬件自行设置,也可以软件手动设置*/
#define S3C2440_NFCONF_init() ( rNFCONF = S3C2440_NFCONF_TACLS_init | /
S3C2440_NFCONF_TWRPH0_init | /
S3C2440_NFCONF_TWRPH1_init | /
S3C2440_NFCONF_BusWidth_init )
//NFCONT
#define S3C2440_NFCONT_LockTight_init (0<<13)
#define S3C2440_NFCONT_SoftLock_init (0<<12)
#define S3C2440_NFCONT_EnbIllegalAccINT_init (1<<10) /*enable Illegal INT*/
#define S3C2440_NFCONT_EnbRnBINT_init (0<<9) /*禁止Rnb中断*/
#define S3C2440_NFCONT_RnB_TransMode_init (0<<8) /*上升沿产生RnB中断*/
#define S3C2440_NFCONT_SpareECCLock_init (1<<6)
#define S3C2440_NFCONT_MainECCLock_init (1<<5)
#define S3C2440_NFCONT_InitECC_init (1<<4)
#define S3C2440_NFCONT_Reg_nCE_init (1<<1) /*不选择nand flash芯片*/
#define S3C2440_NFCONT_MODE_init (0) /*NAND flash controller disable (Don’t work) 使用时在此处置1*/
#define S3C2440_NFCONT_init() ( rNFCONT = S3C2440_NFCONT_LockTight_init | /
S3C2440_NFCONT_SoftLock_init | /
S3C2440_NFCONT_EnbIllegalAccINT_init | /
S3C2440_NFCONT_EnbRnBINT_init | /
S3C2440_NFCONT_RnB_TransMode_init | /
S3C2440_NFCONT_SpareECCLock_init | /
S3C2440_NFCONT_MainECCLock_init | /
S3C2440_NFCONT_InitECC_init | /
S3C2440_NFCONT_Reg_nCE_init | /
S3C2440_NFCONT_MODE_init )
//NFSTAT
#define S3C2440_NFSTAT_init() ( rNFSTAT &= 0x3 )
//NFESTAT0
#define S3C2440_NFESTAT0_init() ( rNFESTAT0 = 0 )
//NFESTAT1
#define S3C2440_NFESTAT1_init() ( rNFESTAT1 = 0 )
//
#define select_nand() ( rNFCONT &= ~(1<<1) ) /*选择芯片*/
#define dis_select_nand() ( rNFCONT |= 1<<1 ) /*不选择芯片*/
#define controller_enable() ( rNFCONT |= 1 ) /*开控制*/
#define controller_disable() ( rNFCONT &= ~1 ) /*禁止控制*/
//
extern void nand_flash_init(void);//初始化
extern int nand_block_erase(U32 num);//num要删除的块号
extern int nand_page_write(U32 addr,U8 *buffer,U32 size);//addr要写的起始页地址,buffer要写的缓存,size要写的字节大小最大为4G
extern int nand_page_read(U32 addr,U8 *buffer,U32 size);//addr开始页地址,从每页00地址开始读
extern int nand_random_read(U32 paddr,U32 offset,U8 *data); //随机读数据 paddr页地址,offset页内偏移地址
extern int nand_random_write(U32 paddr,U32 offset,U8 data);//随机写,paddr页地址,offset页内偏移地址
extern void nand_test_bad_block(void);//测试坏块函数,并标记在nand_bbi变量里和spare区最后一个地址(如果非0xff则为坏块)
#endif
NAND-FLASH.c内容:
/***********
128K Pages
2048 Blocks
1 device = 2048 blocks =128k pages
1 block = 64 pages
1 page = (2k +64)bytes
***********/
#include "2440addr.h"
#include "NAND-FLASH.h"
#include "uart.h"
#include "lhg_def.h"
//#include "iic_lhg.h"
#define NAND_DEBUG 1
#define USE_ECC 1
nand_id_info nand_id;//定义登记芯片ID的全局变量
bad_block_info nand_bbi;//定义来登记坏用的全局变量
void init_nand_bbi(void)//初始化变量
{
U32 i;
nand_bbi.sum=0;
for (i=0;i<MAX_NAND_BLOCK;i++)
nand_bbi.area[i]=0;//
}
void nand_mask_bad_block(U32 n)//标志坏块,n是坏块的块号
{
#ifdef NAND_DEBUG
Uart_Printf("NAND found and mask a bad block=%d .",n);
#endif
if (nand_bbi.area[n]!=1)
{
nand_bbi.area[n]=1;
nand_bbi.sum++;
nand_random_write(n*64,2048+64-1,0);//每块的第一个spare的最后一个字节,标志本块是否为坏块,非0xff为坏块
}
}
int detect_nand_busy(void)//检测是否忙
{
U32 a;
a=0;
while(!(rNFSTAT&(1<<2)))
{
a++;
if (a==5000000)//等待超时
{
Uart_Printf("/r/n Error: Detect Nand Busy time out!!! /r/n");
rNFSTAT |= (1<<2);//清忙标志
return -1;//错误返回-1
}
}
rNFSTAT |= (1<<2);//清忙标志
return 1;
}
void nand_reset(void)//复位
{
rNFCMD = NAND_CMD_RES;
detect_nand_busy();//检测忙
}
void control_start(void){ //开启
select_nand();
controller_enable();
rNFSTAT |= (1<<2);//清忙标志
nand_reset();
}
void control_end(void) //关闭
{
dis_select_nand();
controller_disable();
}
void ecc_main_init(void)//初始化ECC值
{
rNFCONT |= 1<<4;//initEcc
}
void ecc_main_start(void)//开始main ECC
{
rNFCONT &= ~(1<<5);//unlock
}
void ecc_main_end(void)//结束main ECC
{
rNFCONT |= 1<<5;//unlock
}
void ecc_spare_start(void)//开始spare ECC
{
// rNFCONT |= 1<<4; //initEcc
rNFCONT &= ~(1<<6); //unlock
}
void ecc_spare_end(void)//结束spare ECC
{
rNFCONT |= 1<<6; //unlock
}
void __irq nandINT(void) //中断函数
{
//此处写处理代码
#ifdef NAND_DEBUG
Uart_Printf("/r/n Nand Error... In interrupt now!!!");//只有错误才会进入中断
#endif
rSRCPND |= 0x1<<INT_NFCON; //清中断标志位
rINTPND |= 0x1<<INT_NFCON; //清中断标志位
}
void nand_read_id(void)//读取芯片ID信息
{
control_start();//开控制
rNFCMD = NAND_CMD_READ_ID;
rNFADDR = 0;
//读ID
nand_id.IDm=(U8)rNFDATA8;
nand_id.IDd=(U8)rNFDATA8;
nand_id.ID3rd=(U8)rNFDATA8;
nand_id.ID4th=(U8)rNFDATA8;
nand_id.ID5th=(U8)rNFDATA8;
#ifdef NAND_DEBUG
Uart_Printf("/r/n Read NAND Flash ID:");
Uart_Printf("/r/n NAND Mark code: 0x%x ",nand_id.IDm);//打印ID信息
Uart_Printf("/r/n NAND Device code: 0x%x ",nand_id.IDd);
Uart_Printf("/r/n NAND 3rdID code: 0x%x ",nand_id.ID3rd);
Uart_Printf("/r/n NAND 4thID code: 0x%x ",nand_id.ID4th);
Uart_Printf("/r/n NAND 5thID code: 0x%x ",nand_id.ID5th);
#endif
control_end();//关控制
}
int nand_block_erase(U32 num)//num要删除的块号
{
num=num*64;
control_start();//开控制
nand_reset();//复位
rNFCMD = NAND_CMD_BLOCK_ERASE_1st;
rNFADDR = num&0xff;
rNFADDR = (num>>8)&0xff;
rNFADDR = (num>>16)&0xff;
rNFCMD = NAND_CMD_BLOCK_ERASE_2st;
detect_nand_busy();//
rNFCMD =NAND_CMD_READ_STATUS; //读状态
if (rNFDATA8&1)
{
#ifdef NAND_DEBUG
Uart_Printf("/r/n Error:nand erase error... block=0x%x",num/64);
#endif
control_end();//关控制
nand_mask_bad_block(num/64);//登记为坏块
return -1;//删除错误返回0
}
control_end();//关控制
#ifdef NAND_DEBUG
Uart_Printf("/r/n NAND block %d erase completed.",num/64);
#endif
return 1;
}
int nand_page_write(U32 addr,U8 *buffer,U32 size) //addr要写的起始页地址,buffer要写的缓存,size要写的字节大小最大为4G
{
U32 i,n,p,temp,ecc;
U8 *bu;
bu=buffer;
temp=0;
n=size/2048+(((size%2048)==0)?0:1); //计算出要写的页数,小于一页的部分当作一页
for (i=0;i<n;i++)
{
control_start();//开控制
nand_reset();//复位
#ifdef USE_ECC
ecc_main_init();
ecc_main_start();//可以产生main区ECC
#endif
// detect_nand_busy();//检测忙
rNFCMD = NAND_CMD_WRITE_PAGE_1st;
rNFADDR = 0; //从每页的0地址开始
rNFADDR = 0; //从每页的0地址开始
rNFADDR = (addr)&0xff;
rNFADDR = (addr>>8)&0xff;
rNFADDR = (addr>>16)&0xff;
for (p=0;p<2048;p++)//写入一页
{
temp=temp+1;
if (temp>size)
rNFDATA8 = 0xff;//多余的填写0xff
else
rNFDATA8 = *(bu+p);
}
delay_lhg(100,100);//
#ifdef USE_ECC
ecc_main_end();//锁定main区ecc
ecc=rNFMECC0;//main ECC值写入备用区的头0~4个地址内
ecc_spare_start();//开始sprare区ECC
rNFDATA8 = ecc&0xff;
rNFDATA8 = (ecc>>8)&0xff;
rNFDATA8 = (ecc>>16)&0xff;
rNFDATA8 = (ecc>>24)&0xff;
ecc_spare_end();
delay_lhg(100,100);//
ecc = rNFSECC;//spare ECC值写入备用区的5~6两个地址内
rNFDATA8 = ecc&0xff;
rNFDATA8 = (ecc>>8)&0xff;
#endif
bu=bu+2048;//页增量
addr++;
rNFCMD = NAND_CMD_WRITE_PAGE_2st;
detect_nand_busy();//检测忙
rNFCMD =NAND_CMD_READ_STATUS; //读状态
if (rNFDATA8&1)
{
#ifdef NAND_DEBUG
Uart_Printf("/r/n nand write page error: page addr=0x%d",addr-1);//写入失败,以后改进
#endif
control_end();//关控制
nand_mask_bad_block((addr-1)/64);//登记为坏块
return -1;//写入错误返回-1
}
control_end();//关控制
}
return 1;//成功返回1
}
int nand_page_read(U32 addr,U8 *buffer,U32 size)//addr开始页地址,从每页00地址开始读
{
U32 i,n,p,temp,ecc;
U8 *bu,no;
bu=buffer;
temp=0;
n=size/2048+(((size%2048)==0)?0:1); //计算出要读的页数,小于一页的部分当作一页
for (i=0;i<n;i++)
{
control_start();//开控制
nand_reset();//复位
#ifdef USE_ECC
rNFESTAT0 = 0;//复位错误标志位
ecc_main_init();
ecc_main_start();//可以产生main区ECC
#endif
rNFCMD = NAND_CMD_READ_1st;
rNFADDR = 0;
rNFADDR = 0;
rNFADDR = addr&0xff;
rNFADDR = (addr>>8)&0xff;
rNFADDR = (addr>>16)&0xff;
rNFCMD = NAND_CMD_READ_2st;
detect_nand_busy();
for (p=0;p<2048;p++)
{
temp=temp+1;
if (temp>size)
no=rNFDATA8;//多余的读出来扔掉
else
*(bu+p) = rNFDATA8;
}
#ifdef USE_ECC
rNFESTAT0=0;
ecc_main_end();//锁定main区ECC
delay_lhg(100,100);//
ecc_spare_start();//解锁spare区ecc
ecc=rNFDATA8;//从flash读出main区ECC
no=rNFDATA8;
ecc |= ((U32)no)<<8;
no=rNFDATA8;
ecc |= ((U32)no)<<16;
no=rNFDATA8;
ecc |= ((U32)no)<<24;
rNFMECCD0 = ((ecc&0xff00)<<8)|(ecc&0xff);//硬件检验main ECC
rNFMECCD1 = ((ecc&0xff000000)>>8)|((ecc&0xff0000)>>16);
ecc_spare_end();//锁定spare区ecc
delay_lhg(100,100);//
ecc=rNFDATA8;//从flash读出spare区ECC的值
no=rNFDATA8;
ecc |= ((U32)no)<<8;
rNFSECCD = ((ecc&0xff00)<<8)|(ecc&0xff);//硬件检验spare ECC
delay_lhg(100,100);//延时一会
ecc=rNFESTAT0&0xffffff;//ecc只是临时用一下错误状态,并非ecc内容
if (ecc!=0)//有错误
{
//以后再优化
#ifdef NAND_DEBUG
Uart_Printf("/r/n Nand ecc check error... page addr=0x%x,NFESTAT0=0x%x ",addr,ecc);
#endif
nand_mask_bad_block((addr+i)/64);//登记为坏块
return -1;//
}
#endif
bu=bu+2048;
addr++;
control_end();//关控制
}
return 1;
}
int nand_random_read(U32 paddr,U32 offset,U8 *data) //随机读数据 paddr页地址,offset页内偏移地址
{
control_start();//开控制
nand_reset();//复位
rNFCMD = NAND_CMD_READ_1st;
rNFADDR = 0;
rNFADDR = 0;
rNFADDR = paddr&0xff;
rNFADDR = (paddr>>8)&0xff;
rNFADDR = (paddr>>16)&0xff;
rNFCMD = NAND_CMD_READ_2st;
detect_nand_busy();
rNFCMD = NAND_CMD_RANDOM_READ_1st;
rNFADDR = offset&0xff; //写入页内偏移地址
rNFADDR = (offset>>8)&0xff;
rNFCMD = NAND_CMD_RANDOM_READ_2st;
*data = rNFDATA8;
control_end();
return 1;
}
int nand_random_write(U32 paddr,U32 offset,U8 data)//随机写,paddr页地址,offset页内偏移地址
{
control_start();//开控制
nand_reset();//复位
rNFCMD = NAND_CMD_WRITE_PAGE_1st;
rNFADDR = 0;
rNFADDR = 0;
rNFADDR = paddr&0xff;
rNFADDR = (paddr>>8)&0xff;
rNFADDR = (paddr>>16)&0xff;
rNFCMD = NAND_CMD_RANDOM_WRITE;
rNFADDR = offset&0xff; //写入页内偏移地址
rNFADDR = (offset>>8)&0xff;
rNFDATA8 = data;
rNFCMD = NAND_CMD_WRITE_PAGE_2st;
detect_nand_busy();//检测忙
rNFCMD =NAND_CMD_READ_STATUS; //读状态
if (rNFDATA8&1)
{
#ifdef NAND_DEBUG
Uart_Printf("/r/n Error:nand random write error... paddr=0x%x,offset=0x%x ",paddr,offset);
#endif
return -1;//删除错误返回0
}
control_end();
return 1;//成功返回1
}
void nand_test_bad_block(void)//测试坏块函数,并标记spare区最后一个地址,如果非0xff则为坏块
{
U8 dest[64*2048];//一个块的main区容量
U8 src [64*2048];
U32 i,k;
#ifdef NAND_DEBUG
Uart_Printf("/r/n test and mask bad block is begain. /r/n");
#endif
//
//main区检测
for (i=0;i<64*2048;i++)
{
dest[i]=0xff;//初始化缓冲区
src [i]=0;
}
//删除所有块
for (i=0;i<MAX_NAND_BLOCK;i++)
{
nand_block_erase(i);
}
for (i=0;i<MAX_NAND_BLOCK;i++)//测试所有块的main区是否有坏块
{
nand_page_write(i*64,src,64*2048);
nand_page_read(i*64,dest,64*2048);//使用了ecc校验读出来即可登记坏块信息
}
for (i=0;i<64*2048;i++)
{
dest[i]=0;//初始化缓冲区
src [i]=0xff;
}
//删除所有块
for (i=0;i<MAX_NAND_BLOCK;i++)
{
nand_block_erase(i);
}
for (i=0;i<MAX_NAND_BLOCK;i++)//测试所有块的main区是否有坏块
{
nand_page_write(i*64,src,64*2048);
nand_page_read(i*64,dest,64*2048);//使用了ecc校验读出来即可登记坏块信息
}
//
//spare区检测
for (i=0;i<64;i++)
{
dest[i]=0xff;//初始化缓冲区
src [i]=0;
}
//删除所有块
for (i=0;i<MAX_NAND_BLOCK;i++)
{
nand_block_erase(i);
}
for (i=0;i<MAX_NAND_BLOCK*64;i++)//测试所有块的spare区是否有坏块
{
if ( nand_bbi.area[i/64] ==1 )//如果是坏块则跳过
continue;
for (k=0;k<64;k++)
{
nand_random_write(i,2048+k,src[k]);
nand_random_read(i,2048+k,&dest[k]);
if (dest[k]!=src[k])//不相等则登记为坏块
{
nand_mask_bad_block(i/64);
break;
}
}
}
for (i=0;i<64;i++)
{
dest[i]=0x0;//初始化缓冲区
src [i]=0xff;
}
//删除所有块
for (i=0;i<MAX_NAND_BLOCK;i++)
{
nand_block_erase(i);
}
for (i=0;i<MAX_NAND_BLOCK*64;i++)//测试所有块的spare区是否有坏块
{
if ( nand_bbi.area[i/64] ==1 )//如果是坏块则跳过
continue;
for (k=0;k<64;k++)
{
nand_random_write(i,2048+k,src[k]);
nand_random_read(i,2048+k,&dest[k]);
if (dest[k]!=src[k])//不相等则登记为坏块
{
nand_mask_bad_block(i/64);
break;
}
}
}
#ifdef NAND_DEBUG
Uart_Printf("/r/n test and mask bad block is over. /r/n");
#endif
}
void nand_flash_init(void)//初始化
{
#ifdef NAND_DEBUG
Uart_Printf("/r/nNAND FLASH init");//
#endif
//中断入口地址
pISR_NFCON = (U32)nandINT;
//配置GPIO
rGPGUP |= 0x7<<13; //GPG13~15关闭上位
rGPGCON &= ~((U32)0x3f<<26);//GPG13~15为输入
//初始化各寄存器
S3C2440_NFCONF_init();
S3C2440_NFCONT_init();
S3C2440_NFSTAT_init();
S3C2440_NFESTAT0_init();
S3C2440_NFESTAT1_init();
//关于中断
rINTMSK &= ~(0x1<<INT_NFCON) ; //中断
rINTMOD &= ~(0x1<<INT_NFCON); //IRQ
rSRCPND |= 0x1<<INT_NFCON; //清中断标志位
rINTPND |= 0x1<<INT_NFCON; //清中断标志位
init_nand_bbi();//初始化全局变量
nand_read_id();//读ID
nand_test_bad_block();//测试并登记坏块
}