W25QxxxJV系列flash 4线模式驱动 使用HC32F460 QSPI驱动
QSPI可以使用4线模式对SPI flash进行读取,但是写入只能使用1线模式,目前测试使用的是W25Q128JV,注意不是所有的都支持4线模式,4线模式下通常只支持读取,目前测试主要是写入操作,读取操作直接进入ROM模式,使用内存映射即可,非常方便。/*************************************************************************************************************
* 文件名: W25QxxJV.c
* 功能: W25QxxJV QSPI flash驱动
* 作者: cp1300@139.com
* 创建时间: 2021-08-25
* 最后修改时间:2021-08-25
* 详细: 注意:本接口所有操作均为单线模式,扩展SPI模式下操作,读取通常直接由MCU的QSPI映射完成,写入操作必须是扩展SPI模式下完成
* 所有的操作必须退出ROM映射模式之后才能操作,并且需要做好信号量保护
* 正常情况下只需要写入与读取状态的时候使用本驱动所提供的接口,读取直接使用ROM映射模式
*************************************************************************************************************/
#include "SYSTEM.H"
#include "W25QxxJV.H"
//操作超时
#define W25QxxJV_TIME_OUT 120*1000 //超时时间,单位ms,120S
//调试开关
#define W25QxxJV_DBUG 1
#if W25QxxJV_DBUG
#include "system.h"
#define W25QxxJV_debug(format,...) uart_printf("W25QxxJV:"format,##__VA_ARGS__)
#else
#define W25QxxJV_debug(format,...) /\
/
#endif //W25QxxJV_DBUG
//页 256字节
//4KB为一个扇区Sector 扇区擦除
//16个扇区为1个块区Block
static u8 W25QxxJV_ReadSR2(W25QxxJV_HANDLE *pHandle); //读取W25QxxJV 状态2
static u8 W25QxxJV_ReadSR3(W25QxxJV_HANDLE *pHandle); //读取W25QxxJV 状态3
static u8 W25QxxJV_ReadSR(W25QxxJV_HANDLE *pHandle); //读取W25Q16 状态
static bool W25QxxJV_WaitBusy(W25QxxJV_HANDLE *pHandle); //等待W25Q16空闲
static void W25QxxJV_WriteSR(W25QxxJV_HANDLE *pHandle, u8 sr); //写状态寄存器
static void W25QxxJV_WriteSR2(W25QxxJV_HANDLE *pHandle, u8 sr); //写状态寄存器2
static void W25QxxJV_WriteProtect(W25QxxJV_HANDLE *pHandle,bool EN); //使能或失能存储区写保护
static void W25QxxJV_WriteEnable(W25QxxJV_HANDLE *pHandle); //写使能
static void W25QxxJV_WriteDisable(W25QxxJV_HANDLE *pHandle); //写失能
static bool W25QxxJV_WritePage(W25QxxJV_HANDLE *pHandle,u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);//在一页(0~65535)内写入少于256个字节的数据,在指定地址开始写入最大256字节的数据
/*************************************************************************************************************************
* 函数 : W25QxxJV_ID W25QxxJV_Init(W25QxxJV_HANDLE *pHandle,
u8 (*ExtendedSPI_ReadByte)(void),
void (*ExtendedSPI_WriteByte)(u8 data),
void (*EnterDirectMode)(void),
void (*ExitDirectMode)(void),
void (*DelayMs)(u32 ms);
* 功能 : 初始化W25Q16 句柄
* 参数 : pHandle:句柄;
u8 (*ExtendedSPI_ReadByte)(void); //扩展模式SPI,读一个字节
void (*ExtendedSPI_WriteByte)(u8 data); //扩展模式SPI,写一个字节
void (*EnterDirectMode)(void); //进入直接模式-准备开始执行命令
void (*ExitDirectMode)(void); //退出直接模式-一条命令执行结束,拉高CS
void (*DelayMs)(u32 ms); //系统ms延时
* 返回 : W25QxxJV_ID
* 依赖 : 底层读写函数
* 作者 : cp1300@139.com
* 时间 : 2021-08-25
* 最后修改时间 : 2021-08-25
* 说明 : 用于初始化W25QxxJV芯片句柄
*************************************************************************************************************************/
W25QxxJV_ID W25QxxJV_Init(W25QxxJV_HANDLE *pHandle,
u8 (*ExtendedSPI_ReadByte)(void),
void (*ExtendedSPI_WriteByte)(u8 data),
void (*EnterDirectMode)(void),
void (*ExitDirectMode)(void),
void (*DelayMs)(u32 ms))
{
u32 ID;
if(pHandle == NULL)
{
DEBUG("无效的句柄\r\n");
}
if(ExtendedSPI_ReadByte == NULL || ExtendedSPI_WriteByte == NULL || EnterDirectMode==NULL || ExitDirectMode == NULL || DelayMs == NULL)
{
DEBUG("无效的接口\r\n");
}
//进行句柄的初始化
pHandle->ExtendedSPI_ReadByte = ExtendedSPI_ReadByte; //扩展模式SPI,读一个字节
pHandle->ExtendedSPI_WriteByte = ExtendedSPI_WriteByte; //扩展模式SPI,写一个字节
pHandle->EnterDirectMode = EnterDirectMode; //进入直接模式-准备开始执行命令
pHandle->ExitDirectMode= ExitDirectMode; //退出直接模式-一条命令执行结束,拉高CS
pHandle->DelayMs = DelayMs; //系统ms延时接口
pHandle->DelayMs(8);
W25QxxJV_ResetDevice(pHandle); //复位设备
pHandle->DelayMs(2);
ID = W25QxxJV_ReadID(pHandle); //通过读取ID判断是否初始化成功
switch(ID)
{
case W25Q16_ID:
{
W25QxxJV_debug("读取W25Q16 ID成功!(0x%04X)\r\n",ID);
pHandle->Model= W25Q16;
}break;
case W25Q32_ID:
{
W25QxxJV_debug("读取W25Q32 ID成功!(0x%04X)\r\n",ID);
pHandle->Model= W25Q32;
}break;
case W25Q64_ID:
{
W25QxxJV_debug("读取W25Q64 ID成功!(0x%04X)\r\n",ID);
pHandle->Model= W25Q64;
}break;
case W25Q128_ID:
{
W25QxxJV_debug("读取W25Q128 ID成功!(0x%04X)\r\n",ID);
pHandle->Model= W25Q128;
}break;
case W25Q256_ID:
{
W25QxxJV_debug("读取W25Q256 ID成功!(0x%04X)\r\n",ID);
pHandle->Model= W25Q256;
}break;
default:
{
W25QxxJV_debug("初始化W25QxxJV 失败!(0x%04X)\r\n",ID);
pHandle->Model= FLASH_NULL;
}break;
}
//2018-02-08 增加flash写保护检查
if(pHandle->Model != FLASH_NULL)
{
ID = W25QxxJV_ReadStatus(pHandle); //读取状态寄存器
W25QxxJV_debug("W25QxxJV状态寄存器:0x%X\r\n",ID);
if(ID & (BIT2|BIT3|BIT4|BIT5|BIT6))
{
DEBUG("W25QxxJV写保护被开启了,正在尝试关闭写保护!\r\n");
W25QxxJV_WriteEnable(pHandle); //写使能
W25QxxJV_WriteSR(pHandle, 0); //写W25Q16的状态寄存器
W25QxxJV_WriteSR2(pHandle, 0); //写W25Q16的状态寄存器2
W25QxxJV_WriteDisable(pHandle); //写使能关闭
}
}
return pHandle->Model;
}
/*************************************************************************************************************************
* 函数 : u32 W25QxxJV_ReadJEDEC_ID(W25QxxJV_HANDLE *pHandle)
* 功能 : 读取W25QxxJV JEDEC ID
* 参数 : pHandle:句柄
* 返回 : ID
* 依赖 : 底层读写函数
* 作者 : cp1300@139.com
* 时间 : 2017-11-23
* 最后修改时间 : 2021-08-25
* 说明 :
*************************************************************************************************************************/
u32 W25QxxJV_ReadJEDEC_ID(W25QxxJV_HANDLE *pHandle)
{
u32 Temp = 0;
pHandle->EnterDirectMode(); //进入直接模式
pHandle->ExtendedSPI_WriteByte(0x9F); //发送读取 JEDEC ID命令
Temp |= pHandle->ExtendedSPI_ReadByte();
Temp <<= 8;
Temp |= pHandle->ExtendedSPI_ReadByte();
Temp <<= 8;
Temp |= pHandle->ExtendedSPI_ReadByte();
pHandle->ExitDirectMode(); //退出直接模式
return Temp;
}
/*************************************************************************************************************************
* 函数 : u8 W25QxxJV_ReadSR2(W25QxxJV_HANDLE *pHandle)
* 功能 : 读取W25QxxJV 状态2
* 参数 : pHandle:句柄
* 返回 :
* 依赖 : 底层读写函数
* 作者 : cp1300@139.com
* 时间 : 2017-11-23
* 最后修改时间 : 2021-08-25
* 说明 : 读取状态标志
*************************************************************************************************************************/
u8 W25QxxJV_ReadSR2(W25QxxJV_HANDLE *pHandle)
{
u8 byte = 0;
pHandle->EnterDirectMode(); //进入直接模式
pHandle->ExtendedSPI_WriteByte(0X35); //0x35发送读取状态寄存器命令
byte = pHandle->ExtendedSPI_ReadByte(); //读取一个字节
pHandle->ExitDirectMode(); //退出直接模式
return byte;
}
/*************************************************************************************************************************
* 函数 : u8 W25QxxJV_ReadSR3(W25QxxJV_HANDLE *pHandle)
* 功能 : 读取W25QxxJV 状态3
* 参数 : pHandle:句柄
* 返回 :
* 依赖 : 底层读写函数
* 作者 : cp1300@139.com
* 时间 : 2017-11-23
* 最后修改时间 : 2021-08-25
* 说明 : 读取状态标志
*************************************************************************************************************************/
u8 W25QxxJV_ReadSR3(W25QxxJV_HANDLE *pHandle)
{
u8 byte = 0;
pHandle->EnterDirectMode(); //进入直接模式
pHandle->ExtendedSPI_WriteByte(0X15); //0x15发送读取状态寄存器命令
byte = pHandle->ExtendedSPI_ReadByte(); //读取一个字节
pHandle->ExitDirectMode(); //退出直接模式
return byte;
}
/*************************************************************************************************************************
* 函数 : u8 W25QxxJV_ReadSR(W25QxxJV_HANDLE *pHandle)
* 功能 : 读取W25Q16 状态
* 参数 : pHandle:句柄
* 返回 : BIT 7 6 5 4 3 2 1 0
SPRRVTB BP2 BP1 BP0 WEL BUSY
SPR:默认0,状态寄存器保护位,配合WP使用
TB,BP2,BP1,BP0:FLASH区域写保护设置
WEL:写使能锁定
BUSY:忙标记位(1,忙;0,空闲)
默认:0x00
* 依赖 : 底层读写函数
* 作者 : cp1300@139.com
* 时间 : 20110630
* 最后修改时间 : 2021-08-25
* 说明 : 读取状态标志
*************************************************************************************************************************/
u8 W25QxxJV_ReadSR(W25QxxJV_HANDLE *pHandle)
{
u8 byte = 0;
pHandle->EnterDirectMode(); //进入直接模式
pHandle->ExtendedSPI_WriteByte(W25Q_ReadStatusReg); //0x05发送读取状态寄存器命令
byte = pHandle->ExtendedSPI_ReadByte(); //读取一个字节
pHandle->ExitDirectMode(); //退出直接模式
return byte;
}
/*************************************************************************************************************************
* 函数 : bool W25QxxJV_WaitBusy(void)
* 功能 : 等待W25Q16空闲
* 参数 : pHandle:句柄
* 返回 : TRUE:成功;FALSE:超时
* 依赖 : 底层读写函数
* 作者 : cp1300@139.com
* 时间 : 20110630
* 最后修改时间 : 2021-08-25
* 说明 : 2014-08-20:添加超时处理
*************************************************************************************************************************/
bool W25QxxJV_WaitBusy(W25QxxJV_HANDLE *pHandle)
{
u32 TimeOut = W25QxxJV_TIME_OUT;
while (((W25QxxJV_ReadSR(pHandle)&0x01)==0x01) && (TimeOut--)) //等待BUSY位清空
{
pHandle->DelayMs(1);
}
if(TimeOut == 0)
{
W25QxxJV_debug("操作超时!\r\n");
return FALSE;
}
else return TRUE;
}
/*************************************************************************************************************************
* 函数 : void W25QxxJV_WriteSR(W25QxxJV_HANDLE *pHandle, u8 sr)
* 功能 : 写W25Q16的状态寄存器
* 参数 : pHandle:句柄,状态值 ;只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
* 返回 : 无
* 依赖 : 底层读写函数
* 作者 : cp1300@139.com
* 时间 : 20110630
* 最后修改时间 : 2021-08-25
* 说明 : 只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
*************************************************************************************************************************/
void W25QxxJV_WriteSR(W25QxxJV_HANDLE *pHandle, u8 sr)
{
pHandle->EnterDirectMode(); //进入直接模式
pHandle->ExtendedSPI_WriteByte(W25Q_WriteStatusReg); //发送写取状态寄存器命令
pHandle->ExtendedSPI_WriteByte(sr); //写入一个字节
pHandle->ExitDirectMode(); //退出直接模式
}
/*************************************************************************************************************************
* 函数 : void W25QxxJV_WriteSR2(W25QxxJV_HANDLE *pHandle, u8 sr)
* 功能 : 写W25Q16的状态寄存器2
* 参数 : pHandle:句柄,状态值 ;
* 返回 : 无
* 依赖 : 底层读写函数
* 作者 : cp1300@139.com
* 时间 : 2017-11-23
* 最后修改时间 : 2021-08-25
* 说明 :
*************************************************************************************************************************/
void W25QxxJV_WriteSR2(W25QxxJV_HANDLE *pHandle, u8 sr)
{
pHandle->EnterDirectMode(); //进入直接模式
pHandle->ExtendedSPI_WriteByte(W25QxxJV_WRITE_STATUS2); //发送写取状态寄存器命令
pHandle->ExtendedSPI_WriteByte(sr); //写入一个字节
pHandle->ExitDirectMode(); //退出直接模式
}
/*************************************************************************************************************************
* 函数 : u32 W25QxxJV_ReadStatus(W25QxxJV_HANDLE *pHandle)
* 功能 : 读取W25Q16 状态(3个状态寄存器)
* 参数 : pHandle:句柄
* 返回 : 状态
* 依赖 : 底层读写函数
* 作者 : cp1300@139.com
* 时间 : 2017-11-23
* 最后修改时间 : 2021-08-25
* 说明 : 读取状态标志
*************************************************************************************************************************/
u32 W25QxxJV_ReadStatus(W25QxxJV_HANDLE *pHandle)
{
u32 temp;
temp = W25QxxJV_ReadSR3(pHandle);
temp <<= 8;
temp |= W25QxxJV_ReadSR2(pHandle);
temp <<= 8;
temp |= W25QxxJV_ReadSR(pHandle);
return temp;
}
/*************************************************************************************************************************
* 函数 : void W25QxxJV_WriteProtect(W25QxxJV_HANDLE *pHandle,bool EN)
* 功能 : 使能或失能W25Q16存储区写保护
* 参数 : pHandle:句柄,EN:ENABLE,写保护使能,否则失能
* 返回 : 无
* 依赖 : 底层读写函数
* 作者 : cp1300@139.com
* 时间 : 20110630
* 最后修改时间 : 2021-08-25
* 说明 : 无
*************************************************************************************************************************/
void W25QxxJV_WriteProtect(W25QxxJV_HANDLE *pHandle,bool EN)
{
W25QxxJV_WriteEnable(pHandle); //写使能
if(EN)
W25QxxJV_WriteSR(pHandle, 0x3c); //存储区保护
else
W25QxxJV_WriteSR(pHandle,0x00); //取消存储区保护
W25QxxJV_WriteDisable(pHandle); //写禁止
}
/*************************************************************************************************************************
* 函数 : void W25QxxJV_WriteEnable(W25QxxJV_HANDLE *pHandle)
* 功能 : W25Q16写使能
* 参数 : pHandle:句柄
* 返回 : 无
* 依赖 : 底层读写函数
* 作者 : cp1300@139.com
* 时间 : 20110630
* 最后修改时间 : 2021-08-25
* 说明 : 将WEL置位,使能W25Q16写数据
*************************************************************************************************************************/
void W25QxxJV_WriteEnable(W25QxxJV_HANDLE *pHandle)
{
pHandle->EnterDirectMode(); //进入直接模式
pHandle->ExtendedSPI_WriteByte(W25Q_WriteEnable); //发送写使能
pHandle->ExitDirectMode(); //退出直接模式
}
/*************************************************************************************************************************
* 函数 : void W25QxxJV_WriteDisable(W25QxxJV_HANDLE *pHandle)
* 功能 : W25Q16写失能
* 参数 : pHandle:句柄
* 返回 : 无
* 依赖 : 底层读写函数
* 作者 : cp1300@139.com
* 时间 : 20110630
* 最后修改时间 : 2021-08-25
* 说明 : 将WEL清零 ,失能W25Q16写数据
*************************************************************************************************************************/
void W25QxxJV_WriteDisable(W25QxxJV_HANDLE *pHandle)
{
pHandle->EnterDirectMode(); //进入直接模式
pHandle->ExtendedSPI_WriteByte(W25Q_WriteDisable); //发送写禁止指令
pHandle->ExitDirectMode(); //退出直接模式
}
/*************************************************************************************************************************
* 函数 : u16 W25QxxJV_ReadID(W25QxxJV_HANDLE *pHandle)
* 功能 : 读取W25Q16 ID
* 参数 : pHandle:句柄
* 返回 : ID, 0xEF14
* 依赖 : 底层读写函数
* 作者 : cp1300@139.com
* 时间 : 20110630
* 最后修改时间 : 2021-08-25
* 说明 : W25Q16的ID:0xEF14
*************************************************************************************************************************/
u16 W25QxxJV_ReadID(W25QxxJV_HANDLE *pHandle)
{
u16 Temp = 0;
pHandle->EnterDirectMode(); //进入直接模式
pHandle->ExtendedSPI_WriteByte(0x90); //发送读取ID命令
pHandle->ExtendedSPI_WriteByte(0x00);
pHandle->ExtendedSPI_WriteByte(0x00);
pHandle->ExtendedSPI_WriteByte(0x00);
Temp|=(u16)pHandle->ExtendedSPI_ReadByte()<< 8;
Temp|=pHandle->ExtendedSPI_ReadByte();
pHandle->ExitDirectMode(); //退出直接模式
return Temp;
}
/*************************************************************************************************************************
* 函数 : void W25QxxJV_Read(W25QxxJV_HANDLE *pHandle,u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)
* 功能 : 从W25Q16 在指定地址开始读取指定长度的数据
* 参数 : pHandle:句柄,pBuffer:数据缓冲区
ReadAddr:起始地址,24bit
NumByteToRead:要读取的字节数(最大65535)
* 返回 : 无
* 依赖 : 底层读写函数
* 作者 : cp1300@139.com
* 时间 : 20110630
* 最后修改时间 : 2021-08-25
*************************************************************************************************************************/
void W25QxxJV_Read(W25QxxJV_HANDLE *pHandle,u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)
{
u16 i;
pHandle->EnterDirectMode(); //进入直接模式
pHandle->ExtendedSPI_WriteByte(W25Q_ReadData); //发送读取命令
pHandle->ExtendedSPI_WriteByte((u8)((ReadAddr) >> 16)); //发送24bit地址
pHandle->ExtendedSPI_WriteByte((u8)((ReadAddr) >> 8));
pHandle->ExtendedSPI_WriteByte((u8)ReadAddr);
for(i=0;i<NumByteToRead;i++)
{
pBuffer = pHandle->ExtendedSPI_ReadByte(); //循环读数
}
pHandle->ExitDirectMode(); //退出直接模式
}
/*************************************************************************************************************************
* 函数 : bool W25QxxJV_WritePage(W25QxxJV_HANDLE *pHandle,u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
* 功能 : SPI在一页(0~65535)内写入少于256个字节的数据,在指定地址开始写入最大256字节的数据
* 参数 : pHandle:句柄,pBuffer:数据缓冲区
WriteAddr:起始地址,24bit
NumByteToWrite:要写的字节数(最大256)
* 返回 : TRUE:成功;FALSE:失败,超时
* 依赖 : 底层读写函数
* 作者 : cp1300@139.com
* 时间 : 20110630
* 最后修改时间 : 2021-08-25
* 说明 : NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!
*************************************************************************************************************************/
bool W25QxxJV_WritePage(W25QxxJV_HANDLE *pHandle,u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
u16 i;
bool status;
W25QxxJV_WriteEnable(pHandle); //SET WEL
pHandle->EnterDirectMode(); //进入直接模式
pHandle->ExtendedSPI_WriteByte(W25Q_PageProgram); //发送写页命令
pHandle->ExtendedSPI_WriteByte((u8)((WriteAddr) >> 16)); //发送24bit地址
pHandle->ExtendedSPI_WriteByte((u8)((WriteAddr) >> 8));
pHandle->ExtendedSPI_WriteByte((u8)WriteAddr);
for(i = 0;i < NumByteToWrite;i ++)
{
pHandle->ExtendedSPI_WriteByte(pBuffer); //循环写数
}
pHandle->ExitDirectMode(); //退出直接模式
status = W25QxxJV_WaitBusy(pHandle); //等待写入结束
W25QxxJV_WriteDisable(pHandle); //写禁止
return status;
}
/*************************************************************************************************************************
* 函数 : bool W25QxxJV_EraseSector(W25QxxJV_HANDLE *pHandle,u32 Dst_Addr)
* 功能 : 擦除一个扇区
* 参数 : pHandle:句柄,Dst_Addr:扇区地址 0~511 for W25Q16
* 返回 : TRUE:成功;FALSE:失败,超时
* 依赖 : 底层读写函数
* 作者 : cp1300@139.com
* 时间 : 20110630
* 最后修改时间 : 2021-08-25
* 说明 : 擦除一个扇区的最少时间:150ms
*************************************************************************************************************************/
bool W25QxxJV_EraseSector(W25QxxJV_HANDLE *pHandle,u32 Dst_Addr)
{
bool status;
Dst_Addr *= 4096; //计算扇区起始地址
W25QxxJV_WriteEnable(pHandle); //写使能
if(W25QxxJV_WaitBusy(pHandle) == FALSE) //检测忙信号
{
W25QxxJV_WriteDisable(pHandle); //写禁止
return FALSE;
}
pHandle->EnterDirectMode(); //进入直接模式
pHandle->ExtendedSPI_WriteByte(W25Q_SectorErase); //发送扇区擦除指令
pHandle->ExtendedSPI_WriteByte((u8)((Dst_Addr) >> 16)); //发送24bit地址
pHandle->ExtendedSPI_WriteByte((u8)((Dst_Addr) >> 8));
pHandle->ExtendedSPI_WriteByte((u8)Dst_Addr);
pHandle->ExitDirectMode(); //退出直接模式
status = W25QxxJV_WaitBusy(pHandle); //等待擦除完成
W25QxxJV_WriteDisable(pHandle); //写禁止
return status;
}
/*************************************************************************************************************************
* 函数 : bool W25QxxJV_EraseChip(W25QxxJV_HANDLE *pHandle)
* 功能 : 擦除整个芯片,格式化
* 参数 : pHandle:句柄
* 返回 : TRUE:成功;FALSE:失败,超时
* 依赖 : 底层读写函数
* 作者 : cp1300@139.com
* 时间 : 20110630
* 最后修改时间 : 2021-08-25
* 说明 : 整片擦除时间:
W25Q16:25s
W25Q32:40s
W25Q64:40s
等待时间超长...
*************************************************************************************************************************/
bool W25QxxJV_EraseChip(W25QxxJV_HANDLE *pHandle)
{
bool status;
W25QxxJV_WriteEnable(pHandle); //写使能
if(W25QxxJV_WaitBusy(pHandle)==FALSE) //检测忙信号
{
W25QxxJV_WriteDisable(pHandle); //写禁止
return FALSE;
}
pHandle->EnterDirectMode(); //进入直接模式
pHandle->ExtendedSPI_WriteByte(W25Q_ChipErase); //发送片擦除命令
pHandle->ExitDirectMode(); //退出直接模式
status = W25QxxJV_WaitBusy(pHandle); //等待芯片擦除结束
W25QxxJV_WriteDisable(pHandle); //写禁止
return status;
}
/*************************************************************************************************************************
* 函数 : bool W25QxxJV_WriteNoCheck(W25QxxJV_HANDLE *pHandle,u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
* 功能 : 无检验写W25QxxJV
* 参数 : pHandle:句柄,pBuffer :数据存储区
WriteAddr:开始写入的地址(24bit)
NumByteToWrite:要写入的字节数(最大65535)
* 返回 : TRUE:成功;FALSE:失败,超时
* 依赖 : 底层读写函数
* 作者 : cp1300@139.com
* 时间 : 20110630
* 最后修改时间 : 2021-08-25
* 说明 : 必须确保所写的地址范围内的数据全部为0xFF,否则在非0xFF处写入的数据将失败!
在指定地址开始写入指定长度的数据,但是要确保地址不越界!
具有自动换页功能
*************************************************************************************************************************/
bool W25QxxJV_WriteNoCheck(W25QxxJV_HANDLE *pHandle,u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
u16 pageremain;
pageremain = 256 - WriteAddr % 256; //单页剩余的字节数
if(NumByteToWrite <= pageremain)
pageremain = NumByteToWrite; //不大于256个字节
while(1)
{
if(W25QxxJV_WritePage(pHandle, pBuffer,WriteAddr,pageremain)==FALSE)
{
return FALSE;
}
if(NumByteToWrite == pageremain)
break; //写入结束了
else //NumByteToWrite>pageremain
{
pBuffer += pageremain;
WriteAddr += pageremain;
NumByteToWrite -= pageremain; //减去已经写入了的字节数
if(NumByteToWrite > 256)
pageremain = 256; //一次可以写入256个字节
else
pageremain = NumByteToWrite; //不够256个字节了
}
}
return TRUE;
}
/*************************************************************************************************************************
* 函数 : bool W25QxxJV_Write(W25QxxJV_HANDLE *pHandle,u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
* 功能 : 写SPI FLASH 在指定地址开始写入指定长度的数据
* 参数 : pHandle:句柄,pBuffer :数据存储区
WriteAddr:开始写入的地址(24bit)
NumByteToWrite:要写入的字节数(最大65535)
* 返回 : 无
* 依赖 : 底层读写函数
* 作者 : cp1300@139.com
* 时间 : 20110630
* 最后修改时间 : 2021-08-25
* 说明 : 该函数带擦除操作!
该函数需要的堆栈很大,注意
*************************************************************************************************************************/
bool W25QxxJV_Write(W25QxxJV_HANDLE *pHandle,u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
u32 secpos;
u16 secoff;
u16 secremain;
u16 i;
secpos = WriteAddr / 4096; //扇区地址 0~511 for W25Q16
secoff = WriteAddr % 4096; //在扇区内的偏移
secremain = 4096 - secoff; //扇区剩余空间大小
if(NumByteToWrite <= secremain)
secremain = NumByteToWrite; //不大于4096个字节
while(1)
{
W25QxxJV_Read(pHandle,pHandle->SwapBuff,secpos*4096,4096);//读出整个扇区的内容
for(i = 0;i < secremain;i ++) //校验数据
{
if(pHandle->SwapBuff != 0xff)break; //需要擦除
}
if(i < secremain) //需要擦除
{
if(W25QxxJV_EraseSector(pHandle,secpos)==FALSE) //擦除这个扇区
{
return FALSE;
}
for(i = 0;i < secremain;i ++) //复制
{
pHandle->SwapBuff = pBuffer;
}
if(W25QxxJV_WriteNoCheck(pHandle,pHandle->SwapBuff,secpos*4096,4096)==FALSE)//写入整个扇区
{
return FALSE;
}
}
else
{
if(W25QxxJV_WriteNoCheck(pHandle,pBuffer,WriteAddr,secremain)==FALSE)//写已经擦除了的,直接写入扇区剩余区间.
{
return FALSE;
}
}
if(NumByteToWrite==secremain) break; //写入结束了
else//写入未结束
{
secpos ++; //扇区地址增1
secoff = 0; //偏移位置为0
pBuffer += secremain; //指针偏移
WriteAddr += secremain; //写地址偏移
NumByteToWrite -= secremain; //字节数递减
if(NumByteToWrite > 4096)
secremain = 4096; //下一个扇区还是写不完
else
secremain = NumByteToWrite; //下一个扇区可以写完了
}
}
return TRUE;
}
/*************************************************************************************************************************
* 函数 : bool W25QxxJV_WriteAboveOneSector(W25QxxJV_HANDLE *pHandle,u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
* 功能 : 写SPI FLASH 在指定地址开始使用增量方式写入指定长度的数据(保留之前的数据,会清除当前地址之后的数据,只能在一个扇区范围内)
* 参数 : pHandle:句柄,pBuffer :数据存储区
WriteAddr:开始写入的地址(24bit)
NumByteToWrite:要写入的字节数(最大65535)
* 返回 : 无
* 依赖 : 底层读写函数
* 作者 : cp1300@139.com
* 时间 : 20110630
* 最后修改时间 : 2021-08-25
* 说明 : 该函数带擦除操作,不会判断是否需要擦除,会直接执行擦除操作!
该函数需要的堆栈很大,注意
只会将当前地址之前的数据进行重写写入,并且写入当前的数据,之后的数据将不会进行还原,不能超出一个扇区范围
*************************************************************************************************************************/
bool W25QxxJV_WriteAboveOneSector(W25QxxJV_HANDLE *pHandle,u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
u32 secpos;
u16 secoff;
u16 secremain;
u16 i;
secpos = WriteAddr / 4096; //扇区地址 0~511 for W25Q16
secoff = WriteAddr % 4096; //在扇区内的偏移
secremain = 4096 - secoff; //扇区剩余空间大小
if(NumByteToWrite > secremain)
NumByteToWrite = secremain; //不大于剩余空间
W25QxxJV_Read(pHandle, pHandle->SwapBuff,secpos*4096, secoff); //读出需要写入之前的数据
if(W25QxxJV_EraseSector(pHandle,secpos)==FALSE) //擦除这个扇区
{
return FALSE;
}
for(i = 0;i < NumByteToWrite;i ++) //复制当前要写入的数据
{
pHandle->SwapBuff = pBuffer;
}
if(W25QxxJV_WriteNoCheck(pHandle,pHandle->SwapBuff,secpos*4096,secoff+NumByteToWrite)==FALSE)//写入数据
{
return FALSE;
}
return TRUE;
}
/*************************************************************************************************************************
* 函数 : void W25QxxJV_PowerDown(W25QxxJV_HANDLE *pHandle)
* 功能 : 进入掉电模式
* 参数 : pHandle:句柄
* 返回 : 无
* 依赖 : 底层读写函数
* 作者 : cp1300@139.com
* 时间 : 20110630
* 最后修改时间 : 2021-08-25
* 说明 : 函数不带信号量保护,使用需要注意,调用后将无法读写flash
*************************************************************************************************************************/
void W25QxxJV_PowerDown(W25QxxJV_HANDLE *pHandle)
{
pHandle->EnterDirectMode(); //进入直接模式
pHandle->ExtendedSPI_WriteByte(W25Q_PowerDown); //发送掉电命令
pHandle->ExitDirectMode(); //退出直接模式
}
/*************************************************************************************************************************
* 函数 : void W25QxxJV_WAKEUP(W25QxxJV_HANDLE *pHandle)
* 功能 : 唤醒W25QxxJV
* 参数 : pHandle:句柄
* 返回 : 无
* 依赖 : 底层读写函数
* 作者 : cp1300@139.com
* 时间 : 20110630
* 最后修改时间 : 2021-08-25
* 说明 : 函数不带信号量保护
*************************************************************************************************************************/
void W25QxxJV_WAKEUP(W25QxxJV_HANDLE *pHandle)
{
pHandle->EnterDirectMode(); //进入直接模式
pHandle->ExtendedSPI_WriteByte(W25Q_ReleasePowerDown);// send W25Q_PowerDown command 0xAB
pHandle->ExitDirectMode(); //退出直接模式
pHandle->DelayMs(1); //等待TRES1
}
/*************************************************************************************************************************
* 函数 : bool W25QxxJV_EraseBlock64KB(W25QxxJV_HANDLE *pHandle,u32 Block_Addr)
* 功能 : 擦除一个64KB块
* 参数 : pHandle:句柄,Block_Addr:块地址,1块=64KB
* 返回 : TRUE:成功;FALSE:失败,超时
* 依赖 : 底层读写函数
* 作者 : cp1300@139.com
* 时间 : 2016-12-03
* 最后修改时间 : 2021-08-25
* 说明 : 擦除一个的最少时间:150ms
*************************************************************************************************************************/
bool W25QxxJV_EraseBlock64KB(W25QxxJV_HANDLE *pHandle,u32 Block_Addr)
{
bool status;
Block_Addr *= 64*1024; //计算块的起始地址
W25QxxJV_WriteEnable(pHandle); //写使能
if(W25QxxJV_WaitBusy(pHandle) == FALSE) //检测忙信号
{
W25QxxJV_WriteDisable(pHandle); //写禁止
return FALSE;
}
pHandle->EnterDirectMode(); //进入直接模式
pHandle->ExtendedSPI_WriteByte(W25Q_BlockErase); //发送64KB块擦除指令
pHandle->ExtendedSPI_WriteByte((u8)((Block_Addr) >> 16)); //发送24bit地址
pHandle->ExtendedSPI_WriteByte((u8)((Block_Addr) >> 8));
pHandle->ExtendedSPI_WriteByte((u8)Block_Addr);
pHandle->ExitDirectMode(); //退出直接模式
status = W25QxxJV_WaitBusy(pHandle); //等待擦除完成
W25QxxJV_WriteDisable(pHandle); //写禁止
return status;
}
/*************************************************************************************************************************
* 函数 : void W25QxxJV_ResetDevice(W25QxxJV_HANDLE *pHandle)
* 功能 : 复位设备
* 参数 : pHandle:句柄
* 返回 : 无
* 依赖 : 底层读写函数
* 作者 : cp1300@139.com
* 时间 : 2021-08-25
* 最后修改时间 : 2021-08-25
* 说明 :
*************************************************************************************************************************/
void W25QxxJV_ResetDevice(W25QxxJV_HANDLE *pHandle)
{
pHandle->EnterDirectMode(); //进入直接模式
pHandle->ExtendedSPI_WriteByte(W25QxxJV_EnableReset); //使能复位
pHandle->ExitDirectMode(); //退出直接模式
pHandle->DelayMs(1);
pHandle->EnterDirectMode(); //进入直接模式
pHandle->ExtendedSPI_WriteByte(W25QxxJV_Reset); //复位
pHandle->ExitDirectMode(); //退出直接模式
pHandle->DelayMs(1);
}
/*************************************************************************************************************
* 文件名: W25QxxJV.h
* 功能: W25QxxJV QSPI flash驱动
* 作者: cp1300@139.com
* 创建时间: 2021-08-25
* 最后修改时间:2021-08-25
* 详细: 注意:本接口所有操作均为单线模式,扩展SPI模式下操作,读取通常直接由MCU的QSPI映射完成,写入操作必须是扩展SPI模式下完成
* 所有的操作必须退出ROM映射模式之后才能操作,并且需要做好信号量保护
*************************************************************************************************************/
#ifndef __W25QxxJV_H_
#define __W25QxxJV_H_
#include "system.h"
//W25QxxJV ID定义
#define W25Q16_ID 0XEF14
#define W25Q32_ID 0XEF15
#define W25Q64_ID 0XEF16
#define W25Q128_ID 0XEF17
#define W25Q256_ID 0X4019
//W25QxxJV类型
typedef enum
{
W25Q16 = 0, //W25Q16
W25Q32 = 1, //W25Q32
W25Q64 = 2, //W25Q64
W25Q128 = 3, //W25Q128
W25Q256 = 4, //W25Q256
FLASH_NULL = 0XFF, //无效
}W25QxxJV_ID;
//W25QxxJV系列芯片句柄
typedef struct
{
u8 (*ExtendedSPI_ReadByte)(void); //扩展模式SPI,读一个字节
void (*ExtendedSPI_WriteByte)(u8 data); //扩展模式SPI,写一个字节
void (*EnterDirectMode)(void); //进入直接模式-准备开始执行命令
void (*ExitDirectMode)(void); //退出直接模式-一条命令执行结束,拉高CS
void (*DelayMs)(u32 ms); //延时,毫秒
W25QxxJV_ID Model; //芯片型号
u8 SwapBuff; //一个扇区大小的交换缓冲区(用于写数据擦除时临时备份),当然你可以修改为指针方式,共用其他位置的缓冲区,达到节省内存的目的,仅仅用于W25QxxJV_Write
}W25QxxJV_HANDLE;
//相关接口函数
//初始化W25QxxJV句柄
W25QxxJV_ID W25QxxJV_Init(W25QxxJV_HANDLE *pHandle,
u8 (*ExtendedSPI_ReadByte)(void),
void (*ExtendedSPI_WriteByte)(u8 data),
void (*EnterDirectMode)(void),
void (*ExitDirectMode)(void),
void (*DelayMs)(u32 ms));
u32 W25QxxJV_ReadJEDEC_ID(W25QxxJV_HANDLE *pHandle); //读取W25QXX JEDEC ID
u16 W25QxxJV_ReadID(W25QxxJV_HANDLE *pHandle); //读取W25Q16 ID
void W25QxxJV_Read(W25QxxJV_HANDLE *pHandle,u8* pBuffer,u32 ReadAddr,u16 NumByteToRead);//在指定地址开始读取指定长度的数据
bool W25QxxJV_EraseSector(W25QxxJV_HANDLE *pHandle,u32 Dst_Addr); //擦除一个扇区
bool W25QxxJV_EraseBlock64KB(W25QxxJV_HANDLE *pHandle,u32 Block_Addr) ; //擦除一个64KB块
bool W25QxxJV_EraseChip(W25QxxJV_HANDLE *pHandle); //擦除整个芯片,格式化
bool W25QxxJV_WriteNoCheck(W25QxxJV_HANDLE *pHandle,u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite); //无检验写W25QxxJV
bool W25QxxJV_Write(W25QxxJV_HANDLE *pHandle,u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite); //写SPI FLASH 在指定地址开始写入指定长度的数据
bool W25QxxJV_WriteAboveOneSector(W25QxxJV_HANDLE *pHandle,u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite); //写SPI FLASH 在指定地址开始使用增量方式写入指定长度的数据(保留之前的数据,会清除当前地址之后的数据,只能在一个扇区范围内)
void W25QxxJV_PowerDown(W25QxxJV_HANDLE *pHandle); //进入掉电模式
void W25QxxJV_WAKEUP(W25QxxJV_HANDLE *pHandle); //唤醒W25QxxJV
u32 W25QxxJV_ReadStatus(W25QxxJV_HANDLE *pHandle); //读取状态
void W25QxxJV_ResetDevice(W25QxxJV_HANDLE *pHandle); //复位设备
//指令表-单线模式(传统的SPI模式指令)
#define W25Q_WriteEnable 0x06
#define W25Q_WriteDisable 0x04
#define W25Q_ReadStatusReg 0x05
#define W25Q_WriteStatusReg 0x01
#define W25Q_ReadData 0x03
#define W25Q_FastReadData 0x0B
#define W25Q_FastReadDual 0x3B
#define W25Q_PageProgram 0x02
#define W25Q_BlockErase 0xD8
#define W25Q_SectorErase 0x20
#define W25Q_ChipErase 0xC7
#define W25Q_PowerDown 0xB9
#define W25Q_ReleasePowerDown 0xAB
#define W25Q_DeviceID 0xAB
#define W25Q_ManufactDeviceID 0x90
#define W25Q_JedecDeviceID 0x9F
#define W25QxxJV_JedecDeviceID 0x9F
#define W25QxxJV_QPI_Mode 0x38
#define W25QxxJV_EnableReset 0x66
#define W25QxxJV_Reset 0x99
#define W25QxxJV_EXIT_QPI_MODE 0xFF
#define W25QxxJV_WRITE_STATUS2 0x31
#define W25QxxJV_WRITE_STATUS3 0x11
#endif //__W25QxxJV_H_
页:
[1]