Projects for geeks

Simple DAC

11 Aug 2009

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 RMS=5V*0.6=3V.

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.

Code snippets

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

Sample code

The sample code changes output level from 0 to 5V (0.5V step) every second. Changes can be observed on voltmeter or oscilloscope.

Chip ATmega32
System clock 8MHz
Compiler AVR Studio + GCC
AVR hardware timer0
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.

Package icon pwm_dac.zip2.93 KB