标题:
S3C2440-Nandflash
[打印本页]
作者:
我是MT
时间:
2014-3-19 15:10
标题:
S3C2440-Nandflash
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_RE
ADI
D 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_READ
ST
ATE 0x70
#define NF_CMMD(cmd) r
NFC
MD = 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;
}
作者:李万鹏
欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/)
Powered by Discuz! 7.0.0