673 lines
23 KiB
C
673 lines
23 KiB
C
/*****************************************************************************/
|
|
/* Copyright (C) 2024-25 Sathish Kumar P - All Rights Reserved */
|
|
/* You may use, distribute and modify this code under the */
|
|
/* terms of the XYZ license, which unfortunately wont be */
|
|
/* written for another century. */
|
|
/* */
|
|
/* You should have received a copy of the XYZ license with */
|
|
/* this file. If not, please write to: sathishembeddedgeek@gmail.com, */
|
|
/* or visit : */
|
|
/*****************************************************************************/
|
|
|
|
/*****************************************************************************/
|
|
/* FILENAME : App_Flash.c */
|
|
/* */
|
|
/* DESCRIPTION : Application flash interface */
|
|
/* */
|
|
/* NOTES : Copyright Sathish Kumar P. All rights reserved. */
|
|
/* */
|
|
/* AUTHOR : Sathish Kumar */
|
|
/* sathishembeddedgeek@gmail.com */
|
|
/* */
|
|
/* START DATE : 20th December 2024 */
|
|
/* */
|
|
/* VERSION DATE WHO DETAIL */
|
|
/* 00.00.01 20DEC24 Sathish Kumar initial version */
|
|
/* */
|
|
/*****************************************************************************/
|
|
|
|
/*****************************************************************************/
|
|
/* */
|
|
/* I N C L U D E S */
|
|
/* */
|
|
/*****************************************************************************/
|
|
#include "App_Flash.h"
|
|
|
|
/* FES Common Lib Headers.*/
|
|
#include <Log.h>
|
|
|
|
#if (HW_TYPE == HW_DEV_BRD)
|
|
#include "n25q128a.h"
|
|
#elif (HW_TYPE == HW_OWN_BRD)
|
|
#warning "Invalid HW Selected!!!"
|
|
#else
|
|
#warning "Invalid HW Selected!!!"
|
|
#endif
|
|
|
|
/*****************************************************************************/
|
|
/* */
|
|
/* D E F I N I T I O N S */
|
|
/* */
|
|
/*****************************************************************************/
|
|
|
|
/*****************************************************************************/
|
|
/* */
|
|
/* C O N S T A N T S & V A R I A B L E S */
|
|
/* */
|
|
/*****************************************************************************/
|
|
|
|
static volatile uint8_t cmd_it_cb_flag;
|
|
static volatile uint8_t rx_it_cb_flag;
|
|
static volatile uint8_t tx_it_cb_flag;
|
|
static volatile uint8_t status_it_cb_flag;
|
|
|
|
QSPI_CommandTypeDef s_command;
|
|
|
|
/*****************************************************************************/
|
|
/* */
|
|
/* F U N C T I O N P R O T O T Y P E S */
|
|
/* */
|
|
/*****************************************************************************/
|
|
static uint8_t QSPI_ResetMemory(QSPI_HandleTypeDef *hqspi);
|
|
static uint8_t QSPI_DummyCyclesCfg(QSPI_HandleTypeDef *hqspi);
|
|
static uint8_t QSPI_WriteEnable(QSPI_HandleTypeDef *hqspi);
|
|
static uint8_t QSPI_AutoPollingMemReady(QSPI_HandleTypeDef *hqspi, uint32_t Timeout);
|
|
|
|
static inline uint8_t QSPI_SendCmd(QSPI_HandleTypeDef *hqspi,QSPI_CommandTypeDef *s_cmd);
|
|
static inline uint8_t QSPI_TxData(QSPI_HandleTypeDef *hqspi,uint8_t *tx_data);
|
|
static inline uint8_t QSPI_RxData(QSPI_HandleTypeDef *hqspi,uint8_t *rx_data);
|
|
static inline uint8_t QSPI_AutoPolling(QSPI_HandleTypeDef *hqspi,QSPI_CommandTypeDef *s_cmd,QSPI_AutoPollingTypeDef *s_cfg,uint32_t timeout);
|
|
static uint8_t QSPI_EnableQSPIMode(QSPI_HandleTypeDef *hqspi);
|
|
|
|
/*****************************************************************************/
|
|
/* */
|
|
/* F U N C T I O N S */
|
|
/* */
|
|
/*****************************************************************************/
|
|
|
|
/*****************************************************************************/
|
|
/* See header file of description */
|
|
/*****************************************************************************/
|
|
uint8_t App_Flash_Init()
|
|
{
|
|
/* QSPI memory reset */
|
|
if (QSPI_ResetMemory(FLASH_QSPI_HDL) != 0)
|
|
{
|
|
return HAL_ERROR;
|
|
}
|
|
|
|
if (QSPI_EnableQSPIMode(FLASH_QSPI_HDL) != 0)
|
|
{
|
|
return HAL_ERROR;
|
|
}
|
|
|
|
/* Configuration of the dummy cycles on QSPI memory side */
|
|
if (QSPI_DummyCyclesCfg(FLASH_QSPI_HDL) != 0)
|
|
{
|
|
return HAL_ERROR;
|
|
}
|
|
|
|
return HAL_OK;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* See header file of description */
|
|
/*****************************************************************************/
|
|
uint8_t App_Flash_HW_Test()
|
|
{
|
|
uint8_t writebuf[] = "Hello world from QSPI";
|
|
uint8_t readbuf[50];
|
|
uint16_t wrlen = strlen((char*)writebuf);
|
|
|
|
if(App_Flash_Erase_Block(0)!=0)
|
|
{
|
|
LOG(LOG_ERROR, "App_Flash_Erase_Block Failed !!!");
|
|
return 1;
|
|
}
|
|
if(App_Flash_Write(writebuf, 0, wrlen)!=0)
|
|
{
|
|
LOG(LOG_ERROR, "App_Flash_Write Failed !!!");
|
|
return 2;
|
|
}
|
|
if(App_Flash_Read(readbuf, 0,wrlen)!=0)
|
|
{
|
|
LOG(LOG_ERROR, "App_Flash_Read Failed !!!");
|
|
return 3;
|
|
}
|
|
|
|
if(strncmp((char*)readbuf,(char*)writebuf,wrlen)!=0)
|
|
{
|
|
LOG(LOG_ERROR, "content mismatch Failed !!!");
|
|
return 3;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* See header file of description */
|
|
/*****************************************************************************/
|
|
uint8_t App_Flash_Read(uint8_t* pData, uint32_t ReadAddr, uint32_t Size)
|
|
{
|
|
#if (HW_TYPE == HW_DEV_BRD)
|
|
|
|
/* Initialize the read command */
|
|
s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
|
|
s_command.Instruction = QUAD_INOUT_FAST_READ_CMD;
|
|
s_command.AddressMode = QSPI_ADDRESS_4_LINES;
|
|
s_command.AddressSize = QSPI_ADDRESS_24_BITS;
|
|
s_command.Address = ReadAddr;
|
|
s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
|
|
s_command.DataMode = QSPI_DATA_4_LINES;
|
|
s_command.DummyCycles = N25Q128A_DUMMY_CYCLES_READ_QUAD;
|
|
s_command.NbData = Size;
|
|
s_command.DdrMode = QSPI_DDR_MODE_DISABLE;
|
|
s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
|
|
s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
|
|
|
|
/* Configure the command */
|
|
if (QSPI_SendCmd(FLASH_QSPI_HDL, &s_command) != HAL_OK)
|
|
{
|
|
return HAL_ERROR;
|
|
}
|
|
|
|
/* Set S# timing for Read command: Min 20ns for N25Q128A memory */
|
|
MODIFY_REG(hqspi.Instance->DCR, QUADSPI_DCR_CSHT, QSPI_CS_HIGH_TIME_2_CYCLE);
|
|
|
|
/* Reception of the data */
|
|
if (QSPI_RxData(FLASH_QSPI_HDL, pData) != HAL_OK)
|
|
{
|
|
return HAL_ERROR;
|
|
}
|
|
|
|
/* Restore S# timing for nonRead commands */
|
|
MODIFY_REG(hqspi.Instance->DCR, QUADSPI_DCR_CSHT, QSPI_CS_HIGH_TIME_5_CYCLE);
|
|
|
|
return HAL_OK;
|
|
|
|
#else
|
|
return HAL_ERROR;
|
|
#endif
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* See header file of description */
|
|
/*****************************************************************************/
|
|
uint8_t App_Flash_Write(uint8_t* pData, uint32_t WriteAddr, uint32_t Size)
|
|
{
|
|
|
|
uint32_t end_addr, current_size, current_addr;
|
|
|
|
/* Calculation of the size between the write address and the end of the page */
|
|
current_size = APP_FLASH_PAGE_SIZE - (WriteAddr % APP_FLASH_PAGE_SIZE);
|
|
|
|
/* Check if the size of the data is less than the remaining place in the page */
|
|
if (current_size > Size)
|
|
{
|
|
current_size = Size;
|
|
}
|
|
|
|
/* Initialize the address variables */
|
|
current_addr = WriteAddr;
|
|
end_addr = WriteAddr + Size;
|
|
|
|
#if (HW_TYPE == HW_DEV_BRD)
|
|
/* Initialize the program command */
|
|
s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
|
|
s_command.Instruction = EXT_QUAD_IN_FAST_PROG_CMD;
|
|
s_command.AddressMode = QSPI_ADDRESS_4_LINES;
|
|
s_command.AddressSize = QSPI_ADDRESS_24_BITS;
|
|
s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
|
|
s_command.DataMode = QSPI_DATA_4_LINES;
|
|
s_command.DummyCycles = 0;
|
|
s_command.DdrMode = QSPI_DDR_MODE_DISABLE;
|
|
s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
|
|
s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
|
|
#endif
|
|
|
|
/* Perform the write page by page */
|
|
do
|
|
{
|
|
#if (HW_TYPE == HW_OWN_BRD)
|
|
#else
|
|
s_command.Address = current_addr;
|
|
#endif
|
|
|
|
|
|
s_command.NbData = current_size;
|
|
|
|
/* Enable write operations */
|
|
if (QSPI_WriteEnable(FLASH_QSPI_HDL) != HAL_OK)
|
|
{
|
|
return HAL_ERROR;
|
|
}
|
|
|
|
/* Configure the command */
|
|
if (QSPI_SendCmd(FLASH_QSPI_HDL, &s_command) != HAL_OK)
|
|
{
|
|
return HAL_ERROR;
|
|
}
|
|
|
|
/* Transmission of the data */
|
|
if (QSPI_TxData(FLASH_QSPI_HDL, pData) != HAL_OK)
|
|
{
|
|
return HAL_ERROR;
|
|
}
|
|
|
|
/* Configure automatic polling mode to wait for end of program */
|
|
if (QSPI_AutoPollingMemReady(FLASH_QSPI_HDL, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != 0)
|
|
{
|
|
return HAL_ERROR;
|
|
}
|
|
|
|
/* Update the address and size variables for next page programming */
|
|
current_addr += current_size;
|
|
pData += current_size;
|
|
current_size = ((current_addr + APP_FLASH_PAGE_SIZE) > end_addr) ? (end_addr - current_addr) : APP_FLASH_PAGE_SIZE;
|
|
} while (current_addr < end_addr);
|
|
|
|
return HAL_OK;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* See header file of description */
|
|
/*****************************************************************************/
|
|
uint8_t App_Flash_Erase_Block(uint32_t BlockAddress)
|
|
{
|
|
#if (HW_TYPE == HW_DEV_BRD)
|
|
/* Initialize the erase command */
|
|
s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
|
|
s_command.Instruction = SUBSECTOR_ERASE_CMD;
|
|
s_command.AddressMode = QSPI_ADDRESS_1_LINE;
|
|
s_command.AddressSize = QSPI_ADDRESS_24_BITS;
|
|
s_command.Address = BlockAddress;
|
|
s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
|
|
s_command.DataMode = QSPI_DATA_NONE;
|
|
s_command.DummyCycles = 0;
|
|
s_command.DdrMode = QSPI_DDR_MODE_DISABLE;
|
|
s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
|
|
s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
|
|
|
|
/* Enable write operations */
|
|
if (QSPI_WriteEnable(FLASH_QSPI_HDL) != HAL_OK)
|
|
{
|
|
return HAL_ERROR;
|
|
}
|
|
|
|
/* Send the command */
|
|
if (QSPI_SendCmd(FLASH_QSPI_HDL, &s_command) != HAL_OK)
|
|
{
|
|
return HAL_ERROR;
|
|
}
|
|
|
|
/* Configure automatic polling mode to wait for end of erase */
|
|
if (QSPI_AutoPollingMemReady(FLASH_QSPI_HDL, N25Q128A_SUBSECTOR_ERASE_MAX_TIME) != 0)
|
|
{
|
|
return HAL_ERROR;
|
|
}
|
|
return HAL_OK;
|
|
#elif (HW_TYPE == HW_OWN_BRD)
|
|
return HAL_ERROR;
|
|
#else
|
|
return HAL_ERROR;
|
|
#endif
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* See header file of description */
|
|
/*****************************************************************************/
|
|
uint8_t App_Flash_Erase_Chip(void)
|
|
{
|
|
#if (HW_TYPE == HW_DEV_BRD)
|
|
|
|
/* Initialize the erase command */
|
|
s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
|
|
s_command.Instruction = BULK_ERASE_CMD;
|
|
s_command.AddressMode = QSPI_ADDRESS_NONE;
|
|
s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
|
|
s_command.DataMode = QSPI_DATA_NONE;
|
|
s_command.DummyCycles = 0;
|
|
s_command.DdrMode = QSPI_DDR_MODE_DISABLE;
|
|
s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
|
|
s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
|
|
|
|
/* Enable write operations */
|
|
if (QSPI_WriteEnable(FLASH_QSPI_HDL) != 0)
|
|
{
|
|
return HAL_ERROR;
|
|
}
|
|
|
|
/* Send the command */
|
|
if (QSPI_SendCmd(FLASH_QSPI_HDL, &s_command) != HAL_OK)
|
|
{
|
|
return HAL_ERROR;
|
|
}
|
|
|
|
/* Configure automatic polling mode to wait for end of erase */
|
|
if (QSPI_AutoPollingMemReady(FLASH_QSPI_HDL, N25Q128A_BULK_ERASE_MAX_TIME) != 0)
|
|
{
|
|
return HAL_ERROR;
|
|
}
|
|
|
|
return HAL_OK;
|
|
#else
|
|
return HAL_ERROR;
|
|
#endif
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* See header file of description */
|
|
/*****************************************************************************/
|
|
uint8_t App_Flash_GetStatus(void)
|
|
{
|
|
#if (HW_TYPE == HW_DEV_BRD)
|
|
|
|
uint8_t reg;
|
|
|
|
/* Initialize the read flag status register command */
|
|
s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
|
|
s_command.Instruction = READ_FLAG_STATUS_REG_CMD;
|
|
s_command.AddressMode = QSPI_ADDRESS_NONE;
|
|
s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
|
|
s_command.DataMode = QSPI_DATA_1_LINE;
|
|
s_command.DummyCycles = 0;
|
|
s_command.NbData = 1;
|
|
s_command.DdrMode = QSPI_DDR_MODE_DISABLE;
|
|
s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
|
|
s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
|
|
|
|
/* Configure the command */
|
|
if (QSPI_SendCmd(FLASH_QSPI_HDL, &s_command) != HAL_OK)
|
|
{
|
|
return HAL_ERROR;
|
|
}
|
|
|
|
/* Reception of the data */
|
|
if (QSPI_RxData(FLASH_QSPI_HDL, ®) != HAL_OK)
|
|
{
|
|
return HAL_ERROR;
|
|
}
|
|
|
|
/* Check the value of the register */
|
|
if ((reg & (N25Q128A_FSR_PRERR | N25Q128A_FSR_VPPERR | N25Q128A_FSR_PGERR | N25Q128A_FSR_ERERR)) != 0)
|
|
{
|
|
return HAL_ERROR;
|
|
}
|
|
else if ((reg & (N25Q128A_FSR_PGSUS | N25Q128A_FSR_ERSUS)) != 0)
|
|
{
|
|
return HAL_TIMEOUT;
|
|
}
|
|
else if ((reg & N25Q128A_FSR_READY) != 0)
|
|
{
|
|
return HAL_OK;
|
|
}
|
|
else
|
|
{
|
|
return HAL_BUSY;
|
|
}
|
|
|
|
#elif (HW_TYPE == HW_OWN_BRD)
|
|
return HAL_ERROR;
|
|
#else
|
|
return HAL_ERROR;
|
|
#endif
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* See header file of description */
|
|
/*****************************************************************************/
|
|
uint8_t App_Flash_GetInfo(Flash_Info* pInfo)
|
|
{
|
|
/* Configure the structure with the memory configuration */
|
|
pInfo->FlashSize = APP_FLASH_SIZE;
|
|
pInfo->EraseSectorSize = APP_FLASH_SUBSECTOR_SIZE;
|
|
pInfo->EraseSectorsNumber = (APP_FLASH_SIZE/APP_FLASH_SUBSECTOR_SIZE);
|
|
pInfo->ProgPageSize = APP_FLASH_PAGE_SIZE;
|
|
pInfo->ProgPagesNumber = (APP_FLASH_SIZE/APP_FLASH_PAGE_SIZE);
|
|
|
|
return HAL_OK;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* See header file of description */
|
|
/*****************************************************************************/
|
|
static inline uint8_t QSPI_SendCmd(QSPI_HandleTypeDef *hqspi,QSPI_CommandTypeDef *s_cmd)
|
|
{
|
|
return HAL_QSPI_Command(hqspi, s_cmd,HAL_QPSI_TIMEOUT_DEFAULT_VALUE);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* See header file of description */
|
|
/*****************************************************************************/
|
|
static inline uint8_t QSPI_TxData(QSPI_HandleTypeDef *hqspi,uint8_t *tx_data)
|
|
{
|
|
return HAL_QSPI_Transmit(hqspi, tx_data,HAL_QPSI_TIMEOUT_DEFAULT_VALUE);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* See header file of description */
|
|
/*****************************************************************************/
|
|
static inline uint8_t QSPI_RxData(QSPI_HandleTypeDef *hqspi,uint8_t *rx_data)
|
|
{
|
|
return HAL_QSPI_Receive(hqspi, rx_data,HAL_QPSI_TIMEOUT_DEFAULT_VALUE);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* See header file of description */
|
|
/*****************************************************************************/
|
|
static inline uint8_t QSPI_AutoPolling(QSPI_HandleTypeDef *hqspi,QSPI_CommandTypeDef *s_cmd,QSPI_AutoPollingTypeDef *s_cfg,uint32_t timeout)
|
|
{
|
|
return HAL_QSPI_AutoPolling(hqspi, s_cmd, s_cfg,timeout);
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief This function reset the QSPI memory.
|
|
* @param hqspi: QSPI handle
|
|
*/
|
|
static uint8_t QSPI_EnableQSPIMode(QSPI_HandleTypeDef *hqspi)
|
|
{
|
|
#if (HW_TYPE == HW_OWN_BRD)
|
|
#else
|
|
return HAL_OK;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* @brief This function reset the QSPI memory.
|
|
* @param hqspi: QSPI handle
|
|
*/
|
|
static uint8_t QSPI_ResetMemory(QSPI_HandleTypeDef *hqspi)
|
|
{
|
|
#if (HW_TYPE == HW_DEV_BRD)
|
|
|
|
/* Initialize the reset enable command */
|
|
s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
|
|
s_command.Instruction = RESET_ENABLE_CMD;
|
|
s_command.AddressMode = QSPI_ADDRESS_NONE;
|
|
s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
|
|
s_command.DataMode = QSPI_DATA_NONE;
|
|
s_command.DummyCycles = 0;
|
|
s_command.DdrMode = QSPI_DDR_MODE_DISABLE;
|
|
s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
|
|
s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
|
|
|
|
/* Send the command */
|
|
if (QSPI_SendCmd(hqspi, &s_command) != HAL_OK)
|
|
{
|
|
return HAL_ERROR;
|
|
}
|
|
|
|
/* Send the reset memory command */
|
|
s_command.Instruction = RESET_MEMORY_CMD;
|
|
if (QSPI_SendCmd(hqspi, &s_command) != HAL_OK)
|
|
{
|
|
return HAL_ERROR;
|
|
}
|
|
|
|
/* Configure automatic polling mode to wait the memory is ready */
|
|
if (QSPI_AutoPollingMemReady(hqspi, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != 0)
|
|
{
|
|
return HAL_ERROR;
|
|
}
|
|
return HAL_OK;
|
|
#elif (HW_TYPE == HW_OWN_BRD)
|
|
|
|
return HAL_ERROR;
|
|
#else
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* @brief This function configure the dummy cycles on memory side.
|
|
* @param hqspi: QSPI handle
|
|
*/
|
|
static uint8_t QSPI_DummyCyclesCfg(QSPI_HandleTypeDef *hqspi)
|
|
{
|
|
#if (HW_TYPE == HW_DEV_BRD)
|
|
|
|
uint8_t reg;
|
|
|
|
/* Initialize the read volatile configuration register command */
|
|
s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
|
|
s_command.Instruction = READ_VOL_CFG_REG_CMD;
|
|
s_command.AddressMode = QSPI_ADDRESS_NONE;
|
|
s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
|
|
s_command.DataMode = QSPI_DATA_1_LINE;
|
|
s_command.DummyCycles = 0;
|
|
s_command.NbData = 1;
|
|
s_command.DdrMode = QSPI_DDR_MODE_DISABLE;
|
|
s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
|
|
s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
|
|
|
|
/* Configure the command */
|
|
if (QSPI_SendCmd(hqspi, &s_command) != HAL_OK)
|
|
{
|
|
return HAL_ERROR;
|
|
}
|
|
|
|
/* Reception of the data */
|
|
if (HAL_QSPI_Receive(hqspi, ®,HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
|
|
{
|
|
return HAL_ERROR;
|
|
}
|
|
|
|
/* Enable write operations */
|
|
if (QSPI_WriteEnable(hqspi) != 0)
|
|
{
|
|
return HAL_ERROR;
|
|
}
|
|
|
|
/* Update volatile configuration register (with new dummy cycles) */
|
|
s_command.Instruction = WRITE_VOL_CFG_REG_CMD;
|
|
MODIFY_REG(reg, N25Q128A_VCR_NB_DUMMY, (N25Q128A_DUMMY_CYCLES_READ_QUAD << POSITION_VAL(N25Q128A_VCR_NB_DUMMY)));
|
|
|
|
/* Configure the write volatile configuration register command */
|
|
if (QSPI_SendCmd(hqspi, &s_command) != HAL_OK)
|
|
{
|
|
return HAL_ERROR;
|
|
}
|
|
|
|
/* Transmission of the data */
|
|
if (QSPI_TxData(hqspi, ®) != HAL_OK)
|
|
{
|
|
return HAL_ERROR;
|
|
}
|
|
#endif
|
|
return HAL_OK;
|
|
}
|
|
|
|
/**
|
|
* @brief This function send a Write Enable and wait it is effective.
|
|
* @param hqspi: QSPI handle
|
|
*/
|
|
static uint8_t QSPI_WriteEnable(QSPI_HandleTypeDef *hqspi)
|
|
{
|
|
QSPI_CommandTypeDef s_command;
|
|
QSPI_AutoPollingTypeDef s_config;
|
|
|
|
/* Enable write operations */
|
|
s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
|
|
s_command.Instruction = WRITE_ENABLE_CMD;
|
|
s_command.AddressMode = QSPI_ADDRESS_NONE;
|
|
s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
|
|
s_command.DataMode = QSPI_DATA_NONE;
|
|
s_command.DummyCycles = 0;
|
|
s_command.DdrMode = QSPI_DDR_MODE_DISABLE;
|
|
s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
|
|
s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
|
|
|
|
if (QSPI_SendCmd(hqspi, &s_command) != HAL_OK)
|
|
{
|
|
return HAL_ERROR;
|
|
}
|
|
|
|
#if (HW_TYPE == HW_DEV_BRD)
|
|
/* Configure automatic polling mode to wait for write enabling */
|
|
s_config.Match = N25Q128A_SR_WREN;
|
|
s_config.Mask = N25Q128A_SR_WREN;
|
|
s_config.MatchMode = QSPI_MATCH_MODE_AND;
|
|
s_config.StatusBytesSize = 1;
|
|
s_config.Interval = 0x10;
|
|
s_config.AutomaticStop = QSPI_AUTOMATIC_STOP_ENABLE;
|
|
|
|
s_command.Instruction = READ_STATUS_REG_CMD;
|
|
s_command.DataMode = QSPI_DATA_1_LINE;
|
|
|
|
if (QSPI_AutoPolling(hqspi, &s_command, &s_config,HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
|
|
{
|
|
return HAL_ERROR;
|
|
}
|
|
|
|
return HAL_OK;
|
|
#elif (HW_TYPE == HW_OWN_BRD)
|
|
return HAL_ERROR;
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* @brief This function read the SR of the memory and wait the EOP.
|
|
* @param hqspi: QSPI handle
|
|
* @param Timeout
|
|
*/
|
|
static uint8_t QSPI_AutoPollingMemReady(QSPI_HandleTypeDef *hqspi, uint32_t Timeout)
|
|
{
|
|
QSPI_CommandTypeDef s_command;
|
|
QSPI_AutoPollingTypeDef s_config;
|
|
|
|
#if (HW_TYPE == HW_DEV_BRD)
|
|
|
|
/* Configure automatic polling mode to wait for memory ready */
|
|
s_command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
|
|
s_command.Instruction = READ_STATUS_REG_CMD;
|
|
s_command.AddressMode = QSPI_ADDRESS_NONE;
|
|
s_command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
|
|
s_command.DataMode = QSPI_DATA_1_LINE;
|
|
s_command.DummyCycles = 0;
|
|
s_command.DdrMode = QSPI_DDR_MODE_DISABLE;
|
|
s_command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
|
|
s_command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
|
|
|
|
s_config.Match = 0;
|
|
s_config.Mask = N25Q128A_SR_WIP;
|
|
s_config.MatchMode = QSPI_MATCH_MODE_AND;
|
|
s_config.StatusBytesSize = 1;
|
|
s_config.Interval = 0x10;
|
|
s_config.AutomaticStop = QSPI_AUTOMATIC_STOP_ENABLE;
|
|
|
|
#elif (HW_TYPE == HW_OWN_BRD)
|
|
|
|
#endif
|
|
if (QSPI_AutoPolling(hqspi, &s_command, &s_config,Timeout) != HAL_OK)
|
|
{
|
|
return HAL_ERROR;
|
|
}
|
|
|
|
return HAL_OK;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/* */
|
|
/* E N D O F F I L E */
|
|
/* */
|
|
/*****************************************************************************/
|