打印
[其他]

HC32F460 QSPI底层驱动(W25Q128)

[复制链接]
4709|13
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
tpgf|  楼主 | 2021-10-1 18:24 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

//QSPI========================================================================================================
#define QSPI_BASE                       (0x9c000000UL)      //寄存器基址

typedef struct
{
        vu32         CR;                        //控制寄存器
        vu32         CSCR;                //片选控制寄存器
        vu32         FCR;                //格式控制寄存器
        vu32         SR;                        //状态寄存器
        vu32         DCOM;                //直接通信指令寄存器
        vu32         CCMD;                //指令代码寄存器
        vu32         XCMD;                //XIP模式代码寄存器
        u32                Reserved1[2];
        vu32         SR2;                //标志清除寄存器(只写 0x0024h
        u32                Reserved2[2+500+1];       
        vu32         EXAR;               
}QSPI_TypeDef;
#define QSPI          ((QSPI_TypeDef *) QSPI_BASE)

使用特权

评论回复
沙发
tpgf|  楼主 | 2021-10-1 18:25 | 只看该作者
/*************************************************************************************************************
* 文件名                :        hc32f46x_qspi.c
* 功能                        :        HC32F46X QSPI驱动
* 作者                        :        cp1300@139.com
* 创建时间                :        2021-08-25
* 最后修改时间        :        2021-08-25
* 详细                        :        使用直接模式进行访问,关闭XIP模式;
*************************************************************************************************************/
#include "hc32f46x.h"
#include "hc32f46x_map.h"
#include "system.h"
#include "hc32f46x_qspi.h"

void QSPI_SPI_WriteByte(u8 data) {QSPI->DCOM = data;}        //QSPI直通模式下扩展SPI模式发送一字节数据
u8 QSPI_SPI_ReadByte(void) {return QSPI->DCOM;}                        //QSPI直通模式下扩展SPI模式读取一字节数据
void QSPI_EnterDirectMode(void) {QSPI->CR |= BIT5;}     //进入直接模式-准备开始执行命令
void QSPI_ExitDirectMode(void){QSPI->CR &= ~BIT5;}      //退出直接模式-一条命令执行结束,拉高CS

/*************************************************************************************************************************
* 函数                        :        void QSPI_Init(QSPI_ADDR_WIDTH AddrWidth, u8 ClockDiv, u8 DummyCnt)
* 功能                        :        QSPI初始化
* 参数                        :        AddrWidth:地址宽度;ClockDiv:HCLK时钟分频(2-64分频);DummyCnt:虚拟周期数量
* 返回                        :        无
* 依赖                        :        底层读写函数
* 作者                        :        cp1300@139.com
* 时间                        :        2021-08-25
* 最后修改时间         :         2021-08-25
* 说明                        :         默认初始化后为标准读取-1位,扩展SPI模式,ROM映射模式
*************************************************************************************************************************/
void QSPI_Init(QSPI_ADDR_WIDTH AddrWidth, u8 ClockDiv, u8 DummyCnt)
{
        SYS_DeviceClockEnable(DEV_QSPI, TRUE);           //使能时钟
    QSPI->CR = 0;
        QSPI->CSCR = 0;  
        QSPI->CSCR |= 2<<4;  //将 QSSN有效时间延长 128个 QSCK周期
    QSPI->CSCR |= 1<<0;  //SSN信号最小无效时间选择 2个QSCLK周期
        QSPI->FCR = 0;   
        QSPI->FCR |= 1<<6;  //WP高电平
    QSPI->FCR |= 1<<5;  //比 QSCK第一个上升沿提前 1.5个 QSCK输出 QSSN
    QSPI->FCR |= 1<<4;  //比 QSCK最后一个上升沿滞后 1.5个 QSCK释放 QSSN

    QSPI_SetClockDiv(ClockDiv);                                        //设置时钟分频
        QSPI_SetAddrWidth(AddrWidth);                                //QSPI设置地址线宽度
        QSPI_SetDummyCnt(DummyCnt);                                        //QSPI设置虚拟周期数量(只对快速指令ROM映射模式下有效)

        QSPI->SR2 |= BIT7;                                                        //清除RAER位
}


/*************************************************************************************************************************
* 函数                        :        void QSPI_SetClockDiv(u8 ClockDiv)
* 功能                        :        QSPI时钟分频设置
* 参数                        :        ClockDiv:HCLK时钟分频(2-64分频)
* 返回                        :        无
* 依赖                        :        底层读写函数
* 作者                        :        cp1300@139.com
* 时间                        :        2021-08-25
* 最后修改时间         :         2021-08-25
* 说明                        :        
*************************************************************************************************************************/
void QSPI_SetClockDiv(u8 ClockDiv)
{
        u32 temp;
       
        if(ClockDiv < 2) ClockDiv = 2;
        if(ClockDiv > 64) ClockDiv = 64;
       

        if(ClockDiv % 2) //奇数分频,设置占空比修正       
        {
                QSPI->FCR |= BIT15;
        }
        else
        {
                QSPI->FCR &= ~BIT15;
        }
        temp = QSPI->CR;
        temp &= ~(0x3F<<16);        //清除之前配置
        temp |= (u32)(ClockDiv-1) << 16;
        QSPI->CR = temp;
}


/*************************************************************************************************************************
* 函数                        :        void QSPI_SetMode(QSPI_MODE_SELECT mode)
* 功能                        :        QSPI工作模式设置
* 参数                        :        mode工作模式
* 返回                        :        无
* 依赖                        :        底层读写函数
* 作者                        :        cp1300@139.com
* 时间                        :        2021-08-25
* 最后修改时间         :         2021-08-25
* 说明                        :         QSPI_STAND_READ                        =        0,        //标准读取-1位
                                        QSPI_FAST_READ                        =        1,        //快速读取-1位
                                        QSPI_2WIRE_FAST_READ        =        2,        //二线式输出快速读-只有数据是2位
                                        QSPI_2WIRE_IO_FAST_READ        =        3,        //二线式输入输出快速读-地址与数据都是2位
                                        QSPI_4WIRE_FAST_READ        =        4,        //四线式输出快速读-只有数据是4位,地址还是1位
                                        QSPI_4WIRE_IO_FAST_READ        =        5,        //四线式输入输出快速读-就是使用4位的数据与地址线
                                        由于常用的W25Q系列只支持1-4-4 也就是命令阶段只支持1线模式,因此无论哪种模式下,命令均为1线模式,也就是命令
                                                发送阶段均固定为扩展SPI模式
*************************************************************************************************************************/
void QSPI_SetMode(QSPI_MODE_SELECT mode)
{
        u32 temp = QSPI->CR;
       
        temp &= ~(0X3F<<8);                                //清除SPI协议设置,全部变为默认的扩展SPI协议,也就是1线
        temp &= ~0x07;                                        //清除掉工作模式,设置为标准模式
        switch(mode)
        {
                case QSPI_STAND_READ:                //标准读取-1位  1-1-1
                {
                       
                }break;
                case QSPI_FAST_READ        :                //快速读取-1位   1-1-1
                {
                        temp |= QSPI_FAST_READ;
                }break;
                case QSPI_2WIRE_FAST_READ:        //二线式输出快速读-只有数据是2位   1-1-2
                {
                        temp |= QSPI_2WIRE_FAST_READ;
                        temp |= QSPI_PROTO_2_WIRE << 12;        //设置数据阶段SPI协议
                }break;
                case QSPI_2WIRE_IO_FAST_READ://二线式输入输出快速读-地址与数据都是2位  1-2-2
                {
                        temp |= QSPI_2WIRE_IO_FAST_READ;
                        temp |= QSPI_PROTO_2_WIRE << 12;        //设置数据阶段SPI协议
                        temp |= QSPI_PROTO_2_WIRE << 10;        //设置地址阶段SPI协议
                }break;
                case QSPI_4WIRE_FAST_READ:        //四线式输出快速读-只有数据是4位,地址还是1位  1-1-4
                {
                        temp |= QSPI_4WIRE_FAST_READ;
                        temp |= QSPI_PROTO_4_WIRE << 12;        //设置数据阶段SPI协议
                }break;
                case QSPI_4WIRE_IO_FAST_READ://四线式输入输出快速读-就是使用4位的数据与地址线   1-4-4
                {
                        temp |= QSPI_4WIRE_IO_FAST_READ;
                        temp |= QSPI_PROTO_4_WIRE << 12;        //设置数据阶段SPI协议
                        temp |= QSPI_PROTO_4_WIRE << 10;        //设置地址阶段SPI协议
                }break;
                default: //使用默认的1-1-1
                {
                       
                }break;
        }

        QSPI->CR = temp;
}


/*************************************************************************************************************************
* 函数                        :        void QSPI_SetAddrWidth(QSPI_ADDR_WIDTH width)
* 功能                        :        QSPI设置地址线宽度
* 参数                        :        width:地址线宽度,见 QSPI_ADDR_WIDTH
* 返回                        :        无
* 依赖                        :        底层读写函数
* 作者                        :        cp1300@139.com
* 时间                        :        2021-08-25
* 最后修改时间         :         2021-08-25
* 说明                        :         QSPI_ADDR_WIDTH:
                                                QSPI_ADDR_8BIT        =        0,
                                                QSPI_ADDR_16BIT        =        1,
                                                QSPI_ADDR_24BIT        =        2,
                                                QSPI_ADDR_32BIT        =        3,
*************************************************************************************************************************/
void QSPI_SetAddrWidth(QSPI_ADDR_WIDTH width)
{
        u32 temp;
       
        temp = QSPI->FCR;
        temp &= ~(0x07<<0);        //清除之前配置
        temp |= width;
        if(width == QSPI_ADDR_32BIT)       
        {
                temp |= BIT2;        //使能4字节地址读指令代码
        }
        QSPI->FCR = temp;
}


/*************************************************************************************************************************
* 函数                        :        void QSPI_SetDummyCnt(u8 cnt)
* 功能                        :        QSPI设置虚拟周期数量(只对快速指令ROM映射模式下有效)
* 参数                        :        cnt:虚拟周期数量3-18个
* 返回                        :        无
* 依赖                        :        底层读写函数
* 作者                        :        cp1300@139.com
* 时间                        :        2021-08-25
* 最后修改时间         :         2021-08-25
* 说明                        :
*************************************************************************************************************************/
void QSPI_SetDummyCnt(u8 cnt)
{
        u32 temp;
       
        if(cnt < 3) cnt = 3;
        if(cnt > 18) cnt = 18;
        temp = QSPI->FCR;
        temp &= ~(0x0f<<8);        //清除之前配置
        temp |= (u32)(cnt-3) << 8;
        QSPI->FCR = temp;
}

/*************************************************************************************************************************
* 函数                        :        void QSPI_EnterRomMode(bool isEnable)
* 功能                        :        QSPI设置是否使能ROM访问模式
* 参数                        :        isEnable:TRUE:使能ROM访问模式;FALSE:退出ROM访问模式
* 返回                        :        无
* 依赖                        :        底层读写函数
* 作者                        :        cp1300@139.com
* 时间                        :        2021-08-25
* 最后修改时间         :         2021-08-25
* 说明                        :        使能ROM访问模式后,将开启预读取功能
                                        直通模式下,必须退出ROM模式才能完成一次命令发送,只有进入ROM模式才能出发NSS变为高电平
*************************************************************************************************************************/
void QSPI_EnterRomMode(bool isEnable)
{
        if(isEnable)        //使能
        {
                QSPI->CR &= ~BIT5;        //使能ROM访问模式
                QSPI->CR |= BIT3;        //使能预读取功能
        }
        else
        {
                QSPI->CR |= BIT5;        //关闭ROM访问模式
                QSPI->CR &= ~BIT3;        //关闭预读取功能
        }
}




使用特权

评论回复
板凳
tpgf|  楼主 | 2021-10-1 18:25 | 只看该作者
/*************************************************************************************************************
* 文件名                :        hc32f46x_qspi.h
* 功能                        :        HC32F46X QSPI驱动
* 作者                        :        cp1300@139.com
* 创建时间                :        2021-08-25
* 最后修改时间        :        2021-08-25
* 详细                        :       
*************************************************************************************************************/
#ifndef _HC32F46X_QSPI_H_
#define _HC32F46X_QSPI_H_
#include "hc32f46x_system.h"

//SPI协议选择
typedef enum
{
        QSPI_PROTO_EXPAND        =        0,        //扩展SPI协议
        QSPI_PROTO_2_WIRE        =        1,        //二线式 SPI协议
        QSPI_PROTO_4_WIRE        =        1,        //四线式 SPI协议
}QSPI_PROTO_SELECT;

//SPI接口读取模式选择
typedef enum
{
        QSPI_STAND_READ                        =        0,        //标准读取-1位          1-1-1
        QSPI_FAST_READ                        =        1,        //快速读取-1位          1-1-1
        QSPI_2WIRE_FAST_READ        =        2,        //二线式输出快速读-只有数据是2位        1-1-2
        QSPI_2WIRE_IO_FAST_READ        =        3,        //二线式输入输出快速读-地址与数据都是2位        1-2-2
        QSPI_4WIRE_FAST_READ        =        4,        //四线式输出快速读-只有数据是4位,地址还是1位 1-1-4
        QSPI_4WIRE_IO_FAST_READ        =        5,        //四线式输入输出快速读-就是使用4位的数据与地址线 1-4-4
        //QSPI_CUSTOM_STAND_READ        =        6,        //自定义标准读取
        //QSPI_CUSTOM_FAST_READ        =        7,        //自定义快速读取
}QSPI_MODE_SELECT;


//地址长度定义
typedef enum
{
        QSPI_ADDR_8BIT        =        0,
        QSPI_ADDR_16BIT        =        1,
        QSPI_ADDR_24BIT        =        2,
        QSPI_ADDR_32BIT        =        3,
}QSPI_ADDR_WIDTH;


void QSPI_Init(QSPI_ADDR_WIDTH AddrWidth, u8 ClockDiv, u8 DummyCnt);//QSPI初始化
void QSPI_SetClockDiv(u8 ClockDiv);                                //QSPI时钟分频设置
void QSPI_SetMode(QSPI_MODE_SELECT mode);                //QSPI工作模式设置
void QSPI_SetDummyCnt(u8 cnt);                                        //QSPI设置虚拟周期数量(只对快速指令ROM映射模式下有效)
void QSPI_EnterRomMode(bool isEnable);                        //QSPI设置是否使能ROM访问模式
void QSPI_SetAddrWidth(QSPI_ADDR_WIDTH width);        //QSPI设置地址线宽度
void QSPI_SPI_WriteByte(u8 data);                                //QSPI直通模式下扩展SPI模式发送一字节数据
u8 QSPI_SPI_ReadByte(void);                                                //QSPI直通模式下扩展SPI模式读取一字节数据
void QSPI_EnterDirectMode(void);                //进入直接模式-准备开始执行命令
void QSPI_ExitDirectMode(void);                 //退出直接模式-一条命令执行结束,拉高CS


#endif //_HC32F46X_QSPI_H_


使用特权

评论回复
地板
tpgf|  楼主 | 2021-10-1 18:25 | 只看该作者
进行测试 W25Q128JV 支持4线操作


/*************************************************************************************************************
* 文件名:                        QSPI_test.c
* 功能:                        QSPI测试
* 作者:                        cp1300@139.com
* 创建时间:                2021-08-25
* 最后修改时间:        2021-08-25
* 详细:                        FLASH要用W25Q128JV系列
*************************************************************************************************************/
#include "system.h"
#include "hc32f46x_system.h"
#include "test.h"
#include "hc32f46x_qspi.h"
#include "W25QxxJV.h"

W25QxxJV_HANDLE mW25QxxJV_Handle;
u8 buff[256];

//QSPI测试
void QSPI_test(void)
{  
    W25QxxJV_ID id;
        u16 i;
    u8 *p = (u8 *)0x98000000;   //QSPI地址映射

    QSPI_Init(QSPI_ADDR_24BIT, 8, 6);//QSPI初始化

    //初始化QSPI IO接口
    //CS:PB1  CLK:PB14  D0:PB13 D1:PB12 D2:PB10 D3:PB2
    SYS_GPIOx_SetAF(GPIOB, 1, 7);
    SYS_GPIOx_SetAF(GPIOB, 14, 7);
    SYS_GPIOx_SetAF(GPIOB, 13, 7);
    SYS_GPIOx_SetAF(GPIOB, 12, 7);
    SYS_GPIOx_SetAF(GPIOB, 10, 7);
    SYS_GPIOx_SetAF(GPIOB, 2, 7);

        QSPI_SetMode(QSPI_STAND_READ);        //QSPI工作模式设置
        QSPI_EnterRomMode(FALSE);                //QSPI设置是否使能ROM访问模式-退出ROM模式
    while(1)
    {
        id = W25QxxJV_Init(&mW25QxxJV_Handle,
            QSPI_SPI_ReadByte, QSPI_SPI_WriteByte,
            QSPI_EnterDirectMode, QSPI_ExitDirectMode,
            SYS_DelayMS);
        if(id != FLASH_NULL) break;
        SYS_DelayMS(1000);
    }
   
        //写入flash
        for(i = 0;i < 256;i ++)
        {
                buff = i;
        }

    uart_printf("开始写入W25QxxJv测试...\r\n");
        if(W25QxxJV_Write(&mW25QxxJV_Handle, buff, 0, 256) == TRUE)//写SPI FLASH 在指定地址开始写入指定长度的数据
        {
                uart_printf("写入W25QxxJv成功\r\n");
        }
        else
        {
                uart_printf("写入W25QxxJv失败\r\n");
        }
       

    QSPI_SetMode(QSPI_4WIRE_IO_FAST_READ);        //QSPI工作模式设置
        QSPI_EnterRomMode(TRUE);                //QSPI设置是否使能ROM访问模式-使能ROM模式
    uart_printf("开始读取W25QxxJv测试...\r\n");
    for(i = 0;i < 256;i ++)
    {
        uart_printf("%02X ", p);
    }
    uart_printf("\r\n");


    QSPI_SetMode(QSPI_STAND_READ);        //QSPI工作模式设置
        QSPI_EnterRomMode(FALSE);                //QSPI设置是否使能ROM访问模式-退出ROM模式
    while(1)
    {

        uart_printf("ID:0x%X\r\n", W25QxxJV_ReadStatus(&mW25QxxJV_Handle));
        SYS_DelayMS(1000);
    }
}




注意:对flash的读取使用ROM模式,直接访问指定地址即可读取,任何其它操作,包括写,读取状态等,都需要退出4线模式以及ROM模式,进入直通模式,操作完成后,设置为1-4-4也就是1命令线,4地址线,4数据线模式,然后进入ROM模式,即可直接通过地址映射方式读取flash。

使用特权

评论回复
5
gygp| | 2021-10-3 21:39 | 只看该作者
使用fatfs读写速度怎么样  

使用特权

评论回复
6
chenci2013| | 2021-10-3 21:40 | 只看该作者
w25q128的驱动和w25q64一样吗

使用特权

评论回复
7
biechedan| | 2021-10-3 21:40 | 只看该作者
如何使用w25q128升级程序

使用特权

评论回复
8
wangdezhi| | 2021-10-3 21:40 | 只看该作者
驱动的速度怎么样   

使用特权

评论回复
9
isseed| | 2021-10-3 21:40 | 只看该作者
能使用操作系统吗?   

使用特权

评论回复
10
xietingfeng| | 2021-10-3 21:40 | 只看该作者
QSPI?      

使用特权

评论回复
11
suzhanhua| | 2021-10-3 21:41 | 只看该作者
HC32F460速度怎么样  

使用特权

评论回复
12
uiint| | 2021-10-3 21:41 | 只看该作者
没有工程文件吗?         

使用特权

评论回复
13
hellosdc| | 2021-10-3 21:42 | 只看该作者
HC32F460 没有eeprom吗?   

使用特权

评论回复
14
mituzu| | 2021-10-3 21:42 | 只看该作者
这个是官网的程序吗   

使用特权

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

本版积分规则

1897

主题

15568

帖子

11

粉丝