I tried the async as you suggested but I couldn't get it right..
I did try again with DMA though and the timer provided by the ADC lib and I get better result than before, I can get the 32 inputs at 22.05kHz, so sampling frequency is only divided by 2 now.
I have a simple procedural code if you want to try, I haven't yet converted it into my Input class:
Code:
#include <ADC.h>
#include "DMAChannel.h"
DMAChannel dmaChannel1;
DMAChannel dmaChannel2;
ADC* adc;
uint16_t adc1PinIndex = 0;
uint16_t adc2PinIndex = 1;
const uint16_t buffSize = 128;
const uint16_t inputsCount = 32;
uint16_t buffers[inputsCount][buffSize * 2] {{0}};
uint16_t bufferCount[inputsCount] = {0};
uint16_t val1 = 0;
uint16_t val2 = 0;
uint16_t isr1Count = 0;
uint16_t isr2Count = 0;
uint16_t muxIndex = 0;
uint8_t pinToChannel[4] = {
7, // 14/A0 AD_B1_02
8, // 15/A1 AD_B1_03
12, // 16/A2 AD_B1_07
11, // 17/A3 AD_B1_06
};
void setup() {
Serial.flush();
Serial.begin(9600);
while (!Serial && millis() < 5000) ;
pinMode(A0, INPUT);
pinMode(A1, INPUT);
pinMode(A2, INPUT);
pinMode(A3, INPUT);
pinMode(2, OUTPUT);
pinMode(3, OUTPUT);
pinMode(4, OUTPUT);
// Reset multiplexer to channel 0
digitalWriteFast(2, LOW);
digitalWriteFast(3, LOW);
digitalWriteFast(4, LOW);
dmaChannel1.source((volatile uint16_t &)(ADC1_R0));
dmaChannel1.destination((volatile uint16_t &)val1);
dmaChannel1.transferSize(2);
dmaChannel1.transferCount(1);
dmaChannel1.interruptAtCompletion();
dmaChannel1.attachInterrupt(isr1);
dmaChannel1.triggerAtHardwareEvent(DMAMUX_SOURCE_ADC1);
dmaChannel1.enable();
dmaChannel2.source((volatile uint16_t &)(ADC2_R0));
dmaChannel2.destination((volatile uint16_t &)val2);
dmaChannel2.transferSize(2);
dmaChannel2.transferCount(1);
dmaChannel2.interruptAtCompletion();
dmaChannel2.attachInterrupt(isr2);
dmaChannel2.triggerAtHardwareEvent(DMAMUX_SOURCE_ADC2);
dmaChannel2.enable();
adc = new ADC();
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);
adc->adc0->setSamplingSpeed(ADC_SAMPLING_SPEED::VERY_HIGH_SPEED);
adc->adc0->enableDMA();
adc->adc0->startSingleRead(A0);
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);
adc->adc1->setSamplingSpeed(ADC_SAMPLING_SPEED::VERY_HIGH_SPEED);
adc->adc1->enableDMA();
adc->adc1->startSingleRead(A1);
// Should be *16 to get 128 samples in 2902ms with 32 inputs
// but then it actually takes 3780ms, so max is *8 to get 64 samples under 2902ms
adc->adc0->startTimer(AUDIO_SAMPLE_RATE * 16);
adc->adc1->startTimer(AUDIO_SAMPLE_RATE * 16);
}
void loop() {
}
void isr1() {
isr1Count++;
if (isr1Count <= 2) {
uint16_t inputIndex = muxIndex + 8 * adc1PinIndex;
buffers[inputIndex][bufferCount[inputIndex]] = val1;
bufferCount[inputIndex]++;
if (adc1PinIndex == 0) {
// Switching ADC1 mux to pin A2
adc1PinIndex = 2;
ADC1_HC0 = pinToChannel[adc1PinIndex] | ADC_HC_AIEN;
}
}
processBothIsr();
dmaChannel1.clearInterrupt();
asm("DSB");
}
void isr2() {
isr2Count++;
if (isr2Count <= 2) {
uint16_t inputIndex = muxIndex + 8 * adc2PinIndex;
buffers[inputIndex][bufferCount[inputIndex]] = val2;
bufferCount[inputIndex]++;
if (adc2PinIndex == 1) {
// Switching ADC2 mux to pin A3
adc2PinIndex = 3;
ADC2_HC0 = pinToChannel[adc2PinIndex] | ADC_HC_AIEN;
}
}
processBothIsr();
dmaChannel2.clearInterrupt();
asm("DSB");
}
elapsedMicros timer;
void processBothIsr() {
if (isr1Count < 2 || isr2Count < 2) {
return;
}
// Switching ADC1 mux to pin A0
adc1PinIndex = 0;
ADC1_HC0 = pinToChannel[adc1PinIndex] | ADC_HC_AIEN;
// Switching ADC2 mux to pin A1
adc2PinIndex = 1;
ADC2_HC0 = pinToChannel[adc2PinIndex] | ADC_HC_AIEN;
isr1Count = 0;
isr2Count = 0;
muxIndex++;
muxIndex = muxIndex % 8;
digitalWriteFast(2, muxIndex & 1);
digitalWriteFast(3, muxIndex & 2);
digitalWriteFast(4, muxIndex & 4);
if (bufferCount[inputsCount - 1] >= buffSize) {
// UNCOMENT THIS TO LOOK AT THE SIGNALS
// for (int i = 0; i < buffSize; i++) {
// for (int j = 0; j < inputsCount; j++) {
// if (j % 8 == 0) {
// Serial.print(buffers[j][i]);
// Serial.print(",");
// }
// }
// Serial.println("");
// }
// UNCOMMENT THIS TO LOOK AT THE TIMING
Serial.println(timer);
timer = 0;
Serial.flush();
for (int i = 0; i < inputsCount; i++) {
bufferCount[i] = 0;
}
}
}
You can try without having the hardware, this code is serial printing the timing it takes to get 128 samples.
It looks like the bottleneck is the switch of the ADCs mux in isr1 and isr2:
ADC1_HC0 = pinToChannel[adc1PinIndex] | ADC_HC_AIEN;
and
ADC21_HC0 = pinToChannel[adc2PinIndex] | ADC_HC_AIEN;
Though these instruction does not affect the timing in processBothIsr, probably because this one runs less frequently.
Not sure if there is a faster way to switch the muxs? Or else any idea?