Maximum DMA ADC speed on Teensy 4.1 with 2 channels

was-ja

Well-known member
Hi,

my best season greeting to all forum participants and PJRC Team!

Let me please, ask you regarding to the maximum possible ADC speed on Teensy 4.1 by using DMA and two channels.

To get it myself I slightly modified the example adc_dma from Teensydoino (1.56) and attachint it here:

Code:
#include <ADC.h>
#include <AnalogBufferDMA.cpp>

// ADACK_2_4, ADACK_4_0, ADACK_5_2 and ADACK_6_2, ACS ADC_CONVERSION_SPEED::VERY_HIGH_SPEED

#define ACS ADC_CONVERSION_SPEED::VERY_HIGH_SPEED
#define ASS ADC_SAMPLING_SPEED::HIGH_VERY_HIGH_SPEED
// resolution
#define ARE 8

const int readPin_adc_0 = 24;
const int readPin_adc_1 = 26;


ADC *adc = new ADC();

const uint32_t buffer_size = 16*1024;

#define MT DMAMEM
// #define MT

MT static volatile uint16_t __attribute__((aligned(32))) dma_adc_buff1[buffer_size];
MT static volatile uint16_t __attribute__((aligned(32))) dma_adc_buff2[buffer_size];
AnalogBufferDMA abdma1(dma_adc_buff1, buffer_size, dma_adc_buff2, buffer_size);

MT static volatile uint16_t __attribute__((aligned(32))) dma_adc2_buff1[buffer_size];
MT static volatile uint16_t __attribute__((aligned(32))) dma_adc2_buff2[buffer_size];
AnalogBufferDMA abdma2(dma_adc2_buff1, buffer_size, dma_adc2_buff2, buffer_size);

// const uint32_t initial_average_value = 2048;

unsigned int CurTime[2];
int LoopCount[2];


void ProcessAnalogData(AnalogBufferDMA *pabdma, int8_t adc_num);


void setup()
{ //    while (!Serial && millis() < 5000) ;

    pinMode(LED_BUILTIN, OUTPUT);
    pinMode(readPin_adc_0, INPUT);
    pinMode(readPin_adc_1, INPUT);

    Serial.begin(9600);
    Serial.println("Setup ADC_0");

    adc->adc0->setAveraging(0); // set number of averages
    adc->adc1->setAveraging(0); // set number of averages

    adc->adc0->setResolution(ARE);
    adc->adc1->setResolution(ARE);

    adc->adc0->setConversionSpeed(ACS);
    adc->adc0->setSamplingSpeed(ASS);
    adc->adc1->setConversionSpeed(ACS);
    adc->adc0->setSamplingSpeed(ASS);

    abdma1.init(adc, ADC_0);
    abdma2.init(adc, ADC_1);

    adc->adc1->startContinuous(readPin_adc_1);
    adc->adc0->startContinuous(readPin_adc_0);

    Serial.println("End Setup");
    CurTime[0]=CurTime[1]=ARM_DWT_CYCCNT;
    LoopCount[0]=LoopCount[1]=0;
}


void loop()
{ if (abdma1.interrupted()) ProcessAnalogData(&abdma1, 0);
  if (abdma2.interrupted()) ProcessAnalogData(&abdma2, 1);
}


void ProcessAnalogData(AnalogBufferDMA *pabdma, int8_t adc_num)
{ uint16_t min_val = 0xffff;
  uint16_t max_val = 0;

  volatile uint16_t *pbuffer = pabdma->bufferLastISRFilled();
  int Len = pabdma->bufferCountLastISRFilled();
  if ((uint32_t)pbuffer >= 0x20200000u)  arm_dcache_delete((void*)pbuffer, sizeof(dma_adc_buff1));

  if(((LoopCount[adc_num]++)&31)==0)
  { float sum = 0.0;
    float sum2 = 0.0;
    float InvLen = 1./(float)Len;

    for(int i=0; i<Len; i++)
    { uint16_t val = *pbuffer;
      if (val < min_val) min_val = *pbuffer;
      if (val > max_val) max_val = *pbuffer;
      sum += val;
    }
    sum*=InvLen;
    for(int i=0; i<Len; i++)
    { float val = ((float)(*pbuffer)) - sum;
      sum2 += val*val;
    }

    sum2*=InvLen;
    unsigned int ct=ARM_DWT_CYCCNT;
    unsigned int ct2=CurTime[adc_num]; CurTime[adc_num]=ct;
    unsigned int tpc=(((ct-ct2)>>19)+1)>>1;
    Serial.printf(" %d - %u(%u): Ticks/Conversion=%d, Len=%d, %u <= %u(%u) <= %u\n", adc_num, pabdma->interruptCount(), pabdma->interruptDeltaTime(), tpc, Len,
      min_val, (unsigned int)(sum+0.5), (unsigned int)(sum2+0.5), max_val);
  }
  pabdma->clearInterrupt();
}

where ACS - conversion speed, ASS - sampling speed and ARE - the resolution.

With this settings I found very impressive 3.4MS/s on the first channel and 2MS/s on the second running the test at 600MHz clock and surprisingly slowdown at overclocked 812MHz as: 2.3MHz and 1.3MHz

I have questions, please, help me to understand:

  • why the first channel works faster that the second one, or I need to adjust something at the second channel to run it at the same speed as the first channel?
  • I would like to work at slightly overclocked CPU (i.e. 812MHz) and it seems that all other my computational parts works ok on this speed (I have small radiator on CPU). Please, suggest how to force Teensy 4.1 to work at 812MHz at least with the same speed of ADC as on the 600MHz?

Thank you!
 
The first my question
  1. why the first channel works faster that the second one, or I need to adjust something at the second channel to run it at the same speed as the first channel?
is fixed, I played with many possible variants for readPin_adc_0 and readPin_adc_1 and figured out that I did not set ADC_SAMPLING_SPEED::HIGH_VERY_HIGH_SPEED for the second channel.

However, my second question
  1. I would like to work at slightly overclocked CPU (i.e. 812MHz) and it seems that all other my computational parts works ok on this speed (I have small radiator on CPU). Please, suggest how to force Teensy 4.1 to work at 812MHz at least with the same speed of ADC as on the 600MHz?

is still open. I found that for 12 bit resolution, I have CPU/ADC clock ratio for any 600-1008MHz equal to 480, so, it seems that I need to change ADC clock appropriately, but I do not know what else I can insert for setConversionSpeed or setSamplingSpeed.

Thank you!
 
Back
Top