531 lines
15 KiB
C
531 lines
15 KiB
C
/**
|
||
* @file flash.c
|
||
* @author xxx
|
||
* @date 2024-02-07 11:49:34
|
||
* @brief
|
||
* @copyright Copyright (c) 2024 by xxx, All Rights Reserved.
|
||
* @attention
|
||
*
|
||
* ST 的官方驱动 LL 库并没有 flash 驱动。这里自己实现。
|
||
*
|
||
* 1. 由于在 stm32l4xx_ll_system.h 中存在部分 FLASH 操作函数(ACR寄存器的处理)且不全面
|
||
* 因此这里需要额外处理(重命名)
|
||
*
|
||
* 2. Main memory
|
||
* (1) FLASH_ACR 完成
|
||
* (2) FLASH_PDKEYR 完成
|
||
* (3) FLASH_KEYR 完成
|
||
* (4) FLASH_OPTKEYR 完成
|
||
* (5) FLASH_SR 完成
|
||
* (6) FLASH_CR 完成
|
||
* (7) FLASH_ECCR 完成
|
||
* (8) FLASH_OPTR 未完成
|
||
* (9) FLASH_PCROP1SR 未完成
|
||
* 后续寄存器 均未完成
|
||
* 3. Information block
|
||
* - System memory
|
||
* - OTP area
|
||
* - Option bytes
|
||
* 4. 根据 HAL 库的实现,相比于与手册的推荐流程,擦写的执行序列还有其他操作。
|
||
*
|
||
* 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 "flash.h"
|
||
#include "stm32l4xx_ll_rcc.h"
|
||
#include "stm32l4xx_ll_system.h"
|
||
#include "stm32l4xx_ll_pwr.h"
|
||
#ifdef USE_FULL_ASSERT
|
||
#include "stm32_assert.h"
|
||
#else
|
||
#define assert_param(expr) ((void)0U)
|
||
#endif /* USE_FULL_ASSERT */
|
||
|
||
#define WHILE_MAX 10000U
|
||
/** @addtogroup STM32L4xx_LL_Driver
|
||
* @{
|
||
*/
|
||
|
||
/** @addtogroup FLASH_LL
|
||
* @{
|
||
*/
|
||
|
||
/* Private types -------------------------------------------------------------*/
|
||
/* Private variables ---------------------------------------------------------*/
|
||
/* Private constants ---------------------------------------------------------*/
|
||
/** @addtogroup FLASH_LL_Private_Constants
|
||
* @{
|
||
*/
|
||
|
||
/**
|
||
* @}
|
||
*/
|
||
|
||
/* Private macros ------------------------------------------------------------*/
|
||
/** @addtogroup FLASH_LL_Private_Macros
|
||
* @{
|
||
*/
|
||
|
||
#define IS_LL_FLASH_WRITE_ADDR(__ADDR__) ((__ADDR__) % LL_FLASH_ALIGNMENT_MIN_SIZE == 0)
|
||
|
||
/**
|
||
* @}
|
||
*/
|
||
/* Private function prototypes -----------------------------------------------*/
|
||
/** @defgroup FLASH_LL_Private_Functions FLASH Private functions
|
||
* @{
|
||
*/
|
||
|
||
/**
|
||
* @}
|
||
*/
|
||
|
||
/* Exported functions --------------------------------------------------------*/
|
||
/** @addtogroup FLASH_LL_Exported_Functions
|
||
* @{
|
||
*/
|
||
/**
|
||
* @brief Clear All Error in SR
|
||
* @param FLASHx FLASH Instance
|
||
* @retval None
|
||
*/
|
||
void LL_FLASH_ClearAllErrorFlag(void)
|
||
{
|
||
LL_FLASH_ClearFlag_OPTVERR(FLASH);
|
||
LL_FLASH_ClearFlag_RDERR(FLASH);
|
||
LL_FLASH_ClearFlag_FASTERR(FLASH);
|
||
LL_FLASH_ClearFlag_MISERR(FLASH);
|
||
LL_FLASH_ClearFlag_PGSERR(FLASH);
|
||
LL_FLASH_ClearFlag_SIZERR(FLASH);
|
||
LL_FLASH_ClearFlag_PGAERR(FLASH);
|
||
LL_FLASH_ClearFlag_WRPERR(FLASH);
|
||
LL_FLASH_ClearFlag_PROGERR(FLASH);
|
||
LL_FLASH_ClearFlag_OPERR(FLASH);
|
||
}
|
||
|
||
/**
|
||
* @brief Flush the instruction and data caches.
|
||
* @retval None
|
||
*/
|
||
void LL_FLASH_FlushCaches(void)
|
||
{
|
||
/* Flush instruction cache */
|
||
if (LL_FLASH_IsEnabledInstructionCache(FLASH))
|
||
{
|
||
LL_FLASH_DisableInstructionCache(FLASH);
|
||
/* Reset instruction cache */
|
||
LL_FLASH_InstructionCacheReset(FLASH);
|
||
/* Enable instruction cache */
|
||
LL_FLASH_EnableInstructionCache(FLASH);
|
||
}
|
||
|
||
/* Flush data cache */
|
||
if (LL_FLASH_IsEnabledDataCache(FLASH))
|
||
{
|
||
LL_FLASH_ZCS_DisableDataCache(FLASH);
|
||
/* Reset data cache */
|
||
LL_FLASH_DataCacheReset(FLASH);
|
||
/* Enable data cache */
|
||
LL_FLASH_ZCS_EnableDataCache(FLASH);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief Erase Page
|
||
* @param pageno Page number
|
||
* @retval None
|
||
*/
|
||
ErrorStatus LL_FLASH_ErasePage(uint32_t pageno)
|
||
{
|
||
uint16_t count = 0;
|
||
/* Check that no Flash memory operation is ongoing by checking the BSY bit */
|
||
while (LL_FLASH_IsActiveFlag_BSY(FLASH))
|
||
{
|
||
if (count++ > WHILE_MAX)
|
||
{
|
||
return ERROR;
|
||
}
|
||
}
|
||
count = 0;
|
||
|
||
/* Check and clear all error programming flags due to a previous programming. If not, PGSERR is set. */
|
||
LL_FLASH_ClearAllErrorFlag();
|
||
|
||
/* Set the PER bit and select the page you wish to erase (PNB) with the associated bank (BKER) in the Flash control register (FLASH_CR). */
|
||
LL_FLASH_EnablePageErase(FLASH);
|
||
|
||
if (pageno >= LL_FLASH_BANK1_PAGE_NUM)
|
||
{
|
||
pageno -= LL_FLASH_BANK1_PAGE_NUM;
|
||
LL_FLASH_SetErasePageBank(FLASH, LL_FLASH_BANK2);
|
||
}
|
||
else
|
||
{
|
||
LL_FLASH_SetErasePageBank(FLASH, LL_FLASH_BANK1);
|
||
}
|
||
|
||
LL_FLASH_SetErasePageNo(FLASH, pageno);
|
||
|
||
/* Set the STRT bit in the FLASH_CR register. */
|
||
LL_FLASH_EraseStart(FLASH);
|
||
|
||
/* Wait for the BSY bit to be cleared in the FLASH_SR register. */
|
||
while (LL_FLASH_IsActiveFlag_BSY(FLASH))
|
||
{
|
||
if (count++ > WHILE_MAX)
|
||
{
|
||
return ERROR;
|
||
}
|
||
}
|
||
count = 0;
|
||
|
||
/* 完成只有需要清除擦除标志. */
|
||
LL_FLASH_DisablePageErase(FLASH);
|
||
|
||
/* Flush the caches to be sure of the data consistency */
|
||
LL_FLASH_FlushCaches();
|
||
|
||
return SUCCESS;
|
||
}
|
||
|
||
/**
|
||
* @brief Erase bank
|
||
* @param bank This parameter can be one of the following values:
|
||
* @arg @ref LL_FLASH_BANK1
|
||
* @arg @ref LL_FLASH_BANK2
|
||
* @retval None
|
||
*/
|
||
ErrorStatus LL_FLASH_EraseBank(uint32_t bank)
|
||
{
|
||
uint16_t count = 0;
|
||
/* Check that no Flash memory operation is ongoing by checking the BSY bit */
|
||
while (LL_FLASH_IsActiveFlag_BSY(FLASH))
|
||
{
|
||
if (count++ > WHILE_MAX)
|
||
{
|
||
return ERROR;
|
||
}
|
||
}
|
||
count = 0;
|
||
|
||
/* Check and clear all error programming flags due to a previous programming. If not, PGSERR is set. */
|
||
LL_FLASH_ClearAllErrorFlag();
|
||
|
||
/* Set the MER1 bit or/and MER2 (depending on the bank) in the Flash control register (FLASH_CR).
|
||
Both banks can be selected in the same operation. */
|
||
if (bank == LL_FLASH_BANK1)
|
||
{
|
||
LL_FLASH_EnableBank1Erase(FLASH);
|
||
}
|
||
else
|
||
{
|
||
LL_FLASH_EnableBank2Erase(FLASH);
|
||
}
|
||
|
||
/* Set the STRT bit in the FLASH_CR register. */
|
||
LL_FLASH_EraseStart(FLASH);
|
||
|
||
/* Wait for the BSY bit to be cleared in the FLASH_SR register. */
|
||
while (LL_FLASH_IsActiveFlag_BSY(FLASH))
|
||
{
|
||
if (count++ > WHILE_MAX)
|
||
{
|
||
return ERROR;
|
||
}
|
||
}
|
||
count = 0;
|
||
|
||
/* 完成只有需要清除擦除标志. */
|
||
LL_FLASH_DisableBank1Erase(FLASH);
|
||
LL_FLASH_DisableBank2Erase(FLASH);
|
||
|
||
/* Flush the caches to be sure of the data consistency */
|
||
LL_FLASH_FlushCaches();
|
||
return SUCCESS;
|
||
}
|
||
|
||
/**
|
||
* @brief Erase Chip
|
||
* @param None
|
||
* @retval None
|
||
*/
|
||
ErrorStatus LL_FLASH_EraseChip(void)
|
||
{
|
||
uint16_t count = 0;
|
||
/* Check that no Flash memory operation is ongoing by checking the BSY bit */
|
||
while (LL_FLASH_IsActiveFlag_BSY(FLASH))
|
||
{
|
||
if (count++ > WHILE_MAX)
|
||
{
|
||
return ERROR;
|
||
}
|
||
}
|
||
count = 0;
|
||
|
||
/* Check and clear all error programming flags due to a previous programming. If not, PGSERR is set. */
|
||
LL_FLASH_ClearAllErrorFlag();
|
||
|
||
/* Set the MER1 bit or/and MER2 (depending on the bank) in the Flash control register (FLASH_CR).
|
||
Both banks can be selected in the same operation. */
|
||
LL_FLASH_EnableBank1Erase(FLASH);
|
||
LL_FLASH_EnableBank2Erase(FLASH);
|
||
|
||
/* Set the STRT bit in the FLASH_CR register. */
|
||
LL_FLASH_EraseStart(FLASH);
|
||
|
||
/* Wait for the BSY bit to be cleared in the FLASH_SR register. */
|
||
while (LL_FLASH_IsActiveFlag_BSY(FLASH))
|
||
{
|
||
if (count++ > WHILE_MAX)
|
||
{
|
||
return ERROR;
|
||
}
|
||
}
|
||
count = 0;
|
||
|
||
/* 完成只有需要清除擦除标志. */
|
||
LL_FLASH_DisableBank1Erase(FLASH);
|
||
LL_FLASH_DisableBank2Erase(FLASH);
|
||
|
||
/* Flush the caches to be sure of the data consistency */
|
||
LL_FLASH_FlushCaches();
|
||
return SUCCESS;
|
||
}
|
||
|
||
/**
|
||
* @brief Program Double Word
|
||
* @param address specifies the address to be programmed.
|
||
* @param data specifies the data to be programmed.
|
||
* @retval An ErrorStatus enumeration value:
|
||
* - SUCCESS: Write successfully
|
||
* - ERROR: error
|
||
*/
|
||
ErrorStatus LL_FLASH_ProgramDoubleWord(uint32_t address, uint64_t data)
|
||
{
|
||
assert_param(!IS_LL_FLASH_WRITE_ADDR(address));
|
||
uint16_t count = 0;
|
||
/* Check that no Flash memory operation is ongoing by checking the BSY bit */
|
||
while (LL_FLASH_IsActiveFlag_BSY(FLASH))
|
||
{
|
||
if (count++ > WHILE_MAX)
|
||
{
|
||
return ERROR;
|
||
}
|
||
}
|
||
count = 0;
|
||
/* Check and clear all error programming flags due to a previous programming. If not, PGSERR is set. */
|
||
LL_FLASH_ClearAllErrorFlag();
|
||
|
||
/* Set the PG bit in the Flash control register (FLASH_CR). */
|
||
LL_FLASH_EnableProgram(FLASH);
|
||
|
||
/* Perform the data write operation at the desired memory address, inside main memory
|
||
block or OTP area. Only double word can be programmed. */
|
||
/* Program the double word */
|
||
*(__IO uint32_t *)address = (uint32_t)data;
|
||
*(__IO uint32_t *)(address + 4U) = (uint32_t)(data >> 32);
|
||
|
||
/* Check that no Flash memory operation is ongoing by checking the BSY bit */
|
||
while (LL_FLASH_IsActiveFlag_BSY(FLASH))
|
||
{
|
||
if (count++ > WHILE_MAX)
|
||
{
|
||
return ERROR;
|
||
}
|
||
}
|
||
count = 0;
|
||
/* Check that EOP flag is set in the FLASH_SR register (meaning that the programming
|
||
operation has succeed), and clear it by software. */
|
||
if (LL_FLASH_IsActiveFlag_EOP(FLASH))
|
||
{
|
||
LL_FLASH_ClearFlag_EOP(FLASH);
|
||
}
|
||
|
||
/* Clear the PG bit in the FLASH_CR register if there no more programming request anymore. */
|
||
LL_FLASH_DisableProgram(FLASH);
|
||
|
||
/* Flush the caches to be sure of the data consistency */
|
||
LL_FLASH_FlushCaches();
|
||
|
||
return SUCCESS;
|
||
}
|
||
|
||
/**
|
||
* @brief Program
|
||
* @param address specifies the address to be programmed.
|
||
* @param data specifies the data to be programmed.
|
||
* @param num specifies the data number
|
||
* @retval An ErrorStatus enumeration value:
|
||
* - SUCCESS: Write successfully
|
||
* - ERROR: error
|
||
*/
|
||
ErrorStatus LL_FLASH_Program(uint32_t address, uint8_t data[], uint32_t num)
|
||
{
|
||
static uint64_t DataT = 0;
|
||
uint32_t T = 0, S = 0;
|
||
uint16_t count = 0;
|
||
assert_param(!IS_LL_FLASH_WRITE_ADDR(address));
|
||
|
||
if (num == 0)
|
||
{
|
||
return SUCCESS;
|
||
}
|
||
|
||
/* Check that no Flash memory operation is ongoing by checking the BSY bit */
|
||
while (LL_FLASH_IsActiveFlag_BSY(FLASH))
|
||
{
|
||
if (count++ > WHILE_MAX)
|
||
{
|
||
return ERROR;
|
||
}
|
||
}
|
||
count = 0;
|
||
/* Check and clear all error programming flags due to a previous programming. If not, PGSERR is set. */
|
||
LL_FLASH_ClearAllErrorFlag();
|
||
|
||
/* Set the PG bit in the Flash control register (FLASH_CR). */
|
||
LL_FLASH_EnableProgram(FLASH);
|
||
|
||
/* Perform the data write operation at the desired memory address, inside main memory
|
||
block or OTP area. Only double word can be programmed. */
|
||
T = num;
|
||
while (num > 0)
|
||
{
|
||
DataT = 0;
|
||
if (num >= 8)
|
||
{
|
||
for (int i = 0; i < LL_FLASH_ALIGNMENT_MIN_SIZE; i++)
|
||
{
|
||
DataT = DataT << 8;
|
||
DataT |= data[S + 7 - i];
|
||
}
|
||
S += LL_FLASH_ALIGNMENT_MIN_SIZE;
|
||
num -= LL_FLASH_ALIGNMENT_MIN_SIZE;
|
||
}
|
||
else
|
||
{
|
||
for (int i = 0; i < num; i++)
|
||
{
|
||
DataT = DataT << 8;
|
||
DataT |= data[T - 1 - i];
|
||
}
|
||
num = 0;
|
||
}
|
||
|
||
/* Program the double word */
|
||
*(__IO uint32_t *)address = (uint32_t)DataT;
|
||
*(__IO uint32_t *)(address + 4U) = (uint32_t)(DataT >> 32);
|
||
|
||
/* Check that no Flash memory operation is ongoing by checking the BSY bit */
|
||
while (LL_FLASH_IsActiveFlag_BSY(FLASH))
|
||
{
|
||
if (count++ > WHILE_MAX)
|
||
{
|
||
return ERROR;
|
||
}
|
||
}
|
||
count = 0;
|
||
|
||
/* Check that EOP flag is set in the FLASH_SR register (meaning that the programming
|
||
operation has succeed), and clear it by software. */
|
||
if (LL_FLASH_IsActiveFlag_EOP(FLASH))
|
||
{
|
||
LL_FLASH_ClearFlag_EOP(FLASH);
|
||
}
|
||
|
||
address += LL_FLASH_ALIGNMENT_MIN_SIZE;
|
||
}
|
||
|
||
/* Clear the PG bit in the FLASH_CR register if there no more programming request anymore. */
|
||
LL_FLASH_DisableProgram(FLASH);
|
||
|
||
/* Flush the caches to be sure of the data consistency */
|
||
LL_FLASH_FlushCaches();
|
||
|
||
return SUCCESS;
|
||
}
|
||
|
||
/**
|
||
* @}
|
||
*/
|
||
|
||
/** @addtogroup FLASH_LL_Private_Functions
|
||
* @{
|
||
*/
|
||
/**
|
||
* @brief Fast program a row double-word (64-bit) at a specified address.
|
||
* @param address: specifies the address to be programmed.
|
||
* @param DataAddress: specifies the address where the data are stored.
|
||
* @retval None
|
||
*/
|
||
void LL_FLASH_ProgramFast(uint32_t address, uint32_t DataAddress)
|
||
{
|
||
uint8_t row_index = (2 * LL_FLASH_ROW_SIZE);
|
||
__IO uint32_t *dest_addr = (__IO uint32_t *)address;
|
||
__IO uint32_t *src_addr = (__IO uint32_t *)DataAddress;
|
||
|
||
/* Check the parameters */
|
||
assert_param(IS_FLASH_MAIN_MEM_ADDRESS(address));
|
||
|
||
/* Set FSTPG bit */
|
||
LL_FLASH_EnableFastProgram(FLASH);
|
||
|
||
/* Disable interrupts to avoid any interruption during the loop */
|
||
__disable_irq();
|
||
|
||
/* Program the double word of the row */
|
||
do
|
||
{
|
||
*dest_addr = *src_addr;
|
||
dest_addr++;
|
||
src_addr++;
|
||
row_index--;
|
||
} while (row_index != 0U);
|
||
|
||
/* Re-enable the interrupts */
|
||
__enable_irq();
|
||
|
||
LL_FLASH_DisableFastProgram(FLASH);
|
||
}
|
||
|
||
/**
|
||
* @brief Fast program a row double-word (64-bit) at a specified address.
|
||
* @param address: specifies the address to be programmed.
|
||
* @param data: specifies the data to be programmed.
|
||
* @retval None
|
||
*/
|
||
ErrorStatus LL_FLASH_Read(uint32_t address, uint8_t data[], uint32_t num)
|
||
{
|
||
if (num == 0)
|
||
{
|
||
return SUCCESS;
|
||
}
|
||
for (uint32_t i = 0; i < num; i++)
|
||
{
|
||
data[i] = *(__IO uint8_t *)(address + i);
|
||
}
|
||
|
||
return SUCCESS;
|
||
}
|
||
|
||
/**
|
||
* @}
|
||
*/
|
||
|
||
/**
|
||
* @}
|
||
*/
|
||
|
||
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
|