使用了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); }