baetis
Posts: 24
Joined: Tue Dec 22, 2015 6:19 pm

Using RPi GPIOs as Low Speed Analog I/O

Tue Oct 09, 2018 8:49 pm

I started an Raspberry Pi project a couple days ago. I wanted to reacquaint with the RPi and try out a few ideas.

Figured that it would be intesrting to see how good an analog signal I can make with an RPi gpio pin, a few resistors and capacitor. Then I want to read this analog signal with a RPi gpio pin, a resistor and capacitor and use the RPi to calculate a voltage measurement. I'm expecting about 1% accuracy (and hoping for something in the 9-10 bit equivalent range). The sample rate that I am targeting is one sample per second.

I plan to submit my documentation on this project as I go along, today's dump is how the design is shaping up after thinking things out a bit and finding my hardware and setting up a project bench.

The slide in the attachment was made to organize my thoughts and adapt to capacitor values that I had on hand.

Within the next few days there should be a post on how the analog output worked out and tested.
Attachments
rpiadc.png
rpiadc.png (35.07 KiB) Viewed 815 times

baetis
Posts: 24
Joined: Tue Dec 22, 2015 6:19 pm

Re: Using RPi GPIOs as Low Speed Analog I/O

Wed Oct 10, 2018 3:01 am

The process of building and testing my PWM analog out circuit took less time than expected and the reults of testing were pleasing.

I decided to start the pigpiod daemon with the 1us sampling rate instead of the 5us standard. I wanted to have at least 1000 steps in my PWM range at a frequency high enough to filter out with a 1uF capacitor and a parallel resistance below 1Kohm (I ordered 25 100uF capacitors from ebay that should be here next Monday for $3, at which point I will decrese my resistance values). So, 1000 steps range x 1KHz PWM frequency gives me the 1us sampling rate selected.

My resistance divider along with the output impedence of RPi gpio27 got me remarkably close to the desired 1.000V full range output voltage (0.993V) and I trimmed out the rest to get 1.000V full scale. My testing was done with 3 1/2 digit multimeter (should have calibrated to 99mV at duty=99 in retrospect).

Testing involved setting the PWM range (pigs pwm 27 <duty>) value at about 50 points along the curve. Since the duty cycle range is set from 0-1000 and the full scale voltage is 0.000-1.000V we are expecting our each step in our selected duty parameter to equal 1mV in output voltage.

The measured results were excellent with each tested pwm <duty> above 100mV (where I am limited to 3 digits and 1mV resolution) giving the exact expected result. Below 100mV my worst case measurements were off by -.0.1 - +0.3mV. I think I can conclude that the circuit is accurate within +/- 0.1% of full scale. This is within the circuit requirements to generate accurate test voltages.

Next step will be to start sorting out and setting up and programming to read notifications. I did this before a few years ago with some HC-SC04 Ultrasonic Range Transducers and some other projects.This will need to be working before analog input circuitry and programming can be tested.
Attachments
Slide2.PNG
Slide2.PNG (25.71 KiB) Viewed 789 times

baetis
Posts: 24
Joined: Tue Dec 22, 2015 6:19 pm

Re: Using RPi GPIOs as Low Speed Analog I/O

Wed Oct 10, 2018 3:44 pm

This post will touch on two topics: a) turning on and using notifications and b) gpio pin control latency.

a) The attached gpiod Notifications Cheatsheet pretty much walks through a list of notification commands, the structure of the data read from the notification pipe and some programming hints. In short, the function getc does work. If anyone actually cares about my perl script, I could pretty it up and post. You may be able to find code to perform this function in python on Joan’s pigpio web page and adapt it for your use.
rpiadc.png
rpiadc.png (19.68 KiB) Viewed 758 times


b) Lets take a quick look at the latency of our gpio commands. I am running a headless PRi 2 B+ running Raspian Jessie. When pigpiod is not running the command tops (part of `sudo apt install procps` package, run tops and then enter ‘1’) reports all four cpu cores at about 99+% idle, about every 5 seconds one core goes to about 20% system utilization for a while. Running pigpiod –s 5 consumes a pretty constant 5% user utilization moving around cores; not unsurprisingly, running pigpiod –s 1 consumes 25% of a core, but moves from core to core every few seconds.
Lets generate a pulse on a gpio pin and write a script to calculate the pulse width with notifications and a pigpiod sampling rate of 1us.
pigs w 17 0; pigs w 17 1;pigs w 17 0
Pulse width range of 50 pulses – 9.39ms to 13.98ms
pigs w 17 0 w 17 1 w 17 0
Pulse width range of 50 pulses – 174us to 254us
echo "w 17 0" >/dev/pigpio; echo "w 17 1" >/dev/pigpio; echo "w 17 0" >/dev/pigpio
Pulse width range of 50 pulses – 30us to 312us
echo "w 17 0 w 17 1 w 17 0" >/dev/pigpio
Pulse width range of 50 pulses – 21us to 24us
Looks like calling the pigs commands one at a time from a system terminal (or from a program) would not work for our application. Issuing a single pigs command call from a terminal (or program) would lead to a lot of inaccuracy in the pulse width measurement of the discharging capacitor that we are going to have to do, even with extensive averaging. I guess the third way shows use that what we split up isn’t necessarily but back together. Fortunately, directing the stdout directly with a single echo system call to the /dev/pigpio kernel module input will work good enough and represent only a couple 0.1% of error which could probably average out (or pick the median after removing mavericks – like the very infrequent wrap) and subtract the 22us. This error could maybe be reduced slightly by writing our program as a kernel module, we’re talking about living with an added random error of probably less than 0.1% vs a factor of maybe 100 in the development time between a script using pigpio and writing and installing a linux kernel module. Good enough other error rms effects will probably dwarf this one.

Next post will be about reading pulse width of capacitor discharge (after that, if the data works out, we will treat turning pulse width measurement into a voltage input measurement and calibration, followed by another on displaying measured waveform remotely maybe).

baetis
Posts: 24
Joined: Tue Dec 22, 2015 6:19 pm

Re: Using RPi GPIOs as Low Speed Analog I/O

Wed Oct 10, 2018 4:41 pm

Don't know if anybody cares, but, I started up pigpiod (as discussed in previous post) and pinned it to cpu core 0 -

sudo taskset -c 0 pigpiod -s 1 &

The following commands pulse width variation (caused by latency variations maybe) :

echo "w 17 0 w 17 1 w 17 0" >/dev/pigpio

actually increased. The base stayed pretty much the same but about 5% of pulses were up to 200usec longer.
So that didn't help.

Idahowalker
Posts: 371
Joined: Wed Jan 03, 2018 5:43 pm

Re: Using RPi GPIOs as Low Speed Analog I/O

Thu Oct 11, 2018 12:32 am

Something to consider.

On the measurement node. Between measurements, bring the RPi input to a high impermanence state or remove the connection by opening the connection (a bilaterial switch) to the cap with a electronic switch, also, open the input to the cap, and short the sampling cap to ground to get rid of the previous reading. Keeping the input to the RPi open, open the short to ground, close the input to allow the cap to sample, open the input, close the connection to the RPi and do the measurement.
Without knowing why you are deleting my postings, I will not know how...

baetis
Posts: 24
Joined: Tue Dec 22, 2015 6:19 pm

Re: Using RPi GPIOs as Low Speed Analog I/O

Thu Oct 11, 2018 2:13 am

exactly - but the RPi GPIO output has one of those switches built in. With the pigs commands you can put the pin in logic low output (pigs w <gpio#> 0) or logic high output (pigs w <gpio#> 1) or high impedance (pigs mode <gpio#> r).

The measurement node is isolated from the voltage to be measured by 100K ohm resistor (which could be larger), so we don't need an electronic switch to disconnect the input either. The impedance of the voltage input source will be close to 100ohms once I get my 100uF capacitors promised for Friday - so the difference in impedance will be about 1000x and affect the input voltage very little.

So the steps - at the end of the last measurement the GPIO is high impedance. We start the measurement by outputting a logic high directly to the capacitor which will quickly charge to Vcc. After a millisecond or so, we then tristate the GPIO effectively isolating it's output from the circuit and start the timer (we use a notification trick for this). The voltage on the capacitor will begin discharging through the 100K resistor to the voltage to be measured (the higher the input voltage is the longer it will take to discharge the capacitor voltage to logic low threshold). When the voltage reaches a logic low state we can read the time from when the tristate was applied to when the logic level went low and calculate the input voltage (very nonlinear - my next post will go through the equations for this part, probably tomorrow am).

I am kinda confident that this circuit will be as accurate as an 8bit ADC (~0.25%), I'm tring to keep design tradeoffs around <0.1% - and with not much wiring.

Thanks

Idahowalker
Posts: 371
Joined: Wed Jan 03, 2018 5:43 pm

Re: Using RPi GPIOs as Low Speed Analog I/O

Thu Oct 11, 2018 11:51 am

baetis wrote:
Thu Oct 11, 2018 2:13 am
(very nonlinear - my next post will go through the equations for this part, probably tomorrow am).


Thanks
The last time I put together a A:D converter, it looked a whole lot like this but without the LED's.
Image


You can be sure, I'll be following along.
Without knowing why you are deleting my postings, I will not know how...

baetis
Posts: 24
Joined: Tue Dec 22, 2015 6:19 pm

Re: Using RPi GPIOs as Low Speed Analog I/O

Thu Oct 11, 2018 9:26 pm

Before testing our circuit lets consider what we expect the results to be, we walk through this on the accompanying slide. The situation that we have is much like the capacitor discharge curve : Vcap = Vinit x exp (-t/RC), where the initial voltage,Vinit, is precharged to Vcc. The twist in our case is that were are not discharging to 0 Volts – we’re discharging to the input voltage that we are trying to measure, call it Vin. The capacitor voltage that we care about is at the point where the RPi GPIO input logic transitions from reading a high state to reading a low point. The RPi is at system ground, but the capacitor is not. The voltage across the capacitor will not be the logic threshold transition point, call it Vt. The voltage across the capacitor plus the input measurement voltage adds up to make the GPIO input voltage, so, the voltage across the capacitor at this point will be Vcap = Vt – Vin. The rest of the slide derives the form of the equation that we want in convenient variables.
rpiadc.png
rpiadc.png (91.69 KiB) Viewed 648 times
Next we will be collecting data and attempting to fit our expected curve with the data.

PS
I started collecting some data and have observed a slight problem. The input of the RPi GPIO has a lower impedance than what I wanted/hoped. Analysis of the little data that I have so far looks like a 50Kohm resistance to ground on the inputs. I’ll be looking at four paths forward to deal with this (not entirely unexpected) setback.
a) Go ahead with original plan – the measurements may not fit the curve very well, but I may be able to force fit the curve with brute force methods – hopefully not as drastic as a calibration lookup table. The are still thousands of microseconds resolution between 0v and 1v input variation in the pulse output (but about a third as many as I expected). Also the repeatablity looks better than a .2% sigma – so far not aweful.
b) Work the equation with the 50K resistor to ground as another variable (won’t do much for us as far as accuracy goes but might simplify the curve fitting equation). I do like having an equation from physics fitting the curve – shows that you know how it works.
c) Best/Worse option is to replace 100Kohm isolation with 3V only rail-to-rail op amp buffer. This will take a while to select the component, obtain parts and increase wiring, and it adds like 10x to hardware complexity – we are approaching the complexity of an i2c dac without offering any advantages.
d) Adding another GPIO into the circuit and reconfiguring the passive components might be worth taking a look at in the interim.

User avatar
OutoftheBOTS
Posts: 662
Joined: Tue Aug 01, 2017 10:06 am

Re: Using RPi GPIOs as Low Speed Analog I/O

Thu Oct 11, 2018 9:39 pm

interesting read.

Even if no one is posting on the thread doesn't mean no one is following.

baetis
Posts: 24
Joined: Tue Dec 22, 2015 6:19 pm

Re: Using RPi GPIOs as Low Speed Analog I/O

Fri Oct 12, 2018 4:16 pm

So, collected some data last night with the system pretty much as described in previous post, major exception was that the precharge capacitor was changed from 1uF to 0.1uF. The untouched data is shown in the accompanying slide.
rpiadc.png
rpiadc.png (104.39 KiB) Viewed 603 times

In looking at the notification data using the 0.1uF capacitor it was the case that many of the transitions were single events from logic one to logic zero. I figured, ‘oh great! all I got to do is calculate pulse width on first one to zero timestamp’, and that’s what I did and that is a major reason why the data on the page above is such a mess. When I started digging in there was a strong correlation between those measurements on the low end of the histogram in the previous page and threshold detection events that were prolonged with multiple transitions.

baetis
Posts: 24
Joined: Tue Dec 22, 2015 6:19 pm

Re: Using RPi GPIOs as Low Speed Analog I/O

Fri Oct 12, 2018 4:17 pm

(continued from last post) To illustrate this I replaced the 0.1uf with the 1uF and recorded all of the transitions for several measurement cycles (truth be told the reason I replaced the capacitor in the first place was because I didn’t like all of the logic transitions that I was getting with 1uF and wasn’t sure how to deal with them). The slide below shows 14 trigger detection events from 14 different measurement cycles and discussion.

rpiadc1.png
rpiadc1.png (94.37 KiB) Viewed 603 times

This is going to require more than a couple lines of code and some and rearchitecting the data flow of my program so I want to think about it for a while. Essentially, the solution will be a DSP algorithm to filter out noise on the Raspberry Pi’s Broadcom processor power rails which are messing up the input logic levels by millivolts.
I’ve pretty much given up on additional passive filtering because I figure the noise is internal to the processor (although the wiring could be neater – but its one Hertz), unless anyone has ideas…

Idahowalker
Posts: 371
Joined: Wed Jan 03, 2018 5:43 pm

Re: Using RPi GPIOs as Low Speed Analog I/O

Fri Oct 12, 2018 4:37 pm

baetis wrote:
Fri Oct 12, 2018 4:17 pm
I’ve pretty much given up on additional passive filtering because I figure the noise is internal to the processor (although the wiring could be neater – but its one Hertz), unless anyone has ideas…
Send the noise through a inverter and feed it back into the sample. Might want to send the sample through a buffer amp, using the same chip as the inverter , to delay the sample to match the inverter latency for better noise reduction. The outputs of the amps could then just be tied together.

Yup, not passive at all.
Without knowing why you are deleting my postings, I will not know how...

baetis
Posts: 24
Joined: Tue Dec 22, 2015 6:19 pm

Re: Using RPi GPIOs as Low Speed Analog I/O

Sat Oct 13, 2018 4:59 pm

When we left off last time we were talking about how to reduce the stream of transition notifications that you get while the capacitor is discharging. We figure that this is probably due to noise within the Rpi cpu package. The technique we were using used the first H->L transition to calculate pulse width and we were getting a +/-30% voltage accuracy, by collecting 10 samples and using the center value of the first transition measurements we reduced our measured error to approximately +/-2%.
The way that I decided to try to filter the waveforms showed a few posts back (‘Detecting Discharge Time End Point’) was to move a window across the incoming wave form and average the ones and zeros. The point that I selected as the reported H->L transition was where the average of the signal in the window moved from above 0.5 to below 0.5.
I used the data that I had from the 14 waveforms that were collected earlier and calculated the sigma of the filtered results. I experimented with how wide a window to use and found the error reached a good value at a window size below 60us.
The calculated sigma for the ‘first transition’ detection method on the 14 transitions netted us a sigma of 0.33%. The same data filtered over a 60us window netted a sigma of 0.072% - a 4.5 x improvement!
Error is typically expressed +/-3 sigma and the voltage error is on the order of twice the pulse width measurement (because the discharge non-linearity). So here’s what we can hope for :
Edge Detection Method Pulse Width Error Voltage Error
First H->L Edge +/-1% +/-2%
Window Filter +/-0.2% +/-0.4%
Next step is to collect and analyze a bunch of data.

baetis
Posts: 24
Joined: Tue Dec 22, 2015 6:19 pm

Re: Using RPi GPIOs as Low Speed Analog I/O

Tue Oct 16, 2018 6:48 pm

Over the weekend had to put stuff away until I could get back to it this am. At first I was having big problems reproducing my results until I noticed that the variation in my readings was much greater with PWM setting other than 0 and 1000. In other words lots of activity on other gpios made the reproducibility of measurements up to 10x worse, increasing loading on gpios also had big effect. This effect could perhaps be mitigated by better wiring and separating PWM from adc grounds – didn’t try this yet. The measurement repeatability was not affected much by hardwire ethernet connection, wifi ethernet usb dongel, mouse/keyboard usb dongel or hdmi output to terminal.
For the measurements presented in this post the input voltage is generated by a resistance divider (around 1k thevenin equivalent), and the pwm is shut off. The discharge node capacitance was also upped to 0.2uF. At a capacitance of 0.1uF about 90% of discharges gave a single notification occurrence, doubling the capacitance showed >99% of discharge h->l transitions reporting multiple notifications (mostly 10-100).
The data in the histograms below is raw data taken with Vin ~ 100mV. Each individual measurement took approximately 100msec.
Attachments
rpiadc.png
rpiadc.png (54.5 KiB) Viewed 489 times

baetis
Posts: 24
Joined: Tue Dec 22, 2015 6:19 pm

Re: Using RPi GPIOs as Low Speed Analog I/O

Wed Oct 17, 2018 9:24 pm

OK this post is where we do calibration and accuracy measurements….
While collecting data for calibration I stumbled on one rather pleasant surprise, and while fitting data another pleasant surprise.
The input voltage for our measurement was generated with a variable resistor between RPi 5.0V and GND, the center tap was adjusted for voltages starting at 0v with 0.1v increments as measured by my DVM. The average time for discharge was recorded along with the distribution sigma. The surprise was that the range, instead of the 0-1V that was designed for, extended way up to over 4volts – and presumably down to below several volts below ground. This is a positive affect of the 50Kohm to ground input impedance of the Rpi gpio pin. It turned out that at input voltages larger than 2.5V the windowing algorithm being used to average notification transitions started to take a long time and would have meant either slowing the measurements or changing the algorithm. Below GND the curve continues to get steeper and would affect the accuracy – so the calibration was done over the input voltage range of 0-2.5V. The recorded data was then plotted.
To calibrate the system we would like to come up with an equation that closely fits the data in the plot so that once we measure a pulse width we can calculate what the input voltage present at that pulse width measurement. I took my data to a website offering to fit my data to a wide range of equations – I did not get solutions that I liked for any of their polynomial or exponential functions. I decided to try the equation that was derived a few posts back of the form – Vin=Vcc*(Vt/Vcc-exp(-t/RC)/{1-exp(-t/RC)). I plugged in Vcc=3.3 and Vt/Vcc=0.5 and adjusted RC for a good zero crossing. By adjusting my value for Vcc, it turned out that a value of 8.8 fit the data very well. I was surprised when I realized the value of 8.8 is the value of Vcc times the discharge resistor divided by the gpio input impedance! Those following this thread will remember back to when the gpio input impedance became apparent, I decided go with first of four options - forge ahead and worry later, thinking that calculating the effect of the gpio impedance to ground would be complicated – turns out that it reduces very simply:
Vin=(Rin/Rgpio)*Vcc*(Vt/Vcc-exp(-t/RC)/{1-exp(-t/RC)) , where Rgpio is the 50K resitor to ground on the Rpi gpios and Rin is the discharge resistor in out circuit.
Also, very nicely the RC value that we curve fit is very very close to Cdis*(Rin||Rgpio), the discharge capacitor in our circuit time the parallel combination of the gpio resistance and the discharge resistor.

rpiadc.png
rpiadc.png (50.13 KiB) Viewed 450 times
Summary

Generating an analog output signal from a digital output is easy to do and in common practice, however, as a method to have a built-in self test for our analog input measurement it turned out a fail. It seems that the activity on the gpios, inherent with PWM operation, put too much noise on the measurement nodes effective h->l threshold voltage.

Using a GPIO with a resistor and a capacitor to measure analog voltage has actually turned out more robust and accurate than I thought likely when starting this project. We have demonstrated an ADC with a wide input range (may be -3V to 7V) with +/-0.5% accuracy over much of that range. Single measurements can be made at 10Hz while sacrificing accuracy to +/-1%. The circuit is easily calibrated (perhaps through automated two point calibration) and that calibration appears to depend mostly on resistor and capacitor values – which are most likely pretty stable over time/temp.

baetis
Posts: 24
Joined: Tue Dec 22, 2015 6:19 pm

Re: Using RPi GPIOs as Low Speed Analog I/O

Fri Oct 19, 2018 4:05 pm

Had a few hours this morning and did a couple things.
The program to extract the average h->l transition at pulse width end was taking a long time to run when you start getting 100's of 'bounces' at end of pulse width due to its low slope. The program had been implemented with (transition interval) x (window averaging period) calculations and I changed it to (transition interval) + 2*(window averaging period) speeding it up considerably and giving the same results.
A calibration plot is shown below where a 9v battery was used to explore a wider input range than you can get with RPi supplies.
calibrate-6-4.png
calibrate-6-4.png (14.41 KiB) Viewed 405 times
X axis is pulse width of discharge in usec and Y axis is input measurement voltage.

As you can see we start getting pretty soft with inputs above about 3.5v (the capacitor does not get below the gpio threshold detect value ever about about 4.2volts input). The fitting curve was the same as used in previous post but be be adjusted for better fit. I continued the negative voltage down to -6V and the data still fits the curve pretty well. It should be noted that with the components that were selected the slope down here is pretty steep which would be limiting the accuracy to around +/-2% (increasing the size of the capacitor would help this).

It looks as though with appropriate component selection a single gpio pin can be used as an analog input with:
100Kohm input impedance, +/-0.5% accuracy and an imput range of -5V to 5V with only a capacitor and a couple resistors external components.

Return to “Automation, sensing and robotics”