Computing stuff tied to the physical world

From PWM to voltage

In Hardware on Jun 30, 2013 at 00:01

The Arduino library has always had an “analogWrite()” function, even though the ATmega doesn’t have any way to generate a varying voltage. So why the name?

Well, what most microcontrollers can do is generate a pulse-width modulated signal, also known as PWM. That’s just a fancy way of saying that the microcontroller periodically generates a pulse, of which the width can be varied under software control.

Here’s a diagram from the Arduino PWM tutorial page:


It’s very easy to generate an approximate voltage between 0 and VCC by simply adding a resistor and capacitor to act as low-pass filter:


That image came from an excellent article by Scott Daniels about this same topic on the website. Check it out for much more background information.

So how do these two little components turn PWM into a voltage?

To see what’s going on, we can tie the resistor to a PWM pin, call “analogWrite(N,128)” to set a 50% duty cycle, i.e. halfway, and then watch the result on the oscilloscope:


(the above was generated with a 10 kΩ resistor and 0.1 µF capacitor – see this calculator)

What you’d want to see, is a 1.65 V output. As you can see, the real thing is indeed centered around that value, but with a lot of “ripple” (yellow = PWM, blue = Vout).

The reason for this is that the PWM is charging and discharging the capacitor. The average value will be the ratio of on-to-off time against the digital voltage, i.e. 3.3V, so a 50% duty cycle will lead to half VCC, i.e. 1.65V. But it’s still charging & discharging.

Can we get a cleaner signal out, without that up-and-down wavering? Yes: by increasing the repetition rate, i.e. the PWM frequency (while keeping the on-off ratio the same). There’s a page on the Arduino site about this. Here’s the result, with a 64x higher rate:


The purple line is a third probe on the same output signal, but set to AC coupling and much higher sensitivity, to show that the ripple is still there, but much much lower.

Now I can get to the point of this post: my intention was to see if I could generate a clean sawtooth wave, to use as supply voltage (after amplification) for testing circuits at various VCC levels – as used a few times in recent posts. Here’s the code I came up with:

void setup () {}

void loop () {
  for (int i = 0; i < 256; ++i) {
    analogWrite(5, i);

The result is this:


And now there’s indeed a sawtooth, but as you can see, sudden changes like the down edge take some time to pass through the RC filter, somewhat messing up the signal.

No problem, if we increase the delay time in the above loop by a factor of 50:


Voilá – things are fine now. A pretty smooth voltage ramp from 0 to VCC every 2 seconds, all from a single digital output pin on the ATmega – we’ve created a DAC!