I’m using a core32 to log data from environmental sensors.
I’m concerned that writing 100bytes to a file every few seconds would result in the modification to the SD card’s file allocation table (FAT) that over time may cause it to fail.
As a result I’ve made my logging code so that it caches the log records and writes them in one pass 10kbytes at a time.
I have the core hooked up with an analog input monitoring a signal derived from the 12V pre-regulated supply. If the power fails (or is switched off) the falling voltage is detected and the cache is written to disk followed by a call to FILE_WriteClose() .
My assumption is that the FAT is only modified when the FILE_WriteClose() is called.
I havn’t looked at the filesystem code underlying the midibox FILE module, so my questions are:
Does the SD card have inbuilt wear leveling?
Does my strategy of write caching help the situation?
Assuming I’m writing lots of data over time what is the best strategy for getting maximum life fro m the SD Card?
Using such a 2 GB card, one would have to write 10 TB of data, before it becomes unusable. So I would buy an “industrial” one and not worry too much about optimizing/caching on the software side.
Industrial strength sounds like a good idea in any case!
I suppose if FILE_WriteClose() results in a change to the FAT table then the caching which I’ve written and somewhat tested would result in a x100 reduction in “wear” in the scenario I described.
So whilst the application can afford 10k of RAM I guess I’ll keep it in.
Here’s the code:
#define db MIOS32_MIDI_SendDebugMessage
/*
* DataLog.c
*
* Created on: 21/06/2012
* Author: Dug
*/
#include <mios32.h>
#include <FreeRTOS.h>
#include <task.h>
#include <semphr.h>
#include "file.h"
#include <string.h>
#include "debug.h"
#define CACHE_SIZE 10000
u32 DataLog_Index;
u8 DataLog_Cache[CACHE_SIZE];
xSemaphoreHandle DataLog_Mutex;
u8 DataLog_Held;
void CacheCommit();
void DataLog_Init(){
if(DataLog_Mutex==NULL)
DataLog_Mutex=xSemaphoreCreateMutex();
xSemaphoreTake(DataLog_Mutex,portMAX_DELAY);
DataLog_Index=0; //start of cache
xSemaphoreGive(DataLog_Mutex);
}
void DataLog_AddLine(char* s){ //cache null terminated string
char eol[3]="\r\n"; //Windows compatable EOL
xSemaphoreTake(DataLog_Mutex,portMAX_DELAY);
db("AddLine:%s",s);
if ((strlen(s)+3+DataLog_Index)>CACHE_SIZE){
CacheCommit(); //cache now empty
db("cache comitted.");
}
strcpy((char*)&DataLog_Cache[DataLog_Index],s);
strcat((char*)&DataLog_Cache[DataLog_Index],eol);
db("line cached:%s",&DataLog_Cache[DataLog_Index]);
DataLog_Index+=strlen(s)+2; //include eol but not null
xSemaphoreGive(DataLog_Mutex);
}
void CacheCommit(){ //only called from these functions
s32 r=FILE_FileExists("DataLog.txt");
switch(r){
case 0: FILE_WriteOpen("DataLog.txt",1); //create new
db("DataLog Create");
break;//create
case 1: FILE_WriteOpen("DataLog.txt",0); //re-open existing
db("DataLog Append");
break;
default:db("Datalog.txt problem");return;
}
FILE_WriteSeek(FILE_WriteGetCurrentSize());
FILE_WriteBuffer(DataLog_Cache,DataLog_Index);
DataLog_Index=0;
FILE_WriteClose();
}
void DataLog_Commit(){ //write cache physical sd card
xSemaphoreTake(DataLog_Mutex,portMAX_DELAY);
CacheCommit();
xSemaphoreGive(DataLog_Mutex);
}
void DataLog_HoldSafe(){ //commit to disk but don't release mutex (we've lost power)
if (DataLog_Mutex&&(DataLog_Held==0)){ //only if DataLog has been initializied
xSemaphoreTake(DataLog_Mutex,portMAX_DELAY); //take but don't release
DataLog_Held=1;
CacheCommit();
}
}
void DataLog_UnHold(){ //power back up so release logging for use by tasks
if (DataLog_Held){
xSemaphoreGive(DataLog_Mutex); //release mutex now
DataLog_Held=0;
}
}
void DataLog_PrintFile(){
file_t F;
char s[120]="";
u8 b[2]={0,0};
xSemaphoreTake(DataLog_Mutex,portMAX_DELAY);
if (FILE_ReadOpen(&F,"DataLog.txt")<0){
db("DataLog_Printfile problem");
xSemaphoreGive(DataLog_Mutex);
return;
}
while(FILE_ReadByte(&b[0])>=0){ //read until EOF
if(b[0]=='\r')continue;
if(b[0]=='\n'){
b[0]=0;
strcat(s,(char*)b); //null terminate
db("->%s<-",(char*)s); //print line
strcpy(s,""); //reset
}else
strcat(s,(char*)b); //add char
}
FILE_ReadClose(&F);
xSemaphoreGive(DataLog_Mutex);
}