How to program 16-bit pwms in the different Arduino boards ?

Hello,

I currently have an Arduino Mega board and one of my needs is to produce PWM signal at high frequency (for example 30khz), and if possible with 12bits or 16bits resolution. I also plan to buy an Arduino Due to be able to do this and to have more power.

Concerning the PWM frequency, on Arduino Mega this is fine : when modifying TCCRxB register, I change the prescale, thus the frequency. So I can get the 30khz frequency. But concerning the 16-bits PWM, I haven't seen much information, and I don't know if it's possible to have this 16bits (or 10 or 12?) resolution. ( Well now i am pretty sure it's impossibe as I realise that 30k* 65k is about 2ghz... )

As this Mega board is too weak for my project, I thought "ok let's buy the new Arduino Due, with this powerfull Atmel 32 bits microcontroler it's probably easy to get some kind of 12bits PWM at high frequency"
But I get confused. The Atmel documentation of the SAM3X says
Up to 8-channel 16-bit PWM (PWMC) with Complementary Output, Fault Input, 12- bit Dead Time Generator Counter for Motor Control

But when I look at the analogWriteResolution() reference I can read
The Due has the following hardare capabilities:

  • 12 pins with 8-bit PWM (as the other AVR based boards)*
  • 2 pins with 12-bit DAC (Digital-to-Analog Converter)*

Here I talk about PWM pins, not DAC.

So, to sum up :

  1. Can Arduino Mega produce PWM with 10 or 12bits resolution? at what frequency ?

  2. Can Arduino Due produce PWM with 12 or 16bits resolution? at what frequency? How do we do this?
    On Due if I write

analogWriteResolution(12);
analogWrite(PWMpin, 3017);

Will I really get a PWM with duty cycle 3017/4096 ?

Thanks for your answers

The fastest way would be to read the datasheet. The pwm module is very easy to configure.

Hi,
You were pointing in the right direction in your first post - 65535 * 30K = faster than a Mega and also faster than a Due.

As the resolution gets higher, you need much faster PWM Clocks that any of the Arduinos will get you.

Out of interest, why do you need PWM as opposed to a DAC which does not suffer from the clock limitation ?

Duane B

rcarduino.blogspot.com

Hello,

dhenry:
The fastest way would be to read the datasheet. The pwm module is very easy to configure.

Oh, is it really so easy ? Indeed I can't tell as I don't know anything about the Atmel SAM3X. But I've made some research on the forum and it seems that several people have spent some time on it, and it's not so simple to make an easy-to-use librairy. Though you are write I will try to look in more details at the datasheet... :slight_smile:

DuaneB:
You were pointing in the right direction in your first post - 65535 * 30K = faster than a Mega and also faster than a Due.

As the resolution gets higher, you need much faster PWM Clocks that any of the Arduinos will get you.

Out of interest, why do you need PWM as opposed to a DAC which does not suffer from the clock limitation ?

Yes I understand this limitation of "frequency*resolution < clock", but first I want to be able to configure PWMs as needed. By default it's 1khz 8bit on the Due, for example I would like 4khz 12bit resolution.
I checked that analogWriteResolution() doesn't change anything concerning this, as expected.

Why PWM ? for at least 2 reasons :

  • there are more PWM pins than DAC on an Arduino board... and I need probably about 10 output of this kind
  • the hardware I decided to use "after" the Arduino is now designed for receiving a pwm, as it was done using Arduino Mega.
  • I reserve the 2 DAC of the Due for some other use ...

If you really need that high resolution of pwm, you may try analog sampling, which, in theory, has infinite bits of resolutions. It uses a flip-flop, a few resistors + capacitors, plus a clock (which can be generated by yours arduino).

Hello,

Today I ate quite a lot of ATMEL SAM3X datasheet and indeed, after focusing on this PWM subject for a couple of hours, we can do nice programming of PWMs on the Due !!

I diretly configured SAM3X registers, it may be "brutal" and sometimes unappropriate, anyway I put here some draft code, it could help someone :

    // Let's say we want to configure PWM channel 0, PWML0 which is on pin34 Due board, and corresponds to the port C.2

    // First the peripheral must be activated instead off the general GPIO
    REG_PIOC_PDR = 4;
    // Moreover it's peripheral B (not A), which means we have to change PIO_ABSR[2]
    REG_PIOC_ABSR = REG_PIOC_ABSR | 4; 
    
    // Now concerning PWM settings...

    // activation of the clock dedicated to PWM peripheral (id36), which is fifth bit of PMC_PCSR1
    REG_PMC_PCER1 = REG_PMC_PCER1 | 16;
    
    // activation of PWM_ENA register with channel 0
    REG_PWM_ENA = REG_PWM_SR | 1; 
    
    // setting the period (which can take any 16 bit value...)
    REG_PWM_CPRD0 = 5000; 
    // note : with this 5000 setting, we have thus 1/5000 resol, with a frequency of 16khz, if we use the defaut main clock : that's good
 
    // Finally, setting the duty cycle to the value you want (between 0 and the period, of course)
    REG_PWM_CDTY0 = 1000;
    
    // And here we can check at the oscilloscope that we get a nice PWM on pin 34, with a period of 60usec... nice!

Now I have one question :

Why do the PWM pin are indicated on pin number 2 to 13 on arduino boards !! ?
It seems that the 8 true PWM channels are not these ones... for example here I used the channel 0, which can be found on pin 34, 35 and other pins...

And one other interesting remark :
We can actually reach very nice resolution, especially if we use low (or high) pwm commands. For example, the hardware I use with this pwm is highly non linear, and I need to be precise with low duty cycle.
As I don't really care about precise frequency (let's say anything between 8khz and 20khz is fine), I can modify the period as well as the duty cycle.
So, from the duty cycle 1/5000, I can go to 2/5000, but also I can use 1/4990, 1/4900, etc.
And it works well.

Hope this will be usefull for someone.

dammien:
Hello,

Today I ate quite a lot of ATMEL SAM3X datasheet and indeed, after focusing on this PWM subject for a couple of hours, we can do nice programming of PWMs on the Due !!

I diretly configured SAM3X registers, it may be "brutal" and sometimes unappropriate, anyway I put here some draft code, it could help someone :

    // Let's say we want to configure PWM channel 0, PWML0 which is on pin34 Due board, and corresponds to the port C.2

// First the peripheral must be activated instead off the general GPIO
    REG_PIOC_PDR = 4;
    // Moreover it's peripheral B (not A), which means we have to change PIO_ABSR[2]
    REG_PIOC_ABSR = REG_PIOC_ABSR | 4;
   
    // Now concerning PWM settings...

// activation of the clock dedicated to PWM peripheral (id36), which is fifth bit of PMC_PCSR1
    REG_PMC_PCER1 = REG_PMC_PCER1 | 16;
   
    // activation of PWM_ENA register with channel 0
    REG_PWM_ENA = REG_PWM_SR | 1;
   
    // setting the period (which can take any 16 bit value...)
    REG_PWM_CPRD0 = 5000;
    // note : with this 5000 setting, we have thus 1/5000 resol, with a frequency of 16khz, if we use the defaut main clock : that's good

// Finally, setting the duty cycle to the value you want (between 0 and the period, of course)
    REG_PWM_CDTY0 = 1000;
   
    // And here we can check at the oscilloscope that we get a nice PWM on pin 34, with a period of 60usec... nice!




Now I have one question :

Why do the PWM pin are indicated on pin number 2 to 13 on arduino boards !! ? 
It seems that the 8 true PWM channels are not these ones... for example here I used the channel 0, which can be found on pin 34, 35 and other pins...

And one other interesting remark :
We can actually reach very nice resolution, especially if we use low (or high) pwm commands. For example, the hardware I use with this pwm is highly non linear, and I need to be precise with low duty cycle.
As I don't really care about precise frequency (let's say anything between 8khz and 20khz is fine), I can modify the period as well as the duty cycle.
So, from the duty cycle 1/5000, I can go to 2/5000, but also I can use 1/4990, 1/4900, etc.
And it works well.

Hope this will be usefull for someone.

You did a nice job. I have tested your code. Works fine. Please help me in changing the frequency of PWM. I need low frequency of 20Hz to 50Hz.

What do you mean by 16-bit PWM ? A PWM signal is either on or off. Thats 1-bit.

If you are considering some kind of audio synthesizer, then "16-bit" makes sense, but it's not really PWM then.

Or else i am confused.

Can anybody please help me to generate a pulse with period of 20ms (50Hz). I am trying to use the above code, but i am not getting the result.
Actually i want the pulse to be on for 1.5ms and off for 18.5ms.

 // First the peripheral must be activated instead off the general GPIO
    REG_PIOC_PDR = 4;
    // Moreover it's peripheral B (not A), which means we have to change PIO_ABSR[2]
    REG_PIOC_ABSR = REG_PIOC_ABSR | 4; 
    
    // Now concerning PWM settings...

    // activation of the clock dedicated to PWM peripheral (id36), which is fifth bit of PMC_PCSR1
    REG_PMC_PCER1 = REG_PMC_PCER1 | 16;
    
    // activation of PWM_ENA register with channel 0
    REG_PWM_ENA = REG_PWM_SR | 1; 
    
    // setting the period (which can take any 16 bit value...)65536
    REG_PWM_CPRD0 = 3281;  //84000000/512/50hz
    // note : with this  setting, we have thus , with a frequency of 50hz,  
    // Finally, setting the duty cycle to the value you want (between 0 and the period, of course)
    REG_PWM_CDTY0 = 246;      //7.5% duty cycle(7.5*3281/100)

Instead i am getting period in us.
Any help will be appreciated.

Thanks//

To get 16bit resolution on a PWM output, you have to have a 16-bit counter. (more or less. The idea of changing the clock rate when using longer time intervals would work. I guess you'd get prescaler_bits*timer_bits of overall resolution, with possible holes due to not all prescaler variables being available.) The Uno only has one 16bit timer, and the MEGA only has 4 (Timer/Counter 1, 3, 4, and 5; I don't know if those are the ones brought out to pins.) It looks like the sam3x has a dedicated "PWM controllers" (w 16bit counters) rather than driving PWM off of more general purpose timers, but I'm not very familiar with the ARM chips.

Why do the PWM pin are indicated on pin number 2 to 13 on arduino boards !! ?

Someone decided that it would be nice to group the PWM pins "together" as board outputs. The board pin-numbers are completely unrelated to chip pin numbers, and reflect some combination of "logical grouping" and "ease of PCB routing."

michinyon:
What do you mean by 16-bit PWM ? A PWM signal is either on or off. Thats 1-bit.

If you are considering some kind of audio synthesizer, then "16-bit" makes sense, but it's not really PWM then.

Or else i am confused.

No, 16 bit refers to the resolution of the output duty cycle, which for a 16 bit counter can
be 16 bit (actually a tiny bit more since a 16 bit counter can support 2^16+1 duty
cycles from 0..2^16 inclusive.

You can increase the resolution of PWM above the theoretical maximum by accumulating
truncation errors and alternating between neighbouring true PWM values, but
at the expense of an interrupt firing on every PWM cycle, something like this:

volatile int desired_pwm ;  // lets say 16 bit
volatile int error = 0 ;

void pwm_int ()  // this must run every pwm cycle
{
  int actual_pwm = (desired_pwm + error + 128) >> 8 ;  // limited to 8 bits
  error += desired_pwm - (actual_pwm << 8) ;
  PWM_REG = actual_pwm ;  // whatever hardware register controls the PWM
}

This is just like the dither code used to generate high colour resolution on a
limited palette colour display.

There are more sophisticated ways to do the above "dithering" that reduce
low frequency spurious signals, known as "noise shaping".