新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > s3c2440對norflash的操作

        s3c2440對norflash的操作

        作者: 時間:2016-11-11 來源:網絡 收藏
        norflash和nandflash是應用不同技術而實現的非易失閃存。它們之間的各自特點在這里就不做介紹了,而只把s3c2440對norflash的操作做一講解。我們用的norflash為EN29LV160AB,其實對各種型號的norflash進行讀寫等操作差別不大。

        本文引用地址:http://www.104case.com/article/201611/316866.htm

        對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;

        }

        }

        }



        關鍵詞: s3c2440norflas

        評論


        技術專區

        關閉
        主站蜘蛛池模板: 乐至县| 舟曲县| 交口县| 余姚市| 盐城市| 长乐市| 宜州市| 芦溪县| 武汉市| 泸定县| 五大连池市| 无极县| 河津市| 乡宁县| 阳高县| 格尔木市| 中西区| 略阳县| 柳江县| 太康县| 赤城县| 沙河市| 都江堰市| 克东县| 双柏县| 江源县| 西贡区| 石城县| 木兰县| 湘潭县| 壶关县| 大悟县| 蒲城县| 曲周县| 万年县| 姚安县| 镇安县| 扎囊县| 蒙城县| 会昌县| 襄樊市|