Go Down

Topic: how to get the 1Msps in Due (Read 373 times) previous topic - next topic

ED201

May 13, 2018, 02:50 pm Last Edit: May 13, 2018, 10:52 pm by surbyte
I went over bunch of examples, some were producing just garbage and the fastest I got was 666 ksps

here is code from other place, the author claims it is 1 Msps

the data sheet says 1 Msps ( http://www.microchip.com/wwwproducts/en/ATSAM3X8E )




Code: [Select]
  void setup() {
     Serial.begin(9600);
     int t=analogRead(0);

     ADC->ADC_MR |= 0x80; // these lines set free running mode on adc 7 (pin A0)
     ADC->ADC_CR=2;
     ADC->ADC_CHER=0x80;
   }

   void loop() {
     int t=micros();
     int q=0;
     int a0;
     for(int i=0;i<1000000;i++){
       while((ADC->ADC_ISR & 0x80)==0); // wait for conversion
       a0=ADC->ADC_CDR[7];              // read data
       q+=a0;
     }
     t=micros()-t;
     Serial.print("1 million conversions in ");Serial.print(t);Serial.println(" micros");
   }


but it takes 1 500 000 microseconds for 1 000 000 samples,

Due has been around for a while and I cannot find code which gives 1 Msps

Am I missing something

thanks

Moderator: Add tags to code and link.

ard_newbie

Try this code, ADC and DAC in Free Running mode:

Code: [Select]

/*******************************************************************/
/*  Free Run ADC conversions of 1 analog input (A0)                */
/*  Free Run DAC output on channel 1 (DAC1)                        */
/*******************************************************************/

void setup()
{
  pinMode(LED_BUILTIN, OUTPUT);
  adc_setup();
  dac_setup();

}

void loop()
{

}

/*************  Configure adc_setup 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_DIS                      // Hardware trigger disable
                  | ADC_MR_FREERUN
                  | ADC_MR_PRESCAL(0);                  // Or PRESCAL (1) to limit to 21 MHz !

  ADC->ADC_ACR = ADC_ACR_IBCTL(0b01);                   // For frequencies > 500 KHz

  ADC->ADC_IER = ADC_IER_EOC7;                          // End Of Conversion interrupt enable for channel 7
  NVIC_EnableIRQ(ADC_IRQn);                             // Enable ADC interrupt
  ADC->ADC_CHER = ADC_CHER_CH7;                         // Enable Channel 7 = A0
}

void ADC_Handler() {
  static uint32_t Count;
 
  DACC->DACC_CDR = ADC->ADC_CDR[7];                    // Reading ADC->ADC_CDR[i] clears EOCi bit
 
 // For debugging only
  if (Count++ == 1000000) {
    Count = 0;
    PIOB->PIO_ODSR ^= PIO_ODSR_P27;
  }

}

/*************  Configure adc_setup function  *******************/
void dac_setup ()
{

  PMC->PMC_PCER1 = PMC_PCER1_PID38;                   // DACC power ON

  DACC->DACC_CR = DACC_CR_SWRST ;                     // Reset DACC
  DACC->DACC_MR = DACC_MR_TRGEN_DIS                   // Hardware trigger disable, Free Run mode               
                  | DACC_MR_USER_SEL_CHANNEL1         // select channel 1
                  | DACC_MR_REFRESH (1)
                  | DACC_MR_STARTUP_8
                  | DACC_MR_MAXS;

  DACC->DACC_CHER = DACC_CHER_CH1;                   // enable channel 1 = DAC1

}



ED201

#2
May 14, 2018, 02:46 am Last Edit: May 14, 2018, 02:49 am by ED201
I found a fix, it is now 1 us, 1 Msps but I cannot find a way to do this on pin A7, everybody is using pin A0


int x;                                           // read value
int myArray[20000];
void setup()
{
  Serial.begin(9600);                                           // initialize the serial port:
  REG_ADC_MR = 0x10380180;                       // change from 10380200 to 10380180, 1 is the PREESCALER and 8 means FREERUN
  ADC -> ADC_CHER = 0x80;                        // enable ADC on pin A0

}



void loop() {

  int t = micros();                    // init time an elapsed time, in micro seconds
 
 for(int i = 0; i < 20000; i++) {
   
    // instrucction to measure
    while((ADC->ADC_ISR & 0x80)==0);             // wait for conversion
   
                       
   myArray = ADC->ADC_CDR[7];         // read value A0               
  } 
 
  int t1 = micros();                         
 
 
  Serial.println(t1-t);
   
  delay(2000);
}

ard_newbie

#3
May 14, 2018, 04:58 am Last Edit: May 14, 2018, 11:04 am by ard_newbie
Edit your post and post your code between code tags <\>


To sample A7 instead of A0, enable ADC channel 0 (From https://gist.github.com/pklaus/5921022):

Code: [Select]

volatile int bufn, obufn;
const uint16_t bufsize = 256;
uint16_t buf[4][bufsize];  

void setup()
{
  SerialUSB.begin(250000);
  while(!SerialUSB);
  pinMode(LED_BUILTIN, OUTPUT);
  adc_setup();
}

void loop() {
  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;
}


/***************  Configure ADC  ******************************/
void adc_setup() {
  
  PMC->PMC_PCER1 |= PMC_PCER1_PID37; // Enable ADC

  ADC->ADC_MR = ADC_MR_TRGEN_DIS
                | ADC_MR_FREERUN_ON                         // free running
                | ADC_MR_PRESCAL(0);

  ADC->ADC_ACR = ADC_ACR_IBCTL(0b01);
  ADC->ADC_CHER = ADC_CHER_CH0;           // Enable Channel 0 = A7

  ADC->ADC_IER |= ADC_IER_ENDRX ;

  NVIC_EnableIRQ(ADC_IRQn);               // Enable ADC interrupt

  /*************      PDC/DMA  buffer filling *******************/

  ADC->ADC_RPR = (uint32_t)buf[0];        // DMA buffer
  ADC->ADC_RCR = bufsize;
  ADC->ADC_RNPR = (uint32_t)buf[1];       // next DMA buffer
  ADC->ADC_RNCR = bufsize;
  bufn = obufn = 1;
  ADC->ADC_PTCR |= ADC_PTCR_RXTEN;        // Enable PDC receiver channel request
  ADC->ADC_CR = ADC_CR_START;
}

void ADC_Handler() {                // move DMA pointer to next buffer

  if ( ADC->ADC_ISR & ADC_ISR_ENDRX) {
    bufn = (bufn + 1) & 3;
    ADC->ADC_RNPR = (uint32_t)buf[bufn];
    ADC->ADC_RNCR = bufsize;
  }

  // For debugging only
  static uint32_t Count;
  if (Count++ > 3906) { // 3906 = 10E6/256
    Count = 0;
    PIOB->PIO_ODSR ^= PIO_ODSR_P27;
  }
}




With the code below, I can reach 999 938 Hz in Free Running Mode with Prescal (1) and 1 908 976 Hz with Prescal (0), not bad for ADC conversions followed by DAC conversions of 128 Half words buffers:

Code: [Select]

/*******************************************************************************************/
/*                  ADC and DAC in FREE Running Mode and PDC DMA                           */
/*******************************************************************************************/

volatile uint32_t Timestamp, Oldmicros;
volatile boolean Flag;
volatile uint8_t bufn, bufn_dac;
const uint16_t bufsize = 128;            // size must be a power of 2
const uint8_t bufnumber = 4;             // bufnumber > 2, better be a power of 2
const uint8_t _bufnumber = bufnumber - 1;
uint16_t buf[bufnumber][bufsize];        // bufnumber buffers of bufsize samples,

void setup()
{
  Serial.begin(250000);
  pinMode(LED_BUILTIN, OUTPUT);
  adc_setup();
  dac_setup();
  Oldmicros = micros();
}

void loop()
{
  if (Flag) {
    Flag = false;
    Serial.println(Timestamp);
  }
}

/*************  Configure adc_setup 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_DIS                      // Free Running Mode selected
                  | ADC_MR_FREERUN
                  | ADC_MR_PRESCAL(0);                  // Or PRESCAL (1) to reduce ADC frequency to 21 MHz

  ADC->ADC_ACR = ADC_ACR_IBCTL(0b01);                   // For frequencies > 500 KHz

  ADC->ADC_IER = ADC_IER_ENDRX;                         // End Of Conversion interrupt enable for channel 7
  NVIC_EnableIRQ(ADC_IRQn);                             // Enable ADC interrupt
  ADC->ADC_CHER = ADC_CHER_CH7;                         // Enable Channel 7 = A0

  /*********  code for PDC/DMA  buffer filling sequence **********/
  ADC->ADC_RPR = (uint32_t)buf[1];                      // DMA buffer - First one will be buf[1]
  ADC->ADC_RCR = bufsize;
  ADC->ADC_RNPR = (uint32_t)buf[2];                     // next DMA buffer
  ADC->ADC_RNCR = bufsize;
  bufn = 2;
  ADC->ADC_PTCR |= ADC_PTCR_RXTEN;                      // Enable PDC Receiver channel request
  ADC->ADC_CR = ADC_CR_START;
}

/*********  Call back function for ADC PDC/DMA **************/
void ADC_Handler () {

  bufn = (bufn + 1) & _bufnumber;
  ADC->ADC_RNPR = (uint32_t)buf[bufn];
  ADC->ADC_RNCR = bufsize;

  bufn_dac = (bufn_dac + 1) & _bufnumber;
  DACC->DACC_TNPR = (uint32_t)buf[bufn_dac];
  DACC->DACC_TNCR = bufsize;

  // For debugging only
  static uint32_t Count;
  if (Count++ == 7812) { //1000000/128 ~7812
    Timestamp = micros() - Oldmicros;
    Oldmicros = micros();
    Flag = true;
    Count = 0;
    PIOB->PIO_ODSR ^= PIO_ODSR_P27;  // Toggle LED_BUILTIN every 1 Hz
  }

}

/*************  Configure dac_setup function  *******************/
void dac_setup ()
{

  PMC->PMC_PCER1 = PMC_PCER1_PID38;                   // DACC power ON

  DACC->DACC_CR = DACC_CR_SWRST ;                     // Reset DACC
  DACC->DACC_MR = DACC_MR_TRGEN_DIS                   // Free Running Mode selected
                  | DACC_MR_USER_SEL_CHANNEL1         // select channel 1
                  | DACC_MR_REFRESH (1)
                  | DACC_MR_STARTUP_8
                  | DACC_MR_MAXS;

  DACC->DACC_ACR = DACC_ACR_IBCTLCH0(0b10)
                   | DACC_ACR_IBCTLCH1(0b10)
                   | DACC_ACR_IBCTLDACCORE(0b01);

  DACC->DACC_IER = DACC_IER_ENDTX;
  DACC->DACC_CHER = DACC_CHER_CH1;                    // enable channel 1 = DAC1

  /*************   configure PDC/DMA  for DAC *******************/
  DACC->DACC_TPR  = (uint32_t)buf[0];                 // DMA buffer
  DACC->DACC_TCR  = bufsize;
  DACC->DACC_TNPR = (uint32_t)buf[1];                 // next DMA buffer
  DACC->DACC_TNCR =  bufsize;
  bufn_dac = 1;
  DACC->DACC_PTCR = DACC_PTCR_TXTEN;                  // Enable PDC Transmit channel request

}





ED201

thanks for the code, it is a little long, the code  I found is short and simple and it seems to be working.


I am not sure if I understand it well but I need to make a call to ADC_Handler() withing the loop() to take one 1 us reading. it is a little complicated to understand it right away, I am not so good.

Speklap

I am not sure if I understand it well but I need to make a call to ADC_Handler() withing the loop() to take one 1 us reading. it is a little complicated to understand it right away, I am not so good.
No, ADC_Handler gets called when your conversion is complete, -if you enable the interrupt. You set the speed of the adc in its setup registers. No need to call it within the loop().

I came on the same link as ard's, not quite sure you can do shorter than that.

Go Up