CubeMX STM32H7 OctoSPI Quad SPI Flash

XT25F128F模块图
QSPI NOR Flash结构

1 OctoSPI

1.1 SPI(Serial Peripheral Interface)

基本特性:

  • 数据线数量: 1 条数据线(MOSI/MISO,半双工或全双工)
  • 通信模式: 主从模式,支持多从机
  • 时钟频率: 通常几 MHz 到几十 MHz
  • 典型应用: 传感器、EEPROM、ADC/DAC 等低速外设

优点: 接口简单,引脚少,成本低
缺点: 带宽有限,不适合大数据量传输

1.2 DualSPI

基本特性:

  • 数据线数量: 2 条数据线(IO0、IO1)
  • 工作模式: 两条数据线同时传输数据
  • 理论带宽: 相比 SPI 翻倍
  • 典型应用: NOR Flash、串行存储器

特点: 在 SPI 基础上增加了一条数据线,提高了传输速率

1.3 QSPI(Quad SPI)

基本特性:

  • 数据线数量: 4 条数据线(IO0、IO1、IO2、IO3)
  • 工作模式: 四条数据线同时传输数据
  • 理论带宽: 相比 SPI 提高 4 倍
  • 典型应用: 大容量 NOR/NAND Flash、执行代码(XIP)

特点:

  • 支持 Execute-in-Place(XIP)模式,可直接从 Flash 执行代码
  • 常用于需要较高带宽的存储应用

1.4 OSPI(Octo SPI / Octal SPI)

基本特性:

  • 数据线数量: 8 条数据线
  • 工作模式: 八条数据线同时传输数据
  • 理论带宽: 相比 SPI 提高 8 倍
  • 时钟模式: 支持 SDR(单倍数据率)和 DDR(双倍数据率)
  • 典型应用: 高性能存储、AI 加速器、图形显示缓冲

STM32H7 OctoSPI 特点:

  • 支持 8 线模式(Octal)和 4 线模式(Quad)
  • 最高时钟频率可达 133MHz(SDR)或 266MHz(DDR)
  • 支持内存映射模式,外部 Flash 可映射到 CPU 地址空间
  • 支持双 Flash 模式,可同时连接两个 Flash 器件

1.5 对比总结

特性 SPI DualSPI QSPI OSPI
数据线 1 线 2 线 4 线 8 线
理论带宽
引脚需求 中等 较多
成本 较低 中等
适用场景 低速外设 中等速率存储 高速存储/XIP 超高速存储/图形
STM32 支持 全系列 H7/F7 等 H7/F7 等 H7 系列

1.6 选择建议

  1. SPI: 适合传感器、配置芯片等低速通信
  2. DualSPI: 适合中等数据量的串行存储器
  3. QSPI: 适合需要从 Flash 直接执行代码的应用
  4. OSPI: 适合图形显示、AI 推理、高速数据采集等高性能场景

对于 STM32H7 项目,如果使用大容量 Flash 存储程序或数据,QSPI/OSPI 是更好的选择,特别是需要 XIP 功能时。

2 CubeMX STM32H7 OctoSPI配置QSPI

2.1 CubeMX OctoSPI模式配置

在这里插入图片描述

2.2 CubeMX OctoSPI参数配置

在这里插入图片描述

2.3 MPU配置

在这里插入图片描述
必须添加内存管理单元的0X90000000,否则无法使用内存映射(XIP)模式

3 XT25F128 NOR Flash

3.1 头文件

/**
  ******************************************************************************
  * @file    XT25F128F.h
  * @brief   XT25F128F Quad SPI Flash Driver Header File
  *          Based on STM32H723 OctoSPI Peripheral
  *          XT25F128F-W: 128Mbit (16MB) Quad I/O Serial Flash
  ******************************************************************************
  * @attention
  *
  * - Flash Size: 128Mbit = 16MByte
  * - Sector Size: 64KB (Block), 4KB (Subsector)
  * - Page Size: 256 Bytes
  * - Quad I/O Fast Read (EBH): up to 133MHz
  * - Quad Page Program (32H): up to 133MHz
  *
  ******************************************************************************
  */

#ifndef __XT25F128F_H
#define __XT25F128F_H

#ifdef __cplusplus
 extern "C" {
#endif

#include "stm32h7xx_hal.h"

/* ========================================================================== */
/*                         XT25F128F Device Configuration                     */
/* ========================================================================== */

/** @defgroup XT25F128F_Size Flash Size Definitions
  * @{
  */
#define XT25F_FLASH_SIZE                     0x1000000U  /* 128 MBits => 16MBytes */
#define XT25F_SECTOR_SIZE                    0x10000U    /* 256 sectors of 64KBytes */
#define XT25F_SUBSECTOR_SIZE                 0x1000U     /* 4096 subsectors of 4kBytes */
#define XT25F_PAGE_SIZE                      0x100U      /* 65536 pages of 256 bytes */

#define XT25F_DUMMY_CYCLES_READ_QUAD         6U          /* Default dummy cycles for Quad I/O Read (DC0=0) */
#define XT25F_DUMMY_CYCLES_READ_QUAD_DC1     10U         /* Dummy cycles when DC0=1 */
#define XT25F_AUTOPOLLING_INTERVAL_TIME      0x10U       /* Auto-polling interval */

#define XT25F_MEMORY_BASE_ADDR   0x90000000U
/**
  * @}
  */

/* ========================================================================== */
/*                         XT25F128F Command Definitions                      */
/* ========================================================================== */

/** @defgroup XT25F128F_Commands Flash Command Definitions
  * @{
  */

/* Write Enable / Disable Commands */
#define XT25F_WRITE_ENABLE_CMD               0x06U  /* Set Write Enable Latch */
#define XT25F_WRITE_DISABLE_CMD              0x04U  /* Reset Write Enable Latch */
#define XT25F_ENABLE_VOLATILE_SR_CMD         0x50U  /* Enable Volatile Status Register */

/* ID Commands */
#define XT25F_READ_JEDEC_ID_CMD              0x9FU  /* Read JEDEC ID (3 bytes) */
#define XT25F_FULLID_CMD                     0x90U  /* Read Manufacturer & Device ID */
#define XT25F_READ_UID_CMD                   0x4BU  /* Read Unique 64-bit ID */
#define XT25F_POWERUP_CMD                    0xABU  /* Release Power-down / Read Device ID */

/* Read Commands */
#define XT25F_READ_DATA_CMD                  0x03U  /* Standard Read (1-1-1) */
#define XT25F_FAST_READ_CMD                  0x0BU  /* Fast Read (1-1-1) with dummy */
#define XT25F_FAST_READ_DUAL_OUT_CMD         0x3BU  /* Fast Read Dual Output (1-1-2) */
#define XT25F_FAST_READ_DUAL_IO_CMD          0xBBU  /* Fast Read Dual I/O (1-2-2) */
#define XT25F_FAST_READ_QUAD_OUT_CMD         0x6BU  /* Fast Read Quad Output (1-1-4) */
#define XT25F_FAST_READ_QUAD_IO_CMD          0xEBU  /* Fast Read Quad I/O (1-4-4) */

/* Program Commands */
#define XT25F_PAGE_PROGRAM_CMD               0x02U  /* Page Program (1-1-1) */
#define XT25F_PAGE_PROGRAM_QUAD_INP_CMD      0x32U  /* Quad Page Program (1-1-4) */

/* Erase Commands */
#define XT25F_SECTOR_ERASE_CMD               0x20U  /* Sector Erase (4KB) */
#define XT25F_32KB_BLOCK_ERASE_CMD           0x52U  /* 32KB Block Erase */
#define XT25F_64KB_BLOCK_ERASE_CMD           0xD8U  /* 64KB Block Erase */
#define XT25F_CHIP_ERASE_CMD                 0xC7U  /* Chip Erase (or 60H) */

/* Status Register Commands */
#define XT25F_READ_SR1_CMD                   0x05U  /* Read Status Register 1 */
#define XT25F_READ_SR2_CMD                   0x35U  /* Read Status Register 2 */
#define XT25F_READ_SR3_CMD                   0x15U  /* Read Status Register 3 */
#define XT25F_WRITE_SR1_CMD                  0x01U  /* Write Status Register 1 */
#define XT25F_WRITE_SR2_CMD                  0x31U  /* Write Status Register 2 */
#define XT25F_WRITE_SR3_CMD                  0x11U  /* Write Status Register 3 */

/* Reset Commands */
#define XT25F_ENABLE_RST_CMD                 0x66U  /* Enable Reset */
#define XT25F_RESET_CMD                      0x99U  /* Reset Device */

/* Power Control */
#define XT25F_POWERDOWN_CMD                  0xB9U  /* Deep Power Down */

/* Security Register Commands */
#define XT25F_READ_SECURITY_REG_CMD          0x48U  /* Read Security Registers */
#define XT25F_PROG_SECURITY_REG_CMD          0x42U  /* Program Security Registers */
#define XT25F_ERASE_SECURITY_REG_CMD         0x44U  /* Erase Security Registers */

/* Block Lock Commands */
#define XT25F_GLOBAL_LOCK_CMD                0x7EU  /* Global Block Lock */
#define XT25F_GLOBAL_UNLOCK_CMD              0x98U  /* Global Block Unlock */
#define XT25F_IND_BLOCK_LOCK_CMD             0x36U  /* Individual Block Lock */
#define XT25F_IND_BLOCK_UNLOCK_CMD           0x39U  /* Individual Block Unlock */
#define XT25F_READ_BLOCK_LOCK_CMD            0x3DU  /* Read Block Lock */

/* Advanced Commands */
#define XT25F_READ_SFDP_CMD                  0x5AU  /* Read SFDP Register */
#define XT25F_ERASEPROG_SUSPEND_CMD          0x75U  /* Erase/Program Suspend */
#define XT25F_ERASEPROG_RESUME_CMD           0x7AU  /* Erase/Program Resume */
#define XT25F_SET_BURST_WRAP_CMD             0x77U  /* Set Burst with Wrap */
/**
  * @}
  */

/* ========================================================================== */
/*                      XT25F128F Status Register Bits                        */
/* ========================================================================== */

/** @defgroup XT25F128F_Status_Register Status Register Bit Definitions
  * @{
  */

/* Status Register 1 (S7-S0) */
#define XT25F_SR1_SRP0                       ((uint8_t)0x80)  /* Status Register Protect 0 */
#define XT25F_SR1_SEC                        ((uint8_t)0x40)  /* Sector Protect Bit */
#define XT25F_SR1_TB                         ((uint8_t)0x20)  /* Top/Bottom Protect */
#define XT25F_SR1_BP2                        ((uint8_t)0x10)  /* Block Protect 2 */
#define XT25F_SR1_BP1                        ((uint8_t)0x08)  /* Block Protect 1 */
#define XT25F_SR1_BP0                        ((uint8_t)0x04)  /* Block Protect 0 */
#define XT25F_SR1_WEL                        ((uint8_t)0x02)  /* Write Enable Latch */
#define XT25F_SR1_WIP                        ((uint8_t)0x01)  /* Write In Progress */

/* Status Register 2 (S15-S8) */
#define XT25F_SR2_SRP1                       ((uint8_t)0x80)  /* Status Register Protect 1 */
#define XT25F_SR2_QE                         ((uint8_t)0x02)  /* Quad Enable (S9) */
#define XT25F_SR2_SUS                        ((uint8_t)0x01)  /* Suspend Status */

/* Status Register 3 (S23-S16) */
#define XT25F_SR3_DRV1                       ((uint8_t)0x40)  /* Driver Strength 1 */
#define XT25F_SR3_DRV0                       ((uint8_t)0x20)  /* Driver Strength 0 */
#define XT25F_SR3_WPS                        ((uint8_t)0x04)  /* Write Protect Selection */
/**
  * @}
  */

/* ========================================================================== */
/*                      XT25F128F JEDEC ID Definition                         */
/* ========================================================================== */

/** @defgroup XT25F128F_ID Device ID Definitions
  * @{
  */
#define XT25F_MANUFACTURER_ID                0x0BU  /* XTX Manufacturer ID */
#define XT25F_MEMORY_TYPE                    0x40U  /* Memory Type */
#define XT25F_CAPACITY_128MBIT               0x18U  /* 128Mbit Capacity */
/**
  * @}
  */

/* ========================================================================== */
/*                        OctoSPI Configuration Defaults                      */
/* ========================================================================== */

/** @defgroup XT25F128F_OSPI_Config OctoSPI Configuration
  * @{
  */
#define XT25F_OSPI_FLASH_ID                  HAL_OSPI_FLASH_ID_1
#define XT25F_OSPI_TIMEOUT                   5000U  /* 5s timeout for safety */
/**
  * @}
  */

/* ========================================================================== */
/*                          Public Function Prototypes                        */
/* ========================================================================== */

/** @defgroup XT25F128F_Functions Public Functions
  * @{
  */

/* Initialization / Configuration */
HAL_StatusTypeDef XT25F_Init(OSPI_HandleTypeDef *hospi);
HAL_StatusTypeDef XT25F_Reset(OSPI_HandleTypeDef *hospi);
HAL_StatusTypeDef XT25F_EnableQuadMode(OSPI_HandleTypeDef *hospi);

/* ID Functions */
HAL_StatusTypeDef XT25F_ReadJEDECID(OSPI_HandleTypeDef *hospi, uint8_t *pData);
HAL_StatusTypeDef XT25F_ReadUID(OSPI_HandleTypeDef *hospi, uint8_t *pData);

/* Read Functions - Quad SPI Mode */
HAL_StatusTypeDef XT25F_Read(OSPI_HandleTypeDef *hospi, uint8_t *pData,
                              uint32_t ReadAddr, uint32_t Size);
HAL_StatusTypeDef XT25F_FastReadQuadIO(OSPI_HandleTypeDef *hospi, uint8_t *pData,
                                       uint32_t ReadAddr, uint32_t Size);
HAL_StatusTypeDef XT25F_FastReadQuadOut(OSPI_HandleTypeDef *hospi, uint8_t *pData,
                                        uint32_t ReadAddr, uint32_t Size);

/* Write Functions */
HAL_StatusTypeDef XT25F_Write(OSPI_HandleTypeDef *hospi, uint8_t *pData,
                               uint32_t WriteAddr, uint32_t Size);
HAL_StatusTypeDef XT25F_PageProgram(OSPI_HandleTypeDef *hospi, uint8_t *pData,
                                     uint32_t WriteAddr, uint32_t Size);
HAL_StatusTypeDef XT25F_QuadPageProgram(OSPI_HandleTypeDef *hospi, uint8_t *pData,
                                         uint32_t WriteAddr, uint32_t Size);

/* Write Mode Selection */
typedef enum {
    XT25F_WRITE_MODE_STANDARD = 0,  /* Standard Page Program (02H), 1-1-1 */
    XT25F_WRITE_MODE_QUAD     = 1   /* Quad Page Program (32H), 1-1-4 */
} XT25F_WriteModeTypeDef;

void XT25F_SetWriteMode(XT25F_WriteModeTypeDef mode);
XT25F_WriteModeTypeDef XT25F_GetWriteMode(void);

/* Erase Functions */
HAL_StatusTypeDef XT25F_EraseSector(OSPI_HandleTypeDef *hospi, uint32_t SectorAddress);
HAL_StatusTypeDef XT25F_EraseBlock32K(OSPI_HandleTypeDef *hospi, uint32_t BlockAddress);
HAL_StatusTypeDef XT25F_EraseBlock64K(OSPI_HandleTypeDef *hospi, uint32_t BlockAddress);
HAL_StatusTypeDef XT25F_EraseChip(OSPI_HandleTypeDef *hospi);

/* Status Register Functions */
HAL_StatusTypeDef XT25F_ReadStatusRegister(OSPI_HandleTypeDef *hospi,
                                            uint8_t *pData, uint8_t RegNum);
HAL_StatusTypeDef XT25F_WriteStatusRegister(OSPI_HandleTypeDef *hospi,
                                             uint8_t Value, uint8_t RegNum);

/* Memory Mapped Mode */
HAL_StatusTypeDef XT25F_EnableMemoryMappedMode(OSPI_HandleTypeDef *hospi);
HAL_StatusTypeDef XT25F_DisableMemoryMappedMode(OSPI_HandleTypeDef *hospi);

/* Polling / Status Functions */
HAL_StatusTypeDef XT25F_AutoPollingMemReady(OSPI_HandleTypeDef *hospi);
HAL_StatusTypeDef XT25F_WriteEnable(OSPI_HandleTypeDef *hospi);
HAL_StatusTypeDef XT25F_IsBusy(OSPI_HandleTypeDef *hospi);

/* Power Control */
HAL_StatusTypeDef XT25F_EnterPowerDown(OSPI_HandleTypeDef *hospi);
HAL_StatusTypeDef XT25F_ReleasePowerDown(OSPI_HandleTypeDef *hospi);
/**
  * @}
  */

#ifdef __cplusplus
}
#endif

#endif /* __XT25F128F_H */

3.2 源文件

/**
  ******************************************************************************
  * @file    XT25F128F.c
  * @brief   XT25F128F Quad SPI Flash Driver
  *          Based on STM32H723 OctoSPI Peripheral
  *          Quad I/O Mode (1-4-4) with Memory-Mapped Support
  ******************************************************************************
  * @attention
  *
  * 本驱动基于STM32H723的OctoSPI外设,实现XT25F128F的Quad SPI模式驱动。
  * 支持间接模式读写、自动轮询、内存映射模式等功能。
  *
  *  Quad I/O Fast Read (EBH): 指令(1线) + 地址(4线) + 数据(4线)
  *  Quad Page Program (32H):  指令(1线) + 地址(1线) + 数据(4线)
  *
  ******************************************************************************
  */

#include "XT25F128F.h"

/* ========================================================================== */
/*                         Private Variables                                  */
/* ======================================================================= */
static XT25F_WriteModeTypeDef g_write_mode = XT25F_WRITE_MODE_QUAD;  /* Default Quad mode */
/* ========================================================================== */
/*                         Private Function Prototypes                        */
/* ========================================================================== */

static HAL_StatusTypeDef XT25F_Command_CommonCfg(OSPI_RegularCmdTypeDef *sCommand);
static HAL_StatusTypeDef XT25F_Command_ReadCfg(OSPI_RegularCmdTypeDef *sCommand);
static HAL_StatusTypeDef XT25F_Command_WriteCfg(OSPI_RegularCmdTypeDef *sCommand);
static HAL_StatusTypeDef XT25F_EnsureReady(OSPI_HandleTypeDef *hospi);

/* ========================================================================== */
/*                         Private Helper Functions                           */
/* ========================================================================== */

/**
  * @brief  Ensure OSPI is in ready state, abort if necessary
  * @param  hospi: OSPI handle
  * @retval HAL status
  */
static HAL_StatusTypeDef XT25F_EnsureReady(OSPI_HandleTypeDef *hospi)
{
    uint32_t state = HAL_OSPI_GetState(hospi);

    /* If already ready, return OK */
    if (state == HAL_OSPI_STATE_READY) {
        return HAL_OK;
    }

    /* If not in ready state, try to abort to reset state machine */
    if (HAL_OSPI_Abort(hospi) != HAL_OK) {
        /* If abort fails, try a more aggressive reset */
        hospi->State = HAL_OSPI_STATE_READY;
        hospi->ErrorCode = HAL_OSPI_ERROR_NONE;
    }

    /* Wait for ready state after abort */
    uint32_t timeout = 10000;
    while ((HAL_OSPI_GetState(hospi) != HAL_OSPI_STATE_READY) && (timeout > 0)) {
        timeout--;
    }

    /* If still not ready, force reset the state */
    if (HAL_OSPI_GetState(hospi) != HAL_OSPI_STATE_READY) {
        hospi->State = HAL_OSPI_STATE_READY;
        hospi->ErrorCode = HAL_OSPI_ERROR_NONE;
    }

    return HAL_OK;
}

/**
  * @brief  Configure common OSPI command parameters
  * @param  sCommand: Pointer to OSPI command structure
  * @retval HAL status
  */
static HAL_StatusTypeDef XT25F_Command_CommonCfg(OSPI_RegularCmdTypeDef *sCommand)
{
    sCommand->OperationType       = HAL_OSPI_OPTYPE_COMMON_CFG;
    sCommand->FlashId             = XT25F_OSPI_FLASH_ID;
    sCommand->InstructionDtrMode  = HAL_OSPI_INSTRUCTION_DTR_DISABLE;
    sCommand->AddressDtrMode      = HAL_OSPI_ADDRESS_DTR_DISABLE;
    sCommand->DataDtrMode         = HAL_OSPI_DATA_DTR_DISABLE;
    sCommand->DQSMode             = HAL_OSPI_DQS_DISABLE;
    sCommand->SIOOMode            = HAL_OSPI_SIOO_INST_EVERY_CMD;
    sCommand->AlternateBytesMode  = HAL_OSPI_ALTERNATE_BYTES_NONE;
    sCommand->AlternateBytes      = 0;
    sCommand->AlternateBytesSize  = HAL_OSPI_ALTERNATE_BYTES_NONE;
    sCommand->AlternateBytesDtrMode = HAL_OSPI_ALTERNATE_BYTES_DTR_DISABLE;
    sCommand->InstructionMode     = HAL_OSPI_INSTRUCTION_1_LINE;
    sCommand->InstructionSize     = HAL_OSPI_INSTRUCTION_8_BITS;
    sCommand->AddressSize         = HAL_OSPI_ADDRESS_24_BITS;
    sCommand->DummyCycles         = 0;
    sCommand->NbData              = 0;

    return HAL_OK;
}

/**
  * @brief  Configure read OSPI command parameters
  * @param  sCommand: Pointer to OSPI command structure
  * @retval HAL status
  */
static HAL_StatusTypeDef XT25F_Command_ReadCfg(OSPI_RegularCmdTypeDef *sCommand)
{
    sCommand->OperationType       = HAL_OSPI_OPTYPE_READ_CFG;
    sCommand->FlashId             = XT25F_OSPI_FLASH_ID;
    sCommand->InstructionDtrMode  = HAL_OSPI_INSTRUCTION_DTR_DISABLE;
    sCommand->AddressDtrMode      = HAL_OSPI_ADDRESS_DTR_DISABLE;
    sCommand->DataDtrMode         = HAL_OSPI_DATA_DTR_DISABLE;
    sCommand->DQSMode             = HAL_OSPI_DQS_DISABLE;
    sCommand->SIOOMode            = HAL_OSPI_SIOO_INST_EVERY_CMD;
    sCommand->AlternateBytesMode  = HAL_OSPI_ALTERNATE_BYTES_NONE;
    sCommand->AlternateBytes      = 0;
    sCommand->AlternateBytesSize  = HAL_OSPI_ALTERNATE_BYTES_NONE;
    sCommand->AlternateBytesDtrMode = HAL_OSPI_ALTERNATE_BYTES_DTR_DISABLE;
    sCommand->InstructionMode     = HAL_OSPI_INSTRUCTION_1_LINE;
    sCommand->InstructionSize     = HAL_OSPI_INSTRUCTION_8_BITS;
    sCommand->AddressSize         = HAL_OSPI_ADDRESS_24_BITS;

    return HAL_OK;
}

/**
  * @brief  Configure write OSPI command parameters
  * @param  sCommand: Pointer to OSPI command structure
  * @retval HAL status
  */
static HAL_StatusTypeDef XT25F_Command_WriteCfg(OSPI_RegularCmdTypeDef *sCommand)
{
    sCommand->OperationType       = HAL_OSPI_OPTYPE_WRITE_CFG;
    sCommand->FlashId             = XT25F_OSPI_FLASH_ID;
    sCommand->InstructionDtrMode  = HAL_OSPI_INSTRUCTION_DTR_DISABLE;
    sCommand->AddressDtrMode      = HAL_OSPI_ADDRESS_DTR_DISABLE;
    sCommand->DataDtrMode         = HAL_OSPI_DATA_DTR_DISABLE;
    sCommand->DQSMode             = HAL_OSPI_DQS_DISABLE;
    sCommand->SIOOMode            = HAL_OSPI_SIOO_INST_EVERY_CMD;
    sCommand->AlternateBytesMode  = HAL_OSPI_ALTERNATE_BYTES_NONE;
    sCommand->AlternateBytes      = 0;
    sCommand->AlternateBytesSize  = HAL_OSPI_ALTERNATE_BYTES_NONE;
    sCommand->AlternateBytesDtrMode = HAL_OSPI_ALTERNATE_BYTES_DTR_DISABLE;
    sCommand->InstructionMode     = HAL_OSPI_INSTRUCTION_1_LINE;
    sCommand->InstructionSize     = HAL_OSPI_INSTRUCTION_8_BITS;
    sCommand->AddressSize         = HAL_OSPI_ADDRESS_24_BITS;

    return HAL_OK;
}

/* ========================================================================== */
/*                         Initialization Functions                           */
/* ========================================================================== */

/**
  * @brief  Initialize the XT25F128F Flash and OctoSPI interface
  * @param  hospi: OSPI handle
  * @retval HAL status
  */
HAL_StatusTypeDef XT25F_Init(OSPI_HandleTypeDef *hospi)
{
    HAL_StatusTypeDef status;

    /* 1. Reset the Flash device */
    status = XT25F_Reset(hospi);
    if (status != HAL_OK) {
        return HAL_ERROR;
    }

    HAL_Delay(1);

    /* 2. Wait for Flash to be ready */
    status = XT25F_AutoPollingMemReady(hospi);
    if (status != HAL_OK) {
        return HAL_ERROR;
    }

    /* 3. Enable Quad Mode (set QE bit in Status Register 2) */
    status = XT25F_EnableQuadMode(hospi);
    if (status != HAL_OK) {
        return HAL_ERROR;
    }

    /* 4. Wait for Flash to be ready */
    status = XT25F_AutoPollingMemReady(hospi);
    if (status != HAL_OK) {
        return HAL_ERROR;
    }

    return HAL_OK;
}

/**
  * @brief  Reset the XT25F128F Flash device
  * @param  hospi: OSPI handle
  * @retval HAL status
  */
HAL_StatusTypeDef XT25F_Reset(OSPI_HandleTypeDef *hospi)
{
    OSPI_RegularCmdTypeDef sCommand = {0};

    /* Enable Reset Command (66H) */
    XT25F_Command_CommonCfg(&sCommand);
    sCommand.Instruction    = XT25F_ENABLE_RST_CMD;
    sCommand.AddressMode    = HAL_OSPI_ADDRESS_NONE;
    sCommand.Address        = 0;
    sCommand.DataMode       = HAL_OSPI_DATA_NONE;

    if (HAL_OSPI_Command(hospi, &sCommand, XT25F_OSPI_TIMEOUT) != HAL_OK) {
        return HAL_ERROR;
    }

    /* Reset Command (99H) */
    sCommand.Instruction = XT25F_RESET_CMD;

    if (HAL_OSPI_Command(hospi, &sCommand, XT25F_OSPI_TIMEOUT) != HAL_OK) {
        return HAL_ERROR;
    }

    HAL_Delay(1);

    return HAL_OK;
}

/**
  * @brief  Enable Quad SPI mode by setting QE bit
  * @param  hospi: OSPI handle
  * @retval HAL status
  */
HAL_StatusTypeDef XT25F_EnableQuadMode(OSPI_HandleTypeDef *hospi)
{
    uint8_t sr2 = 0;

    /* Read Status Register 2 */
    if (XT25F_ReadStatusRegister(hospi, &sr2, 2) != HAL_OK) {
        return HAL_ERROR;
    }

    /* Check if Quad Enable bit is already set */
    if ((sr2 & XT25F_SR2_QE) != 0) {
        return HAL_OK;  /* Quad mode already enabled */
    }

    /* Set Quad Enable bit */
    sr2 |= XT25F_SR2_QE;

    /* Write Status Register 2 */
    if (XT25F_WriteStatusRegister(hospi, sr2, 2) != HAL_OK) {
        return HAL_ERROR;
    }

    return HAL_OK;
}

/* ========================================================================== */
/*                           ID Functions                                     */
/* ========================================================================== */

/**
  * @brief  Read JEDEC ID (Manufacturer ID + Memory Type + Capacity)
  * @param  hospi: OSPI handle
  * @param  pData: Pointer to data buffer (3 bytes)
  * @retval HAL status
  */
HAL_StatusTypeDef XT25F_ReadJEDECID(OSPI_HandleTypeDef *hospi, uint8_t *pData)
{
    OSPI_RegularCmdTypeDef sCommand = {0};

    XT25F_Command_CommonCfg(&sCommand);
    sCommand.Instruction    = XT25F_READ_JEDEC_ID_CMD;
    sCommand.AddressMode    = HAL_OSPI_ADDRESS_NONE;
    sCommand.Address        = 0;
    sCommand.DataMode       = HAL_OSPI_DATA_1_LINE;
    sCommand.DummyCycles    = 0;
    sCommand.NbData         = 3;

    if (HAL_OSPI_Command(hospi, &sCommand, XT25F_OSPI_TIMEOUT) != HAL_OK) {
        return HAL_ERROR;
    }

    if (HAL_OSPI_Receive(hospi, pData, XT25F_OSPI_TIMEOUT) != HAL_OK) {
        return HAL_ERROR;
    }

    return HAL_OK;
}

/**
  * @brief  Read Unique 64-bit ID
  * @param  hospi: OSPI handle
  * @param  pData: Pointer to data buffer (8 bytes / 64 bits)
  * @retval HAL status
  */
HAL_StatusTypeDef XT25F_ReadUID(OSPI_HandleTypeDef *hospi, uint8_t *pData)
{
    OSPI_RegularCmdTypeDef sCommand = {0};

    XT25F_Command_CommonCfg(&sCommand);
    sCommand.Instruction    = XT25F_READ_UID_CMD;
    sCommand.AddressMode    = HAL_OSPI_ADDRESS_NONE;
    sCommand.Address        = 0;
    sCommand.DataMode       = HAL_OSPI_DATA_1_LINE;
    sCommand.DummyCycles    = 32;  /* 4 Dummy Bytes = 32 bits */
    sCommand.NbData         = 8;   /* 64-bit Unique ID */

    if (HAL_OSPI_Command(hospi, &sCommand, XT25F_OSPI_TIMEOUT) != HAL_OK) {
        return HAL_ERROR;
    }

    if (HAL_OSPI_Receive(hospi, pData, XT25F_OSPI_TIMEOUT) != HAL_OK) {
        return HAL_ERROR;
    }

    return HAL_OK;
}

/* ========================================================================== */
/*                         Read Functions - Quad SPI Mode                     */
/* ========================================================================== */

/**
  * @brief  Read data from Flash using Quad I/O Fast Read (EBH command)
  *         This is the default high-speed read function (1-4-4 mode)
  * @param  hospi: OSPI handle
  * @param  pData: Pointer to data buffer
  * @param  ReadAddr: Start address to read from
  * @param  Size: Number of bytes to read
  * @retval HAL status
  */
HAL_StatusTypeDef XT25F_Read(OSPI_HandleTypeDef *hospi, uint8_t *pData,
                              uint32_t ReadAddr, uint32_t Size)
{
    return XT25F_FastReadQuadIO(hospi, pData, ReadAddr, Size);
}

/**
  * @brief  Quad I/O Fast Read (EBH command, 1-4-4 mode)
  *         Instruction: 1 line, Address: 4 lines, Data: 4 lines
  *         Default dummy cycles: 6 (DC0=0)
  * @param  hospi: OSPI handle
  * @param  pData: Pointer to data buffer
  * @param  ReadAddr: Start address to read from
  * @param  Size: Number of bytes to read
  * @retval HAL status
  */
HAL_StatusTypeDef XT25F_FastReadQuadIO(OSPI_HandleTypeDef *hospi, uint8_t *pData,
                                       uint32_t ReadAddr, uint32_t Size)
{
    OSPI_RegularCmdTypeDef sCommand = {0};

    if (Size == 0) {
        return HAL_OK;
    }

    XT25F_Command_CommonCfg(&sCommand);
    sCommand.Instruction    = XT25F_FAST_READ_QUAD_IO_CMD;
    sCommand.AddressMode    = HAL_OSPI_ADDRESS_4_LINES;   /* Address: 4 lines */
    sCommand.Address        = ReadAddr;
    sCommand.DataMode       = HAL_OSPI_DATA_4_LINES;      /* Data: 4 lines */
    sCommand.DummyCycles    = XT25F_DUMMY_CYCLES_READ_QUAD;  /* 6 dummy cycles */
    sCommand.NbData         = Size;

    if (HAL_OSPI_Command(hospi, &sCommand, XT25F_OSPI_TIMEOUT) != HAL_OK) {
        return HAL_ERROR;
    }

    if (HAL_OSPI_Receive(hospi, pData, XT25F_OSPI_TIMEOUT) != HAL_OK) {
        return HAL_ERROR;
    }

    return HAL_OK;
}

/**
  * @brief  Quad Output Fast Read (6BH command, 1-1-4 mode)
  *         Instruction: 1 line, Address: 1 line, Data: 4 lines
  * @param  hospi: OSPI handle
  * @param  pData: Pointer to data buffer
  * @param  ReadAddr: Start address to read from
  * @param  Size: Number of bytes to read
  * @retval HAL status
  */
HAL_StatusTypeDef XT25F_FastReadQuadOut(OSPI_HandleTypeDef *hospi, uint8_t *pData,
                                        uint32_t ReadAddr, uint32_t Size)
{
    OSPI_RegularCmdTypeDef sCommand = {0};

    if (Size == 0) {
        return HAL_OK;
    }

    XT25F_Command_CommonCfg(&sCommand);
    sCommand.Instruction    = XT25F_FAST_READ_QUAD_OUT_CMD;
    sCommand.AddressMode    = HAL_OSPI_ADDRESS_1_LINE;    /* Address: 1 line */
    sCommand.Address        = ReadAddr;
    sCommand.DataMode       = HAL_OSPI_DATA_4_LINES;      /* Data: 4 lines */
    sCommand.DummyCycles    = 8;   /* 1 Dummy Byte = 8 clocks */
    sCommand.NbData         = Size;

    if (HAL_OSPI_Command(hospi, &sCommand, XT25F_OSPI_TIMEOUT) != HAL_OK) {
        return HAL_ERROR;
    }

    if (HAL_OSPI_Receive(hospi, pData, XT25F_OSPI_TIMEOUT) != HAL_OK) {
        return HAL_ERROR;
    }

    return HAL_OK;
}

/* ========================================================================== */
/*                        Write Mode Control Functions                        */
/* ========================================================================== */

/**
  * @brief  Set write mode (standard or quad)
  * @param  mode: Write mode
  * @retval None
  */
void XT25F_SetWriteMode(XT25F_WriteModeTypeDef mode)
{
    g_write_mode = mode;
}

/**
  * @brief  Get current write mode
  * @retval Current write mode
  */
XT25F_WriteModeTypeDef XT25F_GetWriteMode(void)
{
    return g_write_mode;
}

/* ========================================================================== */
/*                             Write Functions                                */
/* ========================================================================== */

/**
  * @brief  Write data to Flash (page-by-page programming)
  *         Uses the currently selected write mode (standard or quad)
  * @param  hospi: OSPI handle
  * @param  pData: Pointer to data buffer
  * @param  WriteAddr: Start address to write to
  * @param  Size: Number of bytes to write
  * @retval HAL status
  */
HAL_StatusTypeDef XT25F_Write(OSPI_HandleTypeDef *hospi, uint8_t *pData,
                               uint32_t WriteAddr, uint32_t Size)
{
    uint32_t end_addr;
    uint32_t current_size;
    uint32_t current_addr;
    uint8_t *data_ptr;
    HAL_StatusTypeDef status;

    if (Size == 0) {
        return HAL_OK;
    }

    /* Calculate first page boundary */
    current_addr = WriteAddr;
    end_addr = WriteAddr + Size;
    data_ptr = pData;

    /* Calculate size for first page */
    current_size = XT25F_PAGE_SIZE - (WriteAddr % XT25F_PAGE_SIZE);
    if (current_size > Size) {
        current_size = Size;
    }

    /* Perform the write page by page */
    while (current_addr < end_addr) {
        /* Enable write operations */

        status = XT25F_WriteEnable(hospi);
        if (status != HAL_OK) {
            return HAL_ERROR;
        }

        /* Program current page based on selected mode */
        if (g_write_mode == XT25F_WRITE_MODE_QUAD) {
            status = XT25F_QuadPageProgram(hospi, data_ptr, current_addr, current_size);
        } else {
            status = XT25F_PageProgram(hospi, data_ptr, current_addr, current_size);
        }

        if (status != HAL_OK) {
            return HAL_ERROR;
        }

        /* Wait for write operation to complete */
        status = XT25F_AutoPollingMemReady(hospi);
        if (status != HAL_OK) {
            return HAL_ERROR;
        }

        /* Update address and size for next page */
        current_addr += current_size;
        data_ptr += current_size;

        /* Calculate size for next page */
        current_size = (end_addr - current_addr) > XT25F_PAGE_SIZE
                        ? XT25F_PAGE_SIZE
                        : (end_addr - current_addr);
    }

    return HAL_OK;
}

/**
  * @brief  Standard Page Program (02H command, 1-1-1 mode)
  *         Instruction: 1 line, Address: 1 line, Data: 1 line
  *         Note: Data must be within a single page
  * @param  hospi: OSPI handle
  * @param  pData: Pointer to data buffer
  * @param  WriteAddr: Start address to write to
  * @param  Size: Number of bytes to write (max 256 bytes per page)
  * @retval HAL status
  */
HAL_StatusTypeDef XT25F_PageProgram(OSPI_HandleTypeDef *hospi, uint8_t *pData,
                                     uint32_t WriteAddr, uint32_t Size)
{
    OSPI_RegularCmdTypeDef sCommand = {0};

    if ((Size == 0) || (Size > XT25F_PAGE_SIZE)) {
        return HAL_ERROR;
    }

    /* Ensure OSPI is in ready state */
    if (XT25F_EnsureReady(hospi) != HAL_OK) {
        return HAL_ERROR;
    }

    XT25F_Command_CommonCfg(&sCommand);
    sCommand.Instruction    = XT25F_PAGE_PROGRAM_CMD;
    sCommand.AddressMode    = HAL_OSPI_ADDRESS_1_LINE;
    sCommand.Address        = WriteAddr;
    sCommand.DataMode       = HAL_OSPI_DATA_1_LINE;
    sCommand.DummyCycles    = 0;
    sCommand.NbData         = Size;

    /* Temporarily set FIFO threshold to 1 to prevent timeout
     * with small transfer sizes (< original FifoThreshold) */
    uint32_t orig_fifo = hospi->Init.FifoThreshold;
    if (Size < orig_fifo) {
        hospi->Init.FifoThreshold = 1;
        HAL_OSPI_Init(hospi);
    }

    if (HAL_OSPI_Command(hospi, &sCommand, XT25F_OSPI_TIMEOUT) != HAL_OK) {
        if (Size < orig_fifo) {
            hospi->Init.FifoThreshold = orig_fifo;
            HAL_OSPI_Init(hospi);
        }
        return HAL_ERROR;
    }

    if (HAL_OSPI_Transmit(hospi, pData, XT25F_OSPI_TIMEOUT) != HAL_OK) {
        if (Size < orig_fifo) {
            hospi->Init.FifoThreshold = orig_fifo;
            HAL_OSPI_Init(hospi);
        }
        return HAL_ERROR;
    }

    /* Restore original FIFO threshold */
    if (Size < orig_fifo) {
        hospi->Init.FifoThreshold = orig_fifo;
        HAL_OSPI_Init(hospi);
    }

    return HAL_OK;
}

/**
  * @brief  Quad Page Program (32H command, 1-1-4 mode)
  *         Instruction: 1 line, Address: 1 line, Data: 4 lines
  *         Note: Data must be within a single page
  * @param  hospi: OSPI handle
  * @param  pData: Pointer to data buffer
  * @param  WriteAddr: Start address to write to
  * @param  Size: Number of bytes to write (max 256 bytes per page)
  * @retval HAL status
  */
HAL_StatusTypeDef XT25F_QuadPageProgram(OSPI_HandleTypeDef *hospi, uint8_t *pData,
                                         uint32_t WriteAddr, uint32_t Size)
{
    OSPI_RegularCmdTypeDef sCommand = {0};

    if ((Size == 0) || (Size > XT25F_PAGE_SIZE)) {
        return HAL_ERROR;
    }

    /* Ensure OSPI is in ready state */
    if (XT25F_EnsureReady(hospi) != HAL_OK) {
        return HAL_ERROR;
    }

    XT25F_Command_CommonCfg(&sCommand);
    sCommand.Instruction    = XT25F_PAGE_PROGRAM_QUAD_INP_CMD;
    sCommand.AddressMode    = HAL_OSPI_ADDRESS_1_LINE;    /* Address: 1 line */
    sCommand.Address        = WriteAddr;
    sCommand.DataMode       = HAL_OSPI_DATA_4_LINES;      /* Data: 4 lines */
    sCommand.DummyCycles    = 0;
    sCommand.NbData         = Size;

    /* Temporarily set FIFO threshold to 1 to prevent timeout
     * with small transfer sizes (< original FifoThreshold) */
    uint32_t orig_fifo_q = hospi->Init.FifoThreshold;
    if (Size < orig_fifo_q) {
        hospi->Init.FifoThreshold = 1;
        HAL_OSPI_Init(hospi);
    }

    /* Send command, address, and data */
    if (HAL_OSPI_Command(hospi, &sCommand, XT25F_OSPI_TIMEOUT) != HAL_OK) {
        if (Size < orig_fifo_q) {
            hospi->Init.FifoThreshold = orig_fifo_q;
            HAL_OSPI_Init(hospi);
        }
        return HAL_ERROR;
    }

    if (HAL_OSPI_Transmit(hospi, pData, XT25F_OSPI_TIMEOUT) != HAL_OK) {
        if (Size < orig_fifo_q) {
            hospi->Init.FifoThreshold = orig_fifo_q;
            HAL_OSPI_Init(hospi);
        }
        return HAL_ERROR;
    }

    /* Restore original FIFO threshold */
    if (Size < orig_fifo_q) {
        hospi->Init.FifoThreshold = orig_fifo_q;
        HAL_OSPI_Init(hospi);
    }

    return HAL_OK;
}

/* ========================================================================== */
/*                           Erase Functions                                  */
/* ========================================================================== */

/**
  * @brief  Erase a 4KB sector
  * @param  hospi: OSPI handle
  * @param  SectorAddress: Address within the sector to erase
  * @retval HAL status
  */
HAL_StatusTypeDef XT25F_EraseSector(OSPI_HandleTypeDef *hospi, uint32_t SectorAddress)
{
    OSPI_RegularCmdTypeDef sCommand = {0};

    /* Enable write operations */
    if (XT25F_WriteEnable(hospi) != HAL_OK) {
        return HAL_ERROR;
    }

    XT25F_Command_CommonCfg(&sCommand);
    sCommand.Instruction    = XT25F_SECTOR_ERASE_CMD;
    sCommand.AddressMode    = HAL_OSPI_ADDRESS_1_LINE;
    sCommand.Address        = SectorAddress & 0xFFFFFFU;
    sCommand.DataMode       = HAL_OSPI_DATA_NONE;
    sCommand.DummyCycles    = 0;
    sCommand.NbData         = 0;

    if (HAL_OSPI_Command(hospi, &sCommand, XT25F_OSPI_TIMEOUT) != HAL_OK) {
        return HAL_ERROR;
    }

    /* Wait for erase operation to complete */
    if (XT25F_AutoPollingMemReady(hospi) != HAL_OK) {
        return HAL_ERROR;
    }

    return HAL_OK;
}

/**
  * @brief  Erase a 32KB block
  * @param  hospi: OSPI handle
  * @param  BlockAddress: Address within the block to erase
  * @retval HAL status
  */
HAL_StatusTypeDef XT25F_EraseBlock32K(OSPI_HandleTypeDef *hospi, uint32_t BlockAddress)
{
    OSPI_RegularCmdTypeDef sCommand = {0};

    /* Enable write operations */
    if (XT25F_WriteEnable(hospi) != HAL_OK) {
        return HAL_ERROR;
    }

    XT25F_Command_CommonCfg(&sCommand);
    sCommand.Instruction    = XT25F_32KB_BLOCK_ERASE_CMD;
    sCommand.AddressMode    = HAL_OSPI_ADDRESS_1_LINE;
    sCommand.Address        = BlockAddress & 0xFFFFFFU;
    sCommand.DataMode       = HAL_OSPI_DATA_NONE;
    sCommand.DummyCycles    = 0;
    sCommand.NbData         = 0;

    if (HAL_OSPI_Command(hospi, &sCommand, XT25F_OSPI_TIMEOUT) != HAL_OK) {
        return HAL_ERROR;
    }

    /* Wait for erase operation to complete */
    if (XT25F_AutoPollingMemReady(hospi) != HAL_OK) {
        return HAL_ERROR;
    }

    return HAL_OK;
}

/**
  * @brief  Erase a 64KB block
  * @param  hospi: OSPI handle
  * @param  BlockAddress: Address within the block to erase
  * @retval HAL status
  */
HAL_StatusTypeDef XT25F_EraseBlock64K(OSPI_HandleTypeDef *hospi, uint32_t BlockAddress)
{
    OSPI_RegularCmdTypeDef sCommand = {0};

    /* Enable write operations */
    if (XT25F_WriteEnable(hospi) != HAL_OK) {
        return HAL_ERROR;
    }

    XT25F_Command_CommonCfg(&sCommand);
    sCommand.Instruction    = XT25F_64KB_BLOCK_ERASE_CMD;
    sCommand.AddressMode    = HAL_OSPI_ADDRESS_1_LINE;
    sCommand.Address        = BlockAddress & 0xFFFFFFU;
    sCommand.DataMode       = HAL_OSPI_DATA_NONE;
    sCommand.DummyCycles    = 0;
    sCommand.NbData         = 0;

    if (HAL_OSPI_Command(hospi, &sCommand, XT25F_OSPI_TIMEOUT) != HAL_OK) {
        return HAL_ERROR;
    }

    /* Wait for erase operation to complete */
    if (XT25F_AutoPollingMemReady(hospi) != HAL_OK) {
        return HAL_ERROR;
    }

    return HAL_OK;
}

/**
  * @brief  Erase the entire chip
  * @param  hospi: OSPI handle
  * @retval HAL status
  * @note   This operation can take up to 100 seconds
  */
HAL_StatusTypeDef XT25F_EraseChip(OSPI_HandleTypeDef *hospi)
{
    OSPI_RegularCmdTypeDef sCommand = {0};

    /* Enable write operations */
    if (XT25F_WriteEnable(hospi) != HAL_OK) {
        return HAL_ERROR;
    }

    XT25F_Command_CommonCfg(&sCommand);
    sCommand.Instruction    = XT25F_CHIP_ERASE_CMD;
    sCommand.AddressMode    = HAL_OSPI_ADDRESS_NONE;
    sCommand.Address        = 0;
    sCommand.DataMode       = HAL_OSPI_DATA_NONE;
    sCommand.DummyCycles    = 0;
    sCommand.NbData         = 0;

    if (HAL_OSPI_Command(hospi, &sCommand, XT25F_OSPI_TIMEOUT) != HAL_OK) {
        return HAL_ERROR;
    }

    /* Wait for erase operation to complete */
    if (XT25F_AutoPollingMemReady(hospi) != HAL_OK) {
        return HAL_ERROR;
    }

    return HAL_OK;
}

/* ========================================================================== */
/*                      Status Register Functions                             */
/* ========================================================================== */

/**
  * @brief  Read Status Register
  * @param  hospi: OSPI handle
  * @param  pData: Pointer to store register value
  * @param  RegNum: Register number (1, 2, or 3)
  * @retval HAL status
  */
HAL_StatusTypeDef XT25F_ReadStatusRegister(OSPI_HandleTypeDef *hospi,
                                            uint8_t *pData, uint8_t RegNum)
{
    OSPI_RegularCmdTypeDef sCommand = {0};

    XT25F_Command_CommonCfg(&sCommand);
    sCommand.AddressMode    = HAL_OSPI_ADDRESS_NONE;
    sCommand.Address        = 0;
    sCommand.DataMode       = HAL_OSPI_DATA_1_LINE;
    sCommand.DummyCycles    = 0;
    sCommand.NbData         = 1;

    switch (RegNum) {
        case 1:
            sCommand.Instruction = XT25F_READ_SR1_CMD;
            break;
        case 2:
            sCommand.Instruction = XT25F_READ_SR2_CMD;
            break;
        case 3:
            sCommand.Instruction = XT25F_READ_SR3_CMD;
            break;
        default:
            return HAL_ERROR;
    }

    if (HAL_OSPI_Command(hospi, &sCommand, XT25F_OSPI_TIMEOUT) != HAL_OK) {
        return HAL_ERROR;
    }

    if (HAL_OSPI_Receive(hospi, pData, XT25F_OSPI_TIMEOUT) != HAL_OK) {
        return HAL_ERROR;
    }

    return HAL_OK;
}

/**
  * @brief  Write Status Register
  * @param  hospi: OSPI handle
  * @param  Value: Value to write to the register
  * @param  RegNum: Register number (1, 2, or 3)
  * @retval HAL status
  */
HAL_StatusTypeDef XT25F_WriteStatusRegister(OSPI_HandleTypeDef *hospi,
                                             uint8_t Value, uint8_t RegNum)
{
    OSPI_RegularCmdTypeDef sCommand = {0};
    uint8_t cmd = 0;

    switch (RegNum) {
        case 1:
            cmd = XT25F_WRITE_SR1_CMD;
            break;
        case 2:
            cmd = XT25F_WRITE_SR2_CMD;
            break;
        case 3:
            cmd = XT25F_WRITE_SR3_CMD;
            break;
        default:
            return HAL_ERROR;
    }

    /* Enable Volatile Status Register Write (50H command) */
    XT25F_Command_CommonCfg(&sCommand);
    sCommand.Instruction    = XT25F_ENABLE_VOLATILE_SR_CMD;
    sCommand.AddressMode    = HAL_OSPI_ADDRESS_NONE;
    sCommand.Address        = 0;
    sCommand.DataMode       = HAL_OSPI_DATA_NONE;
    sCommand.DummyCycles    = 0;
    sCommand.NbData         = 0;

    if (HAL_OSPI_Command(hospi, &sCommand, XT25F_OSPI_TIMEOUT) != HAL_OK) {
        return HAL_ERROR;
    }

    /* Write Enable */
    if (XT25F_WriteEnable(hospi) != HAL_OK) {
        return HAL_ERROR;
    }

    /* Write Status Register */
    sCommand.Instruction    = cmd;
    sCommand.DataMode       = HAL_OSPI_DATA_1_LINE;
    sCommand.NbData         = 1;

    if (HAL_OSPI_Command(hospi, &sCommand, XT25F_OSPI_TIMEOUT) != HAL_OK) {
        return HAL_ERROR;
    }

    if (HAL_OSPI_Transmit(hospi, &Value, XT25F_OSPI_TIMEOUT) != HAL_OK) {
        return HAL_ERROR;
    }

    /* Wait for write operation to complete */
    if (XT25F_AutoPollingMemReady(hospi) != HAL_OK) {
        return HAL_ERROR;
    }

    return HAL_OK;
}

/* ========================================================================== */
/*                      Memory Mapped Mode Functions                          */
/* ========================================================================== */

/**
  * @brief  Enable Memory-Mapped mode for Quad I/O Fast Read
  *         After enabling, Flash can be accessed like internal memory at address 0x90000000
  * @param  hospi: OSPI handle
  * @retval HAL status
  */
HAL_StatusTypeDef XT25F_EnableMemoryMappedMode(OSPI_HandleTypeDef *hospi)
{
    OSPI_RegularCmdTypeDef sCommand = {0};
    OSPI_MemoryMappedTypeDef sMemMappedCfg = {0};

    /* Configure Read Configuration for Memory-Mapped Mode */
    XT25F_Command_ReadCfg(&sCommand);
    sCommand.Instruction    = XT25F_FAST_READ_QUAD_IO_CMD;
    sCommand.AddressMode    = HAL_OSPI_ADDRESS_4_LINES;   /* Address: 4 lines */
    sCommand.Address        = 0;
    sCommand.DataMode       = HAL_OSPI_DATA_4_LINES;      /* Data: 4 lines */
    sCommand.DummyCycles    = XT25F_DUMMY_CYCLES_READ_QUAD;  /* 6 dummy cycles */
    sCommand.NbData         = 0;

    if (HAL_OSPI_Command(hospi, &sCommand, XT25F_OSPI_TIMEOUT) != HAL_OK) {
        return HAL_ERROR;
    }

    /* Configure Write Configuration for Memory-Mapped Mode */
    XT25F_Command_WriteCfg(&sCommand);
    sCommand.Instruction    = XT25F_PAGE_PROGRAM_QUAD_INP_CMD;
    sCommand.AddressMode    = HAL_OSPI_ADDRESS_1_LINE;    /* Address: 1 line */
    sCommand.Address        = 0;
    sCommand.DataMode       = HAL_OSPI_DATA_4_LINES;      /* Data: 4 lines */
    sCommand.DummyCycles    = 0;
    sCommand.NbData         = 0;

    if (HAL_OSPI_Command(hospi, &sCommand, XT25F_OSPI_TIMEOUT) != HAL_OK) {
        return HAL_ERROR;
    }

    /* Initialize Memory Mapped Mode */
    sMemMappedCfg.TimeOutActivation = HAL_OSPI_TIMEOUT_COUNTER_DISABLE;

    if (HAL_OSPI_MemoryMapped(hospi, &sMemMappedCfg) != HAL_OK) {
        return HAL_ERROR;
    }

    return HAL_OK;
}

/**
  * @brief  Disable Memory-Mapped mode and return to indirect mode
  * @param  hospi: OSPI handle
  * @retval HAL status
  */
HAL_StatusTypeDef XT25F_DisableMemoryMappedMode(OSPI_HandleTypeDef *hospi)
{
    /* Abort any ongoing operation to exit memory-mapped mode */
    if (HAL_OSPI_Abort(hospi) != HAL_OK) {
        return HAL_ERROR;
    }

    return HAL_OK;
}

/* ========================================================================== */
/*                      Polling / Status Functions                            */
/* ========================================================================== */

/**
  * @brief  Auto-polling to wait for Flash to be ready (WIP bit = 0)
  * @param  hospi: OSPI handle
  * @retval HAL status
  */
HAL_StatusTypeDef XT25F_AutoPollingMemReady(OSPI_HandleTypeDef *hospi)
{
    OSPI_RegularCmdTypeDef sCommand = {0};
    OSPI_AutoPollingTypeDef sConfig = {0};

    XT25F_Command_CommonCfg(&sCommand);
    sCommand.Instruction    = XT25F_READ_SR1_CMD;
    sCommand.AddressMode    = HAL_OSPI_ADDRESS_NONE;
    sCommand.Address        = 0;
    sCommand.DataMode       = HAL_OSPI_DATA_1_LINE;
    sCommand.DummyCycles    = 0;
    sCommand.NbData         = 1;

    if (HAL_OSPI_Command(hospi, &sCommand, XT25F_OSPI_TIMEOUT) != HAL_OK) {
        return HAL_ERROR;
    }

    sConfig.Match           = 0x00U;
    sConfig.Mask            = XT25F_SR1_WIP;
    sConfig.MatchMode       = HAL_OSPI_MATCH_MODE_AND;
    sConfig.Interval        = XT25F_AUTOPOLLING_INTERVAL_TIME;
    sConfig.AutomaticStop   = HAL_OSPI_AUTOMATIC_STOP_ENABLE;

    if (HAL_OSPI_AutoPolling(hospi, &sConfig, XT25F_OSPI_TIMEOUT) != HAL_OK) {
        return HAL_ERROR;
    }

    return HAL_OK;
}

/**
  * @brief  Send Write Enable command
  * @param  hospi: OSPI handle
  * @retval HAL status
  */
HAL_StatusTypeDef XT25F_WriteEnable(OSPI_HandleTypeDef *hospi)
{
    OSPI_RegularCmdTypeDef sCommand = {0};
    OSPI_AutoPollingTypeDef sConfig = {0};

    /* Ensure OSPI is in ready state */
    if (XT25F_EnsureReady(hospi) != HAL_OK) {
        return HAL_ERROR;
    }

    /* Send Write Enable Command */
    XT25F_Command_CommonCfg(&sCommand);
    sCommand.Instruction    = XT25F_WRITE_ENABLE_CMD;
    sCommand.AddressMode    = HAL_OSPI_ADDRESS_NONE;
    sCommand.Address        = 0;
    sCommand.DataMode       = HAL_OSPI_DATA_NONE;
    sCommand.DummyCycles    = 0;
    sCommand.NbData         = 0;

    if (HAL_OSPI_Command(hospi, &sCommand, XT25F_OSPI_TIMEOUT) != HAL_OK) {
        return HAL_ERROR;
    }

    /* Read Status Register 1 and poll for WEL bit set */
    sCommand.Instruction = XT25F_READ_SR1_CMD;
    sCommand.DataMode    = HAL_OSPI_DATA_1_LINE;
    sCommand.NbData      = 1;

    if (HAL_OSPI_Command(hospi, &sCommand, XT25F_OSPI_TIMEOUT) != HAL_OK) {
        return HAL_ERROR;
    }

    sConfig.Match           = XT25F_SR1_WEL;   /* WEL bit = 1 */
    sConfig.Mask            = XT25F_SR1_WEL;
    sConfig.MatchMode       = HAL_OSPI_MATCH_MODE_AND;
    sConfig.Interval        = XT25F_AUTOPOLLING_INTERVAL_TIME;
    sConfig.AutomaticStop   = HAL_OSPI_AUTOMATIC_STOP_ENABLE;

    if (HAL_OSPI_AutoPolling(hospi, &sConfig, XT25F_OSPI_TIMEOUT) != HAL_OK) {
        return HAL_ERROR;
    }

    return HAL_OK;
}

/**
  * @brief  Check if Flash is busy (WIP bit)
  * @param  hospi: OSPI handle
  * @retval HAL_OK if not busy, HAL_ERROR if busy
  */
HAL_StatusTypeDef XT25F_IsBusy(OSPI_HandleTypeDef *hospi)
{
    uint8_t status_register = 0;

    if (XT25F_ReadStatusRegister(hospi, &status_register, 1) != HAL_OK) {
        return HAL_ERROR;
    }

    status_register = status_register & XT25F_SR1_WIP;

    return (status_register ? HAL_ERROR : HAL_OK);
}

/* ========================================================================== */
/*                          Power Control Functions                           */
/* ========================================================================== */

/**
  * @brief  Enter Deep Power-Down mode
  * @param  hospi: OSPI handle
  * @retval HAL status
  */
HAL_StatusTypeDef XT25F_EnterPowerDown(OSPI_HandleTypeDef *hospi)
{
    OSPI_RegularCmdTypeDef sCommand = {0};

    XT25F_Command_CommonCfg(&sCommand);
    sCommand.Instruction    = XT25F_POWERDOWN_CMD;
    sCommand.AddressMode    = HAL_OSPI_ADDRESS_NONE;
    sCommand.Address        = 0;
    sCommand.DataMode       = HAL_OSPI_DATA_NONE;
    sCommand.DummyCycles    = 0;
    sCommand.NbData         = 0;

    if (HAL_OSPI_Command(hospi, &sCommand, XT25F_OSPI_TIMEOUT) != HAL_OK) {
        return HAL_ERROR;
    }

    return HAL_OK;
}

/**
  * @brief  Release from Deep Power-Down mode
  * @param  hospi: OSPI handle
  * @retval HAL status
  */
HAL_StatusTypeDef XT25F_ReleasePowerDown(OSPI_HandleTypeDef *hospi)
{
    OSPI_RegularCmdTypeDef sCommand = {0};

    XT25F_Command_CommonCfg(&sCommand);
    sCommand.Instruction    = XT25F_POWERUP_CMD;
    sCommand.AddressMode    = HAL_OSPI_ADDRESS_NONE;
    sCommand.Address        = 0;
    sCommand.DataMode       = HAL_OSPI_DATA_NONE;
    sCommand.DummyCycles    = 0;
    sCommand.NbData         = 0;

    if (HAL_OSPI_Command(hospi, &sCommand, XT25F_OSPI_TIMEOUT) != HAL_OK) {
        return HAL_ERROR;
    }

    HAL_Delay(1);  /* Wait for device to wake up */

    return HAL_OK;
}

3.3 主函数

XT25F_Init(&hospi1);
  XT25F_EnableQuadMode(&hospi1);
  XT25F_WriteEnable(&hospi1);
  uint8_t flash_data[] = {0x55,0x5a,0xa5,0xaa};
  XT25F_EraseSector(&hospi1, 0x000000);
  XT25F_Write(&hospi1,flash_data,0x000000,4);
  XT25F_EnableMemoryMappedMode(&hospi1);
  uint8_t* ptr = (uint8_t *)(XT25F_MEMORY_BASE_ADDR);
  printf("%02X\n\r",*ptr);
  printf("%02X\n\r",*++ptr);
  printf("%02X\n\r",*++ptr);
  printf("%02X\n\r",*++ptr);

4 注意事项

1、写入数据前进行写使能
2、时钟配置不能只看宣传页的最高频率,得看所有模式下的最低频率
3、Chip Select High Time必须大于数据电平跳变的时间
4、浮栅晶体管存储单元特性必须先擦除再写入,擦除后所有的位为高电平

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐