I have designed a system that is essentially a data logger. It is a custom board based on the T4.1 chipset, but I have verified the problem with a standard T4.1.
When I go to start logging, I create the file and pre-allocate the desired file length in order to get the fastest write speed. The SD Card is also fully formatted before each logging session. So if do the allocate, stop, pull the card, and read the file, it is all 0's, as expected. However, If I start logging, and lose power during data collection, the file is also all 0's. I could see losing some portion of the data, but not all of it. It can occur after gigabytes have been written. The file length shows as the allocated amount. I don't 'flush' until right before 'close' (the flush is probably redundant there). Does this mean a flush (or close) has to happen to have anything stored? I mean, the data has to be stored, but perhaps the file system hides that?
Of course, I should guard against this, but sometimes stuff happens, and I'd hate to see the data lost.
Here is a really pared down version of the code:
Pins 0 and 1 must be connected when run on a T4.1. On my board the data clock is generated elsewhere.
When I go to start logging, I create the file and pre-allocate the desired file length in order to get the fastest write speed. The SD Card is also fully formatted before each logging session. So if do the allocate, stop, pull the card, and read the file, it is all 0's, as expected. However, If I start logging, and lose power during data collection, the file is also all 0's. I could see losing some portion of the data, but not all of it. It can occur after gigabytes have been written. The file length shows as the allocated amount. I don't 'flush' until right before 'close' (the flush is probably redundant there). Does this mean a flush (or close) has to happen to have anything stored? I mean, the data has to be stored, but perhaps the file system hides that?
Of course, I should guard against this, but sometimes stuff happens, and I'd hate to see the data lost.
Here is a really pared down version of the code:
Pins 0 and 1 must be connected when run on a T4.1. On my board the data clock is generated elsewhere.
Code:
#define T4CLOCK
#define USBCOMMS
#define DATA_FILE_NAME "test.bin"
const uint32_t FILE_SIZE_MB = 256;
econst size_t WR_SIZE = 512;
const size_t WR_WORDS = WR_SIZE / 4;
uint64_t FILE_SIZE = (uint64_t)1048576UL * (uint64_t)FILE_SIZE_MB;
uint32_t blocksWanted = (uint32_t)(FILE_SIZE / WR_SIZE);
#define PWMRES 4 // PWM resolution 8 bits = 256 steps
#define PWMSTEPS 16 // to match PWMRES: there are 256 steps
const int amp = PWMSTEPS / 2 - 1;
Stream* serialCmd;
const int CLKpin = 0;
const int DCLKpin = 1;
void ClkInterrupt()
{
portStatusReg = mask; // we worked around the Teensyduino handler, so we need to reset the status flag ourself
*inPtr = IMXRT_GPIO9_DIRECT; // inCnt; // maxBuffUsage; //pos++; //outCnt; //
if (++inPtr >= inEndPtr) inPtr = inBufPtr;
inCnt++; // count up words of data
asm volatile("dsb"); // avoid double calls due to possible bus sync issues
}
// =====================================
// ====== S E T U P ====
// =====================================
void setup()
{
Serial6.begin(19200);
Serial.begin(19200); // open USB serial, need it for real time mode
#ifdef DUALCONTROL
while (!Serial) ;
#endif
pinMode(DCLKpin, INPUT);
#ifdef USBCOMMS
serialCmd = &Serial;
#else
serialCmd = &Serial6;
#endif
int sdTry;
for (sdTry = 0; sdTry < 4; sdTry++)
if (SD.begin(BUILTIN_SDCARD)) break;
if (sdTry == 4)
{
SD.sdfs.end();
serialCmd->println("!!! ===== SD FAIL ==== !!!"); //sd.initErrorHalt(&Serial6);
}
}
loop()
{
takeData();
while(1);
}
//==================================
// === T A K E D A T A ====
// ===============================
void takeData(void)
{
digitalWriteFast(13, LOW); // LED off
if (!file.open(DATA_FILE_NAME, O_RDWR | O_CREAT | O_TRUNC)) serialCmd->println("open failed");
if (!file.preAllocate(FILE_SIZE))
{
serialCmd->println("preAllocate failed");
return;
}
uint32_t bytes_out;
blocksWritten = 0; // reset block count
inCnt = 0;
maxBuffUsage = 0;
outCnt = 0;
inPtr = inBufPtr;
outPtr = buf;
#ifdef T4CLOCK
analogWriteRes(PWMRES); // write PWM resolution
analogWriteFrequency(CLKpin, 4500000); //42500000); //500000); //500000);
analogWrite(CLKpin, amp);
#endif
serialCmd->println("running");
// start up data read interrupt after everything is ready to go
attachInterrupt(DCLKpin, nullptr, RISING); //CHANGE); // let Teensyduino do the setup work
attachInterruptVector(IRQ_GPIO6789, ClkInterrupt); // override Teensyduino handler and invoke the callback directly
NVIC_ENABLE_IRQ(IRQ_GPIO6789);
NVIC_SET_PRIORITY(IRQ_GPIO6789, 0); // 0 is highest priority, might be good to reduce a bit
#ifdef T4CLOCK
analogWriteRes(PWMRES); // write PWM resolution
analogWriteFrequency(CLKpin, 4500000); //42500000); //500000); //500000);
analogWrite(CLKpin, amp);
#endif
// ===== Here is where it all happens ========
while (blocksWritten < blocksWanted)
{
while (inCnt < WR_WORDS); // wait til we have 512 bytes or more
digitalWriteFast(13, HIGH);
noInterrupts();
inCnt -= WR_WORDS;
if (inCnt > maxBuffUsage) maxBuffUsage = inCnt;
interrupts();
outCnt++;
if ((bytes_out = file.write(outPtr, WR_SIZE)) != WR_SIZE) //BUF_SIZE) != BUF_SIZE) {
serialCmd->print('#'); // error("write failed");
outPtr += WR_SIZE;
if (outPtr == bufEndPtr) outPtr = buf;
blocksWritten++;
}
#ifdef T4CLOCK
analogWrite(0, 0); // turn off freq gen
#endif
file.flush();
file.close();
}