新闻中心

EEPW首页 > 嵌入式系统 > 设计应用 > S3C2440-Nandflash

S3C2440-Nandflash

作者: 时间:2016-12-02 来源:网络 收藏
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;
}
}
上一页 1 2 下一页

评论


技术专区

关闭