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

2 篇文章 0 订阅
2 篇文章 0 订阅

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

一、硬件准备

1、MX25L25645G引脚示意如图:

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

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

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

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

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

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

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

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

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

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

二,QSPI配置

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

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

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

下图则是QSPI的一些配置;

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

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

三、驱动编写

       这是相关指令的头文件,大家大致可以从变量名以及后面的英文注释分析出该指令的实际功能,比如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字节模式。

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");
}

        本人已调通,但是每个人的实际应用情况不同,硬件时钟,线路等等都有区别,以上是该模块全部代码,如果无法直接使用请根据实际情况进行调整,多多尝试。

  • 12
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
根据引用和引用的内容,可以总结出STM32h750 QSPI W25Q64驱动的一些关键信息。 首先,W25Q64是一种SPI NOR Flash芯片,它被连接到STM32h750的QSPI(Quad SPI)接口上。W25Q64的引脚连接为PB2、PB6、PF6、PF7、PF8和PF9。 在设置QSPI时,一些关键的配置参数需要注意。首先是时钟预分频器(clock prescaler),根据W25Q256的最高时钟频率为104MHz,因此需要将分频设置为2。其次是闪存大小(FLASH SIZE),W25Q64的大小为8MB,所以需要将设置为2的(22-1)次方。时钟模式(Clock Mode)应设置为Low,表示CLK空闲时为低电平。芯片选择(Chip Select)需要设置为High Time为5,以确保高电平持续时间大于50ns。 另外,为了保证正常的工作,所有的QSPI引脚都应该设置为very high,而NCS脚(PB6)必须设置为PULL-UP。关于为什么要设置为PULL-UP,具体原因在引用中没有提及。 最后,需要注意W25Q64与W25Q256之间的一些区别。首先是地址位数,W25Q64只支持24位地址,而W25Q256支持24位和32位地址。其次是读写状态寄存器的不同,W25Q64的读状态寄存器为05h和35h,而W25Q256的为05h、35h和15h。写状态寄存器也有所不同,W25Q64的为01h,而W25Q256的为01h、31h和11h。 综上所述,STM32h750的QSPI可以通过相应的配置来驱动W25Q64芯片。需要注意的是,具体的配置参数和引脚连接可能还取决于具体的硬件设计和应用需求。<span class="em">1</span><span class="em">2</span> #### 引用[.reference_title] - *1* [STM32H750 QSPI间接模式 W25Q64](https://blog.csdn.net/smallerlang/article/details/127921384)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [STM32H753 STM32H743 STM32H750 QSPI W25Q256 下载算法](https://blog.csdn.net/c101028/article/details/132073746)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值