Close

Docs: How the variant system works on Arduino Zero boards

A project log for HACK

HackAday Cortex Kit

michele-perlaMichele Perla 07/07/2016 at 20:487 Comments

Hey there,

long time no see.

Albert from avdweb is working on a SAM D21 board, and he wrote me an email asking me for some help; he acknoweldged that the Arduino Zero has way less broken out pins on the three headers than the SAM D21 G actually has, but he does not know how to use them on his board that has all the pins broken out, and he also cannot find where those are defined in the variant files. He actually asked me what happened to pins:

pin19 PB10

pin20 PB11

pin21 PA12

pin22 PA13

pin37 PB22

pin38 PB23

pin39 PA27

pin41 PA28

pin45 PA30

pin46 PA31

pin48 PB3

This is my answer to him:

you can find the base stuff inside variant.h, variant.cpp under your variant folder, and under WVariant.h in the core folder.

The WVariant.h is the file that defines all the bits that you have to put in the control registers of the SAMD1 in order to configure all the functions described in Chapter 6 of the Datasheet; I suppose you already have a copy of the datasheet but here's the link anyway, just in case:

http://www.atmel.com/images/atmel-42181-sam-d21_datasheet.pdf

Values for the bigger SAMD21J (I'm sure they're working on some kind of Arduino Zero Mega) are already defined, and my version also contains the ones for the smaller D21 E.

The variant.h and the variant.cpp use the values defined in WVariant.h; there's a description array called PinDescription g_APinDescription[] in variant.cpp that contains all the control values for all pins. These values are represented with a struct PinDescription, declared in WVariant.h, that is as follows:

/* Types used for the table below */

typedef struct _PinDescription

{

  EPortType       ulPort ;

  uint32_t        ulPin ;

  EPioType        ulPinType ;

  uint32_t        ulPinAttribute ;

  EAnalogChannel  ulADCChannelNumber ; /* ADC Channel number in the SAM device */

  EPWMChannel     ulPWMChannel ;

  ETCChannel      ulTCChannel ;

  EExt_Interrupts ulExtInt ;

} PinDescription ;

/* Pins table to be instantiated into variant.cpp */

extern const PinDescription g_APinDescription[] ;

As you can see, the struct contains values that almost match the heading of Table 6.1 of the datasheet:

EPortType ulPort corresponds to the first two letters of the value at column I/O PIN in table 6.1

ulPin corresponds to the last two numbers of the value at column I/O PIN in table 6.1

e.g.: PORTA, 21 corresponds to pin 30 of the SAM D21G.

To understand the other values, just look at the declarations of the types and structs in WVariant.h.

Position i of g_APinDescriptor[] corresponds to pin i of the Arduino Zero or of your board.

Let's check Arduino Zero's first lines of this array and comment some pins:

{

  // 0..13 - Digital pins

  // ----------------------

  // 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,  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, 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 },

As you can see, the first two pins (corresponding to pin 16 and 15 of the D21G) have everything disabled but the SERCOM module, which can be used for SPI, I2C (not for all pins though), USART, etc.

In this case, those are set for Serial communication.

This SERCOM modules are set using values inside the SERCOM.* files. To fix the values at startup, you edit the defines in variant.h.

Pin 2 is set as a normal digital pin, nothing else.

Pin 3 is set a timer by default, it means it can use either as a normal timer output or as a PWM output; it is indeed associated with PWM channel PWM0_CH1; please note that again in WVariant.h you can see that PWMx_CHy corresponds to TCC or TC channels in this order. The basic difference between using a Timer Capture module or a Timer Capture for Control applications module is that the latter has more features, it's more precise (see chapters 29 and 30 intros).

After all the real pins are defined in the array, extra pins are defined;

e.g.:

  { PORTA, 24, PIO_COM, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, // USB/DM

  { PORTA, 25, PIO_COM, PIN_ATTR_NONE, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE }, // USB/DP

are the USB differential data lines pins; if you do not want to use USB on your project, you might reuse these as SERCOM pads or as PWM Channels.

You could also add other rows to the array to have presets of alternate pin functions that you may want to enable when the code is running, like the DAC function:

// 43 - Alternate use of A0 (DAC output)

  { PORTA,  2, PIO_ANALOG, PIN_ATTR_ANALOG, DAC_Channel0, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_2 }, // DAC/VOUT

Now, after this explanation, to come back to your question, the pins you reported are NOT unused on the Zero, they are in fact used as follow:

pin19 PB10 - SPI MOSI (broken out on the ICSP header)

pin20 PB11 - SPI MISO (broken out on the ICSP header)

pin21 PA12 - SPI SCK (broken out on the ICSP header)

pin22 PA13 - GPIO used with the EDBG chip on the board

pin37 PB22 - UART TX used with the EDBG chip

pin38 PB23 - UART RX used with the EDBG chip

pin39 PA27 - TX on board LED (output only)

pin41 PA28 - USB Host Enable pin

pin45 PA30 - SWCLK debug clock line used with the EDBG chip

pin46 PA31 - SWD debug data line used with the EDBG chip

pin48 PB3 - RX on board LED (output only)

Of course, if you do not want all that stuff, you can map those pins to whatever function you may prefer, as briefly described above.

I really hope the information reported here will be useful to you, or to anybody else, since this covers most of the stuff that I've never covered on my project log, therefore I'll post most of this on my project log.

I also thank you for giving me an excuse to go back through this project (I haven't done anything in the last two months on this) in order to fill some of the important documentation stuff that it is missing.

Cheers,

Michele

Discussions

Modmatic wrote 01/12/2020 at 05:15 point

Hi there, thanks for this. One thing that doesn't quite make sense is that I see many examples of using the DAC but via A0, rather than the alternate pin (43, per the code you posted). How do these examples work properly? I would think that the actual pin definition would need to have the proper configuration (despite the fact that the two definitions point to the same physical pin PA02). Does it render the duplicate definition essentially useless?

  Are you sure? yes | no

maxholgasson wrote 07/11/2019 at 17:57 point

Hi,

thanks for that nice article!

One further question I'm asking myself:

in variant.h it's written

#define PIN_LED_TXL          (34u)

and e.g.

#define PIN_A0               (24ul)

What does that "u" behind the number 34 stands for and what the "ul" behind 24?

  Are you sure? yes | no

Modmatic wrote 01/12/2020 at 05:17 point

"ul" = unsigned long

"u" = unsigned

It's simply a way of writing a literal, but have it be a special type of integer, rather than the default (likely signed int).

  Are you sure? yes | no

maxholgasson wrote 01/14/2020 at 01:16 point

Thanks but why don't thex simply write 24? Why do you need those u or ul?

  Are you sure? yes | no

Modmatic wrote 01/14/2020 at 01:46 point

If you just write 24 without the "u" or "ul", it will be interpreted as a signed integer. On AVR Arduinos (like the UNO), that would be a 16-bit value that can be negative or positive. The "ul" tells it to be interpreted as a 32-bit non-negative number.

As to why pin numbers should be 32-bit non-negative numbers, I'm not sure, but that's what the "u" and "ul" do.

Usually, they're needed so that math is done correctly. For instance, if you do the following on an Arduino UNO:

unsigned long x = 32767 + 1

you'd get an unexpected result (it may roll over to −32768). This is because the default value type, signed integer, has a max value of 32767 and can't correctly store anything more than that value.

But the following would give the expected result of 32768:

unsigned long x = 32767ul + 1ul

(I forget the details but leaving the "ul" off the 1 would still probably work since at lease one operand is an unsigned long).

Hope that helps clarify!

  Are you sure? yes | no

maxholgasson wrote 01/15/2020 at 11:42 point

Thanks for your explanation!

  Are you sure? yes | no

Ken Yap wrote 01/15/2020 at 11:48 point

You need to read up on C types and implied conversions.

  Are you sure? yes | no