Read from ADC - Arduino Due

Hi I using this code to read from ADC (A0 channel 7):

val = *(ADC->ADC_CDR + 7) ;

This is where the conversion resulte is.

I am trying now to read from 2 diffrent channels. This is because I need to sample to diffrent signals (at the same time).

I have tried to understand how to read from two channels one after another but I dont understand how can I do it.

what I have now is:
I am using timer for sampling frequency, then at (EOF - End Of Conversion) I get an interrupt, then I read from the ADC using the code above the sample.

I dont understand the names of different channels and unfortunately I dont know were I can find it.
I want to use A1 also (that is Channel 6), but how can I read from both of them. And as far as I understand, when more then one channel is active then the ADC speed will increed.

You can find header files here (as well as on your PC):

https://android.googlesource.com/platform/external/arduino-ide/+/f876b2abdebd02acfa4ba21e607327be4f9668d4/hardware/arduino/sam/system/CMSIS/Device/ATMEL/sam3xa/include/component

To read channel X ADC conversion:

Val = ADC->ADC_CDR[X];

There are numerous example sketches for ADC conversions in the DUE sub forum.

So it would be the same to write:

val = *(ADC->ADC_CDR + 7);

as:

Val = ADC->ADC_CDR[7];

?

And thank you for your response.

I have written the code so that on EOC (End Of Conversion) of channel 7 I will receive an interrupt, can I at the same interrupt read from to different channels? like:

Val_1 = ADC->ADC_CDR[7];
Val_2 = ADC->ADC_CDR[6];

I have written the code so I can sample at the rate of 12kHz using timer.
Do I need to change the sample rate then?

Where is your code ?

This is the code that is being executed when the EOC occurs:

void ADC_Handler (void)
{
  if (ADC->ADC_ISR & ADC_ISR_EOC7)   // ensure there was an End-of-Conversion and we read the ISR reg
  {
    val = *(ADC->ADC_CDR + 7) ;  // get conversion result
    samples [sptr] = val ;           // stick in circular buffer
    sptr = (sptr+1) & BUFMASK ;      // move pointer

    EOC_flag = true;
  }
}

If you trigger conversions thru a timer, the ADC peripheral will automatically multiply the timer frequency by the number of channel conversions (up to 1 Msps).

Post ALL the code you are actually unsing.

Timer setup

// Timer Counter (TC)
void ADC_Timer_Setup(int Frequency){
	pmc_enable_periph_clk (TC_INTERFACE_ID + 0 * 3 + 0) ; // clock the TC0 channel 0

	TcChannel * t = &(TC0->TC_CHANNEL)[0] ;    // pointer to TC0 registers for its channel 0
	// The pointer is used insted of writing: TC0->TC_CHANNEL[0], I can write: t->

	t->TC_CCR = TC_CCR_CLKDIS ;  // disable internal clocking while setup regs 		
	t->TC_IDR = 0xFFFFFFFF ;     // disable interrupts								
	t->TC_SR ;                   // read int status reg to clear pending 			

	// In Waveform Mode TIOB is set as output 
	t->TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK4 |   	// use TCLK4 (prescale by 128, = 656.25kHz)
	          TC_CMR_WAVE |                 	// waveform mode
	          TC_CMR_WAVSEL_UP_RC |         	// count-up PWM using RC as threshold
	          TC_CMR_EEVT_XC0 |     			// Set external events from XC0 (this setup TIOB as output)
	          TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_CLEAR |
	          TC_CMR_BCPB_CLEAR | TC_CMR_BCPC_CLEAR ;


	t->TC_RC =  0x080;	// 0x80 = 5.126kHz
	t->TC_RA =  80; 	// setup time for next itteration
	t->TC_CMR = (t->TC_CMR & 0xFFF0FFFF) | TC_CMR_ACPA_CLEAR | TC_CMR_ACPC_SET ;  	// set clear and set from RA and RC compares
																					
	// re-enable local clocking and switch to hardware trigger source.
	t->TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG ;
}

The ADC setup is:

void ADC_setup() {

  PMC->PMC_PCER1 |= PMC_PCER1_PID37;  // ADC power on
  ADC->ADC_CR = ADC_CR_SWRST; // Reset ADC 			

  NVIC_EnableIRQ (ADC_IRQn); // enable ADC interrupt vector

  ADC->ADC_IDR = 0xFFFFFFFF; // Disables all interrupts
  ADC->ADC_IER = 0x80; // Enable End-Of-Conversion interrupt on Channel 7 -> A0  

  ADC->ADC_CHDR = 0xFFFFFFFF; // Disables all channels
  ADC->ADC_CHER = 0x80; // Enable channel 7				

  ADC->ADC_MR = ADC->ADC_MR & 0xFFFFFFF0; // Disable Trigers | 4 lowesr bits of ADC_MR 
  ADC->ADC_MR = ADC->ADC_MR | 0x2; // TRGEN on => External trigger 
  ADC->ADC_MR = ADC->ADC_MR | 0x1; // TIOA Output of the Timer Counter Channel 0 
  ADC->ADC_MR = ADC->ADC_MR & 0xFFFFFFEF; // 12-bit resolution 
}

After a fast reading, that should work for 1 channel.

However it would be much more readable if you would use proper names that you can find in the datasheet and the header files.

An example sketch to read n channels:

/*******************************************************************************/
/*                     10 Hz ADC conversions for nchannels                     */
/*******************************************************************************/
#define SampleRate  (10)
uint8_t channels[] = {5,7};

#define nchannels (sizeof(channels))
uint16_t Buf[nchannels];

volatile boolean FlagConversion;

void setup()
{
  Serial.begin(250000);
  pinMode(LED_BUILTIN, OUTPUT);
  adc_setup();
  tc_setup();
}

void loop()
{

  if (FlagConversion == true)
  {
    FlagConversion = false;
    for (int i = 0; i < nchannels; i++)
    {
      printf(" ADC[%d] = %d\n", channels[i], Buf[i]);
    }
  }
}

/*************  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_PRESCAL(1) |
                  ADC_MR_TRGSEL_ADC_TRIG3;               // Trigger by TIOA2 Rising edge

  ADC->ADC_IDR = ~(0ul);
  ADC->ADC_CHDR = ~(0ul);
  for (int i = 0; i < nchannels; i++)
  {
    ADC->ADC_CHER |= ADC_CHER_CH0 << channels[i];
    ADC->ADC_IER |= ADC_IER_EOC0 << channels[i];
  }
  //ADC->ADC_ACR = ADC_ACR_IBCTL(0b01);                    // For frequencies > 500 KHz
  ADC->ADC_PTCR |= ADC_PTCR_RXTDIS | ADC_PTCR_TXTDIS;    // Disable PDC DMA
  NVIC_EnableIRQ(ADC_IRQn);                              // Enable ADC interrupt
}

/*************  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 = F_CPU / 8 / SampleRate; //<*********************  Frequency = (Mck/8)/TC_RC  Hz = 10 Hz
  TC0->TC_CHANNEL[2].TC_RA = 50;  //<********************   Any Duty cycle in between 1 and (TC_RC - 1)

  TC0->TC_CHANNEL[2].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Software trigger TC2 counter and enable
}

void ADC_Handler() {

  static uint32_t LedBlink;
  static uint8_t Count;
  uint8_t status = ADC->ADC_ISR;

  for (int i = 0; i < nchannels; i++)
  {
    if (status & (ADC_ISR_EOC0 << channels[i]))
    {
      Buf[i] = (uint16_t) * (ADC->ADC_CDR + channels[i]);
    }
  }
  if (Count++ == nchannels)
  {
    Count = 0;
    FlagConversion = true;
  }
  // For debugging only : Blink every 1 Hz
  if (LedBlink++ == (SampleRate * nchannels))
  {
    LedBlink = 0;
    PIOB->PIO_ODSR ^= PIO_ODSR_P27;
  }
}

I understand it might be difficult to read, I apologize in front but I written the ADC & Timer setup using the chip datasheet.

I wanted to know if someone can please tell me if I have written the ADC setup for to channels (For A0 and A1) correctly?

void ADC_setup() {

  PMC->PMC_PCER1 |= PMC_PCER1_PID37;  // ADC power on
  ADC->ADC_CR = ADC_CR_SWRST; // Reset ADC

  NVIC_EnableIRQ (ADC_IRQn); // enable ADC interrupt vector

  ADC->ADC_IDR = 0xFFFFFFFF; // Disables all interrupts
  // ADC->ADC_IER = 0x80; // Enable End-Of-Conversion interrupt on Channel 7 -> A0
  ADC->ADC_IER = 0x0C0; // Enable End-Of-Conversion interrupt on Channel 7 -> A0 & Channel 6 -> A1.
  
  ADC->ADC_CHDR = 0xFFFFFFFF; // Disables all channels
  // ADC->ADC_CHER = 0x80; // Enable channel 7
  ADC->ADC_CHER = 0x0C0; // Enable channel 7 & 6 for A0 & A1
  
  ADC->ADC_MR = ADC->ADC_MR & 0xFFFFFFF0; // Disable Trigers | 4 lowesr bits of ADC_MR
  ADC->ADC_MR = ADC->ADC_MR | 0x2; // TRGEN on => External trigger
  ADC->ADC_MR = ADC->ADC_MR | 0x1; // TIOA Output of the Timer Counter Channel 0

  ADC->ADC_MR = ADC->ADC_MR & 0xFFFFFFEF; // 12-bit resolution
}

And just to clarify:
If let say I setup the timer with a frequency of 12kHz, using two channels would multiply this setting.

What I would like to know and filed to find is who will the ADC sample from two channels? (using the EOC interrupt)

For example if I work with the timer with a frequency of 12kHz, Two channel -> double the frequency of the time; will the ADC firts after 12kHz sample A0 and after another 12kHz it will sample A1, then again after 12kHz it will sample A0 and so... ?

OR
It will sample A0, the immediately it will sample A1 ..... ?

Again thanks for helping.