HC32F460 QSPI底层驱动(W25Q128)

本文档详细介绍了HC32F46X微控制器的QSPI(四线串行外设接口)的初始化、时钟分频设置、工作模式配置、地址线宽度设置、虚拟周期数量设定以及ROM访问模式的启用和禁用。通过示例代码展示了如何使用QSPI与外部Flash进行标准读取和快速读取操作,同时提供了QSPI直通模式下扩展SPI模式的读写数据函数。QSPI测试部分演示了对W25Q128JV Flash的初始化、写入和高速读取过程,强调了在不同模式下的操作注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


//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)
/*************************************************************************************************************
 * 文件名		:	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;	//关闭预读取功能
	}
}



/*************************************************************************************************************
 * 文件名		:	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_

//进行测试 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] = 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[i]);
    }
    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。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cp1300

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值