please help understand PWM on SAMD21

odd and even pair of what ?

The port pins. The SAMD21G IO pins are logically arranged as two 32-bit ports A and B, though most of the port B pins are unused. The port pins on port A range from PA00 to PA31.

why PA21 is paired with PA20 and not PA18 ?

Because the port pins are paired as even and odd numbers starting from 0. So the first pair is PA00 and PA01, the second pair is PA02 and PA03, and so on.

I am also seeing TCC timers in E , how come?

Yes, the TC and some TCC timers are mapped to peripheral E. On some pins this allows you to multiplex between different timers on the same pin. A full list is given Table 6.1 PORT Function Multiplexing table in the SAMD21G datasheet.

why selected peripherial A-G ?

I mistyped. It should be A-H.

how dividing by 2 selects the given PMUX register can you give an example ?

Port A has 32 pins from PA00 to PA31 and therefore has 32 "Pin Configuration" registers. So you can access the PMUXEN bit by simply specifying the port and the pin number.

However, there are only half the number of "Peripheral Multiplexer" registers, one for each port pair. So port A has only 16 registers. To access the "Peripheral Multiplexer" registers it's necessary divide the port pin number by 2. For example to access PA21 you need to divide 21 by 2 this gives you "Peripheral Multiplexer" register number 10, which is the register for the pair PA20 and PA21.

hi Martin !
getting clearer but still some questions .

Each "Peripheral Multiplexing" 8-bit register is split into two 4-bit halves, odd and even, with each half specifying
the selected peripheral A-H. PORT_PMUX_PMUXO_F is used to set the odd port for peripheral F and PORT_PMUX_PMUXE_F to
set the even port. To select a given PMUX register you specify the even pair divided by 2 (>> 1),
as there are only half the number of "Peripheral Multiplexing" registers.

// Connect the TCC0 timer to port pin PA21 - port pins are paired odd PMUO and even PMUXE

// F & E specify the timers: TCC0, TCC1 and TCC2
  PORT->Group[PORTA].PMUX[20 >> 1].reg = PORT_PMUX_PMUXO_F;

in above example i see PMUXO but I do not see PMUXE ?
and you are dividing by 20 cause its the even member of the pair? so if the pin in question was A01 i would divide 0 by 2 giving PMUX0 ?

For example to access PA21 you need to divide 21 by 2 this gives you "Peripheral Multiplexer" register number 10, which is the register for the pair PA20 and PA21.

here why are you dividing 21 by 2 and not 20 by 2 ? since 20 is the even member of the pair as in above example.

thanks for your patience and time

Hi aliyesami,

and you are dividing by 20 cause its the even member of the pair?

I know I said specify the even pair, but come to think of it, it doesn't really matter, as dividing 20 or 21 by 2 in integer arithmetic will give you 10.

in above example i see PMUXO but I do not see PMUXE ?

If you require the even port pin then just use PORT_PMUX_PMUXE_F, or for both just logically OR the two, PORT_PMUX_PMUXO_F | PORT_PMUX_PMUXE_F;

I need a strong drink !
think iam getting close to understanding this .

Martin how did you translate this . how does g_APinDescription[6] translate to pin A21 and where the port A is specified in first command ?

 // Connect the TCC0 timer to digital output D7 - port pins are paired odd PMUO and even PMUXE
  // F & E specify the timers: TCC0, TCC1 and TCC2
  PORT->Group[g_APinDescription[6].ulPort].PMUX[g_APinDescription[6].ulPin >> 1].reg =    PORT_PMUX_PMUXO_F;

to this ?

// Connect the TCC0 timer to port pin PA21 - port pins are paired odd PMUO and even PMUXE
  // F & E specify the timers: TCC0, TCC1 and TCC2
  PORT->Group[PORTA].PMUX[20 >> 1].reg = PORT_PMUX_PMUXO_F;

which port and pin would this translate to ?

PORT->Group[g_APinDescription[11].ulPort].PMUX[g_APinDescription[11].ulPin >> 1].reg = PORT_PMUX_PMUXO_E | PORT_PMUX_PMUXE_E;

The g_APinDescription is an array held in the Arduino file "variant.cpp". It's essentially a look up table used by Arduino for each pin and defines its functions. It's used to translate between the Arduino's pin numbering system and processor's.

The Arduino number system makes the Arduino code portable between different processors. If you choose to use the Arduino numbering system in your code, then g_APinDescription.ulPort[] gives you the processor port on which the pin resides. In the case of the SAMD21G either A or B, (in reality it returns 0 for A, 1 for B), while g_APinDescription.ulPin[] gives the corresponding port pin.

For instance, if we specify Arduino pin 6, or position 6 in the g_APinDescription array, we can see this corresponds to port A, pin 20 in the "variant.cpp" file:

{ 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]

You can also map the Arduino pins to processor port pins by looking at the Arduino Zero's schematic diagram.

which port and pin would this translate to ?

Arduino Zero's digital pin 11 is connected to port pin PA16:

{ PORTA, 16, PIO_TIMER, (PIN_ATTR_DIGITAL|PIN_ATTR_PWM|PIN_ATTR_TIMER), No_ADC_Channel, PWM2_CH0, TCC2_CH0, EXTERNAL_INT_0 }, // TCC2/WO[0]

So g_APinDescription[11].ulPin would return 16. In your line of code your also setting both the odd and even pins, so this will also multiplex PA16's odd pair, PA17 as well. 16 / 2 = 8 so this will select "Peripheral Multiplexer" register 8.

Essentially you're mapping from Arduino pin number, to port pin number, to "Peripheral Multiplexer" register number.

On my Windows machine the "variant.cpp" file currently located here: C:\Users\Computer\AppData\Local\Arduino15\packages\arduino\hardware\samd\1.6.4\variants\arduino_zero\variant.cpp.

I often keep this file open in an editor like Notepad++, while working with the SAMD21G's port pins with the Arduino IDE.

1 Like

hi Martin !

looking at the following code I have two more questions if you don't mind :

// Connect the TCC0 timer to port pin PA21 - port pins are paired odd PMUO and even PMUXE
// F & E specify the timers: TCC0, TCC1 and TCC2 
1) PORT->Group[PORTA].PMUX[20 >> 1].reg = PORT_PMUX_PMUXO_F;


2) PORT->Group[g_APinDescription[11].ulPort].PMUX[g_APinDescription[11].ulPin >> 1].reg = PORT_PMUX_PMUXO_E | PORT_PMUX_PMUXE_E;

would translate to following ? considering the Arduino pin11 mapping to PA16 in the 
https://github.com/arduino/ArduinoCore-samd/blob/master/variants/arduino_zero/variant.cpp file 

3) PORT->Group[PORTA].PMUX[16 >> 1].reg = PORT_PMUX_PMUXO_E | PORT_PMUX_PMUXE_E;

1- why in first command we are only setting Odd bit where as in command 3 we are setting both odd and even bits?
I understand that PA20 is using TCC0(F) and PA16 is use TCC2(E) .

2- is my translation from 2) to 3) correct ?

thanks

Hi aliyesami,

1- why in first command we are only setting Odd bit where as in command 3 we are setting both odd and even bits?
I understand that PA20 is using TCC0(F) and PA16 is use TCC2(E) .

The first command is just connecting the port pin PA21 to peripheral F, as you say timer TCC0.

2- is my translation from 2) to 3) correct ?

Yes, your translation is correct.

Here both pins, PA16 and PA17 are being connected to peripheral E, in this case the TCC2 timer.

MartinL:

PORT->Group[PORTA].PMUX[20 >> 1].reg = PORT_PMUX_PMUXO_F;

The above command is just connecting the port pin PA21 to peripheral F, timer TCC0.

but the command below is connecting both PA16 and PA17 pins to peripheral E, in this case the TCC2 timer.

PORT->Group[PORTA].PMUX[16 >> 1].reg = PORT_PMUX_PMUXO_E | PORT_PMUX_PMUXE_E;

hi Martin
I am not getting this, both commands look identical and yet the first command is only setting the odd member where as the second command is setting the even and odd member both.
can you please explain a bit more

thanks

PORT->Group[PORTA].PMUX[20 >> 1].reg = PORT_PMUX_PMUXO_F;

Thee above command is just connecting the port pin PA21 to peripheral F, timer TCC0.

Actually, that's not true. This command is connecting PA21 to peripheral F, AND PA20 to peripheral 0 (default GPIO, probably.)

Hi westfw,

This command is connecting PA21 to peripheral F, AND PA20 to peripheral 0 (default GPIO, probably.)

Actually, it's the PMUXEN bit in the pin configuration register that determines if a pin is a GPIO or not. Not the peripheral multiplexer PMUX register.

Ok. It looks like PMUXfield = 0 implies using the pin as an external interrupt (on samd21)
(I guess, assuming that PMUXEN = 1 AND PMUX = 0.)

In any case, setting the whole byte to a 4-bit value seems particularly dangerous. You don't know whether other code elsewhere has set the other half of PMUX to some other value for the other pin (and since PMUX does essentially ALL the peripherals, it could have been code that was "logically" far away.)

  PORT->Group[PORTA].PMUX[20 >> 1].bit.PMUXO = PORT_PMUX_PMUXO_F_Val;
and
  PORT->Group[PORTA].PMUX[20 >> 1].bit.PMUXE = PORT_PMUX_PMUXE_F_Val;

is much safer. (assuming I did that right...)

Efficiency fiends (say, if you're considering a SAMD10) will notice that setting N/2 full byte values in PMUX (2 pins at a time) is going to be MUCH better than setting N 4-bit bitfields.

Hi aliyesami,

I think the best way to get an understanding, is to step through some diagrams in the SAMD21 datasheet, after all they say a picture speaks a thousand words.

If you take a look at the pinout for the SAMD21G on page 15 of the datasheet. Here you'll see the port A and port B pins. On the SAMD21G you'll notice that all the pins on port A are used, but only a handful of pins on port B.

If you now scroll through to page 404. This is the pin configuration register. There's one of these registers for each port pin. So there are 32 of these registers for port A. The PMUXEN bit acts as a switch. If it's 0 the corresponding pin is a GPIO, 1 and the pin is turned into a (yet to be defined) peripheral.

Which perhipheral? Well, that's determined by the peripheral multiplexer register on page 402. For whatever reason, the Atmel designers decided to save a few registers, so instead of having one register per pin, which would keep thinks nice and simple, they'd use one register per pin pair, (odd and even), which starts to complicate things.

So, unlike the pin configuration register where there's one for each pin, there are only half the number of peripheral multiplexer registers. So there are only 16 of these registers for port A. As there are only half the number of registers it's necessary to divide the port pin number by 2.

Returning to page 402, you can see how the register is divided into two halves. One half specifies the odd pin, the other the even. You'll also see in the register desciption that there are two tables (tables 22-3 and 22-4), showing that the number loaded into each half of the register represents a peripheral A-H. In your code these numbers are represented by PORT_PMUX_PMUXE_A, PORT_PMUX_PMUXO_A, PORT_PMUX_MUXE_B, etc... all the way to PORT_MUX_PMUXE_H and PORT_MUX_PMUXO_H.

I guess the next question is: what are peripherals A-H? What pins are connected to what peripherals is shown in the Port Function Multiplexing table (table 6-1) on page 20 of the datasheet. As you can see for pin PA20, peripheral F connects the pin to timer TCC0, while peripheral E connects it to timer TC7. With this understanding it's possible to select pins on which you wish to output you PWM signal.

I think what makes it difficult, is that Atmel decided to use half the number of peripheral mutliplex registers. If they'd've used one register per pin, it would've made everything much easier to comprehend.

Hi westfw,

Your code:

PORT->Group[PORTA].PMUX[20 >> 1].bit.PMUXO = PORT_PMUX_PMUXO_F_Val;
and
  PORT->Group[PORTA].PMUX[20 >> 1].bit.PMUXE = PORT_PMUX_PMUXE_F_Val;

....doesn't make sense in this context. The PORT_PMUX_PMUXO_F and PORT_PMUX_PMUXE_F definitions represent 4-bit nibbles, not bits.

The ".bit" fields of the Atmel CMSIS files are bitfield definitions of fields smaller than the whole register, not actually individual bits. So for PMUX there is:

typedef union {
  struct {
    uint8_t  PMUXE:4;          /*!< bit:  0.. 3  Peripheral Multiplexing Even       */
    uint8_t  PMUXO:4;          /*!< bit:  4.. 7  Peripheral Multiplexing Odd        */
  } bit;                       /*!< Structure used for bit  access                  */
  uint8_t reg;                 /*!< Type      used for register access              */
} PORT_PMUX_Type;

(I accept that this is yet-another-bad-naming decision, but at least Atmel is quite consistent with the .reg (whole register) and .bit (sub-register bitfields) unions...)

Hi westfw,

The ".bit" fields of the Atmel CMSIS files are bitfield definitions of fields smaller than the whole register, not actually individual bits.

Thanks. I didn't know that. I thought that bit meant bit, rather than bitfield.

so what's the consensus , instead of

PORT->Group[PORTA].PMUX[20 >> 1].reg = PORT_PMUX_PMUXO_F;

I should use the following ?

PORT->Group[PORTA].PMUX[20 >> 1].bit.PMUXO = PORT_PMUX_PMUXO_F_Val;
and
PORT->Group[PORTA].PMUX[20 >> 1].bit.PMUXE = PORT_PMUX

and if this is the case should the PWM example in the start of this thread should be re-written?

I think I got it finally . so the following command though might work should ideally be written as setting both the even and the odd bytes.

PORT->Group[PORTA].PMUX[20 >> 1].reg = PORT_PMUX_PMUXO_F;

should ideally be written as

PORT->Group[PORTA].PMUX[20 >> 1].reg = PORT_PMUX_PMUXO_F | PORT_PMUX_PMUXE_F ;

this will set the register PMUX10 even and odd bytes and depending on the PINCFG20.PMUXEN and PINCFG21.PMUXEN bits being set , will connect these pins as follows :

TCC0 channel 6 to PA20
TCC0 channel 7 to PA21

am I right ?

Hi aliyesami,

westfw was just pointing out that:

PORT->Group[PORTA].PMUX[20 >> 1].reg = PORT_PMUX_PMUXO_F;

... will set the even side of the register to zero, however that part of the register might have been set by code elsewhere, in order to set up a perhipheral on the even pin.

To prevent this you can either use bitfields, as westfw suggested:

PORT->Group[PORTA].PMUX[20 >> 1].bit.PMUXO = PORT_PMUX_PMUXO_F_Val;

... or use the shift and mask method with the logical OR operator:

PORT->Group[PORTA].PMUX[20 >> 1].reg |= PORT_PMUX_PMUXO_F;

Either way is OK, though using the bitfields method is probably a tiny bit more efficient.

hi Martin !

I now understand westfw point.
so is my analysis in thread #36 above correct ?

Hi aliyesami,

so is my analysis in thread #36 above correct ?

Yes, you've got it. It'll set up timer TCC0 on pins PA20 and PA21, provided the PMUXEN for both pins are set.

If you happen to be using only one pin, then use this to set up PA20:

PORT->Group[PORTA].PMUX[20 >> 1].reg |= PORT_PMUX_PMUXE_F;

or this to set up PA21:

PORT->Group[PORTA].PMUX[20 >> 1].reg |= PORT_PMUX_PMUXO_F;
1 Like