AT32F435教程:使用QSPI读写W25Q256存储器

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

博客原文链接

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

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

单片机引脚 引脚定义 W25Q256引脚
35 QSPI_IO0 5脚DI
32 QSPI_IO1 2脚DO
33 QSPI_IO2 3脚WP
34 QSPI_IO3 7脚HOLD
36 QSPI_SCK 6脚CLK
47 QSPI_CS 1脚CS#

如下图所示:
W25Q256引脚图MCU引脚图
AT32F435的QSPI简介:
官方文档参考:
链接: AN0088_AT32_MCU_QSPI_Application_Note_ZH_V2.0.3

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

命令类型 命令 说明
W25X_WriteEnable 0x06 写使能
W25X_ReadStatusReg1 0x05 读状态寄存器1
W25X_ReadStatusReg2 0x35 读状态寄存器2
W25X_ReadStatusReg3 0x15 读状态寄存器3
W25X_WriteStatusReg2 0x31 写状态寄存器2
W25X_ManufactDeviceID 0x90 读手册ID
W25X_Enable4ByteAddr 0xB7 使能4字节地址模式
W25X_ChipErase 0xC7 全片擦除
W25X_SectorErase 0x20 扇区擦除
W25X_EnterQPIMode 0x38 使能QSPI模式
W25X_SetReadParam 0xC0 设置读速度
W25X_FastReadData 0x0B 快速读取数据
W25X_PageProgram 0x02 页编程

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

状态寄存器3 S23 S22 S21 S20 S19 S18 S17 S16
位说明 HODL/RST DRV1 DRV0 WPS ADP ADS
状态寄存器2 S15 S14 S13 S12 S11 S10 S9 S8
位说明 SUS CMP LB3 LB2 LB1 QE SRP1
状态寄存器1 S7 S6 S5 S4 S3 S2 S1 S0
位说明 SRP0 TB BP3 BP2 BP1 BP0 BUSY

上面三个状态寄存器,我们只关心我们需要用到的一些位: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 注明来意。也可技术交流

转载本文章请注明出处

物联沃分享整理
物联沃-IOTWORD物联网 » AT32F435教程:使用QSPI读写W25Q256存储器

发表评论