Stabilizing analog read on a potentiometer - Page 1
Forum section: electronics / projects

author: Etesla
author_rank: Regular Contributor
timestamp: July 19, 2019, 05:18:57 pm
content: Hi all, I am trying to address an issue that pops up from time to time. When I use an ADC on a microcontroller to read a potentiometer, I find that more often than not, the ADC reading fluctuates a lot. For me, this is problematic. What are some ways to minimize ADC reading flickering? I believe that the solution will be in software, as no amount of filtering or caps across the potentiometer seem to help in the case where I have the pot adjusted right between two ADC steps. Is there a common sort of algorithm designed to deal with this, I imagine, fairly common issue? I'm thinking of some sort of averaging and buffer type stuff, but I don't want it to get too complicated. Thanks in advance!p.s. I would have gone with an encoder, but the PCB is already made with the potentiometers, and I don't have time to make a new one

author: guenthert
author_rank: Frequent Contributor
timestamp: July 19, 2019, 05:55:07 pm
content: I see there two distinct issuesi) fluctuations in the value reported by the ADC regardless of the source (typically referred to as "noise")ii) fluctuations in the voltage when using the potentiometer as a voltage divider.For i) check your ADC's (or microcontroller in this case) manual for noise specifications. What you observe might be well within the specs. The manufacturor might have also recommendations on how to minimize the noise (e.g. seperate analog and digital GND, turning off other facilities of the MCU during conversation, etc.). And yes, this can be addressed also in software. Easiest might be to throw away the lowest bits. If your MCU has a 10b ADC, but you need to distinguish only some 100 potentiometer positions, you can mask the lower three or four bits. With any luck, that's all you need to do. Then there's of course averaging and decimation. Lots of info (and application notes) about that. Keep in mind that the noise is distributed across a wide spectrum, but the potentiometer is going to be turned only so fast --> digital low pass filter. Google is your friend.ii) if the potentiometer isn't turned, there shouldn't be any noise (other then Johnson noise of course). There might be some noise (non-monotonic change of potential at the wiper) when turning. That can be reduced by connecting a capacity between the wiper and one of the other terminals.

author: Kleinstein
author_rank: Super Contributor
timestamp: July 19, 2019, 06:08:24 pm
content: Unless there is something really wrong (like missing decoupling, to high a pot value) there should not be much noise in reading the potentiometer. How much depends on the µC. Normally the pot should use the same reference as the ADC - so both using VCC or both from an external reference.The usual way to reduce noise is filtering in software, like averaging over several readings. Usually averaging 4 reading could reduce the noise by a factor of 2. Averaging over a multiple if a power line period (e.g. 16.6 ms or 20 ms) can give additional suppression of power line related hum - however with just a pot this should normally not be an issue.

author: Nominal Animal
author_rank: Super Contributor
timestamp: July 19, 2019, 06:58:34 pm
content: It is possible the ADC is flipping between two results, because the voltage it measures is between those two values.Let's say the potentiometer value used isV. SumNpotentiometer samples at regular intervals intoS. IfS<= (V-1)×NorS>= (V+1)×N, updateV=S/N; otherwise keepVunchanged.This means that the value you use for each potentiometer only changes everyNsamples. You'll need at least a 16-bit variable for each potentiometer, and you might want to use a power of twoN(16, 32, 64, 128, or 256) to keep the division simple. (It also biases zero over 255, as it rounds towards zero. That is fixable.)

author: duak
author_rank: Super Contributor
timestamp: July 19, 2019, 07:40:00 pm
content: What microcontroller or A/D are you using? How many LS bits of noise are you seeing?Do some values seem to be sticky or more common? If the A/D is a successive approximation converter without using a Sample and Hold in front of it will tend to give a more noisy data stream.Others have suggested stronger or more effective filters by averaging a number of samples and may be needed to get around noisy data.A code tragment implementing a simple low pass filter with characteristics similar to that of a first order filter is:static int result = 0;result = (GetAtoDSample() + result) >> 1;What this does is take the newest sample and adds the previous value and then divides by two. The previous value is of course half of the previous sample plus half of its previous value, and so on. If I remember my constants correctly, an RC network is equivalent to the sum of 63.2 % of the sample and 36.8 % of the previous value but that's a bit harder to do with simple adding and shifting. Note that the variable result has to be persistant so that it can keep a running average of the samples.

author: patrick1
author_rank: Contributor
timestamp: July 19, 2019, 07:55:15 pm
content: sounds painful too try and solve via software, as pot deterioration over time will just make the problem worse irrespective of software.really i would recommend, playing around too find a transistor setup, that you are charging a larger cap via resistors / transistor. the analog guys can hopfully help with this.

author: Sterno
author_rank: Contributor
timestamp: July 19, 2019, 08:29:22 pm
content: I have had good luck filtering a noisy analog signal by taking 16 sucessive readings on the ADC, storing these in an array, summing the total and then shifting that result 2 bits to the right.If I recall, method is called "decimate and shift" and is said to preserve accuracy which can be lost through standard averaging.All depends on your required sample rateCode snippet from an Arduino Project below.void readPressure(){for (int i=0; i<16; i++){valA = analogRead(pressureSensor);delay (5);samplingArray= valA;}for (int j=0; j<16; j++){int x = samplingArray;valB = valB + x;}pressureReading = valB >>2 ;valB=0;}*edit Added details

author: IDEngineer
author_rank: Super Contributor
timestamp: July 19, 2019, 10:35:13 pm
content: Start by eliminating some variables. Remove the pot and connect the A/D input to a fixed voltage source, for example a resistor divider with a cap from the center point to ground. Two equal, reasonably low value resistors will give you a decently low impedance source from which to charge the sample/hold amplifier. Confirm with a scope that the center point isn't too noisy (or all you'll be doing is sampling noise).Convert that, and see what sort of behavior your A/D gives you. It should be reasonably stable, e.g. not wildly varying values. If this isn't stable, you have a problem in your circuitry, MCU-A/D configuration, or software. Work out those problems before reintroducing the pot.Once things are stable when you have a stable input voltage, THEN add the pot. Any new issues will be related to the pot because you'll have confidence that the rest of your system is working.Report back!

author: Etesla
author_rank: Regular Contributor
timestamp: July 20, 2019, 11:06:24 pm
content: Thank you all for your suggestions. I ended up trying averaging in software first since it came up a lot (using the 10 most recent readings, reading every 10 ms or so) and it worked a treat. I now get steady readings, and twisting the pot makes the numerical result have a cool interpolation effect where the number appears to approach and settle out to its final value. The problem I was describing I think only happened when the actual voltage being read was 'between' to ADC values, hence why adding capacitance didn't really do a whole lot. I might add some sort of hysteresis type stuff in software to make it even more robust against changing values, but for now this lightweight averaging solution is working great.

author: IDEngineer
author_rank: Super Contributor
timestamp: July 21, 2019, 02:41:34 am
content: Quote from: Etesla on July 20, 2019, 11:06:24 pmThat's how filtering works. It's not "interpolation", it's settling. Totally predictable and expected behavior.QuoteYou're saying the original "ADC reading fluctuatesa lot" was a single LSB? C'mon, it would be amazing if it DIDN'T fluctuate at least one LSB. That wasn't cause for concern, or comment, at all.

author: ajb
author_rank: Super Contributor
timestamp: July 21, 2019, 03:12:30 am
content: Two other factors two consider are sampling time and source impedance, especially when the ADC is reading multiple channels in sequence. Excessive impedance (IE, a high-value pot) and inadequate sampling time will mean that the ADC's sampling capacitor will not have adequate time to charge to the new input value before the ADC conversion is started. The datasheet should describe the sampling capacitance and from that and the source impedance you can determine the sampling time required for a given resolution.

author: Siwastaja
author_rank: Super Contributor
timestamp: July 21, 2019, 09:59:16 am
content: Two different things, with a common solution:1) ADC input impedance requirement. Say you have a 10k pot, acting as a series resistance. The ADC internally has a tiny (say, 20pF) sampling capacitor which is charged during a tiny sampling period (say, microseconds). It cannot charge completely through your high resistance during that time, so it shows someremainingcharge left from previous conversions, causing strange fluctuations.To solve this, you need a low-impedance driver, the options are:a) A very low-value potentiometer (hundreds of ohms max), drawing power all the time,b) opamp bufferc) capacitor (between the ADC input and ground) orders of magnitude larger than the ADC sampling capacitor - say 10nF to 100nF is often just right.The capacitor solution is obviously the cheapest and simplest (and the way to go), but the gotcha you need to know about is that you need to limit the conversion rate (i.e., don't leave the ADC freerunning at highest sample rate), so that the capacitor has time to replenish. Won't work with high-frequency stuff - works great with things you are going to sample slowly anyway (analog human interface devices, temperature sensors, battery voltages...)2) filtration, for any reason. Now, if you use the cap for providing low impedance, it doubles as an analog RC filter as well. Two flies with one stone! In this regard, using a larger cap than strictly required due to #1 might make sense. So go for values like 1uF, and so on.And,3) if you still have a problem, consider adding software filtration. An analog RC only works on short timescales (otherwise, MASSIVE capacitors would be required), but a software filter can easily work over seconds or minutes if you need to.A cumulative average (IIR filter) is easy:static int32_t flt = 0;flt = (15*flt + (int32_t)newest_val*256)/16;filtered_value = flt/256;(This example takes 1/16 (6.25%) of the newest value and 15/16 (93.75%) of the accumulated filtered value each time. Input value is multiplied by 256 and the result again divided by 256 to increase intermediate processing resolution.)I'm almost sure your actual problem is simply caused by #1. A very typical catch for the young players, and the datasheets won't always mention this low impedance requirement clearly because they assume you know it.

author: IconicPCB
author_rank: Super Contributor
timestamp: July 21, 2019, 10:25:19 am
content: In case of AVR ensure thevenin equivalent resistance is less than 10K.

author: David Hess
author_rank: Super Contributor
timestamp: July 21, 2019, 12:27:31 pm
content: You might be expecting too much. The settability of a common single turn potentiometer is about 1 part in 100 or roughly 7 bits. The best ones can achieve 1 part in 200 or roughly 8 bits. So if you are expecting more than about 7 or 8 noise free bits, then forget it.And this limitation gives a minimum goal for how much noise reduction should be done if you accept 7 or 8 bits at the most. If I was starting from a 12 bit conversion, then I would average at least 12-7+1=6 bits of resolution away or 2^6=64 samples. If you still have excessive noise after that, then look for sources in the hardware.Beyond that, various schemes can be used to "divide" the potentiometer's travel into zones using hysteresis which will prevent quantization noise. Modern test instruments often get this very wrong because few bother with human factors engineering.

author: mikerj
author_rank: Super Contributor
timestamp: July 21, 2019, 12:42:59 pm
content: Quote from: Sterno on July 19, 2019, 08:29:22 pmThis is using oversampling to generate more bits of resolution e.g. samplinge 16 times and you get 2 more bits of resolution, providing you have more than 0.5 LSB of noise present. This will not reduce noise in the final value, in fact it will most likely increase it.

author: Neomys Sapiens
author_rank: Super Contributor
timestamp: July 21, 2019, 04:55:32 pm
content: Another function that can help is a 'sliding window'. This would mean that you only use samples (the first, the last or the average) whose difference to each other stays within a certain amount.

author: langwadt
author_rank: Super Contributor
timestamp: July 21, 2019, 05:08:40 pm
content: Quote from: mikerj on July 21, 2019, 12:42:59 pmwith a few assumption on the noise such as a zero mean it can

author: IDEngineer
author_rank: Super Contributor
timestamp: July 22, 2019, 02:19:08 am
content: Quote from: David Hess on July 21, 2019, 12:27:31 pmExactly, which is why I said that a toggling LSB isn't unexpected at all. The pot could cause it, noise on the signal from the pot could cause it, his supply rails could cause it... the list is long.I haven't seen where he tells us the resolution of his A/D but I bet it's more than eight bits.

author: IDEngineer
author_rank: Super Contributor
timestamp: July 22, 2019, 02:21:40 am
content: Quote from: Etesla on July 20, 2019, 11:06:24 pmBTW, think in binary (or hex), not decimal. Unless you have a solid mathematical reason to average ten samples, try sampling eight or 16. The math becomes a matter of bit shifts instead of actual division, and processors are significantly more efficient at the former than the latter.

author: David Hess
author_rank: Super Contributor
timestamp: July 22, 2019, 03:22:03 am
content: Quote from: IDEngineer on July 22, 2019, 02:19:08 am12 bit microcontroller ADCs toggle a lot more than just the LSB even under ideal conditions.The potentiometer will be quieter than practically any microcontroller ADC but the potentiometer's limited settability places a limit on precision so there is no reason not to filter the ADC output down to 7 or 8 bits at best removing most or all of the noise.

author: ogden
author_rank: Super Contributor
timestamp: July 22, 2019, 08:18:23 am
content: Quote from: Siwastaja on July 21, 2019, 09:59:16 am^^^ This. 10k POT + 0.1uF, 10-20 samples/sec. No software averaging/integration whatsoever.Quote from: David Hess on July 22, 2019, 03:22:03 amRight. You just take 8 most significant bits out of ADC and you are done.

author: mikerj
author_rank: Super Contributor
timestamp: July 22, 2019, 09:08:49 am
content: Quote from: langwadt on July 21, 2019, 05:08:40 pmIf you have zero noise then a) oversampling won't get you any more resolution, and b) you don't need any noise filtering to start with.

author: langwadt
author_rank: Super Contributor
timestamp: July 22, 2019, 12:52:51 pm
content: Quote from: mikerj on July 22, 2019, 09:08:49 amzero _mean_

author: Nominal Animal
author_rank: Super Contributor
timestamp: July 22, 2019, 02:18:32 pm
content: langwadt is correct.While it might be unintuitive, it is easy to see how this happens if we apply a bit of math here. (Lemme know if you don't want me to post this kind of posts, BTW.)To describe the noise, we need to know two of its parameters: its mean (which we assume is zero here), and its standard deviationσ. The noise distribution does not need to be Gaussian, as we only useσas a measure of the noise magnitude: by definition, 68.3% of noise samples are withinσof its mean.When we average samples, zero-mean-noise tends to cancel itself. With anN-sample average, the standard deviation of the noise isσ/N.To work this out, letxibe the noiseless samples, andyibe the corresponding sampled noise, so that the actual measurementiis (xi+yi). When you take the average of thoseNactual measurements, you can split the sum into two, and get the mean of the noiseless samples plus the average mean of the noise samples. (If you do the same keeping the standard deviation labeling, using any notation you want, you'll see that averaging overNsamples, the standard deviation of the noise is indeedσ/N.)(In fact, if we know both the mean and the standard deviation of the noise, and the original signal is sufficiently stable (low change rate), the noise can be compensated to give a similar result, i.e. 1/Nreduction in the magnitude of the noise, if their dependency on the original signal is such that every unique measurement mean corresponds to a specific original signal mean. But you might get stuff like differential equations that have to be numerically solved to do that. It is useful with e.g. very sensitive low-temperature sensors, where the thermal noise can we well characterized and thus compensated for.)My own post above attempted to point out that if you use integer math (and with microcontrollers, you usually prefer integer math, unless your microcontroller has fast hardware floating point capabilities at sufficient precision), you'll want to do hysteresis and other similar analysis in the sum, prior to the division, because although the noise is higher, the added precision allows correspondingly more precise controls. My example shows that if your noise is ±0.5 bits (-1-0 or 0+1, or something in between) in the averaged value, with a bit of smart hysteresis in the sum you can keep the averaged value stable and noise-free.