/**************************************************************
timing PSRAM datalogger for Teensy 4.1 with PSRAM - NO SD LOGGING - ONLY extRAM_t4.h required
https://forum.pjrc.com/threads/60754-Teensy-3-6-SD-Card-missing-time-in-milliseconds?p=238352&viewfull=1#post238352
Written for Teensy 3.6 by M. Borgerson May 7, 2020
********************************************************************/
elapsedMicros logTime;
//#define USE_logtime
#define LOG_US (1000000 / SAMPLERATE)
#ifdef USE_logtime
#else
IntervalTimer ADCTimer;
#endif
struct datrec {
uint32_t millitime;
uint32_t microtime;
uint32_t DWTCount;
uint32_t byteswritten;
};
uint32_t logIndex = 0;
#define SAMPLERATE 20000
#define USE_PSRAM 1
#ifdef USE_PSRAM
#include <extRAM_t4.h>
extRAM_t4 eRAM;
#define BUFFSIZE 9190*2
//uint8_t config = 2; //0 - init eram only, 1-init flash only, 2-init both
//These have been replaced with defines for:
//INIT_PSRAM_ONLY
//INIT_FLASH_ONLY
//INIT_PSRM_FLASH
uint8_t config = INIT_PSRAM_ONLY;
// FAILS with 0x70050000 at offset 0x4000
struct datrec *dbuff0 = (datrec *)0x70000000;
struct datrec *dbuff1 = (datrec *)0x70090000;
uint32_t psram_base_addr = 0x70000000;
uint32_t sbuff0_addr = 0x70000000-psram_base_addr;
uint32_t sbuff1_addr = 0x70090000-psram_base_addr;
//define a struct joining MYDATA_t to an array of bytes to be stored
uint32_t arraySize = sizeof(datrec);
typedef union MYDATA4RAM_t {
datrec datastruct;
uint8_t Packet[sizeof(datrec)];
};
MYDATA4RAM_t readdata; //data read from memory
MYDATA4RAM_t mydata; //data write to memory
#else // THIS USES RAM1 and RAM2 buffer to show _isr() log works with same size buffers
#define BUFFSIZE 9190*2
struct datrec dbuff1[BUFFSIZE];
struct datrec *dbuff0 = (datrec *)malloc(BUFFSIZE*sizeof(datrec));
#endif
#define DEBUGPRINT true
int logging = 0;
char method = 0;
// these variables are declared volatile because they are changed or used in the
// interrupt handler
volatile uint16_t saveidx = 0;
volatile int16_t writebuffnum = -1;
volatile uint16_t savebuffnum = 0;
volatile uint32_t filestartmilli = 0;
volatile uint32_t byteswritten = 0;
const int ledpin = 13;
#define LEDON digitalWriteFast(ledpin, HIGH);
#define LEDOFF digitalWriteFast(ledpin, LOW);
const char compileTime [] = "Timing Data Logger Compiled on " __DATE__ " " __TIME__;
void setup() {
if ( ARM_DWT_CYCCNT == ARM_DWT_CYCCNT ) { // activate ARM cycle counter
ARM_DEMCR |= ARM_DEMCR_TRCENA; // Assure Cycle Counter active
ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
}
pinMode(ledpin, OUTPUT);
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial && millis() < 4000 );
Serial.println("\n" __FILE__ " " __DATE__ " " __TIME__);
Serial.println(compileTime);
// see if the card is present and can be initialized:
Serial.print("Initializing SD card...");
#ifdef USE_PSRAM
eRAM.begin(config);
Serial.print("Start PSRAM for LOGGING :: ");
#else
Serial.print("LOGGING from RAM :: ");
#endif
Serial.printf("\nTwin buffers of size %u Bytes\n" , sizeof(datrec)*BUFFSIZE );
Serial.println("Enter command selected from ( r, R{eRAM}, q, s, f, E(Erase), d(eRAM dump))");
}
void FastBlink(void) { // blink forever
while (1) {
LEDON
delay(50);
LEDOFF
delay(50);
}
}
static uint32_t elapsedUS = 0;
uint32_t loopCnt = 0, loopSpd;
elapsedMillis elapsedLoop = 0;
void loop() {
// put your main code here, to run repeatedly:
char ch;
uint32_t microselapsed;
elapsedMicros wrtelapsed = 0;
loopCnt++;
if ( elapsedLoop >= 1000 ) {
loopSpd = loopCnt;
loopCnt = 0;
elapsedLoop -= 1000;
}
#ifdef USE_logtime
if ( logging && logTime > LOG_US ) {
logTime -= LOG_US;
if ( logging == 1 )
ADCChore();
esle if ( logging == 2 )
ADCChore_eRAM();
}
#endif
if (Serial.available()) {
ch = Serial.read();
if (ch == 'r') {
method = 'r';
StartLogging(1);
}
if (ch == 'R') {
#ifdef USE_PSRAM
method = 'R';
Serial.println(method);
StartLogging(2);
#endif
}
if (ch == 'E') {
eRAM.eraseDevice();
}
if (ch == 'd') {
method = 'd';
}
if (ch == 's') {
StopLogging();
Serial.printf("SUCCESS:: working address ZERO at 0x70000000\n");
dbuff0 = (datrec *)0x70000000;
dbuff1 = (datrec *)0x70090000;
sbuff0_addr = 0x70000000 - psram_base_addr;
sbuff1_addr = 0x70090000 - psram_base_addr;
Serial.printf("Buf0 Addr = %x, Buf1 Addr = %x\n", sbuff0_addr, sbuff1_addr);
}
if (ch == 'f') { // FAILS with 0x70050000 at offset 0x4000
StopLogging();
Serial.printf("FAIL:: working address ZERO at 0x70050000\n");
dbuff0 = (datrec *)0x70050000;
dbuff1 = (datrec *)0x70090000;
sbuff0_addr = 0x70050000 - psram_base_addr;
sbuff1_addr = 0x70090000 - psram_base_addr;
Serial.printf("Buf0 Addr = %x, Buf1 Addr = %x\n", sbuff0_addr, sbuff1_addr);
}
if (ch == 'q') {
StopLogging();
Serial.println("Enter command selected from ( r, R{eRAM}, q, s, f, E(Erase), d(eRAM dump))");
}
}
// Now check to see if a buffer is ready to be written to SD
// writebuffnum will be set in the interrupt handler
if (writebuffnum == 0) { //is dbuff0 ready?
LEDON
writebuffnum = -1;
if (1) { //if the file is open write dbuff0
wrtelapsed = 0;
WriteBinary(&dbuff0[0], BUFFSIZE);
microselapsed = wrtelapsed;
if (DEBUGPRINT) {
loopCnt = loopCnt / (micros() - elapsedUS);
Serial.printf("0] %6.2f mSec fill [lp#%lu] \t", (float)(micros() - elapsedUS) / 1000.0, loopSpd );
Serial.print("Writing dbuff0 to data file. tmilli = ");
Serial.printf(" %lu took %6.2f mSec\n", dbuff0[0].millitime, (float)microselapsed / 1000.0);
elapsedUS = micros();
}
}
LEDOFF
}
else if (writebuffnum == 1) { // is dbuff1 ready?
LEDON
writebuffnum = -1;
if (1) { //if the file is open write dbuff0
wrtelapsed = 0;
WriteBinary(&dbuff1[0], BUFFSIZE);
microselapsed = wrtelapsed;
if (DEBUGPRINT) {
loopCnt = loopCnt / (micros() - elapsedUS);
Serial.printf("1] %6.2f mSec fill [lp#%lu] \t", (float)(micros() - elapsedUS) / 1000.0, loopSpd );
Serial.print("Writing dbuff1 to data file. tmilli = ");
Serial.printf(" %lu took %6.2f mSec\n", dbuff1[0].millitime, (float)(microselapsed) / 1000.0);
elapsedUS = micros();
}
}
LEDOFF
}
//delay(5);
} // end of the loop() function
// write a buffer to the file as binary record. This takes more time and file space than
// binary format, but can be directly read on PC.
// Note that we have to cast datrec pointer to a uint8_t pointer
// to keep the SD libraryhappy
static uint32_t firstWrap = 999;
static uint32_t overWrap = 999;
void WriteBinary(volatile struct datrec *drp, size_t numstructs) {
// dataFile.write((uint8_t *)drp, numstructs * sizeof(datrec));
Serial.printf(" \t BUFF at %lX\n", drp );
uint32_t lastM = 0;
uint32_t kk, lastB;
uint32_t ii = 0, iiL = 0;
if(method == 'r'){
lastB = drp[0].byteswritten;
for ( kk = 0; kk < numstructs; kk++) {
if ( lastM != drp[kk].millitime || lastB != drp[kk].byteswritten ) {
if ( overWrap == 999 ) {
overWrap = ii - 1;
Serial.printf(" \t HIDE overWrap of %lu <<<<< \n", overWrap );
}
else if ( firstWrap == 999 ) {
firstWrap = ii;
Serial.printf(" \t HIDE firstWrap of %lu <<<<< \n", firstWrap );
}
if ( lastM != drp[kk].millitime )
lastM = drp[kk].millitime;
if ( ii != iiL && ii != firstWrap && ii != overWrap ) {
Serial.printf("skipped %lu\t", ii);
Serial.printf("%lu, %lu, %lX, %lu\t", drp[kk].millitime, drp[kk].microtime, drp[kk].DWTCount, drp[kk].byteswritten);
Serial.printf(" \t kk==%lu ????\n", kk );
}
iiL = ii;
ii = 0;
}
else
ii++;
}
kk = numstructs - 1;
Serial.printf("\tDONE %lu >> %lu, %lu, %lX, %lu\n", kk, drp[kk].millitime, drp[kk].microtime, drp[kk].DWTCount, drp[kk].byteswritten);
//dataFile.write((uint8_t *)drp, numstructs * sizeof(datrec));
} else if(method == 'R') {
uint32_t ramAddr = (uint32_t *)drp;
ramAddr -= psram_base_addr;
uint32_t vmillitime = 0, vmicrotime = 0, vDWTCount = 0, vbyteswritten = 0;
eRAM.readArray(ramAddr, arraySize, readdata.Packet);
lastB = vbyteswritten;
for ( kk = 0; kk < numstructs/sizeof(datrec); kk++) {
eRAM.readArray(ramAddr+arraySize*kk, arraySize, readdata.Packet);
vmillitime = readdata.datastruct.millitime;
vmicrotime = readdata.datastruct.microtime;
vDWTCount = readdata.datastruct.DWTCount;
vbyteswritten = readdata.datastruct.byteswritten;
if ( lastM != vmillitime || lastB != vbyteswritten ) {
if ( overWrap == 999 ) {
overWrap = ii - 1;
Serial.printf(" \t HIDE overWrap of %lu <<<<< \n", overWrap );
}
else if ( firstWrap == 999 ) {
firstWrap = ii;
Serial.printf(" \t HIDE firstWrap of %lu <<<<< \n", firstWrap );
}
if ( lastM != vmillitime )
lastM = vmillitime;
if ( ii != iiL && ii != firstWrap && ii != overWrap ) {
Serial.printf("skipped %lu\t", ii);
Serial.printf("%lu, %lu, %lX, %lu\t", vmillitime, vmicrotime, vDWTCount, vbyteswritten);
Serial.printf(" \t kk==%lu ????\n", kk );
}
iiL = ii;
ii = 0;
}
else
ii++;
}
kk = numstructs/sizeof(datrec) - 1;
Serial.printf("\tDONE %lu >> %lu, %lu, %lX, %lu\n", kk, vmillitime, vmicrotime, vDWTCount, vbyteswritten);
//dataFile.write((uint8_t *)drp, numstructs * sizeof(datrec));
}
}
void ADCChore_eRAM(void) { // eRAM function access
uint32_t tmilli;
tmilli = millis() - filestartmilli;
byteswritten += sizeof(datrec); // update global bytes written count
// save in the proper buffer--defined by savebuffnum
if (savebuffnum == 0) { // put data in dbuff0
mydata.datastruct.millitime = tmilli;
mydata.datastruct.microtime = logIndex++;
mydata.datastruct.DWTCount = (uint32_t)sbuff0_addr+psram_base_addr + (sizeof(datrec) * saveidx);
mydata.datastruct.byteswritten = 0;
eRAM.writeArray(sbuff0_addr + saveidx*sizeof(datrec), arraySize, mydata.Packet);
//Serial.printf("%x\n", sbuff0_addr + saveidx*sizeof(datrec)+psram_base_addr);
saveidx ++;
if (saveidx >= BUFFSIZE) { // mark buffer for write to SD
Serial.printf("\t\t %d FULL e @ %lu sbuff>%lX \n", savebuffnum, saveidx, sbuff0_addr+ saveidx);
writebuffnum = 0;
savebuffnum = 1; // start saving in other buffer on next interrupt
saveidx = 0; // start at beginning of next buffer
}
} else { // must be saving to dbuff1
mydata.datastruct.millitime = tmilli;
mydata.datastruct.microtime = logIndex++;
mydata.datastruct.DWTCount = (uint32_t)sbuff1_addr+psram_base_addr + (sizeof(datrec) * saveidx);
mydata.datastruct.byteswritten = 1;
eRAM.writeArray(sbuff1_addr + saveidx*sizeof(datrec), arraySize, mydata.Packet);
saveidx ++;
if (saveidx >= BUFFSIZE) { // mark buffer for write to SD
Serial.printf("\t\t %d FULL e @ %lu sbuff>%lX \n", savebuffnum, saveidx, sbuff1_addr+ saveidx);
writebuffnum = 1;
savebuffnum = 0; // start saving in other buffer on next interrupt
saveidx = 0; // start at beginning of next buffer
if(method == 'd'){
StopLogging();
dump();
}
}
}
}
void ADCChore(void) { // DIRECT ACCESS
uint32_t tmilli;
tmilli = millis() - filestartmilli;
byteswritten += sizeof(datrec); // update global bytes written count
// save in the proper buffer--defined by savebuffnum
if (savebuffnum == 0) { // put data in dbuff0
dbuff0[saveidx].millitime = tmilli;
dbuff0[saveidx].microtime = logIndex++;
dbuff0[saveidx].DWTCount = (uint32_t)dbuff0 + (sizeof(datrec) * saveidx);;
dbuff0[saveidx].byteswritten = 0;
saveidx++;
if (saveidx >= BUFFSIZE) { // mark buffer for write to SD
Serial.printf("\t\t %d FULL D @ %lu &dbuff>%lX\n", savebuffnum, saveidx, (uint32_t)&dbuff0[saveidx]);
writebuffnum = 0;
savebuffnum = 1; // start saving in other buffer on next interrupt
saveidx = 0; // start at beginning of next buffer
}
} else { // must be saving to dbuff1
dbuff1[saveidx].millitime = tmilli;
dbuff1[saveidx].microtime = logIndex++;
dbuff1[saveidx].DWTCount = (uint32_t)dbuff1 + (sizeof(datrec) * saveidx);;
//dbuff1[saveidx].byteswritten = byteswritten;
dbuff1[saveidx].byteswritten = 1;
saveidx++;
if (saveidx >= BUFFSIZE) { // mark buffer for write to SD
Serial.printf("\t\t %d FULL D @ %lu &dbuff>%lX\n", savebuffnum, saveidx, (uint32_t)&dbuff1[saveidx]);
writebuffnum = 1;
savebuffnum = 0; // start saving in other buffer on next interrupt
saveidx = 0; // start at beginning of next buffer
}
}
}
void StartLogging(int logType) {
// we open in a mode that creates a new file each time
// instead of appending as in the Arduino example
if ( logging ) return;
logging = logType;
elapsedUS = micros();
loopCnt = 0;
firstWrap = 999;
overWrap = 999;
logIndex = 0;
Serial.println("Starting logging");
// initialize some variables for the buffers
saveidx = 0; // start saving at beginning of buffer
savebuffnum = 0; // start saving in dbuff0
writebuffnum = -1; // indicates no buffer ready yet
// start the interval timer to begin logging
filestartmilli = millis();
byteswritten = 0;
#ifdef USE_logtime
logTime = 0;
#else
if ( logging == 1 ) {
Serial.println("PSRAM DIRECT _isr() logging");
ADCTimer.begin(ADCChore, 1000000 / SAMPLERATE); //begin() expects timer period in microseconds
}
else if ( logging == 2 ) {
Serial.println("PSRAM eRAM _isr() logging");
ADCTimer.begin(ADCChore_eRAM, 1000000 / SAMPLERATE); //begin() expects timer period in microseconds
}
#endif
}
void StopLogging(void) {
if ( 0 == logging ) return;
Serial.println("Stopping logging");
logging = 0;
#ifdef USE_logtime
logTime = 0;
if (DEBUGPRINT) Serial.println("eMicros Log halted");
#else
ADCTimer.end();
if (DEBUGPRINT) Serial.println("ADCTimer halted");
#endif
delay(10);
writebuffnum = -1;
Serial.printf("%lu KBytes written to file.\n", byteswritten / 1024);
// in the interest of simplicity, we ignore any partial buffer at the end
}
void dump(){
uint32_t kk;
uint32_t vmillitime = 0, vmicrotime = 0, vDWTCount = 0, vbyteswritten = 0;
uint32_t numstructs = BUFFSIZE;
Serial.printf("DUMP OF BUFFER 0 at %x offset\n", sbuff0_addr);
for ( kk = 0; kk < numstructs/sizeof(datrec); kk++) {
eRAM.readArray(sbuff0_addr+arraySize*kk, arraySize, readdata.Packet);
vmillitime = readdata.datastruct.millitime;
vmicrotime = readdata.datastruct.microtime;
vDWTCount = readdata.datastruct.DWTCount;
vbyteswritten = readdata.datastruct.byteswritten;
Serial.printf("\tbuff0 %lu >> %lu, %lu, %lX, %lu\n", kk, vmillitime, vmicrotime, vDWTCount, vbyteswritten);
}
Serial.printf("DUMP OF BUFFER 1 at %x offset\n", sbuff1_addr);
for ( kk = 0; kk < numstructs/sizeof(datrec); kk++) {
eRAM.readArray(sbuff1_addr+arraySize*kk, arraySize, readdata.Packet);
vmillitime = readdata.datastruct.millitime;
vmicrotime = readdata.datastruct.microtime;
vDWTCount = readdata.datastruct.DWTCount;
vbyteswritten = readdata.datastruct.byteswritten;
Serial.printf("\tbuff1 %lu >> %lu, %lu, %lX, %lu\n", kk, vmillitime, vmicrotime, vDWTCount, vbyteswritten);
}
}