Teensy 4.1, Windows 11
I am writing 8 floats to the SD card every 10us using IntervalTimer & a ring buffer (full listing below) for 12 seconds.
Everything appears to working fine except after the 12s is up, I close the files & put the system into an endless MTP.loop() I cant see the generated files in the Win 11 explorer. The Teensy shows but not the file structure.
What am I missing here?
EDIT: Additional info, I can see the files if I take the SD card out of the Teensy & put it into an SD card reader.
I am writing 8 floats to the SD card every 10us using IntervalTimer & a ring buffer (full listing below) for 12 seconds.
Everything appears to working fine except after the 12s is up, I close the files & put the system into an endless MTP.loop() I cant see the generated files in the Win 11 explorer. The Teensy shows but not the file structure.
What am I missing here?
EDIT: Additional info, I can see the files if I take the SD card out of the Teensy & put it into an SD card reader.
C++:
#include <Arduino.h>
#include <SD.h>
#include <MTP_Teensy.h>
#include "SdFat.h"
#include "RingBuf.h"
#define SD_CONFIG SdioConfig(FIFO_SDIO) // Force native 4-bit SDIO mode
// Allocate a 128 KB Ring Buffer in DTCM
// 128 KB can cache about 4,000 samples (~40ms of backup buffer if the SD stalls)
const size_t RING_BUF_SIZE = 128 * 1024;
// Pre-allocate a 500 MB file block on the card to avoid allocation delays
const uint64_t FILE_SIZE = 500ULL * 1024ULL * 1024ULL;
SdFs sd; // SD card instance
FsFile file; // File instance for ring buffer to write to on the SD card
RingBuf<FsFile, RING_BUF_SIZE> rb; // Ring buffer
IntervalTimer samplingTimer; // Timer IRQ instance
// Run time counter
uint32_t start_time;
// 8 floats = 32 bytes per sample packet
struct DataPacket
{
float values[8];
};
// Volatile tracking variables for the ISR
volatile uint32_t totalSamplesLogged = 0;
volatile bool bufferOverflowed = false;
// Dummy data generator
void readSensors(DataPacket *packet)
{
for (int i = 0; i < 8; i++)
{
packet->values[i] = 1.234f * (i + 1);
}
}
// 10 Microsecond Interrupt Service Routine (ISR)
void samplingISR()
{
DataPacket packet;
readSensors(&packet);
// Check if ring buffer has space for 32 bytes
if (rb.bytesFree() >= sizeof(packet))
{
rb.write((const uint8_t*)&packet, sizeof(packet));
totalSamplesLogged++;
}
else
{
bufferOverflowed = true; // Memory full! SD card couldn't keep up
}
}
void setup()
{
Serial.begin(9600);
while (!Serial && millis() < 4000);
MTP.begin();
Serial.print("Initializing SD... ");
if (!sd.begin(SD_CONFIG))
{
sd.initErrorHalt(&Serial);
}
else
{
MTP.addFilesystem(SD, "SD Card");
}
// Add backup file of previous run
if (SD.exists("fastlog.bin"))
{
if (SD.exists("fastlog_old.bin"))
{
SD.remove("fastlog_old.bin");
}
SD.rename("fastlog.bin", "fastlog_old.bin");
}
if (!file.open("fastlog.bin", O_RDWR | O_CREAT | O_TRUNC))
{
Serial.println("Failed to open file.");
return;
}
else
{
Serial.println("File opened.");
}
// Pre-allocate contiguous sectors on the physical flash memory
Serial.print("Pre-allocating space... ");
if (!file.preAllocate(FILE_SIZE))
{
Serial.println("Allocation failed!");
return;
}
rb.begin(&file);
Serial.println("\nSystem ready. Logging starting.");
// Start the hardware timer to fire exactly every 10 microseconds (100 kHz)
samplingTimer.begin(samplingISR, 10);
start_time = millis();
}
void loop()
{
// Check for disaster metrics
if (bufferOverflowed)
{
samplingTimer.end(); // Emergency stop
file.close();
Serial.println("\n[CRITICAL ERROR]: Buffer Overflow! SD card write stalled too long.");
while (1)
{
MTP.loop();
}
}
// Maximum sector block size optimized for the Teensy 4.1 controller
size_t n = rb.bytesUsed();
// Only execute a write sector command if there's data AND the SD card is idle
if (n >= 128 && !file.isBusy())
{
// Write full chunks of 128-byte sectors at a time
rb.writeOut(128);
}
// Print statistical metrics safely outside the fast stream
static uint32_t lastPrint = 0;
if (millis() - lastPrint > 2000)
{
lastPrint = millis();
Serial.printf("Logged total: %lu samples | Buffer free: %d bytes\n", totalSamplesLogged, rb.bytesFree());
}
// End condition, close SD file & eneter MTP loop
if (millis() - start_time > 12000)
{
file.close();
Serial.println("\nFinished sampling");
while(1)
{
MTP.loop();
}
}
}
Last edited: