Potentiometer hysteresis / "debouncing"

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Potentiometer hysteresis / "debouncing"

3,180 Views
PatrickT
Contributor I

Hey guys,

 

This is an old-as-the-hills embedded problem, but I can't find a solution around online so maybe I'm calling it the wrong thing?

 

I have an application (not that it matters, but MC9S12C and D family, CW 5.0 compiler) where I am trying to use a potentiometer for front panel control.  Of course there is a ton of jitter using the raw ATD data.  Is there a common hardware or software solution to smooth this out?  I can write an algorithm of course (I'm thinking something like taking the value most in the direction of travel over the last several reads) but would prefer if there's some boiler-plate type thing out there that's already been tested out.

 

Thanks very much :smileyhappy:

Labels (1)
Tags (1)
0 Kudos
11 Replies

1,634 Views
rocco
Senior Contributor II

Hi Patrick,

 

I have an algorithm that I've been using for 10 years. It works well on the noisy HC08 AtoD. It does the job in two steps.

 

First step is to filter the input. I use the exponential filter, the one that uses "a" and "1-a" as filter coefficients, because all I need is a simple subtract-shift-add for each new sample. The math is trivial, and it only takes 2 or three bytes of ram. I can supply HC05 or HC08 assembly-language code if it would help.

 

The second step is harder to explain, but it creates "detents" in the potentiometer, such that the result doesn't dither if the analog value is at the cusp of two digital values. The afore-mentioned filter holds the result in an integer+fraction form (one byte for the fractional portion, created by the shift). It basically weights that result toward the center (the .5 value) of the result, moving it away from the cusp, making the result less susceptible to noise. Again, I have some assembly-language code.

 

let me know if you would like a better description.

0 Kudos

1,634 Views
bigmac
Specialist III

Hello Patrick and Rocco,

 

For Rocco's filtering method, I think that the following expressions may represent a similar arrangement, which would be approximately equivalent to a single pole analog low pass filter function.

 

IfRdg is the current ATD sample, the filtered value Filt would be updated using the following expression:

 

Filt = (Filt * (N - 1) + Rdg) / N;

where N is a constant value that determines the effective time constant, in conjunction with the sampling rate.

 

If N is a power of 2, represented by the expression 2^i, the following alternative expression is equivalent to the one above, and might possibly be a little faster:

 

Filt = ((Filt << i) + Rdg - Filt) >> i;

 

I might potentially choose i = 3 or 4 (giving N = 8 or 16), but this will depend, to some extent, on the sampling rate and the nature of the measurement uncertainty.  Higher values may also result in sluggish operation of the control.

 

Maybe an alternative method of eliminating any dither would be to create a dead band between the ranges for each position, effectively providing hysteresis.  How many "detents" are you attempting to simulate within the rotational range?  Have you also considered the use of a rotary encoder?

 

Regards,

Mac

 

0 Kudos

1,634 Views
kef
Specialist I

Hi

 

Mac, unfortunately both these filter formulas don't work properly using integer math.

 

 Filt = (Filt * (N - 1) + Rdg) / N;

 Filt = ((Filt << i) + Rdg - Filt) >> i;

 

The problem is that fractional part of Rdg/N or Rdg>>i is lost, and filter may stick well below or well above Rdg. See attached Excel file. The bigger N is, the worse is sticking effect. You will need a lot of noise to cope over those "sticking barriers".

 

Rocco rearranged formula works better. Rdg is always fully accumulated into accumulator variable, filter doesn't stick, and truncation error affects only filter delays. Formula is made rearranging your equation like this

 

 Filt = (Filt * N  + Rdg - Filt) / N;

 Filt * N = Filt * N  + Rdg - Filt;

or

 Acc = Acc + Rdg - Acc / N;

 Filt = Acc / N;

 

Rocco, in fact your code is doing shift-sub-add-shift :smileyhappy:. You are returning hi byte of FIRvalue accumulator, which is FIRvalue>>8. But yes, one divide operation can be eliminated like this:

 

 Filt = Acc / N;

 Acc = Acc + Rdg - Filt;

0 Kudos

1,634 Views
rocco
Senior Contributor II

 


kef wrote:

Rocco, in fact your code is doing shift-sub-add-shift :smileyhappy:. You are returning hi byte of FIRvalue accumulator, which is FIRvalue>>8.


 

Hi Kef,

 

I had to go back and look at the code to see what you meant. I was then surprised that I had no "real" shifts at all.

 

You are correct that grabbing the high byte only would count as a shift by eight, but at least it takes no extra cycles. I have always done that part like that. What was different with the code I posted and my prior filters is that in the newer code I don't do a shift at the end either. Setting N to 256 eliminated that shift as well.

 

 

Hi Patrick,

 

As Mac pointed out, the time-constant can be changed on-the-fly, and my older code could do this by changing the final shift-count (to any power-of-2 between 2 and 256). The code that I posted is fixed at N=256. If you need, I can dig-up that older code (but I doubt you would need it with everything Mac and Kef posted).

0 Kudos

1,634 Views
bigmac
Specialist III

Hello Kef,

 

Thankyou for bringing the truncation issue to my attention.  I think there was a small error in your spreadsheet formula (i.e multiplying by 8, rather than 7), but this had little effect on the inherent truncation problem.

 

I rather like the final method of your previous post, but I would reverse the order of the two formulas so that the calculation of the new Filt value is not delayed.

 

 Acc = Acc + Rdg - Filt;

 Filt = Acc / N;  or

 Filt = Acc >> i;

This also give scope for a simple method to dynamically vary the time constant, to provide rapid settling whilst distant from the target range, and a larger time constant close to the target value.

 

e.g.

 i++;              // Increase time constant

 Acc = Filt << i;  // New start value for Acc

 

In this case, I don't think that the inital truncation of the Acc value should have a significant effect, provided the error threshold value for switching the time constant. has sufficient hysteresis.

.

Hello Patrick,

 

Assuming a 270 degree rotation potentiometer, I would guess that the resolution should be no smaller than one degree, and might possibly need to be larger, depending on non-linearities within the resistive track, and the amount of "creep" due to friction and other mechanical issues.  Using no higher than 8-bit ADT resolution would seem reasonable.  Of course your samples could be 10-bit measurements, which could then be rounded to 8-bit, or even 7-bit values, before being applied to the filter function.

 

Rotary encoders intended as a manual control would typically have 16 - 32 indents per revolution.  Of course, multiple revolutions may be used during the control process.  The encoders may also incorporate a momentary push button for longitudinal movement of the shaft.

 

Why do you need "absolute knob position" on start up?  The current trend is not to require an absolute position of the control itself, but to provide functional status indication using LEDs or other display method.  The current status of each control would usually be stored in non-volatile memory prior to power down, and then restored on the next start up.

 

Regards,

Mac

 

0 Kudos

1,634 Views
rocco
Senior Contributor II

 

Hi Mac and Kef,
You guys must be in a different time zone, as you get so much done while I sleep (well, I know Mac is in oz).

bigmac wrote:

Why do you need "absolute knob position" on start up?


 

Hi Mac,

I also prefer encoder-pots. I can't speak for Patrick, but there are two situations where I need to use analog pots.

 

The first situation is for slide-pots or rocker-pots (the type used for zoom control).

 

The second is when the position indication is physically engraved on the knob itself. I know that an electronic indicator can replace the markings, but that can be difficult (and expensive) to implement on a small, hand-held device.

0 Kudos

1,634 Views
PatrickT
Contributor I

 

Thanks again for all the help guys, this has been some fantastic learning for me.:smileyhappy:  See below.

rocco wrote:
bigmac wrote:

Why do you need "absolute knob position" on start up?


 I also prefer encoder-pots.(...)

 

You guys and apparently the rest of the industry. :smileyhappy:

 

The application is a remote control box to enter several numeric values into a third party host system, using a one-way protocol I am not at liberty to change.  I figure a painted line on a knob is a lot cheaper, more accurate, more power friendly, and more I/O friendly than maintaining any digital position indicating apparatus I know of, when dealing with over a dozen knobs.

 

Furthermore it's possible to pre-empt the remote by using front panel controls.  With an analog knob, nobody is expecting that to update itself, since it's just a knob sitting there on a board.  But if you add an LCD or a value indicator, all of a sudden people will wonder why it's not updating to match the host.  Purely aesthetic but I've seen it happen.

 

Same thing with my desire for push-pull pots.  At a glance you can see the position of the switch, and if the function is related to the function of the knob, why not just put the switch on the knob, instead of on a separate toggle switch next to it??  But apparently that's no longer en vogue.

 

Incidentally if there's a forum here or ANYWHERE for this type of user interface component discussion, I would love to know about it.  I have yet to see the app notes page for ALPS for example. :smileyhappy:  Some companies have great tact switches or "shaftless" encoders... but I'll be darned if I can figure out how to integrate those into a finished design.  Presumably someone somewhere sells floating shafts?  I've not had any luck

 

Another example, I've all but given up on finding 4-position slide switches, so may just do a momentary toggle or circular sprung switch/encoder (ex: ALPS SLLB series) with mini LEDs for some of the "choose one of these 4 options" type inputs.  But even for those options, I find I like old-school switches a lot more than cost and overhead of maintaining a muxed LED matrix.

 

I'm always open to better ideas!

 

Do appreciate the time and info, you guys are great!

 

0 Kudos

1,634 Views
kef
Specialist I

Mac,

Probably it was not clear, but *(N-1) is rearranged and -1 is removed this way

 

 Filt = (Filt * (N - 1) + Rdg) / N;

 Filt = (Filt * N  + Rdg - Filt) / N;

 

and I still believe excel formulas are fine.

  

Yes, Filt can be calculated after accumulation, but at the cost of dedicated static variable for Filt. You decide, less RAM or one sample delay.

Initial truncation of course doesn't matter. Yes, Acc = Filt * N  is necessary to apply new time constant without side effects in filter output.

 

Regards

0 Kudos

1,634 Views
rocco
Senior Contributor II

Hi Mac and Patrick, (welcome back from vacation, Mac).

 

Actually, the "official" equation would be:

 

Filt = Rdg*a + Filt*(1-a);

 

where 'a' is a fraction greater than 0 and less than 1. The greater 'a' is, the longer the time constant (more filtering). It basically says "Take 'a' amount from the new sample, and (1-a) amount from the filtered value, and sum them, with the result  unscaled (multiplied by 1)". The time constant is 1 over the sample frequency, Fs times 'a', or:


Time Constant = 1/(Fs*a)


Mac's equation is the same, but rearranged for integer math. N is the reciprocal of "a", and the time constant  is N/Fs.

 

The way I do it saves one shift operation (I do "subtract,shift,add" as opposed to "shift,add,subtract,shift", but I don't think it would translate from assembler to C efficiently. I also preserve the lower significant bits that are lost by that final shift. They are the fractional part  mentioned. I'm not sure how the algorithm works without them.

 

As for my "detents", I simply add or subtract a bias toward the center point of each integer value, based on the fractional part. As an example, if the filtered value is 27.2, I would add 'a/2' to bias the result toward 27.5 (the detent). If it was 27.6, I would subtract 'a/2'. This prevents noise from "dithering" the value off of 27 when the actual analog value is near 26 or 28. In effect, there is a detent for each integer value in its center.

0 Kudos

1,634 Views
PatrickT
Contributor I

Thanks for the info, guys.  Rocco, I don't know if you saw the message I sent, but if you don't mind I'd love a look at the code.  Though the followup provides enough info to get started.

 

The application needs maximum angular resolution, so yes, I don't want overt deadband, but having "virtual deadband" in the fractions revealed by the filter math should be fine.  I'll have to play with it, I'm more a digital comms guy, so all this analog human interface stuff is new to me. :smileyhappy:

 

Also it needs absolute knob position upon startup, so I don't think a rotary encoder would work.  I've looked at rotary position sensors but those seem to cost as much or more as pots.  Although they do have better capability to be combined with switches, and I would love to emulate a push-pull knob, or at least a push knob that I can do a software toggle with.

 

Speaking of detented pots (totally off topic now), I've had no luck at all finding 4-detent slide or rotary pots, at least in modest quantity.  Even small 4 position slide switches seem all but obsolete.  Again, rotary switches are an option, but are large and expensive.  Apparently my interface design skills are still a couple generations behind!

 

Thanks again!

 

0 Kudos

1,634 Views
rocco
Senior Contributor II

Patrick, I received your message after I responded above. I sent you the following link:

www.remotehead.com/PrivateFolders/Freescale/AtoD.sa8

0 Kudos