/** ******************************************************************************* * @file w25qxx.c * @brief This midware file provides firmware functions to W25QXX group spi flash. @verbatim Change Logs: Date Author Notes 2022-03-31 CDT First version @endverbatim ******************************************************************************* * Copyright (C) 2022, Xiaohua Semiconductor Co., Ltd. All rights reserved. * * This software component is licensed by XHSC under BSD 3-Clause license * (the "License"); You may not use this file except in compliance with the * License. You may obtain a copy of the License at: * opensource.org/licenses/BSD-3-Clause * ******************************************************************************* */ /******************************************************************************* * Include files ******************************************************************************/ #include "hal_w25qxx.h" /** * @addtogroup BSP * @{ */ /** * @addtogroup Components * @{ */ /** * @defgroup W25QXX Flash Driver for W25QXX * @{ */ /******************************************************************************* * Local type definitions ('typedef') ******************************************************************************/ /******************************************************************************* * Local pre-processor symbols/macros ('#define') ******************************************************************************/ /** * @defgroup W25QXX_Local_Macros W25QXX Local Macros * @{ */ #define W25QXX_FLAG_BUSY (1UL << 0U) #define W25QXX_FLAG_WEL (1UL << 1U) /*!< Write Enable Latch */ #define W25QXX_FLAG_SUSPEND (1UL << 15U) /*!< Write Enable Latch */ #define LOAD_CMD(a, cmd, addr) do { \ (a)[0U] = (cmd); \ (a)[1U] = (uint8_t)((addr) >> 16U); \ (a)[2U] = (uint8_t)((addr) >> 8U); \ (a)[3U] = (uint8_t)(addr); \ } while (0U) /** * @} */ /******************************************************************************* * Global variable definitions (declared in header file with 'extern') ******************************************************************************/ /******************************************************************************* * Local function prototypes ('static') ******************************************************************************/ /******************************************************************************* * Local variable definitions ('static') ******************************************************************************/ /******************************************************************************* * Function implementation - global ('extern') and local ('static') ******************************************************************************/ /** * @addtogroup W25QXX_Local_Functions W25QXX Local Functions * @{ */ /** * @brief W25QXX write command. * @param [in] pstcW25qxxLL Pointer to a @ref stc_w25qxx_ll_t structure. * @param [in] u8Cmd Command of W25QXX. * @param [in] pu8CmdData Pointer to a buffer that contains the data following the command. * @param [in] u32CmdDataLen The length of the command data in bytes. * @retval int32_t: * - LL_OK: No error occurred. * - LL_ERR_TIMEOUT: SPI timeout. */ static int32_t W25QXX_WriteCmd(const stc_w25qxx_ll_t *pstcW25qxxLL, \ uint8_t u8Cmd, const uint8_t *pu8CmdData, uint32_t u32CmdDataLen) { int32_t i32Ret; pstcW25qxxLL->Active(); i32Ret = pstcW25qxxLL->Trans(&u8Cmd, 1U); if ((i32Ret == LL_OK) && (pu8CmdData != NULL) && (u32CmdDataLen > 0UL)) { i32Ret = pstcW25qxxLL->Trans(pu8CmdData, u32CmdDataLen); } pstcW25qxxLL->Inactive(); return i32Ret; } /** * @brief W25QXX read command. * @param [in] pstcW25qxxLL Pointer to a @ref stc_w25qxx_ll_t structure. * @param [in] u8Cmd Command of W25QXX. * @param [in] pu8CmdData Pointer to a buffer that contains the data following the command. * @param [in] u32CmdDataLen The length of the command data in bytes. * @param [in] pu8Info The information of the command. * @param [in] u8InfoLen The length of the information. * @retval int32_t: * - LL_OK: No error occurred. * - LL_ERR_TIMEOUT: SPI timeout. */ static int32_t W25QXX_ReadCmd(const stc_w25qxx_ll_t *pstcW25qxxLL, \ uint8_t u8Cmd, uint8_t *pu8CmdData, uint32_t u32CmdDataLen, uint8_t *pu8Info, uint8_t u8InfoLen) { int32_t i32Ret; pstcW25qxxLL->Active(); i32Ret = pstcW25qxxLL->Trans(&u8Cmd, 1U); if ((i32Ret == LL_OK) && (pu8CmdData != NULL) && (u32CmdDataLen > 0UL)) { i32Ret = pstcW25qxxLL->Trans(pu8CmdData, u32CmdDataLen); } if ((i32Ret == LL_OK) && (pu8Info != NULL) && (u8InfoLen > 0UL)) { i32Ret = pstcW25qxxLL->Receive(pu8Info, (uint32_t)u8InfoLen); } pstcW25qxxLL->Inactive(); return i32Ret; } /** * @brief W25QXX write data. * @param [in] pstcW25qxxLL Pointer to a @ref stc_w25qxx_ll_t structure. * @param [in] u8Cmd Command of W25QXX. * @param [in] u32Addr The start address of the data to be written. * @param [in] pu8Data The data to be written. * @param [in] u32DataLen The length of the data in bytes. * @retval int32_t: * - LL_OK: No error occurred. * - LL_ERR_TIMEOUT: SPI timeout. */ static int32_t W25QXX_Wt(const stc_w25qxx_ll_t *pstcW25qxxLL, \ uint8_t u8Cmd, uint32_t u32Addr, \ const uint8_t *pu8Data, uint32_t u32DataLen) { uint8_t au8Cmd[4U]; int32_t i32Ret; LOAD_CMD(au8Cmd, u8Cmd, u32Addr); pstcW25qxxLL->Active(); i32Ret = pstcW25qxxLL->Trans(au8Cmd, 4U); if ((i32Ret == LL_OK) && (pu8Data != NULL) && (u32DataLen > 0UL)) { i32Ret = pstcW25qxxLL->Trans(pu8Data, u32DataLen); } pstcW25qxxLL->Inactive(); return i32Ret; } /** * @brief W25QXX read data. * @param [in] pstcW25qxxLL Pointer to a @ref stc_w25qxx_ll_t structure. * @param [in] u8Cmd Command of W25QXX. * @param [in] u32Addr The start address of the data to be written. * @param [in] pu8Data The data to be stored. * @param [in] u32DataLen The length of the data in bytes. * @retval int32_t: * - LL_OK: No error occurred. * - LL_ERR_TIMEOUT: SPI timeout. */ static int32_t W25QXX_Rd(const stc_w25qxx_ll_t *pstcW25qxxLL, \ uint8_t u8Cmd, uint32_t u32Addr, \ uint8_t *pu8Data, uint32_t u32DataLen) { uint8_t au8Cmd[4U]; int32_t i32Ret; LOAD_CMD(au8Cmd, u8Cmd, u32Addr); pstcW25qxxLL->Active(); i32Ret = pstcW25qxxLL->Trans(au8Cmd, 4U); if (i32Ret == LL_OK) { i32Ret = pstcW25qxxLL->Receive(pu8Data, u32DataLen); } pstcW25qxxLL->Inactive(); return i32Ret; } /** * @brief W25QXX Write enable. * @param [in] pstcW25qxxLL Pointer to a @ref stc_w25qxx_ll_t structure. * @retval int32_t: * - LL_OK: No error occurred. * - LL_ERR_TIMEOUT: SPI timeout. */ static int32_t W25QXX_WriteEnable(const stc_w25qxx_ll_t *pstcW25qxxLL) { return W25QXX_WriteCmd(pstcW25qxxLL, W25QXX_WRITE_ENABLE, NULL, 0U); } /** * @brief W25QXX Write disable. * @param [in] pstcW25qxxLL Pointer to a @ref stc_w25qxx_ll_t structure. * @retval int32_t: * - LL_OK: No error occurred. * - LL_ERR_TIMEOUT: SPI timeout. */ static int32_t W25QXX_WriteDisable(const stc_w25qxx_ll_t *pstcW25qxxLL) { return W25QXX_WriteCmd(pstcW25qxxLL, W25QXX_WRITE_DISABLE, NULL, 0U); } /** * @brief Wait for processing done. * @param [in] pstcW25qxxLL Pointer to a @ref stc_w25qxx_ll_t structure. * @retval int32_t: * - LL_OK: No error occurred. * - LL_ERR_TIMEOUT: SPI timeout or W25QXX timeout. */ static int32_t W25QXX_WaitProcessDone(const stc_w25qxx_ll_t *pstcW25qxxLL) { uint8_t u8Status; int32_t i32Ret = LL_ERR_TIMEOUT; volatile uint32_t u32Timecount = W25QXX_TIMEOUT; while (u32Timecount-- != 0UL) { i32Ret = W25QXX_ReadStatus(pstcW25qxxLL, W25QXX_READ_STATUS_REGISTER_1, &u8Status); if ((i32Ret == LL_OK) && ((u8Status & W25QXX_FLAG_BUSY) == 0U)) { break; } } return i32Ret; } /** * @} */ /** * @defgroup W25QXX_Global_Functions W25QXX Global Functions * @{ */ /** * @brief Initializes W25QXX. * @param [in] pstcW25qxxLL Pointer to a @ref stc_w25qxx_ll_t structure. * @retval int32_t: * - LL_OK: No error occurred. * - LL_ERR_INVD_PARAM: Invalid parameter */ int32_t W25QXX_Init(const stc_w25qxx_ll_t *pstcW25qxxLL) { int32_t i32Ret = LL_ERR_INVD_PARAM; if (pstcW25qxxLL != NULL) { pstcW25qxxLL->Init(); i32Ret = LL_OK; } return i32Ret; } /** * @brief De-Initialize W25QXX. * @param [in] pstcW25qxxLL Pointer to a @ref stc_w25qxx_ll_t structure. * @retval int32_t: * - LL_OK: No error occurred. * - LL_ERR_INVD_PARAM: Invalid parameter */ int32_t W25QXX_DeInit(const stc_w25qxx_ll_t *pstcW25qxxLL) { int32_t i32Ret = LL_ERR_INVD_PARAM; if (pstcW25qxxLL != NULL) { pstcW25qxxLL->DeInit(); i32Ret = LL_OK; } return i32Ret; } /** * @brief Read manufacturer device ID. * @param [in] pstcW25qxxLL Pointer to a @ref stc_w25qxx_ll_t structure. * @param [out] pu16ID Pointer to an address to store the device ID. * @retval int32_t: * - LL_OK: No error occurred. * - LL_ERR_INVD_PARAM: Invalid parameter. * - LL_ERR_TIMEOUT: SPI timeout. */ int32_t W25QXX_GetManDeviceId(const stc_w25qxx_ll_t *pstcW25qxxLL, uint16_t *pu16ID) { uint8_t au8TempId[2U]; uint8_t au8Dummy[3U] = {0U}; uint16_t u16ManID; int32_t i32Ret = LL_ERR_INVD_PARAM; if ((pstcW25qxxLL != NULL) && (pu16ID != NULL)) { i32Ret = W25QXX_ReadCmd(pstcW25qxxLL, W25QXX_MANUFACTURER_DEVICE_ID, au8Dummy, 3U, au8TempId, 2U); if (i32Ret == LL_OK) { u16ManID = (uint16_t)au8TempId[0U] << 8U; u16ManID |= au8TempId[1U]; *pu16ID = u16ManID; } } return i32Ret; } /** * @brief Read unique ID. * @param [in] pstcW25qxxLL Pointer to a @ref stc_w25qxx_ll_t structure. * @param [out] pu8UniqueId Pointer to a buffer the 64 bit unique ID to be stored. * @retval int32_t: * - LL_OK: No error occurred. * - LL_ERR_INVD_PARAM: Invalid parameter. * - LL_ERR_TIMEOUT: SPI timeout. */ int32_t W25QXX_GetUniqueId(const stc_w25qxx_ll_t *pstcW25qxxLL, uint8_t *pu8UniqueId) { uint8_t au8Dummy[4U] = {0U}; int32_t i32Ret = LL_ERR_INVD_PARAM; if ((pstcW25qxxLL != NULL) && (pu8UniqueId != NULL)) { i32Ret = W25QXX_ReadCmd(pstcW25qxxLL, W25QXX_READ_UNIQUE_ID, au8Dummy, 4U, pu8UniqueId, 8U); } return i32Ret; } /** * @brief Read status register. * @param [in] pstcW25qxxLL Pointer to a @ref stc_w25qxx_ll_t structure. * @param [in] u8SrRdCmd Command of reading status register. * @arg W25QXX_READ_STATUS_REGISTER_1: Read status register 1. * @arg W25QXX_READ_STATUS_REGISTER_2: Read status register 2. * @arg W25QXX_READ_STATUS_REGISTER_3: Read status register 3. * @param [out] pu8Status Pointer to an address the status value to be stored. * @retval int32_t: * - LL_OK: No error occurred. * - LL_ERR_INVD_PARAM: Invalid parameter. * - LL_ERR_TIMEOUT: SPI timeout. */ int32_t W25QXX_ReadStatus(const stc_w25qxx_ll_t *pstcW25qxxLL, uint8_t u8SrRdCmd, uint8_t *pu8Status) { int32_t i32Ret = LL_ERR_INVD_PARAM; if ((pstcW25qxxLL != NULL) && (pu8Status != NULL)) { i32Ret = W25QXX_ReadCmd(pstcW25qxxLL, u8SrRdCmd, NULL, 0U, pu8Status, 1U); } return i32Ret; } /** * @brief Write status register. * @param [in] pstcW25qxxLL Pointer to a @ref stc_w25qxx_ll_t structure. * @param [in] u8SrWtCmd Command of writting status register. * @arg W25QXX_WRITE_STATUS_REGISTER_1: Write status register 1. * @arg W25QXX_WRITE_STATUS_REGISTER_2: Write status register 2. * @arg W25QXX_WRITE_STATUS_REGISTER_3: Write status register 3. * @param [in] u8Value 8bit value of the specified status register. * @retval int32_t: * - LL_OK: No error occurred. * - LL_ERR_INVD_PARAM: Invalid parameter. * - LL_ERR_TIMEOUT: SPI timeout. */ int32_t W25QXX_WriteStatus(const stc_w25qxx_ll_t *pstcW25qxxLL, uint8_t u8SrWtCmd, uint8_t u8Value) { int32_t i32Ret = LL_ERR_INVD_PARAM; if (pstcW25qxxLL != NULL) { i32Ret = W25QXX_WriteCmd(pstcW25qxxLL, u8SrWtCmd, &u8Value, 1U); } return i32Ret; } /** * @brief Power down. * @param [in] pstcW25qxxLL Pointer to a @ref stc_w25qxx_ll_t structure. * @retval int32_t: * - LL_OK: No error occurred. * - LL_ERR_INVD_PARAM: Invalid parameter. * - LL_ERR_TIMEOUT: SPI timeout. */ int32_t W25QXX_PowerDown(const stc_w25qxx_ll_t *pstcW25qxxLL) { int32_t i32Ret = LL_ERR_INVD_PARAM; if (pstcW25qxxLL != NULL) { i32Ret = W25QXX_WriteCmd(pstcW25qxxLL, W25QXX_POWER_DOWN, NULL, 0U); if (i32Ret == LL_OK) { pstcW25qxxLL->Delay(1U); } } return i32Ret; } /** * @brief Release power down. * @param [in] pstcW25qxxLL Pointer to a @ref stc_w25qxx_ll_t structure. * @retval int32_t: * - LL_OK: No error occurred. * - LL_ERR_INVD_PARAM: Invalid parameter. * - LL_ERR_TIMEOUT: SPI timeout. */ int32_t W25QXX_ReleasePowerDown(const stc_w25qxx_ll_t *pstcW25qxxLL) { int32_t i32Ret = LL_ERR_INVD_PARAM; if (pstcW25qxxLL != NULL) { i32Ret = W25QXX_WriteCmd(pstcW25qxxLL, W25QXX_RELEASE_POWER_DOWN_ID, NULL, 0U); if (i32Ret == LL_OK) { pstcW25qxxLL->Delay(1U); } } return i32Ret; } /** * @brief Ease chip. * @param [in] pstcW25qxxLL Pointer to a @ref stc_w25qxx_ll_t structure. * @retval int32_t: * - LL_OK: No error occurred. * - LL_ERR_INVD_PARAM: Invalid parameter. * - LL_ERR_TIMEOUT: SPI timeout or W25QXX timeout. */ int32_t W25QXX_EraseChip(const stc_w25qxx_ll_t *pstcW25qxxLL) { int32_t i32Ret = LL_ERR_INVD_PARAM; if (pstcW25qxxLL != NULL) { i32Ret = W25QXX_WriteEnable(pstcW25qxxLL); if (i32Ret == LL_OK) { i32Ret = W25QXX_WaitProcessDone(pstcW25qxxLL); } if (i32Ret == LL_OK) { i32Ret = W25QXX_WriteCmd(pstcW25qxxLL, W25QXX_CHIP_ERASE, NULL, 0U); } if (i32Ret == LL_OK) { i32Ret = W25QXX_WaitProcessDone(pstcW25qxxLL); } } return i32Ret; } /** * @brief Ease sector. * @param [in] pstcW25qxxLL Pointer to a @ref stc_w25qxx_ll_t structure. * @param [in] u32Addr Any address of the specified sector. * @retval int32_t: * - LL_OK: No error occurred. * - LL_ERR_INVD_PARAM: Invalid parameter. * - LL_ERR_TIMEOUT: SPI timeout or W25QXX timeout. */ int32_t W25QXX_EraseSector(const stc_w25qxx_ll_t *pstcW25qxxLL, uint32_t u32Addr) { int32_t i32Ret = LL_ERR_INVD_PARAM; if (pstcW25qxxLL != NULL) { i32Ret = W25QXX_WriteEnable(pstcW25qxxLL); if (i32Ret == LL_OK) { i32Ret = W25QXX_WaitProcessDone(pstcW25qxxLL); } if (i32Ret == LL_OK) { i32Ret = W25QXX_Wt(pstcW25qxxLL, W25QXX_SECTOR_ERASE, u32Addr, NULL, 0U); } if (i32Ret == LL_OK) { i32Ret = W25QXX_WaitProcessDone(pstcW25qxxLL); } if (i32Ret == LL_OK) { i32Ret = W25QXX_WriteDisable(pstcW25qxxLL); } } return i32Ret; } /** * @brief W25QXX read data. * @param [in] pstcW25qxxLL Pointer to a @ref stc_w25qxx_ll_t structure. * @param [in] u32Addr The start address of the data to be read. * @param [in] pu8ReadBuf The pointer to the buffer contains the data to be stored. * @param [in] u32NumByteToRead Buffer size in bytes. * @retval int32_t: * - LL_OK: No error occurred. * - LL_ERR_INVD_PARAM: Invalid parameter. * - LL_ERR_TIMEOUT: SPI timeout. */ int32_t W25QXX_ReadData(const stc_w25qxx_ll_t *pstcW25qxxLL, uint32_t u32Addr, \ uint8_t *pu8ReadBuf, uint32_t u32NumByteToRead) { int32_t i32Ret = LL_ERR_INVD_PARAM; if ((pstcW25qxxLL != NULL) && (pu8ReadBuf != NULL) && (u32NumByteToRead != 0UL)) { i32Ret = W25QXX_Rd(pstcW25qxxLL, W25QXX_READ_DATA, u32Addr, pu8ReadBuf, u32NumByteToRead); } return i32Ret; } /** * @brief W25QXX page program. * @param [in] pstcW25qxxLL Pointer to a @ref stc_w25qxx_ll_t structure. * @param [in] u32Addr Start address of the page. * @param [in] pu8Data Pointer to a buffer that contains the data to be written. * @param [in] u32NumByteToProgram Size of the buffer. * @retval int32_t: * - LL_OK: No error occurred. * - LL_ERR_INVD_PARAM: Invalid parameter. * - LL_ERR_TIMEOUT: SPI timeout or W25QXX timeout. */ int32_t W25QXX_PageProgram(const stc_w25qxx_ll_t *pstcW25qxxLL, uint32_t u32Addr, \ const uint8_t *pu8Data, uint32_t u32NumByteToProgram) { int32_t i32Ret = LL_ERR_INVD_PARAM; if ((pstcW25qxxLL != NULL) && (pu8Data != NULL) && (u32NumByteToProgram != 0UL)) { i32Ret = W25QXX_WriteEnable(pstcW25qxxLL); if (i32Ret == LL_OK) { i32Ret = W25QXX_Wt(pstcW25qxxLL, W25QXX_PAGE_PROGRAM, u32Addr, pu8Data, u32NumByteToProgram); } if (i32Ret == LL_OK) { i32Ret = W25QXX_WaitProcessDone(pstcW25qxxLL); } } return i32Ret; } /** * @} */ /** * @} */ /** * @} */ /** * @} */ /******************************************************************************* * EOF (not truncated) ******************************************************************************/