Understanding variant.cpp

I am using a SAMD51-based board, and would like to assign different functions to some of the pins. I have a superficial understanding of variant.cpp and how the different function (timers, ADCs, PWMs, as laid out in the spec sheets) are assigned to Arduino pin numbers through variant.cpp file. And I've seen posts about specific problems and pins. But how do you learn to edit the variant files? Is there a tutorial, a video, a manual somewhere that explains the system from scratch? Because I surely don't know enough to start messing with things.

Can you back up a level and explain what you are trying to accomplish?
It sounds like you are NOT creating a typical app sketch. Although I know of the variant.cpp file, I have never and would never mess with it unless I was building a new board.

What board exactly? Is it a custom board you have designed? What alternative pin assignments are you looking for?

Yes, it is a new custom-built board. I have the variant file for this board's predecessor. But a few pins have been changed and I need to alter the function of those pins. As I said, I understand the principles of what the variant file is doing, but I don't know enough of the details.

For example, I'd like to change PB10, 12 and 13 to PWM DAC's. And PB09 to an ADC.

I added my own specific board to the ESP32 core, with which i mainly set specific default settings which i would otherwise have to ensure would be correct for the board i cloned.

The first step i would recommend is that you more or less clone a current board within the core and give it your preferred name. Then look at the specific variant.cpp and see what you want to change.

I am not sure what the 8 different values for each pin mean. 5 6 and 7 are the sources for ADC, PWM, and Timer. But where do you find a list of the possible values, and what do some of the other columns mean?

Well how about you first post the contents of the particular variant.cpp file (or even a link to the core repository would help already)

the only SAMD51 "core" I know of is the one from Adafruit, but they have several different variants. (So we sort-of need to know which variant you are starting from, and what your hardware actually looks like (particularly: which SAMD51 chip?")

It looks like PB12 and PB13 are already PWM outputs, and PB09 is already an analog input. (This from the Metro m4 variant):

  { PORTB,  13, PIO_DIGITAL, PIN_ATTR_PWM_F, No_ADC_Channel, TCC3_CH1, TC4_CH1, EXTERNAL_INT_13 }, // d4
 :
  { PORTB,  12, PIO_DIGITAL, PIN_ATTR_PWM_F, No_ADC_Channel, TCC3_CH0, TC4_CH0, EXTERNAL_INT_12 }, // d7
 :
  { PORTB,  9, PIO_ANALOG, (PIN_ATTR_ANALOG|PIN_ATTR_PWM_E), ADC_Channel3, TC4_CH1, TC4_CH1, EXTERNAL_INT_9 },

So you can probably make whatever pin PB10 is on (not used in the Metro m4 variant I'm looking at) look like the existing PB12, PB13 lines (although with different TCC/TC info. Note that multiple pins can be tied to a specific timer, so I hope your needs don't conflict (but having coliding definitions in the pin table shouldn't cause problems unless you actually use more than one of the conflicting pins.)

In general, the table used for the SAM pin translation isn't well organized for having multiple functions available on a single pin, but one of the interesting things adafruit has done is multiply-defined certain chip pins as multiple Arduino pins, depending on the function desired. So for example, there is PA2:

 // 14..19 - Analog pins
  // --------------------
  { PORTA,  2, PIO_ANALOG, PIN_ATTR_ANALOG, ADC_Channel0, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_2 },
  :
  // ----------------------
  // 37 - 38 Alternate use of A0 and A1 (DAC output)
  { PORTA,  2, PIO_ANALOG, PIN_ATTR_ANALOG, DAC_Channel0, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_2 }, // DAC/VOUT[0]

Here's what my variant.cpp file looks like. PB09 is the line I am concerned with. It's line 9 of the array. What I am finding is that digitalRead(9) works fine. I get 0 or 1 depending on the voltage applied to the PB09 pin. However, analogRead(9) returns 4095 no matter what the voltage is.

Maybe the problem is not the variant file, but the way I am addressing the pin in my sketch.

 { PORTB,  0, PIO_ANALOG, PIN_ATTR_ANALOG, ADC_Channel12, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE },
  { PORTB,  1, PIO_ANALOG, PIN_ATTR_ANALOG, ADC_Channel13, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE },
  { PORTA,  2, PIO_ANALOG, PIN_ATTR_ANALOG, ADC_Channel0, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_2 },
  { PORTA,  3, PIO_ANALOG, PIN_ATTR_ANALOG, No_ADC_Channel, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_3 },
  { PORTA,  4, PIO_ANALOG, PIN_ATTR_ANALOG, ADC_Channel4, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_NONE },
  { PORTA,  5, PIO_ANALOG, PIN_ATTR_ANALOG, ADC_Channel5, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_5 },
  { PORTB,  6, PIO_ANALOG, PIN_ATTR_ANALOG, ADC_Channel8, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_6 },
  { PORTB,  7, PIO_ANALOG, PIN_ATTR_ANALOG, ADC_Channel9, NOT_ON_PWM, NOT_ON_TIMER, EXTERNAL_INT_7 },
  { PORTB,  8, PIO_SERCOM_ALT, PIN_ATTR_ANALOG, ADC_Channel0, TC4_CH0, TC4_CH0, EXTERNAL_INT_8 },
  { PORTB,  9, PIO_SERCOM_ALT, PIN_ATTR_ANALOG, ADC_Channel1, TC4_CH1, TC4_CH1, EXTERNAL_INT_9 },
  { PORTA,  10, PIO_ANALOG, PIN_ATTR_ANALOG, ADC_Channel10, TCC0_CH2, TCC0_CH2, EXTERNAL_INT_10 },
  { PORTA,  11, PIO_ANALOG, PIN_ATTR_ANALOG, ADC_Channel11, TCC0_CH3, TCC0_CH3, EXTERNAL_INT_11 },

  // 12..Digital functions
  { PORTA,  12, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, TCC0_CH6, TCC0_CH6, EXTERNAL_INT_12 },
  { PORTA,  13, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, TC2_CH1, TCC0_CH7, EXTERNAL_INT_13 },  
  { PORTB,  14, PIO_DIGITAL, PIN_ATTR_PWM_E, No_ADC_Channel, TC5_CH0, TC5_CH0, EXTERNAL_INT_14 },
  { PORTB,  15, PIO_DIGITAL, PIN_ATTR_PWM_E, No_ADC_Channel, TC5_CH1, TC5_CH1, EXTERNAL_INT_15 },  
  { PORTA,  16, PIO_SERCOM, PIN_ATTR_DIGITAL, No_ADC_Channel, TCC1_CH0, TC2_CH0, EXTERNAL_INT_NONE },
  { PORTA,  17, PIO_SERCOM, PIN_ATTR_DIGITAL, No_ADC_Channel, TCC1_CH1, TC2_CH1, EXTERNAL_INT_NONE },  
  { PORTA,  18, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, TC3_CH0, TC3_CH0, EXTERNAL_INT_2 },
  { PORTA,  19, PIO_DIGITAL, PIN_ATTR_DIGITAL, No_ADC_Channel, TC3_CH1, TC3_CH1, EXTERNAL_INT_NONE },
  { PORTA,  20, PIO_TIMER, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM_F), No_ADC_Channel, TCC1_CH4, TCC0_CH0, EXTERNAL_INT_4 },

The obvious.. How are you addressing the pin in your sketch ?

I am anyway puzzled by the data in the file
there is more than 1 pin connected to ADC_Channel0 but i am not very familiar with the processor.

a = analogRead(9);

PB9 is only ADC "Channel 0" if you're using ADC1.

I think either change the channel to ADC_Channel3 or the attribute to PIN_ATTR_ANALOG_ALT

image

I tried both (one at a time), but no change. I'm not used to working with the variant file. I just made the change in a text editor, saved and then restarted Arduino. By "attribute", you mean change PIN_ATTR_ANALOG to PIN_ATTR_ANALOG_ALT, right?

There is nothing official, and I am not aware of anything created by the community either. I agree with @Deva_Rishi's recommendation is to study the code in the core variants of relevant existing platforms.

As @westfw already mentioned, the "Adafruit SAMD Boards" platform should serve as a useful reference for your project.

The important thing to understand is that the only "magic" the Arduino platform framework applies to the core variant is adding the path to the compiler's "search path", and compiling the source files under the folder. Beyond that, it is just standard C++/C code (and assembly too if you want to get crazy with it). You are free to do whatever you like in that code.

A core variant is intended to contain Arduino API implementation code specific to a subset of the platform's boards. Conversely, the core will generally contain code universal to all boards. But again, there is nothing in the platform framework that enforces such a separation of concerns, and in fact you will surely find some code in the core that is specific to a subset of boards.

Very cool! If you like, please post a link to your custom Arduino boards platform once it is publicly accessible. I'm always interested in learning of the existence of new platforms, and I'm sure other forum members will also be interested.

Although it is true that you must restart Arduino IDE (or alternatively use the Tools > Reload Board Data feature) before it will recognize changes made to the boards.txt, platform.txt, or programmers.txt files, this is not at all necessary when you make changes to the code files.

Thanks, much, ptillisch. This is very helpful and I am indeed looking at lots of examples. The one specific question I still have is what are the possible values for the third and fourth elements in the array, like PIO_DIGITAL and PIN_ATTR_DIGITAL. And what do they do? I think I understand the rest of the values, at least superficially.

I think that what you are looking for can be found in the core file WVariant.h

For the Adafruit M4 board it is at
"C:\Users\Owner\AppData\Local\Arduino15\packages\adafruit\hardware\samd\1.7.17\cores\arduino\WVariant.h"

To see how they're used, you need to dig into the particular code, which tends to use pinPeripheral(pin, PIO_ANALOG); This does the work of setting the PMUX appopiately, but it can have problems if you're setting a function that isn't described in the table.

and then there may be code in functions that deal with the peripheral: