教程:AT32F435 QSPI 读写W25Q256

本文使用的是雅特力 AT32F435VGT7作为测试QSPI接口的单片机主要参数如下:

博客原文链接

封装:LQFP100
Flash:1024KB
RAM:384KB
最大主频:288MHz
QSPI接口Flash:W25Q256FVEG
QSPI接口为2个 这里使用的是QSPI1

使用的接口为QSPI1,引脚对应如下:

单片机引脚引脚定义W25Q256引脚
35QSPI_IO05脚DI
32QSPI_IO12脚DO
33QSPI_IO23脚WP
34QSPI_IO37脚HOLD
36QSPI_SCK6脚CLK
47QSPI_CS1脚CS#

如下图所示:
W25Q256引脚图MCU引脚图
AT32F435的QSPI简介:
官方文档参考:
链接: AN0088_AT32_MCU_QSPI_Application_Note_ZH_V2.0.3在这里插入图片描述

W25Q256需要几个重要的命令如下表:

命令类型命令说明
W25X_WriteEnable0x06写使能
W25X_ReadStatusReg10x05读状态寄存器1
W25X_ReadStatusReg20x35读状态寄存器2
W25X_ReadStatusReg30x15读状态寄存器3
W25X_WriteStatusReg20x31写状态寄存器2
W25X_ManufactDeviceID0x90读手册ID
W25X_Enable4ByteAddr0xB7使能4字节地址模式
W25X_ChipErase0xC7全片擦除
W25X_SectorErase0x20扇区擦除
W25X_EnterQPIMode0x38使能QSPI模式
W25X_SetReadParam0xC0设置读速度
W25X_FastReadData0x0B快速读取数据
W25X_PageProgram0x02页编程

这里参考正点原子STM32F767开发文档说明:

状态寄存器3S23S22S21S20S19S18S17S16
位说明HODL/RSTDRV1DRV0WPSADPADS
状态寄存器2S15S14S13S12S11S10S9S8
位说明SUSCMPLB3LB2LB1QESRP1
状态寄存器1S7S6S5S4S3S2S1S0
位说明SRP0TBBP3BP2BP1BP0BUSY

上面三个状态寄存器,我们只关心我们需要用到的一些位:ADSQEBUSY 位。其他位
的说明,请看 W25Q256 的数据手册。

ADS 位,表示 W25Q256 当前的地址模式,是一个只读位,当 ADS=0 的时候,表示当前是
3 字节地址模式,当 ADS=1 的时候,表示当前是 4 字节地址模式,我们需要使用 4 字节地址模
式,所以在读取到该位为 0 的时候,必须通过 W25X_Enable4ByteAddr 指令,设置为 4 字节地
址模式。

QE 位,用于使能 4 线模式(Quad),此位可读可写,并且是可以保存的(掉电后可以继续
保持上一次的值)。在本章,我们需要用到 4 线模式,所以在读到该位为 0 的时候,必须通过
W25X_WriteStatusReg2 指令设置此位为 1,表示使能 4 线模式。

BUSY 位,用于表示擦除/编程操作是否正在进行,当擦除/编程操作正在进行时,此位为 1,
此时 W25Q256 不接受任何指令,当擦除/编程操作完成时,此位为 0。此位为只读位,我们在执
行某些操作的时候,必须等待此位为 0。

W25X_ManufactDeviceID 指令,用于读取 W25Q256 的 ID,可以用于判断 W25Q256 是否正常。对于 W25Q256 来说:MF[7:0]=0XEF,ID[7:0]=0X18。

W25X_EnterQPIMode 指令,用于设置 W25Q256 进入 QPI 模式。上电时,W25Q256 默认是 SPI
模式,我们需要通过该指令设置其进入 QPI 模式。注意:在发送该指令之前,必须先设置状态
寄存器 2 的 QE 位为 1!!

W25X_Enable4ByteAddr 指令,用于设置 W25Q256 进入 4 字节地址模式。当读取到 ADS 位为
0 的时候,我们必须通过此指令将 W25Q256 设置为 4 字节地址模式,否则将只能访问 16MB 的地
址空间。

W25X_SetReadParam 指令,可以用于设置读参数控制位 P[5:4],具体参考数据手册
我们这里设置 P[5:4]=11,即可工作在 104Mhz的时钟频率下。此时,读取数据时的 dummy 时钟个数为 8 个(参见 W25X_FastReadData 指令)

W25X_WriteEnable 指令,用于设置 W25Q256 写使能。在执行擦除、编程、写状态寄存器等
操作之前,都必须通过该指令,设置 W25Q256 写使能,否则无法写入。

W25X_FastReadData 指令,用于读取 FLASH 数据,在发送完该指令以后,就可以读取 W25Q256
的数据了。该指令发送完成后,我们可以持续读取 FLASH 里面的数据,只要不停的给时钟,就
可以不停的读取数据。

W25X_PageProgram 指令,用于编程 FLASH(写入数据到 FLASH),该指令发送完成后,最
多可以一次写入 256 字节到 W25Q256,超过 256 字节则需要多次发送该指令。

W25X_SectorErase 指令,用于擦除一个扇区(4KB)的数据。因为 FLASH 具有只可以写 0,
不可以写 1 的特性,所以在写入数据的时候,一般需要先擦除(归 1),再写。W25Q256 的最小
擦除单位为一个扇区(4KB)。该指令在写入数据的时候,经常要有用。

W25X_ChipErase 指令,用于全片擦除 W25Q256。

接下来设置W25Q256的初始化步骤:
一 初始化AT32 QSPI接口IO引脚并设置为复用

	gpio_init_type gpio_init_struct;
	
  crm_periph_clock_enable(CRM_QSPI1_PERIPH_CLOCK, TRUE);
  crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE);
  crm_periph_clock_enable(CRM_GPIOB_PERIPH_CLOCK, TRUE);
  crm_periph_clock_enable(CRM_GPIOC_PERIPH_CLOCK, TRUE);


  gpio_default_para_init(&gpio_init_struct);

  //io0
  gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
  gpio_init_struct.gpio_out_type  = GPIO_OUTPUT_PUSH_PULL;
  gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
  gpio_init_struct.gpio_pins = GPIO_PINS_0;
  gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
  gpio_init(GPIOB, &gpio_init_struct);
  gpio_pin_mux_config(GPIOB, GPIO_PINS_SOURCE0, GPIO_MUX_10);

  //io1
  gpio_init_struct.gpio_pins = GPIO_PINS_7;
  gpio_init(GPIOA, &gpio_init_struct);
  gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE7, GPIO_MUX_10);

  //io2
  gpio_init_struct.gpio_pins = GPIO_PINS_4;
  gpio_init(GPIOC, &gpio_init_struct);
  gpio_pin_mux_config(GPIOC, GPIO_PINS_SOURCE4, GPIO_MUX_10);

	//io3
  gpio_init_struct.gpio_pins = GPIO_PINS_5;
  gpio_init(GPIOC, &gpio_init_struct);
  gpio_pin_mux_config(GPIOC, GPIO_PINS_SOURCE5, GPIO_MUX_10);

  //sck
  gpio_init_struct.gpio_pins = GPIO_PINS_1;
  gpio_init(GPIOB, &gpio_init_struct);
  gpio_pin_mux_config(GPIOB, GPIO_PINS_SOURCE1, GPIO_MUX_9);

  //cs
  gpio_init_struct.gpio_pins = GPIO_PINS_10;
  gpio_init(GPIOB, &gpio_init_struct);
  gpio_pin_mux_config(GPIOB, GPIO_PINS_SOURCE10, GPIO_MUX_9);

二 切换 QSPI 控制器到 XIP 模式或命令从模式

qspi_xip_enable(QSPI1, FALSE);

三 设置 HCLK 到 SCLK 的分频
这里时钟288Mhz 设置为4分频就是72MHz

qspi_clk_division_set(QSPI1, QSPI_CLK_DIV_4);

四 设置 SCLK 在 idle 时的电位

qspi_sck_mode_set(QSPI1, QSPI_SCK_MODE_0);

五 设置 FLASH 规格中 Status 的 WIP 位置

qspi_busy_config(QSPI1, QSPI_BUSY_OFFSET_0);

六 使能QSPI模式

W25Qxx_QSPI_Enable();

七 设置为4字节地址模式,否则只能读到16MB

W25Qxx_QSPI_4ByteAdd();

八 设置QSPI模式为最大时钟104MHz

W25Qxx_QSPI_MaxSCK();

杰西莱展示QSPI具体的QSPI.c和QSPI.h和main.c文件
一 QSPI.h文件

#ifndef __QSPIFLASH_H
#define __QSPIFLASH_H




#include "system.h"



//指令表
#define W25X_WriteEnable        0x06 
#define W25X_WriteDisable       0x04 
#define W25X_ReadStatusReg1     0x05 
#define W25X_ReadStatusReg2     0x35 
#define W25X_ReadStatusReg3     0x15 

#define W25X_WriteStatusReg1    0x01 
#define W25X_WriteStatusReg2    0x31 
#define W25X_WriteStatusReg3    0x11 

#define W25X_ReadData			     0x03 
#define W25X_FastReadData		   0x0B 
#define W25X_FastReadDual		   0x3B 
#define W25X_PageProgram		   0x02
#define W25X_QPIPageProgram    0x32
#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 W25X_Enable4ByteAddr   0xB7
#define W25X_Exit4ByteAddr     0xE9
#define W25X_SetReadParam		   0xC0 
#define W25X_EnterQPIMode      0x38
#define W25X_ExitQPIMode       0xFF


void W25Qxx_QSPI_Init(void);

void W25Qxx_QSPI_Enable(void);
u16  W25Qxx_QSPI_readID(void);
void W25Qxx_QSPI_4ByteAdd(void);
void W25Qxx_QSPI_MaxSCK(void);
void W25Qxx_QSPI_EraseChip(void);
void W25Qxx_QSPI_EraseSector(u32 Addr);
void W25Qxx_QSPI_Page(u8* pbuff,u32 Addr,u16 wlen);

void W25Qxx_QSPI_Read(u8* pbuff,u32 Addr,u16 rlen);
void W25Qxx_QSPI_Write(u8* pBuffer,u32 WriteAddr,u16 wlen);

#endif

二 QSPI.c文件

#include "QSPIFlash.h"
#include "CH340N.h"

u8 W25Qxx_QSPI_readSR(u8 cmd);
void W25Qxx_QSPI_SendCMD(u8 cmd);
void W25Qxx_QSPI_writeSR(u8 cmd,u8 data);

//初始化
void W25Qxx_QSPI_Init(void)
{
	gpio_init_type gpio_init_struct;
	
  crm_periph_clock_enable(CRM_QSPI1_PERIPH_CLOCK, TRUE);
  crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE);
  crm_periph_clock_enable(CRM_GPIOB_PERIPH_CLOCK, TRUE);
	crm_periph_clock_enable(CRM_GPIOC_PERIPH_CLOCK, TRUE);


  gpio_default_para_init(&gpio_init_struct);

  //io0
  gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
  gpio_init_struct.gpio_out_type  = GPIO_OUTPUT_PUSH_PULL;
  gpio_init_struct.gpio_mode = GPIO_MODE_MUX;
  gpio_init_struct.gpio_pins = GPIO_PINS_0;
  gpio_init_struct.gpio_pull = GPIO_PULL_NONE;
  gpio_init(GPIOB, &gpio_init_struct);
  gpio_pin_mux_config(GPIOB, GPIO_PINS_SOURCE0, GPIO_MUX_10);

  //io1
  gpio_init_struct.gpio_pins = GPIO_PINS_7;
  gpio_init(GPIOA, &gpio_init_struct);
  gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE7, GPIO_MUX_10);

  //io2
  gpio_init_struct.gpio_pins = GPIO_PINS_4;
  gpio_init(GPIOC, &gpio_init_struct);
  gpio_pin_mux_config(GPIOC, GPIO_PINS_SOURCE4, GPIO_MUX_10);

	//io3
  gpio_init_struct.gpio_pins = GPIO_PINS_5;
  gpio_init(GPIOC, &gpio_init_struct);
  gpio_pin_mux_config(GPIOC, GPIO_PINS_SOURCE5, GPIO_MUX_10);

  //sck
  gpio_init_struct.gpio_pins = GPIO_PINS_1;
  gpio_init(GPIOB, &gpio_init_struct);
  gpio_pin_mux_config(GPIOB, GPIO_PINS_SOURCE1, GPIO_MUX_9);

  //cs
  gpio_init_struct.gpio_pins = GPIO_PINS_10;
  gpio_init(GPIOB, &gpio_init_struct);
  gpio_pin_mux_config(GPIOB, GPIO_PINS_SOURCE10, GPIO_MUX_9);
	
	qspi_xip_enable(QSPI1, FALSE);
  qspi_clk_division_set(QSPI1, QSPI_CLK_DIV_4);
  qspi_sck_mode_set(QSPI1, QSPI_SCK_MODE_0);
  qspi_busy_config(QSPI1, QSPI_BUSY_OFFSET_0);
	
	W25Qxx_QSPI_Enable();
	W25Qxx_QSPI_4ByteAdd();
	W25Qxx_QSPI_MaxSCK();
}



//使能QSPI 4线模式
void W25Qxx_QSPI_Enable(void)
{
	u8 state=0;
	qspi_cmd_type qspi_cmd_struct;
	
	//读状态寄存器2
	qspi_cmd_struct.pe_mode_enable = FALSE;
  qspi_cmd_struct.pe_mode_operate_code = 0;
  qspi_cmd_struct.instruction_code = W25X_ReadStatusReg2;
  qspi_cmd_struct.instruction_length = QSPI_CMD_INSLEN_1_BYTE;
  qspi_cmd_struct.address_code = 0;
  qspi_cmd_struct.address_length = QSPI_CMD_ADRLEN_0_BYTE;
  qspi_cmd_struct.data_counter = 1;
  qspi_cmd_struct.second_dummy_cycle_num = 0;
  qspi_cmd_struct.operation_mode = QSPI_OPERATE_MODE_111;
  qspi_cmd_struct.read_status_config = QSPI_RSTSC_HW_AUTO;
  qspi_cmd_struct.read_status_enable = FALSE;
  qspi_cmd_struct.write_data_enable = FALSE;
	qspi_cmd_operation_kick(QSPI1,&qspi_cmd_struct);
	while(qspi_flag_get(QSPI1, QSPI_RXFIFORDY_FLAG) == RESET);
	state=qspi_byte_read(QSPI1);
	while(qspi_flag_get(QSPI1, QSPI_CMDSTS_FLAG) == RESET);
	qspi_flag_clear(QSPI1, QSPI_CMDSTS_FLAG);
	
	if((state&0x02)==0)
	{
		//发送写使能
		qspi_cmd_struct.pe_mode_enable = FALSE;
		qspi_cmd_struct.pe_mode_operate_code = 0;
		qspi_cmd_struct.instruction_code = W25X_WriteEnable;
		qspi_cmd_struct.instruction_length = QSPI_CMD_INSLEN_1_BYTE;
		qspi_cmd_struct.address_code = 0;
		qspi_cmd_struct.address_length = QSPI_CMD_ADRLEN_0_BYTE;
		qspi_cmd_struct.data_counter = 0;
		qspi_cmd_struct.second_dummy_cycle_num = 0;
		qspi_cmd_struct.operation_mode = QSPI_OPERATE_MODE_111;
		qspi_cmd_struct.read_status_config = QSPI_RSTSC_HW_AUTO;
		qspi_cmd_struct.read_status_enable = FALSE;
		qspi_cmd_struct.write_data_enable = TRUE;
		qspi_cmd_operation_kick(QSPI1,&qspi_cmd_struct);
		while(qspi_flag_get(QSPI1, QSPI_CMDSTS_FLAG) == RESET);
		qspi_flag_clear(QSPI1, QSPI_CMDSTS_FLAG);
		
		//写状态寄存器
		state|=1<<1;
		qspi_cmd_struct.pe_mode_enable = FALSE;
		qspi_cmd_struct.pe_mode_operate_code = 0;
		qspi_cmd_struct.instruction_code = W25X_WriteStatusReg2;
		qspi_cmd_struct.instruction_length = QSPI_CMD_INSLEN_1_BYTE;
		qspi_cmd_struct.address_code = 0;
		qspi_cmd_struct.address_length = QSPI_CMD_ADRLEN_0_BYTE;
		qspi_cmd_struct.data_counter = 1;
		qspi_cmd_struct.second_dummy_cycle_num = 0;
		qspi_cmd_struct.operation_mode = QSPI_OPERATE_MODE_111;
		qspi_cmd_struct.read_status_config = QSPI_RSTSC_HW_AUTO;
		qspi_cmd_struct.read_status_enable = FALSE;
		qspi_cmd_struct.write_data_enable = TRUE;
		qspi_cmd_operation_kick(QSPI1,&qspi_cmd_struct);
	
		while(qspi_flag_get(QSPI1, QSPI_TXFIFORDY_FLAG) == RESET);
		qspi_byte_write(QSPI1, state);
	
		while(qspi_flag_get(QSPI1, QSPI_CMDSTS_FLAG) == RESET);
		qspi_flag_clear(QSPI1, QSPI_CMDSTS_FLAG);
	}
	
	//发送4线命令
	qspi_cmd_struct.pe_mode_enable = FALSE;
	qspi_cmd_struct.pe_mode_operate_code = 0;
	qspi_cmd_struct.instruction_code = W25X_EnterQPIMode;
	qspi_cmd_struct.instruction_length = QSPI_CMD_INSLEN_1_BYTE;
	qspi_cmd_struct.address_code = 0;
	qspi_cmd_struct.address_length = QSPI_CMD_ADRLEN_0_BYTE;
	qspi_cmd_struct.data_counter = 0;
	qspi_cmd_struct.second_dummy_cycle_num = 0;
	qspi_cmd_struct.operation_mode = QSPI_OPERATE_MODE_111;
	qspi_cmd_struct.read_status_config = QSPI_RSTSC_HW_AUTO;
	qspi_cmd_struct.read_status_enable = FALSE;
	qspi_cmd_struct.write_data_enable = TRUE;
	qspi_cmd_operation_kick(QSPI1,&qspi_cmd_struct);
	while(qspi_flag_get(QSPI1, QSPI_CMDSTS_FLAG) == RESET);
	qspi_flag_clear(QSPI1, QSPI_CMDSTS_FLAG);
}

//读W25Q256FVEG ID
u16 W25Qxx_QSPI_readID(void)
{
	u16 temp=0,ID=0;
	
	qspi_cmd_type qspi_cmd_struct;
	
	qspi_cmd_struct.pe_mode_enable = FALSE;
  qspi_cmd_struct.pe_mode_operate_code = 0;
  qspi_cmd_struct.instruction_code = W25X_ManufactDeviceID;
  qspi_cmd_struct.instruction_length = QSPI_CMD_INSLEN_1_BYTE;
  qspi_cmd_struct.address_code = 0;
  qspi_cmd_struct.address_length = QSPI_CMD_ADRLEN_3_BYTE;
  qspi_cmd_struct.data_counter = 2;
  qspi_cmd_struct.second_dummy_cycle_num = 0;
  qspi_cmd_struct.operation_mode = QSPI_OPERATE_MODE_444;
  qspi_cmd_struct.read_status_config = QSPI_RSTSC_HW_AUTO;
  qspi_cmd_struct.read_status_enable = FALSE;
  qspi_cmd_struct.write_data_enable = FALSE;
	qspi_cmd_operation_kick(QSPI1,&qspi_cmd_struct);
	while(qspi_flag_get(QSPI1, QSPI_RXFIFORDY_FLAG) == RESET);
	
	temp=qspi_half_word_read(QSPI1);
	
	while(qspi_flag_get(QSPI1, QSPI_CMDSTS_FLAG) == RESET);
	qspi_flag_clear(QSPI1, QSPI_CMDSTS_FLAG);
	
	ID=(temp<<8)|(temp>>8);
	
	return ID;
}

//写使能
void W25Qxx_QSPI_writeEN(void)
{
	qspi_cmd_type qspi_cmd_struct;
	
	qspi_cmd_struct.pe_mode_enable = FALSE;
	qspi_cmd_struct.pe_mode_operate_code = 0;
	qspi_cmd_struct.instruction_code = W25X_WriteEnable;
	qspi_cmd_struct.instruction_length = QSPI_CMD_INSLEN_1_BYTE;
	qspi_cmd_struct.address_code = 0;
	qspi_cmd_struct.address_length = QSPI_CMD_ADRLEN_0_BYTE;
	qspi_cmd_struct.data_counter = 0;
	qspi_cmd_struct.second_dummy_cycle_num = 0;
	qspi_cmd_struct.operation_mode = QSPI_OPERATE_MODE_444;
	qspi_cmd_struct.read_status_config = QSPI_RSTSC_HW_AUTO;
	qspi_cmd_struct.read_status_enable = FALSE;
	qspi_cmd_struct.write_data_enable = TRUE;
	qspi_cmd_operation_kick(QSPI1,&qspi_cmd_struct);
	while(qspi_flag_get(QSPI1, QSPI_CMDSTS_FLAG) == RESET);
	qspi_flag_clear(QSPI1, QSPI_CMDSTS_FLAG);
}

//设置4字节地址模式
void W25Qxx_QSPI_4ByteAdd(void)
{
	qspi_cmd_type qspi_cmd_struct;
	
	qspi_cmd_struct.pe_mode_enable = FALSE;
	qspi_cmd_struct.pe_mode_operate_code = 0;
	qspi_cmd_struct.instruction_code = W25X_Enable4ByteAddr;
	qspi_cmd_struct.instruction_length = QSPI_CMD_INSLEN_1_BYTE;
	qspi_cmd_struct.address_code = 0;
	qspi_cmd_struct.address_length = QSPI_CMD_ADRLEN_0_BYTE;
	qspi_cmd_struct.data_counter = 0;
	qspi_cmd_struct.second_dummy_cycle_num = 0;
	qspi_cmd_struct.operation_mode = QSPI_OPERATE_MODE_444;
	qspi_cmd_struct.read_status_config = QSPI_RSTSC_HW_AUTO;
	qspi_cmd_struct.read_status_enable = FALSE;
	qspi_cmd_struct.write_data_enable = TRUE;
	qspi_cmd_operation_kick(QSPI1,&qspi_cmd_struct);
	while(qspi_flag_get(QSPI1, QSPI_CMDSTS_FLAG) == RESET);
	qspi_flag_clear(QSPI1, QSPI_CMDSTS_FLAG);
}


//忙检查
void W25Qxx_QSPI_Busy(void)
{
	u8 state=0;
	qspi_cmd_type qspi_cmd_struct;
	do
	{
		qspi_cmd_struct.pe_mode_enable = FALSE;
		qspi_cmd_struct.pe_mode_operate_code = 0;
		qspi_cmd_struct.instruction_code = W25X_ReadStatusReg1;
		qspi_cmd_struct.instruction_length = QSPI_CMD_INSLEN_1_BYTE;
		qspi_cmd_struct.address_code = 0;
		qspi_cmd_struct.address_length = QSPI_CMD_ADRLEN_0_BYTE;
		qspi_cmd_struct.data_counter = 1;
		qspi_cmd_struct.second_dummy_cycle_num = 0;
		qspi_cmd_struct.operation_mode = QSPI_OPERATE_MODE_444;
		qspi_cmd_struct.read_status_config = QSPI_RSTSC_HW_AUTO;
		qspi_cmd_struct.read_status_enable = FALSE;
		qspi_cmd_struct.write_data_enable = FALSE;
		qspi_cmd_operation_kick(QSPI1,&qspi_cmd_struct);
		while(qspi_flag_get(QSPI1, QSPI_RXFIFORDY_FLAG) == RESET);
		state=qspi_byte_read(QSPI1);
		while(qspi_flag_get(QSPI1, QSPI_CMDSTS_FLAG) == RESET);
		qspi_flag_clear(QSPI1, QSPI_CMDSTS_FLAG);
	}while(state&0x01);
}


//设置读最大时钟104MHz
void W25Qxx_QSPI_MaxSCK(void)
{
	u8 data=0;
	qspi_cmd_type qspi_cmd_struct;
	
	W25Qxx_QSPI_writeEN();
	W25Qxx_QSPI_Busy();
	
	data=3<<4;
	qspi_cmd_struct.pe_mode_enable = FALSE;
	qspi_cmd_struct.pe_mode_operate_code = 0;
	qspi_cmd_struct.instruction_code = W25X_SetReadParam;
	qspi_cmd_struct.instruction_length = QSPI_CMD_INSLEN_1_BYTE;
	qspi_cmd_struct.address_code = 0;
	qspi_cmd_struct.address_length = QSPI_CMD_ADRLEN_0_BYTE;
	qspi_cmd_struct.data_counter = 1;
	qspi_cmd_struct.second_dummy_cycle_num = 0;
	qspi_cmd_struct.operation_mode = QSPI_OPERATE_MODE_444;
	qspi_cmd_struct.read_status_config = QSPI_RSTSC_HW_AUTO;
	qspi_cmd_struct.read_status_enable = FALSE;
	qspi_cmd_struct.write_data_enable = TRUE;
	qspi_cmd_operation_kick(QSPI1,&qspi_cmd_struct);

	while(qspi_flag_get(QSPI1, QSPI_TXFIFORDY_FLAG) == RESET);
	qspi_byte_write(QSPI1, data);

	while(qspi_flag_get(QSPI1, QSPI_CMDSTS_FLAG) == RESET);
	qspi_flag_clear(QSPI1, QSPI_CMDSTS_FLAG);
}

//整片擦除
void W25Qxx_QSPI_EraseChip(void)
{
	qspi_cmd_type qspi_cmd_struct;
	
	W25Qxx_QSPI_writeEN();
	W25Qxx_QSPI_Busy();
	
	qspi_cmd_struct.pe_mode_enable = FALSE;
	qspi_cmd_struct.pe_mode_operate_code = 0;
	qspi_cmd_struct.instruction_code = W25X_ChipErase;
	qspi_cmd_struct.instruction_length = QSPI_CMD_INSLEN_1_BYTE;
	qspi_cmd_struct.address_code = 0;
	qspi_cmd_struct.address_length = QSPI_CMD_ADRLEN_0_BYTE;
	qspi_cmd_struct.data_counter = 0;
	qspi_cmd_struct.second_dummy_cycle_num = 0;
	qspi_cmd_struct.operation_mode = QSPI_OPERATE_MODE_444;
	qspi_cmd_struct.read_status_config = QSPI_RSTSC_HW_AUTO;
	qspi_cmd_struct.read_status_enable = FALSE;
	qspi_cmd_struct.write_data_enable = TRUE;
	qspi_cmd_operation_kick(QSPI1,&qspi_cmd_struct);
	while(qspi_flag_get(QSPI1, QSPI_CMDSTS_FLAG) == RESET);
	qspi_flag_clear(QSPI1, QSPI_CMDSTS_FLAG);
	
	W25Qxx_QSPI_Busy();
}


//扇区擦除
void W25Qxx_QSPI_EraseSector(u32 Addr)
{
	qspi_cmd_type qspi_cmd_struct;
	
	W25Qxx_QSPI_writeEN();
	
	qspi_cmd_struct.pe_mode_enable = FALSE;
	qspi_cmd_struct.pe_mode_operate_code = 0;
	qspi_cmd_struct.instruction_code = W25X_SectorErase;
	qspi_cmd_struct.instruction_length = QSPI_CMD_INSLEN_1_BYTE;
	qspi_cmd_struct.address_code = Addr;
	qspi_cmd_struct.address_length = QSPI_CMD_ADRLEN_4_BYTE;
	qspi_cmd_struct.data_counter = 0;
	qspi_cmd_struct.second_dummy_cycle_num = 0;
	qspi_cmd_struct.operation_mode = QSPI_OPERATE_MODE_444;
	qspi_cmd_struct.read_status_config = QSPI_RSTSC_HW_AUTO;
	qspi_cmd_struct.read_status_enable = FALSE;
	qspi_cmd_struct.write_data_enable = TRUE;
	qspi_cmd_operation_kick(QSPI1,&qspi_cmd_struct);
	while(qspi_flag_get(QSPI1, QSPI_CMDSTS_FLAG) == RESET);
	qspi_flag_clear(QSPI1, QSPI_CMDSTS_FLAG);
	
	W25Qxx_QSPI_Busy();
}

//读取数据
void W25Qxx_QSPI_Read(u8* pbuff,u32 Addr,u16 rlen)
{
	u16 len=0,i=0;
	qspi_cmd_type qspi_cmd_struct;
	
	qspi_cmd_struct.pe_mode_enable = FALSE;
  qspi_cmd_struct.pe_mode_operate_code = 0;
  qspi_cmd_struct.instruction_code = W25X_FastReadData;
  qspi_cmd_struct.instruction_length = QSPI_CMD_INSLEN_1_BYTE;
  qspi_cmd_struct.address_code = Addr;
  qspi_cmd_struct.address_length = QSPI_CMD_ADRLEN_4_BYTE;
  qspi_cmd_struct.data_counter = rlen;
  qspi_cmd_struct.second_dummy_cycle_num = 8;
  qspi_cmd_struct.operation_mode = QSPI_OPERATE_MODE_444;
  qspi_cmd_struct.read_status_config = QSPI_RSTSC_HW_AUTO;
  qspi_cmd_struct.read_status_enable = FALSE;
  qspi_cmd_struct.write_data_enable = FALSE;
	qspi_cmd_operation_kick(QSPI1,&qspi_cmd_struct);
	
	do
	{
		if(rlen>=128) //FIFO最大128字节
			len=128;
		else
			len=rlen;
		
		while(qspi_flag_get(QSPI1, QSPI_RXFIFORDY_FLAG) == RESET);
		for(i = 0; i < len; i++)
    {
      *pbuff++ = qspi_byte_read(QSPI1);
    }
		rlen-=len;
	}while(rlen);
	
	while(qspi_flag_get(QSPI1, QSPI_CMDSTS_FLAG) == RESET){}
  qspi_flag_clear(QSPI1, QSPI_CMDSTS_FLAG);
}


// 页写入 最大256字节
void W25Qxx_QSPI_Page(u8* pbuff,u32 Addr,u16 wlen)
{
	u16 len=0,i=0;
	qspi_cmd_type qspi_cmd_struct;
	
	do
  {
		W25Qxx_QSPI_writeEN();
		W25Qxx_QSPI_Busy();
		
		qspi_cmd_struct.pe_mode_enable = FALSE;
		qspi_cmd_struct.pe_mode_operate_code = 0;
		qspi_cmd_struct.instruction_code = W25X_PageProgram;
		qspi_cmd_struct.instruction_length = QSPI_CMD_INSLEN_1_BYTE;
		qspi_cmd_struct.address_code = Addr;
		qspi_cmd_struct.address_length = QSPI_CMD_ADRLEN_4_BYTE;
		qspi_cmd_struct.data_counter = wlen;
		qspi_cmd_struct.second_dummy_cycle_num = 0;
		qspi_cmd_struct.operation_mode = QSPI_OPERATE_MODE_444;
		qspi_cmd_struct.read_status_config = QSPI_RSTSC_HW_AUTO;
		qspi_cmd_struct.read_status_enable = FALSE;
		qspi_cmd_struct.write_data_enable = TRUE;
		qspi_cmd_operation_kick(QSPI1,&qspi_cmd_struct);
		
		if(wlen>=128)
			len=128;
		else
			len=wlen;
		
    for(i = 0; i < len; i++)
    {
      while(qspi_flag_get(QSPI1, QSPI_TXFIFORDY_FLAG) == RESET);
      qspi_byte_write(QSPI1, *pbuff++);
    }
    wlen -= len;
    Addr += len;
		
    while(qspi_flag_get(QSPI1, QSPI_CMDSTS_FLAG) == RESET);
    qspi_flag_clear(QSPI1, QSPI_CMDSTS_FLAG);
		
    W25Qxx_QSPI_Busy();
  }while(wlen);
}

//不检查擦除写入
void W25Qx_QSPI_wNoCheck(u8* pBuffer,u32 WriteAddr,u16 wlen)   
{ 			 		 
	u16 pageremain;	   
	pageremain=256-WriteAddr%256; //单页剩余的字节数		 	    
	if(wlen<=pageremain)pageremain=wlen;//不大于256个字节
	while(1)
	{	   
		W25Qxx_QSPI_Page(pBuffer,WriteAddr,pageremain);
		
		if(wlen==pageremain)
			break;//写入结束了
	 	else //NumByteToWrite>pageremain
		{
			pBuffer+=pageremain;
			WriteAddr+=pageremain;
			
			wlen-=pageremain;			  //减去已经写入了的字节数
			
			if(wlen>256)
				pageremain=256; 							//一次可以写入256个字节
			else 
				pageremain=wlen; 	  //不够256个字节了
		}
	}   
} 


//数据写入函数
u8 W25QXX_BUFFER[4096];		 
void W25Qxx_QSPI_Write(u8* pBuffer,u32 WriteAddr,u16 wlen)   
{ 
	u32 secpos;
	u16 secoff;
	u16 secremain;	   
 	u16 i;    
	u8 * W25QXX_BUF;	  
   	W25QXX_BUF=W25QXX_BUFFER;	     
 	secpos=WriteAddr/4096;//扇区地址  
	secoff=WriteAddr%4096;//在扇区内的偏移
	secremain=4096-secoff;//扇区剩余空间大小   
 	//printf("ad:%X,nb:%X\r\n",WriteAddr,NumByteToWrite);//测试用
 	if(wlen<=secremain)secremain=wlen;//不大于4096个字节
	while(1) 
	{	
		W25Qxx_QSPI_Read(W25QXX_BUF,secpos*4096,4096);//读出整个扇区的内容
		for(i=0;i<secremain;i++)//校验数据
		{
			if(W25QXX_BUF[secoff+i]!=0XFF)break;//需要擦除  	  
		}
		if(i<secremain)//需要擦除
		{
			W25Qxx_QSPI_EraseSector(secpos);//擦除这个扇区
			for(i=0;i<secremain;i++)	   //复制
			{
				W25QXX_BUF[i+secoff]=pBuffer[i];	  
			}
			W25Qx_QSPI_wNoCheck(W25QXX_BUF,secpos*4096,4096);//写入整个扇区  

		}
		else 
			W25Qx_QSPI_wNoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间. 				   
		if(wlen==secremain)
			break;//写入结束了
		else//写入未结束
		{
			secpos++;//扇区地址增1
			secoff=0;//偏移位置为0 	 

		  pBuffer+=secremain;  		  //指针偏移
			WriteAddr+=secremain;		  //写地址偏移	   
		  wlen-=secremain;//字节数递减
			
			if(wlen>4096)
				secremain=4096;					  //下一个扇区还是写不完
			else 
				secremain=wlen;	//下一个扇区可以写完了
		}	 
	};	 
}

三main.c文件

#include "system.h"
#include "at32f435_437_clock.h"
#include "CH340N.h"
#include "Time_APP.h"
#include "QSPIFlash.h"


u8 buf1[]="10086111";
u8 buf2[20]={0};

int main(void)
{
	u8 state1=0,state2=0,state3=0;
	
	system_clock_config();
	
	SYS_clock_Init();
	
	SYS_nvic_config();
	
	User_Time_Init();
	
	CH340N_Init(115200);
	
	
	W25Qxx_QSPI_Init();
	
	CH340_Printf("AT32 MCU init\r\n");
	CH340_Printf("Flash ID:%04X\r\n",W25Qxx_QSPI_readID());
	
	CH340_Printf("Erase done\r\n");
	
	W25Qxx_QSPI_EraseSector(0);
	W25Qxx_QSPI_Page(buf1,0,strlen((char *)buf1));
	W25Qxx_QSPI_Read(buf2,0,strlen((char *)buf1));
	
	CH340_Printf("read:%s\r\n",buf2);
	

	while(1)
	{
	
	}
}

到这里文章已完结,后续测试一下读写的速度。以上内容如有侵权请联系我删除。
个人微信:LLQuser 注明来意。也可技术交流

转载本文章请注明出处

好的,我会尽力为您解答。首先,让我们来了解一下凑硬币问题。 在凑硬币问题中,我们需要找到最少的硬币数来凑成给定的金额。例如,如果我们需要凑成 11 元,而有 1 元、2 元、5 元三种不同面值的硬币可供使用,那么最少需要使用 3 枚硬币,即 5 元、5 元和 1 元。 下面是一种使用动态规划解决凑硬币问题的方法: 1. 创建一个长度为 amount+1 的数组 dp,其中 dp[i] 表示凑成面值为 i 元所需的最少硬币数。 2. 初始化 dp[0] 为 0,其余元素初始化为一个较大的数(例如 amount+1),表示当前面值无法被凑成。 3. 遍历硬币数组 coins,对于每个硬币,遍历 dp 数组,更新 dp[j] 的值为 dp[j-coins[i]]+1,其中 j>=coins[i],表示当前面值可以被凑成。 4. 最终 dp[amount] 就是凑成 amount 元所需的最少硬币数,如果 dp[amount]>amount,则表示无法凑成给定金额。 下面是使用 JavaScript 实现该算法的代码: ```javascript function coinChange(coins, amount) { const dp = new Array(amount + 1).fill(amount + 1); dp[0] = 0; for (let i = 0; i < coins.length; i++) { for (let j = coins[i]; j <= amount; j++) { dp[j] = Math.min(dp[j], dp[j - coins[i]] + 1); } } return dp[amount] > amount ? -1 : dp[amount]; } ``` 这个函数接受两个参数,一个是硬币数组 coins,另一个是需要凑成的金额 amount。最终返回凑成 amount 元所需的最少硬币数,如果无法凑成则返回 -1。 例如,若 coins = [1, 2, 5],amount = 11,则 coinChange(coins, amount) 的返回值为 3,表示凑成 11 元最少需要 3 枚硬币。 希望这个简单的解释和代码能够帮到您,如果您还有其他问题,请随时问我。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值