I am trying to sample 4 to 8 Analog input signals coming from microphones on the Teensy 3.1 simultaneously.
Unfortunately there are only two ADC's available on the Teensy 3.1. So to measure 4-8 signals I want to sequentially measure all the target pins and then start again with the first pin. To achieve high sample rates I use DMA channels to lighten the load of the CPU.
The first DMA channel is configured to copy the measured samples from the ADC's output register to a buffer. I tested a sketch in which I only sampled one signal and It seemed to work.
To measure multiple channels I added a second DMA channel. The second DMA channel is configured to rewrite the ADC's configuration register with different values each time the first DMA channel is activated. The target pin numbers are kept in static array and the second DMA channel is supposed to copy the pin number to the ADC's register and thereby switching to a different target pin and starting a new configuration.
Unfortunately this does not seem to work. Any idea what I am doing wrong here?
Thanks in advance
Unfortunately there are only two ADC's available on the Teensy 3.1. So to measure 4-8 signals I want to sequentially measure all the target pins and then start again with the first pin. To achieve high sample rates I use DMA channels to lighten the load of the CPU.
The first DMA channel is configured to copy the measured samples from the ADC's output register to a buffer. I tested a sketch in which I only sampled one signal and It seemed to work.
To measure multiple channels I added a second DMA channel. The second DMA channel is configured to rewrite the ADC's configuration register with different values each time the first DMA channel is activated. The target pin numbers are kept in static array and the second DMA channel is supposed to copy the pin number to the ADC's register and thereby switching to a different target pin and starting a new configuration.
Unfortunately this does not seem to work. Any idea what I am doing wrong here?
Thanks in advance
Code:
#include <string.h>
#include "ADC.h"
#include <DMAChannel.h>
#define BUF_SIZE 256
#define NO_BUFFERS 4
DMAMEM static volatile uint16_t __attribute__((aligned(BUF_SIZE+0))) adcbuffer[NO_BUFFERS][BUF_SIZE];
volatile int ibuf;
volatile int obuf;
ADC *adc = new ADC();
DMAChannel* dma1 = new DMAChannel(false);
DMAChannel* dma2 = new DMAChannel(false);
const uint32_t ChannelsCfg [] = { 6, 7, 15, 4 }; //pin 20-23
void setup() {
Serial.begin(9600); // USB is always 12 Mbit/sec
delay(1000);
Serial.println("Ready");
delay(1000);
}
void loop() {
// clear buffer
for (int i = 0; i < NO_BUFFERS; ++i){
for (int j = 0; j < BUF_SIZE; ++j){
adcbuffer[i][j] = 50000;
}
}
ibuf = 0;
obuf = 0;
setup_dma();
setup_adc();
for (int i = 0; i < BUF_SIZE; ++i){
while(obuf==ibuf);
Serial.write((uint8_t *)adcbuffer[obuf],512);
obuf=(obuf+1)&3;
}
for (;;) {}
}
void setup_dma() {
dma1->begin(true); // allocate the DMA channel first
dma1->TCD->SADDR = &ADC0_RA; // where to read from
dma1->TCD->SOFF = 0; // source increment each transfer
dma1->TCD->ATTR = DMA_TCD_ATTR_SSIZE(1);
dma1->TCD->NBYTES_MLNO = 2; // bytes per transfer
dma1->TCD->SLAST = 0;
dma1->destinationBuffer(adcbuffer[0], 512); // destinaton sizeof(adcbuffer[0])
dma1->triggerAtHardwareEvent(DMAMUX_SOURCE_ADC0);
dma1->disableOnCompletion(); // require restart in code
dma1->interruptAtCompletion();
dma1->attachInterrupt(dma1_isr);
dma1->enable();
dma2->begin(true); // allocate the DMA channel first
dma2->sourceCircular(ChannelsCfg, 4);
dma2->destination(ADC0_SC1A);
//dma2->triggerAtHardwareEvent(DMAMUX_SOURCE_ADC0);
//read somewhere that DMAMUX_SOURCE_ADC0 is only able to trigger one DMA channel
dma2->triggerAtTransfersOf(*dma1);
dma2->enable();
}
void setup_adc() {
adc->setAveraging(32); // set number of averages
adc->setResolution(16); // set bits of resolution
adc->setReference(INTERNAL);
adc->adc0->enableDMA(); //ADC0_SC2 |= ADC_SC2_DMAEN; // using software trigger, ie writing to ADC0_SC1A
ADC0_SC1A = ChannelsCfg[0]; //write target to register to start first conversion
}
void dma1_isr(void) {
ibuf=(ibuf+1)&3;
dma1->destinationBuffer(adcbuffer[ibuf],512); //sizeof(adcbuffer[ibuf])
dma1->enable();
dma1->clearInterrupt();
}