赞
踩

固定的厂商编号(M7-M0)和不同类型 FLASH 芯片独有的编号(ID15-ID0) 手册中提供的值如下:

0x9F紧跟指令编码的三个字节“(M7-M0)”、“(ID15-ID8)”及“(ID7-ID0)” 时序图如下:

主机首先通过 MOSI 线向 FLASH 芯片发送第一个字节数据为“9F h” ,当 FLASH 芯片收到该数据后,它会解读成主机向它发送了“JEDEC 指令”,然后它就作出该命令的响应: 通过 MISO 线把它的厂商 ID(M7-M0)及芯片类型(ID15-0)发送给主机,主机接收到指令响应后可进行校验。 常见的作用是主机端通过读取设备 ID 来测试硬件是否连接正常,或用于识别设备
SPI使用的主要读写函数,发送一个字节的数据
u8 SPI_FLASH_SendByte(u8 byte) { SPITimeout = SPIT_FLAG_TIMEOUT; //等待发送缓冲区为空,TXE while (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_TXE) == RESET) { if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(0); } //把要写入的数据写到发送缓冲区 SPI_I2S_SendData(FLASH_SPIx , byte); //设置超时时间 SPITimeout = SPIT_FLAG_TIMEOUT; //等待接收缓冲区非空 RXNE while (SPI_I2S_GetFlagStatus(FLASH_SPIx , SPI_I2S_FLAG_RXNE) == RESET) { if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(1); } //从缓冲区中读取数据 return SPI_I2S_ReceiveData(FLASH_SPIx ); }
发送命令码后需要读三个字节

函数实现如下:用于验证读出的是否是0XEF4017(手册中)
u32 SPI_FLASH_ReadID(void) { u32 Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0; //CS拉低,产生起始信号 SPI_FLASH_CS_LOW(); //发送0x9F SPI_FLASH_SendByte(0X9F); //读取一个字节 Temp0 = SPI_FLASH_SendByte(Dummy_Byte); //读取一个字节 Temp1 = SPI_FLASH_SendByte(Dummy_Byte); //读取一个字节 Temp2 = SPI_FLASH_SendByte(Dummy_Byte); //CS拉高,停止通讯 SPI_FLASH_CS_HIGH(); //计算出返回值 Temp = (Temp0 << 16) | (Temp1 << 8) | Temp2; return Temp; }
中间有三个dumpy 在变成的时候也需要空出来

函数实现如下
u32 SPI_FLASH_ReadDeviceID(void) { u32 Temp = 0; SPI_FLASH_CS_LOW(); /* Send "RDID " instruction */ SPI_FLASH_SendByte(0xAB); //根据手册命令需要Dummy_Byte三次 SPI_FLASH_SendByte(Dummy_Byte); SPI_FLASH_SendByte(Dummy_Byte); SPI_FLASH_SendByte(Dummy_Byte); //在第四次时读取ID Temp = SPI_FLASH_SendByte(Dummy_Byte); /* Deselect the FLASH: Chip Select high */ SPI_FLASH_CS_HIGH(); return Temp; }
只要向 FLASH 芯片发送了读状态寄存器的指令, FLASH 芯片就会持续向主机返回最新的状态寄存器内容,直到收到 SPI 通讯的停止信号。
//写使能命令 void SPI_FLASH_WriteEnable(void) { SPI_FLASH_CS_LOW(); SPI_FLASH_SendByte(0x06); SPI_FLASH_CS_HIGH(); } #define WIP_Flag 0x01 //写等待停止信号 void SPI_FLASH_WaitForWriteEnd(void) { u8 FLASH_Status = 0; SPI_FLASH_CS_LOW(); SPI_FLASH_SendByte(0x05);//ReadStatusReg do { FLASH_Status = SPI_FLASH_SendByte(Dummy_Byte); } while ((FLASH_Status & WIP_Flag) == SET); SPI_FLASH_CS_HIGH(); }
FLASH 存储器的特性决定了它只能把原来为“ 1”的数据位改写成“0”,而原来为“0”的数据位不能直接改写为“1”。所以这里涉及到数据“擦除”的概念,在写入前,必须要对目标存储矩阵进行擦除操作,把矩阵中的数据位擦除为“1”,在数据写入的
时候,如果要存储数据“1”,那就不修改存储矩阵 ,在要存储数据“0”时,才更改该位。

FLASH 芯片的最小擦除单位为扇区(Sector),而一个块(Block)包含 16 个扇区。如下所示

删除4K扇区,程序源码如下:
//读写统一地址 #define FLASH_WriteAddress 0x00000 #define FLASH_ReadAddress FLASH_WriteAddress #define FLASH_SectorToErase FLASH_WriteAddress void SPI_FLASH_SectorErase(u32 SectorAddr) { //需要开写使能 SPI_FLASH_WriteEnable(); SPI_FLASH_WaitForWriteEnd(); SPI_FLASH_CS_LOW(); SPI_FLASH_SendByte(0x20); //三字节地址需要分三次发送,先高位字节,再中间,后低 SPI_FLASH_SendByte((SectorAddr & 0xFF0000) >> 16); SPI_FLASH_SendByte((SectorAddr & 0xFF00) >> 8); SPI_FLASH_SendByte(SectorAddr & 0xFF); SPI_FLASH_CS_HIGH(); SPI_FLASH_WaitForWriteEnd(); }
void SPI_FLASH_PageWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite) { //写使能 SPI_FLASH_WriteEnable(); SPI_FLASH_CS_LOW(); SPI_FLASH_SendByte(0x02); SPI_FLASH_SendByte((WriteAddr & 0xFF0000) >> 16); SPI_FLASH_SendByte((WriteAddr & 0xFF00) >> 8); SPI_FLASH_SendByte(WriteAddr & 0xFF); //错误处理 if(NumByteToWrite > SPI_FLASH_PerWritePageSize) { NumByteToWrite = SPI_FLASH_PerWritePageSize; FLASH_ERROR("SPI_FLASH_PageWrite too large!"); } while (NumByteToWrite--) { SPI_FLASH_SendByte(*pBuffer); pBuffer++; } SPI_FLASH_CS_HIGH(); SPI_FLASH_WaitForWriteEnd(); }
Flash 数据写入示意图
数据写入流程图

程序源码
#define SPI_FLASH_PageSize 256 //pBuffer 写地址指针;WriteAddr写地址;NumByteToWrite写长度 //需要判断WriteAddr写地址是否在首地址还是中间地址 //NumByteToWrite写大小,是整页还是非整页 void SPI_FLASH_BufferWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite) { u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp = 0; //取模,检查有几页 Addr = WriteAddr % SPI_FLASH_PageSize; //差count 个数值,刚好对齐到页地址 count = SPI_FLASH_PageSize - Addr; //整数页 NumOfPage = NumByteToWrite / SPI_FLASH_PageSize; //不满一页的字节数 NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize; //Addr=0 刚好按页对齐 if (Addr == 0) { // NumByteToWrite < SPI_FLASH_PageSize if (NumOfPage == 0) { SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite); } else //一页写步下 NumByteToWrite > SPI_FLASH_PageSize { while (NumOfPage--) { SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize); WriteAddr += SPI_FLASH_PageSize; pBuffer += SPI_FLASH_PageSize; } //剩余不满的写完 SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle); } } //地址不对齐 else { /* NumByteToWrite < SPI_FLASH_PageSize */ if (NumOfPage == 0) { //当前剩余的count比NumOfSingle 小,一页写不完 if (NumOfSingle > count) { temp = NumOfSingle - count; //先写完当页 SPI_FLASH_PageWrite(pBuffer, WriteAddr, count); WriteAddr += count; pBuffer += count; //写剩余的 SPI_FLASH_PageWrite(pBuffer, WriteAddr, temp); } else//当前的剩余count 能写完NumByteToWrite { SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite); } } else /* NumByteToWrite > SPI_FLASH_PageSize */ { //地址不对齐多出的count分开处理,重新计算页数 NumByteToWrite -= count; NumOfPage = NumByteToWrite / SPI_FLASH_PageSize; NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize; //先写完count个数据 SPI_FLASH_PageWrite(pBuffer, WriteAddr, count); // WriteAddr += count; pBuffer += count; //写整页 while (NumOfPage--) { SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize); WriteAddr += SPI_FLASH_PageSize; pBuffer += SPI_FLASH_PageSize; } //多余不满一页的情况 if (NumOfSingle != 0) { SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle); } } } }
//pBuffer读到的缓冲区; ReadAddr 读取Flash地址;NumByteToRead 读数据量 void SPI_FLASH_BufferRead(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead) { SPI_FLASH_CS_LOW(); SPI_FLASH_SendByte(0x03); SPI_FLASH_SendByte((ReadAddr & 0xFF0000) >> 16); SPI_FLASH_SendByte((ReadAddr& 0xFF00) >> 8); SPI_FLASH_SendByte(ReadAddr & 0xFF); while (NumByteToRead--) { *pBuffer = SPI_FLASH_SendByte(Dummy_Byte); pBuffer++; } SPI_FLASH_CS_HIGH(); }
参考资料:
W25Q64 datasheet
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。