S3C2440-Nandflash

阅读更多

哈尔滨理工大学软件工程专业08-7李万鹏原创作品,转载请标明出处

http://blog.csdn.net/woshixingaaa/archive/2011/01/23/6159940.aspx

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

三星公司是最主要的nandflash供应商,因此在它所开发的各类处理器中,实现对nandflash的支持就不足为奇了。s3c2440不仅具有nandflash的接口,而且还可以利用某些机制实现直接从nandflash启动并运行程序。本文只介绍如何对nandflash实现读、写、擦除等基本操作,不涉及nandflash启动程序的问题。

在这里,我们使用的nandflash为K9F2G08U0A,它是8位的nandflash。不同型号的nandflash的操作会有所不同,但硬件引脚基本相同,这给产品的开发带来了便利。因为不同型号的PCB板是一样的,只要更新一下软件就可以使用不同容量大小的nandflash。

K9F2G08U0A的一页为(2K+64)字节(加号前面的2K表示的是main区容量,加号后面的64表示的是spare区容量),它的一块为64页,而整个设备包括了2048个块。这样算下来一共有2112M位容量,如果只算main区容量则有256M字节(即256M×8位)。要实现用8个IO口来要访问这么大的容量,K9F2G08U0A规定了用5个周期来实现。第一个周期访问的地址为A0~A7;第二个周期访问的地址为A8~A11,它作用在IO0~IO3上,而此时IO4~IO7必须为低电平;第三个周期访问的地址为A12~A19;第四个周期访问的地址为A20~A27;第五个周期访问的地址为A28,它作用在IO0上,而此时IO1~IO7必须为低电平。前两个周期传输的是列地址,后三个周期传输的是行地址。通过分析可知,列地址是用于寻址页内空间,行地址用于寻址页,如果要直接访问块,则需要从地址A18开始。

#include "2440addr.h" #define CMD_READ1 0x00 #define CMD_READ2 0x30 #define CMD_READID 0x90 #define CMD_RESET 0xFF #define CMD_WRITE1 0x80 #define CMD_WRITE2 0x10 #define CMD_BLOCKERASE1 0x60 #define CMD_BLOCKERASE2 0xD0 #define CMD_RANDOMWRITE 0x85 #define CMD_RANDOMREAD1 0x05 #define CMD_RANDOMREAD2 0xE0 #define CMD_READSTATE 0x70 #define NF_CMMD(cmd) rNFCMD = cmd #define NF_ADDR(addr) rNFADDR = addr #define NF_WRDATA(data) rNFDATA = data #define NF_WRDATA8(data) rNFDATA8 = data #define NF_RDDATA() rNFDATA #define NF_RDDATA8() rNFDATA8 #define NF_CE_L() rNFCONT &= ~(0x1<<1) #define NF_CE_H() rNFCONT |= 0x1<<1 #define NF_MECC_LOCK() rNFCONT |= 0x1<<5 #define NF_MECC_ULOCK() rNFCONT &= ~(0x1<<5) #define NF_SECC_LOCK() rNFCONT |= 0x1<<6 #define NF_SECC_ULOCK() rNFCONT &= ~(0x1<<6) #define NF_RESETECC() rNFCONT |= 0x1<<4 #define NF_WAITRB() while(!(rNFSTAT&0x1)) #define NF_CLEARRB() rNFSTAT |= 0x1<<2 #define NF_DETECT() while(!(rNFSTAT&0x1<<2)) #define TACLS 1 #define TWRPH0 1 #define TWRPH1 1 #define U32 unsigned int #define U8 unsigned char U8 buffer[2048], Ecc[6]; U8 cmd, data, command; U32 block, add, pagenumber, count; U8 NF_BlockErase(U32 block){ //擦除以块为单位 U8 state; NF_CE_L(); //打开nandflash片选 NF_CLEARRB(); //等待R/nB信号就绪 NF_CMMD(CMD_BLOCKERASE1); NF_ADDR((block<<6)&0xff); NF_ADDR((block>>2)&0xff); NF_ADDR((block>>10)&0xff); NF_CMMD(CMD_BLOCKERASE2); NF_WAITRB(); NF_CMMD(CMD_READSTATE); do{ state = NF_RDDATA8(); }while(!(state&0x40)); if(state&0x1){ while(!(rUTRSTAT0&0x4)); rUTXH0 = 0x40; return 0x40; //0x40块擦除失败 } else{ while(!(rUTRSTAT0&0x4)); rUTXH0 = 0x60; return 0x60; //0x60块擦除成功 } } U8 NF_PageWrite(U32 pagenumber){ U32 i, mecc, secc; U8 state; NF_CE_L(); NF_RESETECC(); //复位ECC NF_CLEARRB(); NF_CMMD(CMD_WRITE1); NF_ADDR(0x00); NF_ADDR(0x00); NF_ADDR(pagenumber&0xff); NF_ADDR((pagenumber>>8)&0xff); NF_ADDR((pagenumber>>16)&0xff); //先解锁main区,然后在main区读写,产生main区ECC校验,然后锁定ECC,这样ECC就被硬件写入rNFMECC0/1,我们将它读到spare //区应该存放校验的位置2048~2051。在读写spare区的时候,即产生spare区的ECC,硬件把它自动写入rNFSECC中,会产生spare区 //的校验,两个字节,写到2052~2053,在读取的时候,我们将main区的ECC和spare区的ECC读出来,放入rNFMECCD0/1和rNFSECC中, //硬件完成rNFMECC0/1,rNFSECC和rNFMECCD0/1,rNFSECCD的校验。 NF_MECC_ULOCK(); //解锁main区的ECC for(i = 0; i < 2048; i++){ NF_WRDATA8((char)(i+1)); //这个过程中产生ECC } NF_MECC_LOCK(); //锁定main区ECC mecc = rNFMECC0; //读取main区ECC Ecc[0] = (U8)(mecc&0xff); Ecc[1] = (U8)((mecc>>8)&0xff); Ecc[2] = (U8)((mecc>>16)&0xff); Ecc[3] = (U8)((mecc>>24)&0xff); NF_SECC_ULOCK(); //解锁main区的ECC for(i = 0; i < 4; i++){ NF_WRDATA8(Ecc[i]); //将maina区的ECC写入spare前4个字节,这个过程产生spare区的ECC } NF_SECC_LOCK(); //锁定spare区的ECC secc = rNFSECC; //读取spare区的ECC Ecc[4] = (secc)&0xff; Ecc[5] = (secc>>8)&0xff; for(i = 4; i < 6; i++){ NF_WRDATA8(Ecc[i]); //将spare区的ECC写入spare } NF_CMMD(CMD_WRITE2); NF_DETECT(); //等待R/nB信号变高,即不忙 NF_CMMD(CMD_READSTATE); //发读状态命令, 0x70 do{ state = NF_RDDATA8(); //检查状态 I/O 位0为0 是写成功 1 是失败, I/O 位6为0表示忙 为1是就绪 }while(!(state&0x40)); if(state&0x1){ while(!(rUTRSTAT0&0x4)); rUTXH0 = 0x43; return 0x43; //0x43随机写失败 } else{ while(!(rUTRSTAT0&0x4)); rUTXH0 = 0x63; return 0x63; //0x63随机写成功 } } U8 NF_PageRead(U32 pagenumber){ U32 i, mecc, secc; NF_CE_L(); NF_RESETECC(); NF_CLEARRB(); NF_CMMD(CMD_READ1); NF_ADDR(0x00); NF_ADDR(0x00); NF_ADDR(pagenumber&0xff); NF_ADDR((pagenumber>>8)&0xff); NF_ADDR((pagenumber>>16)&0xff); NF_CMMD(CMD_READ2); NF_WAITRB(); NF_MECC_ULOCK(); for(i = 0; i < 2048; i++){ buffer[i] = NF_RDDATA8(); } NF_MECC_LOCK(); NF_SECC_ULOCK(); mecc = NF_RDDATA(); NF_SECC_LOCK(); rNFMECCD0 = ((mecc&0xff00)<<8) | (mecc&0xff); //读取刚才的ECC 让rNFMECCD0/1,rNFSECCD与rNFMECC0/1,RNFSECC比较,看是否发生错误 rNFMECCD1 = ((mecc&0xff000000)>>8) | ((mecc&0xff0000)>>16); //校验是因为nandflash很容易发生位反转,坏块 secc = NF_RDDATA(); rNFSECCD = ((secc&0xff00)<<8)|(secc&0xff); NF_CE_H(); if((rNFESTAT0 & 0x0f) == 0x0){ //如果低4位都是0,说明没有错误 while(!(rUTRSTAT0&0x4)); rUTXH0 = 0x66; for(i = 0; i < 8; i++){ while(!(rUTRSTAT0&0x4)); rUTXH0 = buffer[i]; } return 0x66; } else{ while(!(rUTRSTAT0&0x4)); rUTXH0 = 0x44; return 0x44; } } void NF_Init(){ //GPA 17~22接在nandflash上 rGPACON = rGPACON & (~(0x3f<<17)) | (0x3f<<17); //TACLS为CLE/ALE有效到nWE有效之间的持续时间,TWRPH0为nWE的有效持续时间,TWRPH1为nWE无效到CLE/ALE无效之间的持续时间,这些时间都是以HCLK为单位的(本文程序中的HCLK=100MHz) rNFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4)|(0<<0); //非锁定,屏蔽nandflash中断,初始化ECC及锁定main区和spare区ECC,使能nandflash片选及控制器 rNFCONT = (0<<13)|(0<<12)|(1<<6)|(1<<5)|(1<<4)|(1<<1)|(1<<0); } void NF_Reset(){ NF_CE_L(); NF_WAITRB(); NF_CMMD(CMD_RESET); NF_DETECT(); NF_CE_H(); } void NF_ReadID(){ char MID, DID, cyc3, cyc4, cyc5; NF_CE_L(); NF_CLEARRB(); NF_CMMD(CMD_READID); NF_ADDR(0x00); MID = NF_RDDATA8(); //厂商ID while(!(rUTRSTAT0&0x4)); rUTXH0 = MID; DID = NF_RDDATA8(); //设备ID while(!(rUTRSTAT0&0x4)); rUTXH0 = DID; cyc3 = NF_RDDATA8(); //其他信息 while(!(rUTRSTAT0&0x4)); rUTXH0 = cyc3; cyc4 = NF_RDDATA8(); while(!(rUTRSTAT0&0x4)); rUTXH0 = cyc4; cyc5 = NF_RDDATA8(); while(!(rUTRSTAT0&0x4)); rUTXH0 = cyc5; NF_CE_H(); } U8 NF_RandomWrite(U32 pagenumber, U32 add, U8 data){ //根据时序来就行 U8 state; NF_CE_L(); NF_CMMD(CMD_WRITE1); NF_ADDR(0x00); NF_ADDR(0x00); NF_ADDR(pagenumber&0xff); NF_ADDR((pagenumber>>8)&0xff); NF_ADDR((pagenumber>>16)&0xff); NF_CMMD(CMD_RANDOMWRITE); NF_ADDR(add&0xff); //确定页内地址 NF_ADDR((add>>8)&0xff); NF_WRDATA8(data); //写数据 NF_CMMD(CMD_WRITE2); NF_WAITRB(); NF_CMMD(CMD_READSTATE); do{ state = NF_RDDATA8(); }while(!(state&0x40)); if(state&0x1){ while(!(rUTRSTAT0&0x4)); rUTXH0 = 0x44; return 0x44; //0x44随机写失败 } else{ while(!(rUTRSTAT0&0x4)); rUTXH0 = 0x66; return 0x66; //0x66随机写成功 } } U8 NF_RandomRead(U32 pagenumber, U32 add){ //根据时序来就行 char ch; NF_CE_L(); NF_CMMD(CMD_READ1); NF_ADDR(0x00); NF_ADDR(0x00); NF_ADDR(pagenumber&0xff); NF_ADDR((pagenumber>>8)&0xff); NF_ADDR((pagenumber>>16)&0xff); NF_CMMD(CMD_READ2); NF_WAITRB(); NF_CMMD(CMD_RANDOMREAD1); NF_ADDR(add&0xff); NF_ADDR((add>>8)&0xff); NF_CMMD(CMD_RANDOMREAD2); ch = NF_RDDATA8(); //读数据 while(!(rUTRSTAT0&0x4)); rUTXH0 = ch; NF_CE_H(); return ch; } U8 NF_IsBadBlock(U32 block){ U8 result; result = NF_RandomRead(block*64, 2054); //0~2047是main区数据,2048~2051是main区的ECC校验,2052~2053是spare区的ECC校验 if(result == 0x33){ while(!(rUTRSTAT0&0x4)); rUTXH0 = 0x62; return 0x62; //0x62是坏块 } else{ while(!(rUTRSTAT0&0x4)); rUTXH0 = 0x42; return 0x42; //0x42不是坏块 } } U8 NF_MarkBadblock(U32 block){ U8 result; result = NF_RandomWrite(block*64, 2054, 0x33); //0x33标记坏块 block*64得到的是block块第一个页的地址 if(result == 0x66){ while(!(rUTRSTAT0&0x4)); rUTXH0 = 0x61; return 0x61; //0x61标记坏块成功 } else{ while(!(rUTRSTAT0&0x4)); rUTXH0 = 0x41; return 0x41; //0x41标记坏块失败 } } void __irq UART0_ISR(void){ U8 ch; rSRCPND |= (0x1<<28); rINTPND |= (0x1<<28); rSUBSRCPND |= 0x1<<0; if(rUTRSTAT0 & 0x1){ ch = rURXH0; while(!(rUTRSTAT0&0x4)); rUTXH0 = ch; cmd = ch; } } int Main(){ count = 0; rULCON0 = 0x3; rUCON0 = (1<<11)|(1<<2)|(1<<0); rUBRDIV0 = 26; rSRCPND = 0x1<<28; rINTPND = 0x1<<28; rSUBSRCPND = 0x1<<0; rINTMSK &= ~(0x1<<28); rINTSUBMSK &= ~(0x1<<0); pISR_UART0 = (U32)UART0_ISR; NF_Init(); while(1){ switch(cmd){ case 0x11: NF_Reset(); break; case 0x22: NF_ReadID(); break; case 0x33: NF_RandomRead(2001*64,0x3); break; case 0x44: NF_RandomWrite(2001*64,2052,0xbc); break; case 0x55: NF_PageRead(2001*64); break; case 0x66: while(!(rUTRSTAT0&0x4)); rUTXH0 = 0xba; NF_PageWrite(2001*64); break; case 0x77: NF_BlockErase(2001); break; case 0x88: NF_IsBadBlock(2001); break; case 0x99: NF_MarkBadblock(2001); break; } cmd = 0; } return 0; }

你可能感兴趣的:(三星,算法,Blog)