/*******************************************************
One MegaSample T4.0 ADC 12-bit
MJB 4/2/20
updated 8/9/2020
NOTE: This is a proof-of-concept program written for
a novice programmer. In the interest of simplicity
it uses global variables and simple functions without
parameters.
The sampling runs fast enough at 600MHz that ther is
time for the native SDIO interface to write the data
to an sd card.
The program uses the ADC library from Pedvide, which
is one of the libraries installed by Teensyduino.
If you plan to do high-speed ADC acquisition, learning
how this library works should be high on your TO-DO
list.
***************************************************************/
#include "SdFat.h"
#include "sdios.h"
#include <TimeLib.h>
#include <ADC.h>
/*******************************************************/
// when USEMTP is defined, you can upload file with MTP,
// but you have to have the MTP library and modified USB files
#define USEMTP
#ifdef USEMTP
#include "MTP.h"
#include <Storage.h>
#include <usb1_mtp.h>
MTPStorage_SD storage;
MTPD mtpd(&storage);
#endif
/*******************************************************/
IntervalTimer ADCTimer;
// instantiate a new ADC object
ADC *adc = new ADC(); // adc object;
// use an elapsedmillis object to control collection time
elapsedMillis collectmillis;
// This version uses SdFs, which can be either FAT32 or EXFat
SdFs sdf;
SdioCard sdc;
FsFile logFile;
#define SD_CONFIG SdioConfig(FIFO_SDIO)
const char compileTime [] = "T4.1 MegaSample 12-bit logger Compiled on " __DATE__ " " __TIME__;
const int admarkpin = 32; // Changed to end pins on T4.1
const int wrmarkpin = 33;
const int ledpin = 13;
// ADMARKHI and ADMARKLO are used to observe ADC timing on oscilloscope
#define ADMARKHI digitalWriteFast(admarkpin, HIGH);
#define ADMARKLO digitalWriteFast(admarkpin, LOW);
// 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);
#define LEDOFF digitalWriteFast(ledpin, LOW);
#define ADBLOCKSIZE (1024 * 100) // 2 x 200KBytes to leave some room for other users of DMAMEM
uint16_t adcbuff0[ADBLOCKSIZE];
uint16_t adcbuff1[ADBLOCKSIZE];
volatile uint16_t inbuffnum = 0;
volatile bool logging = false;
uint16_t *inbuffptr, *sdbuffptr;
volatile uint32_t adcidx;
uint32_t totalbytes;
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
delay(500); // wait for Serial to open
Serial.println(compileTime);
pinMode(admarkpin, OUTPUT);
pinMode(wrmarkpin, OUTPUT);
pinMode(ledpin, OUTPUT);
pinMode(A0, INPUT_DISABLE); // disable digital keeper resistors
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::HIGH_SPEED); // change the sampling speed
adc->adc1->setAveraging(1 ); // set number of averages
adc->adc1->setResolution(12); // set bits of resolution
adc->adc1->setConversionSpeed(ADC_CONVERSION_SPEED::VERY_HIGH_SPEED); // change the conversion speed
adc->adc1->setSamplingSpeed(ADC_SAMPLING_SPEED::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
#ifdef USEMTP
StartMTP();
ARM_DEMCR |= ARM_DEMCR_TRCENA;
ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
#endif
}
void loop() {
// put your main code here, to run repeatedly:
char ch;
if (Serial.available()) {
ch = Serial.read();
if (ch == 'l') LogADC();
if (ch == 's') ShowADC();
if (ch == 'd') sdf.ls(LS_SIZE | LS_DATE | LS_R);
}
#ifdef USEMTP
mtpd.loop();
#endif
}
/*****************************************************
This is the intervaltimer interrupt handler
8/9/2020 Simplified to have main thread control
logging.
Changed per jonr to alternate ADCs
This code takes about 220nS when logging
******************************************************/
volatile uint32_t dwtlast;
volatile uint32_t maxinterval;
volatile uint16_t overflows;
void ADCChore(void) {
uint16_t value;
uint32_t dwt;
dwt = ARM_DWT_CYCCNT;
ADMARKHI
if (adcidx & 0x01) { //Read ADC0 and restart it
value = adc->adc0->readSingle();
adc->adc0->startSingleRead(A0);
} else { //Read ADC1 and restart it
value = adc->adc1->readSingle();
adc->adc1->startSingleRead(A0);
}
if (logging) { // Save result to buffer
inbuffptr[adcidx] = value;
adcidx++;
if (adcidx >= ADBLOCKSIZE) { // switch buffers at end
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(logging)
ADMARKLO
// ADMARHI to ADMARKLO takes about 180nS at 600MHz
// So some oversampling may be possible
}
//keep track of max clock cycles between interrupts
if(dwt-dwtlast > maxinterval) maxinterval = (dwt-dwtlast);
dwtlast = dwt;
}
/******************************************************
Read 7 seconds of data from ADCs at 1 microsecond intervals
Store the results in adcbuffer;
note that MTP loop is not called during logging
*****************************************************/
void LogADC(void) {
uint32_t totalbytes = 0;
Serial.println("Reading ADC Samples");
inbuffnum = 0;
inbuffptr = &adcbuff0[0];
sdbuffptr = NULL;
adcidx = 0;
if (!OpenLogFile()) {
Serial.print("Could not open log file.");
return;
}
ADCTimer.priority(50); // medium-high priority
ADCTimer.begin(ADCChore, 1.0); // start timer at 1 microsecond intervals
delay(1); // wait 1msec before starting logging
dwtlast = ARM_DWT_CYCCNT;
maxinterval = 0;
logging = true;
collectmillis = 0; // reset the elapsedmillis timer
do {
if (sdbuffptr != NULL) { // when data in buffer, write to SD card
WRMARKHI
// logFile.write(sdbuffptr, ADBLOCKSIZE*2);// save block of 16-bit words
sdbuffptr = NULL;
totalbytes += ADBLOCKSIZE;
// Serial.print("."); // mark each 128KB block written
WRMARKLO
}
} while(collectmillis < 7200); // A bit of extra time to make sure last buffer is written
logging = false;
ADCTimer.end(); // stop the timer
logFile.truncate(); //truncate to amount actually written
logFile.close();
Serial.printf("\nADC Read %lu samples\n", totalbytes);
Serial.printf("Maximum Sampling interval was %6.2f microseconds\n", (float)maxinterval/600.0);
}
bool OpenLogFile(void) {
uint64_t alloclength;
if (!logFile.open("Log1MS12B.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;
}
/******************************************************
Display data from adcbuffer0 in lines of 20 values
Only the first NUMTOSHOW values are displayed
*****************************************************/
#define NUMTOSHOW 1000 // change to alter numbers output
void ShowADC(void) {
uint32_t sendidx;
Serial.println("ADC Data");
// Sending the full 128K samples would a long time!
for (sendidx = 0; sendidx < NUMTOSHOW; sendidx++) {
Serial.printf("% 5u", adcbuff0[sendidx]);
if ((sendidx % 20) == 19) Serial.println();
}
}
bool StartSDCard() {
if (!sdf.cardBegin(SD_CONFIG)) {
Serial.println("cardBegin failed");
}
if (!sdf.volumeBegin()) {
Serial.println("volumeBegin failed");
}
if (!sdf.begin(SdioConfig(FIFO_SDIO))) {
Serial.println("\nSD File initialization failed.\n");
return false;
} else Serial.println("initialization done.");
if (sdf.fatType() == FAT_TYPE_EXFAT) {
Serial.println("Type is exFAT");
} else {
Serial.printf("Type is FAT%d\n", int16_t(sdf.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());
}
#ifdef USEMTP
void StartMTP(void){
Serial.println("Starting MTP Responder");
usb_mtp_configure();
if(!Storage_init(&sdf)) {
Serial.println("Could not initialize MTP Storage!");
}
}
#endif