#include <ADC.h> //library for easier ADC implementation
#include <DMAChannel.h> //library for easier DMA implementation
#include <array> //use C++ array structs
#include <SdFat.h> // work with SD card
#include <IntervalTimer.h> // Library to create intervals
#define BUF_DIM 32768/2 //size of buffer that holds data from ADC
/*----------------------------------------------------------------*/
const uint32_t pdbfreq = 10000; // sampling speed [Hz] max ~300MHz - actually the trigger frequency of the programmable delay block
uint32_t duration = 60; // duration of each measure-cycle [s]
String Name = "Log"; // file name prefix
unsigned long debug_start;
/*----------------------------------------------------------------*/
/*DECLARATIONS adc and pins---------------------------------------*/
ADC *adc = new ADC(); // used to declare the adc->### object
IntervalTimer timer0, timer1; // timers
/*PINS------------------------------------------------------------*/
const uint8_t adc_pin0_1 = A9; // A2 is connected to ADC0
const uint8_t adc_pin0_2 = A8;
const uint8_t adc_pin1 = A22; // A22 is connected to ADC1
/*----------------------------------------------------------------*/
/*Interval Timer------------------------------------------------------------*/
const int period0_1 = 120; // us
const int period0_2 = 120; // us
const int readPeriod = 100000; // us
elapsedMillis timed_read_elapsed;
bool startTimerValue0;
bool startTimerValue1;
/*----------------------------------------------------------------*/
/*Buffer declarations----------------------------------------------*/
std::array<volatile uint16_t, (uint32_t)(BUF_DIM * 2)> buffer __attribute__ ((aligned (BUF_DIM))); // size of buffer is limited due to the Teensy's program memory
std::array<volatile uint16_t, (uint32_t)(BUF_DIM * 2)> buffer1 __attribute__ ((aligned (BUF_DIM)));
uint32_t FILE_SIZE = 0; //initial variables for filesize and pointer to...
uint32_t last = 0;
uint32_t last1 = 0;
uint32_t bytes = 0;
float preceil = 0;
float scale = 0;
/*----------------------------------------------------------------*/
/*DECLARATIONS dma and tcd----------------------------------------*/
DMAChannel dma; // used to declare the dma.### object for the first channel
DMAChannel dma1; // used to declare the dma.### object for the second channel
typeof(*dma.TCD) tcd_mem[4] __attribute__ ((aligned (32))) ; // alignment of four 32-byte blocks; needed for four different TCDs
typeof(*dma1.TCD) tcd1_mem[4] __attribute__ ((aligned (32))) ;
/*----------------------------------------------------------------*/
/*DECLARATIONS microSD card files---------------------------------*/
SdFatSdioEX sd; // used to declare the sd.### object (Sdfat); do not use SdFatSdioEX sd
uint16_t fileNr = 0; // after a given duration a new file is created; fileNr is an index used for the filename
uint16_t fileNr1 = 0;
File file; // file object for logging data
File file1; // file object for logging data
/*----------------------------------------------------------------*/
// function creates new files for data logging
/*----------------------------------------------------------------*/
void filestuff() {
fileNr++;
String filename = Name + "_dma0_" + fileNr + ".bin";
char fname[30];
filename.toCharArray(fname, 30);
Serial.print("filename: ");
Serial.println(filename);
if (!file.open(fname, O_RDWR | O_CREAT)) {
sd.errorHalt("open dma0 file failed");
}
}
void filestuff1() {
fileNr1++;
String filename1 = Name + "_dma1_" + fileNr1 + ".bin";
char fname1[30];
filename1.toCharArray(fname1, 30);
Serial.print("filename: ");
Serial.println(filename1);
if (!file1.open(fname1, O_RDWR | O_CREAT)) {
sd.errorHalt("open dma1 file failed");
}
}
void setup()
{
/*Serial monitor--------------------------------------------------*/
debug_start = millis();
Serial.begin(115200);
while (!Serial && ((millis() - debug_start) <= 5000));
Serial.println("Begin Setup\n");
/*----------------------------------------------------------------*/
/*FileSetup-------------------------------------------------------*/
/*
String filename = Name + "_dma0_" + fileNr + ".bin"; // create filenames
char fname[30];
filename.toCharArray(fname, 30);
String filename1 = Name + "_dma1_" + fileNr + ".bin";
char fname1[30];
filename1.toCharArray(fname1, 30);
Serial.println(filename);
Serial.println(filename1);
if (! sd.begin()) { // start sdio interface to SD card
sd.initErrorHalt("SdFatSdio begin() failed");
} sd.chvol();
if (!file.open(fname, O_RDWR | O_CREAT)) { // create SD card files
sd.errorHalt("open dma0 failed");
}
if (!file1.open(fname1, O_RDWR | O_CREAT)) {
sd.errorHalt("open dma1 failed");
}
delay(100);
/*----------------------------------------------------------------*/
/*DurationSetup---------------------------------------------------*/
/*
bytes = ((duration*1000000)/(1000000/pdbfreq))* 2;
preceil = bytes/BUF_DIM;
scale = ceil(preceil); // round up preceil value
FILE_SIZE = (scale+2) * BUF_DIM; // after writing FILE_SIZE uint16_t values to a file a new file is created
/*----------------------------------------------------------------*/
/*Mode Setup------------------------------------------------------*/
pinMode(13, OUTPUT); // built-in LED is at PIN 13 in Teensy 3.5
pinMode(adc_pin0_1, INPUT); // configure as analog input pins
pinMode(adc_pin0_2, INPUT);
pinMode(adc_pin1, INPUT);
/*----------------------------------------------------------------*/
/*ADC Setup-------------------------------------------------------*/
//adc->startSingleRead(adc_pin0_1, ADC_0); // start ADC conversion
adc->startSingleRead(adc_pin1, ADC_1);
adc->adc0->setAveraging ( 1 ); // ADC configuration
adc->adc0->setResolution ( 12 );
adc->adc0->setConversionSpeed ( ADC_CONVERSION_SPEED::HIGH_SPEED);
adc->adc0->setSamplingSpeed ( ADC_SAMPLING_SPEED::HIGH_SPEED );
adc->adc0->setReference (ADC_REFERENCE::REF_3V3);
adc->adc1->setAveraging ( 1 ); // ADC configuration
adc->adc1->setResolution ( 12 );
adc->adc1->setConversionSpeed ( ADC_CONVERSION_SPEED::HIGH_SPEED);
adc->adc1->setSamplingSpeed ( ADC_SAMPLING_SPEED::HIGH_SPEED );
adc->adc1->setReference (ADC_REFERENCE::REF_3V3);
/*----------------------------------------------------------------*/
/* DMA ----------------------------------------------------------*/
dma.source ( ADC0_RA); // source is the ADC result register
dma.transferSize ( 2); // set 2, one uint16_t value are two bytes
dma.triggerAtHardwareEvent (DMAMUX_SOURCE_ADC0); // DMAMUX alignes source to DMA channel
dma1.source ( ADC1_RA);
dma1.transferSize ( 2);
dma1.triggerAtHardwareEvent (DMAMUX_SOURCE_ADC1);
/*----------------------------------------------------------------*/
/*TCD-------------------------------------------------------------*/
// configure TCD for first dma
dma.TCD->CITER = BUF_DIM/2;
dma.TCD->BITER = BUF_DIM/2;
dma.TCD->DOFF = 2; // set 2, one uint16_t value are two bytes
dma.TCD->CSR = 0x10;
dma.TCD->DADDR = (volatile void*) &buffer [ 0 * 512] ;
dma.TCD->DLASTSGA = ( int32_t ) &tcd_mem[ 0] ; // points to a 32-byte block that is loaded into the TCD memory of the DMA after major loop completion
memcpy ( &tcd_mem[0], dma.TCD , 32 ) ; // 32-byte block is transferred to &tcd_mem[0]
/*
dma.TCD->DADDR = (volatile void*) &buffer [16 * 512] ;
dma.TCD->DLASTSGA = ( int32_t ) &tcd_mem[ 2] ;
memcpy ( &tcd_mem[1], dma.TCD , 32 ) ;
dma.TCD->DADDR = (volatile void*) &buffer [32 * 512] ;
dma.TCD->DLASTSGA = ( int32_t ) &tcd_mem[ 3] ;
memcpy ( &tcd_mem[2], dma.TCD , 32 ) ;
dma.TCD->DADDR = (volatile void*) &buffer [48 * 512] ;
dma.TCD->DLASTSGA = ( int32_t ) &tcd_mem[ 0] ;
memcpy ( &tcd_mem[3], dma.TCD , 32 ) ;
memcpy ( dma.TCD , &tcd_mem[0], 32 ) ; // 16-byte block that is transferred into the TCD memory of the DMA
*/
// equal configuration for TCD of second dma1
dma1.TCD->CITER = 16 * 512;
dma1.TCD->BITER = 16 * 512;
dma1.TCD->DOFF = 2;
dma1.TCD->CSR = 0x10;
dma1.TCD->DADDR = (volatile void*) &buffer1 [ 0 * 512] ;
dma1.TCD->DLASTSGA = ( int32_t ) &tcd1_mem[ 1] ;
memcpy ( &tcd1_mem[0], dma1.TCD , 32 ) ;
dma1.TCD->DADDR = (volatile void*) &buffer1 [16 * 512] ;
dma1.TCD->DLASTSGA = ( int32_t ) &tcd1_mem[ 2] ;
memcpy ( &tcd1_mem[1], dma1.TCD , 32 ) ;
dma1.TCD->DADDR = (volatile void*) &buffer1 [32 * 512] ;
dma1.TCD->DLASTSGA = ( int32_t ) &tcd1_mem[ 3] ;
memcpy ( &tcd1_mem[2], dma1.TCD , 32 ) ;
dma1.TCD->DADDR = (volatile void*) &buffer1 [48 * 512] ;
dma1.TCD->DLASTSGA = ( int32_t ) &tcd1_mem[ 0] ;
memcpy ( &tcd1_mem[3], dma1.TCD , 32 ) ;
memcpy ( dma1.TCD , &tcd1_mem[0], 32 ) ;
/*----------------------------------------------------------------*/
/*Start DMA and ADC-----------------------------------------------*/
dma.enable(); // enable DMA
dma1.enable();
adc->adc0->enableDMA(); // connect DMA and ADC
adc->adc1->enableDMA();
adc->adc0->stopPDB(); // start PDB conversion trigger
adc->adc0->startPDB(pdbfreq);
adc->adc1->stopPDB();
adc->adc1->startPDB(pdbfreq);
NVIC_DISABLE_IRQ(IRQ_PDB); // we don't want or need the PDB interrupt
//adc->adc0->printError(); // print ADC configuration errors
//adc->adc1->printError();
/*----------------------------------------------------------------*/
/*Timer Setup-------------------------------------------------------*/
startTimerValue0 = timer0.begin(timer0_callback, period0_1);
delayMicroseconds(25);
startTimerValue1 = timer1.begin(timer1_callback, period0_2);
adc->adc0->enableInterrupts(adc0_isr);
Serial.println("Timers started");
/*----------------------------------------------------------------*/
/*Debug-----------------------------------------------------------*/
Serial.println(BUF_DIM);
Serial.println(FILE_SIZE);
Serial.print("bytes: ");
Serial.println(bytes);
Serial.println((uint32_t)&buffer[ 0], HEX); // debug: print memory location of buffer
Serial.println((uint32_t)&buffer[ 16 * 512], HEX);
Serial.println((uint32_t)&buffer[ 32 * 512], HEX);
Serial.println((uint32_t)&buffer[ 48 * 512], HEX);
Serial.println((uint32_t)&buffer1[ 0], HEX);
Serial.println((uint32_t)&buffer1[ 16 * 512], HEX);
Serial.println((uint32_t)&buffer1[ 32 * 512], HEX);
Serial.println((uint32_t)&buffer1[ 48 * 512], HEX);
Serial.println("----------------------------------");
/*----------------------------------------------------------------*/
/*Signal end of Setup method--------------------------------------*/
for (int i = 0; i < 5; i++){ // visual feedback, blink 5 times if the setup was completed
digitalWrite(13, HIGH);
delay(300);
digitalWrite(13, LOW);
delay(300);
}
}
void loop() {
if(startTimerValue0==false) {
Serial.println("Timer0 setup failed");
}
if(startTimerValue1==false) {
Serial.println("Timer1 setup failed");
}
Serial.println(buffer[0]);
delay(100);
/*
while ( ((64*1024-1) & ( (int)dma.TCD->DADDR - last )) > BUF_DIM ){
if (BUF_DIM != (uint32_t)file.write( (char*)&buffer[((last/2)&(32*1024-1))], BUF_DIM) ){
sd.errorHalt("write dma0 failed");
}
last += BUF_DIM ;
if (BUF_DIM != (uint32_t)file1.write( (char*)&buffer1[((last1/2)&(32*1024-1))], BUF_DIM) ){
sd.errorHalt("write dma1 failed");
}
last1 += BUF_DIM ;
}
/*----------------------------------------------------------------*/
/*
if ( last >= FILE_SIZE ) { // check if end of file is reached
file.close();
last = 0; // reset last
filestuff(); // create new files for data logging
}
if ( last1 >= FILE_SIZE ) { // check if end of file is reached
file1.close();
last1 = 0; // reset last
filestuff1(); // create new files for data logging
}
/*----------------------------------------------------------------*/
}
void timer0_callback(void) {
adc->startSingleRead(adc_pin0_1, ADC_0);
}
void timer1_callback(void) {
adc->startSingleRead(adc_pin0_2, ADC_0);
}
void adc0_isr() {
adc->readSingle(); // clears interrupt
// restore ADC config if it was in use before being interrupted by the analog timer
if (adc->adc0->adcWasInUse) {
// restore ADC config, and restart conversion
adc->adc0->loadConfig(&adc->adc0->adc_config);
// avoid a conversion started by this isr to repeat itself
adc->adc0->adcWasInUse = false;
}
}