打印
[其他]

W25QxxxJV系列flash 4线模式驱动 使用HC32F460 QSPI驱动

[复制链接]
4560|5
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
tpgf|  楼主 | 2021-10-1 18:21 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
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;
}




使用特权

评论回复
沙发
tpgf|  楼主 | 2021-10-1 18:22 | 只看该作者

/*************************************************************************************************************************
* 函数                        :        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
                                        SPR  RV  TB 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;
}



使用特权

评论回复
板凳
tpgf|  楼主 | 2021-10-1 18:23 | 只看该作者

/*************************************************************************************************************************
* 函数                        :        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();                                  //退出直接模式
}



使用特权

评论回复
地板
tpgf|  楼主 | 2021-10-1 18:23 | 只看该作者

/*************************************************************************************************************************
* 函数                        :        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;
}



使用特权

评论回复
5
tpgf|  楼主 | 2021-10-1 18:24 | 只看该作者
/*************************************************************************************************************************
* 函数                        :        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[secoff+i] != 0xff)break;                //需要擦除            
                }
                if(i < secremain)                                                                                //需要擦除
                {
                        if(W25QxxJV_EraseSector(pHandle,secpos)==FALSE)                //擦除这个扇区
                        {
                                return FALSE;
                        }
                        for(i = 0;i < secremain;i ++)                                                   //复制
                        {
                                pHandle->SwapBuff[i + secoff] = 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[i + secoff] = 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);
}


使用特权

评论回复
6
tpgf|  楼主 | 2021-10-1 18:24 | 只看该作者
/*************************************************************************************************************
* 文件名:                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[4096];                                                                        //一个扇区大小的交换缓冲区(用于写数据擦除时临时备份),当然你可以修改为指针方式,共用其他位置的缓冲区,达到节省内存的目的,仅仅用于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_


使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

1897

主题

15568

帖子

11

粉丝