S3C2440-Nandflash

发布时间:2011-3-28 14:03    发布者:techshare
关键词: nandflash , S3C2440
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;   
}

作者:李万鹏
本文地址:https://www.eechina.com/thread-60214-1-1.html     【打印本页】

本站部分文章为转载或网友发布,目的在于传递和分享信息,并不代表本网赞同其观点和对其真实性负责;文章版权归原作者及原出处所有,如涉及作品内容、版权和其它问题,我们将根据著作权人的要求,第一时间更正或删除。
银洋电子 发表于 2011-4-1 16:43:49
顶一个,,,,
您需要登录后才可以发表评论 登录 | 立即注册

厂商推荐

关于我们  -  服务条款  -  使用指南  -  站点地图  -  友情链接  -  联系我们
电子工程网 © 版权所有   京ICP备16069177号 | 京公网安备11010502021702
快速回复 返回顶部 返回列表