/**************************************************************
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 *)0x70050000;
struct datrec *dbuff1 = (datrec *)0x70090000;
uint32_t sbuff0_addr = 0x70050000;
uint32_t sbuff1_addr = 0x70090000;
//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
#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 )");
}
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 = ch;
StartLogging(1);
}
if (ch == 'R') {
#ifdef USE_PSRAM
method = ch;
StartLogging(2);
#endif
}
if (ch == 's') {
StopLogging();
Serial.print("SUCCESS:: working address ZERO at 0x70000000");
dbuff0 = (datrec *)0x70000000;
dbuff1 = (datrec *)0x70090000;
sbuff0_addr = 0x70000000;
sbuff1_addr = 0x70090000;
}
if (ch == 'f') { // FAILS with 0x70050000 at offset 0x4000
StopLogging();
Serial.print("FAIL:: working address ZERO at 0x70050000");
dbuff0 = (datrec *)0x70050000;
dbuff1 = (datrec *)0x70090000;
sbuff0_addr = 0x70050000;
sbuff1_addr = 0x70090000;
}
if (ch == 'q') StopLogging();
}
// 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 = 5;
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 {
uint32_t ramAddr = (uint32_t *)drp;
uint32_t dataArray[4];
//lastB = drp[0].byteswritten;
for ( kk = 0; kk < numstructs; kk++) {
[COLOR="#FF0000"] readArray(ramAddr+arraySize*kk, arraySize, readdata.Packet);[/COLOR]
drp[kk].millitime = readdata.datastruct.millitime;
drp[kk].microtime = readdata.datastruct.microtime;
drp[kk].DWTCount = readdata.datastruct.DWTCount;
drp[kk].byteswritten = readdata.datastruct.byteswritten;
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));
}
}
void ADCChore_eRAM(void) { // eRAM function access
#ifdef USE_PSRAM // avoid eRAM non-def error on RAM only test
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
writeLong(sbuff0_addr + saveidx , tmilli);
writeLong(sbuff0_addr + saveidx + 4, logIndex++);
writeLong(sbuff0_addr + saveidx + 8, (uint32_t)sbuff0_addr + (sizeof(datrec) * saveidx));
writeLong(sbuff0_addr + saveidx + 12, 0);
saveidx += sizeof(datrec);
if (saveidx >= BUFFSIZE) { // mark buffer for write to SD
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
writeLong(sbuff1_addr + saveidx , tmilli);
writeLong(sbuff1_addr + saveidx + 4, logIndex++);
writeLong(sbuff1_addr + saveidx + 8, (uint32_t)sbuff1_addr + (sizeof(datrec) * saveidx));
writeLong(sbuff1_addr + saveidx + 12, 1);
saveidx += sizeof(datrec);
if (saveidx >= BUFFSIZE) { // mark buffer for write to SD
writebuffnum = 1;
savebuffnum = 0; // start saving in other buffer on next interrupt
saveidx = 0; // start at beginning of next buffer
}
}
#endif
}
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
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
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 readArray (uint32_t ramAddr, uint32_t length, uint8_t *data)
{
uint32_t ii;
uint8_t *ptrERAM = (uint8_t *)(ramAddr);
//uint32_t timenow = micros();
for ( ii = 0; ii < length; ii++ ) {
data[ii] = ptrERAM[ii];
}
//Serial.printf("Read (us): %d\n", micros()-timenow);
//Serial.printf("read @%06X: ", ramAddr);
//for (uint32_t i=0; i < length; i++) Serial.printf(" %02X", *(((uint8_t *)data) + i));
//Serial.printf("\n");
//Serial.printf("rd @%08X: ", (uint32_t)ptrERAM);
//for (uint32_t i=0; i < length; i++) Serial.printf(" %02X", ptrERAM[i]);
//Serial.printf("\n");
}
void writeArray (uint32_t ramAddr, uint32_t items, uint8_t values[])
{
uint32_t ii;
uint8_t *ptrERAM = (uint8_t *)(ramAddr);
//Serial.printf("write @%06X:\n", ramAddr);
//for (uint32_t i=0; i < items; i++) Serial.printf(" %02X", *(((uint8_t *)values) + i));
//Serial.printf("\n");
//uint32_t timenow = micros();
for ( ii = 0; ii < items; ii++ ) {
ptrERAM[ii] = values[ii];
}
//Serial.printf("Write (us): %d\n", micros()-timenow);
}
void writeLong(uint32_t ramAddr, uint32_t value)
{
uint8_t *buffer = reinterpret_cast<uint8_t *>(&value);
writeArray(ramAddr, 4, buffer);
}
void readLong(uint32_t ramAddr, uint32_t *value)
{
uint8_t buffer[4];
readArray(ramAddr, 4, buffer);
*value = *reinterpret_cast<uint32_t *>(buffer);
}