Introduction
There is plenty of jitter on signals generated using GPIO on the Raspberry Pi and many other computers, and this is due to the fact that Linux can context-switch user processes at any desired point in time dependant on its scheduler (for more information on this, see the Raspberry Pi GPIO Explained guide).
One solution is to use custom driver code, but this is a non-trivial exercise.
Another typical workaround is to offload time-sensitive I/O to external hardware such as an Arduino or (say) a hardware PWM generator for controlling servos or dimming lights more smoothly.
However, what about GPIO configured for input? I was curious if there was any way to improve it.
This short, experiment-based blog post very briefly discusses what can be done, and uses an example of building a voltmeter with the Raspberry Pi. It can be seen that with a few code-level methods it is possible to greatly improve GPIO input timing based measurements.
This is the experiment topology, it is described further below. The device on the left is a photodiode (with the usual anode and cathode connections like any diode) and it generates a small voltage whenever light falls on it.
Here is the experiment in 30 seconds:
Measuring Solar Cell Voltage
The Raspberry Pi GPIO Explained Guide shows examples for configuring inputs on the Raspberry Pi and using them to connect up switches. Code examples are provided in several languages. There is also a voltmeter project in that guide, which measures the frequency of pulses from a voltage-to-frequency converter integrated circuit. The integrated circuit generates a frequency proportional to the applied voltage.
The code relies on averaging to provide a more accurate result. It functions well, with very consistent measurements within a few percentage points of the expected value – not bad for a super-simple circuit.
I got my bread board ready, and connected up the input to a photodiodephotodiode. Photodiodes are small silicon devices that can be used as very miniature solar cells! Here it is in comparison to an LED:
Here is the breadboard layout:
For the full circuit diagram and details, see the GPIO Explained guide. The only difference is that I’ve added the photodiode anode connection to the circuit input, with the cathode connected to 0V. You could also optionally add a small 47nF capacitor across it, in case the room lighting has any flicker. The black square-shaped photodiode can be seen plugged in on the right side of the breadboard, close to the row marked 15. A clear photodiode (rather than a visible-blocking, infra-red pass filter version which is what you can see in the photo) would have been better for room lighting measurement, but it was all I had and it still worked.
I was expecting a voltage of around 250-350mV to be generated by this tiny photodiode/solar cell in normal room light (and this would cause the voltmeter circuit to generate a frequency of 2.5-3.5kHz), and I wanted to measure it using the Raspberry Pi. I could then log the voltage generated during the day, or see if someone switches a lamp on in the room, or place a couple of the photodiodes in series, and so on., as mini science experiments. But then I got curious about how to improve it.
The first addition was to write some code (which will be published in a few days after some tidying) to plot the measured voltage every second. Basically the code runs a small web server on the Raspberry Pi, and draws a graph if anyone types the IP address of it into their web browser. I can view it on any web browser running on the Raspberry Pi, or my laptop, or mobile phone. The x-axis shows in seconds how long the experiment has been running, and the y-axis shows the measured voltage. This was the result in normal home lighting conditions:
Stressed Out
There is also a nice stress test program that can be installed (type sudo apt-get install stress ), and you can use it to provide the cores on the Raspberry Pi with useless time-consuming things to do. You can see the CPU consumption shoot up to close to 100% on any number of its cores, or all four cores if you wish.
To use it, this command stresses out all 4 available cores, for 60 seconds. Press Ctrl-C at any time to quit:
stress --cpu 4 --timeout 60
If you type top –d 1 in a command window, you’ll see a list of processes and their current CPU usage. Press ‘q’ to quit at any time.
Try running the stress program while the voltmeter is running, and the calculated voltage becomes very inaccurate because now the scheduler needs to make CPU time available for the stress program’s tasks (processes). Here is what occurs:
Sleeping, Averaging, Voting, Scheduling!
I experimented with a few tricks to try to get more consistent measurements.
One trick was to force a sleep before computing the frequency. The theory was that the scheduler might be unlikely to context-switch immediately after the sleep function, provided the sleep was long enough to allow the scheduler to enable some other process run. The theory failed - I didn’t get good results with this method; it seemed to have no useful effect.
Another trick was to still use averaging, but now also implement a ‘voting’ method. The plan of attack was that for slow-changing voltages, the code would calculate three sets of averaged values in quick succession, and then ‘average the averages’ by using two of the averaged values out of the set of three, and discard one out of the three.
But which two to choose? The chosen two would be the ones that are most agreeable, i.e. are numerically closest to each other. I figured I was in good company because this is approximately how the space shuttle computers worked. This voting method improved results somewhat!
Note that mathematically it would have been very useful to plot a probability density function from multiple results and see what the distribution reveals about scheduler impact to the measurements, but I didn’t explore this in the very short amount of time I had. It would be an interesting experiment though.
The next method was to attempt to influence the scheduler; it is possible to dynamically influence the priority and scheduling algorithm on-the-fly with Linux. I selected a policy called SCHED_FIFO and set the priority to the highest value. Here is the C code that can perform this:
#include #include #include #include int main(void) { struct sched_param sp; int ret; sp.sched_priority=99; // (0..99) highest priority is 99 ret=sched_setscheduler (getpid(), SCHED_FIFO, &sp); if (ret!=0) { printf("Error setting scheduling policy\n"); } // rest of your program return(0); }
The results were much improved (see screenshot below). With great power comes great responsibility, and it is strongly advised that code runs to completion quickly (or changes the priority again dynamically) and that code should be simple enough that there is low risk of it hanging for a long time.
Summary
No high-end instrumentation would do this, but if you want to use Raspberry Pi inputs for home-science-experiment-grade timing applications, consider scheduler, averaging and voting techniques! It was a lot of fun to measure the small solar cell voltage with such a simple circuit connected up to the Pi, logging away.
Top Comments