S3C2440 NandFlash 读写操作

Nand Flash 就是一个flash闪存 ,存储数据

而S3C2440A 又为Nand Flash 配有相应的控制寄存器,可以直接将Flash 接上处理器

整个代码:

//////////////////LAB13  NandrFlash 应用实验
#define GLOBAL_CLK     1





#include"def.h"
#include"2440addr.h"
#include"2440lib.h"
#include"Nand.h"



/*
//////////////////////////////////////////////////////////////
#define SUM_BLOCKS  2048
#define PAGES_PER_BLOCK    64
#define DATA_BYTES_PER_PAGE  2048
#define SPARE_BYTES_PER_PAGE 64
////////
#define NUM_BLOCKS            0x1000    //  64 MB Smartmedia card.
#define SECTOR_SIZE            512
#define SPARE_SIZE            16
#define PAGES_PER_BLOCK            32

//  For flash chip that is bigger than 32 MB, we need to have 4 step address
//  
#define NFCONF_INIT            0xF830  // 512-byte 4 Step Address
#define NEED_EXT_ADDR            1
//#define NFCONF_INIT            0xA830  // 256-byte 4 Step Address
//#define NEED_EXT_ADDR            0

//#define NFCONF_INIT            0xF840

//  NAND Flash Command. This appears to be generic across all NAND flash chips
#define CMD_READ            0x00    //  Read
#define CMD_READ1            0x01    //  Read1
#define CMD_READ2            0x50    //  Read2
#define CMD_READ3            0x30    //  Read3
#define CMD_READID            0x90    //  ReadID
#define CMD_WRITE1            0x80    //  Write phase 1
#define CMD_WRITE2            0x10    //  Write phase 2
#define CMD_ERASE1            0x60    //  Erase phase 1
#define CMD_ERASE2            0xd0    //  Erase phase 2
#define CMD_STATUS            0x70    //  Status read
#define CMD_RESET            0xff    //  Reset

//  Status bit pattern
#define STATUS_READY            0x40    //  Ready
#define STATUS_ERROR            0x01    //  Error

//  Status bit pattern
#define STATUS_READY            0x40
#define STATUS_ERROR            0x01

#define NF_CMD(cmd)            {rNFCMD  = (cmd); }
#define NF_ADDR(addr)            {rNFADDR = (addr); }    
#define NF_nFCE_L()            {rNFCONT &= ~(1<<1); }  //FCE 片选低电平信号 Enable
#define NF_nFCE_H()            {rNFCONT |= (1<<1); }   // FCE   Disable     
#define NF_RSTECC()            {rNFCONT |= (1<<4); }   //Init Ecc decoder/encoder
#define NF_RDMECC()            (rNFMECC0 )
#define NF_RDSECC()            (rNFSECC )
#define NF_RDDATA()            (rNFDATA)
#define NF_RDDATA8()            (rNFDATA8)
#define NF_WRDATA(data)            {rNFDATA = (data); }

#define NF_WAITRB()            {while(!(rNFSTAT&(1<<0)));}       //等待nandflash不忙 NFSTAT[0]  0 for busy , 1 for ready to operate
#define NF_CLEAR_RB()            {rNFSTAT |= (1<<2); }         //清除RnB信号 to clear this value write 1
#define NF_DETECT_RB()            {while(!(rNFSTAT&(1<<2)));}   //等待RnB信号变高,waiting for ready from busy
#define NF_MECC_UnLock()        {rNFCONT &= ~(1<<5); }        //解锁main区ECC
#define NF_MECC_Lock()            {rNFCONT |= (1<<5); }         //锁定main区ECC
#define NF_SECC_UnLock()        {rNFCONT &= ~(1<<6); }        //解锁spare区ECC
#define NF_SECC_Lock()            {rNFCONT |= (1<<6); }         //锁定spare区ECC

#define    RdNFDat8()            (rNFDATA8)    //byte access
#define    RdNFDat()            RdNFDat8()    //for 8 bit nand flash, use byte access
#define    WrNFDat8(dat)            (rNFDATA8 = (dat))    //byte access
#define    WrNFDat(dat)            WrNFDat8(dat)    //for 8 bit nand flash, use byte access

#define pNFCONF                rNFCONF
#define pNFCMD                rNFCMD  
#define pNFADDR                rNFADDR
#define pNFDATA                rNFDATA
#define pNFSTAT                rNFSTAT
#define pNFECC                rNFECC0  

#define NF_CE_L()            NF_nFCE_L()    //FCE 片选低电平信号 Enable
#define NF_CE_H()            NF_nFCE_H()
#define NF_DATA_R()            rNFDATA
#define NF_ECC()            rNFECC0

// HCLK=100Mhz
#define TACLS                1    // 1-clk(0ns)
#define TWRPH0                4    // 3-clk(25ns)
#define TWRPH1                0    // 1-clk(10ns)  //TACLS+TWRPH0+TWRPH1>=50ns

//////////////////////////
*/


#define CMD_RANDOMREAD1    (0x05)
#define CMD_RANDOMREAD2    (0xE0)
#define CMD_RANDOMWRITE    (0x80)



U8 myNF_RamdomRead(U32 page_number, U32 add)

{

       NF_nFCE_L();                    //打开nandflash片选

       NF_CLEAR_RB();               //清RnB信号

 

       NF_CMD(CMD_READ);           //页读命令周期  0x00h

 

       //写入5个地址周期

       NF_ADDR(0x00);                                            //列地址A0~A7

       NF_ADDR(0x00);                                            //列地址A8~A11

       NF_ADDR((page_number) & 0xff);                  //行地址A12~A19

       NF_ADDR((page_number >> 8) & 0xff);           //行地址A20~A27

       NF_ADDR((page_number >> 16) & 0xff);         //行地址A28

 

       NF_CMD(CMD_READ3);          //页读命令周期0x 30h

 

       NF_DETECT_RB();                    //等待RnB信号变高,即不忙

       NF_CMD(CMD_RANDOMREAD1);                 //随意读命令周期1

       //页内地址

       NF_ADDR((char)(add&0xff));                          //列地址A0~A7

       NF_ADDR((char)((add>>8)&0x0f));                 //列地址A8~A11

       NF_CMD(CMD_RANDOMREAD2);                //随意读命令周期2

      

       return NF_RDDATA8();               //读取数据

}

U8 myNF_RamdomWrite(U32 page_number, U32 add, U8 dat)

{

       U8 temp,stat;
       int i ;
 

       NF_nFCE_L();                    //打开nandflash片选

       NF_CLEAR_RB();               //清RnB信号

 

       NF_CMD(CMD_WRITE1);                //页写命令周期1 0x80h

 

       //写入5个地址周期

       NF_ADDR(0x00);                                     //列地址A0~A7

       NF_ADDR(0x00);                                     //列地址A8~A11

       NF_ADDR((page_number) & 0xff);           //行地址A12~A19

       NF_ADDR((page_number >> 8) & 0xff);    //行地址A20~A27

       NF_ADDR((page_number >> 16) & 0xff);  //行地址A28

 

       NF_CMD(CMD_RANDOMWRITE);                 //随意写命令 0x85h

       //页内地址

       NF_ADDR((char)(add&0xff));                   //列地址A0~A7

       NF_ADDR((char)((add>>8)&0x0f));          //列地址A8~A11


       WrNFDat8(dat);                          //写入数据


       NF_CMD(CMD_WRITE2);                //页写命令周期2  0x10h

      

       for(i = 0; i != 1000; ++i);                 //延时一段时间

   

       NF_CMD(CMD_STATUS);                        //读状态命令

   

       //判断状态值的第6位是否为1,即是否在忙,该语句的作用与NF_DETECT_RB();相同

     do{

       stat =  NF_RDDATA8();

     }while(!(stat&0x40));

   

    NF_nFCE_H();                    //关闭nandflash片选

 

    //判断状态值的第0位是否为0,为0则写操作正确,否则错误

   if (stat & 0x1)

       return 0x44;                  //失败
   else

   return 0x66;                  //成功

}

/*
  自定义坏块表示形式 每一页主数据区为2K == 2048bytes 即是0--2047
  2048--2051   为4字节的主数据区的ECC
  2052---2053   为2字节的spare区的ECC
  定义: 每块的第一页的  2054 处的1个字节,数据如果为0x33 则表示该为坏块
*/
static U8 myNF_IsBadBlock(U32 block)
{
   return myNF_RamdomRead(block*64, 2054);
}


static U8 myNF_MarkBadBlock(U32 block)
{
    U8 result;

   

       result = myNF_RamdomWrite(block*64, 2054, 0x33);

 

       if(result == 0x44)

            return 0x21;                  //写坏块标注失败
       else
            return 0x60;                  //写坏块标注成功
}



static U8 myNF_EraseBlock(U32 addr)

{

       char stat, temp;
       int i ;
       temp = myNF_IsBadBlock(addr);     //判断该块是否为坏块

       if(temp == 0x33)

              return 0x42;           //是坏块,返回

      

       NF_nFCE_L();             //打开片选

       NF_CLEAR_RB();        //清RnB信号

 

       NF_CMD(CMD_ERASE1);         //擦除命令周期1

 

       //写入3个地址周期,从A18开始写起

       NF_ADDR((addr << 6) & 0xff);         //行地址A18~A19   ----IO6 IO7

       NF_ADDR((addr >> 2) & 0xff);         //行地址A20~A27   ----IO0--IO7

       NF_ADDR((addr >> 10) & 0xff);        //行地址A28       ----IO0

      
       NF_CMD(CMD_ERASE2);         //擦除命令周期2

      for(i = 0 ; i != 1000 ; ++i) ;       //延时一段时间

      NF_CMD(CMD_STATUS);          //读状态命令

      
       //判断状态值的第6位是否为1,即是否在忙,该语句的作用与NF_DETECT_RB();相同
     do{

       stat = NF_RDDATA8();

     }while(!(stat&0x40));

   
     NF_nFCE_H();             //关闭nandflash片选

 

    //判断状态值的第0位是否为0,为0则擦除操作正确,否则错误

    if (stat & 0x1)

    {

       temp = myNF_MarkBadBlock(addr>>6);         //标注该块为坏块

       if (temp == 0x21)

              return 0x43;            //标注坏块失败
       else
              return 0x44;           //擦除操作失败

   }
    else

    return 0x66;                  //擦除操作成功

}



static void myNF_Reset(void)
{
    NF_CE_L();     //打开nandflash片选
    NF_CLEAR_RB();  //清除RnB信号
    NF_CMD(CMD_RESET);  
    NF_DETECT_RB();
    NF_CE_H();        //关闭nandflash片选
}

static void myInitNand(void)
{
      //配置芯片引脚
      rGPACON = (rGPACON &~(0x3f<<17) ) |(0x3f<<17);
      //TACLS=1、TWRPH0=2、TWRPH1=0,8位IO
      rNFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4)|(0<<0);
      //非锁定,屏蔽nandflash中断,初始化ECC及锁定main区和spare区ECC,使能nandflash片选及控制器
      rNFCONT = (0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(1<<6)|(1<<5)|(1<<4)|(1<<1)|(1<<0);
      rNFSTAT = 0;
}

static char myNF_ReadID()
{
    char    pMID;
    char    pDID;
    char cyc3, cyc4, cyc5;
    int    i;


    NF_nFCE_L();      //打开nandflash片选
    NF_CLEAR_RB();    //清除RnB信号
    NF_CMD(CMD_READID);    // read id command
    NF_ADDR(0x0);
    for ( i = 0; i < 100; i++ );

    pMID = NF_RDDATA8();  ///Maker code
    pDID = NF_RDDATA8();  ///Device code

    cyc3  = NF_RDDATA8();
    cyc4  = NF_RDDATA8();
    cyc5  = NF_RDDATA8();
    NF_nFCE_H();

    Uart_Printf("Maker Code:  %X\n\r",pMID);  //EC
    Uart_Printf("Device Code:  %X\n\r",pDID); //DA
    Uart_Printf("3rd Cyc Code:  %X\n\r",cyc3);  //10
    Uart_Printf("4th Cyc Code:  %X\n\r",cyc4);  //95
    Uart_Printf("5th Cyc Code:  %X\n\r",cyc5);  //44    
    return (pDID);
}

static void mySB_ReadPage(U32 addr, unsigned char * to)
{
    U32 i;

    myNF_Reset();

    //  Enable the chip
    NF_nFCE_L();
    NF_CLEAR_RB();

    // Issue Read command
    NF_CMD(CMD_READ);

    //  Set up address
    NF_ADDR(0x00);
    NF_ADDR((addr) & 0xff);
    NF_ADDR((addr >> 8) & 0xff);
    NF_ADDR((addr >> 16) & 0xff);


    NF_DETECT_RB();        // wait tR(max 12us)

    for (i = 0; i < 512; i++)
    {
        to[i] =  NF_RDDATA8();
    }

    NF_nFCE_H();

}

static void myLB_ReadPage(U32 addr, unsigned char * to)
{
    U32 i;

    myNF_Reset();

    //  Enable the chip
    NF_nFCE_L();   
    NF_CLEAR_RB();

    // Issue Read command
    NF_CMD(CMD_READ);   ////00

    //  Set up address
    NF_ADDR(0x00);     //column Address  A0 --A7
    NF_ADDR(0x00);     //column Address  A8 --A11
    NF_ADDR((addr) & 0xff);       //Row  Address A12--A19
    NF_ADDR((addr >> 8) & 0xff);  //Row  Address A20--A27
    NF_ADDR((addr >> 16) & 0xff); //Row  Address A28

    NF_CMD(CMD_READ3);   ///30

    NF_DETECT_RB();        // wait tR(max 12us)

    for (i = 0; i < 2048; i++)
    {
        to[i] =  NF_RDDATA8();
    }

    NF_nFCE_H();

}


static unsigned char myNF_ReadPage(U32 addr,unsigned char * to )
{
    U32 i;
    U32 mecc0, secc;
    
    NF_RSTECC();
    NF_MECC_UnLock();          //解锁main区ECC
    //  Enable the chip
    NF_nFCE_L();   
    NF_CLEAR_RB();

    // Issue Read command
    NF_CMD(CMD_READ);   ////00

    //  Set up address
    NF_ADDR(0x00);     //column Address  A0 --A7
    NF_ADDR(0x00);     //column Address  A8 --A11
    NF_ADDR((addr) & 0xff);       //Row  Address A12--A19
    NF_ADDR((addr >> 8) & 0xff);  //Row  Address A20--A27
    NF_ADDR((addr >> 16) & 0xff); //Row  Address A28

    NF_CMD(CMD_READ3);   ///30

    NF_DETECT_RB();        // wait tR(max 12us)

    for (i = 0; i < 2048; i++)
    {
        to[i] =  NF_RDDATA8();
    }

    NF_MECC_Lock();                     //锁定main区ECC值
    
    NF_SECC_UnLock();                  //解锁spare区ECC
    mecc0=NF_RDDATA();        //读spare区的前4个地址内容,即第2048~2051地址,这4个字节为main区的ECC

       //把读取到的main区的ECC校验码放入NFMECCD0/1的相应位置内

       rNFMECCD0=((mecc0&0xff00)<<8)|(mecc0&0xff);

       rNFMECCD1=((mecc0&0xff000000)>>8)|((mecc0&0xff0000)>>16);

            

       NF_SECC_Lock();               //锁定spare区的ECC值

 

       secc=NF_RDDATA();           //继续读spare区的4个地址内容,即第2052~2055地址,其中前2个字节为spare区的ECC值

       //把读取到的spare区的ECC校验码放入NFSECCD的相应位置内

       rNFSECCD=((secc&0xff00)<<8)|(secc&0xff);
    
    NF_nFCE_H();
    
    
    //判断所读取到的数据是否正确

       if ((rNFESTAT0&0xf) == 0x0)

              return 0x66;                  //正确

        else

              return 0x44;                  //错误
}

static U8 myNF_WritePage(U32 addr,unsigned char * from )
{
       U32 i, mecc0, secc;

       U8 stat, temp;
       U8 ECCBuf[6];
       int j;

       temp = myNF_IsBadBlock(addr>>6);              //判断该块是否为坏块

       if(temp == 0x33)

              return 0x42;           //是坏块,返回

 

       NF_RSTECC();                   //复位ECC

       NF_MECC_UnLock();          //解锁main区的ECC

 

       NF_nFCE_L();             //打开nandflash片选

       NF_CLEAR_RB();        //清RnB信号

 

       NF_CMD(CMD_WRITE1);                //页写命令周期1

 

       //写入5个地址周期

       NF_ADDR(0x00);                                     //列地址A0~A7

       NF_ADDR(0x00);                                     //列地址A8~A11

       NF_ADDR((addr) & 0xff);           //行地址A12~A19

       NF_ADDR((addr >> 8) & 0xff);    //行地址A20~A27

       NF_ADDR((addr >> 16) & 0xff);  //行地址A28

      
      
       //写入一页数据

       for (i = 0; i < 2048; i++)

       {
             //Uart_Printf("Write  %X  \n\r",from[i]);
              WrNFDat8((unsigned char)(from[i]));
              
              
       }

      

       NF_MECC_Lock();                     //锁定main区的ECC值

      

       mecc0=rNFMECC0;                    //读取main区的ECC校验码

       //把ECC校验码由字型转换为字节型,并保存到变量数组ECCBuf中

       ECCBuf[0]=(U8)(mecc0&0xff);

       ECCBuf[1]=(U8)((mecc0>>8) & 0xff);

       ECCBuf[2]=(U8)((mecc0>>16) & 0xff);

       ECCBuf[3]=(U8)((mecc0>>24) & 0xff);

 

       NF_SECC_UnLock();                  //解锁spare区的ECC

       //把main区的ECC值写入到spare区的前4个字节地址内,即第2048~2051地址

       for(i=0;i<4;i++)

       {
               
              WrNFDat8(ECCBuf[i]);

      }

 

       NF_SECC_Lock();                      //锁定spare区的ECC值

       secc=rNFSECC;                   //读取spare区的ECC校验码

       //把ECC校验码保存到全局变量数组ECCBuf中

       ECCBuf[4]=(U8)(secc&0xff);

       ECCBuf[5]=(U8)((secc>>8) & 0xff);

       //把spare区的ECC值继续写入到spare区的第2052~2053地址内

       for(i=4;i<6;i++)

       {

              WrNFDat8(ECCBuf[i]);

       }

 

       NF_CMD(CMD_WRITE2);                //页写命令周期2

 

      for(i = 0 ; i != 1000; ++i)  
          ;

   

      NF_CMD(CMD_STATUS);                 //读状态命令 0X70H

   

       //判断状态值的第6位是否为1,即是否在忙,该语句的作用与NF_DETECT_RB();相同

      do{

              stat = NF_RDDATA8();

      }while(!(stat&0x40));   /// stat == x04  is ready    

   

     NF_nFCE_H();                    //关闭nandflash片选

 

    //判断状态值的第0位是否为0,为0则写操作正确,否则错误

     if (stat & 0x1)

     {

       temp = myNF_MarkBadBlock(addr>>6);         //标注该页所在的块为坏块

       if (temp == 0x21)

              return 0x43;            //标注坏块失败

       else

              return 0x44;           //写操作失败

    }
     else

     return 0x66;                  //写操作成功  
}



//////////////
static U32 cpu_freq;
static U32 UPLL;

static void cal_cpu_bus_clk(void)
{
    U32 val;
    U8 m, p, s;
    
    val = rMPLLCON;
    m = (val>>12)&0xff;
    p = (val>>4)&0x3f;
    s = val&3;

    //(m+8)*FIN*2 不要超出32位数!
    FCLK = ((m+8)*(FIN/100)*2)/((p+2)*(1<<s))*100;
    
    val = rCLKDIVN;
    m = (val>>1)&3;
    p = val&1;    
    val = rCAMDIVN;
    s = val>>8;
    
    switch (m) {
    case 0:
        HCLK = FCLK;
        break;
    case 1:
        HCLK = FCLK>>1;
        break;
    case 2:
        if(s&2)
            HCLK = FCLK>>3;
        else
            HCLK = FCLK>>2;
        break;
    case 3:
        if(s&1)
            HCLK = FCLK/6;
        else
            HCLK = FCLK/3;
        break;
    }
    
    if(p)
        PCLK = HCLK>>1;
    else
        PCLK = HCLK;
    
    if(s&0x10)
        cpu_freq = HCLK;
    else
        cpu_freq = FCLK;
        
    val = rUPLLCON;
    m = (val>>12)&0xff;
    p = (val>>4)&0x3f;
    s = val&3;
    UPLL = ((m+8)*FIN)/((p+2)*(1<<s));
    UCLK = (rCLKDIVN&8)?(UPLL>>1):UPLL;
}

void Main(void)
{
    U8 buff[2112];
    int i ;
    U8  ret;
    U16 temp = 0 ;
    U32  flashid;
    char ID ;
     
    ChangeClockDivider(3,1);///1:3:6
    //ChangeMPllValue(127,2,1);///405MHz
     ChangeMPllValue(60,4,1);///95.96MHz
    cal_cpu_bus_clk();
   // Port_Init();
    //Isr_Init();
    
    Uart_Init(0,115200);
    Uart_Select(0);
    Uart_Printf("the main is running \n\r");
    
    myInitNand();
    ID=  myNF_ReadID();
    
    Uart_Printf("the ID is %x  \n\r",ID);
     
     myNF_ReadPage(0,buff);
     for( i = 0; i != 2048 ; ++i)
       Uart_Printf("%X  ",buff[i]);
       Uart_Printf("\n\r\n\r\n\r");
     
      
      myNF_EraseBlock(0);
     //memset(buff,0,2048*sizeof(U8));      
     memset(buff,0xAA,2048*sizeof(U8));
     for(i=0; i!= 1000; ++i);
      
     ret = myNF_WritePage(0,buff);
     if(0x66 == ret )
     {
       Uart_Printf("data write  success  %X \n\r",ret);
       for( i = 0; i != 2048 ; ++i)
          Uart_Printf("%X  ",buff[i]);
       Uart_Printf("\n\r\n\r\n\r");
       
     }
     else
     {
        Uart_Printf("data write  error %X  \n\r",ret);
     }
     
     for(i=0; i!= 1000; ++i);    
     myNF_ReadPage(0,buff);
     for( i = 0; i != 2048 ; ++i)
       Uart_Printf("%X  ",buff[i]);
     Uart_Printf("\n\r\n\r\n\r");  
      
       
}

//////////////////////////////////////////////////////

整个数据中,关键是要看S3C2440A的芯片手册,和Nand Flash 的手册,我的Nand Flash是K9F2G08U0B ,整个Flash的

Commad Sets ,其实整个数据的读取,还是对其他设备编程一样,主要是开始对什么是Nand Flash根本不知道,还是以为NanaFlash 和RAM是不是差不多,但是到现在才感觉到,NandFlash就是一个i数据存储设备,而且块设备,是用来做为数据存储用的,只是S3C2440A里面集成了对应NandFlash的接口芯片,这样就可以直接就将这外部存储设备直接硬连接到总线上。

既然和一般的通过接口芯片控制的设备一样,那么控制方式也是一样,软件控制硬件,软件改变控制器中寄存器的数据,修改电平,控制硬件工作。

在看NandFlash的过程中,主要是Command Sets需要知道,然后就是NandFlash IC 的数据手册,确实自己看得太少这种介绍硬件IC的手册,根本不懂怎么去看,凭着学数字电路的记忆,靠着感觉来把整个数据手册给看了,而且还是英语,看起来的速度还是慢。

要点:

1,地址空间,我这是共有2048个block,每个block有64个page ,每页 是 2K+64BYTES ,2K是主数据存储区域,存储用户数据,而64所在的spare区域,前4Bytes和2bytes是用来存储MECC和SECC的,64-4-2=58 bytes的空间,其实是可以用来自定义使用的 ,在定义模块为坏块的时候,就可以在这个区域里,加上一个标志,标识其为坏块。

2,地址,在读取和写入数据的时候,因为存储空间的原因,所以地址需要29位,而这29是分5次来传输的,column值页内地址,而row值是页地址;还有就是,因为每页的地址空间是2K+64,所以地址长度是12位,而不是11位。

3,锁ECC和解锁ECC,其是要锁的目标是NandFlash控制中的ECC纠错码产生器,而不是NandFlash中的ECC存储区;锁,就是硬件计算ECC的功能关闭

4,往NandFlash写入数据前,需要将该block Erase ,然后在写入数据,否则,写零可以直接写入,但是想在对应的bit上面写1的话,就就是不可能的。

你可能感兴趣的:(S3C2440 NandFlash 读写操作)