Arduino due analog PDC, acces ADC_SEQR2 data

Hi.

I'm trying to create an API for reading all analog input trough PDC controler. I inspire myself by this example: arduino-due-adc-channel-sequence

I check pin map with : Due-pinout-WEB.png

I modify a bit the code for reading all 12 input and put it into an array buffer. It work fine for A0 to A7, then when it come into reading data from ADC_SEQR2, it seem to do not work.

Reading work for A0 to A7, then when it come to A8 to A11, if I put A8 to ground, A8, A9 and A11 readings goes down to 0...

Here the code. Thanks.

#include <Streaming.h>

 
int const NUM_CHANNELS=12;
uint16_t global_ADCounts_Array[NUM_CHANNELS];  // holds the raw data from the analog to digital 

void setup_AtoD(){
 
// Arduino Due ADC->DMA 
// first modified by contravalent from code on the  internet by "Stimmer"

// fills the array global_ADCounts_Array in the background, with data from the ADC  

 pmc_enable_periph_clk(ID_ADC);   //power management controller told to turn on adc
 ADC->ADC_CR |=1; //reset the adc
 ADC->ADC_MR= 0x9038ff00;  //this setting is used by arduino. 

  ADC->ADC_CHDR=0xFFFFFFFF;   // disable all channels   
  ADC->ADC_CHER=0x3cff;       //   use channels A0 to A11 //
  ADC->ADC_MR |=0x80000000;   //USEQ bit set, saying use the sequence
  ADC->ADC_SEQR1=0x01234567;  // use A0 to A7 in order into array 
  ADC->ADC_SEQR2=0x0000dcba;      //use A8 to A11 following in order into array
  
  NVIC_EnableIRQ(ADC_IRQn); // interrupt controller set to enable adc.
  ADC->ADC_IDR=~((1<<27)); // interrupt disable register, disables all interrupts but ENDRX
  ADC->ADC_IER=(1<<27);   // interrupt enable register, enables only ENDRX
  Serial.println();
  Serial.print("mode register ="); Serial.println(REG_ADC_MR, HEX); 
  Serial.print("channel enabled register ="); Serial.println(REG_ADC_CHSR, HEX);
  Serial.print("sequence register1 ="); Serial.println(REG_ADC_SEQR1, HEX); 
  Serial.print("sequence register2 ="); Serial.println(REG_ADC_SEQR2, HEX);
  Serial.print("interrupts ="); Serial.println(REG_ADC_IMR, HEX); delay(5000);
 // following are the DMA controller registers for this peripheral
 // "receive buffer address" 
 ADC->ADC_RPR=(uint32_t) global_ADCounts_Array;   // DMA receive pointer register  points to beginning of global_ADCount
 // "receive count" 
 ADC->ADC_RCR=NUM_CHANNELS;  //  receive counter set to 12
 // "next-buffer address"
 ADC->ADC_RNPR=(uint32_t)global_ADCounts_Array; // next receive pointer register DMA global_ADCounts_Arrayfer  points to second set of data 
 // and "next count"
 ADC->ADC_RNCR=NUM_CHANNELS;   //  and next counter is set to 12
 // "transmit control register"
 ADC->ADC_PTCR=1;  // transfer control register for the DMA is set to enable receiver channel requests
 // now that all things are set up, it is safe to start the ADC.....
 ADC->ADC_MR |=0x80; // mode register of adc bit seven, free run, set to free running. starts ADC

}



void ADC_Handler()
{     // for the ATOD: re-initialize DMA pointers and count 
 int f=ADC->ADC_ISR;  //   read the interrupt status register 

 if (f & (1<<27)){ /// check the bit "endrx"  in the status register
  /// set up the "next pointer register" 
  ADC->ADC_RNPR=(uint32_t) global_ADCounts_Array;  // "receive next pointer" register set to global_ADCounts_Array 
  // set up the "next count"
  ADC->ADC_RNCR=NUM_CHANNELS;  // "receive next" counter set to 12
   
 }

}

void setup(){
  Serial.begin(115200); 

  
  setup_AtoD();
  analogReadResolution(12);
}

void loop() {

  unsigned int temp[NUM_CHANNELS];
  for (int i = 0; i < NUM_CHANNELS; i++){
    temp[i] = global_ADCounts_Array[i]&=0xFFF;  //keep only data on buffer
    Serial << "temp " << i  << " = " << temp[i] << endl;

  }
  Serial << endl;
  delay(500);
}

In order to get a clean code, I suggest that you read (for example) this thread, from reply #8 to the end :

https://forum.arduino.cc/index.php?topic=137635.0

Then try to follow step by step this sequence:

  • Try an ADC conversion with a PDC DMA with only 2 channels (say ch7 and ch6)

  • set ADC_SEQR1 with ch7 and ch6, set USEQ bit accordingly

  • set ADC_SEQR1 with 8 channels

  • set ADC_SEQR1 and SEQR2 with 8 channels in SEQR1 and 1 channel in SEQR2

  • Finaly set ADC_SEQR1 and ADC_SEQR2 with all your available channels

I've done all of this already. and found the solution by trying each position of the SEQR2 one by one and find out that I have to offset 23 position to the left the sequence... dont ask me why.. :confused: but now it work:

ADC->ADC_SEQR2=0x0000dcba;      //use A8 to A11 following in order into array
//replace by --->
ADC->ADC_SEQR2=0x00dcba00;      //use A8 to A11 following in order into array

Huh?! The example you gave was a left shift 8 bits. I don't see anything that relates to 23.

But, what might explain it is this: The people who made the Due think that they're super hilarious. Look at the pinout here:

You can see that analog inputs 0 through 7 are actually backward of the analog numbering at the hardware itself. That is, Arduino analog input 0 is actually hardware input 7, Arduino input 5 is really hardware input 2, etc. When you do the direct hardware register access you need to use the hardware numbers not arduino numbers and this gets super confusing. Now, look past A7 -> Arduino analog inputs A8 through A11 are really hardware analog inputs 10 through 13. This means you have to shift things up 2 when translating Arduino numbers A8 through A11 up to hardware numbers. And, you have to invert the ordering for the first 8. Nothing could be simpler!

It's easy to forget these details and really screw things up.

pinout is checked. And selection on sequencer have been set in order as well.

the only thing that is strange, is the correction I add to do.

his means you have to shift things up 2 when translating Arduino numbers A8 through A11

ADC->ADC_SEQR2=0x00dcba00;      //use A8 to A11 following in order into array

abcd in HEX is 10,11,12,13.

What I mean is, in ADC_SEQR2, each number represente a position in the sequencer, right to left. I expect startin at the fist position should follow directly ADC_SEQR1. but it didn't. Wrote like I done, it work...

_The people who made the Due think that they're super hilarious _ :slight_smile: Well it might be the case, nonetheless the only explanation I found for the weird correspondence between ADC ch7 <--> A0, …, ADC ch0<--> A7 is that it simplifies the PCB manufacturing.

Regarding channels positions of ADC_SEQR2, care must be taken to set ADC_CHER accordingly, e.g. :
(I didn't tested, though)

/* See pinout diagram : The only available channels
  are : 0,1,2,3,4,5,6,7,10,11,12,13,15
  Note that ch15 is the built in temperature sensor !!

  Here is an example of selection, you can't select more than
   16 consecutive channels thru SEQR1 and SEQR2
*/

uint8_t channels[10] = { 7, 6, 7, 0, 3, 2, 1, 0, 10, 11};

/* todo : add sanity checks for ADC channel numbers selected  */

void setup () {
  for (uint8_t i = 0; i < 10; i++) {
    if (channels[i] < 8) {

      ADC->ADC_SEQR1 |= channels[i] << i * 4;
      /* Note: if USEQ = 1 in ADC_MR register, CHx= i corresponds to the ith channel of the sequence      described in ADC_SEQR1 and ADC_SEQR2.  */
      ADC->ADC_CHER |= channels[i] << i;
    }

    else {

      ADC->ADC_SEQR2 |= channels[i] << 4 * (i - 8);
      ADC->ADC_CHER |= channels[i] << i;
    }
  }

}

void loop() {

}

Hum. Like the idea for function adding channel to the sequencer. I probably use it for further library development option... :stuck_out_tongue:

However, I dough that ADC_CHER cause my problem. If it was the case, the channel will read nothing? isn't?

At least with your solution, it avoid human typo mistake.

But for now I double check my register, it is set to : 3CFF.
That correspond to 0011 1100 1111 1111. So channel 0-7 enable, 8 and 9 disable, 10 to 13 enable...

Regards.

Nitrof