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;i<NumByteToRead;i++) { pBuffer[i] = pHandle->SPI_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.h
/************************************************************************************************************* * 文件名: 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); }