## Linearizing Logarithmic Potentiometer

44 posts / 0 new
Author
Message

I have a logarithmic single turn pot. I am pumping the value into a 10 bit ADC converter on my mega88pa, writing in GCC, C.

When I take the output value (0-1023) and divide by 100, I can make 10 steps which will be used as 10 settings for my project. 0 through 99 / 100 = 0 (mode 0). 100-199 output from the adc would correlate to mode 1 since 199/100 = 1, and so on and so forth.

If my pot were linear, a tenth of a twist would be mode one. a second tenth of a twist would be mode 2, etc.

Changing a pot to linear is not an option. The log pot needs a quarter twist to give me 100 or greater value, a eighth twist to give me a 200, a 16th to give me 300 (so on and so forth more or less). Do I have to use Switch/Case statements and say if ADC value is 0-200, then 0, if 200-300, mode 1, if 300-350 mode 2, 350-380 mode three, etc etc so that the dial will switch linearly in between modes.

Is this my only option? Please give me hints, I havn't had to include math.h yet, don't really want to, was thinking some smarty on here would know another work around, I can't think of any.

I'm guessing you don't want to add an op-amp and linearize the output in hardware...

Scanning a lookup table with the thresholds for transitioning to the next level would be an option, but not much different from what you have already considered above.

JC
JC

Thanks JC, I actually could add an op amp, didn't realize that would be a solution. I just googled op amp circuits and I can do an exponential and a log circuit (didn't know op amps did this at all!) but it doesn't tell me what kind of diode to use. Will any diode work as long as I have the right resistor? Really really good, thank you =)

An analogue circuit based on log / anti log charateristics of semiconductor junction is a possible solution.

While the function is well behaved it has thermal problems as well as bandwidth problems ( bandwidth is gain related... gain is input signal level related ).

You could use a diode network in the feedback path to implement piecewise linear approximation. Same issues with gain bandwidth but probably more robust as far as the junction temperature is concerned.

Conditional statements in software are Your best option as You are looking only for a limited set of transition points.

You responded saying you can add a log amp after I wrote the following response so my comments may no longer be relavent except saying that without hardware linearization (log amp or linear pot) you may have poor performance.

It sounds like you are kidding yourself. Using your example the last 1/10 would be 1/2048 turn. Realistically you only have about 0-400 available (1/64 turn) if you are lucky. I doubt you can adjust it that fine.

If you make the first 1/10 equal 1/2 turn (rather than 1/4) you double the resolution. Otherwise the last 1/2 turn is wasted. This is just a matter of scaling.

You said changing to a linear pot is not an option but depending on the application a log pot may not perform as you expect since the upper half of the pot is unusable and/or unsettable.

As for the code, the source code will be simpler (i.e. fewer lines and easier to read) if you use if/else statments. Depending on the compiler and optimization the code size may be the same for switch and if/else.

Last Edited: Fri. Jun 8, 2012 - 09:34 PM

aero1 wrote:
It sounds like you are kidding yourself. Using your example the last 1/10 would be 1/2048 turn. Realistically you only have about 0-400 available (1/64 turn) if you are lucky. I doubt you can adjust it that fine.

I am poor at communication, I only want to have 10 steps on knob and i want the 10 steps to be evenly spaced. I am going to try these diode thingies out I guess, if all else fails, I will if else it to death.

I would go with the software solution. Just add a bit of hysteresis, something along the lines of:

```switch(setting)
{
case 0: if (in>100) setting=1;
else if (in<80) setting=0;
break;

case 1: if (in>200) setting=2;
else if (in<180) setting=1;
break;
.
.
.
}
```

smkipus wrote:
I have a logarithmic single turn pot. I am pumping the value into a 10 bit ADC converter on my mega88pa, writing in GCC, C.

When I take the output value (0-1023) and divide by 100, I can make 10 steps which will be used as 10 settings for my project. 0 through 99 / 100 = 0 (mode 0). 100-199 output from the adc would correlate to mode 1 since 199/100 = 1, and so on and so forth.

If my pot were linear, a tenth of a twist would be mode one. a second tenth of a twist would be mode 2, etc.

Changing a pot to linear is not an option. The log pot needs a quarter twist to give me 100 or greater value, a eighth twist to give me a 200, a 16th to give me 300 (so on and so forth more or less). Do I have to use Switch/Case statements and say if ADC value is 0-200, then 0, if 200-300, mode 1, if 300-350 mode 2, 350-380 mode three, etc etc so that the dial will switch linearly in between modes.

Is this my only option? Please give me hints, I havn't had to include math.h yet, don't really want to, was thinking some smarty on here would know another work around, I can't think of any.

"Log" pots are not logarithmic but an approximation usually in two straight line segments.

I would do what you suggest but measure the actual output of the pot to determine where the switching points should be.

kevin

Funny spectrum of answers. escape having to tweak the hardware by adding software, escape having to tweak the software by adding hardware. I cant help but notice you have a 10 bit a/d and want 10 steps. Its done. Each bit is 1/10 of the of the throw. 60 dB range, 6dB per bit.

Imagecraft compiler user

My first thought too, Bob - except that the pot linearity (logearity???) probably doesn't match the angular position terribly well, and the full range of the pot may well not match the full range of the ADC - which will require scaling and reduces it to the 'if then' chain discussed above.

The noise induced by the wiper could also be an issue; with a five volt range and ten bits of ADC you're looking at ~5mv/bit. You'll probably want to take multiple samples and average of a significant time - a few tenths of a second or so.

```// assuming a complete 0-1023 range; scale if required
val = 0;
if (x > 512) val = 9;
if (x > 256) val = 8;
if (x > 128) val = 7;
...
if (x > 4) val = 2;
if (x > 2) val = 1;
// and no else required
```

Good. We have concensus from two continents. Just change the > to >= in the example and we are in complete agreement.

Imagecraft compiler user

Left field suggestion ... ditch the log pot for a rotary encoder and apply whatever transfer function you want. Save the last position for next use.

Cheers,

Ross

Ross McKenzie ValuSoft Melbourne Australia

Ok, my memory doesn't serve me well, and I might be totally off, but I remember that there were some tricks used to linearize NTC's and such that can be interesting to review: they wer something as simple as add a voltage divider (or a simple resistor) to the pot.

Mmmm, a good idea to play with and to investigate over the 'net.

Guillem.
"Common sense is the least common of the senses" Anonymous.

You can turn a linear pot into a sort-of logarithmic pot with an extra resistor across the wiper and a outer terminal.

bobgardner wrote:
Good. We have concensus from two continents. Just change the > to >= in the example and we are in complete agreement.

Oh damn, I got the tests upside down. Should have started with the 'x > 2' test:

```// assuming a complete 0-1023 range; scale if required
val = 0;
if (x > 2) val = 1;
if (x > 4) val = 2;
...
if (x > 128) val = 7;
if (x > 256) val = 8;
if (x > 512) val = 9;
// and no else required ```

jayjay1974 wrote:
I would go with the software solution. Just add a bit of hysteresis, something along the lines of:

```switch(setting)
{
case 0: if (in>100) setting=1;
else if (in<80) setting=0;
break;

case 1: if (in>200) setting=2;
else if (in<180) setting=1;
break;
.
.
.
}
```

why do we need two statements for each one? I never really was trained on hysteresis, read a wikipedia article on it, from what I understood, hysteresis is when something reads different depending on direction... 0 a scale, you weigh something on a scale then take it off but the scale no longer reads 0. How does hysteresis effect a ADC reading? All I'm doing is setting up a dial knob that will be turned to a position by a user and left alone. Regardless, the other technical guys at my job have asked me how I plan on preventing hysteresis and I guess I played it off without understanding. Your code snippet says it protects against hysteresis but I am slow I guess

kevin_white wrote:
"Log" pots are not logarithmic but an approximation usually in two straight line segments.

I would do what you suggest but measure the actual output of the pot to determine where the switching points should be.

kevin

Well my potentiometer doesn't seem to have two linear portions, the 0th region (the amount of twist to go from an adc reading of 0 to 100 to 200 to 300) is huge, the 9th region is tiny, and each region decreases in size. If I measure the output at each setpoint, I'm basically using my eyeballs to guess where I want the switching points to be right? Everything I build is so darn crude I need to find a new field...

In my code to get from 0 to 1 the input must be higher then 100, but to go back to 0 it must first fall below 80. So if the input jitters between 100 and 101 the setting won't constantly flicker between 0 and 1. Filtering the input only lowers the frequency at which it jitters but you can't get rid of it.

My code is just one way to solve it, if you a linear pot, you could use a simple equation for it.

Basically hysteresis means nothing more than having two thresholds instead of only one; one to set the output low, and one to set it high. Anything between these two threshold do not change the output.

smkipus wrote:
jayjay1974 wrote:
I would go with ...
Well my potentiometer doesn't seem to have two linear portions, the 0th region (the amount of twist to go from an adc reading of 0 to 100 to 200 to 300) is huge, the 9th region is tiny, and each region decreases in size. If I measure the output at each setpoint, I'm basically using my eyeballs to guess where I want the switching points to be right? Everything I build is so darn crude I need to find a new field...

Here is a description of the various tapers .. Figure 4 and the following description is relevant. There may also be an almost dead region at each end where not much happens.

kevin

Rule the First - don't add hardware when software can easily do the job.

Rule the Second - as others have suggested, actually measure the ADC input at your desired "switching points" and use those values in your software.

Thanks guys, I'll do the software solution as it may be faster for me to do than learning how to make a Logarithmic op amp. I just wanted to learn the right way to do it, in my imagination, I don't picture the pros sitting there reading out ADC values from hardware and manually hard programming in the set points, but I guess a real embedded engineer wouldn't use a pot as a 10 position knob anyways, they'd use the rotary encoder as someone suggested. Thanks again for all the responses!

Actually a pro might very well read the pot and record the values, unless the pot was so well specified that the information could be gotten from the datasheet.

But yeah, a pro would probably use some form of encoder if possible. Still there are cases when the hardware is already specified, and you just gotta live with it.

Pots with detents are commonly used as a switch/encoder.

A pro would likely use a couple of pots to check for any device variation.

I have a Roland MT32 synth module from the mid-eighties that uses a simple pot to select sounds and other settings. As those parameters can also be changed over MIDI in realtime they included a little trick on volume settings that the pot must first be rotated to the actual setting before it 'catches' and actually changes the setting.

Being such old device, controllers in those days did not have on-boards ADCs so I guess they used a simple RC circuit/comparator/timer to digitize the pot setting.

My car's climate control uses three rotary detented absolute position switches for the two temperature settings and air settings. Each switch is 5 bits or so.

The choice of switch depends if you want it to be absolute so the the knob psychical position is an indication of the actual setting.

kevin_white wrote:
Here is a description of the various tapers .. Figure 4 and the following description is relevant. There may also be an almost dead region at each end where not much happens.

kevin

There IS a deadzone region at each side where nothing happens. Upon closer examining / characterizing this potentiometer, I ran into exactly what you were talking about here and it kinda screwed me. Too bad I guess, wish I would have known this stuff before I set this design iteration in stone (pot is irremovable as it is literally set in epoxy). I learned a bunch from this thread, turns out you all know what you are talking about yet again.

```void Ana_Fan_Setter(void)
{
uint16_t help1;

if (help1>7)
{
FanStartLevel=10;
}
else if (help1<=7)
{
FanStartLevel=0;
return;
}
if (help1>117)
{
FanStartLevel=20;
}
else if (help1<112)
{
FanStartLevel=10;
return;
}
if (help1>254)
{
FanStartLevel=30;
}
else if (help1<249)
{
FanStartLevel=20;
return;
}
if (help1>374)
{
FanStartLevel=40;
}
else if (help1<369)
{
FanStartLevel=30;
return;
}
if (help1>490)
{
FanStartLevel=50;
}
else if (help1<485)
{
FanStartLevel=40;
return;
}
if (help1>606)
{
FanStartLevel=60;
}
else if (help1<601)
{
FanStartLevel=50;
return;
}
if (help1>735)
{
FanStartLevel=70;
}
else if (help1<730)
{
FanStartLevel=60;
return;
}
if (help1>864)
{
FanStartLevel=659;
}
else if (help1<859)
{
FanStartLevel=70;
return;
}
if (help1>977)
{
FanStartLevel=90;
}
else if (help1<972)
{
FanStartLevel=80;
return;
}
return;
}```

I am not sure if a switch statement could have did this or not.

Last Edited: Tue. Jun 12, 2012 - 03:22 PM

I often wonder how folks commit to final hardware without first prototyping stuff and proving the design will work first. Must be the modern way they are teaching "design" in college? In the old days you had a dev/eval board often wired up to a load of breadboards or strips of vero or whatever and you tested each subsection of the circuit. When happy you committed this to copper. Maybe delivery time-scales these day are such that there's no longer time to do this kind of thing? In the long run I imagine it often ends up actually costing more time/money.

clawson wrote:
I often wonder how folks commit to final hardware without first prototyping stuff and proving the design will work first. Must be the modern way they are teaching "design" in college? In the old days you had a dev/eval board often wired up to a load of breadboards or strips of vero or whatever and you tested each subsection of the circuit. When happy you committed this to copper. Maybe delivery time-scales these day are such that there's no longer time to do this kind of thing? In the long run I imagine it often ends up actually costing more time/money.

=( I was a roofer/shingler before I was a programmer, I have a nonstop voice in the back of my mind saying "go go go, no time for that, go go go go go"... I figured with this one something as simple as a potentiometer, do I really need to prototype a potentiometer, that's so middle school, oh well, i did a dirty workaround (offset my knob by reefing down the set screw as to eliminate a dead zone on one side) and learned a lesson.

Hey smkipus:

Switch statement can't switch over conditionals or over an expression, only for constants/preprocessor defines. You can't do something like

```   switch(a)
{
case a < b: // do sth
case a > (b+K): // do sth.
.
.
.
.
}
```

and expect the compiler not to moan about it

On the other hand, what I'd do if I were you, with the info you provided and with what I understood is to:

b) use a pot (preferably linear) and implement an N point moving average filter in software, then take the output and grab data from a table (which can be log/antilog/sine/cos/tan/..... You can adittionally decimate, shift, convert.. after/before access the table.

c) use a pot (preferably linear) but use a simple decimation. Do You have 10 bits available? Just right shift the conversion result 6 places to the right and you'll get the upper bits, and hence 16 steps (you'll have to work out over this)

Oh!, certainly, the pot wiper voltage must swing between zero and Vref for this to work!

cheers
Nachus

Note that GCC (or is it C99?) has an extension that allows ranges in case statements - this compiles without error:

```#include

int n;

int main(void) {
switch (n) {
case 0 ... 7: //stuff
break;

case 8 ... 31: // other stuff
break;
}
}
```

A few features more in GCC and it's no longer C, but a whole new language that happens to be able to compile standard C.

clawson wrote:
I often wonder how folks commit to final hardware without first prototyping stuff and proving the design will work first. Must be the modern way they are teaching "design" in college? In the old days you had a dev/eval board often wired up to a load of breadboards or strips of vero or whatever and you tested each subsection of the circuit. When happy you committed this to copper. Maybe delivery time-scales these day are such that there's no longer time to do this kind of thing? In the long run I imagine it often ends up actually costing more time/money.
I agree Cliff. Even for a very, very simple recent design (below) I always do a prototype to prove that the code works with the silicon. It adds maybe 2 hours to the project and 100 percent confidence.

## Attachment(s):

Ross McKenzie ValuSoft Melbourne Australia

smkipus wrote:
... i did a dirty workaround ... and learned a lesson.
The experienced engineer has many such battle scars. Gain enough of them and you start to get a feel for what works and what doesn't.

Sadly this feature seems to have been lost on the MBA's of the world as they hire the low bidder (and are shocked when the cost of the rework is double the estimate of the people with a clue).

Quote:

A few features more in GCC and it's no longer C, but a whole new language that happens to be able to compile standard C.

True, Case Ranges is just one of many:

http://gcc.gnu.org/onlinedocs/gc...

OT:

Ross,

Single sided board with no jumpers!

Wow. I gave up on those a long time ago...

Nice job.

JC

Use an autorouter?

Imagecraft compiler user

Ah...

I saw one chip, (VUSB project?), and just figured Ross did it manually. (And perhaps he did.)

My projects are all small, < 500 parts, and I've always manually routed my PCBs.

In any event, the board has neither a pot nor a rotary encoder, so sorry for derailing the thread...

JC

nachus001 wrote:
c) use a pot (preferably linear) but use a simple decimation. Do You have 10 bits available? Just right shift the conversion result 6 places to the right and you'll get the upper bits, and hence 16 steps (you'll have to work out over this)

Oh!, certainly, the pot wiper voltage must swing between zero and Vref for this to work!

cheers
Nachus

Great solutions, will probably implement this into the next few I'm going to make. The encoder would be cool but all the small cheap ones on digikey were only incremental rotary encoders and I've never used them as selector dials before. Just out of curiosity/laziness, how would you set up an encoder to always know which direction the arrow on it's knob is pointing even after lots of powerdowns. If the machine is left with the knob pointed to number 3, turned off, and starts back up, how does the mega8 know it's still at 3 without using eeprom or having the user always start it up at position setting 0. As far as having a knob that can have perfectly spaced apart settings, this is the best solution. Far better than my guessing at angles and hardcoding the sets.

Really neat design you posted Russ. You don't have one extra hole drilled in that circuit board than you needed. Did you make the whole board at home with stickers and acid or toner transfer?

How much money do you wish to spend?

One can get generic rotary encoders with the ability to know if one is rotating clockwise or counterclockwise, but without absolute position info, or get expensive ones with absolute position info.

Another approach is to use a cheap CW/CCW pulse info encoder and have a small LED at each of the "positions". There is no pointer on the dial, just a round knob. If position #3's led is lit up, and the user rotates the dial CW, you are now at position #4, turn off Led #3, turn on Led #4.

If you want a volume control, then you light them all up like a bar graph.

You can power up at your discretion. Either use the user's last setting, or always turn on at the midpoint, or the lowest setting, etc. The Led tells the user what the setting is on power up.

One could also impliment this with "touch" sensing, but I've not worked with that yet, and a rotary encoder is pretty foolproof.

JC

Guys (in no particular order),

Yes ... single sided, no jumpers, manually routed, Eagle, UV sensitive photo exposure, ammonium persulphate etchant, V-USB using ATTiny85, produces a single keypress for one pushbutton switch and a depress key for the latching switch and a different release key upon release, provides control signals to a PC application that I wrote to control another Windows application ... all for a client.

The point of which was just to say that "some of us" still do the hardware to use while developing the software.

Cheers,

Ross

Ross McKenzie ValuSoft Melbourne Australia

smkipus wrote:
nachus001 wrote:
c) use a pot (preferably linear) but use a simple decimation. Do You have 10 bits available? Just right shift the conversion result 6 places to the right and you'll get the upper bits, and hence 16 steps (you'll have to work out over this)

Oh!, certainly, the pot wiper voltage must swing between zero and Vref for this to work!

cheers
Nachus

Great solutions, will probably implement this into the next few I'm going to make. The encoder would be cool but all the small cheap ones on digikey were only incremental rotary encoders and I've never used them as selector dials before. Just out of curiosity/laziness, how would you set up an encoder to always know which direction the arrow on it's knob is pointing even after lots of powerdowns. If the machine is left with the knob pointed to number 3, turned off, and starts back up, how does the mega8 know it's still at 3 without using eeprom or having the user always start it up at position setting 0. As far as having a knob that can have perfectly spaced apart settings, this is the best solution. Far better than my guessing at angles and hardcoding the sets.

Really neat design you posted Russ. You don't have one extra hole drilled in that circuit board than you needed. Did you make the whole board at home with stickers and acid or toner transfer?

I used that crude decimation with a MIDI potentiometer hub using the ATmega16 ADC option that presents the upper8 bits of the 10 bit word in a byte form, and rotating one place to the right (since midi controller data is 7 bit wide) That was a "pot flicker killer"

As for the encoder it's easy to read it, since the encoder has two pins that are always in "quadrature" You need an interrupt and a bit of patience to do that http://industrial.panasonic.com/...

But if you need a "mechanical memory" (like a pot with a pointer knob pointing to a printed scale in a panel) to be remembered by the controller thru power cycles, the best you can do is to use a potentiometer.

Or... do as the Clavia guys, and use an encoder/endless pot and an active circular led bar scale to replace the knob's pointer then store that info in EEPROM, as it's implemented in the Clavia Nord Lead synths :D

Regards
Nachus

smkipus wrote:
[Just out of curiosity/laziness, how would you set up an encoder to always know which direction the arrow on it's knob is pointing even after lots of powerdowns. If the machine is left with the knob pointed to number 3, turned off, and starts back up, how does the mega8 know it's still at 3 without using eeprom...

Why are you eliminating EEPROM as a solution? That's pretty much what it was made for.

kk6gm wrote:
Why are you eliminating EEPROM as a solution? That's pretty much what it was made for.
Thanks for the input, I haven't begun going this route yet, was just curious about prospective methods to use. EEPROM seems like the most obvious way to me but to be honest, I was just trying to see if there was a slicker way/trick to doing it.

Most encoders I've seen (like on helicopter flight display controls) dont have an index line on them. You spin em left and right and some number in the display next to them incs and decs. When the power goes off, snd comes back on, you either need to start at some logical default, or pull a stored value out of somewhere like eeprom.

Imagecraft compiler user