I need to read 4 channels of the DUE and transfer the readings at an sampling rate of 50 kHz. I read posts about DUE ADC and I know that Due can reach 1 MSPS, however when printing is involved the speed exponentially goes down.
The below code is printing part. I made my tests with 2 channels and could reach to ~5 kHz only. I see that the speed goes down due to printing. Any help/suggestions appreciated.
t = micros();
for (i = 0; i<10000; i++) {
while ((adc_get_status(ADC)&ADC_ISR_DRDY) != ADC_ISR_DRDY);
SerialUSB.println(adc_get_channel_value(ADC, ADC_CHANNEL_7));
SerialUSB.println(adc_get_channel_value(ADC, ADC_CHANNEL_6));
}
t = micros() - t;
Referring to the example. If I enable two channels, buffer would hold two channels in order like; ch1(0), ch2(0), ch1(1), ch2(1)..., right? Then, the sampling rate would be half I guess?
Once you set an ADC sampling rate, this sampling rate will be the one for each and every ADC channel enabled. The ADC peripheral will automtically apply this sampling rate to all enabled channels. However the sampling rate times the number of ADC enabled channels can't be higher than 1 Msps.
So I made the below changes to the github code you shared. To enable 4 channels;
In line #31
ADC->ADC_CHER = 0xF0; // 1111 0000
And my loop is as follows;
sampleSize = 10000;
unsigned long st = micros();
for (int i = 0; i<sampleSize; i++) {
while(obufn==bufn); // wait for buffer to be full
SerialUSB.write((uint8_t *)buf[obufn],512); // send it - 512 bytes = 256 uint16_t
obufn=(obufn+1)&3;
}
unsigned long et = micros();
SerialUSB.println(sampleSize*(256/4)*1000.0 / (et-st)); // divide 256 by 4 since four channels active
This resulted in 166.66 kHz. Do you see any flaws?
I'm trying to understand as much as I can. But I'm not sure about the changes I'm making this is why I'm consulting in this forum. Can you point my mistakes more specifically. I appreciate your help.
I guess I need to enable interrupts for other channels and compare ADC_ISR with enabled channels.
Here are 2 example sketches for ADC conversions triggered at a precise frequency (44.1 KHz):
Without a PDC DMA, 44.1 KHz sampling, 1 ADC channel:
volatile uint32_t lastConversion;
volatile boolean Flag;
void setup()
{
adc_setup();
tc_setup();
}
void loop()
{
if (Flag== true)
{
Flag = false;
// A new conversion is available
}
}
/************* Configure ADC function *******************/
void adc_setup() {
PMC->PMC_PCER1 |= PMC_PCER1_PID37; // ADC power on
ADC->ADC_CR = ADC_CR_SWRST; // Reset ADC
ADC->ADC_MR |= ADC_MR_TRGEN_EN // Hardware trigger select
| ADC_MR_TRGSEL_ADC_TRIG3 // Trigger by TIOA2
| ADC_MR_PRESCAL(1);
ADC->ADC_ACR = ADC_ACR_IBCTL(0b01); // For frequencies > 500 KHz
ADC->ADC_CHER = ADC_CHER_CH7; // Enable ADC CH7 = A0
ADC->ADC_IER = ADC_IER_EOC7; // Interrupt on End of conversion
NVIC_EnableIRQ(ADC_IRQn); // Enable ADC interrupt
}
void ADC_Handler()
{
lastConversion = ADC->ADC_CDR[7];
Flag = true;
}
/************* Timer Counter 0 Channel 2 to generate PWM pulses thru TIOA2 ************/
void tc_setup() {
PMC->PMC_PCER0 |= PMC_PCER0_PID29; // TC2 power ON : Timer Counter 0 channel 2 IS TC2
TC0->TC_CHANNEL[2].TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK2 // MCK/8, clk on rising edge
| TC_CMR_WAVE // Waveform mode
| TC_CMR_WAVSEL_UP_RC // UP mode with automatic trigger on RC Compare
| TC_CMR_ACPA_CLEAR // Clear TIOA2 on RA compare match
| TC_CMR_ACPC_SET; // Set TIOA2 on RC compare match
TC0->TC_CHANNEL[2].TC_RC = 238; //<********************* Frequency = (Mck/8)/TC_RC Hz = 44.117 Hz
TC0->TC_CHANNEL[2].TC_RA = 40; //<******************** Any Duty cycle in between 1 and 874
TC0->TC_CHANNEL[2].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Software trigger TC2 counter and enable
}
Note that for 2 ADC channels (e.g. ch6 and ch7), ADC conversions would be ch6/ch7/ch6/ch7/....Therefore a single interrupt would be sufficient: Interrupt On End of conversion for ch7 only, then read ADC->ADC_CDR[7] and ADC->ADC_CDR[6] in the interrupt handler.