W25Q128(W25Q系列SPI Flahs和W25X系列的SPI Flash)驱动,使用句柄方式,分离底层,便于移植。
编写一些应用代码,将底层与实际应用进行分离,方便移植使用,具体思路就是讲所需的全局变量,底层与硬件相关的接口集中到结构体中(类似一个句柄,存放所需的变量),这样这个程序就可以重入,并且便于维护移植,比如有多个W25芯片的时候就特别好用,在实际开发过程中,这种情况会经常遇到,这样一次写好,以后到处都能使用。
先上代码W25XXX.c
/*************************************************************************************************************
* 文件名: W25XXX.c
* 功能: W25XXX SPI flash驱动
* 作者: [email protected]
* 邮箱: [email protected]
* 创建时间: 2011年6月30日
* 最后修改时间:2012年6月2日
* 详细: 注意:信号量只能防止多个读取与单个写冲突,不能防止多个写冲突(因为写入也要读取),建议不要在多个地方同时进行写入操作,如果需要请另外增加信号量进行保护
2016-12-03:增加64KB块擦除
2017-04-14:擦除时增加信号量,防止读写冲突
2017-08-22:bool W25XXX_WriteAboveOneSector(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite); //增量写入,写SPI FLASH 在指定地址开始写入指定长度的数据(会清除当前地址之后的数据保留之前的数据,只能在一个扇区范围内)
2017-11-23:当开启了看门狗,会在擦除时进行喂狗,在所有的写使能后添加写禁止,防止误写入数据。
2018-01-14:分离底层,使用句柄方式访问
*************************************************************************************************************/
#include "SYSTEM.H"
#include "W25XXX.H"
//操作超时
#define W25XXX_TIME_OUT 120*1000 //超时时间,单位ms,120S
//调试开关
#define W25XXX_DBUG 0
#if W25XXX_DBUG
#include "system.h"
#define W25XXX_debug(format,...) uart_printf("W25XXX:"format,##__VA_ARGS__)
#else
#define W25XXX_debug(format,...) /\
/
#endif //W25XXX_DBUG
//页 256字节
//4KB为一个扇区Sector 扇区擦除
//16个扇区为1个块区Block
//W25X16
//容量为2M字节,共有32个块区Block,512个扇区Sector
u8 W25QXX_ReadSR2(W25XXX_HANDLE *pHandle); //读取W25QXX 状态2
u8 W25QXX_ReadSR3(W25XXX_HANDLE *pHandle); //读取W25QXX 状态3
u8 W25XXX_ReadSR(W25XXX_HANDLE *pHandle); //读取W25X16 状态
bool W25XXX_WaitBusy(W25XXX_HANDLE *pHandle); //等待W25X16空闲
void W25XXX_WriteSR(W25XXX_HANDLE *pHandle, u8 sr); //写状态寄存器
void W25QXX_WriteSR2(W25XXX_HANDLE *pHandle, u8 sr); //写状态寄存器2
u32 W25XXX_ReadStatus(W25XXX_HANDLE *pHandle); //写状态寄存器3
void W25XXX_WriteProtect(W25XXX_HANDLE *pHandle,bool EN); //使能或失能存储区写保护
void W25XXX_WriteEnable(W25XXX_HANDLE *pHandle); //写使能
void W25XXX_WriteDisable(W25XXX_HANDLE *pHandle); //写失能
bool W25XXX_WritePage(W25XXX_HANDLE *pHandle,u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite);//在一页(0~65535)内写入少于256个字节的数据,在指定地址开始写入最大256字节的数据
/*************************************************************************************************************************
* 函数 : W25XXX_ID W25XXX_Init(W25XXX_HANDLE *pHandle, u8 (*SPI_ReadWrtieByte)(u8 data),void (*SPI_SetCS)(u8 CS_Level),void (*DelayMs)(u8 ms),void (*MutexPen)(void),void (*MutexPost)(void),void (*ClearWatchdog)(void))
* 功能 : 初始化W25X16 句柄
* 参数 : pHandle:句柄;
SPI_ReadWrtieByte: //SPI读写接口
SPI_SetCS: //SPI片选控制接口
DelayMs: //系统ms延时接口
MutexPen: //申请信号量接口
MutexPost: //释放信号量接口
ClearWatchdog: //清除看门狗接口
* 返回 : W25XXX_ID
* 依赖 : 底层读写函数
* 作者 : [email protected]
* 时间 : 20178-01-14
* 最后修改时间 : 20178-01-14
* 说明 : 用于初始化W25XXX芯片句柄
*************************************************************************************************************************/
W25XXX_ID W25XXX_Init(W25XXX_HANDLE *pHandle, u8 (*SPI_ReadWrtieByte)(u8 data),void (*SPI_SetCS)(u8 CS_Level),void (*DelayMs)(u8 ms),void (*MutexPen)(void),void (*MutexPost)(void),void (*ClearWatchdog)(void))
{
u16 ID;
//进行句柄的初始化
pHandle->SPI_ReadWrtieByte = SPI_ReadWrtieByte; //SPI读写接口
pHandle->SPI_SetCS = SPI_SetCS; //SPI片选控制接口
pHandle->DelayMs = DelayMs; //系统ms延时接口
pHandle->MutexPen = MutexPen; //申请信号量接口
pHandle->MutexPost = MutexPost; //释放信号量接口
pHandle->ClearWatchdog = ClearWatchdog; //清除看门狗接口
pHandle->SPI_SetCS(1); //片选无效,在单片机中一般不要对必须的函数指针或内存进行合法判断,如果没有初始化直接进入异常,否则不好调试问题
pHandle->DelayMs(10);
ID = W25XXX_ReadID(pHandle); //通过读取ID判断是否初始化成功
switch(ID)
{
case W25X16_ID:
{
W25XXX_debug("读取W25X16 ID成功!(0x%04X)\r\n",ID);
pHandle->Model= W25X16;
}break;
case W25X32_ID:
{
W25XXX_debug("读取W25X32 ID成功!(0x%04X)\r\n",ID);
pHandle->Model= W25X32;
}break;
case W25X64_ID:
{
W25XXX_debug("读取W25X64 ID成功!(0x%04X)\r\n",ID);
pHandle->Model= W25X64;
}break;
case W25X128_ID:
{
W25XXX_debug("读取W25X128 ID成功!(0x%04X)\r\n",ID);
pHandle->Model= W25X128;
}break;
case W25X256_ID:
{
W25XXX_debug("读取W25X256 ID成功!(0x%04X)\r\n",ID);
pHandle->Model= W25X256;
}break;
default:
{
W25XXX_debug("初始化W25XXX 失败!(0x%04X)\r\n",ID);
pHandle->Model= FLASH_NULL;
}break;
}
return pHandle->Model;
}
/*************************************************************************************************************************
* 函数 : u32 W25QXX_ReadJEDEC_ID(W25XXX_HANDLE *pHandle)
* 功能 : 读取W25QXX JEDEC ID
* 参数 : pHandle:句柄
* 返回 : ID
* 依赖 : 底层读写函数
* 作者 : [email protected]
* 时间 : 2017-11-23
* 最后修改时间 : 2018-01-14
* 说明 :
*************************************************************************************************************************/
u32 W25QXX_ReadJEDEC_ID(W25XXX_HANDLE *pHandle)
{
u32 Temp = 0;
if(pHandle->MutexPen!=NULL)pHandle->MutexPen(); //申请信号量
pHandle->SPI_SetCS(0);
pHandle->SPI_ReadWrtieByte(0x9F); //发送读取 JEDEC ID命令
Temp |= pHandle->SPI_ReadWrtieByte(0XFF);
Temp <<= 8;
Temp |= pHandle->SPI_ReadWrtieByte(0XFF);
Temp <<= 8;
Temp |= pHandle->SPI_ReadWrtieByte(0XFF);
pHandle->SPI_SetCS(1);
if(pHandle->MutexPost!=NULL)pHandle->MutexPost(); //释放信号量
return Temp;
}
/*************************************************************************************************************************
* 函数 : u8 W25QXX_ReadSR2(W25XXX_HANDLE *pHandle)
* 功能 : 读取W25QXX 状态2
* 参数 : pHandle:句柄
* 返回 :
* 依赖 : 底层读写函数
* 作者 : [email protected]
* 时间 : 2017-11-23
* 最后修改时间 : 2018-01-14
* 说明 : 读取状态标志
*************************************************************************************************************************/
u8 W25QXX_ReadSR2(W25XXX_HANDLE *pHandle)
{
u8 byte = 0;
pHandle->SPI_SetCS(0); //使能器件
pHandle->SPI_ReadWrtieByte(0X35); //0x35发送读取状态寄存器命令
byte = pHandle->SPI_ReadWrtieByte(0xff); //读取一个字节
pHandle->SPI_SetCS(1); //取消片选
return byte;
}
/*************************************************************************************************************************
* 函数 : u8 W25QXX_ReadSR3(W25XXX_HANDLE *pHandle)
* 功能 : 读取W25QXX 状态3
* 参数 : pHandle:句柄
* 返回 :
* 依赖 : 底层读写函数
* 作者 : [email protected]
* 时间 : 2017-11-23
* 最后修改时间 : 2018-01-14
* 说明 : 读取状态标志
*************************************************************************************************************************/
u8 W25QXX_ReadSR3(W25XXX_HANDLE *pHandle)
{
u8 byte = 0;
pHandle->SPI_SetCS(0); //使能器件
pHandle->SPI_ReadWrtieByte(0X15); //0x15发送读取状态寄存器命令
byte = pHandle->SPI_ReadWrtieByte(0xff); //读取一个字节
pHandle->SPI_SetCS(1); //取消片选
return byte;
}
/*************************************************************************************************************************
* 函数 : u8 W25XXX_ReadSR(W25XXX_HANDLE *pHandle)
* 功能 : 读取W25X16 状态
* 参数 : pHandle:句柄
* 返回 : BIT 7 6 5 4 3 2 1 0
SPR RV TB BP2 BP1 BP0 WEL BUSY
SPR:默认0,状态寄存器保护位,配合WP使用
TB,BP2,BP1,BP0:FLASH区域写保护设置
WEL:写使能锁定
BUSY:忙标记位(1,忙;0,空闲)
默认:0x00
* 依赖 : 底层读写函数
* 作者 : [email protected]
* 时间 : 20110630
* 最后修改时间 : 2018-01-14
* 说明 : 读取状态标志
*************************************************************************************************************************/
u8 W25XXX_ReadSR(W25XXX_HANDLE *pHandle)
{
u8 byte = 0;
pHandle->SPI_SetCS(0); //使能器件
pHandle->SPI_ReadWrtieByte(W25X_ReadStatusReg); //0x05发送读取状态寄存器命令
byte = pHandle->SPI_ReadWrtieByte(0xff); //读取一个字节
pHandle->SPI_SetCS(1); //取消片选
return byte;
}
/*************************************************************************************************************************
* 函数 : bool W25XXX_WaitBusy(void)
* 功能 : 等待W25X16空闲
* 参数 : pHandle:句柄
* 返回 : TRUE:成功;FALSE:超时
* 依赖 : 底层读写函数
* 作者 : [email protected]
* 时间 : 20110630
* 最后修改时间 : 2018-01-14
* 说明 : 2014-08-20:添加超时处理
*************************************************************************************************************************/
bool W25XXX_WaitBusy(W25XXX_HANDLE *pHandle)
{
u32 TimeOut = W25XXX_TIME_OUT;
while (((W25XXX_ReadSR(pHandle)&0x01)==0x01) && (TimeOut--)) //等待BUSY位清空
{
pHandle->DelayMs(1);
if(pHandle->ClearWatchdog!=NULL) pHandle->ClearWatchdog(); //清除看门狗
}
if(TimeOut == 0)
{
W25XXX_debug("操作超时!\r\n");
return FALSE;
}
else return TRUE;
}
/*************************************************************************************************************************
* 函数 : void W25XXX_WriteSR(W25XXX_HANDLE *pHandle, u8 sr)
* 功能 : 写W25X16的状态寄存器
* 参数 : pHandle:句柄,状态值 ;只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
* 返回 : 无
* 依赖 : 底层读写函数
* 作者 : [email protected]
* 时间 : 20110630
* 最后修改时间 : 2018-01-14
* 说明 : 只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
*************************************************************************************************************************/
void W25XXX_WriteSR(W25XXX_HANDLE *pHandle, u8 sr)
{
pHandle->SPI_SetCS(0); //使能器件
pHandle->SPI_ReadWrtieByte(W25X_WriteStatusReg); //发送写取状态寄存器命令
pHandle->SPI_ReadWrtieByte(sr); //写入一个字节
pHandle->SPI_SetCS(1); //取消片选
}
/*************************************************************************************************************************
* 函数 : void W25QXX_WriteSR2(W25XXX_HANDLE *pHandle, u8 sr)
* 功能 : 写W25X16的状态寄存器2
* 参数 : pHandle:句柄,状态值 ;
* 返回 : 无
* 依赖 : 底层读写函数
* 作者 : [email protected]
* 时间 : 2017-11-23
* 最后修改时间 : 2018-01-14
* 说明 :
*************************************************************************************************************************/
void W25QXX_WriteSR2(W25XXX_HANDLE *pHandle, u8 sr)
{
pHandle->SPI_SetCS(0); //使能器件
pHandle->SPI_ReadWrtieByte(W25QXX_WRITE_STATUS2); //发送写取状态寄存器命令
pHandle->SPI_ReadWrtieByte(sr); //写入一个字节
pHandle->SPI_SetCS(1); //取消片选
}
/*************************************************************************************************************************
* 函数 : u32 W25XXX_ReadStatus(W25XXX_HANDLE *pHandle)
* 功能 : 读取W25X16 状态(3个状态寄存器)
* 参数 : pHandle:句柄
* 返回 : 状态
* 依赖 : 底层读写函数
* 作者 : [email protected]
* 时间 : 2017-11-23
* 最后修改时间 : 2018-01-14
* 说明 : 读取状态标志
*************************************************************************************************************************/
u32 W25XXX_ReadStatus(W25XXX_HANDLE *pHandle)
{
u32 temp;
temp = W25QXX_ReadSR3(pHandle);
temp <<= 8;
temp |= W25QXX_ReadSR2(pHandle);
temp <<= 8;
temp |= W25XXX_ReadSR(pHandle);
return temp;
}
/*************************************************************************************************************************
* 函数 : void W25XXX_WriteProtect(W25XXX_HANDLE *pHandle,bool EN)
* 功能 : 使能或失能W25X16存储区写保护
* 参数 : pHandle:句柄,EN:ENABLE,写保护使能,否则失能
* 返回 : 无
* 依赖 : 底层读写函数
* 作者 : [email protected]
* 时间 : 20110630
* 最后修改时间 : 2018-01-14
* 说明 : 无
*************************************************************************************************************************/
void W25XXX_WriteProtect(W25XXX_HANDLE *pHandle,bool EN)
{
W25XXX_WriteEnable(pHandle); //写使能
if(EN)
W25XXX_WriteSR(pHandle, 0x3c); //存储区保护
else
W25XXX_WriteSR(pHandle,0x00); //取消存储区保护
W25XXX_WriteDisable(pHandle); //写禁止
}
/*************************************************************************************************************************
* 函数 : void W25XXX_WriteEnable(W25XXX_HANDLE *pHandle)
* 功能 : W25X16写使能
* 参数 : pHandle:句柄
* 返回 : 无
* 依赖 : 底层读写函数
* 作者 : [email protected]
* 时间 : 20110630
* 最后修改时间 : 2018-01-14
* 说明 : 将WEL置位,使能W25X16写数据
*************************************************************************************************************************/
void W25XXX_WriteEnable(W25XXX_HANDLE *pHandle)
{
pHandle->SPI_SetCS(0); //使能器件
pHandle->SPI_ReadWrtieByte(W25X_WriteEnable); //发送写使能
pHandle->SPI_SetCS(1); //取消片选
}
/*************************************************************************************************************************
* 函数 : void W25XXX_WriteDisable(W25XXX_HANDLE *pHandle)
* 功能 : W25X16写失能
* 参数 : pHandle:句柄
* 返回 : 无
* 依赖 : 底层读写函数
* 作者 : [email protected]
* 时间 : 20110630
* 最后修改时间 : 2018-01-14
* 说明 : 将WEL清零 ,失能W25X16写数据
*************************************************************************************************************************/
void W25XXX_WriteDisable(W25XXX_HANDLE *pHandle)
{
pHandle->SPI_SetCS(0); //使能器件
pHandle->SPI_ReadWrtieByte(W25X_WriteDisable); //发送写禁止指令
pHandle->SPI_SetCS(1); //取消片选
}
/*************************************************************************************************************************
* 函数 : u16 W25XXX_ReadID(W25XXX_HANDLE *pHandle)
* 功能 : 读取W25X16 ID
* 参数 : pHandle:句柄
* 返回 : ID, 0xEF14
* 依赖 : 底层读写函数
* 作者 : [email protected]
* 时间 : 20110630
* 最后修改时间 : 2018-01-14
* 说明 : W25X16的ID:0xEF14
*************************************************************************************************************************/
u16 W25XXX_ReadID(W25XXX_HANDLE *pHandle)
{
u16 Temp = 0;
if(pHandle->MutexPen!=NULL)pHandle->MutexPen(); //申请信号量
pHandle->SPI_SetCS(0);
pHandle->SPI_ReadWrtieByte(0x90);//发送读取ID命令
pHandle->SPI_ReadWrtieByte(0x00);
pHandle->SPI_ReadWrtieByte(0x00);
pHandle->SPI_ReadWrtieByte(0x00);
Temp|=pHandle->SPI_ReadWrtieByte(0xFF) << 8;
Temp|=pHandle->SPI_ReadWrtieByte(0xFF);
pHandle->SPI_SetCS(1);
if(pHandle->MutexPost!=NULL)pHandle->MutexPost(); //释放信号量
return Temp;
}
/*************************************************************************************************************************
* 函数 : void W25XXX_Read(W25XXX_HANDLE *pHandle,u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)
* 功能 : 从W25X16 在指定地址开始读取指定长度的数据
* 参数 : pHandle:句柄,pBuffer:数据缓冲区
ReadAddr:起始地址,24bit
NumByteToRead:要读取的字节数(最大65535)
* 返回 : 无
* 依赖 : 底层读写函数
* 作者 : [email protected]
* 时间 : 20110630
* 最后修改时间 : 2018-01-14
* 说明 : 无
*************************************************************************************************************************/
void W25XXX_Read(W25XXX_HANDLE *pHandle,u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)
{
u16 i;
if(pHandle->MutexPen!=NULL)pHandle->MutexPen(); //申请信号量
pHandle->SPI_SetCS(0); //使能器件
pHandle->SPI_ReadWrtieByte(W25X_ReadData); //发送读取命令
pHandle->SPI_ReadWrtieByte((u8)((ReadAddr) >> 16)); //发送24bit地址
pHandle->SPI_ReadWrtieByte((u8)((ReadAddr) >> 8));
pHandle->SPI_ReadWrtieByte((u8)ReadAddr);
for(i=0;iSPI_ReadWrtieByte(0xff); //循环读数
}
pHandle->SPI_SetCS(1); //取消片选
if(pHandle->MutexPost!=NULL)pHandle->MutexPost(); //释放信号量
}
/*************************************************************************************************************************
* 函数 : bool W25XXX_WritePage(W25XXX_HANDLE *pHandle,u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
* 功能 : SPI在一页(0~65535)内写入少于256个字节的数据,在指定地址开始写入最大256字节的数据
* 参数 : pHandle:句柄,pBuffer:数据缓冲区
WriteAddr:起始地址,24bit
NumByteToWrite:要写的字节数(最大256)
* 返回 : TRUE:成功;FALSE:失败,超时
* 依赖 : 底层读写函数
* 作者 : [email protected]
* 时间 : 20110630
* 最后修改时间 : 2018-01-14
* 说明 : NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!
*************************************************************************************************************************/
bool W25XXX_WritePage(W25XXX_HANDLE *pHandle,u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
u16 i;
bool status;
if(pHandle->MutexPen!=NULL)pHandle->MutexPen(); //申请信号量
W25XXX_WriteEnable(pHandle); //SET WEL
pHandle->SPI_SetCS(0); //使能器件
pHandle->SPI_ReadWrtieByte(W25X_PageProgram); //发送写页命令
pHandle->SPI_ReadWrtieByte((u8)((WriteAddr) >> 16)); //发送24bit地址
pHandle->SPI_ReadWrtieByte((u8)((WriteAddr) >> 8));
pHandle->SPI_ReadWrtieByte((u8)WriteAddr);
for(i = 0;i < NumByteToWrite;i ++)
pHandle->SPI_ReadWrtieByte(pBuffer[i]); //循环写数
pHandle->SPI_SetCS(1); //取消片选
status = W25XXX_WaitBusy(pHandle); //等待写入结束
W25XXX_WriteDisable(pHandle); //写禁止
if(pHandle->MutexPost!=NULL)pHandle->MutexPost(); //释放信号量
return status;
}
/*************************************************************************************************************************
* 函数 : bool W25XXX_EraseSector(W25XXX_HANDLE *pHandle,u32 Dst_Addr)
* 功能 : 擦除一个扇区
* 参数 : pHandle:句柄,Dst_Addr:扇区地址 0~511 for w25x16
* 返回 : TRUE:成功;FALSE:失败,超时
* 依赖 : 底层读写函数
* 作者 : [email protected]
* 时间 : 20110630
* 最后修改时间 : 2018-01-14
* 说明 : 擦除一个扇区的最少时间:150ms
*************************************************************************************************************************/
bool W25XXX_EraseSector(W25XXX_HANDLE *pHandle,u32 Dst_Addr)
{
bool status;
Dst_Addr *= 4096; //计算扇区起始地址
if(pHandle->MutexPen!=NULL)pHandle->MutexPen(); //申请信号量
W25XXX_WriteEnable(pHandle); //写使能
if(W25XXX_WaitBusy(pHandle) == FALSE) //检测忙信号
{
W25XXX_WriteDisable(pHandle); //写禁止
if(pHandle->MutexPost!=NULL)pHandle->MutexPost(); //释放信号量
return FALSE;
}
pHandle->SPI_SetCS(0); //使能器件
pHandle->SPI_ReadWrtieByte(W25X_SectorErase); //发送扇区擦除指令
pHandle->SPI_ReadWrtieByte((u8)((Dst_Addr) >> 16)); //发送24bit地址
pHandle->SPI_ReadWrtieByte((u8)((Dst_Addr) >> 8));
pHandle->SPI_ReadWrtieByte((u8)Dst_Addr);
pHandle->SPI_SetCS(1); //取消片选
status = W25XXX_WaitBusy(pHandle); //等待擦除完成
W25XXX_WriteDisable(pHandle); //写禁止
if(pHandle->MutexPost!=NULL)pHandle->MutexPost(); //释放信号量
return status;
}
/*************************************************************************************************************************
* 函数 : bool W25XXX_EraseChip(W25XXX_HANDLE *pHandle)
* 功能 : 擦除整个芯片,格式化
* 参数 : pHandle:句柄
* 返回 : TRUE:成功;FALSE:失败,超时
* 依赖 : 底层读写函数
* 作者 : [email protected]
* 时间 : 20110630
* 最后修改时间 : 2018-01-14
* 说明 : 整片擦除时间:
W25X16:25s
W25X32:40s
W25X64:40s
等待时间超长...
*************************************************************************************************************************/
bool W25XXX_EraseChip(W25XXX_HANDLE *pHandle)
{
bool status;
if(pHandle->MutexPen!=NULL)pHandle->MutexPen(); //申请信号量
W25XXX_WriteEnable(pHandle); //写使能
if(W25XXX_WaitBusy(pHandle)==FALSE) //检测忙信号
{
W25XXX_WriteDisable(pHandle); //写禁止
if(pHandle->MutexPost!=NULL)pHandle->MutexPost();//释放信号量
return FALSE;
}
pHandle->SPI_SetCS(0); //使能器件
pHandle->SPI_ReadWrtieByte(W25X_ChipErase); //发送片擦除命令
pHandle->SPI_SetCS(1); //取消片选
status = W25XXX_WaitBusy(pHandle); //等待芯片擦除结束
W25XXX_WriteDisable(pHandle); //写禁止
if(pHandle->MutexPost!=NULL)pHandle->MutexPost(); //释放信号量
return status;
}
/*************************************************************************************************************************
* 函数 : bool W25XXX_WriteNoCheck(W25XXX_HANDLE *pHandle,u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
* 功能 : 无检验写W25XXX
* 参数 : pHandle:句柄,pBuffer :数据存储区
WriteAddr:开始写入的地址(24bit)
NumByteToWrite:要写入的字节数(最大65535)
* 返回 : TRUE:成功;FALSE:失败,超时
* 依赖 : 底层读写函数
* 作者 : [email protected]
* 时间 : 20110630
* 最后修改时间 : 2018-01-14
* 说明 : 必须确保所写的地址范围内的数据全部为0xFF,否则在非0xFF处写入的数据将失败!
在指定地址开始写入指定长度的数据,但是要确保地址不越界!
具有自动换页功能
*************************************************************************************************************************/
bool W25XXX_WriteNoCheck(W25XXX_HANDLE *pHandle,u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
u16 pageremain;
pageremain = 256 - WriteAddr % 256; //单页剩余的字节数
if(NumByteToWrite <= pageremain)
pageremain = NumByteToWrite; //不大于256个字节
while(1)
{
if(W25XXX_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 W25XXX_Write(W25XXX_HANDLE *pHandle,u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
* 功能 : 写SPI FLASH 在指定地址开始写入指定长度的数据
* 参数 : pHandle:句柄,pBuffer :数据存储区
WriteAddr:开始写入的地址(24bit)
NumByteToWrite:要写入的字节数(最大65535)
* 返回 : 无
* 依赖 : 底层读写函数
* 作者 : [email protected]
* 时间 : 20110630
* 最后修改时间 : 2018-01-14
* 说明 : 该函数带擦除操作!
该函数需要的堆栈很大,注意
*************************************************************************************************************************/
bool W25XXX_Write(W25XXX_HANDLE *pHandle,u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
u32 secpos;
u16 secoff;
u16 secremain;
u16 i;
secpos = WriteAddr / 4096; //扇区地址 0~511 for w25x16
secoff = WriteAddr % 4096; //在扇区内的偏移
secremain = 4096 - secoff; //扇区剩余空间大小
if(NumByteToWrite <= secremain)
secremain = NumByteToWrite; //不大于4096个字节
while(1)
{
W25XXX_Read(pHandle,pHandle->SwapBuff,secpos*4096,4096);//读出整个扇区的内容
for(i = 0;i < secremain;i ++) //校验数据
{
if(pHandle->SwapBuff[secoff+i] != 0xff)break; //需要擦除
}
if(i < secremain) //需要擦除
{
if(W25XXX_EraseSector(pHandle,secpos)==FALSE) //擦除这个扇区
{
return FALSE;
}
for(i = 0;i < secremain;i ++) //复制
{
pHandle->SwapBuff[i + secoff] = pBuffer[i];
}
if(W25XXX_WriteNoCheck(pHandle,pHandle->SwapBuff,secpos*4096,4096)==FALSE)//写入整个扇区
{
return FALSE;
}
}
else
{
if(W25XXX_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 W25XXX_WriteAboveOneSector(W25XXX_HANDLE *pHandle,u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
* 功能 : 写SPI FLASH 在指定地址开始使用增量方式写入指定长度的数据(保留之前的数据,会清除当前地址之后的数据,只能在一个扇区范围内)
* 参数 : pHandle:句柄,pBuffer :数据存储区
WriteAddr:开始写入的地址(24bit)
NumByteToWrite:要写入的字节数(最大65535)
* 返回 : 无
* 依赖 : 底层读写函数
* 作者 : [email protected]
* 时间 : 20110630
* 最后修改时间 : 2018-01-14
* 说明 : 该函数带擦除操作,不会判断是否需要擦除,会直接执行擦除操作!
该函数需要的堆栈很大,注意
只会将当前地址之前的数据进行重写写入,并且写入当前的数据,之后的数据将不会进行还原,不能超出一个扇区范围
*************************************************************************************************************************/
bool W25XXX_WriteAboveOneSector(W25XXX_HANDLE *pHandle,u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
u32 secpos;
u16 secoff;
u16 secremain;
u16 i;
secpos = WriteAddr / 4096; //扇区地址 0~511 for w25x16
secoff = WriteAddr % 4096; //在扇区内的偏移
secremain = 4096 - secoff; //扇区剩余空间大小
if(NumByteToWrite > secremain)
NumByteToWrite = secremain; //不大于剩余空间
W25XXX_Read(pHandle, pHandle->SwapBuff,secpos*4096, secoff); //读出需要写入之前的数据
if(W25XXX_EraseSector(pHandle,secpos)==FALSE) //擦除这个扇区
{
return FALSE;
}
for(i = 0;i < NumByteToWrite;i ++) //复制当前要写入的数据
{
pHandle->SwapBuff[i + secoff] = pBuffer[i];
}
if(W25XXX_WriteNoCheck(pHandle,pHandle->SwapBuff,secpos*4096,secoff+NumByteToWrite)==FALSE)//写入数据
{
return FALSE;
}
return TRUE;
}
/*************************************************************************************************************************
* 函数 : void W25XXX_PowerDown(W25XXX_HANDLE *pHandle)
* 功能 : 进入掉电模式
* 参数 : pHandle:句柄
* 返回 : 无
* 依赖 : 底层读写函数
* 作者 : [email protected]
* 时间 : 20110630
* 最后修改时间 : 2018-01-14
* 说明 : 函数不带信号量保护,使用需要注意,调用后将无法读写flash
*************************************************************************************************************************/
void W25XXX_PowerDown(W25XXX_HANDLE *pHandle)
{
pHandle->SPI_SetCS(0); //使能器件
pHandle->SPI_ReadWrtieByte(W25X_PowerDown); //发送掉电命令
pHandle->SPI_SetCS(1); //取消片选
}
/*************************************************************************************************************************
* 函数 : void W25XXX_WAKEUP(W25XXX_HANDLE *pHandle)
* 功能 : 唤醒W25XXX
* 参数 : pHandle:句柄
* 返回 : 无
* 依赖 : 底层读写函数
* 作者 : [email protected]
* 时间 : 20110630
* 最后修改时间 : 2018-01-14
* 说明 : 函数不带信号量保护
*************************************************************************************************************************/
void W25XXX_WAKEUP(W25XXX_HANDLE *pHandle)
{
pHandle->SPI_SetCS(0); //使能器件
pHandle->SPI_ReadWrtieByte(W25X_ReleasePowerDown); // send W25X_PowerDown command 0xAB
pHandle->SPI_SetCS(1); //取消片选
pHandle->DelayMs(1); //等待TRES1
}
/*************************************************************************************************************************
* 函数 : bool W25XXX_EraseBlock64KB(W25XXX_HANDLE *pHandle,u32 Block_Addr)
* 功能 : 擦除一个64KB块
* 参数 : pHandle:句柄,Block_Addr:块地址,1块=64KB
* 返回 : TRUE:成功;FALSE:失败,超时
* 依赖 : 底层读写函数
* 作者 : [email protected]
* 时间 : 2016-12-03
* 最后修改时间 : 2018-01-14
* 说明 : 擦除一个的最少时间:150ms
*************************************************************************************************************************/
bool W25XXX_EraseBlock64KB(W25XXX_HANDLE *pHandle,u32 Block_Addr)
{
bool status;
if(pHandle->MutexPen!=NULL)pHandle->MutexPen(); //申请信号量
Block_Addr *= 64*1024; //计算块的起始地址
W25XXX_WriteEnable(pHandle); //写使能
if(W25XXX_WaitBusy(pHandle) == FALSE) //检测忙信号
{
W25XXX_WriteDisable(pHandle); //写禁止
if(pHandle->MutexPost!=NULL)pHandle->MutexPost();//释放信号量
return FALSE;
}
pHandle->SPI_SetCS(0); //使能器件
pHandle->SPI_ReadWrtieByte(W25X_BlockErase); //发送64KB块擦除指令
pHandle->SPI_ReadWrtieByte((u8)((Block_Addr) >> 16)); //发送24bit地址
pHandle->SPI_ReadWrtieByte((u8)((Block_Addr) >> 8));
pHandle->SPI_ReadWrtieByte((u8)Block_Addr);
pHandle->SPI_SetCS(1); //取消片选
status = W25XXX_WaitBusy(pHandle); //等待擦除完成
W25XXX_WriteDisable(pHandle); //写禁止
if(pHandle->MutexPost!=NULL)pHandle->MutexPost(); //释放信号量
return status;
}
/*************************************************************************************************************
* 文件名: W25XXX.c
* 功能: W25XXX SPI flash驱动
* 作者: [email protected]
* 邮箱: [email protected]
* 创建时间: 2011年6月30日
* 最后修改时间:2012年6月2日
* 详细: 注意:信号量只能防止多个读取与单个写冲突,不能防止多个写冲突(因为写入也要读取),建议不要在多个地方同时进行写入操作,如果需要请另外增加信号量进行保护
2016-12-03:增加64KB块擦除
2017-04-14:擦除时增加信号量,防止读写冲突
2017-08-22:bool W25XXX_WriteAboveOneSector(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite); //增量写入,写SPI FLASH 在指定地址开始写入指定长度的数据(会清除当前地址之后的数据保留之前的数据,只能在一个扇区范围内)
2017-11-23:当开启了看门狗,会在擦除时进行喂狗,在所有的写使能后添加写禁止,防止误写入数据。
2018-01-14:分离底层,使用句柄方式访问
*************************************************************************************************************/
#ifndef __W25XXX_H_
#define __W25XXX_H_
#include "system.h"
//W25XXX ID定义
#define W25X16_ID 0XEF14
#define W25X32_ID 0XEF15
#define W25X64_ID 0XEF16
#define W25X128_ID 0XEF17
#define W25X256_ID 0X4019
//W25XXX类型
typedef enum
{
W25X16 = 0, //W25X16
W25X32 = 1, //W25X32
W25X64 = 2, //W25X64
W25X128 = 3, //W25Q128
W25X256 = 4, //W25Q256
FLASH_NULL = 0XFF, //无效
}W25XXX_ID;
//W25XXX系列芯片句柄
typedef struct
{
u8 (*SPI_ReadWrtieByte)(u8 data); //底层SPI接口,读写一个字节
void (*SPI_SetCS)(u8 CS_Level); //片选控制
void (*DelayMs)(u8 ms); //延时,毫秒
void (*MutexPen)(void); //申请并等待信号量-如果为空,将不使用
void (*MutexPost)(void); //释放信号量-如果为空,将不使用
void (*ClearWatchdog)(void); //清除看门狗-如果为空,将不使用
W25XXX_ID Model; //芯片型号
u8 SwapBuff[4096]; //一个扇区大小的交换缓冲区(用于写数据擦除时临时备份),当然你可以修改为指针方式,共用其他位置的缓冲区,达到节省内存的目的,仅仅用于W25XXX_Write
}W25XXX_HANDLE;
//相关接口函数
//初始化W25X16 句柄
W25XXX_ID W25XXX_Init(W25XXX_HANDLE *pHandle, u8 (*SPI_ReadWrtieByte)(u8 data),void (*SPI_SetCS)(u8 CS_Level),void (*DelayMs)(u8 ms),void (*MutexPen)(void),void (*MutexPost)(void),void (*ClearWatchdog)(void));
u32 W25QXX_ReadJEDEC_ID(W25XXX_HANDLE *pHandle); //读取W25QXX JEDEC ID
u16 W25XXX_ReadID(W25XXX_HANDLE *pHandle); //读取W25X16 ID
void W25XXX_Read(W25XXX_HANDLE *pHandle,u8* pBuffer,u32 ReadAddr,u16 NumByteToRead);//在指定地址开始读取指定长度的数据
bool W25XXX_EraseSector(W25XXX_HANDLE *pHandle,u32 Dst_Addr); //擦除一个扇区
bool W25XXX_EraseBlock64KB(W25XXX_HANDLE *pHandle,u32 Block_Addr) ; //擦除一个64KB块
bool W25XXX_EraseChip(W25XXX_HANDLE *pHandle); //擦除整个芯片,格式化
bool W25XXX_WriteNoCheck(W25XXX_HANDLE *pHandle,u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite); //无检验写W25XXX
bool W25XXX_Write(W25XXX_HANDLE *pHandle,u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite); //写SPI FLASH 在指定地址开始写入指定长度的数据
bool W25XXX_WriteAboveOneSector(W25XXX_HANDLE *pHandle,u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite); //写SPI FLASH 在指定地址开始使用增量方式写入指定长度的数据(保留之前的数据,会清除当前地址之后的数据,只能在一个扇区范围内)
void W25XXX_PowerDown(W25XXX_HANDLE *pHandle); //进入掉电模式
void W25XXX_WAKEUP(W25XXX_HANDLE *pHandle) ; //唤醒W25XXX
//指令表
#define W25X_WriteEnable 0x06
#define W25X_WriteDisable 0x04
#define W25X_ReadStatusReg 0x05
#define W25X_WriteStatusReg 0x01
#define W25X_ReadData 0x03
#define W25X_FastReadData 0x0B
#define W25X_FastReadDual 0x3B
#define W25X_PageProgram 0x02
#define W25X_BlockErase 0xD8
#define W25X_SectorErase 0x20
#define W25X_ChipErase 0xC7
#define W25X_PowerDown 0xB9
#define W25X_ReleasePowerDown 0xAB
#define W25X_DeviceID 0xAB
#define W25X_ManufactDeviceID 0x90
#define W25X_JedecDeviceID 0x9F
#define W25QXX_JedecDeviceID 0x9F
#define W25QXX_QPI_Mode 0x38
#define W25QXX_EnableReset 0x66
#define W25QXX_Reset 0x99
#define W25QXX_EXIT_QPI_MODE 0xFF
#define W25QXX_WRITE_STATUS2 0x31
#define W25QXX_WRITE_STATUS3 0x11
#endif //__W25XXX_H_
//初始化
W25X16_HardwaveInit(); //初始化W25X16底层硬件SPI接口
W25X16_OSMutexCreate(); //W25X16信号量初始化-需要在任务中进行初始化
g_SysFlag.FlashId = W25XXX_Init(&g_W25X16_Handle,
BI_W25_SPI_ReadWrtieByte, //W25XXX SPI通信接口
BI_W25_SPI_SetCS, //W25XXX SPI片选控制接口
BI_OS_DelayMs, //系统ms延时
BI_W25_MutexPen, //W25XXX 信号量申请
BI_W25_MutexPost, //W25XXX 信号量释放
BI_OS_ClearWatchdog //系统看门狗清除
);
/////////////////////////////////////////////////////////////////////////////////////////////
//W25X64 FLASH支持
//SPI接口设置
#define SPI_FLASH_CS PGout(8) //W25X16片选
#define W25X16_SPI_CH SPI_CH1
//W26X16 硬件接口初始化
void __inline W25X16_HardwaveInit(void)
{
SPIx_Init(W25X16_SPI_CH, &SPI_DEFAULT_01, SPI_1_256);//初始化SPI
SYS_DeviceClockEnable(DEV_GPIOG, TRUE);
SYS_GPIOx_OneInit(GPIOG, 8,OUT_PP, SPEED_50M);
}
void W25X16_OSMutexCreate(void); //W25X16信号量初始化-需要在任务中进行初始化
/////////////////////////////////////////////////////////////////////////////////////////////
//信号量定义-信号量也会占用优先级的
#define W25X16_SEMP 1 //W25X16信号量
//信号量初始化-需要在任务中进行初始化
void W25X16_OSMutexCreate(void)
{
INT8U err;
g_SysFlag.w25x16_semp = OSMutexCreate(W25X16_SEMP, &err); //注册W25X16信号量
if(g_SysFlag.w25x16_semp == NULL)
{
DEBUG("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxW25X16信号量初始化失败%dxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\r\n",err);
}
}
/***********************W25X16相关接口************************/
//W25XXX SPI通信接口
u8 BI_W25_SPI_ReadWrtieByte(u8 data)
{
return SPIx_ReadWriteByte(W25X16_SPI_CH, data);
}
//W25XXX SPI片选控制接口
void BI_W25_SPI_SetCS(u8 CS_Level)
{
SPI_FLASH_CS = CS_Level;
}
//W25XXX 信号量申请
void BI_W25_MutexPen(void)
{
INT8U err;
OSMutexPend(g_SysFlag.w25x16_semp, 0, &err); //申请W25X16信号量
}
//W25XXX 信号量释放
void BI_W25_MutexPost(void)
{
OSMutexPost(g_SysFlag.w25x16_semp); //释放W25X16信号量
}
//系统看门狗清除
void BI_OS_ClearWatchdog(void)
{
IWDG_Feed();
}
//系统ms延时
void BI_OS_DelayMs(u8 ms)
{
OSTimeDlyHMSM(0,0,0,ms);
}