嵌入式入門7(Nor Flash)

1、Nor Flash原理及硬件操做

Nor Flash的接口屬於內存類接口,意味着它能夠像內存同樣讀,可是,它不能像內存同樣寫。由於Nor Flash通常保存着重要的數據,不能輕易被修改。要想寫Nor Flash,只能經過發送命令完成。編程

Nor Flash原理圖如圖:數組

image.png

Nor Flash與Nand Flash有什麼不一樣?markdown

Nor Flash有21條數據線,有地址線,有片選信號和讀寫信號。
Nand Flash只有8條數據線,既傳輸命令,又傳輸地址,還傳輸數據。函數

- Nor NAND
接口 與RAM接口相同 I/O接口
容量
簡單 複雜
發出特定命令 複雜
XIP(代碼能夠直接運行) Yes no
性能(擦除) 很是慢(5s) 快(3ms)
性能 寫慢、讀快 寫快、讀快
可靠性 較高,位反轉的比例小於NAND Flash的10% 容易發生位反轉,須要有校驗措施(如TNR)和壞塊管理措施
可擦除次數 10000 ~ 100000 100000 ~ 1000000
生命週期 低於NAND Flash的10% 是Nor Flash的10倍以上
易用性 容易 複雜
主要用途 經常使用於保存關鍵代碼和數據 用於保存數據
價格

Nor Flash支持XIP(Execute in place),即代碼能夠直接在Nor Flash上執行,無需複製到內存中。這是因爲NorF lash的接口與RAM徹底相同,能夠隨機訪問任意地址的數據。Nor Flash進行讀操做的效率很是高,可是擦除和寫操做的效率很低,另外,Nor Flash的容量通常比較小。NAND Flash進行擦除和寫操做的效率更高,而且容量更大。通常而言,Nor Flash用於存儲程序,NAND Flash用於存儲數據。基於NAND Flash的設備一般也要搭配Nor Flash以存儲程字。oop

Flash存儲器件由擦除單元(也稱爲塊)組成,當要寫某個塊時,須要確保這個塊己經被擦除。Nor Flash的塊大小範圍爲64kB、128kB:NAND Flash的塊大小範圍爲8kB,64kB,擦/寫一個Nor Flash塊需4s,而擦/寫一個NAND Flash塊僅需2ms。Nor Flash的塊太大,不只增長了擦寫時間,對於給定的寫操做,Nor Flash也須要更多的擦除操做——特別是小文件,好比一個文件只有IkB,可是爲了保存它卻須要擦除大小爲64kB—128kB的Nor Flash塊。性能

Nor Flash的接口與RAM徹底相同,能夠隨意訪問任意地址的數據。而NAND Flash的 接口僅僅包含幾個I/O引腳,須要串行地訪問。NAND Flash通常以512字節爲單位進行讀寫。這使得Nor Flash適合於運行程序,而NAND Flash更適合於存儲數據。測試

容量相同的狀況下,NAND Flash的體積更小,對於空間有嚴格要求的系統,NAND Flash能夠節省更多空間。市場上Nor Flash的容量一般爲1MB~4MB(也有32MB的Nor Flash),NAND Flash的容量爲8MB~512MB。容量的差異也使得Nor Flash多用於存儲程序,NAND Flash多用於存儲數據。ui

對於Flash存儲器件的可靠性須要考慮3點:位反轉、壞塊和可擦除次數。全部Flash器件都遭遇位反轉的問題:因爲Flash固有的電器特性,在讀寫數據過程當中,偶然會產生一位或幾位數據錯誤(這種機率很低),而NAND Flash出現的機率遠大於Nor Flash,當位反轉發生在關鍵的代碼、數據上時,有可能致使系統崩潰。當僅僅是報告位反轉,從新讀取便可;若是確實發生了位反轉,則必須有相應的錯誤檢測/恢復措施。在NAND Flash上發生位反轉的機率高,推薦使用EDC/ECC進行錯誤檢測和恢復。NAND Flash上面會有壞塊隨機分佈在使用前須要將壞塊掃描出來,確保再也不使用它們,不然會使產品含有嚴重的故障。NAND Flash每塊的可擦除次數一般在100000次左右,是Nor Flash的10倍。另外,由於NAND Flash的塊大小一般是NorF lash的1/8,因此NAND Flash的壽命遠遠超過Nor Flash。spa

嵌入式Linux對Nor、NAND Flash的軟件支持都很成熟。在Nor Flash上經常使用jffs2文件系統,而在NAND Flash經常使用yaffs文件系統。在更底層,有MTD驅動程序實現對它們的讀、寫、擦除操仵,它也實現了EDC/ECC校驗。命令行

2、u-boot操做Nor Flash

將u-boot燒寫到Nor Flash,而後設置爲Nor啓動,進入u-boot命令行界面。

2.一、讀數據

在u-boot上執行:

md.b 0
複製代碼

返回的結果和咱們燒入的bin文件徹底同樣:

OpenJTAG> md.b 0
00000000: 17 00 00 ea 14 f0 9f e5 14 f0 9f e5 14 f0 9f e5    ................
00000010: 14 f0 9f e5 14 f0 9f e5 14 f0 9f e5 14 f0 9f e5    ................
00000020: 60 01 f8 33 c0 01 f8 33 20 02 f8 33 80 02 f8 33    `..3...3 ..3...3
00000030: e0 02 f8 33 00 04 f8 33 20 04 f8 33 ef be ad de    ...3...3 ..3....
複製代碼

因而可知,u-boot能夠像讀內存同樣來讀。

2.二、讀Nor Flash的ID

jz2440採用MX29LV160DBTI-70G的Nor Flash,其芯片手冊包含有一個命令表格,如圖:

image.png

這個命令表格清晰的介紹了任何操做Nor Flash:

  • 讀取芯片vendor id和device id
  • 讀內存(直接讀取)和寫內存
  • 擦除芯片和扇區
  • 進入CFI模式
  • 退出命令模式(往任意地址寫入0xf0)

如今咱們經過讀id操做來說解如何經過u-boot操做Nor Flash。

這裏須要注意的是:因爲JZ2440使用的是16位Nor Flash芯片,因此JZ2440的A1接在Nor Flash的A0上。也就意味着:JZ2440發出的地址右移一位纔是Nor Flash收到的地址。好比:2440發出(555H<<1)地址,Nor Flash才能收到555H這個地址。

因此,整個讀id操做以下:

操做 Nor Flash的操做 2440的操做 u-boot發出的命令 結果
解鎖 往地址555H寫入AAH 往地址AAAH寫入AAH mw.w aaa aa
解鎖 往地址2AAH寫入55H 往地址554H寫入55H mw.w 554 55
命令 往地址555H寫入90H 往地址AAAH寫入90H mw.w aaa 90
讀廠家id 讀0地址獲得廠家ID 讀0地址獲得廠家ID md.w 0 1 00000000: 00c2 .
讀設備id 讀1地址獲得設備ID 讀2地址獲得設備ID md.w 2 1 00000002: 2249 I"
退出讀id狀態 任意地址寫F0H 任意地址寫F0H mw.w 0 f0

2.三、寫Nor Flash

一、測試將數據直接寫入nor flash

打印當前100000地址上的數據:

OpenJTAG> md.w  100000  1
00100000:ffff..
複製代碼

在100000地址上直接寫入1234:

OpenJTAG> mw.w 100000   1234
複製代碼

再從新打印:

OpenJTAG> md.w  100000  1
00100000:ffff
複製代碼

能夠發現,u-boot直接寫入數據到nor flash是無效的,若是將地址改成0x30000000,即SDRAM中,發現寫入數據是有效的。

二、經過命令將數據寫入到nor flash

寫數據以前必須保證,要寫的地址是擦除的。

Nor Flash上操做寫操做 2440上操做寫操做 U-BOOT上操做寫操做
往地址555H寫AAH(解鎖) 往地址AAAH寫AAH(解鎖) mw.w aaa aa
往地址2AAH寫55H(解鎖) 往地址554H寫55H(解鎖) mw.w 554 55
往地址555H寫A0H 往地址AAAH寫A0H mw.w aaa a0
往地址PA寫PD 往地址0x100000寫1234h mw.w 100000 1234

若是地址上的數據不是全0xffff,寫操做會失敗,此時須要先擦除扇區。

Nor Flash操做 u-boot操做
555H AAH mw.w aaa aa
2AAH 55H mw.w 554 55
555H 80H mw.w aaa 80
555H AAH mw.w aaa aa
2AAH 55H mw.w 554 55
SA 30H //往扇區地址寫入30 mw.w 100000 30

3、編程操做Nor Flash

3.一、Nor Flash的兩種規範

一般內核裏面要識別一個 Nor Flash 有兩種方法:

一種是 jedec 探測,就是在內核裏面事先定義一個數組,該數組裏面放有不一樣廠家各個芯片的一些參數,探測的時候將 flash 的 ID 和數組裏面的 ID 一一比較,若是發現相同的,就使用該數組的參數。 jedec 探測的優勢就是簡單,缺點是若是內核要支持的 flash 種類不少,這個數組就會很龐大。內核裏面用 jedec 探測一個芯片時,是先經過發命令來獲取 flash 的 ID,而後和數組比較,可是 flash.c 中連 ID 都是本身經過宏配置的。 Jedec_probe.c中,jedec_table

一種是 CFI(common flash interface)探測,就是直接發各類命令來讀取芯片的信息,好比 ID、容量等,芯片自己就包含了電壓有多大,容量有有多少等信息。

image.png

3.二、編程操做Nor Flash

一、編寫操做菜單

nor_flash.c

#include "my_printf.h"
#include "string_utils.h"

void nor_flash_test() {
    while(1)
    {
        // 打印菜單,供咱們選擇測試內容
        printf("[s] Scan nor flash\n\r");
        printf("[e] Erase nor flash\n\r");
        printf("[w] Write nor flash\n\r");
        printf("[r] Read nor flash\n\r");
        printf("[q] quit\n\r");
        printf("Enter selection:");
        
        char c = getchar();
        printf("%c\n\r\n\r",c);
        
        switch(c)
        {
             case 'q':
             case 'Q':
                return;
                break;

             case 's':
             case 'S':
                do_scan_nor_flash();
                break;

             case 'e':
             case 'E':
                do_erase_nor_flash();
                break;

             case 'w':
             case 'W':
                do_write_nor_flash();
                break;

             case 'r':
             case 'R':
                do_read_nor_flash();
                break;
                
             default:
                break;
        }
    }
}
複製代碼

二、編寫讀寫函數

#define NOR_FLASH_BASE 0

void nor_write_word(unsigned int base, unsigned int offset, unsigned int val)
{
    volatile unsigned short* p = (volatile unsigned short*)(base + (offset << 1));
    *p = val;
}

void nor_cmd(unsigned int offset, unsigned int cmd)
{
    nor_write_word(NOR_FLASH_BASE, offset, cmd);
}

unsigned int nor_read_word(unsigned int base, unsigned int offset)
{
    volatile unsigned short* p = (volatile unsigned short*)(base + (offset << 1));
    return *p;
}

unsigned int nor_data(unsigned int offset)
{
    return nor_read_word(NOR_FLASH_BASE, offset);
}
複製代碼

三、Nor Flash查詢

image.png

[2C]表示有多少個region。

[2E, 2D]表示erase region 1有多少個sectors(扇區),[30, 2F]表示erase region 1每一個扇區的大小。

參考CFI publication 100規範

image.png

/* 進入Nor Flash的CFI模式 * 讀取各種信息 */
void do_scan_nor_flash() {
    char str[4];
    //打印廠家ID、設備ID
    nor_cmd(0x555, 0xaa);   //解鎖
    nor_cmd(0x2aa, 0x55);
    nor_cmd(0x555, 0x90);   //read id
    int vendor = nor_data(0);
    int device = nor_data(1);
    nor_cmd(0, 0xf0);       //reset

    nor_cmd(0x55, 0x98); //進入CFI模式
    str[0] = nor_data(0x10);
    str[1] = nor_data(0x11);
    str[2] = nor_data(0x12);
    str[3] = '\0';
    printf("str = %s\n\r", str);

    //打印容量
    unsigned int size = 1 << (nor_data(0x27));
    printf("vendor=0x%x, device=0x%x, nor size = 0x%x, %dM\n\r", vendor, device, size, size / (1024 * 1024));

    //打印各扇區起始位置
    /** * 名詞解釋: * erase block region:裏面含義1個或多個block,它們的大小同樣 * 一個nor flash含義1個或多個region * 一個region含有1個或多個block(扇區) * * Erase block region information: * 前2字節+1 表示該region有多少個block * 後2字節*256 表示block的大小 */

    unsigned int regions = nor_data(0x2c);
    int i, j;
    int region_info_base = 0x2d;
    int block_addr = 0;
    int count = 0;
    printf("Block/Sector start address:\n\r");
    for (i = 0;i < regions;i++) {
        int blocks = (nor_data(region_info_base) + (nor_data(region_info_base + 1) << 8)) + 1;
        int block_size = (nor_data(region_info_base + 2) + (nor_data(region_info_base + 3) << 8)) * 256;
        region_info_base += 4;

        for (j = 0;j < blocks; j++) {
            //打印每一個block的起始地址
            printHex(block_addr);
            printf(" ");
            block_addr += block_size;
            count++;
            if (count % 5 == 0) {
                printf("\n\r");
            }
        }
    }
    //退出CFI模式
    nor_cmd(0x0, 0xf0);
}
複製代碼

解決ID不對的問題:

image.png

查看反彙編文件,發現它將一次性寫入2個字節,變成每次只寫一個字節。 解決方案,編譯時,增長-march=armv4,表示使用armv4指令集。

image.png

str = QRY
vendor=0xc2, device=0x2249, nor size = 0x200000, 2M
Block/Sector start address:
0x00000000    0x00004000    0x00006000    0x00008000    0x00010000    
0x00020000    0x00030000    0x00040000    0x00050000    0x00060000    
0x00070000    0x00080000    0x00090000    0x000A0000    0x000B0000    
0x000C0000    0x000D0000    0x000E0000    0x000F0000    0x00100000    
0x00110000    0x00120000    0x00130000    0x00140000    0x00150000    
0x00160000    0x00170000    0x00180000    0x00190000    0x001A0000    
0x001B0000    0x001C0000    0x001D0000    0x001E0000    0x001F0000
複製代碼

main函數代碼以下所示。把timer中斷去掉,不然: 測試NOR Flash時進入CFI等模式時, 若是發生了中斷,cpu一定讀NOR Flash,那麼讀不到正確的指令,致使程序崩潰。

四、Nor Flash讀取

void do_read_nor_flash() {
    int i, j;
    unsigned char c;
    unsigned char str[16];

    printf("Enter the address to read: ");
    unsigned char* addr = (unsigned char*)get_uint();

    printf("Data: \n\r");
    for (i = 0; i < 4; i++)
    {
        //打印數值
        for (j = 0; j < 16; j++)
        {
            c = *addr++;
            printf("%02x ", c);
            str[j] = c;
        }

        printf(" ;");
        //打印字符
        for (j = 0; j < 16; j++)
        {
            if (str[j] < 0x20 || str[j]>0x7e) //不可見字符
                putchar('.');
            else
                putchar(str[j]);
        }
        printf("\n\r");
    }
}
複製代碼

運行,輸入地址0

Data: 
27  0  0 ea 14 f0 9f e5 14 f0 9f e5 4d  0  0 ea    ;'...........M...
4c  0  0 ea 4b  0  0 ea  8 f0 9f e5 49  0  0 ea    ;L...K.......I...
2c  0  0 30 70  0  0 30 90  0  0 30  d d3 a0 e3    ;,..0p..0...0....
ff 5f 2d e9  0  0  f e1  c 11 9f e5 aa  1  0 eb    ;._-.............
複製代碼

與bin文件中內容一致。

image.png

五、Nor Flash擦除

void wait_ready(unsigned int addr) {
    unsigned int val;
    unsigned int pre;

    pre = nor_dat(addr >> 1);
    val = nor_dat(addr >> 1);
    while ((val & (1 << 6)) != (pre & (1 << 6)))
    {
        pre = val;
        val = nor_dat(addr >> 1);
    }
}

void do_erase_nor_flash() {
    unsigned int addr;

    printf("Enter the address of sector to erase: ");
    addr = get_uint();

    printf("erasing ...\n\r");

    nor_cmd(0x555, 0xaa);    /* 解鎖 */
    nor_cmd(0x2aa, 0x55);
    nor_cmd(0x555, 0x80);    /* erase sector */

    nor_cmd(0x555, 0xaa);    /* 解鎖 */
    nor_cmd(0x2aa, 0x55);
    nor_cmd(addr >> 1, 0x30);    /* 發出扇區地址 */

    //等待擦除完成
    wait_ready(addr);

    printf("erase completed\n\r");
}
複製代碼

六、Nor Flash寫入

void do_write_nor_flash() {
    printf("Enter the address of sector to write: ");
    unsigned int addr = get_uint();

    unsigned char str[100];
    printf("Enter the string to write: ");
    gets(str);

    printf("writing ...\n\r");
    int i = 0, j = 1;
    unsigned int  val;
    while (str[i] && str[j])
    {
        val = str[i] + (str[j] << 8);
        //燒寫
        nor_cmd(0x555, 0xaa);      /* 解鎖 */
        nor_cmd(0x2aa, 0x55);
        nor_cmd(0x555, 0xa0);	   /* program */
        nor_cmd(addr >> 1, val);   /* 燒寫 */

        //等待燒寫完成:讀數據Q6無變化時表示結束
        wait_ready(addr);

        i += 2;
        j += 2;
        addr += 2;
    }
    val = str[i];
    //燒寫
    nor_cmd(0x555, 0xaa);    /* 解鎖 */
    nor_cmd(0x2aa, 0x55);
    nor_cmd(0x555, 0xa0);	 /* program */
    nor_cmd(addr >> 1, val);   /* 燒寫 */
    //等待燒寫完成:讀數據Q6無變化時表示結束
    wait_ready(addr);

    printf("write completed\n\r");
}
複製代碼
相關文章
相關標籤/搜索