本文使用的是雅特力 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# |
如下图所示:
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 |
上面三个状态寄存器,我们只关心我们需要用到的一些位:ADS、QE 和 BUSY 位。其他位
的说明,请看 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 注明来意。也可技术交流文章来源:https://www.toymoban.com/news/detail-663198.html
转载本文章请注明出处文章来源地址https://www.toymoban.com/news/detail-663198.html
到了这里,关于教程:AT32F435 QSPI 读写W25Q256的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!