310 lines
8.1 KiB
C++
310 lines
8.1 KiB
C++
/**********************************************************************
|
|
Copyright (C) 2018 MisfitTech LLC, All rights reserved.
|
|
|
|
MisfitTech uses a dual license model that allows the software to be used under
|
|
a standard GPL open source license, or a commercial license. The standard GPL
|
|
license requires that all software statically linked with MisfitTec Code is
|
|
also distributed under the same GPL V2 license terms. Details of both license
|
|
options follow:
|
|
|
|
- Open source licensing -
|
|
MisfitTech is a free download and may be used, modified, evaluated and
|
|
distributed without charge provided the user adheres to version two of the GNU
|
|
General Public License (GPL) and does not remove the copyright notice or this
|
|
text. The GPL V2 text is available on the gnu.org web site
|
|
|
|
- Commercial licensing -
|
|
Businesses and individuals that for commercial or other reasons cannot comply
|
|
with the terms of the GPL V2 license must obtain a low cost commercial license
|
|
before incorporating MisfitTech code into proprietary software for distribution in
|
|
any form. Commercial licenses can be purchased from www.misfittech.net
|
|
and do not require any source files to be changed.
|
|
|
|
|
|
This code is distributed in the hope that it will be useful. You cannot
|
|
use MisfitTech's code unless you agree that you use the software 'as is'.
|
|
MisfitTech's code is provided WITHOUT ANY WARRANTY; without even the implied
|
|
warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
PURPOSE. MisfitTech LLC disclaims all conditions and terms, be they
|
|
implied, expressed, or statutory.
|
|
|
|
|
|
Written by Trampas Stern for MisfitTech.
|
|
|
|
Misfit Tech invests time and resources providing this open source code,
|
|
please support MisfitTech and open-source hardware by purchasing
|
|
products from MisfitTech, www.misifittech.net!
|
|
*********************************************************************/
|
|
#include "eeprom.h"
|
|
#include "calibration.h"
|
|
#include "Flash.h"
|
|
#include "board.h" //for divide with rounding macro
|
|
#include <Arduino.h>
|
|
#include "syslog.h"
|
|
|
|
//since we will write the following structure into each page, we need to find our latest page
|
|
// to do this we will use the header to contain a checksum and write counter.
|
|
#define EEPROM_SIZE (FLASH_ROW_SIZE*2)
|
|
|
|
typedef struct {
|
|
uint16_t checksum;
|
|
uint16_t count;
|
|
}eepromHeader_t;
|
|
|
|
#define EEPROM_DATA_SIZE (FLASH_PAGE_SIZE_NZS-sizeof(eepromHeader_t))
|
|
typedef struct {
|
|
eepromHeader_t header;
|
|
uint8_t data[EEPROM_DATA_SIZE];
|
|
} eepromData_t;
|
|
|
|
|
|
|
|
static eepromData_t EEPROMCache;
|
|
|
|
static int32_t NextPageWrite=-1;
|
|
|
|
//we need to reserve two pages for EEPROM
|
|
__attribute__((__aligned__(FLASH_ROW_SIZE))) const uint8_t NVM_eeprom[EEPROM_SIZE]={0xFF};
|
|
|
|
|
|
static uint16_t checksum(uint8_t *ptrData, uint32_t nBytes)
|
|
{
|
|
uint16_t sum=0;
|
|
uint32_t i;
|
|
i=0;
|
|
//LOG("running checksum %d",nBytes);
|
|
while(i<nBytes)
|
|
{
|
|
sum += ptrData[i];
|
|
i++;
|
|
}
|
|
|
|
return sum;
|
|
}
|
|
|
|
static bool isPageGood(uint32_t page)
|
|
{
|
|
eepromData_t *ptrData;
|
|
uint16_t cs;
|
|
ptrData=(eepromData_t *)&NVM_eeprom[page];
|
|
|
|
cs=checksum(ptrData->data, EEPROM_DATA_SIZE);
|
|
//LOG("checksum is %d %d",cs,ptrData->header.checksum);
|
|
|
|
if (cs==ptrData->header.checksum)
|
|
{
|
|
//LOG("Page good %d",page);
|
|
return true;
|
|
}
|
|
//LOG("page bad %d",page);
|
|
return false;
|
|
}
|
|
|
|
static void printEEPROM(uint32_t page)
|
|
{
|
|
eepromData_t *ptrData;
|
|
int i;
|
|
ptrData=(eepromData_t *)&NVM_eeprom[page];
|
|
LOG("count %d", ptrData->header.count);
|
|
LOG("checksum %d", ptrData->header.checksum);
|
|
for (i=0; i<10; i++)
|
|
{
|
|
LOG("Data[%d]=%02X",i,ptrData->data[i]);
|
|
}
|
|
}
|
|
|
|
static uint32_t findLastGoodPage(void)
|
|
{
|
|
uint32_t lastGoodPage=0;
|
|
uint32_t page;
|
|
uint16_t lastCnt=0;
|
|
eepromData_t *ptrData;
|
|
|
|
page=0;
|
|
while(page < (EEPROM_SIZE))
|
|
{
|
|
//LOG("checking page %d",page);
|
|
if (isPageGood(page))
|
|
{
|
|
ptrData=(eepromData_t *)&NVM_eeprom[page];
|
|
|
|
//check for roll over which is OK
|
|
if (lastCnt==16534 && ptrData->header.count==1)
|
|
{
|
|
lastCnt=ptrData->header.count;
|
|
lastGoodPage=page;
|
|
}
|
|
if (ptrData->header.count>lastCnt)
|
|
{
|
|
//make sure we have not rolled over.
|
|
if ((ptrData->header.count-lastCnt)<(16534/2))
|
|
{
|
|
lastCnt=ptrData->header.count;
|
|
lastGoodPage=page;
|
|
}
|
|
}
|
|
}
|
|
page=page + FLASH_PAGE_SIZE_NZS;
|
|
}
|
|
//LOG("last good page %d",lastGoodPage);
|
|
return lastGoodPage;
|
|
}
|
|
|
|
//find the next page to write
|
|
static uint32_t eepromGetNextWritPage(void)
|
|
{
|
|
eepromHeader_t *ptrHeader;
|
|
uint32_t page;
|
|
uint32_t row;
|
|
int blockCount;
|
|
int done=0;
|
|
|
|
//start at first address:
|
|
page=0;
|
|
|
|
while(page < (EEPROM_SIZE))
|
|
{
|
|
//LOG("checking page %d",page);
|
|
ptrHeader=(eepromHeader_t *) &NVM_eeprom[page];
|
|
if (ptrHeader->count == 0xFFFF)
|
|
{
|
|
uint32_t i;
|
|
uint8_t *ptrData;
|
|
//uint8_t erasedByte=(uint8_t)ptrHeader->count;
|
|
bool erased=true;
|
|
|
|
//verify page is erased
|
|
ptrData= (uint8_t *)&NVM_eeprom[page];
|
|
|
|
for (i=0; i<FLASH_PAGE_SIZE_NZS; i++)
|
|
{
|
|
if (ptrData[i] != FLASH_ERASE_VALUE)
|
|
{
|
|
erased=false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (erased)
|
|
{
|
|
//LOG("Found Page %d erased",page);
|
|
return page;
|
|
}
|
|
}
|
|
page=page+FLASH_PAGE_SIZE_NZS;
|
|
}
|
|
//if we get get here all the pages are full...
|
|
// we need to find the page with last good data.
|
|
page=findLastGoodPage();
|
|
|
|
//find which row the page is in
|
|
row=page/FLASH_ROW_SIZE;
|
|
|
|
//increment to next row for erase
|
|
row++;
|
|
if ((row*FLASH_ROW_SIZE)>=EEPROM_SIZE)
|
|
{
|
|
row=0;
|
|
//TODO we should make sure this not where good data is
|
|
// however if it is what should we do?
|
|
}
|
|
|
|
//now we need to erase that row
|
|
//WARNING("Erasing page %d",row*FLASH_ROW_SIZE);
|
|
flashErase(&NVM_eeprom[row*FLASH_ROW_SIZE],FLASH_ROW_SIZE);
|
|
page=row*FLASH_ROW_SIZE;
|
|
//LOG("Next free page is %d",page);
|
|
return page;
|
|
}
|
|
|
|
|
|
eepromError_t eepromInit(void)
|
|
{
|
|
uint32_t page;
|
|
|
|
|
|
//find the last good page offset in flash
|
|
page=findLastGoodPage();
|
|
LOG("EEPROM Init found page %d",page);
|
|
if (isPageGood(page))
|
|
{
|
|
LOG("EEPROM page good %d",page);
|
|
memcpy(&EEPROMCache, &NVM_eeprom[page], sizeof(EEPROMCache));
|
|
|
|
NextPageWrite=eepromGetNextWritPage();
|
|
return EEPROM_OK;
|
|
}
|
|
//ERROR("page is bad");
|
|
memset(&EEPROMCache, 0, sizeof(EEPROMCache));
|
|
NextPageWrite=eepromGetNextWritPage();
|
|
return EEPROM_CORRUPT;
|
|
}
|
|
|
|
|
|
int eepromWriteCache(uint8_t *ptrData, uint32_t size)
|
|
{
|
|
//LOG("Cache write %d",size);
|
|
if (NextPageWrite==-1) //some one did not init the module
|
|
{
|
|
//lets handle gracefully and do it ourselves
|
|
eepromInit();
|
|
}
|
|
if (size>EEPROM_DATA_SIZE)
|
|
{
|
|
size =EEPROM_DATA_SIZE;
|
|
}
|
|
memcpy(EEPROMCache.data, ptrData, size);
|
|
EEPROMCache.header.checksum=checksum(EEPROMCache.data,EEPROM_DATA_SIZE);
|
|
|
|
|
|
return size;
|
|
}
|
|
|
|
int eepromRead(uint8_t *ptrData, uint32_t size) //returns number of bytes actually read, whcih could be less than size requested
|
|
{
|
|
if (NextPageWrite==-1) //some one did not init the module
|
|
{
|
|
//lets handle gracefully and do it ourselves
|
|
eepromInit();
|
|
}
|
|
if (size>EEPROM_DATA_SIZE)
|
|
{
|
|
size =EEPROM_DATA_SIZE;
|
|
}
|
|
if (EEPROMCache.header.count == 0)
|
|
{
|
|
return 0; //cache is new/corrupt
|
|
}
|
|
memcpy(ptrData, EEPROMCache.data, size);
|
|
return size;
|
|
}
|
|
|
|
eepromError_t eepromFlush(void) //flush the cache to flash memory
|
|
{
|
|
if (NextPageWrite==-1)
|
|
{
|
|
ERROR("EEPROM WRITE FAILED");
|
|
return EEPROM_FAILED; //most likely no one has written to cache
|
|
}
|
|
EEPROMCache.header.count++;
|
|
if (EEPROMCache.header.count>=16535)
|
|
{
|
|
EEPROMCache.header.count=1;
|
|
}
|
|
//WARNING("Writting to Page %d",NextPageWrite);
|
|
flashWrite(&NVM_eeprom[NextPageWrite], &EEPROMCache, sizeof(EEPROMCache));
|
|
|
|
// printEEPROM(NextPageWrite);
|
|
|
|
if (!SYSCTRL->PCLKSR.bit.BOD33DET) //if not in brown out condition find next write location
|
|
{
|
|
//LOG("getting next page to write");
|
|
NextPageWrite=eepromGetNextWritPage(); //find next write location and erase if needed
|
|
} else
|
|
{
|
|
//LOG("BOD active");
|
|
NextPageWrite=-1; //else we will just clear NextPageWrite location just in case we recover from brown out
|
|
}
|
|
return EEPROM_OK;
|
|
}
|