Last active
June 3, 2016 17:00
-
-
Save pingud98/5b4ca5cdbd5c3609ea99e59ce3decf9d to your computer and use it in GitHub Desktop.
This script combines the timer triggering functionality with the free running ADC in two channel mode, reading in to a buffer using DMA. The net result is that we can sample events up to the design spec of 5Hz. The only limit on performance is how long it takes to get the data out. The sample rate on the ADC is approx 800ksps, though over 2 chan…
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*notes to Team Cosmic Pi | |
I just got this working in Cosimo's lab using a TTi TG5011 LXI function generator and a Tektronix TDS2024 scope. | |
It took a lot of fine tuning to get the right buffer read out, so that the rising edge can be consistently seen in the samples. | |
The buffer index (i.e. how many buffers back in time you need to go) will definitely change based on the rest of the code, but the principle is here | |
Don't forget that we're interleaving channels 1 and 2, therefore the nth sample is channel 1 and the n+1th sample is channel 2 etc. and if we're reading out 50, it's actually 25 from channel A etc. | |
Also note that the ADC seems to top out (4096) around 2.3V, I was expecting 3.3V, so maybe there's another setting to tweak here. | |
If you are testing this code independently here are your parameters for connection: | |
Set up a SAW wave, with a vertical rising edge, then monotonically decreasing | |
Amplitude (max) 2.3V, never above 3.3V or you'll cook the DUE | |
Frequency - Tested up to 10Hz, our specification max is 5 Hz. | |
Pinouts: | |
Saw wave goes in to D2 (trigger), A6 and A7 (ADC channels) | |
Unexplained - when I hook up one of the ADC inputs to 0V it crashes. I think it might be due to an offset somewhere in the signal generator/earthing mess/laptop USB grounding... | |
*/ | |
volatile int bufn,obufn,pbufn,qbufn; //obuf, pbuf and qbuf are the previously filled buffers (i.e. going backwards in time) | |
uint16_t buf[8][256]; // 8 buffers of 256 readings | |
bool bufferdump=false; //flag for event, when the buffer should be dumped | |
void ADC_Handler(){ // move DMA pointers to next buffer | |
int f=ADC->ADC_ISR; | |
if (f&(1<<27)){ | |
//memcpy(lastbuf, buf[bufn], 256); | |
bufn=(bufn+1)&7; | |
ADC->ADC_RNPR=(uint32_t)buf[bufn]; | |
ADC->ADC_RNCR=256; | |
} | |
} | |
void setup(){ | |
Serial.begin(115200); //serial output at high speed, tested up to 10Hz with this configuration (for a minute or two) | |
//while(!Serial); | |
pmc_enable_periph_clk(ID_ADC); | |
adc_init(ADC, SystemCoreClock, ADC_FREQ_MAX, ADC_STARTUP_FAST); | |
REG_ADC_MR = 0x10380080; // Free run as fast as you can - ADC settings as current core Cosmic Pi code | |
REG_ADC_CHER = 3; // Channels 0 and 1 | |
NVIC_EnableIRQ(ADC_IRQn); | |
ADC->ADC_IDR=~(1<<27); | |
ADC->ADC_IER=1<<27; | |
ADC->ADC_RPR=(uint32_t)buf[0]; // DMA buffer | |
ADC->ADC_RCR=256; | |
ADC->ADC_RNPR=(uint32_t)buf[1]; // next DMA buffer | |
ADC->ADC_RNCR=256; | |
bufn=obufn=1; | |
ADC->ADC_PTCR=1; | |
ADC->ADC_CR=2; | |
TimersStart(); | |
} | |
//Routine copied out of Cosmic Pi core code, only using one interrupt on Pin 2 for this test. | |
void TimersStart() { | |
uint32_t config = 0; | |
// Set up the power management controller for TC0 and TC2 | |
pmc_set_writeprotect(false); // Enable write access to power management chip | |
pmc_enable_periph_clk(ID_TC0); // Turn on power for timer block 0 channel 0 | |
pmc_enable_periph_clk(ID_TC6); // Turn on power for timer block 2 channel 0 | |
// Timer block zero channel zero is connected only to the PPS | |
// We set it up to load regester RA on each PPS and reset | |
// So RA will contain the number of clock ticks between two PPS, this | |
// value should be very stable +/- one tick | |
config = TC_CMR_TCCLKS_TIMER_CLOCK1 | // Select fast clock MCK/2 = 42 MHz | |
TC_CMR_ETRGEDG_RISING | // External trigger rising edge on TIOA0 | |
TC_CMR_ABETRG | // Use the TIOA external input line | |
TC_CMR_LDRA_RISING; // Latch counter value into RA | |
TC_Configure(TC0, 0, config); // Configure channel 0 of TC0 | |
TC_Start(TC0, 0); // Start timer running | |
TC0->TC_CHANNEL[0].TC_IER = TC_IER_LDRAS; // Enable the load AR channel 0 interrupt each PPS | |
TC0->TC_CHANNEL[0].TC_IDR = ~TC_IER_LDRAS; // and disable the rest of the interrupt sources | |
NVIC_EnableIRQ(TC0_IRQn); // Enable interrupt handler for channel 0 | |
// Timer block 2 channel zero is connected to the OR of the PPS and the RAY event | |
config = TC_CMR_TCCLKS_TIMER_CLOCK1 | // Select fast clock MCK/2 = 42 MHz | |
TC_CMR_ETRGEDG_RISING | // External trigger rising edge on TIOA1 | |
TC_CMR_ABETRG | // Use the TIOA external input line | |
TC_CMR_LDRA_RISING; // Latch counter value into RA | |
TC_Configure(TC2, 0, config); // Configure channel 0 of TC2 | |
TC_Start(TC2, 0); // Start timer running | |
TC2->TC_CHANNEL[0].TC_IER = TC_IER_LDRAS; // Enable the load AR channel 0 interrupt each PPS | |
TC2->TC_CHANNEL[0].TC_IDR = ~TC_IER_LDRAS; // and disable the rest of the interrupt sources | |
NVIC_EnableIRQ(TC6_IRQn); // Enable interrupt handler for channel 0 | |
// Set up the PIO controller to route input pins for TC0 and TC2 | |
PIO_Configure(PIOC,PIO_INPUT, | |
PIO_PB25B_TIOA0, // D2 Input | |
PIO_DEFAULT); | |
PIO_Configure(PIOC,PIO_INPUT, | |
PIO_PC25B_TIOA6, // D5 Input | |
PIO_DEFAULT); | |
} | |
// Timer chip interrupt handlers try to get time stamps to within 4 system clock ticks | |
static uint32_t displ = 0; // Display values in loop | |
static uint32_t ppsfl = LOW, // PPS Flag boolean | |
rega0 = 0, // RA reg | |
stsr0 = 0, // Interrupt status register | |
ppcnt = 0; // PPS count | |
// Handle the PPS interrupt in counter block 0 ISR | |
void TC0_Handler() { | |
// This ISR is connected only to the PPS (Pulse Per Second) GPS event | |
// Each time this runs, set the flag to tell the TC6 ISR we have seen it | |
// This logic only works if the TC0 handler gets called before the TC6 handler | |
// hence the debug flag which I look at with a scope to be sure. | |
// I may introduce a small delay line to ensure this is true, so far it is. | |
rega0 = TC0->TC_CHANNEL[0].TC_RA; // Read the RA reg (PPS period) | |
stsr0 = TC_GetStatus(TC0, 0); // Read status and clear load bits | |
//Custom code for read out of ADC on trigger is here | |
bufferdump=true; //set the buffer dump flag | |
Serial.println("trigint"); //announce trigger over serial | |
Serial.println(rega0); //write out the counter value (how many clock counts since last trigger) | |
Serial.print(bufn); //write out the ADC buffer position at the time of the trigger, note that the lab configuration with this code meant the rising edge was 2 buffers back from the one in use. | |
} | |
void loop(){ | |
if (bufferdump) { | |
Serial.println(" bufferdump"); //confirm this part of the code is running | |
//obufn=(bufn+7)&7; //look in the previous buffer (n-1) | |
//pbufn=(bufn+6)&7; //look in the one before that (n-2) | |
qbufn=(bufn+5)&7; // and look in the buffer before that (n-3) | |
//SerialUSB.write((uint8_t *)buf[obufn],512); // send it - 512 bytes = 256 uint16_t - this was the original code, converted to serialise and int the buffer for simplicity | |
Serial.println("n-2 buffer"); //the buffer index, i.e. qbufn | |
for (int s=0; s < 50; s++){ | |
Serial.print(int(s)); //sample index within buffer | |
Serial.print(' '); | |
Serial.println(int(buf[qbufn][s])); //print the value from the buffer | |
} | |
Serial.println(); | |
bufferdump = false;//reset flag | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment