Arduino Zero additional analog inputs?

According to the SAMD21 datasheet, several of the pins we use as "digital" have ADCs:

D8 / AIN6
D9 / AIN7
D4 / AIN16
D3 / AIN17
D1 TXD / AIN18
D0 RXD / AIN19

As well as the following pins we use for system / utility functions:

AREF / AIN1
RX_LED / AIN11

The datasheet goes on to mention we can use all 14 of these at once if desired. I understand that only 6 of these pins have been set up as analog inputs to maintain compatibility with older shields and software, but what is the procedure for adding analog input support for these pins? Is it as simple as editing variant.h and variant.cpp? How about reclaiming the TX and RX LED pins?

Solved my own problem. Adafruit just released a new board with the SAMD21, and their version has support for analog inputs on D8 and D9.

Here's how you do it:

Make a copy of your core files. These are in the Arduino15 folder, which is in different places depending on your OS. In Mac OS X, it's in ~/Library. Copy the /Arduino15/packages/arduino/hardware/samd/1.6.2 folder and stick it somewhere safe in case you want to change back.

Navigate to the /cores/arduino subfolder and find wiring_analog.c. Change the following lines:

uint32_t analogRead( uint32_t ulPin )
{
  uint32_t valueRead = 0;

  if ( ulPin < A0 )
  {
    ulPin += A0 ;
  }

to:

uint32_t analogRead( uint32_t ulPin )
{
  uint32_t valueRead = 0;

  if ( ulPin <= 5 ) // turn '0' -> 'A0'
  {
    ulPin += A0 ;
  }
  if (ulPin == 6) ulPin = PIN_A6;
  if (ulPin == 7) ulPin = PIN_A7;

Next, navigate to the /variants folder, it's back under the 1.6.2 subfolder. Find variant.cpp and variant.h.

In variant.cpp, change:

 { PORTA,  6, PIO_TIMER, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM|PIN_ATTR_TIMER), No_ADC_Channel, PWM1_CH0, TCC1_CH0, EXTERNAL_INT_6 }, // TCC1/WO[0]
  { PORTA,  7, PIO_TIMER, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM|PIN_ATTR_TIMER), No_ADC_Channel, PWM1_CH1, TCC1_CH1, EXTERNAL_INT_7 }, // TCC1/WO[1]

to:

  { PORTA,  6, PIO_TIMER, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM|PIN_ATTR_TIMER|PIN_ATTR_ANALOG), ADC_Channel6, PWM1_CH0, TCC1_CH0, EXTERNAL_INT_6 }, // TCC1/WO[0]
  { 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]

in variant.h, change:

#define NUM_ANALOG_INPUTS    (6u)

to:

#define NUM_ANALOG_INPUTS    (8u)

and change:

/*
 * Analog pins
 */
#define PIN_A0               (14ul)
#define PIN_A1               (15ul)
#define PIN_A2               (16ul)
#define PIN_A3               (17ul)
#define PIN_A4               (18ul)
#define PIN_A5               (19ul)

static const uint8_t A0  = PIN_A0 ;
static const uint8_t A1  = PIN_A1 ;
static const uint8_t A2  = PIN_A2 ;
static const uint8_t A3  = PIN_A3 ;
static const uint8_t A4  = PIN_A4 ;
static const uint8_t A5  = PIN_A5 ;

to:

/*
 * Analog pins
 */
#define PIN_A0               (14ul)
#define PIN_A1               (15ul)
#define PIN_A2               (16ul)
#define PIN_A3               (17ul)
#define PIN_A4               (18ul)
#define PIN_A5               (19ul)
#define PIN_A6               (8ul)
#define PIN_A7               (9ul)

static const uint8_t A0  = PIN_A0 ;
static const uint8_t A1  = PIN_A1 ;
static const uint8_t A2  = PIN_A2 ;
static const uint8_t A3  = PIN_A3 ;
static const uint8_t A4  = PIN_A4 ;
static const uint8_t A5  = PIN_A5 ;
static const uint8_t A6  = PIN_A6 ;
static const uint8_t A7  = PIN_A7 ;

What does it all mean? In wiring_analog.c, we're telling the IDE to interpret calls to the analogRead() function correctly. If you tell it analogRead(0), it's supposed to know that you want to read from pin A0, or digital 14. You can also tell the IDE to analogRead(A0) and it still knows what you're talking about. In this case, we're telling the IDE that we have two new analog pins, A6 and A7, aka D8 and D9.

In variant.cpp, we are assigning the relevant ADC channels and telling the IDE that we're allowed to use D8 and D9 as analog inputs. In variant.h, we are increasing the number of available analog pins to 8 (that's really important!) and also making aliases for A6 and A7 so the IDE knows what they are.

The only downside to this change is that I've lost PWM output on D8 and D9 but that's not important to me right now. If anybody has some pointers about that, I'm all ears.

I think you should also make sure this is correct:

#define analogInputToDigitalPin(p)  ((p < 6u) ? (p) + 14u : -1)

Approximately line 63 of variant.h

I'm working on a similar issue, but trying to add one more analog input beside Adafruit's two additional.

Hi, I need more analog pins. I'm using Sparkfun Dev board. I have made D8 and D9 into A6 and A7 respectively, using the method described above. I still need a few more analog pins. I tried to do the same thing with D3 and D4 to turn them into A8 but neither work. They return a fairly consistent number, but not one associated with the voltage at the pin. with 3.3V/2 , ie 1.65V, on pin 3 it returns 170, and on pin 4 I get 800.

See snippets I changed in variant.h, .cpp and wiring_analog.c. trying to make D3 into A8. Sketch is at the bottom. Can anyone suggest a fix?

variant.cpp

// 0/1 - SERCOM/UART (Serial1)
  { PORTA, 11, PIO_SERCOM, (PIN_ATTR_DIGITAL), No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_11 }, // RX: SERCOM0/PAD[3]
  { PORTA, 10, PIO_SERCOM, (PIN_ATTR_DIGITAL), No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_10 }, // TX: SERCOM0/PAD[2]

  // 2..12
  // Digital Low
  { PORTA, 14, PIO_DIGITAL, (PIN_ATTR_DIGITAL), No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_14 },
//{ PORTA,  9, PIO_TIMER, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM|PIN_ATTR_TIMER), No_ADC_Channel, PWM0_CH1, TCC0_CH1, EXTERNAL_INT_9 }, // TCC0/WO[1]
  { PORTA,  9, PIO_TIMER, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM|PIN_ATTR_TIMER|PIN_ATTR_ANALOG), ADC_Channel17, PWM0_CH1, TCC0_CH1, EXTERNAL_INT_9 }, // TCC0/WO[1]
  { PORTA,  8, PIO_TIMER, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM|PIN_ATTR_TIMER), No_ADC_Channel, PWM0_CH0, TCC0_CH0, EXTERNAL_INT_NMI },  // TCC0/WO[0]
//{ PORTA,  8, PIO_TIMER, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM|PIN_ATTR_TIMER|PIN_ATTR_ANALOG), ADC_Channel16, PWM0_CH0, TCC0_CH0, EXTERNAL_INT_NMI },  // TCC0/WO[0]
  { PORTA, 15, PIO_TIMER, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM|PIN_ATTR_TIMER), No_ADC_Channel, PWM3_CH1, TC3_CH1, EXTERNAL_INT_15 }, // TC3/WO[1]
  { PORTA, 20, PIO_TIMER_ALT, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM|PIN_ATTR_TIMER_ALT), No_ADC_Channel, PWM0_CH6, TCC0_CH6, EXTERNAL_INT_4 }, // TCC0/WO[6]
  { PORTA, 21, PIO_DIGITAL, (PIN_ATTR_DIGITAL), No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_5 },

  // Digital High
 // { PORTA,  6, PIO_TIMER, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM|PIN_ATTR_TIMER), No_ADC_Channel, PWM1_CH0, TCC1_CH0, EXTERNAL_INT_6 }, // TCC1/WO[0]
 // { PORTA,  7, PIO_TIMER, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM|PIN_ATTR_TIMER), No_ADC_Channel, PWM1_CH1, TCC1_CH1, EXTERNAL_INT_7 }, // TCC1/WO[1]
 { PORTA,  6, PIO_TIMER, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM|PIN_ATTR_TIMER|PIN_ATTR_ANALOG), ADC_Channel6, PWM1_CH0, TCC1_CH0, EXTERNAL_INT_6 }, // TCC1/WO[0]
  { 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]

variant.h

/ Number of pins defined in PinDescription array
#define PINS_COUNT           (26u)
#define NUM_DIGITAL_PINS     (20u)
#define NUM_ANALOG_INPUTS    (9u)
#define NUM_ANALOG_OUTPUTS   (1u)
#define analogInputToDigitalPin(p)  ((p < 6u) ? (p) + 14u : -1)

#define PIN_A0               (14ul)
#define PIN_A1               (15ul)
#define PIN_A2               (16ul)
#define PIN_A3               (17ul)
#define PIN_A4               (18ul)
#define PIN_A5               (19ul)
#define PIN_A6               (8ul)
#define PIN_A7               (9ul)
#define PIN_A8               (3ul)
#define PIN_DAC0             (14ul)

static const uint8_t A0  = PIN_A0;
static const uint8_t A1  = PIN_A1;
static const uint8_t A2  = PIN_A2;
static const uint8_t A3  = PIN_A3;
static const uint8_t A4  = PIN_A4;
static const uint8_t A5  = PIN_A5;
static const uint8_t A6  = PIN_A6 ;
static const uint8_t A7  = PIN_A7 ;
static const uint8_t A8  = PIN_A8 ;
static const uint8_t DAC0 = PIN_DAC0;

wiring_analog.c

uint32_t analogRead(uint32_t pin)
{
  uint32_t valueRead = 0;

/*  if (pin < A0) {
    pin += A0;
  }*/
 
 if ( pin <= 5 ) // turn '0' -> 'A0'
  {
    pin += A0 ;
  }
  if (pin == 6) pin = PIN_A6;
  if (pin == 7) pin = PIN_A7;
  if (pin == 8) pin = PIN_A8;

sketch

void setup() {
  SerialUSB.begin(9600);
  while(!SerialUSB);
}

void loop() {
  SerialUSB.println(analogRead(A8));
  delay(500);
}

I think I need to multiplex my brain! The code in wiring_analog.c was causing the problem by changing the value of pin. I got rid of it all together and reference the analog pins as A0-A9 ie analogRead(A9);

uint32_t analogRead(uint32_t pin)
{
 uint32_t valueRead = 0;

/*  if (pin < A0) {
   pin += A0;
*/

}

To make pins D8, D9, D3 and D4 into A6, A7, A8, A9 respectively, apart from the above mentioned change to wiring_analog.c, I made the following code changes:

variant.cpp:

 // 2..12
  // Digital Low
  { PORTA, 14, PIO_DIGITAL, (PIN_ATTR_DIGITAL), No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_14 },
//{ PORTA,  9, PIO_TIMER, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM|PIN_ATTR_TIMER), No_ADC_Channel, PWM0_CH1, TCC0_CH1, EXTERNAL_INT_9 }, // TCC0/WO[1]
  { PORTA,  9, PIO_ANALOG, (PIN_ATTR_PWM|PIN_ATTR_TIMER), ADC_Channel17, PWM0_CH1, TCC0_CH1, EXTERNAL_INT_9 }, // TCC0/WO[1]
//{ PORTA,  8, PIO_TIMER, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM|PIN_ATTR_TIMER), No_ADC_Channel, PWM0_CH0, TCC0_CH0, EXTERNAL_INT_NMI },  // TCC0/WO[0]
  { PORTA,  8, PIO_ANALOG, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM|PIN_ATTR_TIMER), ADC_Channel16, PWM0_CH0, TCC0_CH0, EXTERNAL_INT_NMI },  // TCC0/WO[0]
  { PORTA, 15, PIO_TIMER, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM|PIN_ATTR_TIMER), No_ADC_Channel, PWM3_CH1, TC3_CH1, EXTERNAL_INT_15 }, // TC3/WO[1]
  { PORTA, 20, PIO_TIMER_ALT, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM|PIN_ATTR_TIMER_ALT), No_ADC_Channel, PWM0_CH6, TCC0_CH6, EXTERNAL_INT_4 }, // TCC0/WO[6]
  { PORTA, 21, PIO_DIGITAL, (PIN_ATTR_DIGITAL), No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_5 },

  // Digital High
//{ PORTA,  6, PIO_TIMER, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM|PIN_ATTR_TIMER), No_ADC_Channel, PWM1_CH0, TCC1_CH0, EXTERNAL_INT_6 }, // TCC1/WO[0]
 // { PORTA,  7, PIO_TIMER, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM|PIN_ATTR_TIMER), No_ADC_Channel, PWM1_CH1, TCC1_CH1, EXTERNAL_INT_7 }, // TCC1/WO[1]//
  { PORTA,  6, PIO_ANALOG, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM|PIN_ATTR_TIMER), ADC_Channel6, PWM1_CH0, TCC1_CH0, EXTERNAL_INT_6 }, // TCC1/WO[0]
  { PORTA,  7, PIO_ANALOG, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM|PIN_ATTR_TIMER), ADC_Channel7, PWM1_CH1, TCC1_CH1, EXTERNAL_INT_7 }, // TCC1/WO[1]

variant.h:

/*
 * Analog pins
 */
#define PIN_A0               (14ul)
#define PIN_A1               (15ul)
#define PIN_A2               (16ul)
#define PIN_A3               (17ul)
#define PIN_A4               (18ul)
#define PIN_A5               (19ul)
#define PIN_A6               (8ul)
#define PIN_A7               (9ul)
#define PIN_A8               (3ul)
#define PIN_A9               (4ul)
#define PIN_DAC0             (14ul)

static const uint8_t A0  = PIN_A0;
static const uint8_t A1  = PIN_A1;
static const uint8_t A2  = PIN_A2;
static const uint8_t A3  = PIN_A3;
static const uint8_t A4  = PIN_A4;
static const uint8_t A5  = PIN_A5;
static const uint8_t A6  = PIN_A6;
static const uint8_t A7  = PIN_A7;
static const uint8_t A8  = PIN_A8;
static const uint8_t A9  = PIN_A9;
static const uint8_t DAC0 = PIN_DAC0;
#define ADC_RESOLUTION 12

Changing this line seems to make no difference and I just left it as was.

#define NUM_ANALOG_INPUTS    (6u)