This example shows how to generate analog DC signal (voltage level). Usual way to generate analog signal is to use an external DAC. The same results can be achieved using only limited number of external components and PWM wave.
If we filter (or “smooth“) the PWM signal, the output of the filter is an analog signal.
The level of the analog signal is equal to RMS (root mean square).
RMS for AVR PWM signal (or any square wave) is equal to:
V – power supply voltage, usually 5V
p – pulse width
T – period
For a typical DAC application PWM signal frequency is one of the most important parameters.
Generation of voltage level is rather an easy task, so any timer, any PWM mode and almost any frequency can be used.
Let’s assume we use timer0 with the maximum possible frequency.
Timer0 is 8 bit timer, maximum frequency is available in Fast PWM mode. For 8MHz system clock this is 31.25kHz.
Period T=1/31.250Hz= 0.000032s (=32µs).
If we set 60% duty cycle, pulse width p=0.6*0.000032s=0.0000192s (19.2µs).
For 60% duty cycle and 31.25kHz PWM frequency RMS=5V*(0.0000192/0.000032)= 3V
Because duty cycle is equal to pulse width/period, RMS can be computed as
Note that RMS does not depends on PWM signal frequency, so any frequency can be used
(but only to generate voltage level, for other purposes frequency must be carefully chosen).
RMS and thus analog output level directly depends on duty cycle. To set voltage level on output, duty
cycle must be changed.
Duty cycle is controlled by OCR0 register. Relation between OCR0 and output analog voltage is:
RMS=OCR0*5/256 = 0.0195*OCR0.
Output signal can be adjusted with 8 bit resolution, the same as resolution of the timer.
As mentioned earlier, PWM wave must be filtered. There are many books how to design filters, but we use the simplest possible low pass RC filter. It consist only capacitor and one resistor.
Use R=220kΩ and C=0.1µF. In fact these values are not critical for this example, so similar elements can be used.
Assumed 8MHz system clock, ATmega32, timer0 for PWM generation
PB3 (OC0) pin must be configured as output
DDRB |= _BV(PB3); //OC0 pin as output
Set mode 1 for timer0 – Fast PWM
TCCR0 |= _BV(WGM01) | _BV(WGM00); //mode 1, Fast PWM
Non inverted mode of PWM
TCCR0 |= _BV(COM01); //Clear OC0 on compare match, set OC0 at BOTTOM
Prescaler divider 1 (maximum PWM frequency)
TCCR0 |= _BV(CS00); //prescaler divider 1
Set output voltage to 2.5V
OCR0 = 127; //50% duty cycyle
The sample code pwm_dac.zip changes output level from 0 to 5V (0.5V step) every second. Changes can be observed on voltmeter or oscilloscope.
|Compiler||AVR Studio + GCC|
|External hardware||resistor, capacitor|
Below is an oscilloscope display snapshot. Red line is an analog output, blue one PWM signal (OCR0=100, analog output is 1.95V).
Here is an oscilloscope snapshot for incorrectly designed RC filter (R=1kΩ, C=0.1µF), the output signal is not horizontal line as expected.