Saturday, November 9, 2013

Active low-pass filtering of Arduino's pwm output for a clean, flat, software adjustable signal.

The rather ill-named Arduino's analogWrite() function absolutely does not do what its name suggests: you'd expect it to set the voltage one of the output pins to the given value.

To do this you'd typically need something called a "digital to analog converter", or DAC in short, something the Arduino unfortunately does not come equipped with.

Instead, analogWrite() generates a PWM signal,  i.e. a square wave with "ON" and "OFF" timings designed such that the *average* voltage value is what you requested (the so-called duty cycle of the signal).

This is fine to - say - drive a motor, but certainly doesn't help if you want a clean, flat, noiseless signal.

In fact, the whole premise of PWM is the assumption that the loads it's ever going to drive have a built-in filter. In the case of a motor, the slow response times of the physical device is the filter in question.

But for some applications, PWM just does not cut it: sometimes, you'd like your Arduino to produce a clean, flat, noiseless constant voltage signal that can be controlled in software.

The standard RC low pass filter (such as the one described in this article) kinda works for this and is super easy to build, but it generates a fairly noisy output signal.

For example, below is the standard (490Hz / 50% duty cycle) PWM output of the Arduino fed through such a RC low pass filter:

Bottom line, the output is better than the PWM wave, but still awful choppy.

A larger cap, or maybe a multi-stage RC low-pass filter would of course help smooth out the noise further, but:
  • you might not have larger caps handy
  • you'll still get fairly fat ripple
Here's for example a 4 stage RC low-pass filter.
The output still has ripples:

Looking at the components I have lying around, I tried to put something together to produce a flatter, software adjustable voltage level from my Arduino.

Turns out I had a LM324N quad op-amp, a bunch of caps and resistors, generally enough to put together a four stage active low pass filter, with each stage based on the Sallen-Key Low Pass topology.

I didn't really spend much time doing theoretical analysis of the circuit to figure our caps values, but rather played with what I had lying around until I found a combo that worked well with the signal produced but the Arduino.

Here's what the circuit looks like:

Note: LTSpice does not come standard with models for the LM324, and if you want to play with the circuit above, you'll need to add two files to your LTSpice lib directory (mine happens to be in ~/.wine/drive_c/Program Files (x86)/LTC/LTspiceIV/lib/sym/Opamps , but YMMV).

Put this SYM file in lib/sym/OpAmps
Put this MOD file in lib/sub

This last circuit has a number of advantages:
  • The output is a lot cleaner than the RC filter, and cleanly maps the [0 - 255] range of the analogWrite() function to the full [0-5V] range.
  • Because it is a 12V powered, active circuit, it can easily be hacked  to produce 255 clean levels in [0- X volts] where X is the max output voltage of your OpAmps. All that's needed is a gain bridge on the feedback loop of the 4th stage, and if you are using an OpAmp that can goes rail to rail (which the 324N isn't), you can sweep the full 12V range.
  • I've tried to measure the theoretical output noise in LTSpice, and looks like it is in the 10µV range ... well below the noise level generated by my 12V power supply.
Here's what the thing looks like on a breadboard:

Note that:
  • the quad op-amp is the IC in the center
  • each of the stages is laid out vertically around the op-amp
  • I've added a 330Ω + LED load at the output.
Here's an oscilloscope screenshot of the signal output:

Good enough for me.

No comments :

Post a Comment