[Solved]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.

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 
// modified by contravalent from code on the  internet by "Stimmer"
// this routine reads the two arduino channels A0 and A1
// 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. 
  // prescale :  ADC clock is mck/((prescale+1)*2).  mck is 84MHZ. 
  // prescale : 0xFF=255=164.0625KHz
  //ADC->ADC_MR &=0xFFFF00FF;   //mode register "prescale" zeroed out. 
  //ADC->ADC_MR |=0x0000ff00;   //slow down the adc clock so we don't interrupt so often . this divide sets it very slow
  //ADC->ADC_EMR |= (1<<24);    // turn on channel numbers
  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=0xdcba;      //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("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 4
 // "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 4
 // "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() {

  ADC_Handler();
  int temp[NUM_CHANNELS];
  for (int i = 0; i < NUM_CHANNELS; i++){
    temp[i] = global_ADCounts_Array[i]&=0xFFF;  
    Serial << "temp 0 = " << temp[i] << endl;
  }
  Serial << endl;
  delay(500);
}

Hello nitrof,

Your code is somehow difficult to decipher due to a massive use of magic numbers, but I don't see why ADC_Handler() is called at the beginning of your loop(), it should be only called by ENDRX interruption.

Hi ard_newbie.

Sorry for the mess. I am beginning the skech... and it is only playing with ADC register....

Your code is somehow difficult to decipher due to a massive use of magic numbers, but I don't see why ADC_Handler() is called at the beginning of your loop(), it should be only called by ENDRX interruption.

Yes. you're right, tried it and it work. I didn't knew it was called already.

for clarify thing, I'll try to point out what i've done...

  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=0xdcba;      //use A8 to A11 following in order into array

Each Byte represent a channel on ADC input, base on the schematic that I share earlier.
So on the sequence, It should go, right to left.=, corresponding in SEQR1, Ao to A7,
and on SEQR2, A8 to A11.

pretty not shure about that part... hard to find good reference... :

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

finally, I'm not shure that ADC->ADC_RNPR=(uint32_t) global_ADCounts_Array; point to both SEQR1 and SEQR2...

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

Hope thing are more clear

tried to clean up a bit the code... :

#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);
}

Find out a way.

It appear that the array of data pointed out by 'ADC->ADC_RNPR' do not, for some reason, follow the exact order of sequencer 2 register... to fix it, I add to shit two byte to the left the channel in the sequencer 2.
That way, data on the buffer are in order A0 to A11 :

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

Hello,

I have found kind of issue while using native USB of arduino DUE.
I'm reading all 12ch, but when I use SerialUSB the channel data are scrambled in the buffer, but when I use normal UART Serial then all works as it should.
When I close USB terminal and reopen the channels are scrambled again.

I would apriciate every support. TX. Here is my code:

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

//#define MySerial Serial
#define MySerial SerialUSB

void setup_AtoD(){

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

ADC->ADC_MR &=0xFFFF00FF; //mode register "prescale" zeroed out.
//ADC->ADC_MR |=0x0000ff00; //slow down the adc clock so we don't interrupt so often . this divide sets it very slow

ADC->ADC_MR |= ADC_MR_PRESCAL(1); // ADC clock = mclk / ((prescal+1)*2) mclk is 84MHZ

ADC->ADC_CHDR=0xFFFFFFFF; // disable all channels

ADC->ADC_CHER=0x3cff; // use channels A0 to A11 //
ADC->ADC_SEQR1=0x01234567; // use A0 to A7 in order into array - writing is inverted and beginning by last
ADC->ADC_SEQR2=0x00dcba00; //use A8 to A11 following in order into array

ADC->ADC_MR |=0x80000000; //USEQ bit set, saying use the sequence

//Interrupt settings
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

//DMA settings
// "receive buffer address"
ADC->ADC_RPR=(uint32_t)DAQ_Array; // Assign buffer to DMA
// "receive count"
ADC->ADC_RCR=NUM_CHANNELS; // Set length of the buffer
// "next-buffer address"
ADC->ADC_RNPR=(uint32_t)DAQ_Array; // Assign next pointer DMA buffer
// and "next count"
ADC->ADC_RNCR=NUM_CHANNELS; // Set length of the next pointer buffer
// "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)DAQ_Array; // Assign next pointer DMA buffer
// set up the "next count"
ADC->ADC_RNCR=NUM_CHANNELS; // Set length of the next pointer buffer
DAQ_cnt++;
}
}

void setup(){
MySerial.begin(9600);
setup_AtoD();
}

void loop() {
for(int i=0; i < NUM_CHANNELS; i++){
MySerial.print(DAQ_Array&0xFFF);
MySerial.print("_");
}

MySerial.println(DAQ_cnt-DAQ_cnt_old); //DAQ Frefuency per sec
DAQ_cnt_old=DAQ_cnt;
delay(1000);
}

A few thoughts about this code:

1/ Post your code between code tags

2/ Don't use magic numbers, instaed use constants that you can find in header files
https://android.googlesource.com/platform/external/arduino-ide/+/f876b2abdebd02acfa4ba21e607327be4f9668d4/hardware/arduino/sam/system/CMSIS/Device/ATMEL/sam3xa/include/component/component_adc.h

3/ You need at least 2 buffers. While one is used by SerialUSB, the other one is filled by the PDC DMA with ADC conversions. See example sketches in the DUE sub forum for ADC conversions with a PDC DMA.

4/ Instaed of selecting free running mode for ADC conversions, select a precise sampling rate with a Timer Counter (see example sketches in the DUE sub forum for ADC sampling)

5/ Use a volatile variable , e.g. FLAG, in ADC_Handler() and loop() to inform the loop() that a buffer is ready to be output by SerialUSB.

6/ ADC_SQR is a bit tricky. See example sketches in the DUE sub forum to use this register.

7/Don't cross-post