Arduino Due ADC channel sequence

1 post / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Some help for those working with the sequence registers with the DUE ADC: 

 

The ADC in the DUE is connected so that the Arduino pins A0,A1,A2,A3 correspond to the AVR ADC channels ch7, ch6, ch5, and ch4, respectively.  The converter can be programmed to acquire a sequence of channels with one trigger. Instructions for configuring the acquisition are given in section 43 of the SAM3x data sheet, but are rather confusing, in my opinion.

To use the registers that determine the sequence of channels that are acquired, follow these steps:

 

  1. How many acquisitions do you want in your sequence? You must enable this many channels in the ADC_CHER. (channel enable register) 
  2. Which channels do you want in your sequence?  These channels must be included in the ones that you enable in ADC_CHER.

EXAMPLE:  Want to have a sequence with four acquisitions.  The channels to be acquired are only two, channel 7 and channel 6. (arduino A0 and A1) 

SOLUTIONS:  four bits must be set in ADC_CHER.  Of these 4, two must be channel 7 and channel 6, and the other two are arbitrary.

            example SOL1:  CHER set up for    ch0,ch1 ch6,ch7       CHER= 0x00C3

            example SOL2:  CHER  set up for   ch1, ch6, ch7, ch12  CHER= 0x10C2

 

Now, write the channels that you want acquired into the sequence register positions for the channels that you have enabled. Work in numerical order from the lowest numbered acquisition channel to the highest numbered acquisition channel.  Note that the labels in the sequence register documentation are numbered from 1 instead of zero.  This is simply a confusion (or error) in the documentation. (that is, for example, USCH1 corresponds to the adc channel #0....USCH10 corresponds to the adc channel #9) 

EXAMPLE:  You want to acquire 7,7,6,6 with every trigger

                  USING SOL1 (ch0,ch1,ch6,ch7)

                                  SEQR1= (7<<0) | (7<<4) |(6<<24) |(6<<28); //=0x66000077

                            USING SOL2 (ch1,ch6,ch7,ch12)

                                SEQR1= (7<<4) | (7<<24) |(6<<28);  //=0x67000070

                                SEQR2=(6<<16);                                  // =0x60000

EXAMPLE2:  You want to acquire 6,7,6,7 with every trigger 

                  USING SOL1 (ch0,ch1,ch6,ch7)

                                SEQR1= (6<<0) | (7<<4) |(6<<24) |(7<<28);//=0x76000076

                  USING SOL2 (ch1,ch6,ch7,ch12)

                               SEQR1= (6<<4) | (7<<24) |(6<<28);  //=0x67000060

                               SEQR2=(7<<16);                                   //=0x70000

 

   I have attached code that acquires A1,A0,A1,A0.... like the  SOL1, example 2 above.  The ADC runs in the background, always overwriting a length 4 data array.  (Note that in this code, the ADC is clock is slowed down below the recommended speed.)  

 

 

 


 
int const NUM_CHANNELS=4;
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=0x00C3;       //   use channels 0,1, 6 and 7
  ADC->ADC_MR |=0x80000000;   //USEQ bit set, saying use the sequence
  ADC->ADC_SEQR1=0x76000076;  // use0->6, use1->7, use6->6, use7->7 
  ADC->ADC_SEQR2=0x0000;
  
  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 4
   
 }

}