Goyoyo_Modules/01_Code/Goyoyo_1/src/App_Flash.c
2025-04-09 13:19:26 +05:30

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, &reg) != 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, &reg,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, &reg) != 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 */
/* */
/*****************************************************************************/