Hi, everyone, I am confused by how to extend pwm to 10 bit. It looks like using timer1 library could do this. Is this real? Thank you for help
Read about Timer 1 in the datasheet:
"The PWM resolution for fast PWM can be fixed to 8-bit, 9-bit, or 10-bit, or defined by either ICR1 or OCR1A. The minimum resolution allowed is 2-bit (ICR1 or OCR1A set to 0x0003), and the maximum resolution is 16-bit (ICR1 or OCR1A set to MAX). "
johnwasser:
...the maximum resolution is 16-bit (ICR1 or OCR1A set to MAX).
What value is MAX? Is it a constant? That means that if OCR1A is set to MAX, I can do something like: analogWrite(9, 1013);
What is the corrolation of the value of OCR1A and the bit resolution of the PWM (e.g. how to set it to 5-bit, 6-bit, 7-bit, etc.)?
What value is MAX?
For a 16-bit timer MAX = 0xFFFF. (for an 8-bit timer MAX = 0xFF)
Is it a constant?
Yes, for any given Timer.
That means that if OCR1A is set to MAX, I can do something like:
analogWrite(9, 1013);
No, the analogWrite() works with 8-bit PWM only. You have to set the registers yourself to get 16-bit PWM.
What is the corrolation of the value of OCR1A and the bit resolution of the PWM (e.g. how to set it to 5-bit, 6-bit, 7-bit, etc.)?
If you set TOP to 0xFFFF you get 16-bit PWM.
If you set TOP to 0x7FFF you get 15-bit PWM.
If you set TOP to 0x3FFF you get 14-bit PWM.
If you set TOP to 0x1FFF you get 13-bit PWM.
If you set TOP to 0x0FFF you get 12-bit PWM.
If you set TOP to 0x07FF you get 11-bit PWM.
If you set TOP to 0x03FF you get 10-bit PWM.
If you set TOP to 0x01FF you get 9-bit PWM.
If you set TOP to 0x00FF you get 8-bit PWM.
If you set TOP to 0x007F you get 7-bit PWM.
If you set TOP to 0x003F you get 6-bit PWM.
If you set TOP to 0x001F you get 5-bit PWM.
If you set TOP to 0x000F you get 4-bit PWM.
If you set TOP to 0x0007 you get 3-bit PWM.
If you set TOP to 0x0003 you get 2-bit PWM.
Be sure to set the registers to use OCR1A for TOP. Of course it doesn't make much sense to use a 16-bit timer if you only need 5-bit PWM. Use an 8-bit timer (Timer 2) instead.
John I Know I am asking for trouble here... but how? it is I believe 10 bits now 2^10 = 1024??? and the other resolutions for faster coarser conversion and how to apply them would be found where?. I will Not ask for a freebie answer but would be happy to read the applicable text.
Doc
There is not free lunch. While you can increase your resolution you are also decreasing your frequency. The steps have not change you just added more of them.
Now you can change the step size and frequency by adjusting the prescaler but I don't know the details.
You will find a lot of reading in http://www.atmel.com/Images/doc8271.pdf
While you can increase your resolution you are also decreasing your frequency
Agree.
To OP: what is the application? How "fast" your PWM has to drive?
Have you considered using Pulse-DENSITY-Modulation (using delta-sigma modulation)?
This gives a much more elegant solution to the resolution/frequency trade-off.
Suppose you have 3-bit PWM. That is, you have 8-steps. Note each interval step with 0/1:
0000-0000 completely off
0000-0001 'minimal on' = 1/8 intervals activated
0000-1111 50% on
0000-0011 a little more on
...
1111-1111 fully on
(the '-' is just for readability)
To increase the resolution with 1 bit you have to double the number of interval steps:
0000-0000-0000-0000 completely off
0000-0000-0000-0001 the new 'minimal on' = 1/16th
0000-0000-0000-0011 = 1/8th, but the whole cycle now takes twice as long as previously
0000-0000-1111-1111 = 50% on, but again, the cycle takes twice as long
each time you increase your resolution, the cycle takes twice as long (with the same interrupt frequency)
This results in a trade-off between the max duration of a full cycle and resolution. If your system is slow, you could use a much larger cycle. If it's fast you can't unless you use a low-pass filter.
Take the 50% on cycles for the 3bit and 4bit system:
3bit 0000-1111 which repeats to get: 0000-1111-0000-1111-0000-1111-0000-1111
4bit 0000-0000-1111-1111-0000-0000-1111-1111-0000-0000-1111-1111
you could use a 2bit system and get the same result of 50% on
01-01-01-01-01-01-01-01... which is even nicer with a shorter cycle!
This is what you would get with PDM: 50% of the cycles are turned on. The nice thing is that you don't have to pick a full cycle duration, you automagicly get the optimal spread of your 'on' values among all steps.
For example:
50% gives you 01010101
75% gives you 11-01 repeating... every 4 steps, there's an extra 1
87,5% gives you 11-11-11-01 repeating... (note that it's 11-11 = 100% and 75% = 11-01, avg = 87,5%)
you could use an arbitrary precise number and the slower the response of your system, e.g. your eye with a led or so, the closer you'll approximate this number. Unlike with PWM you don't have to choose the resolution/cycle trade-off, it's always optimal. Your system averages the values of the PDM, it's a low-pass filter.
It's close to 1-bit encoding at a high frequency such used with SACD's. Best of all, it's super easy to implement. If you ever implemented a PID loop, it's identical to the I part of the loop.
Here's example code:
#include <TimerOne.h>
float errorSum = 0;
float SetPoint = 0.0005;//1 is full on, 0 is off
void setup()
{
pinMode(2, OUTPUT);
Timer1.initialize(10);//this is in nanoSeconds
Timer1.attachInterrupt(timer);
}
void loop()
{
}
void timer()
{
if (errorSum < 0 ){
digitalWrite(2,HIGH);
errorSum+=1-SetPoint;
}else{
digitalWrite(2,LOW);
errorSum-=SetPoint;
}
}
Beau83:
Have you considered using Pulse-DENSITY-Modulation (using delta-sigma modulation)?
...
Very nice solution. Could you do the same thing using integers? I'm thinking, and I could be wrong, but floating point math is slower than integer math and you want to keep time in an interrupt service routine as short as possible.
RandallR:
Beau83:
Have you considered using Pulse-DENSITY-Modulation (using delta-sigma modulation)?
...Very nice solution. Could you do the same thing using integers? I'm thinking, and I could be wrong, but floating point math is slower than integer math and you want to keep time in an interrupt service routine as short as possible.
Sure you can. This would give you 15bit resolution (16bit int, but it's signed):
#include <TimerOne.h>
int maxValue = 32767;//max value for int
int errorSum = 0;
int SetPoint = 1000;// SP in % = SP/maxValue
void setup()
{
pinMode(2, OUTPUT);
Timer1.initialize(10);
Timer1.attachInterrupt(timer);
}
void timer()
{
if (errorSum < 0 ){
digitalWrite(2,HIGH);
errorSum+=maxValue-SetPoint;
}else{
digitalWrite(2,LOW);
errorSum-=SetPoint;
}
}
(disclaimer: I just changed the previous code without compiling/running it, but this should work)
If your system reacts slow / big low-pass filter ( e.g. you're controlling temperature of something) and you want to be ultra-precise, you could use long instead of int... but I don't think any real world application would benefit from that.
PS : I'm using it to control temperature by switching a SSR to activate a heating element, which uses AC. You can only switch AC when it's at 0V, i.e. every half cycle. Otherwise you get high voltage spikes, nasty interference stuff. The SSR takes care of only switching at 0V, but this means you're limited to interrupts at 100Hz in Europe, or 120Hz in the US. (at least in theory, I should check if this scheme works correctly with a scope. I 'tested' it by connecting a lightbulb and it seems to switch correctly.)