/*******************************************************
1.0 MegaSample T4.1 ADC using ADC timer to trigger the
ADC collection.
This version saves histogram data for timing interval
and ADC values
MJB 0/19/20
Modified to log 12-bit samples and save as uint16_t
***************************************************************/
#include "SdFat.h"
#include <TimeLib.h>
#include <ADC.h>
// instantiate a new ADC object
ADC *adc = new ADC(); // adc object;
// SdFS file system accepts both FAT and ExFAT cards
SdFs sd;
FsFile logFile;
#define SD_CONFIG SdioConfig(FIFO_SDIO)
const char compileTime [] = "MegaSample logger Compiled on " __DATE__ " " __TIME__;
//const int admarkpin = 1;
const int wrmarkpin = 0;
const int ledpin = 13;
// WRMARKHI and WRMARKLO are used to observe SDC Write timing on oscilloscope
#define WRMARKHI digitalWriteFast(wrmarkpin, HIGH);
#define WRMARKLO digitalWriteFast(wrmarkpin, LOW);
#define LEDON digitalWriteFast(ledpin, HIGH); // Also marks IRQ handler timing
#define LEDOFF digitalWriteFast(ledpin, LOW);
// buffers for histogram data
#define TMHISTOMAX 1000 // max interval 10000 cycles at 600MHz = 6 microseconds
#define ADCMAX 4096 // for 12-bit samples
uint32_t tm_histobuffer[TMHISTOMAX]; // for timing intervals up to 40.96mSec
uint32_t adc_histobuffer[ADCMAX];
// Saving 12-bit data as uint16_t
#define ADBLOCKSIZE 65536
#define SAMPRATE 1000000
// if you need larger buffers for slower SD cards, you can increase these
// sizes, however, you may need to put one of them into DMAMEM
uint16_t adcbuff0[ADBLOCKSIZE];// 64K * 2 bytes = 128K Buffers
uint16_t adcbuff1[ADBLOCKSIZE];
bool verboseflag = false; // true to show some output during sampling
bool writeflag = true; // true to write, false for no writes to SDC
volatile uint16_t inbuffnum = 0;
volatile uint32_t totalsamples = 0;
uint16_t *inbuffptr, *sdbuffptr;
volatile uint32_t adcidx, totalbsamples;
const uint adcpin = A9; // my 2.5V precision reference
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
delay(500);
Serial.println(compileTime);
// activate ARM cycle counter
ARM_DEMCR |= ARM_DEMCR_TRCENA; // Assure Cycle Counter active
ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
pinMode(wrmarkpin, OUTPUT);
pinMode(ledpin, OUTPUT);
pinMode(adcpin, INPUT_DISABLE);
adc->adc0->setAveraging(1 ); // set number of averages
adc->adc0->setResolution(12); // set bits of resolution
adc->adc0->setConversionSpeed(ADC_CONVERSION_SPEED::VERY_HIGH_SPEED); // change the conversion speed
adc->adc0->setSamplingSpeed(ADC_SAMPLING_SPEED::VERY_HIGH_SPEED); // change the sampling speed
if (!StartSDCard()) {
// do fast blink forever
do { // hang with blinking LED
LEDON
delay(100);
LEDOFF
delay(100);
} while (1);
}// end of if (!StartSDCard())
setSyncProvider(getTeensy3Time); // helps put time into file directory data
}
void loop() {
// put your main code here, to run repeatedly:
char ch;
if (Serial.available()) {
ch = Serial.read();
if (ch == 'l') LogADC();
if (ch == 'a') ShowADCHisto();
if (ch == 't') ShowTmHisto();
if (ch == 'v') verboseflag = !verboseflag;
if (ch == 'w') writeflag = !writeflag;
if (ch == 'd') sd.ls(LS_SIZE | LS_DATE | LS_R);
}
}
void ShowSetup(void){
Serial.printf("Collection rate is %lu samples/second.\n",SAMPRATE);
Serial.print("Serial output during collection is ");
if(verboseflag) Serial.println("ON"); else Serial.println("OFF");
Serial.print("SDC Writes are ");
if(writeflag) Serial.println("ON"); else Serial.println("OFF");
}
// define the number of samples to collect as 10 M Samples
#define MAXSAMPLES 10*1024l*1024l
/*****************************************************
This is the ADC timer interrupt handler
******************************************************/
volatile uint32_t lastcycles;
volatile uint16_t overflows;
// This ISR runs in about 120nSec on T4.1 at 600MHz.
// It buffers the data, but SDC writes are user-selected
// timing and adc histogram values are saved in RAM
void adc0_isr() {
uint32_t tmdiff, thiscycles;
uint16_t adc_val;
LEDON;
if (totalsamples < MAXSAMPLES) { // sample until enough collected
thiscycles = ARM_DWT_CYCCNT;
tmdiff = thiscycles - lastcycles;
lastcycles = thiscycles;
totalsamples++;
// Collect ADC value and update the ADC Histogram data
adc_val = adc->adc0->readSingle();
// Save ADC data in buffer
inbuffptr[adcidx] = adc_val; // save the data
adcidx++;
if (adcidx >= ADBLOCKSIZE) { // switch buffers at end
// if main loop hasn't finished with last SD buffer
// we have a potential overflow
if(sdbuffptr != NULL) overflows++;
sdbuffptr = inbuffptr; // set up block for output
if (inbuffnum == 0) { // swap input to other buffer
inbuffptr = &adcbuff1[0];
inbuffnum = 1;
} else {
inbuffptr = &adcbuff0[0];
inbuffnum = 0;
}
adcidx = 0;
}// end of if (adcidx < ADMAX)
// make sure we don't write outside histogram buffers
if (adc_val >= ADCMAX) adc_val = ADCMAX;
if (tmdiff >= TMHISTOMAX) tmdiff = TMHISTOMAX - 1;
// Skip the first two samples, as they often have
// weird timing values. Update histogram data
if (totalsamples > 2) {
tm_histobuffer[tmdiff]++;
adc_histobuffer[adc_val]++;
}
#if defined(__IMXRT1062__) // Teensy 4.0
asm("DSB");
#endif
} // end of if(totalsamples < maxsamples
LEDOFF;
}
/******************************************************
Read MAXSAMPLES from ADC at 1 microsecond intervals
Store the results in adcbuffer;
*****************************************************/
void LogADC(void) {
uint16_t lcount;
Serial.println("Reading ADC Samples");
totalsamples = 0;
inbuffnum = 0;
inbuffptr = &adcbuff0[0];
sdbuffptr = NULL;
overflows = 0;
adcidx = 0;
memset(adc_histobuffer, 0, sizeof(adc_histobuffer)); // clear adc histogram counts
memset(tm_histobuffer, 0, sizeof(tm_histobuffer)); // clear timing histogram counts
ShowSetup();
if (!OpenLogFile()) {
Serial.print("Did not open log file.");
}
adc->adc0->stopTimer();
adc->adc0->startSingleRead(adcpin); // call this to setup everything before the Timer starts, differential is also possible
delay(1);
adc->adc0->readSingle();
// now start the ADC collection timer
adc->adc0->startTimer(SAMPRATE); //frequency in Hz
lastcycles = ARM_DWT_CYCCNT;
adc->adc0->enableInterrupts(adc0_isr);
lcount = 0;
do {
if (sdbuffptr != NULL) { // when data in buffer, write to SD card
WRMARKHI
if (logFile) logFile.write(sdbuffptr, ADBLOCKSIZE * sizeof(uint16_t)); //sample is now 2 bytes
sdbuffptr = NULL; // indicates that we are finished writing
//totalsamples += ADBLOCKSIZE; // incremented in ISR
if(verboseflag)Serial.print("."); // mark each 128KB block written
if (lcount++ > 19) {
if(verboseflag)Serial.println();
lcount = 0;
}
WRMARKLO
}
} while (totalsamples < MAXSAMPLES);
adc->adc0->stopTimer();
if (logFile) {
logFile.truncate(); //truncate to amount actually written
logFile.close();
}
Serial.printf("\nADC Read %lu samples with %u overflows\n",totalsamples,overflows);
}
bool OpenLogFile(void) {
uint64_t alloclength;
if(!writeflag) return false; // don't open file if not writing
if (!logFile.open("Log1MS.dat", O_RDWR | O_CREAT | O_TRUNC)) {
return false;
}
alloclength = (uint64_t)200 * (uint64_t)(1024L * 1024l); //200MB
if (!logFile.preAllocate(alloclength)) {
Serial.println("Pre-Allocation failed.");
return false;
} else {
Serial.println("Pre-Allocation succeeded.");
}
return true;
}
void ShowTmHisto(void) {
uint32_t i;
Serial.println("Timing Histogram Data in ARM Cycle Counts");
for (i = 0; i < TMHISTOMAX; i++) {
if (tm_histobuffer[i] > 0) {
Serial.printf("%5lu %5lu\n", i, tm_histobuffer[i]);
}
}
Serial.println();
}
void ShowADCHisto(void) {
uint32_t i;
Serial.println("ADC Histogram Data in Counts");
for (i = 0; i < ADCMAX; i++) {
if (adc_histobuffer[i] > 0) {
Serial.printf("%5lu %5lu\n", i, adc_histobuffer[i]);
}
}
Serial.println();
}
bool StartSDCard() {
if (!sd.cardBegin(SD_CONFIG)) {
Serial.println("cardBegin failed");
}
if (!sd.volumeBegin()) {
Serial.println("volumeBegin failed");
}
if (!sd.begin(SdioConfig(FIFO_SDIO))) {
Serial.println("\nSD File initialization failed.\n");
return false;
} else Serial.println("initialization done.");
if (sd.fatType() == FAT_TYPE_EXFAT) {
Serial.println("Type is exFAT");
} else {
Serial.printf("Type is FAT%d\n", int16_t(sd.fatType()));
}
// set date time callback function
SdFile::dateTimeCallback(dateTime);
return true;
}
/*****************************************************************************
Read the Teensy RTC and return a time_t (Unix Seconds) value
******************************************************************************/
time_t getTeensy3Time() {
return Teensy3Clock.get();
}
//------------------------------------------------------------------------------
/*
User provided date time callback function.
See SdFile::dateTimeCallback() for usage.
*/
void dateTime(uint16_t* date, uint16_t* time) {
// use the year(), month() day() etc. functions from timelib
// return date using FAT_DATE macro to format fields
*date = FAT_DATE(year(), month(), day());
// return time using FAT_TIME macro to format fields
*time = FAT_TIME(hour(), minute(), second());
}