Roll Your Own Analog Input
Modern computers are inherently digital devices. They work with ones and zeros (although that hasn't always been the case). However, we live in a decidedly analog world, so many systems (including systems on a chip) have methods to generate and read different analog quantities.
I've mentioned it before, but these conversions usually take some form of my old algebra teacher's maxim: Take what you know and use it to figure out what you don't know. Granted, these days you typically buy analog inputs and outputs ready-made, possibly as part of your CPU, even. It can still be useful to understand how they work. In addition to theory, I'll show you a few simple Arduino-based demonstrations you can try.
There are many ways to coax a variable voltage output from a digital device. The classic method is to use an R2R network (see image below). The idea is that each bit contributes to the total output voltage (in this case, four bits).
These give excellent results, but there are a few problems. First, the resistor values can't vary much (high precision resistors). It is also a lot of wiring and consumes a lot of output pins to get any real resolution. In addition, since digital outputs tend to not supply much current, you will probably need larger resistors and a buffer amplifier on the output, further driving complexity.
There is an easier way to get voltage out of a digital pin that doesn't have these problems. It goes without saying, though, that it has its own set of problems. That mechanism is to use pulse width modulation.
Your office probably has a light switch that can turn the light on (1) or off (0). What would happen if you could turn it on for exactly one second and then off for exactly one second? The room brightness would average 50% of the normal value. With such a broad time gap, though, you'd notice it. What if you could flip the switch on and off at, say, 100 microseconds at a time? Then you'd just think the light was dim (in fact, some light dimmers work exactly this way).
By varying the ratio of on-to-off times (the duty cycle), you can go from full off (0%) to full on (100%) and anywhere in between. There are many ways to generate PWM (for example, see one of my earlier blogs). If you are driving a light or a motor, the inertia (or whatever the equivalent to inertia is in optics) will average the signal for you. But what if you really want a voltage?
The average voltage is the integral of the voltage with respect to time. And a capacitor just happens to integrate the voltage applied to it. A simple RC filter will do the trick (see Figure 2 below).
Figure 3 (below) shows a simulation of a 50kHz PWM signal at 50%. One drawback is obvious: The voltage doesn't immediately reach the 2.5V you expect. Also, the signal suffers from some ripple that gets through the RC filter. You can reduce the ripple with a bigger capacitor, but that will cause the time delay to get worse. Or you can reduce the capacitor or the resistor to decrease the wait time, but you then get more ripple. There's no free lunch. Still, a PWM output is useful in many cases.
What about analog input? There are many ways to do that too. Some super fast analog to digital converters use a series of comparators that compare the input voltage to a series of reference voltages. Imagine if you had reference voltages of 1V, 2V, 3V, and 4V. You could use four comparators and by examining the output, you could immediately tell if you had less than 1V (no comparators on), more than 4V (all comparators on), or something in between. This is fast, but wastes a lot of input pins.
A more usual approach is to use a single comparator and generate a changing voltage to compare it to using a digital to analog converter.
The ATMega CPUs have an analog comparator built into it (on pins D6 and D7 for plus and minus, respectively), and so the Arduino can work with this comparator. Of course, these CPUs also have an analog to digital converter built into it, but I wanted to demonstrate how a converter works, not just use one as a black box.
I didn't want to wire up an R2R network, so I decided to use the Arduino's PWM generation as a voltage reference. The ripple will make the conversion less accurate, but it is simple and it does work.
If you want to build the same test setup as I have, you'll need a 1uF capacitor, a 10K resistor, and a potentiometer. You'll also want a solderless breadboard to connect it all up, unless you want to build a permanent circuit. Any value over 5K should be fine for the potentiometer. You could also use a variable power supply. The other components aren't that critical either, but you may have to change the timing of the software depending on the values you choose. Make the following connections to your Arduino board:
Essentially this is creating the same RC circuit in Figure 2 and connecting it to the PWM pin (pin 9). The output goes to one side of the analog comparator (D6). The other side goes to the input voltage, which is taken from the potentiometer. With the right software, you should be able to turn the potentiometer and read the resulting voltage. You can verify the actual voltage with a meter or a scope and it should be within a few tenths of a volt.