Arduino Forum

Products => Arduino Due => Topic started by: Jantje on Jul 25, 2014, 01:52 pm

Title: What are the possible PWM frequencies? And how to set them.
Post by: Jantje on Jul 25, 2014, 01:52 pm
I have been looking for a simple answer on my simple question: "What are the possible PWM frequencies on DUE?"; but I start to fear there is no simple answer.
The datasheet contains some PWM calculation formula's but my current knowledge is insufficient to even make out I'm looking at the right spot.
The playground contains a PWM cheat sheet http://playground.arduino.cc/Main/TimerPWMCheatsheet (http://playground.arduino.cc/Main/TimerPWMCheatsheet) unfortunately there is no due section.

I'm considering to move my project from a mega to a due. As 500Hz is to slow for me I'm looking for a simple table like the one below of the cheat sheet. (I expect 2 due to millis() and timer 0)
Quote
Pins 5 and 6: controlled by Timer 0 in fast PWM mode (cycle length = 256)

Setting    Divisor    Frequency
0x01        1        62500
0x02        8        7812.5
0x03        64        976.5625   <--DEFAULT
0x04        256        244.140625
0x05        1024        61.03515625

Is this info available somewhere?

Best regards
Jantje
Title: Re: What are the possible PWM frequencies? And how to set them.
Post by: WesBrooks on Jul 31, 2014, 03:55 pm
Bump up to the top!

Sorry, no answer but looking into some very similar things myself too, and examples or pointing in the right direction would save some time!
Title: Re: What are the possible PWM frequencies? And how to set them.
Post by: pbrouwer on Jul 31, 2014, 04:12 pm
The default frequency is 1000 Hz, I beleive.

See this thread for more info on changing frequency:

http://forum.arduino.cc/index.php/topic,131323.0.html
Title: Re: What are the possible PWM frequencies? And how to set them.
Post by: Jantje on Jul 31, 2014, 05:00 pm
@pbrouwer
I had read the thread. Didn't find the info I needed.
I reread the thread. I still don't understand it enough to find out what frequencies are possible on which pin and how to do it.

Best regards
Jantje
Title: Re: What are the possible PWM frequencies? And how to set them.
Post by: WesBrooks on Jul 31, 2014, 05:10 pm
Looks like some useful information here too:

http://forum.arduino.cc/index.php?topic=131323.15

Default of 1000Hz would make sense as I saw that on my multi meter when I was checking the output voltage from the motor shield.
Title: Re: What are the possible PWM frequencies? And how to set them.
Post by: pbrouwer on Jul 31, 2014, 06:15 pm
From what i understand of the controllers datasheet, PWM on the due is pretty configurable, much more so than the chip used on the uno or mega.
The chip can have different frequencies for different pins, and because of the variable dividers, available frequencies are only limited by the master clock (they cannot be higher than that). So you should be able to set pretty much any frequency, or at least one very close the the one you like.

This thread has a library that can set two desired frequencies:

http://forum.arduino.cc/index.php/topic,146947.msg1115263.html#msg1115263

Another option (may not be the best option), is to modify variants.h for the DUE  and set the pwm frequency to another value.
Title: Re: What are the possible PWM frequencies? And how to set them.
Post by: MarkT on Jul 31, 2014, 07:50 pm
What is needed is for one of the developers add a variable to replace the #define
in variant.h - or better still to add a pwm frequency variable per-pin.

Note that the exact frequency will be close to, but may not be identical to the
PWM_FREQUENCY value, since there is only an 8-bit resolution divider in
the PWM generators
Title: Re: What are the possible PWM frequencies? And how to set them.
Post by: hobbified on Aug 01, 2014, 09:52 am
Nearly any frequency from microhertz (one cycle per 3 days, or so) to 42 MHz is possible, with millions of options in between, so there's no point in printing a table. Figure out what you want, and then figure out how best to do it :)
Title: Re: What are the possible PWM frequencies? And how to set them.
Post by: Jantje on Aug 01, 2014, 07:12 pm

Nearly any frequency from microhertz (one cycle per 3 days, or so) to 42 MHz is possible, with millions of options in between,

@hobbified
This is very interesting information to me (and I guess many others)

So I have now
Default PWM frequency =1000 Hz (the same for all pwm pins)
frequency range  microhertz (one cycle per 3 days) to 42 MHz
PWM Pins D2-D13 (as to http://forum.arduino.cc/index.php?topic=132130.0 (http://forum.arduino.cc/index.php?topic=132130.0) also pins D38 to D44


Figure out what you want, and then figure out how best to do it :)

This is still missing. I'd like to have a easy way to get from I want this pwm frequency to: "how to do it". That is what I'd like to add to the "cheat sheet" .

This code for maple (not sure how due relates to it) has been linked to a couple of times in this thread http://forum.arduino.cc/index.php?topic=131323.15 (http://forum.arduino.cc/index.php?topic=131323.15)
Supposing I can use code as below to control the PWM frequency for all these pins of the due: (I don't think I can but then I'd like to know the code)
Code: [Select]

    timer.pause();
    timer.setPrescaleFactor(3);
    timer.setOverflow(4000);
    timer.refresh();
    timer.resume();

How do I get from my wanted PWM frequency (say  42 MHz as example) to the PrescaleFacor and to the Overflow value?
And how do I get from my pin number to the timer?

There are 11 (or 18) pwm pins. It can't be that hard to list the resources needed to  set the pwm frequency for those pins?

Best regards
Jantje

Title: Re: What are the possible PWM frequencies? And how to set them.
Post by: bobcousins on Aug 01, 2014, 09:10 pm


There are 11 (or 18) pwm pins. It can't be that hard to list the resources needed to  set the pwm frequency for those pins?


It's not too hard, but it's long : it's all in the SAM3X datasheet, 1455 pages. Creating a short summary of all that info is hard though. The datasheet does contain the quote "The large number of source clocks can make selection difficult." :) But often you don't need to worry too much about the details in the datasheet, just use the library functions Atmel provide.

Really, I usually find reading the library source code is the best way to figure out how to do things. For example, to set the PWM clock, just call PWMC_ConfigureClocks(uint32_t clka, uint32_t clkb, uint32_t mck) and let the function work out what all the registers should be. There is an example in analogWrite, which was referenced in the other thread.

It's complicated slightly because the Arduino library presents pins as "PWM enabled", but under the hood does things differently depending on the hardware.

A 42MHz PWM is probably a little ambitious, for a typical 8 bit PWM, the max freq would be 84MHz/256.

Here is a simple example I just tried for a real PWM pin, to generate 10kHz, 50% duty cycle, using default 8 bit resolution:

Code: [Select]
void setup() {
 // put your setup code here, to run once:

 pinMode (6, OUTPUT);
   
 analogWrite (6, 128);
 // must be called after first analogWrite
 PWMC_ConfigureClocks (256 * 10000, 0, VARIANT_MCK);
}

void loop() {
 // put your main code here, to run repeatedly:

}


And here is what you get on the scope:

(https://farm3.staticflickr.com/2922/14617213358_6af9eb9d3d_o.png)
Title: Re: What are the possible PWM frequencies? And how to set them.
Post by: Jantje on Aug 01, 2014, 10:54 pm
I feel I'm getting somewhere. Thanks for that. But I'm not there ... please keep providing input.

I have now
Default PWM frequency =1000 Hz (the same for all pwm pins)
theoretic frequency range  microhertz (one cycle per 3 days) to 42 MHz
practical frequency range microhertz (one cycle per 3 days) to 328 KHz
PWM Pins D2-D13 (as to http://forum.arduino.cc/index.php?topic=132130.0 also pins D38 to D44

The code to set pin 6 to 10kHz, 50% duty cycle is
Code: [Select]
  analogWrite (6, 128);
  // must be called after first analogWrite
  PWMC_ConfigureClocks (256 * 10000, 0, VARIANT_MCK);



But ....
The PWMC_ConfigureClocks method is defined in [arduino IDE location]/hardware/arduino/sam/system/libsam/include/pwmc.h.
So it is a amtel delivered method implemented in compiled code (so it is "proprietary code")
I however fail to find any documentation on  PWMC_ConfigureClocks.
pwmc.h full documentation on the method is:
Code: [Select]
*    -# Configures PWM clocks A & B to run at the given frequencies using
*       \ref PWMC_ConfigureClocks().

The definition is
Code: [Select]
void PWMC_ConfigureClocks(uint32_t clka, uint32_t clkb, uint32_t mck ) ;
I found nothing in pwmc.h that something should be set before calling this method. The arduino code does state you should call analogwrite before the call. So maybe the code is setting the pwm frequency for all pwm pins? Can I call this multiple times with different frequencies? how do I set PWM pin 2 to 10KHz and pin 13 to 44Khz
Anyone knows a pointer to documentation of PWMC_ConfigureClocks?


I found no information on amtel nor on arduino reference page on this method.

Best regards
Jantje


Title: Re: What are the possible PWM frequencies? And how to set them.
Post by: randomvibe on Aug 01, 2014, 11:08 pm
This library has worked for me and several others.  You can setup up to two unique frequencies on several pins.  Just make sure you read the notes.  Give it a try.  Good luck.

http://forum.arduino.cc/index.php?topic=144446.msg1149044#msg1149044

Title: Re: What are the possible PWM frequencies? And how to set them.
Post by: bobcousins on Aug 02, 2014, 11:48 am

But ....
The PWMC_ConfigureClocks method is defined in [arduino IDE location]/hardware/arduino/sam/system/libsam/include/pwmc.h.
So it is a amtel delivered method implemented in compiled code (so it is "proprietary code")
I however fail to find any documentation on  PWMC_ConfigureClocks.

It's code provided by Atmel as part of their ASF (Atmel Software Framework). It has a BSD style license, so there is no problem to include with open source code. http://asf.atmel.com/docs/latest/
There is no deeper level of documentation than the doxygen comments in the code, apart from example progams. Atmel have the documentation online http://asf.atmel.com/docs/latest/sam3x/html/group__sam__drivers__pwm__group.html, but note the Atmel code used by Arduino is now out of date with respect to the latest ASF.

Quote

pwmc.h full documentation on the method is:
Code: [Select]
*    -# Configures PWM clocks A & B to run at the given frequencies using
*       \ref PWMC_ConfigureClocks().

The definition is
Code: [Select]
void PWMC_ConfigureClocks(uint32_t clka, uint32_t clkb, uint32_t mck ) ;
I found nothing in pwmc.h that something should be set before calling this method. The arduino code does state you should call analogwrite before the call.


The great thing about software is that a complete specification for what the software does is the software itself :) The compiler must be able to compile without reference to further documentation. It really is an essential skill for anyone writing code to be able to read code as well.

If you are not sure what the registers and values mean, then reading the SAM3X datasheet is a complete and accurate specification. There is no point me copying and pasting the SAM3X datasheet, you can read it for yourself!

In this case, it is obvious from reading the code that analogWrite calls PWMC_ConfigureClocks the first time it is called, which would overwrite any previous initialisation. I wouldn't expect that to be documented anywhere, it's just simple programming logic.

Quote
So maybe the code is setting the pwm frequency for all pwm pins? Can I call this multiple times with different frequencies?


Each channel can select from a number of clock sources, the way Arduino sets up is to use the same source for all channels (clka). PWMC_ConfigureClocks() can set clka and clkb, so that gives you two programmable clock sources. The other clock sources are divisors of the main clock, as described in the SAM3X datasheet (p1058)

Quote
CPRE: Channel Pre-scaler
Value Name Description
0b0000 MCK Master clock
0b0001 MCK_DIV_2 Master clock/2
0b0010 MCK_DIV_4 Master clock/4
0b0011 MCK_DIV_8 Master clock/8
0b0100 MCK_DIV_16 Master clock/16
0b0101 MCK_DIV_32 Master clock/32
0b0110 MCK_DIV_64 Master clock/64
0b0111 MCK_DIV_128 Master clock/128
0b1000 MCK_DIV_256 Master clock/256
0b1001 MCK_DIV_512 Master clock/512
0b1010 MCK_DIV_1024 Master clock/1024
0b1011 CLKA Clock A
0b1100 CLKB Clock B


Quote
Anyone knows a pointer to documentation of PWMC_ConfigureClocks?


You have already found it!


Quote
how do I set PWM pin 2 to 10KHz and pin 13 to 44Khz


So far I have been describing the hardware PWM channels, and these are available on pins 6,7,8,9. There are other hwPWM channels, but they are not mapped in Arduino. You would either need to change variant.cpp, or copy the PWM code from analogWrite.

If you look at the Due pinout diagram, if the SAM pin name is "PWMxx" then it's a hardware PWM channel. There are 8 channels, each with a "high" and "low" output. Therefore there are PWML0, PWMH0, etc up to PWML7, PWMH7. Each channel can have a different clock source, which will affect both high and low outputs.

The other pins marked "PWM" in the Due pinout i.e. 2-5 and 9-13, are implemented using timer channels. The timer channels are also capable of producing PWM waveforms, without some of the more advanced PWM features, but can produce more accurate timing.

I will have a look at those and put an example in another post.
Title: Re: What are the possible PWM frequencies? And how to set them.
Post by: bobcousins on Aug 02, 2014, 12:31 pm
Here is a demo sketch:

Code: [Select]
// --------------------------------------------
//
// Hardware PWM with different frequencies demo
//
// Bob Cousins, August 2014
// --------------------------------------------

void setup() {
 // put your setup code here, to run once:

 pinMode (6, OUTPUT);  // PWML7
 pinMode (7, OUTPUT);  // PWML6
 pinMode (8, OUTPUT);  // PWML5
   
 // call analogWrite first to allow Arduino to initialise  
 analogWrite (6, 128);
 analogWrite (7, 128);
 analogWrite (8, 128);
 
 // set clka to 10KHz, clkb to 5kHz
 PWMC_ConfigureClocks (255 * 10000, 255 * 5000, VARIANT_MCK);
 
 // set channel 6 to use clkb
 PWMC_ConfigureChannel (PWM, 6, PWM_CMR_CPRE_CLKB, 0, 0);
 // Note: need to re-enable channel after doing Configure
 PWMC_EnableChannel (PWM, 6);
 
 // set channel 5 to use MCK/16 (actual freq = 84MHz/255/16 = 20588 Hz)
 PWMC_ConfigureChannel (PWM, 5, PWM_CMR_CPRE_MCK_DIV_16, 0, 0);
 PWMC_EnableChannel (PWM, 5);
}

void loop() {
 // put your main code here, to run repeatedly:

}


And scope trace

(https://farm4.staticflickr.com/3878/14621625700_28bc8677e1_o.png) (https://flic.kr/p/oh4DUQ)demo_pwm (https://flic.kr/p/oh4DUQ) by donotdespisethesnake (https://www.flickr.com/people//), on Flickr
Title: Re: What are the possible PWM frequencies? And how to set them.
Post by: bobcousins on Aug 02, 2014, 08:23 pm
As promised here is a sketch using timers for PWM, with different frequencies. You can have 4 different frequencies, because there are 4 timer channels used. Each timer channel can have two outputs, A and B, each with their own duty cycles.

There are a total of 9 timer channels, so in theory you could have 9 different frequencies, but not all the output pins may be mapped in convenient locations, or at all. You would also need to do all the setup code yourself, as they are not handled by analogWrite, or possibly edit variant.cpp.

Code: [Select]
// --------------------------------------------
//
// PWM with timers demo
//
// Bob Cousins, August 2014
// --------------------------------------------

typedef struct {
   Tc *pTC;        // TC0, TC1, or TC2
   byte channel;   // 0-2
   byte output;    // 0 = A, 1 = B
}  tTimerInfo;

tTimerInfo timerLookup [] =
{
 {NULL,0,0}, // 0
 {NULL,0,0},
 {TC0,0,0},  // pin 2 = TIOA0
 {TC2,1,0},  // pin 3 = TIOA7
 {TC2,0,1},  // pin 4 = TIOB6
 {TC2,0,0},  // pin 5 = TIOA6
 {NULL,0,0},
 {NULL,0,0},
 {NULL,0,0},
 {NULL,0,0},
 {TC2,1,1},  // pin 10 = TIOB7
 {TC2,2,0},  // pin 11 = TIOA8
 {TC2,2,1},  // pin 12 = TIOB8
 {TC0,0,1}   // pin 13 = TIOB0
};

/**
* \brief set a pin for PWM using a timer channel
* \param pin       pin to use (0-13 only!)
* \param frequency the frequency
* \param dutyCyle  duty cycle 0-255
* \return          this function returns a count, which is the effective PWM resolution. Returns 0 if pin is not valid
*/

uint32_t setupTimerPwm (byte pin, uint32_t frequency, unsigned dutyCycle)
{
 uint32_t count = VARIANT_MCK/2/frequency;
 tTimerInfo *pTimer = &timerLookup[pin];
 
 if (pTimer != NULL)
 {
   TC_SetRC (pTimer->pTC, pTimer->channel, count);
   if (pTimer->output == 0)
      TC_SetRA (pTimer->pTC, pTimer->channel, count * dutyCycle / 256);
   else
      TC_SetRB (pTimer->pTC, pTimer->channel, count * dutyCycle / 256);
 
   return count;
 }
 else
   return 0;
}

void setup() {
 // put your setup code here, to run once:

 // use the Arduino lib to do initial setup
 analogWrite (2, 128);
 analogWrite (13, 128);
 
 analogWrite (5, 128);
 analogWrite (4, 128);

 analogWrite (3, 128);
 analogWrite (10, 128);

 analogWrite (11, 128);
 analogWrite (12, 128);

 // pins 2 and 3 share the same timer so must have same frequency
 setupTimerPwm (2, 2000, 128);
 setupTimerPwm (13, 2000, 64);
 
 // pins 5 and 4 share the same timer
 setupTimerPwm (5, 3000, 128);
 setupTimerPwm (4, 3000, 64);

 // pins 3 and 10 share the same timer
 setupTimerPwm (3, 4000, 128);
 setupTimerPwm (10, 4000, 64);

 // pins 11 and 12 share the same timer
 setupTimerPwm (11, 5000, 128);
 setupTimerPwm (12, 5000, 64);

}

void loop() {
 // put your main code here, to run repeatedly:

}


Scope traces, showing pins 2, 5, 3 and 11 respectively (these are the 'A' outputs) :

(https://farm4.staticflickr.com/3880/14788142106_55111f50f7_o.png) (https://flic.kr/p/owM6rm)demo2 (https://flic.kr/p/owM6rm) by donotdespisethesnake (https://www.flickr.com/people//), on Flickr

If you find the advice "read the source code" offensive, I'm sorry, but please don't send me PMs to complain. It really is Good Advice Learn to Read the Source, Luke! (http://blog.codinghorror.com/learn-to-read-the-source-luke/). If you don't want to read source code, consider a different hobby/profession!

And if you are using Arduino, you already have it's source code on your computer, so don't say you don't have it. It is really worth studying if you want to learn how it works.
Title: Re: What are the possible PWM frequencies? And how to set them.
Post by: iyahdub on Aug 04, 2014, 10:11 pm
Highly instructive :)
Title: Re: What are the possible PWM frequencies? And how to set them.
Post by: Punthoofd07 on Sep 04, 2015, 04:57 pm
As promised here is a sketch using timers for PWM, with different frequencies. You can have 4 different frequencies, because there are 4 timer channels used. Each timer channel can have two outputs, A and B, each with their own duty cycles.

There are a total of 9 timer channels, so in theory you could have 9 different frequencies, but not all the output pins may be mapped in convenient locations, or at all. You would also need to do all the setup code yourself, as they are not handled by analogWrite, or possibly edit variant.cpp.

Code: [Select]
// --------------------------------------------
//
// PWM with timers demo
//
// Bob Cousins, August 2014
// --------------------------------------------

typedef struct {
    Tc *pTC;        // TC0, TC1, or TC2
    byte channel;   // 0-2
    byte output;    // 0 = A, 1 = B
}  tTimerInfo;

tTimerInfo timerLookup [] =
{
  {NULL,0,0}, // 0
  {NULL,0,0},
  {TC0,0,0},  // pin 2 = TIOA0
  {TC2,1,0},  // pin 3 = TIOA7
  {TC2,0,1},  // pin 4 = TIOB6
  {TC2,0,0},  // pin 5 = TIOA6
  {NULL,0,0},
  {NULL,0,0},
  {NULL,0,0},
  {NULL,0,0},
  {TC2,1,1},  // pin 10 = TIOB7
  {TC2,2,0},  // pin 11 = TIOA8
  {TC2,2,1},  // pin 12 = TIOB8
  {TC0,0,1}   // pin 13 = TIOB0
};

/**
 * \brief set a pin for PWM using a timer channel
 * \param pin       pin to use (0-13 only!)
 * \param frequency the frequency
 * \param dutyCyle  duty cycle 0-255
 * \return          this function returns a count, which is the effective PWM resolution. Returns 0 if pin is not valid
 */

uint32_t setupTimerPwm (byte pin, uint32_t frequency, unsigned dutyCycle)
{
  uint32_t count = VARIANT_MCK/2/frequency;
  tTimerInfo *pTimer = &timerLookup[pin];
 
  if (pTimer != NULL)
  {
    TC_SetRC (pTimer->pTC, pTimer->channel, count);
    if (pTimer->output == 0)
       TC_SetRA (pTimer->pTC, pTimer->channel, count * dutyCycle / 256);
    else
       TC_SetRB (pTimer->pTC, pTimer->channel, count * dutyCycle / 256);
 
    return count;
  }
  else
    return 0;
}

void setup() {
  // put your setup code here, to run once:

  // use the Arduino lib to do initial setup
  analogWrite (2, 128);
  analogWrite (13, 128);
 
  analogWrite (5, 128);
  analogWrite (4, 128);

  analogWrite (3, 128);
  analogWrite (10, 128);

  analogWrite (11, 128);
  analogWrite (12, 128);
 
  // pins 2 and 3 share the same timer so must have same frequency
  setupTimerPwm (2, 2000, 128);
  setupTimerPwm (13, 2000, 64);
 
  // pins 5 and 4 share the same timer
  setupTimerPwm (5, 3000, 128);
  setupTimerPwm (4, 3000, 64);

  // pins 3 and 10 share the same timer
  setupTimerPwm (3, 4000, 128);
  setupTimerPwm (10, 4000, 64);

  // pins 11 and 12 share the same timer
  setupTimerPwm (11, 5000, 128);
  setupTimerPwm (12, 5000, 64);

}

void loop() {
  // put your main code here, to run repeatedly:

}


Scope traces, showing pins 2, 5, 3 and 11 respectively (these are the 'A' outputs) :

(https://farm4.staticflickr.com/3880/14788142106_55111f50f7_o.png) (https://flic.kr/p/owM6rm)demo2 (https://flic.kr/p/owM6rm) by donotdespisethesnake (https://www.flickr.com/people//), on Flickr

If you find the advice "read the source code" offensive, I'm sorry, but please don't send me PMs to complain. It really is Good Advice Learn to Read the Source, Luke! (http://blog.codinghorror.com/learn-to-read-the-source-luke/). If you don't want to read source code, consider a different hobby/profession!

And if you are using Arduino, you already have it's source code on your computer, so don't say you don't have it. It is really worth studying if you want to learn how it works.

Especially made an account to thank you! :-)