/** ********************************************************************* * * @file NVmem.c * @brief * * @date 2024-07-13 12:24:17 * @author CT * * @details Provides an interface for reading and writting to non-volatile memory * Operates with a shadow buffer and write timeout to minimize writes to NV-memory * ************************************************************************* **/ #include #include "stm32g0xx_hal.h" #include "NVmem.h" // write timeout static uint32_t m_last_write = 0; // static int32_t m_shadow_buf_synced = 0; // static uint8_t m_shadow_buf[NVMEM_SIZE] = {0}; // non-volatile memory location uint8_t __attribute__((section (".ConfigData"))) config_data[NVMEM_SIZE] __attribute__ ((aligned (2048))); static int write_flash(void); int32_t NVmem_init(void) { // copy flash to shadow buffer for (uint32_t i = 0; i < NVMEM_SIZE; i++) { m_shadow_buf[i] = config_data[i]; } return (0); } void NVmem_service(void) { // if the write timeout has expried and the shadow buffer has not been synced to NVmem if (((HAL_GetTick() - m_last_write) >= WRITE_TIMEOUT) && (!m_shadow_buf_synced)) { // write shadow buffer to nvmem write_flash(); // set synced flag m_shadow_buf_synced = 1; } } int32_t NVmem_write(uint8_t* data, uint32_t addr, uint32_t len) { // bound check if (addr + len >= NVMEM_SIZE) return (-1); // write data to shadow buf uint32_t data_index = 0; for (uint32_t i = addr; i < len; i++) { m_shadow_buf[i] = data[data_index++]; } // set sync flag to out of sync m_shadow_buf_synced = 0; return (0); } int32_t NVmem_write_immediate(uint8_t* data, uint32_t addr, uint32_t len) { int32_t ret = 0; // bound check if (addr + len >= NVMEM_SIZE) return (-1); // write data to shadow buf uint32_t data_index = 0; for (uint32_t i = addr; i < len; i++) { m_shadow_buf[i] = data[data_index++]; } // write shadow buffer to nvmem write_flash(); // set synced flag m_shadow_buf_synced = 1; return (ret); } int32_t NVmem_read(uint8_t* data, uint32_t addr, uint32_t len) { // bound check if (addr + len >= NVMEM_SIZE) return (-1); // copy data using provided pointer uint32_t index = 0; for (uint32_t i = addr; i < len; i++) { data[index++] = m_shadow_buf[i]; } return (0); } /* PRIVATE FUNCTIONS */ // Read from flash // Write to flash int write_flash(void) { uint32_t page_error = 0; HAL_StatusTypeDef status; HAL_FLASH_Unlock(); // Erase flash area FLASH_EraseInitTypeDef erase_init_struct; erase_init_struct.TypeErase = FLASH_TYPEERASE_PAGES; erase_init_struct.Banks = FLASH_BANK_1; erase_init_struct.Page = (0x801F800 - FLASH_BASE) / FLASH_PAGE_SIZE; erase_init_struct.NbPages = 1; status = HAL_FLASHEx_Erase(&erase_init_struct, &page_error); if (HAL_OK != status) { HAL_FLASH_Lock(); return (-1); } uint64_t buf; uint32_t len = NVMEM_SIZE; uint32_t prog_addr = 0x801F800; uint32_t index = 0; while (len >= 8) { buf = (uint64_t)m_shadow_buf[index]; buf |= ((uint64_t)m_shadow_buf[index + 1] << 8); buf |= ((uint64_t)m_shadow_buf[index + 2] << 16); buf |= ((uint64_t)m_shadow_buf[index + 3] << 24); buf |= ((uint64_t)m_shadow_buf[index + 4] << 32); buf |= ((uint64_t)m_shadow_buf[index + 5] << 40); buf |= ((uint64_t)m_shadow_buf[index + 6] << 48); buf |= ((uint64_t)m_shadow_buf[index + 7] << 56); status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, prog_addr, buf); index += 8; prog_addr += 8; len -= 8; } HAL_FLASH_Lock(); return ((int)status); }