Additional ADC input

I'm playing around with this board, I would like to have an additional ADC on D9 (PA07).

My question is similar to: link. However, I'm not sure which lines to add/edit or remove in variant.cpp.

Is there a way to realize this without modifying these files? I would also like to use the standard ADC inputs (A0-A3).

For additional ADC A4 on D9 (pin9) modify these files:

variant.cpp (C:\Users\name\AppData\Local\Arduino15\packages\SparkFun\hardware\samd\1.7.5\variants\SparkFun_SAMD_Mini)

//Digital high
{ PORTA,  7, PIO_TIMER, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM|PIN_ATTR_TIMER|PIN_ATTR_ANALOG), ADC_Channel7, PWM1_CH1, TCC1_CH1, EXTERNAL_INT_7 }, // TCC1/WO[1]  
//Analog Pins
{ PORTA,  7, PIO_ANALOG, PIN_ATTR_ANALOG, ADC_Channel7, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_7 }, // ADC/AIN[7]

variant.h (C:\Users\name\AppData\Local\Arduino15\packages\SparkFun\hardware\samd\1.7.5\variants\SparkFun_SAMD_Mini):

 #define PIN_A4               (9ul)

wiring_analog.c (C:\Users\name\AppData\Local\Arduino15\packages\SparkFun\hardware\samd\1.7.5\cores\arduino):

if (pin == 9) pin = PIN_A4;
else
{
if (pin < A0) {
  pin += A0;
}
}

Still it would be nice to know if there is a way to achieve the same buth without touching these files.

Hi LeCrAm,

Unfortunately, the analogRead() function doesn't work because digital pin D9 is listed as "No_ADC_Channel" in the "variants.cpp" file and analogRead() uses this entry to reference the correct pin.

Here's how to read 12-bit analog values from digital pin D9 using register manipulation:

// Set-up the ADC to read analog input on A7 (D9) with 12-bit resolution
void setup() {
  Serial.begin(115200);                             // Set-up the native serial port at 115200 bit/s
  while(!SerialUSB);
  ADC->CTRLB.reg = ADC_CTRLB_PRESCALER_DIV512 |    // Divide Clock ADC GCLK by 512 (48MHz/512 = 93.7kHz)
                   ADC_CTRLB_RESSEL_12BIT;         // Set ADC resolution to 12 bits
  while(ADC->STATUS.bit.SYNCBUSY);                 // Wait for synchronization
  ADC->SAMPCTRL.reg = 0x00;                        // Set max Sampling Time Length to half divided ADC clock pulse (5.33us)
  ADC->INPUTCTRL.bit.MUXPOS = ADC_INPUTCTRL_MUXNEG_PIN7_Val;  // Set the analog input to A7
  while(ADC->STATUS.bit.SYNCBUSY);                 // Wait for synchronization
  ADC->CTRLA.bit.ENABLE = 1;                       // Enable the ADC
  while(ADC->STATUS.bit.SYNCBUSY);                 // Wait for synchronization
}

void loop() {
  ADC->SWTRIG.bit.START = 1;                       // Initiate a software trigger to start an ADC conversion
  while(ADC->STATUS.bit.SYNCBUSY);                 // Wait for write synchronization
  while(!ADC->INTFLAG.bit.RESRDY);                 // Wait for the conversion to complete
  ADC->INTFLAG.bit.RESRDY = 1;                     // Clear the result ready (RESRDY) interrupt flag
  while(ADC->STATUS.bit.SYNCBUSY);                 // Wait for read synchronization
  int result = ADC->RESULT.reg;                    // Read the ADC result
  SerialUSB.println(result);                       // Output the result
  delay(500);                                      // Wait for 500ms
}

Thanks Martin,

What should I do when I would like to read 3 channels (A1, A2 and D9)? I'm thinking in this direction, but the readings are wrong:

void ADC_Handler()
{
  static uint16_t counter = 0;                       // Results counter
 
  if (ADC->INTFLAG.bit.RESRDY)                       // Check if the result ready (RESRDY) flag has been set
  {
    ADC->INTFLAG.bit.RESRDY = 1;                     // Clear the RESRDY flag
    while(ADC->STATUS.bit.SYNCBUSY);                 // Wait for read synchronization

    if(counter == 0) adc_val_a = ADC->RESULT.reg;
    if(counter == 1) adc_val_b = ADC->RESULT.reg;
    if(counter == 2) adc_val_c = ADC->RESULT.reg;

    //Change MUX to next input
    ADC->INPUTCTRL.bit.MUXPOS = inputCtrl[counter+1];
    while(ADC->STATUS.bit.SYNCBUSY);                  // Wait for synchronization
  
    if(counter == ADC_CHANNELS-1)                     // Once the required number of samples have been made
    {
      ADC->INPUTCTRL.bit.MUXPOS = inputCtrl[0];       // Prepare for next cycle
      while(ADC->STATUS.bit.SYNCBUSY);                // Wait for synchronization      
      ADC->CTRLA.bit.ENABLE = 0;                      // Disable the ADC
      while(ADC->STATUS.bit.SYNCBUSY);                // Wait for synchronization
      counter = 0;                                    // Reset the counter
      resultsReady = true;                            // Set the resultsReady flag
    }
    else
      counter++;    
  }
}

The ADC will be started each 100us, it seems to work OK, except for the readings, I think it has somehing to do with the MUX registers (MUXPOS and MUXNEG), I'm not sure how these are related to the right ADC pins.

volatile uint32_t inputCtrl[ADC_CHANNELS] = {?, ?, ?};

Ok, found out myself, just don't use the freerun, since it seems you are not allowed to change mux setting during freerun mode. I simply start adc conversion and in the adc handler I change mux register to next channel and retrigger a adc read, if all 3 channels are read, I disable the adc. This takes around 70us in total.

void ADC_Handler()
{
  static uint16_t counter = 0;                       // Results counter
 
  if (ADC->INTFLAG.bit.RESRDY)                       // Check if the result ready (RESRDY) flag has been set
  {
    ADC->INTFLAG.bit.RESRDY = 1;                     // Clear the RESRDY flag
    while(ADC->STATUS.bit.SYNCBUSY);                 // Wait for read synchronization

    if(counter == 0) adc_val_a= ADC->RESULT.reg;
    if(counter == 1) adc_val_b = ADC->RESULT.reg;
    if(counter == 2) adc_val_c = ADC->RESULT.reg;
 
    if(counter < ADC_CHANNELS-1)                        // Once the required number of samples have been made
    {      
      counter++;
      //Change MUX to next input
      ADC->INPUTCTRL.bit.MUXPOS = inputCtrl[counter];
      while(ADC->STATUS.bit.SYNCBUSY);                  // Wait for synchronization
      
      ADC->SWTRIG.bit.START = 1;                         // Initiate a software trigger to start an ADC conversion
      while(ADC->STATUS.bit.SYNCBUSY);                   // Wait for synchronization    
    }
    else
    {
      ADC->INPUTCTRL.bit.MUXPOS = inputCtrl[0];       // Prepare for next cycle
      while(ADC->STATUS.bit.SYNCBUSY);                // Wait for synchronization 
           
      ADC->CTRLA.bit.ENABLE = 0;                      // Disable the ADC
      while(ADC->STATUS.bit.SYNCBUSY);                // Wait for synchronization
      
      counter = 0;                                    // Reset the counter
      resultsReady = true;                            // Set the resultsReady flag
    }
  }
}