赞
踩
本文使用的是雅特力 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 注明来意。也可技术交流
转载本文章请注明出处
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。