JackCarterSmith 8d796385ee
Some fix
Missalign PMIC IRQ flag
2025-05-05 21:23:48 +02:00

427 lines
15 KiB
C

//EEPROM emulation library for STM32F1XX with HAL-Driver
//V2.0
//includes
#include "eeprom.h"
//private function prototypes;
static EEPROM_Result EEPROM_PageTransfer();
static EEPROM_Result EEPROM_SetPageStatus(EEPROM_Page Page, EEPROM_PageStatus PageStatus);
static EEPROM_Result EEPROM_PageToIndex(EEPROM_Page Page);
//global variables
static uint8_t EEPROM_SizeTable[EEPROM_VARIABLE_COUNT]; //EEPROM_SizeTable[i]: actual size of variable i (as EEPROM_Size)
static uint16_t EEPROM_Index[EEPROM_VARIABLE_COUNT]; //EEPROM_Index[i]: actual address of variable i (physical address = EEPROM_START_ADDRESS + EEPROM_Index[i])
//if EEPROM_Index[i] = 0 variable i not assigned
static uint32_t EEPROM_ValidPage = EEPROM_PAGE_NONE;
static uint32_t EEPROM_ReceivingPage = EEPROM_PAGE_NONE;
static uint32_t EEPROM_ErasedPage = EEPROM_PAGE_NONE;
static uint32_t EEPROM_NextIndex = 0;
// initialize the EEPROM & restore the pages to a known good state in case of page's status corruption after a power loss
// - unlock flash
// - read each page status and check if valid
// - if invalid page status, format EEPROM
// - set global variables ValidPage, ReceivingPage and ErasedPage
// - build address index
// - resume page transfer if needed
//
// return: EEPROM_SUCCESS, EEPROM_NO_VALID_PAGE, EEPROM_FULL, EEPROM_ERROR, EEPROM_BUSY, EEPROM_TIMEOUT
EEPROM_Result EEPROM_Init()
{
EEPROM_Result result;
//unlock the flash memory
HAL_FLASH_Unlock();
//read each page status and check if valid
EEPROM_PageStatus PageStatus0 = *((__IO uint16_t*) EEPROM_PAGE0);
EEPROM_PageStatus PageStatus1 = *((__IO uint16_t*) EEPROM_PAGE1);
uint8_t InvalidState = 0;
if (PageStatus0 != EEPROM_VALID && PageStatus0 != EEPROM_RECEIVING && PageStatus0 != EEPROM_ERASED) InvalidState = 1;
if (PageStatus1 != EEPROM_VALID && PageStatus1 != EEPROM_RECEIVING && PageStatus1 != EEPROM_ERASED) InvalidState = 1;
if (PageStatus0 == PageStatus1) InvalidState = 1;
// if invalid page status, format EEPROM (erase both pages and set page0 as valid)
if (InvalidState)
{
FLASH_EraseInitTypeDef EraseDefinitions;
EraseDefinitions.TypeErase = FLASH_TYPEERASE_PAGES;
EraseDefinitions.Banks = FLASH_BANK_1;
EraseDefinitions.PageAddress = EEPROM_PAGE0;
EraseDefinitions.NbPages = 2;
uint32_t PageError;
result = (EEPROM_Result)HAL_FLASHEx_Erase(&EraseDefinitions, &PageError);
if (result != EEPROM_SUCCESS) return result;
result = (EEPROM_Result)HAL_FLASH_Program(EEPROM_SIZE16, EEPROM_PAGE0, EEPROM_VALID);
if (result != EEPROM_SUCCESS) return result;
PageStatus0 = EEPROM_VALID;
PageStatus1 = EEPROM_ERASED;
}
//set global variables ValidPage, ReceivingPage and ErasedPage (one stays EEPROM_PAGE_NONE)
if (PageStatus0 == EEPROM_VALID) EEPROM_ValidPage = EEPROM_PAGE0;
if (PageStatus1 == EEPROM_VALID) EEPROM_ValidPage = EEPROM_PAGE1;
if (PageStatus0 == EEPROM_RECEIVING) EEPROM_ReceivingPage = EEPROM_PAGE0;
if (PageStatus1 == EEPROM_RECEIVING) EEPROM_ReceivingPage = EEPROM_PAGE1;
if (PageStatus0 == EEPROM_ERASED) EEPROM_ErasedPage = EEPROM_PAGE0;
if (PageStatus1 == EEPROM_ERASED) EEPROM_ErasedPage = EEPROM_PAGE1;
//build address index (addresses from receiving page are dominant)
EEPROM_PageToIndex(EEPROM_ValidPage);
EEPROM_PageToIndex(EEPROM_ReceivingPage);
//if needed, resume page transfer or just mark receiving page as valid
if (EEPROM_ReceivingPage != EEPROM_PAGE_NONE)
{
if (EEPROM_ValidPage == EEPROM_PAGE_NONE)
{
result = EEPROM_SetPageStatus(EEPROM_ReceivingPage, EEPROM_VALID);
if (result != EEPROM_SUCCESS) return result;
}
else
{
result = EEPROM_PageTransfer(EEPROM_ValidPage, EEPROM_ReceivingPage);
if (result != EEPROM_SUCCESS) return result;
}
}
return EEPROM_SUCCESS;
}
// returns the last stored variable value which correspond to the passed variable name
// - check if variable name exists
// - check if variable was assigned
// - read variable value from physical address with right size
//
// VariableName: name (number) of the variable to read
// Value: outputs the variable value
// return: EEPROM_SUCCESS, EEPROM_INVALID_NAME, EEPROM_NOT_ASSIGNED
EEPROM_Result EEPROM_ReadVariable(uint16_t VariableName, EEPROM_Value* Value)
{
//check if variable name exists
if (VariableName >= EEPROM_VARIABLE_COUNT) return EEPROM_INVALID_NAME;
//check if variable was assigned
uint32_t Address = EEPROM_START_ADDRESS + EEPROM_Index[VariableName];
if (Address == EEPROM_PAGE0) return EEPROM_NOT_ASSIGNED;
//read variable value from physical address with right size
switch (EEPROM_SizeTable[VariableName])
{
case EEPROM_SIZE16: (*Value).uInt16 = *((__IO uint16_t*) Address); break;
case EEPROM_SIZE32: (*Value).uInt32 = *((__IO uint32_t*) Address); break;
case EEPROM_SIZE64: (*Value).uInt64 = *((__IO uint32_t*) Address) | ((uint64_t) *((__IO uint32_t*) (Address + 4)) << 32); break;
default: return EEPROM_NOT_ASSIGNED;
}
return EEPROM_SUCCESS;
}
// writes variable in EEPROM if page not full
// - get writing page's end address
// - calculate memory usage of variable
// - check if page full
// - check if data is too much to store on one page
// - mark the target page as receiving
// - change next index to receiving page
// - write the variable to target page
// - do page transfer
// - else (if enough space)
// - write variable value
// - create and write variable header (size and name)
// - update index & size table
// - update next index
//
// VariableName: name (number) of the variable to write
// Value: value to be written
// Size: size of "Value" as EEPROM_Size
// return: EEPROM_SUCCESS, EEPROM_NO_VALID_PAGE, EEPROM_FULL, EEPROM_ERROR, EEPROM_BUSY, EEPROM_TIMEOUT
EEPROM_Result EEPROM_WriteVariable(uint16_t VariableName, EEPROM_Value Value, uint8_t Size)
{
EEPROM_Result result;
//get writing page's end address (prefer writing to receiving page)
EEPROM_Page WritingPage = EEPROM_ValidPage;
if (EEPROM_ReceivingPage != EEPROM_PAGE_NONE) WritingPage = EEPROM_ReceivingPage;
if (WritingPage == EEPROM_PAGE_NONE) return EEPROM_NO_VALID_PAGE;
uint32_t PageEndAddress = WritingPage + FLASH_PAGE_SIZE;
//calculate memory usage of variable
uint8_t Bytes = (uint8_t)(2 + (1 << Size));
if (Size == EEPROM_SIZE_DELETED) Bytes = 2;
//check if enough free space or page full
if (EEPROM_NextIndex == 0 || PageEndAddress - EEPROM_NextIndex < Bytes)
{
//check if data is too much to store on one page
uint16_t RequiredMemory = 2;
for (uint16_t i = 0; i < EEPROM_VARIABLE_COUNT; i++)
{
if (i == VariableName) RequiredMemory += (uint16_t)(2 + (1 << Size));
else if (EEPROM_SizeTable[i] != EEPROM_SIZE_DELETED) RequiredMemory += (uint16_t)(2 + (1 << EEPROM_SizeTable[i]));
}
if (RequiredMemory > FLASH_PAGE_SIZE) return EEPROM_FULL;
//mark the empty page as receiving
result = EEPROM_SetPageStatus(EEPROM_ErasedPage, EEPROM_RECEIVING);
if (result != EEPROM_SUCCESS) return result;
//change next index to receiving page
EEPROM_NextIndex = EEPROM_ReceivingPage + 2;
//write the variable to receiving page (by calling this function again)
result = EEPROM_WriteVariable(VariableName, Value, Size);
if (result != EEPROM_SUCCESS) return result;
//do page transfer
result = EEPROM_PageTransfer();
if (result != EEPROM_SUCCESS) return result;
}
//else (if enough space)
else
{
//write variable value
if (Size != EEPROM_SIZE_DELETED)
{
result = (EEPROM_Result)HAL_FLASH_Program(Size, EEPROM_NextIndex + 2, Value.uInt64);
if (result != EEPROM_SUCCESS) return result;
}
//create and write variable header (size and name)
uint16_t VariableHeader = (uint16_t)(VariableName + (Size << 14));
result = (EEPROM_Result)HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, EEPROM_NextIndex, VariableHeader);
if (result != EEPROM_SUCCESS) return result;
//update index & size table
EEPROM_Index[VariableName] = (uint16_t)(EEPROM_NextIndex + 2 - EEPROM_START_ADDRESS);
EEPROM_SizeTable[VariableName] = Size;
if (Size == EEPROM_SIZE_DELETED) EEPROM_Index[VariableName] = 0;
//update next index
EEPROM_NextIndex += Bytes;
if (EEPROM_NextIndex >= PageEndAddress) EEPROM_NextIndex = 0;
}
return EEPROM_SUCCESS;
}
//marks a variable as deleted so it can't be read anymore and is discarded on next page transfer
// - call write variable with size EEPROM_SIZE_DELETED
//
// VariableName: name (number) of the variable to delete
// return: EEPROM_SUCCESS, EEPROM_NO_VALID_PAGE, EEPROM_FULL, EEPROM_ERROR, EEPROM_BUSY, EEPROM_TIMEOUT
EEPROM_Result EEPROM_DeleteVariable(uint16_t VariableName)
{
return EEPROM_WriteVariable(VariableName, (EEPROM_Value) (uint16_t) 0, EEPROM_SIZE_DELETED);
}
// transfers latest variable values from valid page to receiving page
// - get start & end address of valid page (source)
// - copy each variable
// - check if is stored on the source page
// - read variable value
// - write variable to receiving page
// - erase source page
// - mark receiving page as valid
//
// return: EEPROM_SUCCESS, EEPROM_NO_VALID_PAGE, EEPROM_FULL, EEPROM_ERROR, EEPROM_BUSY, EEPROM_TIMEOUT
static EEPROM_Result EEPROM_PageTransfer()
{
EEPROM_Result result;
EEPROM_Value Value;
//get start & end address of valid page (source) (as offset to EEPROM start)
uint16_t StartAddress = (uint16_t)(EEPROM_ValidPage - EEPROM_START_ADDRESS);
uint16_t EndAddress = (uint16_t)(EEPROM_ValidPage - EEPROM_START_ADDRESS + FLASH_PAGE_SIZE);
//copy each variable
for (uint16_t i = 0; i < EEPROM_VARIABLE_COUNT; i++)
{
//check if is stored on the source page
if (StartAddress < EEPROM_Index[i] && EEPROM_Index[i] < EndAddress)
{
//read variable value (if possible)
if (EEPROM_ReadVariable(i, &Value) == EEPROM_SUCCESS)
{
//write variable to receiving page
result = EEPROM_WriteVariable(i, Value, EEPROM_SizeTable[i]);
if (result != EEPROM_SUCCESS) return result;
}
}
}
//erase source page
result = EEPROM_SetPageStatus(EEPROM_ValidPage, EEPROM_ERASED);
if (result != EEPROM_SUCCESS) return result;
//mark receiving page as valid
result = EEPROM_SetPageStatus(EEPROM_ReceivingPage, EEPROM_VALID);
if (result != EEPROM_SUCCESS) return result;
return EEPROM_SUCCESS;
}
// sets the page status and updates references from global variables
// - check if erase operation required
// - remove every variable from index, that is stored on erase page
// - setup erase definitions
// - erase page
// - else write status to flash
// - update global page status variables
//
// Page: page to change the status (as EEPROM_Page)
// PageStatus: page status to set for page (as EEPROM_PageStatus)
// return: EEPROM_SUCCESS, EEPROM_ERROR, EEPROM_BUSY or EEPROM_TIMEOUT
static EEPROM_Result EEPROM_SetPageStatus(EEPROM_Page Page, EEPROM_PageStatus PageStatus)
{
EEPROM_Result result;
//check if erase operation required
if (PageStatus == EEPROM_ERASED)
{
//remove every variable from index, that is stored on erase page
uint16_t StartAddress = (uint16_t)(Page - EEPROM_START_ADDRESS);
uint16_t EndAddress = (uint16_t)(Page - EEPROM_START_ADDRESS + FLASH_PAGE_SIZE);
for (uint16_t i = 0; i < EEPROM_VARIABLE_COUNT; i++)
{
if (StartAddress < EEPROM_Index[i] && EEPROM_Index[i] < EndAddress) EEPROM_Index[i] = 0;
}
//setup erase definitions
FLASH_EraseInitTypeDef EraseDefinitions;
EraseDefinitions.TypeErase = FLASH_TYPEERASE_PAGES;
EraseDefinitions.Banks = FLASH_BANK_1;
EraseDefinitions.PageAddress = Page;
EraseDefinitions.NbPages = 1;
uint32_t PageError;
//erase page
result = (EEPROM_Result)HAL_FLASHEx_Erase(&EraseDefinitions, &PageError);
if (result != EEPROM_SUCCESS) return result;
}
//else write status to flash
else
{
result = (EEPROM_Result)HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, Page, PageStatus);
if (result != EEPROM_SUCCESS) return result;
}
//update global page status variables (remove page from old status and attach to new status)
if (EEPROM_ValidPage == Page) EEPROM_ValidPage = EEPROM_PAGE_NONE;
else if (EEPROM_ReceivingPage == Page) EEPROM_ReceivingPage = EEPROM_PAGE_NONE;
else if (EEPROM_ErasedPage == Page) EEPROM_ErasedPage = EEPROM_PAGE_NONE;
if (PageStatus == EEPROM_VALID) EEPROM_ValidPage = Page;
else if (PageStatus == EEPROM_RECEIVING) EEPROM_ReceivingPage = Page;
else if (PageStatus == EEPROM_ERASED) EEPROM_ErasedPage = Page;
return EEPROM_SUCCESS;
}
// reads the whole page, fills the index with variable addresses and the size table with variable sizes
// - declare variables
// - ignore call when Page is PAGE_NONE
// - get page addresses
// - loop through page addresses
// - read potential variable header
// - if no header written
// - loop through next 4 halfword and check if there is anything written
// - while looping count the size of written data
// - if no data found, last variable of page was reached (return)
// - else (if header written)
// - get size code
// - check for valid name
// - if everything valid, update the index and the size table
// - calculate size in bytes from size code
// - go to next address on page
// - set next free flash address
// - return on loop end
//
// Page: page to search for variables
// return: EEPROM_SUCCESS
static EEPROM_Result EEPROM_PageToIndex(EEPROM_Page Page)
{
//declare variables
uint16_t VariableHeader; //header of current variable (first 2 bits size code, rest name)
uint8_t SizeCode; //size of current variable as Size code
uint8_t Size; //size of current variable in bytes
uint16_t Name; //name of current variable
//ignore call when Page is PAGE_NONE
if (Page == EEPROM_PAGE_NONE) return EEPROM_SUCCESS;
//get page addresses
uint32_t Address = Page + 2;
uint32_t PageEndAddress = Page + FLASH_PAGE_SIZE;
//loop through page starting after page header
while (Address < PageEndAddress)
{
//read potential variable header
VariableHeader = *((__IO uint16_t*) Address);
//if no header written (causes: end of data reached or reset while writing)
if (VariableHeader == 0xFFFF)
{
//loop through next 4 halfword and check if there is anything written
Size = 0;
for (uint8_t i = 2; i <= 8; i += 2)
{
if (Address + i >= PageEndAddress) break;
//while looping count the size of written data (resulting from reset while writing)
if (*((__IO uint16_t*) (Address + i)) != 0xFFFF) Size = i;
}
//if no data found, last variable of page was reached (end loop)
if (Size == 0) break;
}
//else (if header written, proper variable value is following)
else
{
//get size code
SizeCode = (uint8_t)(VariableHeader >> 14);
//check for valid name (VARIABLE_COUNT might have been reduced between builds, but old variables are still in flash)
Name = VariableHeader & 0x3FFF;
if (Name < EEPROM_VARIABLE_COUNT) {
//if everything valid, update the index and the size table
EEPROM_Index[Name] = (uint16_t)(Address + 2 - EEPROM_START_ADDRESS);
EEPROM_SizeTable[Name] = SizeCode;
if (SizeCode == EEPROM_SIZE_DELETED) EEPROM_Index[Name] = 0;
}
//calculate size in bytes from size code
Size = 1 << SizeCode;
if (SizeCode == EEPROM_SIZE_DELETED) Size = 0;
}
//go to next address on page
Address = Address + 2 + Size;
}
//set next free flash address
EEPROM_NextIndex = Address;
if (Address >= PageEndAddress) EEPROM_NextIndex = 0;
//return on loop end
return EEPROM_SUCCESS;
}