Some variant.cpp files have more than one entry that points to the same physical pin. For example (from Arduino Zero's file):

// 14..19 - Analog pins
// --------------------


// 43..45 - Alternate use of A0 (DAC output), 44 SWCLK, 45, SWDIO

The first entry sets up the pin as a regular analog input pin on A0. The second entry sets it up as a DAC on pin IDE pin 43.

The corresponding variant.h file defines the DAC pin and A0 as entry 14, rather than 43. I also have seen plenty of examples online that use the DAC via A0 when working with an Arduino Zero.

 * 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_DAC0             (14ul)

Finally, I see other variants, like this one that don't even bother to create a DAC-specific entry. So presumably it doesn't actually matter? If not, then why is it in the Arduino Zero variant.cpp file at all?

A related question: How does using a pin for multiple purposes work? For instance, if I had a pin I've configured for SPI MOSI (in variant.cpp), and I wanted to also use it for PWM (given the datasheet says the pin is on a SERCOM and a timer), do I actually need two entries in variant.cpp and thus two different IDE pins to use in my sketches (one that has a pin type of PIO_SERCOM and another with PIO_PWM)? I can think of plenty of times working with an Arduino UNO, for instance, where I've just used the same IDE pin number for general IO as well as SPI (MOSI).


1 Answer 1


The analogWrite function checks the PIN_ATTR_ANALOG attribute from pin description in variant.cpp to determine if the pin has DAC capability. Then it checks if the pin is ADC_Channel0 or DAC_Channel0. This is unnecessary and redundant. There is no other use for DAC_Channel0.

In Arduino SAMD core and derived cores the pin 'map' in variant.cpp defines pin capabilities used by Arduino core. It is not a complete copy of the mux table from the datasheet. In some variant.cpp files comments contain the additional mux options of the pin.

For PWM the analogWrite function configures the pin and for SPI, the SPI library configures the pin.

If you want to use the pin for other SERCOM function, the function pinPeripheral is used to set up the pin. Here is a tutorial.

Arduino core doesn't support a configuration of a different wave output then analogWrite. To configure a different wave output or capture, a third party library or direct registers configuration is necessary.

The Arduino pinPeripheral(pin, peripheral) function uses the variant.cpp mapping and the possible parameters are:

PIO_EXTINT=0,     /* The pin is controlled by the associated signal of peripheral A. */
PIO_ANALOG,       /* The pin is controlled by the associated signal of peripheral B. */
PIO_SERCOM,       /* The pin is controlled by the associated signal of peripheral C. */
PIO_SERCOM_ALT,   /* The pin is controlled by the associated signal of peripheral D. */
PIO_TIMER,        /* The pin is controlled by the associated signal of peripheral E. */
PIO_TIMER_ALT,    /* The pin is controlled by the associated signal of peripheral F. */
PIO_COM,          /* The pin is controlled by the associated signal of peripheral G. */
PIO_AC_CLK,       /* The pin is controlled by the associated signal of peripheral H. */
PIO_DIGITAL,      /* The pin is controlled by PORT. */

If you are good with the Arduino variant.cpp configuration, then for direct registers access configuration the pin 'map' from variant.cpp and the pinPeripheral function can be at help if the pin should not be hardcoded. Example from peripherals setup for 'phase cutting' with a Triac in my project:

const PinDescription& pinDesc = g_APinDescription[triacPin]; // Arduino pin description
TCC = (Tcc*) GetTC(pinDesc.ulPWMChannel);
uint8_t tcChannel = GetTCChannelNumber(pinDesc.ulPWMChannel);
bool periF = (pinDesc.ulPinAttribute & PIN_ATTR_TIMER_ALT);

// setup the pin as TCC wave out pin
pinPeripheral(triacPin, periF ? PIO_TIMER_ALT : PIO_TIMER);

TCC->CC[tcChannel].reg = PULSE_PERIOD;
  • Thanks for your response. It doesn’t quite answer the question though about how a variant could get away with not specifying the DAC alternative for A0 (PA02). For instance, the A0 entry doesn’t even specify the DAC channel as the alternate entry does. If A0 were passed to some function that ultimately looks at the channel configuration, it wouldn’t have the right value.
    – Andrew M.
    Commented Jan 12, 2020 at 13:15
  • sorry, I missed the ADC_Channel0/DAC_Channel0 difference. I enhanced the answer with a new first paragraph
    – Juraj
    Commented Jan 12, 2020 at 16:23
  • Thank you! A small side question: What is the consequence of having two different pins on the same timer and channel? For instance, on the SAMD21E, both PA06 and PA30 (SWCLK) have TCC1/WO[0] as the only timer option in the SERCOM mux table.
    – Andrew M.
    Commented Jan 12, 2020 at 21:11
  • 1
    SERCOM is SERialCOMunication so UART, SPI, I2C. you can't set two different pins on the same timer and channel. you can map them on the same timer and channel, but then they can't be used at the same time for PWM
    – Juraj
    Commented Jan 13, 2020 at 6:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.