天天看點

JZ2440:norflash1. 簡介:2. 硬體:3. 程式設計:4. 例子:

采用的器件是:29lv160dbt1-70g

1. 簡介:

norflash 的特點是:

  • NOR Flash 的特點是晶片内執行(XIP ,eXecute In Place),這樣應用程式可以直接在Flash閃存内運作,不必再把代碼讀到系統RAM中。
  • NOR 的傳輸效率很高,在1~4MB的小容量時具有很高的成本效益,但是很低的寫入和擦除速度大大影響到它的性能。
  • 由于擦除NOR器件時是以64~128KB的塊進行的,執行一個寫入/擦除操作的時間為5s
  • NOR Flash 能夠像記憶體一樣讀操作,不能像記憶體寫入和擦除

采用的器件:

  • 大小是:2M Bytes
  • 從 norflash 啟動,norflash 的起始位址是 0
  • 從 nandflash 啟動,norflash 的起始位址是 0x0800 0000

2. 硬體:

給的原理圖還是有點瞎的:

JZ2440:norflash1. 簡介:2. 硬體:3. 程式設計:4. 例子:

這個原理圖上型号沒有跟實際硬體對應上,其中的幾個引腳也是标注“錯誤”,當然作為批量的東西,找一個通用的元件來代替,本質是沒有錯誤的。 闆子上晶片的名字:29lv160dbt1-70g 引腳15:RY/BY# ( Ready/Busy output ) 引腳47:BYTE# ( Selects 8-bit or 16-bit mode )     高電平:16位模式,有效的輸入輸出引腳是 DQ15-DQ0     低電平:8位模式,有效的輸入輸出引腳是 DQ7-DQ0,DQ8-DQ14處于三态,DQ15( the DQ15 pin is used as  an input for the LSB (A-1) address function.  )

從原理圖上看到,引腳47是高電平,這個開發闆采用的是16位模式。 下圖是扇區的位址表:

JZ2440:norflash1. 簡介:2. 硬體:3. 程式設計:4. 例子:
JZ2440:norflash1. 簡介:2. 硬體:3. 程式設計:4. 例子:
JZ2440:norflash1. 簡介:2. 硬體:3. 程式設計:4. 例子:

3. 程式設計:

3.1 讀:系統上電,直接能讀

U16 read_en29lv160ab(U32 addr)
{
       return *((volatile U16 *)(addr));
}
           

3.2 軟體複位:

norflash不僅能硬體複位,也能軟體複位,思路是向任一位址寫入複位指令 0xf0:

void reset_en29lv160ab(void)
{
       *((volatile U16 *)0x0) = 0xf0;
}
           

norflash 的寫和擦除:需要 4 到 6 個周期來完成,每一個周期都要把相應的指令寫入 norflash 中的某一個寄存器:

3.3 寫操作的過程:

  • 第一個周期是把指令 0xaa 寫到位址 0x555 的指令寄存器中
  • 第二個周期是把指令 0x55 寫到位址 0x2aa 的指令寄存器中
  • 第三個周期是把指令 0xa0 寫到位址 0x555 的指令寄存器中
  • 第四個周期是把資料 寫到目的位址中

需要知道的幾點:

  • norflash 接到的是s3c2440 的bank0 上,norflash 的基位址為 0x00000000
  • 之是以把norflash 的位址向左移一位,是因為 s3c2440 的addr1 連結到了 norflash 的 a0 上

check_toggle函數的作用是:用于判斷這次操作是否正确

    它的原理是連續兩次讀取資料總線上的資料,判斷資料總線上的第6位數值(DQ6)是否翻轉,如果沒有翻轉則正确,否則還要判斷第5位(DQ5),以确定是否是因為逾時而引起的翻轉。

#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();
}

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;         //正确
}
           

3.4 擦除操作的過程:

寫操作隻能使“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();
}
           

3.5 讀取晶片的 ID:

  • 第一個周期是把指令 0xaa 寫到位址 0x555 的指令寄存器中
  • 第二個周期是把指令 0x55 寫到位址 0x2aa 的指令寄存器中
  • 第三個周期是把指令 0x90 寫到位址 0x555 的指令寄存器中
  • 第四個周期是讀位址 0x100 中的内容 得到廠商 ID(0x1c)
  • 第四個周期是讀位址 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;
}
           

4. 例子:

下面的程式實作了對一塊區域進行擦除,寫入,并讀出的操作,判斷寫入的資料是否與讀出的資料相同: CFI:是一個記錄晶片資訊的接口,可以通過特定的指令來通路這些資料

……   ……
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;
              }
}
}
           

部落格參考: s3c2440對norflash的操作

繼續閱讀