使用了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 char status=0; SPI_WRITE_ENABLE(); //设置状态器WEL位为1,在进行写状态寄存器之前必须写使能 status=SPI_READ_STATUS(); NSSMD0=0; SPI0DAT=FLASH_WRITE_STATUS; while(!SPIF); SPIF=0; SPI0DAT=status&MASK_CLEAR_BPX; while(!SPIF); SPIF=0; NSSMD0=1;//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 //擦除一个扇区 extern unsigned char xdata buff[256];//缓冲区全局变量,可以保存一个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=0; SPI0DAT=FLASH_READ_ID; while(!SPIF); SPIF=0; SPI0DAT=0; //dummy write to output serial clock while(!SPIF); //wait for value to be read SPIF=0; sendChar(SPI0DAT); SPI0DAT=0; while(!SPIF); SPIF=0; sendChar(SPI0DAT); SPI0DAT=0; while(!SPIF); SPIF=0; sendChar(SPI0DAT); NSSMD0=1; } void SPI_WRITE_ENABLE() { NSSMD0=0; SPI0DAT=FLASH_WRITE_ENABLE; while(!SPIF); SPIF=0; NSSMD0=1; } void SPI_WRITE_DISABLE() { NSSMD0=0; SPI0DAT=FLASH_WRITE_DISABLE; while(!SPIF); SPIF=0; NSSMD0=1; } unsigned char SPI_READ_STATUS() { NSSMD0=0; SPI0DAT=FLASH_READ_STATUS;//可以将类似的这种形式做成一个宏定义 while(!SPIF); SPIF=0; SPI0DAT=0; while(!SPIF); SPIF=0; NSSMD0=1; 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 char status=0; SPI_WRITE_ENABLE(); //设置状态器WEL位为1,在进行写状态寄存器之前必须写使能 status=SPI_READ_STATUS(); NSSMD0=0; SPI0DAT=FLASH_WRITE_STATUS; while(!SPIF); SPIF=0; SPI0DAT=status&MASK_CLEAR_BPX; while(!SPIF); SPIF=0; NSSMD0=1;//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 char status=0; SPI_WRITE_ENABLE(); NSSMD0=0; SPI0DAT=FLASH_SECTOR_ERASE; while(!SPIF); SPIF=0; //any address in the sector,but i choose the first address of sector SPI0DAT=0x00; //high address while(!SPIF); SPIF=0; SPI0DAT=sectors<<4; //middle address while(!SPIF); SPIF=0; SPI0DAT=0x00; //low address while(!SPIF); SPIF=0; NSSMD0=1; 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 char xdata buff[256]; //读一页256 bytes void SPI_READ_Page(unsigned char sectors,unsigned char pages) { unsigned int i=0; NSSMD0=0; SPI0DAT=FLASH_READ_DATA; //command while(!SPIF); SPIF=0; SPI0DAT=0x00; //read address while(!SPIF); SPIF=0; SPI0DAT=(sectors<<4) + pages; while(!SPIF); SPIF=0; SPI0DAT=0x00; while(!SPIF); SPIF=0; for(i=0;i<256;i++) //read a page 256 bytes { //实测每页的数据只有32byte,所以一次连续写32byte SPI0DAT=0; //read datas out while(!SPIF); SPIF=0; buff[i]=SPI0DAT; } NSSMD0=1; } //写一页256 bytes void SPI_WRITE_Page(unsigned char *str,unsigned char sectors,unsigned char pages) { unsigned int i=0; unsigned char status=0; SPI_WRITE_ENABLE(); //在改变数据之前都要进行写使能操作 NSSMD0=0; SPI0DAT=FLASH_WRITE_DATA; //write command while(!SPIF); SPIF=0; SPI0DAT=0x00; //write address while(!SPIF); //最高地址默认为0x00,所以不用传他的参数 SPIF=0; SPI0DAT=(sectors<<4) + pages; while(!SPIF); SPIF=0; SPI0DAT=0x00; while(!SPIF); SPIF=0; for(i=0;i<256;i++) //write a page 256 bytes { SPI0DAT=str[i]; //write data in while(!SPIF); SPIF=0; } NSSMD0=1; do //query until WIP convert from 1 to 0 when write cycle is finished { status=SPI_READ_STATUS(); status=status&0x01; }while(status); }