FLASH memory is a common embedded device for storing parameters, such as Winbond's W25QXX series. This evaluation mainly uses SPI to read and write W25Q128 FLASH experiments.
1. Use SPI to read and write
1. AT32 SPI peripheral features
From the above, we can know that the maximum clock frequency is 72M;
2. Use pins:
PA4---CS;
PA5---CLK
PA6---MISO
PA7---SMOKE
Use SPI1 pin;
3. Programming
.h files
#ifndef __W25QXX_H
#define __W25QXX_H
#include "at32f435_437.h"
//W25X系列/Q系列芯片列表
//W25Q80 ID 0XEF13
//W25Q16 ID 0XEF14
//W25Q32 ID 0XEF15
//W25Q64 ID 0XEF16
//W25Q128 ID 0XEF17 总共65536页; 每页256字节;
#define W25Q80 0XEF13
#define W25Q16 0XEF14
#define W25Q32 0XEF15
#define W25Q64 0XEF16
#define W25Q128 0XEF17
#define W25QXX_CS_L() gpio_bits_reset(GPIOA, GPIO_PINS_4)
#define W25QXX_CS_H() gpio_bits_set(GPIOA, GPIO_PINS_4)
extern uint16_t W25QXX_TYPE;
extern uint32_t W25QXX_SIZE;
extern uint8_t W25QXX_UID[8];
#define W25X_WriteEnable 0x06
#define W25X_WriteDisable 0x04
#define W25X_ReadStatusReg 0x05
#define W25X_WriteStatusReg 0x01
#define W25X_ReadData 0x03
#define W25X_FastReadData 0x0B
#define W25X_FastReadDual 0x3B
#define W25X_PageProgram 0x02
#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
int W25QXX_Init(void);
void W25QXX_ReadUniqueID(uint8_t UID[8]);
uint16_t W25QXX_ReadID(void);
uint8_t W25QXX_ReadSR(void);
void W25QXX_Write_SR(uint8_t sr);
void W25QXX_Write_Enable(void);
void W25QXX_Write_Disable(void);
void W25QXX_Write_NoCheck(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite);
void W25QXX_Read(uint8_t* pBuffer,uint32_t ReadAddr,uint16_t NumByteToRead);
void W25QXX_Write(uint8_t* pBuffer,uint32_t WriteAddr,uint16_t NumByteToWrite);
void W25QXX_Erase_Chip(void);
void W25QXX_Erase_Sector(uint32_t Dst_Addr);
void W25QXX_Wait_Busy(void);
void W25QXX_PowerDown(void);
void W25QXX_WAKEUP(void);
uint32_t W25QXX_ReadCapacity(void);
#endif
.c files
/**
* [url=home.php?mod=space&uid=1307177]@File[/url] w25qxx.c
*
* @brief
*
*/
#include "w25qxx.h"
#include <stdio.h>
#include "at32f435_437_board.h"
uint16_t W25QXX_TYPE = 0;
uint32_t W25QXX_SIZE = 0;
uint8_t W25QXX_UID[8];
void SPI_Init(void) {
gpio_init_type gpio_initstructure;
spi_init_type spi_init_struct;
crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE);
/* software cs, pd0 as a general io to control flash cs */
gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_initstructure.gpio_pull = GPIO_PULL_UP;
gpio_initstructure.gpio_mode = GPIO_MODE_OUTPUT;
gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_initstructure.gpio_pins = GPIO_PINS_4;
gpio_init(GPIOA, &gpio_initstructure);
W25QXX_CS_H();
/* sck */
gpio_initstructure.gpio_pull = GPIO_PULL_UP;
gpio_initstructure.gpio_mode = GPIO_MODE_MUX;
gpio_initstructure.gpio_pins = GPIO_PINS_5;
gpio_init(GPIOA, &gpio_initstructure);
gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE5, GPIO_MUX_5);
/* miso */
gpio_initstructure.gpio_pull = GPIO_PULL_UP;
gpio_initstructure.gpio_pins = GPIO_PINS_6;
gpio_init(GPIOA, &gpio_initstructure);
gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE6, GPIO_MUX_5);
/* mosi */
gpio_initstructure.gpio_pull = GPIO_PULL_UP;
gpio_initstructure.gpio_pins = GPIO_PINS_7;
gpio_init(GPIOA, &gpio_initstructure);
gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE7, GPIO_MUX_5);
crm_periph_clock_enable(CRM_SPI1_PERIPH_CLOCK, TRUE);
spi_default_para_init(&spi_init_struct);
spi_init_struct.transmission_mode = SPI_TRANSMIT_FULL_DUPLEX;
spi_init_struct.master_slave_mode = SPI_MODE_MASTER;
spi_init_struct.mclk_freq_division = SPI_MCLK_DIV_32;
spi_init_struct.first_bit_transmission = SPI_FIRST_BIT_MSB;
spi_init_struct.frame_bit_num = SPI_FRAME_8BIT;
spi_init_struct.clock_polarity = SPI_CLOCK_POLARITY_HIGH;
spi_init_struct.clock_phase = SPI_CLOCK_PHASE_2EDGE;
spi_init_struct.cs_mode_selection = SPI_CS_SOFTWARE_MODE;
spi_init(SPI1, &spi_init_struct);
spi_enable(SPI1, TRUE);
}
//SPI读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
static uint8_t W25QXX_SPI_ReadWriteByte(uint8_t TxData)
{
uint8_t RxData = 0X00;
while(spi_i2s_flag_get(SPI1, SPI_I2S_TDBE_FLAG) == RESET);
spi_i2s_data_transmit(SPI1, TxData);
while(spi_i2s_flag_get(SPI1, SPI_I2S_RDBF_FLAG) == RESET);
RxData = spi_i2s_data_receive(SPI1);
return RxData;
}
//4Kbytes为一个Sector
//16个扇区为1个Block
//W25Q128
//容量为16M字节,共有128个Block,4096个Sector
//初始化SPI FLASH的IO口
int W25QXX_Init(void)
{
SPI_Init();
W25QXX_CS_L(); /* 拉低选中 */
W25QXX_SPI_ReadWriteByte(0XFF);
W25QXX_CS_H(); /* 拉高取消 */
W25QXX_TYPE = W25QXX_ReadID(); // 读取FLASH ID.
printf("W25QXX_Init: W25QXX_TYPE = %x\r\n",W25QXX_TYPE);
W25QXX_SIZE = W25QXX_ReadCapacity(); // 读取容量
printf("W25QXX_Init: W25QXX_TYPE = %d\r\n",W25QXX_SIZE);
W25QXX_ReadUniqueID(W25QXX_UID); // 读取唯一ID
// if((W25QXX_TYPE & 0XEF00) != 0XEF00)
// {
// return -1;
// }
return 0;
}
//读取W25QXX的状态寄存器
//BIT7 6 5 4 3 2 1 0
//SPR RV TB BP2 BP1 BP0 WEL BUSY
//SPR:默认0,状态寄存器保护位,配合WP使用
//TB,BP2,BP1,BP0:FLASH区域写保护设置
//WEL:写使能锁定
//BUSY:忙标记位(1,忙;0,空闲)
//默认:0x00
uint8_t W25QXX_ReadSR(void)
{
uint8_t byte = 0;
W25QXX_CS_L(); //使能器件
W25QXX_SPI_ReadWriteByte(W25X_ReadStatusReg); //发送读取状态寄存器命令
byte = W25QXX_SPI_ReadWriteByte(0Xff); //读取一个字节
W25QXX_CS_H(); //取消片选
return byte;
}
//写W25QXX状态寄存器
//只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以写!!!
void W25QXX_Write_SR(uint8_t sr)
{
W25QXX_CS_L(); //使能器件
W25QXX_SPI_ReadWriteByte(W25X_WriteStatusReg); //发送写取状态寄存器命令
W25QXX_SPI_ReadWriteByte(sr); //写入一个字节
W25QXX_CS_H(); //取消片选
}
//W25QXX写使能
//将WEL置位
void W25QXX_Write_Enable(void)
{
W25QXX_CS_L(); //使能器件
W25QXX_SPI_ReadWriteByte(W25X_WriteEnable); //发送写使能
W25QXX_CS_H(); //取消片选
}
//W25QXX写禁止
//将WEL清零
void W25QXX_Write_Disable(void)
{
W25QXX_CS_L(); //使能器件
W25QXX_SPI_ReadWriteByte(W25X_WriteDisable); //发送写禁止指令
W25QXX_CS_H(); //取消片选
}
//读取芯片ID
//返回值如下:
//0XEF13,表示芯片型号为W25Q80
//0XEF14,表示芯片型号为W25Q16
//0XEF15,表示芯片型号为W25Q32
//0XEF16,表示芯片型号为W25Q64
//0XEF17,表示芯片型号为W25Q128
uint16_t W25QXX_ReadID(void)
{
uint16_t Temp = 0;
W25QXX_CS_L();
W25QXX_SPI_ReadWriteByte(0x90); //发送读取ID命令
W25QXX_SPI_ReadWriteByte(0x00);
W25QXX_SPI_ReadWriteByte(0x00);
W25QXX_SPI_ReadWriteByte(0x00);
Temp |= W25QXX_SPI_ReadWriteByte(0xFF) << 8;
Temp |= W25QXX_SPI_ReadWriteByte(0xFF);
W25QXX_CS_H();
return Temp;
}
uint32_t W25QXX_ReadCapacity(void)
{
int i = 0;
uint8_t arr[4] = {0,0,0,0};
W25QXX_CS_L();
W25QXX_SPI_ReadWriteByte(0x5A);
W25QXX_SPI_ReadWriteByte(0x00);
W25QXX_SPI_ReadWriteByte(0x00);
W25QXX_SPI_ReadWriteByte(0x84);
W25QXX_SPI_ReadWriteByte(0x00);
for(i = 0; i < sizeof(arr); i++)
{
arr[i] = W25QXX_SPI_ReadWriteByte(0xFF);
}
W25QXX_CS_H();
return ((((*(uint32_t *)arr)) + 1) >> 3);
}
void W25QXX_ReadUniqueID(uint8_t UID[8])
{
int i = 0;
W25QXX_CS_L();
W25QXX_SPI_ReadWriteByte(0x4B);
W25QXX_SPI_ReadWriteByte(0x00);
W25QXX_SPI_ReadWriteByte(0x00);
W25QXX_SPI_ReadWriteByte(0x00);
W25QXX_SPI_ReadWriteByte(0x00);
for(i = 0; i < 8; i++)
{
UID[i] = W25QXX_SPI_ReadWriteByte(0xFF);
}
W25QXX_CS_H();
}
//读取SPI FLASH
//在指定地址开始读取指定长度的数据
//pBuffer:数据存储区
//ReadAddr:开始读取的地址(24bit)
//NumByteToRead:要读取的字节数(最大65535)
void W25QXX_Read(uint8_t *pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead)
{
uint16_t i;
W25QXX_CS_L(); //使能器件
W25QXX_SPI_ReadWriteByte(W25X_ReadData); //发送读取命令
W25QXX_SPI_ReadWriteByte((uint8_t)((ReadAddr) >> 16)); //发送24bit地址
W25QXX_SPI_ReadWriteByte((uint8_t)((ReadAddr) >> 8));
W25QXX_SPI_ReadWriteByte((uint8_t)ReadAddr);
for (i = 0; i < NumByteToRead; i++)
{
pBuffer[i] = W25QXX_SPI_ReadWriteByte(0XFF); //循环读数
}
W25QXX_CS_H();
}
//SPI在一页(0~65535)内写入少于256个字节的数据
//在指定地址开始写入最大256字节的数据
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大256),该数不应该超过该页的剩余字节数!!!
void W25QXX_Write_Page(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
uint16_t i;
W25QXX_Write_Enable(); //SET WEL
W25QXX_CS_L(); //使能器件
W25QXX_SPI_ReadWriteByte(W25X_PageProgram); //发送写页命令
W25QXX_SPI_ReadWriteByte((uint8_t)((WriteAddr) >> 16)); //发送24bit地址
W25QXX_SPI_ReadWriteByte((uint8_t)((WriteAddr) >> 8));
W25QXX_SPI_ReadWriteByte((uint8_t)WriteAddr);
for (i = 0; i < NumByteToWrite; i++)
W25QXX_SPI_ReadWriteByte(pBuffer[i]); //循环写数
W25QXX_CS_H(); //取消片选
W25QXX_Wait_Busy(); //等待写入结束
}
//无检验写SPI FLASH
//必须确保所写的地址范围内的数据全部为0XFF,否则在非0XFF处写入的数据将失败!
//具有自动换页功能
//在指定地址开始写入指定长度的数据,但是要确保地址不越界!
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大65535)
//CHECK OK
void W25QXX_Write_NoCheck(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
uint16_t pageremain;
pageremain = 256 - WriteAddr % 256; //单页剩余的字节数
if (NumByteToWrite <= pageremain)
pageremain = NumByteToWrite; //不大于256个字节
while (1)
{
W25QXX_Write_Page(pBuffer, WriteAddr, pageremain);
if (NumByteToWrite == pageremain)
break; //写入结束了
else //NumByteToWrite>pageremain
{
pBuffer += pageremain;
WriteAddr += pageremain;
NumByteToWrite -= pageremain; //减去已经写入了的字节数
if (NumByteToWrite > 256)
pageremain = 256; //一次可以写入256个字节
else
pageremain = NumByteToWrite; //不够256个字节了
}
};
}
//写SPI FLASH
//在指定地址开始写入指定长度的数据
//该函数带擦除操作!
//pBuffer:数据存储区
//WriteAddr:开始写入的地址(24bit)
//NumByteToWrite:要写入的字节数(最大65535)
uint8_t W25QXX_BUFFER[4096];
void W25QXX_Write(uint8_t *pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite)
{
uint32_t secpos;
uint16_t secoff;
uint16_t secremain;
uint16_t i;
uint8_t *W25QXX_BUF;
W25QXX_BUF = W25QXX_BUFFER;
secpos = WriteAddr / 4096; //扇区地址
secoff = WriteAddr % 4096; //在扇区内的偏移
secremain = 4096 - secoff; //扇区剩余空间大小
if (NumByteToWrite <= secremain)
secremain = NumByteToWrite; //不大于4096个字节
while (1)
{
W25QXX_Read(W25QXX_BUF, secpos * 4096, 4096); //读出整个扇区的内容
for (i = 0; i < secremain; i++) //校验数据
{
if (W25QXX_BUF[secoff + i] != 0XFF)
break; //需要擦除
}
if (i < secremain) //需要擦除
{
W25QXX_Erase_Sector(secpos); //擦除这个扇区
for (i = 0; i < secremain; i++) //复制
{
W25QXX_BUF[i + secoff] = pBuffer[i];
}
W25QXX_Write_NoCheck(W25QXX_BUF, secpos * 4096, 4096); //写入整个扇区
}
else
W25QXX_Write_NoCheck(pBuffer, WriteAddr, secremain); //写已经擦除了的,直接写入扇区剩余区间.
if (NumByteToWrite == secremain)
break; //写入结束了
else //写入未结束
{
secpos++; //扇区地址增1
secoff = 0; //偏移位置为0
pBuffer += secremain; //指针偏移
WriteAddr += secremain; //写地址偏移
NumByteToWrite -= secremain; //字节数递减
if (NumByteToWrite > 4096)
secremain = 4096; //下一个扇区还是写不完
else
secremain = NumByteToWrite; //下一个扇区可以写完了
}
};
}
//擦除整个芯片
//等待时间超长...
void W25QXX_Erase_Chip(void)
{
W25QXX_Write_Enable(); //SET WEL
W25QXX_Wait_Busy();
W25QXX_CS_L(); //使能器件
W25QXX_SPI_ReadWriteByte(W25X_ChipErase); //发送片擦除命令
W25QXX_CS_H(); //取消片选
W25QXX_Wait_Busy(); //等待芯片擦除结束
}
//擦除一个扇区
//Dst_Addr:扇区地址 根据实际容量设置
//擦除一个山区的最少时间:150ms
void W25QXX_Erase_Sector(uint32_t Dst_Addr)
{
//监视falsh擦除情况,测试用
Dst_Addr *= 4096;
W25QXX_Write_Enable(); //SET WEL
W25QXX_Wait_Busy();
W25QXX_CS_L(); //使能器件
W25QXX_SPI_ReadWriteByte(W25X_SectorErase); //发送扇区擦除指令
W25QXX_SPI_ReadWriteByte((uint8_t)((Dst_Addr) >> 16)); //发送24bit地址
W25QXX_SPI_ReadWriteByte((uint8_t)((Dst_Addr) >> 8));
W25QXX_SPI_ReadWriteByte((uint8_t)Dst_Addr);
W25QXX_CS_H(); //取消片选
W25QXX_Wait_Busy(); //等待擦除完成
}
//等待空闲
void W25QXX_Wait_Busy(void)
{
while ((W25QXX_ReadSR() & 0x01) == 0x01); // 等待BUSY位清空
}
//进入掉电模式
void W25QXX_PowerDown(void)
{
W25QXX_CS_L(); //使能器件
W25QXX_SPI_ReadWriteByte(W25X_PowerDown); //发送掉电命令
W25QXX_CS_H(); //取消片选
delay_us(3); //等待TPD
}
//唤醒
void W25QXX_WAKEUP(void)
{
W25QXX_CS_L(); //使能器件
W25QXX_SPI_ReadWriteByte(W25X_ReleasePowerDown); // send W25X_PowerDown command 0xAB
W25QXX_CS_H(); //取消片选
delay_us(3); //等待TRES1
}
4. Read and write FLASH ID and size test
at32_led_toggle(LED2);
delay_ms(1000);
printf("SIZE:%d ID:%x \r\n",W25QXX_ReadCapacity(),W25QXX_ReadID());
5. Read and write data test
if (g_key_flag == 1) {
g_key_flag = 0;
W25QXX_Read(g_readdataBuf,0x200,64);
for (i = 0; i < 64;i++) {
printf("%d ",g_readdataBuf[i]);
}
printf("WRITE TEST\r\n");
sprintf((char *)g_writedataBuf,"AT32F437ZMT7 Spi Flash read/write demo");
len = strlen((char *)g_writedataBuf);
W25QXX_Write(g_writedataBuf,0x200,len);
printf("WRITE ok\r\n");
printf("READ TEST\r\n");
W25QXX_Read(g_readdataBuf,0x200,len);
for (i = 0; i < len;i++) {
printf("%c",g_readdataBuf[i]);
}
} else;
2. QSPI read and write FLASH
1. QSPI Features
2. QSPI protocol description
3. Use pins:
PA6---IO0;
PA7---IO1;
PA15---IO2;
PA1---IO3;
PD3---SCK;
PB10---CS;
Use QSPI1 pin;
4. Programming
Mainly refer to the official EN25Q128 routine
.c files
#include "at32f435_437_board.h"
/** @addtogroup AT32F437_periph_examples
* @{
*/
/** @addtogroup 437_QSPI_command_port_using_polling
* @{
*/
#define QSPI_FIFO_DEPTH (32*4)
#define FLASH_PAGE_PROGRAM_SIZE 256
qspi_cmd_type w25q128_cmd_config;
void qspi_busy_check(void);
void qspi_write_enable(void);
/**
* [url=home.php?mod=space&uid=159083]@brief[/url] QSPI GPIO init
* @param none
* @retval none
*/
void QSPI_Init(void) {
gpio_init_type gpio_initstructure;
/* enable the qspi clock */
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_GPIOD_PERIPH_CLOCK, TRUE);
/* QSPI1_IO0 */
gpio_initstructure.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
gpio_initstructure.gpio_out_type = GPIO_OUTPUT_PUSH_PULL;
gpio_initstructure.gpio_mode = GPIO_MODE_MUX;
gpio_initstructure.gpio_pins = GPIO_PINS_6;
gpio_initstructure.gpio_pull = GPIO_PULL_NONE;
gpio_init(GPIOA, &gpio_initstructure);
gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE6, GPIO_MUX_10);
/* QSPI1_IO1 */
gpio_initstructure.gpio_pins = GPIO_PINS_7;
gpio_init(GPIOA, &gpio_initstructure);
gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE7, GPIO_MUX_10);
/* QSPI1_IO2*/
gpio_initstructure.gpio_pins = GPIO_PINS_15;
gpio_init(GPIOA, &gpio_initstructure);
gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE15, GPIO_MUX_10);
/* QSPI1_IO3*/
gpio_initstructure.gpio_pins = GPIO_PINS_1;
gpio_init(GPIOA, &gpio_initstructure);
gpio_pin_mux_config(GPIOA, GPIO_PINS_SOURCE1, GPIO_MUX_9);
/* QSPI1_SCK*/
gpio_initstructure.gpio_pins = GPIO_PINS_3;
gpio_init(GPIOD, &gpio_initstructure);
gpio_pin_mux_config(GPIOD, GPIO_PINS_SOURCE3, GPIO_MUX_9);
/* QSPI1_CS*/
gpio_initstructure.gpio_pins = GPIO_PINS_10;
gpio_init(GPIOB, &gpio_initstructure);
gpio_pin_mux_config(GPIOB, GPIO_PINS_SOURCE10, GPIO_MUX_9);
}
/**
* @brief w25q128 cmd read unique_id
* @param qspi_cmd_struct: the pointer for qspi_cmd_type parameter
* @param addr: read start address
* @param counter: read data counter
* @retval none
*/
void w25q128_cmd_read_unique_id(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 = 0x4B; //快速读指令
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 = 8;
qspi_cmd_struct->second_dummy_cycle_num = 4;
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;
}
/**
* @brief w25q128 cmd read unique_id
* @param qspi_cmd_struct: the pointer for qspi_cmd_type parameter
* @param addr: read start address
* @param counter: read data counter
* @retval none
*/
void w25q128_cmd_read_jedec_id(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 = 0x9F; //读指令
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 = 4;
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;
}
/**
* @brief w25q128 cmd read config
* @param qspi_cmd_struct: the pointer for qspi_cmd_type parameter
* @param addr: read start address
* @param counter: read data counter
* @retval none
*/
void w25q128_cmd_read_config(qspi_cmd_type *qspi_cmd_struct, uint32_t addr, uint32_t counter)
{
qspi_cmd_struct->pe_mode_enable = FALSE;
qspi_cmd_struct->pe_mode_operate_code = 0xff;
qspi_cmd_struct->instruction_code = 0xEB; //快速读指令
qspi_cmd_struct->instruction_length = QSPI_CMD_INSLEN_1_BYTE;
qspi_cmd_struct->address_code = addr ;
qspi_cmd_struct->address_length = QSPI_CMD_ADRLEN_3_BYTE;//地址长度
qspi_cmd_struct->data_counter = counter;
qspi_cmd_struct->second_dummy_cycle_num = 2;
qspi_cmd_struct->operation_mode = QSPI_OPERATE_MODE_144;//快速读指令;4个IO参与输入输出
qspi_cmd_struct->read_status_config = QSPI_RSTSC_HW_AUTO;
qspi_cmd_struct->read_status_enable = FALSE;
qspi_cmd_struct->write_data_enable = FALSE;
}
/**
* @brief w25q128 cmd write config
* @param qspi_cmd_struct: the pointer for qspi_cmd_type parameter
* @param addr: write start address
* @param counter: write data counter
* @retval none
*/
void w25q128_cmd_write_config(qspi_cmd_type *qspi_cmd_struct, uint32_t addr, uint32_t counter)
{
qspi_cmd_struct->pe_mode_enable = FALSE;
qspi_cmd_struct->pe_mode_operate_code = 0;
qspi_cmd_struct->instruction_code = 0x32; //Quad Page Program instruction;写一页的数据 256字节
qspi_cmd_struct->instruction_length = QSPI_CMD_INSLEN_1_BYTE;
qspi_cmd_struct->address_code = addr;
qspi_cmd_struct->address_length = QSPI_CMD_ADRLEN_3_BYTE;
qspi_cmd_struct->data_counter = counter;
qspi_cmd_struct->second_dummy_cycle_num = 0;
qspi_cmd_struct->operation_mode = QSPI_OPERATE_MODE_114; //写页;1条指令;1条数据线输出;4条数据线输入
qspi_cmd_struct->read_status_config = QSPI_RSTSC_HW_AUTO;
qspi_cmd_struct->read_status_enable = FALSE;
qspi_cmd_struct->write_data_enable = TRUE;
}
/**
* @brief w25q128 cmd erase config
* @param qspi_cmd_struct: the pointer for qspi_cmd_type parameter
* @param addr: erase address
* @retval none
*/
void w25q128_cmd_erase_config(qspi_cmd_type *qspi_cmd_struct, uint32_t addr)
{
qspi_cmd_struct->pe_mode_enable = FALSE;
qspi_cmd_struct->pe_mode_operate_code = 0;
qspi_cmd_struct->instruction_code = 0x20; //扇区擦除指令
qspi_cmd_struct->instruction_length = QSPI_CMD_INSLEN_1_BYTE;
qspi_cmd_struct->address_code = addr;
qspi_cmd_struct->address_length = QSPI_CMD_ADRLEN_3_BYTE; //地址长度为3个字节
qspi_cmd_struct->data_counter = 0;
qspi_cmd_struct->second_dummy_cycle_num = 0;
qspi_cmd_struct->operation_mode = QSPI_OPERATE_MODE_111; //SPI MODE;1条数据线输入;1条数据线输出
qspi_cmd_struct->read_status_config = QSPI_RSTSC_HW_AUTO;
qspi_cmd_struct->read_status_enable = FALSE;
qspi_cmd_struct->write_data_enable = TRUE;
}
/**
* @brief w25q128 cmd wren config
* @param qspi_cmd_struct: the pointer for qspi_cmd_type parameter
* @retval none
*/
void w25q128_cmd_wren_config(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 = 0x06; //写使能指令
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;
}
/**
* @brief w25q128 cmd rdsr config
* @param qspi_cmd_struct: the pointer for qspi_cmd_type parameter;
* @retval none
*/
void w25q128_cmd_rdsr_config(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 = 0x05;
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 = TRUE;
qspi_cmd_struct->write_data_enable = FALSE;
}
/**
* @brief w25q128 cmd rdsr config
* @param qspi_cmd_struct: the pointer for qspi_cmd_type parameter;cmd 表示读取状态寄存器第几个
* @retval none
*/
void w25q128_cmd_read_sta_reg(qspi_cmd_type *qspi_cmd_struct,uint8_t cmd) //读状态寄存器
{
qspi_cmd_struct->pe_mode_enable = FALSE;
qspi_cmd_struct->pe_mode_operate_code = 0;
qspi_cmd_struct->instruction_code = cmd;
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;
}
/**
* @brief w25q128 cmd wdsr config //读取状态寄存器
* @param qspi_cmd_struct: the pointer for qspi_cmd_type parameter;cmd 表示读取状态寄存器第几个
* @retval none
*/
void w25q128_cmd_write_sta_reg(qspi_cmd_type *qspi_cmd_struct,uint8_t cmd) //读状态寄存器
{
qspi_cmd_struct->pe_mode_enable = FALSE;
qspi_cmd_struct->pe_mode_operate_code = 0;
qspi_cmd_struct->instruction_code = cmd;
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;
}
/**
* @brief w25q128 cmd wdsr config //读取状态寄存器
* @param qspi_cmd_struct: the pointer for qspi_cmd_type parameter;cmd 表示读取状态寄存器第几个
* @retval none
*/
uint8_t w25q128_cmd_read_write_sta_data(uint8_t rw, uint8_t cmd, uint8_t dta) //读状态寄存器
{
uint8_t data = 0;
if (rw) {
w25q128_cmd_read_sta_reg(&w25q128_cmd_config,cmd);
qspi_cmd_operation_kick(QSPI1, &w25q128_cmd_config);
while(qspi_flag_get(QSPI1, QSPI_RXFIFORDY_FLAG) == RESET);
data = qspi_byte_read(QSPI1);
/* wait command completed */
while(qspi_flag_get(QSPI1, QSPI_CMDSTS_FLAG) == RESET);
qspi_flag_clear(QSPI1, QSPI_CMDSTS_FLAG);
return data;
} else {
qspi_write_enable();
w25q128_cmd_write_sta_reg(&w25q128_cmd_config,cmd);
qspi_cmd_operation_kick(QSPI1, &w25q128_cmd_config);
while(qspi_flag_get(QSPI1, QSPI_TXFIFORDY_FLAG) == RESET);
qspi_byte_write(QSPI1, dta);
while(qspi_flag_get(QSPI1, QSPI_CMDSTS_FLAG) == RESET);
qspi_flag_clear(QSPI1, QSPI_CMDSTS_FLAG);
return 0;
}
}
/**
* @brief qspi read unique_id
* @param addr: the address for read
* @param total_len: the length for read
* @param buf: the pointer for read data
* @retval none
*/
void qspi_data_read_unique_id(uint8_t* buf)
{
uint32_t i, len = 0;
uint32_t total_len = 8;
w25q128_cmd_read_unique_id(&w25q128_cmd_config);
qspi_cmd_operation_kick(QSPI1, &w25q128_cmd_config);
/* read data */
len = total_len;
while(qspi_flag_get(QSPI1, QSPI_RXFIFORDY_FLAG) == RESET)
{
}
for(i = 0; i < len; ++i)
{
*buf++ = qspi_byte_read(QSPI1);
}
/* wait command completed */
while(qspi_flag_get(QSPI1, QSPI_CMDSTS_FLAG) == RESET)
{
}
qspi_flag_clear(QSPI1, QSPI_CMDSTS_FLAG);
}
/**
* @brief qspi read unique_id
* @param addr: the address for read
* @param total_len: the length for read
* @param buf: the pointer for read data
* @retval none
*/
void qspi_data_read_jedec_id(uint8_t* buf)
{
uint32_t i, len = 0;
uint32_t total_len = 4;
w25q128_cmd_read_jedec_id(&w25q128_cmd_config);
qspi_cmd_operation_kick(QSPI1, &w25q128_cmd_config);
/* read data */
len = total_len;
while(qspi_flag_get(QSPI1, QSPI_RXFIFORDY_FLAG) == RESET)
{
}
for(i = 0; i < len; ++i)
{
*buf++ = qspi_byte_read(QSPI1);
}
/* wait command completed */
while(qspi_flag_get(QSPI1, QSPI_CMDSTS_FLAG) == RESET)
{
}
qspi_flag_clear(QSPI1, QSPI_CMDSTS_FLAG);
}
/**
* @brief qspi read data
* @param addr: the address for read
* @param total_len: the length for read
* @param buf: the pointer for read data
* @retval none
*/
void qspi_data_read(uint32_t addr, uint32_t total_len, uint8_t* buf)
{
uint32_t i, len = total_len;
w25q128_cmd_read_config(&w25q128_cmd_config, addr, total_len);
qspi_cmd_operation_kick(QSPI1, &w25q128_cmd_config);
/* read data */
do
{
if(total_len >= QSPI_FIFO_DEPTH)
{
len = QSPI_FIFO_DEPTH;
}
else
{
len = total_len;
}
while(qspi_flag_get(QSPI1, QSPI_RXFIFORDY_FLAG) == RESET)
{
}
for(i = 0; i < len; ++i)
{
*buf++ = qspi_byte_read(QSPI1);
}
total_len -= len;
}while(total_len);
/* wait command completed */
while(qspi_flag_get(QSPI1, QSPI_CMDSTS_FLAG) == RESET)
{
}
qspi_flag_clear(QSPI1, QSPI_CMDSTS_FLAG);
}
/**
* @brief qspi write data
* @param addr: the address for write
* @param total_len: the length for write
* @param buf: the pointer for write data
* @retval none
*/
void qspi_data_write(uint32_t addr, uint32_t total_len, uint8_t* buf)
{
uint32_t i, len;
uint8_t temp = 0;
do
{
temp = w25q128_cmd_read_write_sta_data(1,READ_STATUS_REG2,0);
// printf("0x%.2x\r\n",temp);//读状态2寄存器
w25q128_cmd_read_write_sta_data(0,WRITE_STATUS_REG2,0x02 | temp);//写状态2寄存器
// printf("0x%.2x\r\n",w25q128_cmd_read_write_sta_data(1,READ_STATUS_REG2,0));//读状态2寄存器
qspi_write_enable();
/* send up to 256 bytes at one time, and only one page */
len = (addr / FLASH_PAGE_PROGRAM_SIZE + 1) * FLASH_PAGE_PROGRAM_SIZE - addr;
if(total_len < len)
len = total_len;
w25q128_cmd_write_config(&w25q128_cmd_config, addr, len);
qspi_cmd_operation_kick(QSPI1, &w25q128_cmd_config);
for(i = 0; i < len; ++i)
{
while(qspi_flag_get(QSPI1, QSPI_TXFIFORDY_FLAG) == RESET)
{
}
qspi_byte_write(QSPI1, *buf++);
}
total_len -= len;
addr += len;
/* wait command completed */
while(qspi_flag_get(QSPI1, QSPI_CMDSTS_FLAG) == RESET)
{
}
qspi_flag_clear(QSPI1, QSPI_CMDSTS_FLAG);
qspi_busy_check();
}while(total_len > 0);
}
/**
* @brief qspi erase data
* @param sec_addr: the sector address for erase
* @retval none
*/
void qspi_erase(uint32_t sec_addr)
{
qspi_write_enable();
w25q128_cmd_erase_config(&w25q128_cmd_config, sec_addr);
qspi_cmd_operation_kick(QSPI1, &w25q128_cmd_config);
/* wait command completed */
while(qspi_flag_get(QSPI1, QSPI_CMDSTS_FLAG) == RESET)
{
}
qspi_flag_clear(QSPI1, QSPI_CMDSTS_FLAG);
qspi_busy_check();
}
/**
* @brief qspi check busy
* @param none
* @retval none
*/
void qspi_busy_check(void)
{
w25q128_cmd_rdsr_config(&w25q128_cmd_config);
qspi_cmd_operation_kick(QSPI1, &w25q128_cmd_config);
/* wait command completed */
while(qspi_flag_get(QSPI1, QSPI_CMDSTS_FLAG) == RESET)
{
}
qspi_flag_clear(QSPI1, QSPI_CMDSTS_FLAG);
}
/**
* @brief qspi write enable
* @param none
* @retval none
*/
void qspi_write_enable(void)
{
w25q128_cmd_wren_config(&w25q128_cmd_config);
qspi_cmd_operation_kick(QSPI1, &w25q128_cmd_config);
/* wait command completed */
while(qspi_flag_get(QSPI1, QSPI_CMDSTS_FLAG) == RESET)
{
}
qspi_flag_clear(QSPI1, QSPI_CMDSTS_FLAG);
}
/**
* @}
*/
/**
* @}
*/
main.c code
/**
* @brief main function.
* @param none
* @retval none
*/
int main(void)
{
uint16_t i = 0;
uint16_t err = 0;
system_clock_config();
at32_board_init();
button_exint_init();
QSPI_Init();
/* switch to cmd port */
qspi_xip_enable(QSPI1, FALSE);
/* set sclk */
qspi_clk_division_set(QSPI1, QSPI_CLK_DIV_4);
/* set sck idle mode 0 */
qspi_sck_mode_set(QSPI1, QSPI_SCK_MODE_3);
/* set wip in bit 0 */
qspi_busy_config(QSPI1, QSPI_BUSY_OFFSET_0);
/* erase */
qspi_erase(0);
while(1)
{
at32_led_toggle(LED2);
delay_ms(1000);
if (g_key_flag == 1) {
g_key_flag = 0;
/* read */
/* 读取Unique ID*/
// qspi_data_read_unique_id(rbuf);
// for (i = 0; i < 8;i++) {
// printf("0x%x \r\n",rbuf[i]);
// }
// /* 读取设备ID和制造商ID*/
// qspi_data_read_jedec_id(rbuf);
// for (i = 0; i < 4;i++) {
// printf("0x%x \r\n",rbuf[i]);
// }
qspi_data_read(0, TEST_SIZE, rbuf);
for(i = 0; i < TEST_SIZE; i++)
{
if(rbuf[i] != 0xFF)
{
err = 1;
break;
} else {
err = 0;
}
}
// printf("err %d\r\n",err);
/* program */
sprintf((char *)g_writedataBuf,"Thanks EEworld;Qspi Flash read/write demo on AT32F437ZMT7 ");
memcpy(wbuf,g_writedataBuf,strlen((char *)g_writedataBuf));
qspi_data_write(0, TEST_SIZE, wbuf);
/* read */
qspi_data_read(0, TEST_SIZE, rbuf);
for (i = 2; i < TEST_SIZE;i++) {
printf("%c",rbuf[i]);
}
} else;
}
}
5. Verification
1) Read and write Unique ID, device ID and manufacturer ID
2) Read and write data
Project Files: