please help understand PWM on SAMD21

I am using PWM in AVR and its fairly easy to understand but I can't seem to find any material or articles other than datasheet explaining how PWM works on SAMD21.
to start with can someone explain the following:

1- the information about SAMD21JXX saying it has 24 PWM channels since each of the three 8 bit timers bit can be used for PWM. , how is it done? and how each bit is mapped to a physical pin ?

2- I need 8 PWM signals , which physical pins I can use without sacrificing the UART pins?

3- in AVR I am using the dual slope PWM but the graphs for it looks very different from the dual slope PWM graphs shown for SAMD21 ?

4- is there any example of a dual slope PWM in ASF available or C available ?

thanks

Dual slope PWM on the SAMD21 is covered in the thread "Changing Arduino Zero PWM Frequency": Changing Arduino Zero PWM Frequency - Arduino Zero - Arduino Forum

The TCC timers provide up to 8 PWM outputs. Additional PWM outputs can be provided by the TC timers, although these provide a less fully featured PWM operation.

hi Martin!

I am very new to SAMD21 so bare with my stupid questions. I am using SAMW25 which has a built in SAMD21 , which 8 pins on this module I can use as PWM pins still leaving one UART port available (if these are multiplexed ).

on AVR there are 8 timer registers that I can easily control by storing some value e.g in a 8 bit register I store 255/2 approx. 127 to get 50% duty cycle or I store 255 to get 100% duty cycle etc.

I need to do the same in SAMD21 .

thanks

I am using SAMW25 which has a built in SAMD21 ,...

Have you burnt the Arduino.cc bootloader and are you using Arduino code with this board?

...which 8 pins on this module I can use as PWM pins still leaving one UART port available (if these are multiplexed ).

In the "Changing Arduino Zero PWM Frequency" thread, I allocated timer TCC0 to pins: D2 (PA14), D5 (PA15), D6 (PA20) and D7 (PA21). It's also possible to use timer TCC1 on pins: D4 (PA8) and D3 (PA9), as well as timer TCC2 on pins: D11 (PA16) and D13 (PA17).

This leaves the Serial1 port on pins D0 (PA11) and D1 (PA10) free.

I have not started to use anything yet as I am struggling to understand how the PWM works on SAMD21.
so if I use TCC0 timer on PA14 ,PA15,PA20, PA21 which four registers I can use to control the PWM on these pins ? like in AVR for timer TCCR0 there are two registers OCR0A and OCR0B that I can use to control the PWM .

If you look at the code at the beginning 2nd page of the thread, “Changing Arduino Zero PWM Frequency”: https://forum.arduino.cc/index.php?topic=346731.15, it gives a comparison of dual slope PWM between the AVR and SAMD21.

hi Martin
I looked at the code , its fixing the duty cycle at 50% and changing the frequency , my needs are opposite, I need a fixed frequency between 100Hz-1KHz and have a control over the duty cycle by depositing an integer value in the REG_TCC0_CCBx registers.
can you give me an example for one pin and I can try to expand it to use 8 ?

thanks

reading your example I also have a question , you wrote

"To change the PWM pulse width (phase), just load the timer's CCBx register with a value between 0 and 96. 0 outputs 0V (0% duty cycle), 96 outputs 3.3V (100% duty cycle). Loading the CCBx register with 48 gives a 50% duty cycle.
"

I read analog values in 10bit resolution giving me a values ranging from 0-1023 which I currently deposit in the OCR register.
how can I modify your example to use this range instead of 0-96 ? as this does not give me a very good resolution.

regards

hi Martin
sorry to bombard you with questions , can you please take a look at the SAMW25 module which uses SAMD21 processor and tell me if I can get the following number of pins out of it ?

[

8 PWM signals.
1 UART
4 analog inputs (1023bit resolution)
1 I2C channel
1 SPI channel
4 GPIO

thanks](http://www.atmel.com/Images/Atmel-42395-SmartConnect-ATSAMW25-MR210PA_Datasheet.pdf)

I looked at the code , its fixing the duty cycle at 50% and changing the frequency , my needs are opposite, I need a fixed frequency between 100Hz-1KHz and have a control over the duty cycle by depositing an integer value in the REG_TCC0_CCBx registers.
can you give me an example for one pin and I can try to expand it to use 8 ?

As I said, if you look at the code at the beginning 2nd page of the thread, "Changing Arduino Zero PWM Frequency": Changing Arduino Zero PWM Frequency - Arduino Zero - Arduino Forum, it provides an example of using dual slope PWM at 50Hz on four outputs. To change the duty cycle load the REG_TCC0_CCBx register with a value between 0 and whatever is in the PER (period) register, (in this case 20000).

how can I modify your example to use this range instead of 0-96 ? as this does not give me a very good resolution.

The 50Hz example gives a resolution of 14-bits.

can you please take a look at the SAMW25 module which uses SAMD21 processor and tell me if I can get the following number of pins out of it ?

I’m not familiar with the SAMW25, but the Arduino Zero (that also uses the SAMD21G) would be able to meet these IO requirements.

MartinL:
As I said, if you look at the code at the beginning 2nd page of the thread, "Changing Arduino Zero PWM Frequency": Changing Arduino Zero PWM Frequency - Arduino Zero - Arduino Forum, it provides an example of using dual slope PWM at 50Hz on four outputs. To change the duty cycle load the REG_TCC0_CCBx register with a value between 0 and whatever is in the PER (period) register, (in this case 20000).

The 50Hz example gives a resolution of 14-bits.

hi Martin

just interpreting the PWM from datasheet requires a Phd so my hats off for you to know it so very well.
and thanks for taking your time to share your knowledge on the forum.

I currently use 10 bit resolution. How can I modify your code to use 10bit resolution ? and what would be the range of deposited values in REG_TCC0_CCBx register?

Hi aliyesami,

I currently use 10 bit resolution. How can I modify your code to use 10bit resolution ? and what would be the range of deposited values in REG_TCC0_CCBx register?

Using a base timer frequency of 2MHz (48MHz/3/8) you can adjust the frequency and resolution by chaning the value of the PER (period), for example:

20000 = 50Hz output with 14-bit resolution
2500 = 400Hz output with 11-bit resolution

The value in the REG_TCC0_CCBx is any number between 0 and the value in the PER register. So for 400Hz:

0 = 0% duty cycle
1250 = 50% duty cycle
2500 = 100% duty cycle

hi Martin !

I tried to calculate these values to see if i am understanding you properly .There are 3 forumlas given in the SAMD21 datasheet on page 661-662 .
I started by calculating the value of PER for 10bit resolution and then used that value to calculate the frequency which for me can be any value between 100-1Khz.
but after that i am confused . The datasheet gives the formula for pulse width which i assume is the duty cycle?
so what would be the units in ? for 100% duty cycle what value i would use in the last formula for Pulsewidth to get the value for CCx ?
are my first two steps correct ?

clock frequency = 48Mhz 
N =prescalar = 64

Rpwm_ds = log(PER+1)/log(2)  
PER = 2expRpwd_ds -1 =  2exp10 - 1 = 1023 

fpwm_ds =  fGlck_tcc/(2xNxPER)  = 48000000hz /2x64x1023 = 366.5Hz 

PulseWidth= 2N(PER-CCx)/fGclck_tcc 
PulseWidthXfGclck_tcc =  2N.PER - 2N.CCx
2N.CCx = 2N.PER - (PulseWidth X fGclck_tcc) 
CCx =    (2N.PER - (PulseWidth X fGclck_tcc))/2N

Hi aliyesami,

are my first two steps correct ?

Your calculations are completely correct, only that in my example I reversed the polarity of the waveform, so that when CCBx is 0, it outputs 0V at 0% duty cycle and when CCBx is equal to PER, it outputs a constant 3.3V at 100%.

If you reverse the polarity, this gives:

pulse width = (2 * N * CCx) / fgclk_tcc for CCx <= PER

Where “pulse width” is the width of the (logic high) pulse in seconds.

Note that I’ve used the buffered CCBx registers rather than the CCx. The buffered registers allow changes to be made to the duty cycle without causing gliches on your waveform.

MartinL:
Hi aliyesami,

Your calculations are completely correct, only that in my example I reversed the polarity of the waveform, so that when CCBx is 0, it outputs 0V at 0% duty cycle and when CCBx is equal to PER, it outputs a constant 3.3V at 100%.

If you reverse the polarity, this gives:

pulse width = (2 * N * CCx) / fgclk_tcc for CCx <= PER

Where “pulse width” is the width of the (logic high) pulse in seconds.

Note that I’ve used the buffered CCBx registers rather than the CCx. The buffered registers allow changes to be made to the duty cycle without causing gliches on your waveform.

Hi Martin !
so for my case the Pulse width with be either
2x64x0/48000000=0 or
2x64x1023/48000000 = 2.728ms ?

I am confused about polarity since I will also be getting 0s for CCx=0 or not?

Hi aliyesami,

so for my case the Pulse width with be either
2x64x0/48000000=0 or
2x64x1023/48000000 = 2.728ms ?

I will also be getting 0s for CCx=0 or not?

Yes that's right. At CCx = 0, you'll get a constant 0V output. At CCx = 1023, you'll get a constant 3.3V output.

Notice at Cx = 1023, 1 / 2.728ms = 366.5Hz. In this instance your pulse is taking up the full 2.728ms period, so the output becomes a constant 3.3V.

thanks Martin I learned a lot from you .
now that I know how the PWM wave calculations are done, can you also help me understand this port multiplexing ?
how does the following command work ? I am not able to tie it to the datasheet, i mean why are we doing what we are doing in these commands.

// Enable the port multiplexer for the digital pin D7 
  PORT->Group[g_APinDescription[7].ulPort].PINCFG[g_APinDescription[7].ulPin].bit.PMUXEN = 1;
  
  // 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;

The pins on the SAMD21 can perform as GPIO or number of different peripheral functions: timer, analog input, serial communications, etc.. , but by default they're GPIO.

Each pin has its own "Pin Configuration" register, that amongst other things can activate the input buffer or set the pull-up resistor. One of the bits however is the PMUXEN (Peripheral Multiplexer Enable) bit. Setting this bit switches the pin from GPIO to one of a number of (yet to be defined) peripherals.

The following line sets the PMUXEN in the Pin Configuration register for digital pin D7:

// Enable the port multiplexer for the digital pin D7
  PORT->Group[g_APinDescription[7].ulPort].PINCFG[g_APinDescription[7].ulPin].bit.PMUXEN = 1;

As D7 is actually port pin PA21 on the SAMD21, so you could also write:

// Enable the port multiplexer for the port pin PA21:
  PORT->Group[PORTA].PINCFG[21].bit.PMUXEN = 1;

The next step is to select the peripheral for the given pin, using the "Peripheral Multiplexing" registers. Each peripheral is given a letter from A-H. The TCC timers are listed as peripheral F. While there's one "Pin Configuration" register per pin, there's only one "Peripheral Multiplexing" registers for each odd and even pin pair. (So there are only half the number of "Periphal Multiplexing" registers). For example port pin PA21 (D7) is odd, this is paired with PA20 (D6), which is even.

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 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;

This could also be written as:

// 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;

It took me a while to bend my head around port multiplexing.

thanks Martin ,
first part is clear now on how and why the Pin configuration register has to be set . I have questions about the second part.

MartinL:
The next step is to select the peripheral for the given pin, using the "Peripheral Multiplexing" registers. Each peripheral is given a letter from A-H. The TCC timers are listed as peripheral F. While there's one "Pin Configuration" register per pin, there's only one "Peripheral Multiplexing" registers for each odd and even pin pair. (So there are only half the number of "Periphal Multiplexing" registers). For example port pin PA21 (D7) is odd, this is paired with PA20 (D6), which is even.

odd and even pair of what ?
why PA21 is paired with PA20 and not PA18 ? That would still make it even odd.
Iam also seeing TCC timers in E , how come?

Each "Peripheral Multiplexing" 8-bit register is split into two 4-bit halves, odd and even, with each half specifying the selected peripheral A-G. 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.

why selected peripherial A-G ? and not A- H ?
I am not understanding this even or odd port . can you please explain ?
how dividing by 2 selects the given PMUX register can you give an example ?

1 Like