THE STORY
You've heard it a million times: "The Arduino has analog inputs, why doesn't the Pi?". So, awesome hacker that you are, you decide to do something about it. But it wouldn't be any fun if there weren't some serious constraints. How about it has to be low cost, have low parts count, consist entirely of easy to acquire parts, and be simple to build even for novices? Something like PiFM, where the entire hardware interface is a piece of wire? (OK, a piece of wire is an over-simplification. It works, but you're supposed to add a filter consisting of something like 3 inductors and 2 capacitors or the FCC gets a bit grumpy.)
So you begin thinking: there is a well-known trick used with microcontrollers to measure analog values using a digital input. Here's how it works: you connect a capacitor from a GPIO to ground, and a resistor across the capacitor. You set the GPIO to output a one: this charges the capacitor. Then set the GPIO to an input, and the resistor will discharge the capacitor. All you need to do is measure the time for the input to go from a one to a zero. This works because a digital input can be used as a fairly consistent analog comparator, with a fixed threshold of approximately half the power supply voltage (for most CMOS devices). Typically this technique is used to measure the resistor or the capacitor. But you realize you can measure voltage if the the resistor is disconnected from the ground end of the capacitor and is connected to a voltage source instead. The voltage will, of course, have to be less than the input threshold voltage. But it CAN be below ground!
In microcontrollers it's easy to measure small units of time, but not so with the Pi. So you wonder: how can you sample a GPIO pin at regular intervals? Anything like a shift register will suffice, regularly sampling the input and combining the samples into a byte or word. And guess what, the Pi has two peripherals like this: PCM and SPI, which are both little more than shift registers.
However, there's a small problem: these peripherals can't change a GPIO from input to output automatically while sampling. But maybe there's a way to use another output to do this job? It could charge the capacitor through a diode while an output is high, and would have no affect when it's low. Then the sampling input could remain an input. How about if this reset signal was generated by the SPI output? It would just need to be fed the right data. And the SPI has to output data for the input to work anyway!
Next you review the design to see if things could be further improved. How about eliminating the capacitor? Since digital inputs have some parasitic capacitance associated with them, and the diode adds even more capacitance, maybe that will be enough. So you run some numbers to see if it will work: you pick a 1 megohm resistor (doesn't most test equipment have 1 megohm input resistance?) and take an educated guess that the input capacitance will be about 10 picofarads.
Using your old friend the RC time constant, and assuming a grounded input, you figure out that the capacitance will discharge to the Pi's threshold voltage in around 10 microseconds. This means the sample rate is around 100 kilohertz, which implies you will be able to measure anything lower than half that frequency. It looks like you'll be able to measure audio-range signals: not too shabby! But you realize you can do a little better: since the capacitance doesn't have to be charged up much past the threshold voltage, you can use two diodes in series. This has the effect of cutting the capacitance they contribute in half, decreasing the sample time.
And since the PCM peripheral is similar to SPI, you can add a second channel! Just use a second set of diodes from DOUT to DIN. You have to synchronize the PCM and SPI clocks, but you've done this before. And what's more, both peripherals can be serviced by DMA controllers! No processor intervention is needed during sampling. The only effect there might be...
Read more »
In case you didn't know, the SPI port of the PI (I tested on the old B) has a "feature" (I'm being generous). It does not send 8 but 9 bits ! IIRC, the last bit is repeated for a 2nd clock cycle (while the input value is transferred from the shift register to the data register). This makes your linearization step even more important but I'm not sure it accounts for the double weight of the 8th bit.
Finding this "feature" with the 'scope made me even more angry at Broadcom and the Pi Foundation. But in the end we get what we pay for...