/****************************************************************************** * file Src/flash_if.c * author YuLiang * version 1.0.0 * date 10-Jun-2022 * brief This file provides all the memory related operation functions. ****************************************************************************** * @attention * *

© COPYRIGHT(c) 2019 LandPower

* * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. Neither the name of STMicroelectronics nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ******************************************************************************/ /* Includes ------------------------------------------------------------------*/ #include "main.h" #include "common.h" #include "flash_if.h" /* Private typedef -----------------------------------------------------------*/ /* Private define ------------------------------------------------------------*/ /* 1M0 flash 1 * 1024 * 1024 */ #define FLASH_START_ADRESS 0x08000000 #define FLASH_PAGE_NBPERBANK 256 /* Private macro -------------------------------------------------------------*/ /* Private variables ---------------------------------------------------------*/ /* Private function prototypes -----------------------------------------------*/ /* Private functions ---------------------------------------------------------*/ /* spi flash 写使能. */ static HAL_StatusTypeDef _spi_flash_write_enable(void) { uint8_t cmd = SPI_CMD_WRITE_ENABLE; /* 片选使能. */ SPI_FLASH_CS_ENABLE(); HAL_E_RETURN(HAL_SPI_Transmit(SpiHandle, &cmd, 1, SPI_BUS_TIMEOUT)); SPI_FLASH_CS_DISABLE(); return HAL_OK; } /* 等待 flash 操作完成. */ static HAL_StatusTypeDef _spi_flash_wait_complete(void) { uint8_t cmd = SPI_CMD_STATE_READ; uint8_t status = 0xFF; SPI_FLASH_CS_ENABLE(); HAL_E_RETURN(HAL_SPI_Transmit(SpiHandle, &cmd, 1, SPI_BUS_TIMEOUT)); /* 一直检测 FLASH 状态寄存器状态, 直到 Bit0 位 (BUSY 位) 为 0. */ do { HAL_E_RETURN(HAL_SPI_Receive(SpiHandle, &status, 1, SPI_BUS_TIMEOUT)); } while (SPI_FLASH_BUSY_MASK == (status & SPI_FLASH_BUSY_MASK)); SPI_FLASH_CS_DISABLE(); return HAL_OK; } /* 等待 flash 写使能. */ static HAL_StatusTypeDef _spi_flash_wait_write(void) { uint8_t cmd = SPI_CMD_STATE_READ; uint8_t status = 0xFF; SPI_FLASH_CS_ENABLE(); HAL_E_RETURN(HAL_SPI_Transmit(SpiHandle, &cmd, 1, SPI_BUS_TIMEOUT)); /* 一直检测 FLASH 状态寄存器状态, 直到 Bit1 位 (WEL 位) 为 1. */ do { HAL_E_RETURN(HAL_SPI_Receive(SpiHandle, &status, 1, SPI_BUS_TIMEOUT)); } while (0 == (status & SPI_FLASH_WEL_MASK)); SPI_FLASH_CS_DISABLE(); return HAL_OK; } /* spi flash 页写入操作, size 最大为页大小 (256Byte). */ static HAL_StatusTypeDef _spi_flash_page_write(uint32_t addr, uint8_t *data, uint16_t size) { uint8_t cmd[SPI_CMD_HEADER_LEN] = {0}; /* 写使能. */ HAL_E_RETURN(_spi_flash_write_enable()); /* 等待写使能完成. */ HAL_E_RETURN(_spi_flash_wait_write()); /* 片选使能. */ SPI_FLASH_CS_ENABLE(); cmd[0] = SPI_CMD_PAGE_WRITE; cmd[1] = (addr >> 16) & 0xFF; cmd[2] = (addr >> 8) & 0xFF;; cmd[3] = addr & 0xFF;; /* 发送页写入命令. */ HAL_E_RETURN(HAL_SPI_Transmit(SpiHandle, cmd, SPI_CMD_HEADER_LEN, SPI_BUS_TIMEOUT)); /* 确保写入大小不超过页 (256Byte) 大小. */ if (size > SPI_FLASH_PAGE_SIZE) size = SPI_FLASH_PAGE_SIZE; /* 发送数据. */ HAL_E_RETURN(HAL_SPI_Transmit(SpiHandle, data, size, SPI_BUS_TIMEOUT)); /* 片选去使能. */ SPI_FLASH_CS_DISABLE(); /* 等待 flash busy 状态. */ HAL_E_RETURN(_spi_flash_wait_complete()); return HAL_OK; } /* Public functions ----------------------------------------------------------*/ /** * @brief Unlocks Flash for write access * @param None * @retval None */ void FLASH_If_Init(void) { /* Unlock the Program memory */ HAL_FLASH_Unlock(); /* Clear all FLASH flags */ __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_PGSERR | FLASH_FLAG_WRPERR | FLASH_FLAG_OPTVERR); /* Unlock the Program memory */ HAL_FLASH_Lock(); } /** * @brief This function does an erase of all user flash area * @param start: start of user flash area * @retval FLASHIF_OK : user flash area successfully erased * FLASHIF_ERASEKO : error occurred */ uint32_t FLASH_If_Erase(uint32_t start) { uint32_t NbrOfPages = 0; uint32_t PageError = 0; FLASH_EraseInitTypeDef pEraseInit; HAL_StatusTypeDef status = HAL_OK; /* Unlock the Flash to enable the flash control register access *************/ HAL_FLASH_Unlock(); /* Get the number of page to erase */ NbrOfPages = (FLASH_START_ADRESS + FLASH_SIZE); NbrOfPages = (NbrOfPages - start) / FLASH_PAGE_SIZE; if(NbrOfPages > FLASH_PAGE_NBPERBANK) { pEraseInit.Banks = FLASH_BANK_1; pEraseInit.NbPages = NbrOfPages % FLASH_PAGE_NBPERBANK; pEraseInit.Page = FLASH_PAGE_NBPERBANK - pEraseInit.NbPages; pEraseInit.TypeErase = FLASH_TYPEERASE_PAGES; status = HAL_FLASHEx_Erase(&pEraseInit, &PageError); NbrOfPages = FLASH_PAGE_NBPERBANK; } if(status == HAL_OK) { pEraseInit.Banks = FLASH_BANK_2; pEraseInit.NbPages = NbrOfPages; pEraseInit.Page = FLASH_PAGE_NBPERBANK - pEraseInit.NbPages; pEraseInit.TypeErase = FLASH_TYPEERASE_PAGES; status = HAL_FLASHEx_Erase(&pEraseInit, &PageError); } /* Lock the Flash to disable the flash control register access (recommended to protect the FLASH memory against possible unwanted operation) *********/ HAL_FLASH_Lock(); if (status != HAL_OK) { /* Error occurred while page erase */ return FLASHIF_ERASEKO; } return FLASHIF_OK; } /** * @brief This function writes a data buffer in flash (data are 32-bit aligned). * @note After writing data buffer, the flash content is checked. * @param destination: start address for target location * @param p_source: pointer on buffer with data to write * @param length: length of data buffer (unit is 32-bit word) * @retval uint32_t 0: Data successfully written to Flash memory * 1: Error occurred while writing data in Flash memory * 2: Written Data in flash memory is different from expected one */ uint32_t FLASH_If_Write(uint32_t destination, uint32_t *p_source, uint32_t length) { uint32_t status = FLASHIF_OK; uint32_t i = 0; /* Unlock the Flash to enable the flash control register access *************/ HAL_FLASH_Unlock(); /* DataLength must be a multiple of 64 bit */ for (i = 0; (i < length/2) && (destination <= (USER_FLASH_END_ADDRESS-8)); i++) { /* Device voltage range supposed to be [2.7V to 3.6V], the operation will be done by word */ if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, destination, *((uint64_t *)(p_source+2*i))) == HAL_OK) { /* Check the written value */ if (*(uint64_t*)destination != *(uint64_t *)(p_source+2*i)) { /* Flash content doesn't match SRAM content */ status = FLASHIF_WRITINGCTRL_ERROR; break; } /* Increment FLASH destination address */ destination += 8; } else { /* Error occurred while writing data in Flash memory */ status = FLASHIF_WRITING_ERROR; break; } } /* Lock the Flash to disable the flash control register access (recommended to protect the FLASH memory against possible unwanted operation) *********/ HAL_FLASH_Lock(); return status; } /** * @brief Returns the write protection status of application flash area. * @param None * @retval If a sector in application area is write-protected returned value is a combinaison of the possible values : FLASHIF_PROTECTION_WRPENABLED, FLASHIF_PROTECTION_PCROPENABLED, ... * If no sector is write-protected FLASHIF_PROTECTION_NONE is returned. */ uint32_t FLASH_If_GetWriteProtectionStatus(void) { uint32_t ProtectedPAGE = FLASHIF_PROTECTION_NONE; FLASH_OBProgramInitTypeDef OptionsBytesStruct1, OptionsBytesStruct2, OptionsBytesStruct3, OptionsBytesStruct4; /* Unlock the Flash to enable the flash control register access *************/ HAL_FLASH_Unlock(); OptionsBytesStruct1.WRPArea = OB_WRPAREA_BANK1_AREAA; OptionsBytesStruct1.PCROPConfig = FLASH_BANK_1; OptionsBytesStruct2.WRPArea = OB_WRPAREA_BANK1_AREAB; OptionsBytesStruct2.PCROPConfig = FLASH_BANK_1; OptionsBytesStruct3.WRPArea = OB_WRPAREA_BANK2_AREAA; OptionsBytesStruct3.PCROPConfig = FLASH_BANK_2; OptionsBytesStruct4.WRPArea = OB_WRPAREA_BANK2_AREAB; OptionsBytesStruct4.PCROPConfig = FLASH_BANK_2; /* Check if there are write protected sectors inside the user flash area ***/ HAL_FLASHEx_OBGetConfig(&OptionsBytesStruct1); HAL_FLASHEx_OBGetConfig(&OptionsBytesStruct2); HAL_FLASHEx_OBGetConfig(&OptionsBytesStruct3); HAL_FLASHEx_OBGetConfig(&OptionsBytesStruct4); /* Lock the Flash to disable the flash control register access (recommended to protect the FLASH memory against possible unwanted operation) *********/ HAL_FLASH_Lock(); /* Check PCROP areas */ if(OptionsBytesStruct1.PCROPEndAddr > OptionsBytesStruct1.PCROPStartAddr) { /* check if user area are included inside this range */ if(OptionsBytesStruct1.PCROPStartAddr > APP_ADDRESS) { ProtectedPAGE|= FLASHIF_PROTECTION_PCROPENABLED; } } if(OptionsBytesStruct2.PCROPEndAddr > OptionsBytesStruct2.PCROPStartAddr) { /* check if user area are included inside this range */ if(OptionsBytesStruct1.PCROPStartAddr > APP_ADDRESS) { ProtectedPAGE|= FLASHIF_PROTECTION_PCROPENABLED; } } /* check WRP */ if(OptionsBytesStruct1.WRPEndOffset > OptionsBytesStruct1.WRPStartOffset) { /* check if area is inside the WRP Range */ if((OptionsBytesStruct1.WRPStartOffset * FLASH_PAGE_SIZE + FLASH_BASE) >= APP_ADDRESS) { ProtectedPAGE|= FLASHIF_PROTECTION_WRPENABLED; } } if(OptionsBytesStruct2.WRPEndOffset > OptionsBytesStruct2.WRPStartOffset) { /* check if area is inside the WRP Range */ if((OptionsBytesStruct2.WRPStartOffset * FLASH_PAGE_SIZE + FLASH_BASE) >= APP_ADDRESS) { ProtectedPAGE|= FLASHIF_PROTECTION_WRPENABLED; } } if(OptionsBytesStruct3.WRPEndOffset > OptionsBytesStruct3.WRPStartOffset) { /* check if area is inside the WRP Range */ if((OptionsBytesStruct3.WRPStartOffset * FLASH_PAGE_SIZE + FLASH_BASE + FLASH_PAGE_SIZE * FLASH_PAGE_NBPERBANK) >= APP_ADDRESS) { ProtectedPAGE|= FLASHIF_PROTECTION_WRPENABLED; } } if(OptionsBytesStruct4.WRPEndOffset > OptionsBytesStruct4.WRPStartOffset) { /* check if area is inside the WRP Range */ if((OptionsBytesStruct4.WRPStartOffset * FLASH_PAGE_SIZE + FLASH_BASE + FLASH_PAGE_SIZE * FLASH_PAGE_NBPERBANK) >= APP_ADDRESS) { ProtectedPAGE|= FLASHIF_PROTECTION_WRPENABLED; } } if(OptionsBytesStruct4.RDPLevel == 1) { ProtectedPAGE|= FLASHIF_PROTECTION_RDPENABLED; } return ProtectedPAGE; } /** * @brief Configure the write protection status of user flash area. * @param protectionstate : FLASHIF_WRP_DISABLE or FLASHIF_WRP_ENABLE the protection * @retval uint32_t FLASHIF_OK if change is applied. */ uint32_t FLASH_If_WriteProtectionConfig(uint32_t protectionstate) { FLASH_OBProgramInitTypeDef OptionsBytesStruct1; HAL_StatusTypeDef retr; /* Unlock the Flash to enable the flash control register access *************/ retr = HAL_FLASH_Unlock(); /* Unlock the Options Bytes *************************************************/ retr|= HAL_FLASH_OB_Unlock(); OptionsBytesStruct1.OptionType = OPTIONBYTE_WRP; OptionsBytesStruct1.WRPArea = OB_WRPAREA_BANK1_AREAA; if( protectionstate == FLASHIF_WRP_ENABLE) { /* Enable the WRP protection for all flash BANK1 */ OptionsBytesStruct1.WRPEndOffset = FLASH_PAGE_NBPERBANK - 1; OptionsBytesStruct1.WRPStartOffset = 0x00; } else { /* Remove all WRP protection */ OptionsBytesStruct1.WRPEndOffset = 0x00; OptionsBytesStruct1.WRPStartOffset = 0xFF; } retr|= HAL_FLASHEx_OBProgram(&OptionsBytesStruct1); OptionsBytesStruct1.OptionType = OPTIONBYTE_WRP; OptionsBytesStruct1.WRPArea = OB_WRPAREA_BANK1_AREAB; OptionsBytesStruct1.WRPEndOffset = 0x00; OptionsBytesStruct1.WRPStartOffset = 0xFF; retr|= HAL_FLASHEx_OBProgram(&OptionsBytesStruct1); OptionsBytesStruct1.OptionType = OPTIONBYTE_WRP; OptionsBytesStruct1.WRPArea = OB_WRPAREA_BANK2_AREAA; if( protectionstate == FLASHIF_WRP_ENABLE) { /* Enable the WRP protection for all flash BANK1 */ OptionsBytesStruct1.WRPEndOffset = FLASH_PAGE_NBPERBANK - 1; OptionsBytesStruct1.WRPStartOffset = 0x00; } else { /* Remove all WRP protection */ OptionsBytesStruct1.WRPEndOffset = 0x00; OptionsBytesStruct1.WRPStartOffset = 0xFF; } retr|= HAL_FLASHEx_OBProgram(&OptionsBytesStruct1); OptionsBytesStruct1.RDPLevel = OB_RDP_LEVEL_0; OptionsBytesStruct1.OptionType = OPTIONBYTE_WRP; OptionsBytesStruct1.WRPArea = OB_WRPAREA_BANK2_AREAB; OptionsBytesStruct1.WRPEndOffset = 0x00; OptionsBytesStruct1.WRPStartOffset = 0xFF; retr|= HAL_FLASHEx_OBProgram(&OptionsBytesStruct1); return (retr == HAL_OK ? FLASHIF_OK: FLASHIF_PROTECTION_ERRROR); } /* 读取spi flash id. */ uint32_t spi_flash_read_id(void) { uint8_t cmd = SPI_CMD_READ_ID; uint8_t data[SPI_FLASH_ID_LEN] = {0}; /* 片选使能. */ SPI_FLASH_CS_ENABLE(); /* 发送read id命令. */ HAL_SPI_Transmit(SpiHandle, &cmd, 1, SPI_BUS_TIMEOUT); /* 读取3byte flash id. */ HAL_SPI_Receive(SpiHandle, data, SPI_FLASH_ID_LEN, SPI_BUS_TIMEOUT); /* 片选去使能. */ SPI_FLASH_CS_DISABLE(); return (data[0] << 16) | (data[1] << 8) | (data[2]); } /* 擦除 spi flash, 根据 cmd 传的不一样, 可以擦除 4K, 32K, 64K数据. 注意: 这里不检查 addr 和 cmd 的正确性, 请调用者自行保证. */ HAL_StatusTypeDef spi_flash_erase(uint32_t addr, uint8_t erase_cmd) { uint8_t cmd[SPI_CMD_HEADER_LEN] = {0}; /* 写使能. */ HAL_E_RETURN(_spi_flash_write_enable()); /* 等待写使能完成. */ HAL_E_RETURN(_spi_flash_wait_write()); /* 片选使能. */ SPI_FLASH_CS_ENABLE(); cmd[0] = erase_cmd; cmd[1] = (addr >> 16) & 0xFF; cmd[2] = (addr >> 8) & 0xFF;; cmd[3] = addr & 0xFF;; /* 发送擦除扇区命令. */ HAL_E_RETURN(HAL_SPI_Transmit(SpiHandle, cmd, SPI_CMD_HEADER_LEN, SPI_BUS_TIMEOUT)); /* 片选去使能. */ SPI_FLASH_CS_DISABLE(); /* 等待flash busy状态. */ HAL_E_RETURN(_spi_flash_wait_complete()); return HAL_OK; } /* 擦除整片 spi flash. */ HAL_StatusTypeDef spi_flash_erase_chip(void) { uint8_t cmd = SPI_CMD_CHIP_ERASE; /* 写使能. */ HAL_E_RETURN(_spi_flash_write_enable()); /* 等待写使能完成. */ HAL_E_RETURN(_spi_flash_wait_write()); /* 片选使能. */ SPI_FLASH_CS_ENABLE(); /* 发送擦除扇区命令. */ HAL_E_RETURN(HAL_SPI_Transmit(SpiHandle, &cmd, 1, SPI_BUS_TIMEOUT)); /* 片选去使能. */ SPI_FLASH_CS_DISABLE(); /* 等待flash busy状态. */ HAL_E_RETURN(_spi_flash_wait_complete()); return HAL_OK; } /* 从 spi flash 中读取数据. */ HAL_StatusTypeDef spi_flash_read(uint32_t addr, uint8_t* data, uint16_t size) { uint8_t cmd[SPI_CMD_HEADER_LEN] = {0}; /* 设备刚启动的时候, 读 flash 是全 0, 因为刚上电 flash 自己还没有初始化完成, 所以在这里等 flash busy 状态, 主要是保证读的时候 flash 是准备好的. */ HAL_E_RETURN(_spi_flash_wait_complete()); /* 片选使能. */ SPI_FLASH_CS_ENABLE(); cmd[0] = SPI_CMD_FLASH_READ; cmd[1] = (addr >> 16) & 0xFF; cmd[2] = (addr >> 8) & 0xFF;; cmd[3] = addr & 0xFF;; /* 发送 spi flash 读命令. */ HAL_E_RETURN(HAL_SPI_Transmit(SpiHandle, cmd, SPI_CMD_HEADER_LEN, SPI_BUS_TIMEOUT)); /* 读取size大小的数据到data. */ HAL_E_RETURN(HAL_SPI_Receive(SpiHandle, data, size, SPI_BUS_TIMEOUT)); /* 片选去使能. */ SPI_FLASH_CS_DISABLE(); return HAL_OK; } /* 向 spi flash 写入数据, 起始地址必须是页 (256Byte) 对齐的. */ HAL_StatusTypeDef spi_flash_write(uint32_t addr, uint8_t *data, uint16_t size) { uint8_t pages = 0; uint8_t first_bytes = 0; uint8_t last_bytes = 0; /* 这里划分好数据需要写多少页, 写大小. */ first_bytes = SPI_FLASH_PAGE_SIZE - (addr & (SPI_FLASH_PAGE_SIZE - 1)); if (first_bytes > size) first_bytes = size; last_bytes = (size - first_bytes) & (SPI_FLASH_PAGE_SIZE - 1); pages = (size - first_bytes - last_bytes) / SPI_FLASH_PAGE_SIZE; /* 写第一页. */ if (first_bytes > 0) { HAL_E_RETURN(_spi_flash_page_write(addr, data, first_bytes)); addr += first_bytes; data += first_bytes; } /* 写中间页. */ while(pages--) { HAL_E_RETURN(_spi_flash_page_write(addr, data, SPI_FLASH_PAGE_SIZE)); addr += SPI_FLASH_PAGE_SIZE; data += SPI_FLASH_PAGE_SIZE; } /* 剩余不足一页的部分. */ if (last_bytes > 0) HAL_E_RETURN(_spi_flash_page_write(addr, data, last_bytes)); return HAL_OK; } /************************ (C) COPYRIGHT LandPower *****END OF FILE****/