1071 views|2 replies

270

Posts

0

Resources
The OP
 

[Ateli AT-START-F437 Development Board Review] 07 Using SPI and QSPI to read W25Q128 [Copy link]

 This post was last edited by 怀垂少年梦 on 2024-4-9 23:25

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:

AT32F437-FLASH - QSPI.zip (54.66 MB, downloads: 18)

This post is from Domestic Chip Exchange

Latest reply

I support domestically produced chips and hope that the localization rate of domestically produced chips will reach 70% by 2025.   Details Published on 2024-4-10 12:53
 
 

25

Posts

0

Resources
2
 

I support domestically produced chips and hope that the localization rate of domestically produced chips will reach 70% by 2025.

This post is from Domestic Chip Exchange

Comments

Now that I can use domestically produced components in my products, I will use them.  Details Published on 2024-4-10 14:20
 
 
 

270

Posts

0

Resources
3
 
humancat01 posted on 2024-4-10 12:53 I support domestically produced chips and hope that the localization rate of domestically produced chips will reach 70% by 2025

Now that I can use domestically produced components in my products, I will use them.

This post is from Domestic Chip Exchange
 
 
 

Just looking around
Find a datasheet?

EEWorld Datasheet Technical Support

Featured Posts
Packaging terminology analysis (from "PCB Terminology Manual" V1.0)

Packaging terminology analysis (from "PCB Terminology Manual" V1.0) 1. BGA (ball grid array) ...

Principle of multifunctional motor drive controller based on LKS32MC061C6T8

Share with youA multi-function controller that can be used for electric bicycles, scooters, tricycles, etc., for your ...

[Modelsim FAQ] Error loading design

458789 Cause of the problem The prompt message indicates that there is no Verilog simulation license, indicating that t ...

[TI star product limited time purchase] + AWR683ISK run demo

After the hardware is connected correctly and the development software is installed, you can download and debug the demo ...

[ESP32-Audio-Kit Audio Development Board Review] 2. Download Audio Firmware

This week I downloaded the FLASH software from the official website. I also downloaded the firmware for the audio develo ...

How to use the assembly language delay subroutine to realize the MCU P2 port to light up 8 LED lights in a loop

Please explain it briefly

OTL circuit analysis problems

As shown in the figure, the OTL circuit Why can resistors R5, R6 and diode D1 ensure that transistors V2 and V3 are in ...

39 "Wanli" Raspberry Pi car - ROS learning (ROS project file system structure introduction and import external software packages...

This post was last edited by lb8820265 on 2022-11-1 16:03 The fastest way to learn software is to start with routine ...

Award: Survey | Tektronix Innovation Lab is fully upgraded to solve all the pain points of power testing!

Award Ceremony: Survey | Tektronix Innovation Lab has been fully upgraded to solve all the pain points of power testing! ...

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号
快速回复 返回顶部 Return list