STM32使用QSPI控制FLASH(FLASH芯片型号:MX25L25645G)

这篇具有很好参考价值的文章主要介绍了STM32使用QSPI控制FLASH(FLASH芯片型号:MX25L25645G)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

         本篇详细的记录了如何使用STM32CubeMX配置STM32H723ZGT6的QSPI外设与 SPI Flash 通信(MX25L25645G)。

一、硬件准备

1、MX25L25645G引脚示意如图:

mx25u256 qspi,QSPI,MCU,FLASH,stm32,mcu

手册上的各个引脚的定义如图:

mx25u256 qspi,QSPI,MCU,FLASH,stm32,mcu

        由以上可知,该芯片可以使用SPI/QSPI控制,SPI模式使用SO、SI,SCLK,以及CS角,此状态下WP角被作为写保护,低电平为有效电平,RESET角被作为硬件复位,也是低电平有效。

        而QSPI模式除了CS片选与SCLK时钟角一致,还需要四个引脚IO作为高速传输的通道,分别是SIO0、SIO1、SIO2、SIO3。对于QSPI的解释我另有一篇文章,或者说网上另有不少更加详细的资料,大家可自行查阅。

        下图是该芯片的内存划分,手册中介绍,芯片被划分成了0 - 511共计512块,每块64K。mx25u256 qspi,QSPI,MCU,FLASH,stm32,mcu

MX256足足有256M的空间,如果想全部使用,则需要配置参数,手册中有提到:

mx25u256 qspi,QSPI,MCU,FLASH,stm32,mcu

最直白的办法是进入四地址模式,关于这个MX256刚好有寄存器可以配置。

mx25u256 qspi,QSPI,MCU,FLASH,stm32,mcu

具体如何去做,下面会有详细描写。

最后要注意的就是模式配置,是使用SPI模式还是QSPI模式由下图可见:

mx25u256 qspi,QSPI,MCU,FLASH,stm32,mcu

        想进入QSPI模式需要向芯片写入指令0x35,而且只能使用单线写入,如图七,仅仅使用了SIO0,本人最开始就是使用四线输入,最后一直没写进去导致卡了不少时间。

        关于其他指令,下面讲驱动的时候我会提到一些,这里就不粘贴手册上的表格了,实在是太多了,但是我们实际用上的不过十几个。

二,QSPI配置

        本人使用CUBEX配置QSPI,相对自己搭建工程效率更高一些,在选723择好对应的32型号后开始配置QSPI参数,注意stm32H723上面没有直接的QSPI,只有OSPI(QSPI的升级版,具有8个IO),但是可以将其配置成QSPI模式;

mx25u256 qspi,QSPI,MCU,FLASH,stm32,mcu

        个人根据自己所使用的的引脚去选择,如下本人使用的时钟、片选,以及数据收发所使用的引脚。

  1. mode选择Quad SPI(四线SPI),MX25用作外部flash存储程序并执行;其余选项还未用
  2. Clock根据所用STM32支持几个SPI flash,若多个还需根据硬件设计选择对应引脚的Port,后面参数就选择对应Port就行
  3. Data[3:0]是八线OSPI的低四线,引脚选择一定要按照硬件设计选择

mx25u256 qspi,QSPI,MCU,FLASH,stm32,mcu

下图则是QSPI的一些配置;

        以下参数需根据所用flash的datasheet设置:

  1. Device Size填写值不用减一,即大小2M,则填21(2的21次方为flash大小)
  2. Chip Select High Time,一般是3
  3. Clock Mode 时钟开始的相位(是高还是低)根据需要去选择
  4. Clock Prescaler 分频值,即flash工作时钟频率 = 外设所在总线的频率 / 分频值,要根据器件设置

mx25u256 qspi,QSPI,MCU,FLASH,stm32,mcu

三、驱动编写

       这是相关指令的头文件,大家大致可以从变量名以及后面的英文注释分析出该指令的实际功能,比如FLASH_CMD_QPIID,很明显是QSPI模式下读取芯片ID。

#ifndef __MX25_H__
#define __MX25_H__
#include "main.h"
#define    FLASH_CMD_ENQSPI    0x35    //(QSPI mode)

#define    FLASH_CMD_RDID      0x9F    //RDID (Read Identification)
#define    FLASH_CMD_RES       0xAB    //RES (Read Electronic ID)
#define    FLASH_CMD_REMS      0x90    //REMS (Read Electronic & Device ID)
#define    FLASH_CMD_QPIID     0xAF    //QPIID (QPI ID Read)

//Register comands
#define    FLASH_CMD_WRSR      0x01    //WRSR (Write Status Register)
#define    FLASH_CMD_RDSR      0x05    //RDSR (Read Status Register)
#define    FLASH_CMD_WRSCUR    0x2F    //WRSCUR (Write Security Register)
#define    FLASH_CMD_RDSCUR    0x2B    //RDSCUR (Read Security Register)
#define    FLASH_CMD_RDCR      0x15    //RDCR (Read Configuration Register)
#define    FLASH_CMD_RDEAR     0xC8    //RDEAR (Read Extended Address Register)
#define    FLASH_CMD_WREAR     0xC5    //WREAR (Write Extended Address Register)
#define    FLASH_CMD_WRLR      0x2C    //WRLR (write lock Register)
#define    FLASH_CMD_RDLR      0x2D    //RDLR (read lock Register)
#define    FLASH_CMD_RDSPB     0xE2    //RDSPB (read SPB status)
#define    FLASH_CMD_WRSPB     0xE3    //WRSPB (write SPB bit)
#define    FLASH_CMD_ESSPB     0xE4    //ESSPB (erase all SPB status)
#define    FLASH_CMD_RDDPB     0xE0    //RDDPB (read DPB register)
#define    FLASH_CMD_WRDPB     0xE1    //WRDPB (write DPB register)

//READ comands
#define    FLASH_CMD_READ        0x03    //READ (1 x I/O)
#define    FLASH_CMD_2READ       0xBB    //2READ (2 x I/O)
#define    FLASH_CMD_4READ       0xEB    //4READ (4 x I/O)
#define    FLASH_CMD_FASTREAD    0x0B    //FAST READ (Fast read data).
#define    FLASH_CMD_DREAD       0x3B    //DREAD (1In/2 Out fast read)
#define    FLASH_CMD_QREAD       0x6B    //QREAD (1In/4 Out fast read)
#define    FLASH_CMD_4DTRD       0xED    //4DTRD (Quad DT read)
#define    FLASH_CMD_RDSFDP      0x5A    //RDSFDP (Read SFDP)
#define    FLASH_CMD_READ4B      0x13    //READ4B (1 x I/O with 4 byte address)
#define    FLASH_CMD_FASTREAD4B  0x0C    //FASTREAD4B (1 x I/O with 4 byte address)
#define    FLASH_CMD_2READ4B     0xBC    //2READ4B (2 x I/O with 4 byte address)
#define    FLASH_CMD_4READ4B     0xEC    //4READ4B (4 x I/O with 4 byte address)
#define    FLASH_CMD_DREAD4B     0x3C    //DREAD4B (1In/2 Out fast read with 4 byte addr)
#define    FLASH_CMD_QREAD4B     0x6C    //QREAD4B (1In/4 Out fast read with 4 byte addr)
#define    FLASH_CMD_4DTRD4B     0xEE    //4DTRD4B (Quad DT read with 4 byte address)

//Program comands
#define    FLASH_CMD_WREN     0x06    //WREN (Write Enable)
#define    FLASH_CMD_WRDI     0x04    //WRDI (Write Disable)
#define    FLASH_CMD_PP       0x02    //PP (page program)
#define    FLASH_CMD_4PP      0x38    //4PP (Quad page program)
#define    FLASH_CMD_PP4B     0x12    //PP4B (page program with 4 byte address)
#define    FLASH_CMD_4PP4B    0x3E    //4PP4B (Quad page program with 4 byte address)

//Erase comands
#define    FLASH_CMD_SE       0x20    //SE (Sector Erase)
#define    FLASH_CMD_BE32K    0x52    //BE32K (Block Erase 32kb)
#define    FLASH_CMD_BE       0xD8    //BE (Block Erase)
#define    FLASH_CMD_BE4B     0xDC    //BE4B (Block Erase with 4 byte address)
#define    FLASH_CMD_CE       0x60    //CE (Chip Erase) hex code: 60 or C7
#define    FLASH_CMD_SE4B     0x21    //SE (Sector Erase with 4 byte addr)
#define    FLASH_CMD_BE32K4B  0x5C    //BE32K4B (Block Erase 32kb with 4 byte addr)

//Mode setting comands
#define    FLASH_CMD_FMEN     0x41    //FMEN (Factory Mode Enable)
#define    FLASH_CMD_DP       0xB9    //DP (Deep Power Down)
#define    FLASH_CMD_RDP      0xAB    //RDP (Release form Deep Power Down)
#define    FLASH_CMD_ENSO     0xB1    //ENSO (Enter Secured OTP)
#define    FLASH_CMD_EXSO     0xC1    //EXSO  (Exit Secured OTP)
#define    FLASH_CMD_EQIO     0x35    //EQIO (Enable Quad I/O)
#define    FLASH_CMD_WPSEL    0x68    //WPSEL (Enable block protect mode)
#ifdef SBL_CMD_0x77
#define    FLASH_CMD_SBL      0x77    //SBL (Set Burst Length), new: 0x77
#else
#define    FLASH_CMD_SBL      0xC0    //SBL (Set Burst Length), old: 0xC0
#endif
#define    FLASH_CMD_EN4B     0xB7    //EN4B( Enter 4-byte Mode )
#define    FLASH_CMD_EX4B     0xE9    //EX4B( Exit 4-byte Mode )

//Reset comands
#define    FLASH_CMD_RSTEN     0x66    //RSTEN (Reset Enable)
#define    FLASH_CMD_RST       0x99    //RST (Reset Memory)
#define    FLASH_CMD_RSTQIO    0xF5    //RSTQIO (Reset Quad I/O)

//Security comands
#define    FLASH_CMD_GBLK       0x7E    //GBLK (Gang Block Lock)
#define    FLASH_CMD_GBULK      0x98    //GBULK (Gang Block Unlock)

//Suspend/Resume comands
#ifdef PGM_ERS_0xB0
#define    FLASH_CMD_PGM_ERS_S    0xB0    //PGM/ERS Suspend (Suspends Program/Erase) old: 0xB0
#define    FLASH_CMD_PGM_ERS_R    0x30    //PGM/ERS Erase (Resumes Program/Erase) old: 0x30
#else
#define    FLASH_CMD_PGM_ERS_S    0x75    //PGM/ERS Suspend (Suspends Program/Erase) old: 0xB0
#define    FLASH_CMD_PGM_ERS_R    0x7A    //PGM/ERS Erase (Resumes Program/Erase) old: 0x30
#endif

#define    FLASH_CMD_NOP          0x00    //NOP (No Operation)




// Flash control register mask define
// status register
#define    FLASH_WIP_MASK         0x01
#define    FLASH_LDSO_MASK        0x02
#define    FLASH_QE_MASK          0x40
// security register
#define    FLASH_OTPLOCK_MASK     0x03
#define    FLASH_4BYTE_MASK       0x04
#define    FLASH_WPSEL_MASK       0x80
// configuration reigster
#define    FLASH_DC_MASK          0x80
#define    FLASH_CR_4BYTE_MASK    0x20
#define    FLASH_DC_2BIT_MASK     0xC0
#define    FLASH_DC_3BIT_MASK     0x07
// other
#define    BLOCK_PROTECT_MASK     0xff
#define    BLOCK_LOCK_MASK        0x01


/* OSPI Error codes */
#define OSPI_OK            ((uint8_t)0x00)
#define OSPI_ERROR         ((uint8_t)0x01)
#define OSPI_BUSY          ((uint8_t)0x02)
#define OSPI_NOT_SUPPORTED ((uint8_t)0x04)
#define OSPI_SUSPENDED     ((uint8_t)0x08)



#define MX25_BULK_ERASE_MAX_TIME         250000
#define MX25_SECTOR_ERASE_MAX_TIME       3000
#define MX25_SUBSECTOR_ERASE_MAX_TIME    800


#define MX25_FSR_BUSY                    ((uint8_t)0x01)    /*!< busy */
#define MX25_FSR_WREN                    ((uint8_t)0x02)    /*!< write enable */
#define MX25_FSR_QE                      ((uint8_t)0x02)    /*!< quad enable */
#define MX25_FSR_4ByteAddrMode           ((uint8_t)0x20)    /*!< 4bytes add */

下面进入正题,开始编写读写擦擦除等等驱动接口:

         首先是使用指令0x35,使能QSPI模式;

void QSPI_FLASH_EnQspi(void)
{
	OSPI_RegularCmdTypeDef sCommand;
    sCommand.OperationType        = HAL_OSPI_OPTYPE_COMMON_CFG;
    sCommand.FlashId              = HAL_OSPI_FLASH_ID_1;
    sCommand.Instruction          = FLASH_CMD_ENQSPI;
    sCommand.InstructionMode      = HAL_OSPI_INSTRUCTION_1_LINE;
    sCommand.InstructionSize      = HAL_OSPI_INSTRUCTION_8_BITS;
    sCommand.InstructionDtrMode   = HAL_OSPI_INSTRUCTION_DTR_DISABLE;
    sCommand.AddressMode          = HAL_OSPI_ADDRESS_NONE;
    sCommand.AlternateBytesMode   = HAL_OSPI_ALTERNATE_BYTES_NONE;
    sCommand.DataMode             = HAL_OSPI_DATA_NONE;
    sCommand.DummyCycles          = 0;
    sCommand.DQSMode              = HAL_OSPI_DQS_DISABLE;
    sCommand.SIOOMode             = HAL_OSPI_SIOO_INST_EVERY_CMD;
    /*Enter QUAD mode*/
    if (HAL_OSPI_Command(&hospi1, &sCommand, HAL_OSPI_TIMEOUT_DEFAULT_VALUE)!= HAL_OK) 
	{
		printf("Enable Qspi mode wrong ....\r\n");
        Error_Handler();
	}
    
}

        读取QSPI的芯片ID,注意看,现在使用4Line是,也就是四4IO模式,最后会收到三个字节的ID号(每一台EEPROM是一样的ID号);

uint32_t QSPI_FLASH_ReadID(void)
{
	OSPI_RegularCmdTypeDef s_command;
	uint32_t Temp = 0;
	uint8_t pData[3];
	/* JEDEC ID */

	s_command.OperationType        = HAL_OSPI_OPTYPE_COMMON_CFG;
    s_command.FlashId              = HAL_OSPI_FLASH_ID_1;
    s_command.InstructionMode      = HAL_OSPI_INSTRUCTION_4_LINES;
	s_command.Instruction          = FLASH_CMD_QPIID;
	s_command.AddressMode          = HAL_OSPI_ADDRESS_NONE;
	s_command.AlternateBytesMode   = HAL_OSPI_ALTERNATE_BYTES_NONE;
	s_command.DataMode 			   = HAL_OSPI_DATA_4_LINES;
	s_command.DummyCycles          = 0; 
	s_command.NbData               = 3;
	s_command.DataDtrMode          = HAL_OSPI_DATA_DTR_DISABLE;
	s_command.SIOOMode             = HAL_OSPI_SIOO_INST_EVERY_CMD;
    s_command.InstructionSize      = HAL_OSPI_INSTRUCTION_8_BITS;
    s_command.InstructionDtrMode   = HAL_OSPI_INSTRUCTION_DTR_DISABLE;
    s_command.AddressDtrMode       = HAL_OSPI_ADDRESS_DTR_DISABLE;
    s_command.DQSMode              = HAL_OSPI_DQS_DISABLE;

	

	if (HAL_OSPI_Command(&hospi1, &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
	{
		printf("Read ID Command wrong ....\r\n");
		Error_Handler();
	}
	if (HAL_OSPI_Receive(&hospi1, pData, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
	{		
		printf("Receive wrong ....\r\n");
		Error_Handler();
	}
	Temp = ( pData[2] | pData[1]<<8 )| ( pData[0]<<16 );
	//printf("Temp = 0x%x\r\n",Temp);
	return Temp;
}

        查询QSPI Flash当前工作状态,主要是应用与发出某些指令之后,查询该指令是否已经生效或者已经执行成功;

static uint8_t MX25_OSPI_AutoPollingMemReady(uint32_t Timeout)
{
	OSPI_RegularCmdTypeDef s_command;
	OSPI_AutoPollingTypeDef s_config;
	
	s_command.OperationType      = HAL_OSPI_OPTYPE_COMMON_CFG;
    s_command.FlashId            = HAL_OSPI_FLASH_ID_1;
    s_command.InstructionMode    = HAL_OSPI_INSTRUCTION_4_LINES;
	s_command.Instruction        = FLASH_CMD_RDSR;
	s_command.AddressMode        = HAL_OSPI_ADDRESS_NONE;
	s_command.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE;
	s_command.DataMode 			 = HAL_OSPI_DATA_4_LINES;
	s_command.DummyCycles        = 0; 
	s_command.DataDtrMode        = HAL_OSPI_DATA_DTR_DISABLE;
	s_command.SIOOMode           = HAL_OSPI_SIOO_INST_EVERY_CMD;
    s_command.InstructionSize    = HAL_OSPI_INSTRUCTION_8_BITS;
    s_command.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_DISABLE;
    s_command.AddressDtrMode     = HAL_OSPI_ADDRESS_DTR_DISABLE;
    s_command.DQSMode            = HAL_OSPI_DQS_DISABLE;
	
	if (HAL_OSPI_Command(&hospi1, &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE)
			!= HAL_OK)
	{
		return OSPI_ERROR;
	}
	
	s_config.Match = 0x00;
	s_config.Mask = MX25_FSR_BUSY;
	s_config.MatchMode = HAL_OSPI_MATCH_MODE_AND;
	s_config.Interval = 0x10;
	s_config.AutomaticStop = HAL_OSPI_AUTOMATIC_STOP_ENABLE;
 
	if (HAL_OSPI_AutoPolling(&hospi1, &s_config, Timeout) != HAL_OK)
	{
		return OSPI_ERROR;
	}
	return OSPI_OK;
}

        四地址模式配置,读取CR寄存器配置,如果已经是4字节模式则直接退出,如果还是三字节模式,则配置四字节模式。将CR寄存器第五位置1,则进入4字节模式。

mx25u256 qspi,QSPI,MCU,FLASH,stm32,mcu

uint8_t MX25_OSPI_Addr_Mode_Init(void)
{
	OSPI_RegularCmdTypeDef s_command;
	uint8_t reg;
	

	s_command.OperationType          = HAL_OSPI_OPTYPE_COMMON_CFG;
    s_command.FlashId                = HAL_OSPI_FLASH_ID_1;
    s_command.InstructionMode        = HAL_OSPI_INSTRUCTION_4_LINES;
	s_command.Instruction            = FLASH_CMD_RDCR;
	s_command.AddressMode            = HAL_OSPI_ADDRESS_NONE;
	s_command.AlternateBytesMode     = HAL_OSPI_ALTERNATE_BYTES_NONE;
	s_command.DataMode 			     = HAL_OSPI_DATA_4_LINES;
	s_command.DummyCycles            = 0; 
	s_command.NbData                 = 1;
	s_command.DataDtrMode            = HAL_OSPI_DATA_DTR_DISABLE;
	s_command.SIOOMode               = HAL_OSPI_SIOO_INST_EVERY_CMD;
    s_command.InstructionSize        = HAL_OSPI_INSTRUCTION_8_BITS;
    s_command.InstructionDtrMode     = HAL_OSPI_INSTRUCTION_DTR_DISABLE;
    s_command.AddressDtrMode         = HAL_OSPI_ADDRESS_DTR_DISABLE;
    s_command.DQSMode                = HAL_OSPI_DQS_DISABLE;
	
 

	if (HAL_OSPI_Command(&hospi1, &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE)!= HAL_OK)
	{
		return OSPI_ERROR;
	}

	if (HAL_OSPI_Receive(&hospi1, &reg, HAL_OSPI_TIMEOUT_DEFAULT_VALUE)!= HAL_OK)
	{
		return OSPI_ERROR;
	}
 
     printf("reg = 0x %x\r\n",reg);
	if (((reg) & (MX25_FSR_4ByteAddrMode)) == 0x20) 
	{
		printf("4ByteAddrMode Now !\r\n");
		return OSPI_OK;
	}
	else   
	{

	    s_command.Instruction = FLASH_CMD_EN4B;
		s_command.DataMode = HAL_OSPI_DATA_NONE;
 

		if (HAL_OSPI_Command(&hospi1, &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE)
				!= HAL_OK)
		{
			return OSPI_ERROR;
		}
 
		if (MX25_OSPI_AutoPollingMemReady(MX25_SUBSECTOR_ERASE_MAX_TIME) != OSPI_OK)
		{
			return OSPI_ERROR;
		}
 
		return OSPI_OK;
	}
}

        写使能 ,在写入数据之前需要进行一个写使能;

uint8_t MX25_OSPI_WriteEnable(void)
{
	OSPI_RegularCmdTypeDef s_command;
	OSPI_AutoPollingTypeDef s_config;
	/* 启用写操作 */
	s_command.OperationType      = HAL_OSPI_OPTYPE_COMMON_CFG;
    s_command.FlashId            = HAL_OSPI_FLASH_ID_1;
    s_command.InstructionMode    = HAL_OSPI_INSTRUCTION_4_LINES;
	s_command.Instruction        = FLASH_CMD_WREN;
	s_command.AddressMode        = HAL_OSPI_ADDRESS_NONE;
	s_command.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE;
	s_command.DataMode 			 = HAL_OSPI_DATA_NONE;
	s_command.DummyCycles        = 0; 
	s_command.DataDtrMode        = HAL_OSPI_DATA_DTR_DISABLE;
	s_command.SIOOMode           = HAL_OSPI_SIOO_INST_EVERY_CMD;
    s_command.InstructionSize    = HAL_OSPI_INSTRUCTION_8_BITS;
    s_command.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_DISABLE;
    s_command.AddressDtrMode     = HAL_OSPI_ADDRESS_DTR_DISABLE;
    s_command.DQSMode            = HAL_OSPI_DQS_DISABLE;

	if (HAL_OSPI_Command(&hospi1, &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE)
			!= HAL_OK)
	{
		return OSPI_ERROR;
	}
 
	s_command.Instruction = FLASH_CMD_RDSR;
	s_command.DataMode = HAL_OSPI_DATA_4_LINES;
	s_command.NbData = 1;
	
	if (HAL_OSPI_Command(&hospi1, &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE)!= HAL_OK)
	{
		return OSPI_ERROR;
	}
	/* 配置自动轮询模式等待写启用 */
	s_config.Match = MX25_FSR_WREN;;
	s_config.Mask = MX25_FSR_WREN;;
	s_config.MatchMode = HAL_OSPI_MATCH_MODE_AND;
	s_config.Interval = 0x10;
	s_config.AutomaticStop = HAL_OSPI_AUTOMATIC_STOP_ENABLE;

	if (HAL_OSPI_AutoPolling(&hospi1, &s_config, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
	{
		return OSPI_ERROR;
	}
	return OSPI_OK;
}

        关于MX25的写入,看起来比较复杂,本人也是复制了一些网上的来修改,因为大多数FLASH的块、页、扇区的划分其实是一样的,每个扇区4096个字节,每页256个字节,一般FLASH都支持页写入,也就是一次最多写入256字节,大于256字节则需要写入下一页。同时大于4096字节数据要写入下一个扇区(没有扇区写入,即使是大量数据写入也是一页一页写的)。在写入前一定要判断该地址是否已经写入过数据,若已有数据则需要擦除之后再写入。

uint8_t MX25_BUFFER[4096];		 
void MX25_OSPI_Write(uint8_t* pBuffer,uint32_t WriteAddr,uint32_t Size)
{
	uint32_t secpos;
	uint32_t secoff;
	uint32_t secremain;	   
	uint32_t i;    
	uint8_t *MX25_BUF;	  
	MX25_BUF=MX25_BUFFER;	     
	secpos=WriteAddr/4096;//扇区地址  
	secoff=WriteAddr%4096;//在扇区内的偏移
	secremain=4096-secoff;//扇区剩余空间大小   
	if(Size<=secremain)
	{
		secremain=Size;//不大于4096个字节
	}
	while(1) 
	{	
		MX25_OSPI_Read(MX25_BUF,secpos*4096,4096);//读出整个扇区的内容
		for(i=0;i<secremain;i++)//校验数据
		{
			if(MX25_BUF[secoff+i]!=0XFF)
			{
				break;//需要擦除 
			}				
		}
		if(i<secremain)//需要擦除
		{
			MX25_OSPI_Erase_Block(secpos);		//擦除这个扇区
			for(i=0;i<secremain;i++)	   		//复制
			{
				MX25_BUF[i+secoff]=pBuffer[i];	  
			}
			MX25_OSPI_Write_NoCheck(MX25_BUF,secpos*4096,4096);//写入整个扇区  
		}
		else
		{
			MX25_OSPI_Write_NoCheck(pBuffer,WriteAddr,secremain);//写已经擦除了的,直接写入扇区剩余区间. 
		}			
		if(Size==secremain)
		{
			break;//写入结束了
		}
		else//写入未结束
		{
			secpos++;//扇区地址增1
			secoff=0;//偏移位置为0 	 
			pBuffer+=secremain;  				//指针偏移
			WriteAddr+=secremain;				//写地址偏移	   
			Size-=secremain;			//字节数递减
			if(Size > 4096)
			{
				secremain = 4096;//下一个扇区还是写不完
			}
			else
			{
				secremain = Size;		//下一个扇区可以写完了
			}
		}	 
	} 
}


void MX25_OSPI_Write_NoCheck(uint8_t* pBuffer,uint32_t WriteAddr,uint32_t NumByteToWrite)   
{ 			 		 
	uint32_t pageremain;	   
	pageremain=256-WriteAddr%256; //单页剩余的字节数		 	    
	if(NumByteToWrite<=pageremain)
	{
		pageremain=NumByteToWrite;//不大于256个字节
	}
	while(1)
	{	   
		MX25_OSPI_WritePage(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个字节了
			}
		}
	}    
}

uint8_t MX25_OSPI_WritePage(uint8_t *pData, uint32_t WriteAddr, uint32_t Size)
{
	OSPI_RegularCmdTypeDef s_command;
	
		/* 启用写操作 */
	if (MX25_OSPI_WriteEnable() != OSPI_OK)
	{
		return OSPI_ERROR;
	}
 
	/* 初始化程序命令 */	
	s_command.OperationType          = HAL_OSPI_OPTYPE_COMMON_CFG;
    s_command.FlashId                = HAL_OSPI_FLASH_ID_1;
    s_command.InstructionMode        = HAL_OSPI_INSTRUCTION_4_LINES;
	s_command.Instruction            = FLASH_CMD_PP;
	s_command.AddressMode            = HAL_OSPI_ADDRESS_4_LINES;
    s_command.AddressSize            = HAL_OSPI_ADDRESS_32_BITS;
	s_command.AlternateBytesMode     = HAL_OSPI_ALTERNATE_BYTES_NONE;
	s_command.DataMode 			     = HAL_OSPI_DATA_4_LINES;
	s_command.DummyCycles            = 0; 
	s_command.DataDtrMode            = HAL_OSPI_DATA_DTR_DISABLE;
	s_command.SIOOMode               = HAL_OSPI_SIOO_INST_ONLY_FIRST_CMD;
    s_command.InstructionSize        = HAL_OSPI_INSTRUCTION_8_BITS;
    s_command.InstructionDtrMode     = HAL_OSPI_INSTRUCTION_DTR_DISABLE;
    s_command.AddressDtrMode         = HAL_OSPI_ADDRESS_DTR_DISABLE;
    s_command.DQSMode                = HAL_OSPI_DQS_DISABLE;
	s_command.Address                = WriteAddr;
	s_command.NbData                 = Size;

	/* 配置命令 */
	if (HAL_OSPI_Command(&hospi1, &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE)
			!= HAL_OK)
	{
		return OSPI_ERROR;
	}

	/* 传输数据 */
	if (HAL_OSPI_Transmit(&hospi1, pData, HAL_OSPI_TIMEOUT_DEFAULT_VALUE)
			!= HAL_OK)
	{
		return OSPI_ERROR;
	}
		
		/* 配置自动轮询模式等待程序结束 */
		if (MX25_OSPI_AutoPollingMemReady(HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != OSPI_OK)
		{
			return OSPI_ERROR;
		}
 
	return OSPI_OK;
}

        擦除,关于这个就不细讲了,页擦除,块擦除,全局擦除都有,写入指令就行,注意全局擦除需要不少时间,一定要写入等待程序结束的判断,不然很容易擦除失败。

uint8_t MX25_OSPI_Erase_Block(uint32_t BlockAddress)
{
	OSPI_RegularCmdTypeDef s_command;
	
	/* 初始化擦除命令 */
	s_command.OperationType      = HAL_OSPI_OPTYPE_COMMON_CFG;
    s_command.FlashId            = HAL_OSPI_FLASH_ID_1;
    s_command.InstructionMode    = HAL_OSPI_INSTRUCTION_4_LINES;
	s_command.Instruction        = FLASH_CMD_SE;
	s_command.AddressMode        = HAL_OSPI_ADDRESS_4_LINES;
    s_command.AddressSize        = HAL_OSPI_ADDRESS_32_BITS;
	s_command.Address            = BlockAddress*4096;
	s_command.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE;
	s_command.DataMode 			 = HAL_OSPI_DATA_NONE;
	s_command.DummyCycles        = 0; 
	s_command.DataDtrMode        = HAL_OSPI_DATA_DTR_DISABLE;
	s_command.SIOOMode           = HAL_OSPI_SIOO_INST_EVERY_CMD;
    s_command.InstructionSize    = HAL_OSPI_INSTRUCTION_8_BITS;
    s_command.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_DISABLE;
    s_command.AddressDtrMode     = HAL_OSPI_ADDRESS_DTR_DISABLE;
    s_command.DQSMode            = HAL_OSPI_DQS_DISABLE;
 
	/* 启用写操作 */
	if (MX25_OSPI_WriteEnable() != OSPI_OK)
	{
		return OSPI_ERROR;
	}
 
	/* 发送命令 */
	if (HAL_OSPI_Command(&hospi1, &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE)
			!= HAL_OK)
	{
		return OSPI_ERROR;
	}

	/* 配置自动轮询模式等待擦除结束 */
	if (MX25_OSPI_AutoPollingMemReady(MX25_SUBSECTOR_ERASE_MAX_TIME) != OSPI_OK)
	{
		return OSPI_ERROR;
	}
	return OSPI_OK;
}


uint8_t MX25_OSPI_Erase_Chip(void)
{
	OSPI_RegularCmdTypeDef s_command;
	
	/* 初始化擦除命令 */
  s_command.OperationType      = HAL_OSPI_OPTYPE_COMMON_CFG;
  s_command.FlashId            = HAL_OSPI_FLASH_ID_1;
  s_command.InstructionMode    = HAL_OSPI_INSTRUCTION_4_LINES;
  s_command.Instruction        = FLASH_CMD_CE;
  s_command.AddressMode        = HAL_OSPI_ADDRESS_NONE;
  s_command.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE;
  s_command.DataMode 		   = HAL_OSPI_DATA_NONE;
  s_command.DummyCycles        = 0; 
  s_command.DataDtrMode        = HAL_OSPI_DATA_DTR_DISABLE;
  s_command.SIOOMode           = HAL_OSPI_SIOO_INST_EVERY_CMD;
  s_command.InstructionSize    = HAL_OSPI_INSTRUCTION_8_BITS;
  s_command.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_DISABLE;
  s_command.AddressDtrMode     = HAL_OSPI_ADDRESS_DTR_DISABLE;
  s_command.DQSMode            = HAL_OSPI_DQS_DISABLE;
 
	/* 启用写操作 */
	if (MX25_OSPI_WriteEnable() != OSPI_OK)
	{
		return OSPI_ERROR;
	}
	/* 发送命令 */
	if (HAL_OSPI_Command(&hospi1, &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE)!= HAL_OK)
	{
		return OSPI_ERROR;
	}
	/* 配置自动轮询模式等待擦除结束 */
	if (MX25_OSPI_AutoPollingMemReady(MX25_BULK_ERASE_MAX_TIME) != OSPI_OK)
	{
		return OSPI_ERROR;
	}
	return OSPI_OK;
}


uint8_t MX25_OSPI_Erase_4B(uint32_t Address)
{
	OSPI_RegularCmdTypeDef s_command;
	
	/* 初始化擦除命令 */
	s_command.OperationType      = HAL_OSPI_OPTYPE_COMMON_CFG;
    s_command.FlashId            = HAL_OSPI_FLASH_ID_1;
    s_command.InstructionMode    = HAL_OSPI_INSTRUCTION_4_LINES;
	s_command.Instruction        = FLASH_CMD_BE4B;
	s_command.AddressMode        = HAL_OSPI_ADDRESS_4_LINES;
    s_command.AddressSize        = HAL_OSPI_ADDRESS_32_BITS;
	s_command.Address            = Address;
	s_command.AlternateBytesMode = HAL_OSPI_ALTERNATE_BYTES_NONE;
	s_command.DataMode 			 = HAL_OSPI_DATA_NONE;
	s_command.DummyCycles        = 0; 
	s_command.DataDtrMode        = HAL_OSPI_DATA_DTR_DISABLE;
	s_command.SIOOMode           = HAL_OSPI_SIOO_INST_EVERY_CMD;
    s_command.InstructionSize    = HAL_OSPI_INSTRUCTION_8_BITS;
    s_command.InstructionDtrMode = HAL_OSPI_INSTRUCTION_DTR_DISABLE;
    s_command.AddressDtrMode     = HAL_OSPI_ADDRESS_DTR_DISABLE;
    s_command.DQSMode            = HAL_OSPI_DQS_DISABLE;
 
	/* 启用写操作 */
	if (MX25_OSPI_WriteEnable() != OSPI_OK)
	{
		return OSPI_ERROR;
	}
 
	/* 发送命令 */
	if (HAL_OSPI_Command(&hospi1, &s_command, HAL_OSPI_TIMEOUT_DEFAULT_VALUE)
			!= HAL_OK)
	{
		return OSPI_ERROR;
	}

	/* 配置自动轮询模式等待擦除结束 */
	if (MX25_OSPI_AutoPollingMemReady(MX25_SUBSECTOR_ERASE_MAX_TIME) != OSPI_OK)
	{
		return OSPI_ERROR;
	}
	return OSPI_OK;
}

四,应用

        使能QSPI模式,首先可以读取ID,如果ID获取成功则说明通讯正常,可以直接开始写入数据

再读取,如果成功读出字节写入的数据,说明驱动没有问题,MX25就可以投入使用。

void QpiWriteData(uint8_t *WRBUFFER,uint32_t ADDR)
{
	MX25_OSPI_Write(WRBUFFER,ADDR,1024);
}

void QpiReadDate(uint8_t *RDBUFFER,uint32_t ADDR)
{
	MX25_OSPI_Read(RDBUFFER,ADDR,1024);
}
uint8_t WRBUFFER[1024] = {11,2,3,4,5,6,7,8,9,10};
uint8_t RDBUFFER[1024] = {0};
void MX25_WrRdDate(uint32_t size)
{
	uint32_t i = 0;
	QSPI_FLASH_EnQspi();
    QSPI_FLASH_ReadID();
    MX25_OSPI_Addr_Mode_Init();
    MX25_OSPI_Write(WRBUFFER,0x10,size); 
	MX25_OSPI_Read(RDBUFFER,0x10,size);
	
	for(i = 0 ; i < 1024 ; i++)
	{
		printf("RDBUFFER[%d] = 0x%x\t",i,RDBUFFER[i]);
	}
	printf("\r\n");
}

        本人已调通,但是每个人的实际应用情况不同,硬件时钟,线路等等都有区别,以上是该模块全部代码,如果无法直接使用请根据实际情况进行调整,多多尝试。文章来源地址https://www.toymoban.com/news/detail-855220.html

到了这里,关于STM32使用QSPI控制FLASH(FLASH芯片型号:MX25L25645G)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【STM32】SPI初步使用 读写FLASH W25Q64

    (1) SS( Slave Select):从设备选择信号线,常称为片选信号线,每个从设备都有独立的这一条 NSS 信号线,当主机要选择从设备时,把该从设备的 NSS 信号线设置为低电平,该从设备即被选中,即片选有效,接着主机开始与被选中的从设备进行 SPI通讯。所以 SPI通讯以 NSS 线置低电

    2024年02月10日
    浏览(37)
  • stm32使用外部flash w25Q128实现读写操作

    数据保存是所有项目的基本功能,但是对于STM32C8T6的原flash进行操作,一方面大小有可能不够,另一方面单片机的运行程序本来就放在这个里面,所以还是外接的好。这里选用w25Q128 FLASH存储器,参考实现简单读写。 作为一个初学者,技能都是东拼西凑的,基础可能不扎实,如

    2023年04月08日
    浏览(41)
  • STM32使用QUADSPI读写外部Nor Flash(以W25Q64为例)

    QUADSPI 是一种专用的通信接口,连接单、双或四(条数据线) SPI Flash 存储介质。该接 口可以在以下三种模式下工作: ①间接模式:使用 QUADSPI 寄存器执行全部操作。 ②状态轮询模式:周期性读取外部 Flash 状态寄存器,而且标志位置 1 时会产生中断(如擦除或烧写完成,会

    2024年02月11日
    浏览(36)
  • STM32-SPI通信(W25Q64芯片简介,使用SPI读写W25Q64存储器芯片)

    ​  SPI(Serial Peripheral Interface)是由Motorola公司开发的一种通用数据总线四根通信线:SCK(Serial Clock)、MOSI(Master Output Slave Input)、MISO(Master Input Slave Output)、SS(Slave Select)。 ​SPI通信具有以下特点: 同步,全双工; 支持总线挂载多设备(SPI仅支持一主多从); 在不

    2024年02月08日
    浏览(36)
  • FPGA纯verilog代码读写N25Q128A QSPI Flash 提供工程源码和技术支持

    N25Q128A的参数有很多,作为FPGA开发者,需要关注如下参数: 1、4KBytes为1个Sector(扇区); 2、16个Sector(扇区)是1个Block(块)64KBytes; 3、容量为16M=128Mbite字节,共有256个Block,4096个Sector; 这三个参数直接决定了你怎么组织数据的读写操作,比如你的数据量很小,则考虑写入1个Sector(扇

    2024年02月02日
    浏览(40)
  • STM32 W25QXX芯片

     W25QXX芯片介绍 W25QXX芯片是华邦公司推出的大容量SPI FLASH产品,该系列有W25Q16/32/62/128等。本例程使用W25Q64,W25Q64容量为64Mbits(8M字节):8MB的容量分为128个块(Block)(块大小为64KB),每个块又分为16个扇区(Sector)(扇区大小为4KB);W25Q64的最小擦除单位为一个扇区即4KB,因此在

    2023年04月16日
    浏览(21)
  • 【STM32 CubeMX】SPI_Flash_W25Q64的操作方法

    在嵌入式系统开发中,使用外部 SPI Flash 存储器可以为 STM32 微控制器提供额外的存储空间,以存储程序代码、配置数据等。W25Q64 是一款常见的 SPI Flash 存储器,具有64Mb容量和SPI接口。本文将介绍如何使用 STM32 CubeMX 结合 SPI 库与 W25Q64 SPI Flash 进行集成,以便在 STM32 微控制器上

    2024年02月22日
    浏览(33)
  • STM32F429IGT6使用CubeMX配置SPI通信(W25Q256芯片)

    1、硬件电路 需要系统性的看一下W25Q256芯片手册  2、设置RCC,选择高速外部时钟HSE,时钟设置为180MHz 3、配置SPI 4、生成工程配置   5、读写流程图 5、相关代码 6、实验现象 没有问题!

    2024年02月12日
    浏览(33)
  • stm32同芯片但不同flash工程更换Device出现报错

    stm32同芯片但不同flash工程更换Device出现报错 更换Device,我是从ZE换为C8: 把这个从HD更换为MD 解决!

    2024年02月09日
    浏览(32)
  • STM32CUBUMX配置FLASH(W25Q128)--保姆级教程

    ———————————————————————————————————— ⏩ 大家好哇!我是小光,嵌入式爱好者,一个想要成为系统架构师的大三学生。 ⏩最近在开发一个STM32H723ZGT6的板子,使用STM32CUBEMX做了很多驱动,包括ADC、UART、RS485、EEPROM(IIC)、FLASH(SPI)等等。

    2024年02月15日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包