/** ****************************************************************************** * @file mx25l4006.c * @Author MCD Application Team * @brief This file provides the MX25L4006 drivers. ****************************************************************************** * @attention * * Copyright (c) 2022 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* Includes ------------------------------------------------------------------*/ #include "mx25l4006.h" /** @addtogroup BSP * @{ */ /** @addtogroup Components * @{ */ /** @defgroup MX25L4006 MX25L4006 * @{ */ #define MX25L4006_SPI_TIMEOUT 1000U /* 1000ms */ /** @defgroup MX25L4006_Exported_Functions MX25L4006 Exported Functions * @{ */ /** * @brief Get Flash information * @param pInfo pointer to Device Info structure * @retval Status * - MX25L4006_OK */ int32_t MX25L4006_GetFlashInfo(MX25L4006_Info_t *pInfo) { /* Configure the structure with the memory configuration */ pInfo->FlashSize = MX25L4006_FLASH_SIZE; pInfo->EraseSectorSize = MX25L4006_BLOCK_64K; pInfo->EraseSectorsNumber = (MX25L4006_FLASH_SIZE / MX25L4006_BLOCK_64K); pInfo->EraseSubSectorSize = MX25L4006_SECTOR_4K; pInfo->EraseSubSectorNumber = (MX25L4006_FLASH_SIZE / MX25L4006_SECTOR_4K); pInfo->EraseSubSector1Size = MX25L4006_SECTOR_4K; pInfo->EraseSubSector1Number = (MX25L4006_FLASH_SIZE / MX25L4006_SECTOR_4K); pInfo->ProgPageSize = MX25L4006_PAGE_SIZE; pInfo->ProgPagesNumber = (MX25L4006_FLASH_SIZE / MX25L4006_PAGE_SIZE); return MX25L4006_OK; } /** * @brief Wait until Write In Progress (WIP) bit is equal to 0 * @param Ctx Component object pointer * @retval Status * - MX25L4006_ERROR_AUTOPOLLING * - MX25L4006_ERROR_COMMAND * - MX25L4006_OK */ int32_t MX25L4006_AutoPollingMemReady(SPI_HandleTypeDef *Ctx) { int32_t ret = MX25L4006_OK; uint8_t statusRegister; uint8_t cmd = MX25L4006_READ_STATUS_REG_CMD; /* Send the command */ if (HAL_SPI_Transmit(Ctx, &cmd, 1U, MX25L4006_SPI_TIMEOUT) != HAL_OK) { ret = MX25L4006_ERROR_COMMAND; } else { do { if (HAL_SPI_Receive(Ctx, &statusRegister, 1U, MX25L4006_SPI_TIMEOUT) != HAL_OK) { ret = MX25L4006_ERROR_AUTOPOLLING; } } while ((ret == MX25L4006_OK) && ((statusRegister & MX25L4006_SR_WIP) != 0U)); } return ret; } /** * @brief Wait until Write Enable Latch (WEL) bit is set to 1 * @param Component object pointer * @retval Status * - MX25L4006_ERROR_RECEIVE * - MX25L4006_ERROR_COMMAND * - MX25L4006_OK */ int32_t MX25L4006_AutoPollingMemReadyToWrite(SPI_HandleTypeDef *Ctx) { int32_t ret = MX25L4006_OK; uint8_t statusRegister; uint8_t cmd = MX25L4006_READ_STATUS_REG_CMD; /* Send the command */ if (HAL_SPI_Transmit(Ctx, &cmd, 1U, MX25L4006_SPI_TIMEOUT) != HAL_OK) { ret = MX25L4006_ERROR_COMMAND; } else { do { if (HAL_SPI_Receive(Ctx, &statusRegister, 1U, MX25L4006_SPI_TIMEOUT) != HAL_OK) { ret = MX25L4006_ERROR_RECEIVE; } } while ((ret == MX25L4006_OK) && ((statusRegister & MX25L4006_SR_WREN) != MX25L4006_SR_WREN)); } return ret; } /* Read/Write Array Commands (3 Byte Address Command Set) *********************/ /** * @brief Reads an amount of data from the memory. * @param Ctx Component object pointer * @param pData Pointer to data to be read * @param ReadAddr Read start address * @param Size Size of data to read in Byte * @retval Status * - MX25L4006_ERROR_RECEIVE * - MX25L4006_ERROR_COMMAND * - MX25L4006_OK */ int32_t MX25L4006_Read(SPI_HandleTypeDef *Ctx, uint8_t *pData, uint32_t ReadAddr, uint16_t Size) { int32_t ret = MX25L4006_OK; uint8_t cmd[4U]; /* Send the command */ cmd[0U] = MX25L4006_READ_CMD; cmd[1U] = (uint8_t)((ReadAddr & 0x00FF0000U) >> 16); cmd[2U] = (uint8_t)((ReadAddr & 0x0000FF00U) >> 8); cmd[3U] = (uint8_t)(ReadAddr & 0x000000FFU); if (HAL_SPI_Transmit(Ctx, cmd, 4U, MX25L4006_SPI_TIMEOUT) != HAL_OK) { ret = MX25L4006_ERROR_COMMAND; } else { if (HAL_SPI_Receive(Ctx, pData, Size, MX25L4006_SPI_TIMEOUT) != HAL_OK) { ret = MX25L4006_ERROR_RECEIVE; } } return ret; } /** * @brief Reads an amount of data from the memory. * @param Ctx Component object pointer * @param pData Pointer to data to be read * @param ReadAddr Read start address * @param Size Size of data to read in Byte * @retval Status * - MX25L4006_ERROR_RECEIVE * - MX25L4006_ERROR_COMMAND * - MX25L4006_OK */ int32_t MX25L4006_FastRead(SPI_HandleTypeDef *Ctx, uint8_t *pData, uint32_t ReadAddr, uint16_t Size) { int32_t ret = MX25L4006_OK; uint8_t cmd[5U]; /* Send the command */ cmd[0U] = MX25L4006_FAST_READ_CMD; cmd[1U] = (uint8_t)((ReadAddr & 0x00FF0000U) >> 16); cmd[2U] = (uint8_t)((ReadAddr & 0x0000FF00U) >> 8); cmd[3U] = (uint8_t)(ReadAddr & 0x000000FFU); cmd[4U] = 0xAAU; /* Dummy */ if (HAL_SPI_Transmit(Ctx, cmd, 5U, MX25L4006_SPI_TIMEOUT) != HAL_OK) { ret = MX25L4006_ERROR_COMMAND; } else { if (HAL_SPI_Receive(Ctx, pData, Size, MX25L4006_SPI_TIMEOUT) != HAL_OK) { ret = MX25L4006_ERROR_RECEIVE; } } return ret; } /** * @brief Writes an amount of data to the SPI memory. * For 256 bytes page program, the 8 least significant address bits byte * should be set to 0 this function otherwise returns MX25L4006_ERROR_ADDRESS * @param Ctx Component object pointer * @param pData Pointer to data to be written * @param WriteAddr Write start address * @param Size Size of data to write. Range 1 ~ 256 * @retval Status * - MX25L4006_ERROR_ADDRESS * - MX25L4006_ERROR_COMMAND * - MX25L4006_ERROR_TRANSMIT * - MX25L4006_OK */ int32_t MX25L4006_PageProgram(SPI_HandleTypeDef *Ctx, uint8_t *pData, uint32_t WriteAddr, uint16_t Size) { int32_t ret = MX25L4006_OK; uint8_t cmd[4U]; if ((Size >= MX25L4006_PAGE_SIZE) && ((WriteAddr & 0x000000FFU) != 0U)) { ret = MX25L4006_ERROR_ADDRESS; } else { /* 1- Send Page Program (PP) command */ cmd[0U] = MX25L4006_PAGE_PROG_CMD; cmd[1U] = (uint8_t)((WriteAddr & 0x00FF0000U) >> 16); cmd[2U] = (uint8_t)((WriteAddr & 0x0000FF00U) >> 8); cmd[3U] = (uint8_t)(WriteAddr & 0x000000FFU); if (HAL_SPI_Transmit(Ctx, cmd, 4U, MX25L4006_SPI_TIMEOUT) != HAL_OK) { ret = MX25L4006_ERROR_COMMAND; } else { /* 2- Send the data */ if (HAL_SPI_Transmit(Ctx, pData, Size, MX25L4006_SPI_TIMEOUT) != HAL_OK) { ret = MX25L4006_ERROR_TRANSMIT; } } } return ret; } /** * @brief Erases the specified sector of the SPI memory * MX25L4006 support 4K size block erase command. * @param Ctx Component object pointer * @param SectorAddress Sector address to erase * @retval Status * - MX25L4006_ERROR_COMMAND * - MX25L4006_OK */ int32_t MX25L4006_SectorErase(SPI_HandleTypeDef *Ctx, uint32_t SectorAddress) { int32_t ret = MX25L4006_OK; uint8_t cmd[4U]; /* 1- Send Sector Erase (SE) for erasing the data of chosen block */ cmd[0U] = MX25L4006_SECTOR_ERASE_4K_CMD; cmd[1U] = (uint8_t)((SectorAddress & 0x00FF0000U) >> 16); cmd[2U] = (uint8_t)((SectorAddress & 0x0000FF00U) >> 8); cmd[3U] = (uint8_t)(SectorAddress & 0x000000FFU); if (HAL_SPI_Transmit(Ctx, cmd, 4U, MX25L4006_SPI_TIMEOUT) != HAL_OK) { return MX25L4006_ERROR_COMMAND; } return ret; } /** * @brief Erases the specified block of the SPI memory * MX25L4006 64K size block erase command. * @param Ctx Component object pointer * @param BlockAddress Block address to erase * @param BlockSize Block size to erase * @retval Status * - MX25L4006_ERROR_COMMAND * - MX25L4006_OK */ int32_t MX25L4006_BlockErase(SPI_HandleTypeDef *Ctx, uint32_t BlockAddress, MX25L4006_Erase_t BlockSize) { int32_t ret = MX25L4006_OK; uint8_t cmd[4U]; /* Setup erase command */ switch (BlockSize) { default : case MX25L4006_ERASE_4K : cmd[0U] = MX25L4006_SECTOR_ERASE_4K_CMD; break; case MX25L4006_ERASE_64K : cmd[0U] = MX25L4006_BLOCK_ERASE_64K_CMD; break; case MX25L4006_ERASE_CHIP : return MX25L4006_ChipErase(Ctx); break; } /* 1- Send Block Erase (BE) for erasing the data of chosen block */ cmd[1U] = (uint8_t)((BlockAddress & 0x00FF0000U) >> 16); cmd[2U] = (uint8_t)((BlockAddress & 0x0000FF00U) >> 8); cmd[3U] = (uint8_t)(BlockAddress & 0x000000FFU); if (HAL_SPI_Transmit(Ctx, cmd, 4U, MX25L4006_SPI_TIMEOUT) != HAL_OK) { ret = MX25L4006_ERROR_COMMAND; } return ret; } /** * @brief Whole chip erase of the SPI memory * @param Ctx Component object pointer * @retval Status * - MX25L4006_ERROR_COMMAND * - MX25L4006_OK */ int32_t MX25L4006_ChipErase(SPI_HandleTypeDef *Ctx) { int32_t ret = MX25L4006_OK; uint8_t cmd = MX25L4006_CHIP_ERASE_CMD; /* 1- Send Chip Erase (CE) command to erase whole chip */ if (HAL_SPI_Transmit(Ctx, &cmd, 1U, MX25L4006_SPI_TIMEOUT) != HAL_OK) { ret = MX25L4006_ERROR_COMMAND; } return ret; } /* Register/Setting Commands **************************************************/ /** * @brief This function sets the (WEL) Write Enable Latch bit * @param Ctx Component object pointer * @retval Status * - MX25L4006_ERROR_COMMAND * - MX25L4006_OK */ int32_t MX25L4006_WriteEnable(SPI_HandleTypeDef *Ctx) { int32_t ret = MX25L4006_OK; uint8_t cmd = MX25L4006_WRITE_ENABLE_CMD; /* Send Write Enable (WREN) command to set Write Enable Latch (WEL) bit in the Status Register */ if (HAL_SPI_Transmit(Ctx, &cmd, 1U, MX25L4006_SPI_TIMEOUT) != HAL_OK) { ret = MX25L4006_ERROR_COMMAND; } return ret; } /** * @brief This function resets the (WEL) Write Enable Latch bit * @param Ctx Component object pointer * @retval Status * - MX25L4006_ERROR_COMMAND * - MX25L4006_OK */ int32_t MX25L4006_WriteDisable(SPI_HandleTypeDef *Ctx) { int32_t ret = MX25L4006_OK; uint8_t cmd = MX25L4006_WRITE_DISABLE_CMD; /* Send Write Disable (WRDI) command to unset Write Enable Latch (WEL) bit in the Status Register */ if (HAL_SPI_Transmit(Ctx, &cmd, 1U, MX25L4006_SPI_TIMEOUT) != HAL_OK) { return MX25L4006_ERROR_COMMAND; } return ret; } /** * @brief Read Flash Status register * @param Ctx Component object pointer * @param Value pointer to status register value * @retval Status * - MX25L4006_ERROR_COMMAND * - MX25L4006_ERROR_RECEIVE * - MX25L4006_OK */ int32_t MX25L4006_ReadStatusRegister(SPI_HandleTypeDef *Ctx, uint8_t *Value) { int32_t ret = MX25L4006_OK; uint8_t cmd = MX25L4006_READ_STATUS_REG_CMD; /* Send the command */ if (HAL_SPI_Transmit(Ctx, &cmd, 1U, MX25L4006_SPI_TIMEOUT) != HAL_OK) { ret = MX25L4006_ERROR_COMMAND; } else { if (HAL_SPI_Receive(Ctx, Value, 1U, MX25L4006_SPI_TIMEOUT) != HAL_OK) { ret = MX25L4006_ERROR_RECEIVE; } } return ret; } /** * @brief Write Flash Status register value * @param Ctx Component object pointer * @param Value Status register value * @retval Status * - MX25L4006_ERROR_COMMAND * - MX25L4006_OK */ int32_t MX25L4006_WriteStatusRegister(SPI_HandleTypeDef *Ctx, uint8_t Value) { int32_t ret = MX25L4006_OK; uint8_t cmd[2U]; /* Send the command */ cmd[0U] = MX25L4006_WRITE_STATUS_REG_CMD; cmd[1U] = Value; if (HAL_SPI_Transmit(Ctx, cmd, 2U, MX25L4006_SPI_TIMEOUT) != HAL_OK) { ret = MX25L4006_ERROR_COMMAND; } return ret; } /** * @brief Deep power down * The device is not active and all Write/Program/Erase instruction are ignored. * @param Ctx Component object pointer * @retval Status * - MX25L4006_ERROR_COMMAND * - MX25L4006_OK */ int32_t MX25L4006_EnterDeepPowerDown(SPI_HandleTypeDef *Ctx) { int32_t ret = MX25L4006_OK; uint8_t cmd = MX25L4006_DEEP_POWER_DOWN_CMD; /* Send Deep Powerdown (DP) command to enter deep powerdown mode */ if (HAL_SPI_Transmit(Ctx, &cmd, 1U, MX25L4006_SPI_TIMEOUT) != HAL_OK) { ret = MX25L4006_ERROR_COMMAND; } return ret; } /** * @brief Release Deep power down * The device is now active and all Write/Program/Erase instruction are available. * @param Ctx Component object pointer * @retval Status * - MX25L4006_ERROR_COMMAND * - MX25L4006_OK */ int32_t MX25L4006_ReleaseDeepPowerDown(SPI_HandleTypeDef *Ctx) { int32_t ret = MX25L4006_OK; uint8_t cmd = MX25L4006_RELEASE_FROM_DEEP_POWER_DOWN_CMD; /* Send Release from Deep Powerdown (RDP) command to exit from deep powerdown mode */ if (HAL_SPI_Transmit(Ctx, &cmd, 1U, MX25L4006_SPI_TIMEOUT) != HAL_OK) { ret = MX25L4006_ERROR_COMMAND; } return ret; } /* ID/Security Commands *******************************************************/ /** * @brief Read Flash 3 Byte IDs * Manufacturer ID, Memory type, Memory density * @param Ctx Component object pointer * @param ID pointer to flash id value * @retval Status * - MX25L4006_ERROR_COMMAND * - MX25L4006_ERROR_RECEIVE * - MX25L4006_OK */ int32_t MX25L4006_ReadID(SPI_HandleTypeDef *Ctx, uint8_t *ID) { int32_t ret = MX25L4006_OK; uint8_t cmd = MX25L4006_READ_ID_CMD; /* Send the command */ if (HAL_SPI_Transmit(Ctx, &cmd, 1U, MX25L4006_SPI_TIMEOUT) != HAL_OK) { ret = MX25L4006_ERROR_COMMAND; } else { if (HAL_SPI_Receive(Ctx, ID, 3U, MX25L4006_SPI_TIMEOUT) != HAL_OK) { ret = MX25L4006_ERROR_RECEIVE; } } return ret; } /** * @brief Reads an amount of data from the memory * @param Ctx Component object pointer * @param pData Pointer to data to be read * @param ReadAddr Read start address * @param Size Size of data to read in Byte * @retval Status * - MX25L4006_ERROR_COMMAND * - MX25L4006_ERROR_RECEIVE * - MX25L4006_OK */ int32_t MX25L4006_ReadSFDP(SPI_HandleTypeDef *Ctx, uint8_t *pData, uint32_t ReadAddr, uint16_t Size) { int32_t ret = MX25L4006_OK; uint8_t cmd[5U]; /* Send the command */ cmd[0U] = MX25L4006_READ_SERIAL_FLASH_DISCO_PARAM_CMD; cmd[1U] = (uint8_t)((ReadAddr & 0x00FF0000U) >> 16); cmd[2U] = (uint8_t)(ReadAddr & 0x0000FF00U) >> 8; cmd[3U] = (uint8_t)(ReadAddr & 0x000000FFU); cmd[4U] = 0xAAU; /* Dummy */ if (HAL_SPI_Transmit(Ctx, cmd, 5U, MX25L4006_SPI_TIMEOUT) != HAL_OK) { ret = MX25L4006_ERROR_COMMAND; } else { if (HAL_SPI_Receive(Ctx, pData, Size, MX25L4006_SPI_TIMEOUT) != HAL_OK) { ret = MX25L4006_ERROR_RECEIVE; } } return ret; }