Chinaunix首页 | 论坛 | 博客
  • 博客访问: 670663
  • 博文数量: 255
  • 博客积分: 5000
  • 博客等级: 大校
  • 技术积分: 2811
  • 用 户 组: 普通用户
  • 注册时间: 2010-06-09 13:29
个人简介

IT业行者,行者无疆

文章分类

全部博文(255)

文章存档

2011年(121)

2010年(134)

我的朋友

分类: 嵌入式

2010-10-23 11:01:15

s3c2440对norflash的操作 
 
norflash和nandflash是应用不同技术而实现的非易失闪存。它们之间的各自特点在这里就不做介绍了,而只把s3c2440对norflash的操作做一讲解。我们用的norflash为EN29LV160AB,其实对各种型号的norflash进行读写等操作差别不大。
 
       对norflash的操作主要就是读、写、擦除和识别等。EN29LV160AB的数据宽度可以是8位字节型,也可以是16位的字型,它由EN29LV160AB的某一引脚配置实现的。在这里我们选择字型。
 
对norflash的读操作比较简单,系统上电后会自动进入读模式,而且也不需要额外的命令来实现读操作。下面的函数实现了读操作:
 
U16 read_en29lv160ab(U32 addr)
{
       return *((volatile U16 *)(addr));
}
 
       norflash不仅能够实现硬件复位,而且可以实现软件复位。软件复位的操作是向任一地址写入复位命令0xF0。下面的函数实现了软件复位:
 
void reset_en29lv160ab(void)
{
       *((volatile U16 *)0x0) = 0xf0;
}
 
       norflash的擦除操作和写操作要稍微复杂一些,它们需要4个或6个周期来完成,每一个周期都要把相应的命令写入norflash中的某一命令寄存器中。写操作的过程为第一个周期是把命令0xAA写入地址为0x555的命令寄存器中,第二个周期是把命令0x55写入地址为0x2AA命令寄存器中,第三个周期是把命令0xA0再写入地址为0x555命令寄存器中,第四个周期为真正地把要写入的数据写入到norflash的地址中。下面的函数实现了写操作,其中该函数的两个输入参数分别为要写入的数据和地址,为了方便,我们事先定义好命令寄存器:
 
#define    flash_base              0x00000000
#define    CMD_ADDR0              *((volatile U16 *)(0x555<<1+flash_base))
#define    CMD_ADDR1              *((volatile U16 *)(0x2aa<<1+flash_base))
 
U8 en29lv160ab_program(U32 addr, U16 dat)
{
       CMD_ADDR0 = 0xaa;
       CMD_ADDR1 = 0x55;
       CMD_ADDR0 = 0xa0;
       *((volatile U16 *)(addr)) = dat;
 
       return check_toggle();
}
 
由于我们是把norflash连接到了s3c2440的bank 0上,因此norflash中的地址相对于s3c2440来说基址为0x00000000。而之所以又把norflash中的地址向左移一位(即乘以2),是因为我们是把s3c2440的ADDR1连接到了norflash的A0上的缘故。在该函数中,我们还调用了check_toggle函数,它的作用是用于判断这次操作是否正确,它的原型为:
 
U8 check_toggle()
{
       volatile U16 newtoggle,oldtoggle;
       oldtoggle = *((volatile U16 *)0x0);
 
       while(1)
       {    
              newtoggle = *((volatile U16 *)0x0);
             
              if((oldtoggle & 0x40)==(newtoggle & 0x40))
                     break;
             
              if(newtoggle & 0x20)           //DQ5
              {
                     oldtoggle = *((volatile U16 *)0x0);
                     newtoggle = *((volatile U16 *)0x0);
                    
                     if((oldtoggle & 0x40)==(newtoggle & 0x40))
                            break;
                     else
                            return 0;         //错误
              }    
              oldtoggle = newtoggle;
       }
      
       return 1;         //正确
}
 
它的原理是连续两次读取数据总线上的数据,判断数据总线上的第6位数值(DQ6)是否翻转,如果没有翻转则正确,否则还要判断第5位(DQ5),以确定是否是因为超时而引起的翻转。
 
       写操作只能使“1”变为“0”,而只有擦除才能使“0”变为“1”。因此在写之前一定要先擦除。擦除分为块擦除和整片擦除。块擦除的过程为第一个周期是把命令0xAA写入地址为0x555的命令寄存器中,第二个周期是把命令0x55写入地址为0x2AA命令寄存器中,第三个周期是把命令0x80再写入地址为0x555命令寄存器中,第四个周期是把命令0xAA写入地址为0x555的命令寄存器中,第五个周期是把命令0x55再写入地址为0x2AA命令寄存器中,第六个周期是把命令0x30写入要擦除块的首地址内。下面的函数为块擦除,其中输入参数为要擦除块的首地址:
 
U8 en29lv160ab_sector_erase(U32 section_addr)
{
       CMD_ADDR0 = 0xaa;
       CMD_ADDR1 = 0x55;
       CMD_ADDR0 = 0x80;
       CMD_ADDR0 = 0xaa;
       CMD_ADDR1 = 0x55;
       *((volatile U16 *)(section_addr)) = 0x30;
      
       return check_toggle();
}
 
       对norflash另一个比较常用的操作是读取芯片的ID。读取厂商ID的过程为第一个周期是把命令0xAA写入地址为0x555的命令寄存器中,第二个周期是把命令0x55写入地址为0x2AA命令寄存器中,第三个周期是把命令0x90再写入地址为0x555命令寄存器中,第四个周期为读取地址为0x100中的内容,即厂商ID(0x1C)。读取设备ID的过程的前三个周期与读取厂商ID相同,第四个周期是读取地址为0x01中的内容,即设备ID(0x2249)。下面的函数为读取芯片ID:
 
U32 get_en29lv160ab_id(void)
{
       U32 temp=0;
       CMD_ADDR0 = 0xaa;
       CMD_ADDR1 = 0x55;
       CMD_ADDR0 = 0x90; 
       temp = (*(volatile unsigned short *)(flash_base+ (0x100<<1)))<<16;
       temp |= *(volatile unsigned short *)(flash_base + (1<<1));
      
       return temp;
}
 
下面的程序实现了对一块区域进行擦除,写入,并读出的操作,判断写入的数据是否与读出的数据相同:
 
……   ……
U16 buffer[1024];
char cmd;
……   ……
 
void test_en29lv160ab(void)
{
       U32 temp;
       U8 sta;
       int i;
      
       for(i=0;i<1024;i++)
       buffer[i]=2*i+1;
          
       //读ID
temp = get_en29lv160ab_id();
       while(!(rUTRSTAT0 & 0x2)) ;
       rUTXH0=(U8)((temp&0xff000000)>>24);
       while(!(rUTRSTAT0 & 0x2)) ;
       rUTXH0=(U8)((temp&0x00ff0000)>>16);
       while(!(rUTRSTAT0 & 0x2)) ;
       rUTXH0=(U8)((temp&0x0000ff00)>>8);
       while(!(rUTRSTAT0 & 0x2)) ;
       rUTXH0=(U8)((temp&0x000000ff));
  
reset_en29lv160ab();            //这里一定要复位
    
delay(100);
    
       //擦除块33
       sta=en29lv160ab_sector_erase(0xf0000);
if(sta == 0)
       {
              while(!(rUTRSTAT0 & 0x2)) ;
              rUTXH0=0xaf;             //擦除出错
       }
       else
       {
              for(i=0;i<1024;i++)
              {
                     sta = en29lv160ab_program(0xf0000+(i<<1),buffer[i]);            //写
                     if(sta == 0)           //写出错
                     {
                            while(!(rUTRSTAT0 & 0x2));
                            rUTXH0=0xbf;     
                            break;
                     }
                     delay(200);
              }
             
              if(sta == 1)
              {
                     for(i=0;i<1024;i++)
                     {
                            if(read_en29lv160ab(0xf0000+(i<<1))!=buffer[i])            //读出错
                            {
                                   while(!(rUTRSTAT0 & 0x2)) ;
                                   rUTXH0=0xcf;
                                   sta = 3;
                                   break;
                            }
                     }
                     if(sta !=3)             //全部操作都正确
                     {
                            while(!(rUTRSTAT0 & 0x2)) ;
                            rUTXH0=0x66;     
                     }
              }
       }
       while(!(rUTRSTAT0 & 0x2)) ;
       rUTXH0=0x88;                    //结束
}
 
//简单测试CFI
void test_en29lv160ab_CFI(void)
{
       U16 temp;
      
       *((volatile U16 *)(0x55<<1+flash_base))=0x98;               //CFI命令
       temp = (*(volatile unsigned short *)(flash_base+ (0x10<<1)));
       //while(!(rUTRSTAT0 & 0x2))      ;
       //rUTXH0=(U8)((temp&0xff00)>>8);
       while(!(rUTRSTAT0 & 0x2)) ;
       rUTXH0=(U8)(temp&0x00ff);
      
       temp = (*(volatile unsigned short *)(flash_base+ (0x11<<1)));
       //while(!(rUTRSTAT0 & 0x2))      ;
       //rUTXH0=(U8)((temp&0xff00)>>8);
       while(!(rUTRSTAT0 & 0x2)) ;
       rUTXH0=(U8)(temp&0x00ff);
      
       temp = (*(volatile unsigned short *)(flash_base+ (0x12<<1)));
       //while(!(rUTRSTAT0 & 0x2))      ;
       //rUTXH0=(U8)((temp&0xff00)>>8);
       while(!(rUTRSTAT0 & 0x2)) ;
       rUTXH0=(U8)(temp&0x00ff);
      
       temp = (*(volatile unsigned short *)(flash_base+ (0x13<<1)));
       //while(!(rUTRSTAT0 & 0x2))      ;
       //rUTXH0=(U8)((temp&0xff00)>>8);
       while(!(rUTRSTAT0 & 0x2)) ;
       rUTXH0=(U8)(temp&0x00ff);
      
       temp = (*(volatile unsigned short *)(flash_base+ (0x14<<1)));
       //while(!(rUTRSTAT0 & 0x2))      ;
       //rUTXH0=(U8)((temp&0xff00)>>8);
       while(!(rUTRSTAT0 & 0x2)) ;
       rUTXH0=(U8)(temp&0x00ff);
      
       temp = (*(volatile unsigned short *)(flash_base+ (0x15<<1)));
       //while(!(rUTRSTAT0 & 0x2))      ;
       //rUTXH0=(U8)((temp&0xff00)>>8);
       while(!(rUTRSTAT0 & 0x2)) ;
       rUTXH0=(U8)(temp&0x00ff);
      
       temp = (*(volatile unsigned short *)(flash_base+ (0x16<<1)));
       //while(!(rUTRSTAT0 & 0x2))      ;
       //rUTXH0=(U8)((temp&0xff00)>>8);
       while(!(rUTRSTAT0 & 0x2)) ;
       rUTXH0=(U8)(temp&0x00ff);
      
       temp = (*(volatile unsigned short *)(flash_base+ (0x17<<1)));
       //while(!(rUTRSTAT0 & 0x2))      ;
       //rUTXH0=(U8)((temp&0xff00)>>8);
       while(!(rUTRSTAT0 & 0x2)) ;
       rUTXH0=(U8)(temp&0x00ff);
      
       temp = (*(volatile unsigned short *)(flash_base+ (0x18<<1)));
       //while(!(rUTRSTAT0 & 0x2))      ;
       //rUTXH0=(U8)((temp&0xff00)>>8);
       while(!(rUTRSTAT0 & 0x2)) ;
       rUTXH0=(U8)(temp&0x00ff);
      
       temp = (*(volatile unsigned short *)(flash_base+ (0x19<<1)));
       //while(!(rUTRSTAT0 & 0x2))      ;
       //rUTXH0=(U8)((temp&0xff00)>>8);
       while(!(rUTRSTAT0 & 0x2)) ;
       rUTXH0=(U8)(temp&0x00ff);
      
       temp = (*(volatile unsigned short *)(flash_base+ (0x1a<<1)));
       //while(!(rUTRSTAT0 & 0x2))      ;
       //rUTXH0=(U8)((temp&0xff00)>>8);
       while(!(rUTRSTAT0 & 0x2)) ;
       rUTXH0=(U8)(temp&0x00ff);
}
 
void __irq uartISR(void)
{
       char ch;
       rSUBSRCPND |= 0x1;
       rSRCPND |= 0x1<<28;
       rINTPND |= 0x1<<28;
       ch=rURXH0;
      
       switch(ch)
       {
              case 0x11:                     //get ID
                     cmd = 1;
                     break;
              case 0x66:                    //test CFI
                     cmd = 6;
                     break;
              case 0x77:                    //test norflash
                     cmd = 7;
                     break;
       }
       while(!(rUTRSTAT0 & 0x2)) ;
       rUTXH0=ch;
}
 
void Main(void)
{
       U32 temp;
       int i;
 
//uart0 port
       rGPHCON = 0x00faaa;
rGPHUP  = 0x7ff;
 
//init uart0
rULCON0 = 0x3;
       rUCON0 = 0x5;
rUFCON0 = 0;
       rUMCON0 = 0;
rUBRDIV0 = 26;
       rSRCPND = (0x1<<19)|(0x1<<28);
       rSUBSRCPND = 0x1;
rINTPND = (0x1<<19)|(0x1<<28);
       rINTSUBMSK = ~(0x1);
       rINTMSK = ~((0x1<<19)|(0x1<<28));
       pISR_UART0 = (U32)uartISR;  
 
       cmd = 0;
       while(1)
       {
              switch(cmd)
              {
                     case 1:                   //读ID
                            cmd = 0;
                            temp = get_en29lv160ab_id();
                            while(!(rUTRSTAT0 & 0x2)) ;
                            rUTXH0=(U8)((temp&0xff000000)>>24);
                            while(!(rUTRSTAT0 & 0x2)) ;
                            rUTXH0=(U8)((temp&0x00ff0000)>>16);
                            while(!(rUTRSTAT0 & 0x2)) ;
                            rUTXH0=(U8)((temp&0x0000ff00)>>8);
                            while(!(rUTRSTAT0 & 0x2)) ;
                            rUTXH0=(U8)((temp&0x000000ff));
                            reset_en29lv160ab();
                            break;
 
                     case 0x7:              
                            cmd = 0;
                            test_en29lv160ab();
                            break;
                           
                     case 0x6:
                            cmd = 0;
                            test_en29lv160ab_CFI();
                            reset_en29lv160ab();
                            break;
              }
}
}
 
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/zhaocj/archive/2010/07/04/5712259.aspx
阅读(745) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~