使用STM32控制QSPI接口读写MX25L25645G Flash芯片
本篇详细的记录了如何使用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模式;
个人根据自己所使用的的引脚去选择,如下本人使用的时钟、片选,以及数据收发所使用的引脚。
- mode选择Quad SPI(四线SPI),MX25用作外部flash存储程序并执行;其余选项还未用
- Clock根据所用STM32支持几个SPI flash,若多个还需根据硬件设计选择对应引脚的Port,后面参数就选择对应Port就行
- Data[3:0]是八线OSPI的低四线,引脚选择一定要按照硬件设计选择。
下图则是QSPI的一些配置;
以下参数需根据所用flash的datasheet设置:
- Device Size填写值不用减一,即大小2M,则填21(2的21次方为flash大小)
- Chip Select High Time,一般是3
- Clock Mode 时钟开始的相位(是高还是低)根据需要去选择
- 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, ®, 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");
}
本人已调通,但是每个人的实际应用情况不同,硬件时钟,线路等等都有区别,以上是该模块全部代码,如果无法直接使用请根据实际情况进行调整,多多尝试。
作者:AnXIne