Step 14: How the Pulse Width Modulation works
Electronically "taking the mean of a signal" can be translated to "passing it to a low-pass filter", as seen on the preceding step.
How does the Arduino generate a PWM signal? There is a really good tutorial about PWM here:
We will see just the points that are needed for this project.
In the ATMega328P there are three timers that can be used to generate PWM signals, each one of those has different characteristics that you can use. For each timer correspond two registers called Output Compare Registers A/B (OCRnx) that are used to set the signal duty cycle.
As for the ADC there is a prescaler (see image), that slows down the main clock to have a precise control of the PWM frequency. The slowed down clock is fed to a counter that increments a Timer/Counter Register (TCNTn). This register is continuously compared to the OCRnx, when they are equal a signal is sent to a Waveform Generator that generate a pulse on the output pin. So the trick is setting the OCRnx register to some value to change the mean value of the signal.
If we want a 5 V signal (maximum) we must set a 100% duty cycle or a 255 in the OCRnx (maximum for a 8-bit number), while if we want a 0.5 V signal we must set a 10% duty cycle or a 25 in the OCRnx.
Since the clock has to fill the TCNTn register before starting from the beginning for a new pulse the output frequency of the PWM is:
exempli gratia for the Timer 0 and 2 (8-bit) with no prescaler it will be: 16 MHz / 256 = 62.5 KHz while for Timer 1 (16-bit) it will be 16 MHz / 65536 = 244 Hz.
f = (Main clock) / prescaler / (TCNTn maximum)
I decided to use the Timer number 2 because
- Timer 0 is used internally by the Arduino IDE for functions such as millis();
- Timer 1 has an output frequency too slow because it is a 16-bit timer.
In the ATMega328P there are different kinds of operation mode of the timers, but what I wanted was the Fast PWM one with no prescaling to get the maximum possible output frequency.