使用了MX25L512的SPI介面的Flash

電路連線圖:

總的大小512kb,即64kB,sector的大小為256 Bytes,block的大小為4k Bytes

除錯時出現的問題:

1、Flash只能讀資料,不能寫資料

根源在於Flash的軟體防寫沒有去掉,這樣,寫、擦除,甚至寫狀態暫存器都不能執行。

1)Hardware Protection

Hardware Protection Mode(HPM):by using WP# going low to protect the BP0-BP1 bits and SRWD bit from data change

因為WP#是高電平,所以沒有硬體保護,再來看軟體保護。

2)Software Protection

Software Protection Mode(SPM):by using BP0-BP1 bits to set the part of flash protected from data change

通過下面幾幅圖可知,在WP#高電平情況下write status register可以改變SRWD、BP0、BP1的值為0,從而去掉軟體防寫。

3)程式碼實現去除保護

//在所有需要修改的操作之前必須去除軟體保護BP0,BP1
//FLASH的狀態暫存器
//bit7        bit6    bit5    bit4    bit3    bit2    bit1            bit0
//SRWD        0         0          0        BP1        BP0        WEL                WIP
// 1=status write disable                            1=write enable    1=write in process
#define MASK_CLEAR_BPX 0x73    //掩碼,設定使能寫狀態暫存器,清除BP1、BP0
void SPI_WRITE_STATUS()     //主要是清除保護,可以寫資料
{
    unsigned ;
    SPI_WRITE_ENABLE();    //設定狀態器WEL位為1,在進行寫狀態暫存器之前必須寫使能
    status=SPI_READ_STATUS();

    NSSMD0=;
    SPI0DAT=FLASH_WRITE_STATUS;
    while(!SPIF);
    SPIF=;

    SPI0DAT=status&MASK_CLEAR_BPX;
    while(!SPIF);
    SPIF=;

    NSSMD0=;//cs must go high at the byte boundary,otherwise instruction will be reject and not executed

    do        //query until WIP convert from 1 to 0 when write status register cycle is finished
    {
        status=SPI_READ_STATUS();
        status=status&0x01;
    }while(status);
}

2、sector大小為256 Bytes,但是連續寫256 Bytes,只有最後的32 Bytes寫進去了

  在測試MX25L512的扇區的時候,老是遇到一個問題,寫入256個位元組,但是讀出的是最後32個Bytes,前面的總是0xFF,於是就懷疑是不是扇區的大小沒有datasheet所說的那麼大呢?最後測試發現扇區的大小隻有32Bytes,如果連續寫入的位元組數大於32 Bytes,就會把除最後32 Bytes之外的資料丟棄,只寫最後的32 Bytes。仔細翻看datasheet,發現MX25L512MC-12G的扇區為256 Bytes,而MX25L512IE的扇區只有32Bytes原來具體晶片規格和元件字尾名也有關係。

說明:sector是讀寫資料的最小單元,block是擦除的最小單元。(有時候sector概念類似於page,要看具體的晶片)扇區被擦除後內部的資料變為0xFF。

3、SPI介面波形不對

雖然C8051F320已經交叉配置為串列埠和SPI介面,但是實際情況是還是要在輸出的管腳PushPull,

P0MDOUT=0x1D;//0001 1101

否則可能SPI口的OUT輸出驅動不了,導致波形都沒有。

貼上Flash操作的程式碼:

#ifndef _SPI_CMD_H_
#define _SPI_CMD_H_

#include"misc.h"
////////////////////////////////////////////////
//////////////////MX25L512的flash說明///////////////////
//page:256byte
//sector:4kbyte
//注意MX25L512MC-12G page為256 bytes
//MX25L512IE.. page為32 bytes
///////////////////////////////////////////////

#define FLASH_READ_ID          0x9F    //讀裝置ID
#define FLASH_WRITE_ENABLE     0x06    //寫使能
#define FLASH_WRITE_DISABLE    0x04    //寫禁止
#define FLASH_READ_STATUS      0x05    //讀狀態暫存器
#define FLASH_WRITE_STATUS     0x01    //寫狀態暫存器
#define FLASH_READ_DATA        0x03    //讀資料
#define FLASH_WRITE_DATA       0x02    //寫資料
#define FLASH_SECTOR_ERASE     0x20    //擦除一個扇區

];//緩衝區全域性變數,可以儲存一個page的256位元組
//在標頭檔案中只是申明一下,不能定義,定義變數要在相應的C檔案中定義
//以上不然會報錯:multiple public definitions

void SPI_READ_ID();
void SPI_WRITE_ENABLE();
void SPI_WRITE_DISABLE();
unsigned char SPI_READ_STATUS();
void SPI_WRITE_STATUS();
void SPI_SECTOR_ERASE(unsigned char sectors);
void SPI_READ_Page(unsigned char sectors,unsigned char pages);
void SPI_WRITE_Page(unsigned char *str,unsigned char sectors,unsigned char pages);
void FillDBR();

#endif
#include"SPI_CMD.h"

//#define SPI0INT(x) {SPI0DAT=x;while(!SPIF);SPIF=0;}
//可以用這個定義來取代以下一大段的程式碼
//SPI0DAT=FLASH_READ_ID;
//while(!SPIF);               ----->SPI0DAT=FLASH_READ_ID;
//SPIF=0;
void SPI_READ_ID()
{
    NSSMD0=;

    SPI0DAT=FLASH_READ_ID;
    while(!SPIF);
    SPIF=;

    SPI0DAT=;    //dummy write to output serial clock
    while(!SPIF); //wait for value to be read
    SPIF=;
    sendChar(SPI0DAT);

    SPI0DAT=;
    while(!SPIF);
    SPIF=;
    sendChar(SPI0DAT);    

    SPI0DAT=;
    while(!SPIF);
    SPIF=;
    sendChar(SPI0DAT);

    NSSMD0=;
}

void SPI_WRITE_ENABLE()
{
    NSSMD0=;
    SPI0DAT=FLASH_WRITE_ENABLE;
    while(!SPIF);
    SPIF=;
    NSSMD0=;
}

void SPI_WRITE_DISABLE()
{
    NSSMD0=;
    SPI0DAT=FLASH_WRITE_DISABLE;
    while(!SPIF);
    SPIF=;
    NSSMD0=;
}

unsigned char SPI_READ_STATUS()
{
    NSSMD0=;
    SPI0DAT=FLASH_READ_STATUS;//可以將類似的這種形式做成一個巨集定義
    while(!SPIF);
    SPIF=;

    SPI0DAT=;
    while(!SPIF);
    SPIF=;
    NSSMD0=;

    return SPI0DAT;
}

//在所有需要修改的操作之前必須去除軟體保護BP0,BP1
//FLASH的狀態暫存器
//bit7        bit6    bit5    bit4    bit3    bit2    bit1            bit0
//SRWD        0         0          0        BP1        BP0        WEL                WIP
// 1=status write disable                            1=write enable    1=write in process
#define MASK_CLEAR_BPX 0x73    //掩碼,設定使能寫狀態暫存器,清除BP1、BP0
void SPI_WRITE_STATUS()     //主要是清除保護,可以寫資料
{
    unsigned ;
    SPI_WRITE_ENABLE();    //設定狀態器WEL位為1,在進行寫狀態暫存器之前必須寫使能
    status=SPI_READ_STATUS();

    NSSMD0=;
    SPI0DAT=FLASH_WRITE_STATUS;
    while(!SPIF);
    SPIF=;

    SPI0DAT=status&MASK_CLEAR_BPX;
    while(!SPIF);
    SPIF=;

    NSSMD0=;//cs must go high at the byte boundary,otherwise instruction will be reject and not executed

    do        //query until WIP convert from 1 to 0 when write status register cycle is finished
    {
        status=SPI_READ_STATUS();
        status=status&0x01;
    }while(status);
}

void SPI_SECTOR_ERASE(unsigned char sectors)
{
    unsigned ;
    SPI_WRITE_ENABLE();

    NSSMD0=;

    SPI0DAT=FLASH_SECTOR_ERASE;
    while(!SPIF);
    SPIF=;

    //any address in the sector,but i choose the first address of sector
    SPI0DAT=0x00;            //high address
    while(!SPIF);
    SPIF=;
    SPI0DAT=sectors<<;        //middle address
    while(!SPIF);
    SPIF=;
    SPI0DAT=0x00;            //low address
    while(!SPIF);
    SPIF=;

    NSSMD0=;

    do        //query until WIP convert from 1 to 0 when write status register cycle is finished
    {
        status=SPI_READ_STATUS();
        status=status&0x01;
    }while(status);
}

unsigned ];
//讀一頁256 bytes
void SPI_READ_Page(unsigned char sectors,unsigned char pages)
{
    unsigned ;
    NSSMD0=;
    SPI0DAT=FLASH_READ_DATA; //command
    while(!SPIF);
    SPIF=;

    SPI0DAT=0x00;             //read address
    while(!SPIF);
    SPIF=;
    SPI0DAT=(sectors<<) + pages;
    while(!SPIF);
    SPIF=;
    SPI0DAT=0x00;
    while(!SPIF);
    SPIF=;

    ;i<;i++)            //read a page 256 bytes
    {                        //實測每頁的資料只有32byte,所以一次連續寫32byte
        SPI0DAT=;                 //read datas out
        while(!SPIF);
        SPIF=;
        buff[i]=SPI0DAT;
    }
    NSSMD0=;
}

//寫一頁256 bytes
void SPI_WRITE_Page(unsigned char *str,unsigned char sectors,unsigned char pages)
{
    unsigned ;
    unsigned ;
    SPI_WRITE_ENABLE();      //在改變資料之前都要進行寫使能操作
    NSSMD0=;
    SPI0DAT=FLASH_WRITE_DATA; //write command
    while(!SPIF);
    SPIF=;

    SPI0DAT=0x00;             //write address
    while(!SPIF);                //最高地址預設為0x00,所以不用傳他的引數
    SPIF=;
    SPI0DAT=(sectors<<) + pages;
    while(!SPIF);
    SPIF=;
    SPI0DAT=0x00;
    while(!SPIF);
    SPIF=;

    ;i<;i++)        //write a page 256 bytes
    {
        SPI0DAT=str[i];    //write data in
        while(!SPIF);
        SPIF=;
    }

    NSSMD0=;

    do        //query until WIP convert from 1 to 0 when write cycle is finished
    {
        status=SPI_READ_STATUS();
        status=status&0x01;
    }while(status);
}