Teensy Pot linearity

Status
Not open for further replies.

RogerD

Well-known member
Hey folks, I've been working on a synth project with the Teensy 3.6 and Audio card (fantastic work Paul S. by the way, greatly appreciated) and I have hit a problem with potentiometer accuracy. The pot doesn't return a linear response to the system making it very tricky to set a half turn point to a middle point in the data returned. That is to say that on a scale of 0 to 1023 the physical half way turn returns around 855 rather than roughly 511.5 which is returned at a point nearer to 10 to 15% of the rotation. Has anyone come up with a solution to this that won't chew up the clock?
 
First, there are different kinds of potentiometers, linear ones and logarithmic ones, the latter being designed for audio attenuation purposes. Make sure that you use a linear one.

Second, make sure that the hot end of the potentiometer is connected to not higher than 3.3V when using the Teensy 3.6 and that the analog reference voltage for the ADC is also configured to be 3.3V. The cold end of the potentiometer should be connected to AGND.

Third, for stable readings, a relatively low source impedance is required by the Teensy’s internal delta-sigma ADCs. Make sure that the potentiometer’s resistance is <= 10kOhm and that you “buffer” the signal by bypassing the taper with a 10nF capacitor to AGND.
 
I recommend using a voltmeter. Then you can compare the ADC data to the actual voltage at the pin, to get an idea if the problem is happening inside or outside the chip.
 
Thanks for the brilliantly rapid response Theremingedieur. I think I should have been more specific. I've been in a bubble with this thing for some time now and forgot that my info might not be complete or made better sense outside of my head. (I was also hoping not to bore anyone with a virtual essay but here we go. I'll try not to overdo it.) BTW, isn't that the Robert Moog type theremin design in your pic? I found his circuit schematics not long ago and was thinking of making it next. It has some really interesting interface possibilities for my synth as well. I know a couple of people who are also itching to have theremins and I myself really wanted to make something directly Moogish. Oh, and howdy Paul.
Anyway, the system is loosly Moog modular inspired aimed at a lot of tactile pot control rather than LCD menu squinting and button guessing (although a touch screen or two is coming up next). I'm using both linear and log pots depending on the application in the unit running through MUXs. I'. been trying logs on things like waveform detunes and envelope attacks to try and increase the control at the fine end of the scale which has been working well even considering the disparity at the pin but at this point, temporarily, I have linears on everything else. It's in some of these linear pots that I am having problems. I have the hot ends connected to the 3.3v pin on the Teensy and have them grounded on the common GND rather than the AGND. I should try the AGND (and learn more about it) but at this point I doubt that that will change things significantly considering the actual issue you've pointed out. I also have the reference connected to the 3.3v end physically which seems superfluous at the moment. My main development power supply is a wall wart supplying 9V at 1.5A feeding through a 7805 regulator and caps with the USB supply cut on the Teensy board but for portability I really want battery support as well. I mean Teensys are teensy. Portability is a big part of the point of this for me and it would be a shame to give up on it.
You have hit the nail on the head though. I have about 40 1M pots going at the moment because of the greater draw on the power supply I found from using lower impedance's. With all 10k pots (30 of them in an earlier iteration) on a battery powered setup I was going through a charge on two 3.7v lithium laptop cells in about half an hour so went for the 1Ms to drop the dead drain across the supply rails bringing this up to around 6 to 7 hours or more. Thanks to you I've just checked with 1K and 10K and they do have a much more linear response. Sheesh, I don't know why I didn't think of it. When testing the higher ohm pots I only really looked for increase in noise but found no real change. If anything they have been cleaner signals (Same brand, just as new and clean and tested with several units.) It is counter intuitive to me also but that is what I have been getting consistently so I went for 1M ohms and would have gone higher had my local shop stocked them. I also found that 1M pots stopped dead any crosstalk problems effecting nearby pot settings. I thought that was from an insufficient power supply but even going to a 2 AMP supply with a 1 AMP 7805 regulator wasn't enough to solve crosstalk back when I only had 24 10K pots in an early soldered setup.
Signal stability hasn't been too much of a problem. In the sketch I have set them up to calculate down to ranges that just exceed the needed top and bottom (example below), a range of -0.02 to 1.02 for example, which I then cut off before use to 0.0 to 1.0. This came from a need to silence waveforms that should have been off but were still just leaking out a tone at pot zero because of the slightly shifted 0 result from the MUXs and noise nudging it up above 0 a little. I was about to look into caps this weekend to clean the little bit of working range noise I have been getting (roughly 1% to 2% of the signal at a guess. Almost tolerable but not all of the time depending on the systems settings.). 10nF sounds good. I was wondering what I was going to start with. This was something I was going to have to deal with because with so many pots there is a little bit of flutter in the played note sound with some settings especially when you get to a few layered effects treating a few mixed oscillators. This stood out to me the most when I tried loosely emulating a ring modulator with a couple of waveforms through a multiply module.
I suspect I am going to need to work out an algorithm in the sketch to tweak the log curve resulting from the higher impedance pots. My education is all fine art painting, sculpture and photography so as you might imagine my math is very hit and miss so working out the log is going to be, umm, "interesting". I should be able to apply a formula to return the pot read to a reasonably linear response but how I'm going to do that I have no idea yet. I suppose it will have to be simple and rough to minimize clock cycle consumption but then it wont need to be too perfect to improve on what I have got going right now. At the moment the code for each pot is simple and looking like this with slight individual variations...

// First I set the state of the MUX for the specific pot and then...
for (int i=0; i <= 4; i++) POT = ((float)analogRead(muxZpin) / 990.0) - 0.02; // the 0.02 at the end drops the range a little below 0 to
// contain the pot noise and then I trim it out with ....
if (POT < 0.0) POT = 0.0;
if (POT > 1.0) POT = 1.0;
// Then apply the result to the waveform, mixer, envelope or whatever

I will probably be modifying this to simply introduce a delay to let the signal settle rather than looping it although the loop seems to work better and quicker for some reason. I was going to get to testing and optimizing this in the next code optimization session.
If you or anyone could throw an applicable log formula at me that would steer an old math illiterate in the right direction I'd be really grateful. I think it really just needs to be able to pull the center point of the pot rotation back to the center but I suspect it will probably just move the response curve resulting in the residue of steeper curves dropping at either end of the scale but then, like I said, math illiterate.
 
I recommend using a voltmeter. Then you can compare the ADC data to the actual voltage at the pin, to get an idea if the problem is happening inside or outside the chip.

I tried quickly testing it with my old CRO and came to the conclusion that it had to be the way the ADC was seeing things but it couldn't hurt to check again more carefully with a multimeter. I've been tinkering away at this thing for months and I gotta admit I've taken shortcuts.
BTW, kudos on the system Mr.Stoffregen. I've been waiting for something like the Teensy for years and the Teensy / sound card combination just keeps giving.
 
Thanks for the brilliantly rapid response Theremingedieur. I think I should have been more specific. I've been in a bubble with this thing for some time now and forgot that my info might not be complete or made better sense outside of my head. (I was also hoping not to bore anyone with a virtual essay but here we go. I'll try not to overdo it.) BTW, isn't that the Robert Moog type theremin design in your pic? I found his circuit schematics not long ago and was thinking of making it next. It has some really interesting interface possibilities for my synth as well. I know a couple of people who are also itching to have theremins and I myself really wanted to make something directly Moogish.

You are welcome! The instrument in my pic is a Russian tVox tour Theremin, actually my preferred instrument since it is less temperature sensitive and has a wider pitch range as well as a nicer sound than the Moog Etherwave stuff. Only around 50 of these have been made from 2005 to 2007 and I had much luck to get one.
 
If the log() math is bothersome, you might interpolate from a table of values. Measure a dozen or so settings across the range of your potentiometer. Decide what each output value should be for each measurement. The code would search for the two measured values above and below the current reading. The corrected output would be between the two corresponding output values, computed as a weighted average, where the weighting is simply the fraction of the way from the low to the high measured values. If searching the web for "linear interpolation" doesn't quickly get you to working code, I'll try to provide a better explanation.
 
If you do have an "audio taper" pot, might be worthwhile to know that most of them are actually fabricated with 2 or 3 more-or-less-linear regions on the material that approximate an exponential curve.
 
Something has gone wrong when even 40 pots is chewing thru battery like that.
40 x 3.3v ÷ 10KOhm = 13.2mA

So 13mA difference should not make such a dramatic difference in battery life for something like a 500 mAh lipo.

Also, crosstalk on very low frequency DC signals should be effectively zero compared with the noise. Do you mean other pots output is effected beyond the noise floor of the signals?

If so your crosstalk is from too little current trying to bring the input pin to it's new level (and/or not enough time between setting the mux channel and reading it's value).

You have multiplexing with 1MOhm pots... honestly I'm surprised this works at all let alone as well as you say.

While an output remap should be feasible I'd suggest you may want to spend more time on hardware.

FYI - feeding Teensy its own reference voltage is redundant at best. AGND is typically used to ground pots but in my experience using GND makes little difference but I'm not sure for those massive impedences. I suspect it matters less but I'm no EE.
 
The C function pow(base,exponent) will give you convex/concave functions that will map the [0,1] range to itself.

So if you normalize your input values you can experiment with exponents greater than 1.
I think about 3.75 should work.
 
Last edited:
The C math functions are incredibly slow. I try to avoid them where possible.

To map linear potentiometer readings to an audio curve (called logarithmic, but it is in fact exponential), there is a very quick integer approximation which is precise enough for volume control purposes etc.:
Code:
y = (x * (x + 1)) >> n; // where n is the resolution and x and y are positive integers.
Thus for the 10 bit resolution (0 - 1023) cited in the first post of this thread, you'd chose n=10 which would map the linear 0 to 1023 to pseudo exponential 0 to 1023. y has to be declared to hold at least the double resolution. 20 bits are needed in this example, thus it should be an uint32_t.
 
Nice... that's going in my toolbox.

I knew mine wasn't the best method but thought it was at least something that should work.

But I still think OP should reconsider the hardware. He may find that logarithmic output was contingent on the setting of the previous pin the mux was reading and now readings are being dragged down instead of up...

Especially as he indicates 'crosstalk' between pots.

If the pots are linear I can't think of any reason their output wouldn't be so I suspect it's the input impedance fighting the wimpy signal and insuffient time for the pin the stabilize.

This thread may be informative.

https://forum.pjrc.com/threads/32373-Potentiometers-affecting-each-others-when-connected-to-74HC4051
 
Was just about the mention "crosstalk" problems with pots with a mux chip. Indeed that thread is one of the best ones, especially with the breadboard photo!
 
In this case the problem isn't added capacitance but vastly higher output impedence but the result is similar. You'd need a considerable amount of time to stabilize the voltage you're trying to read.
 
If you do have an "audio taper" pot, might be worthwhile to know that most of them are actually fabricated with 2 or 3 more-or-less-linear regions on the material that approximate an exponential curve.

Yeah Paul. I was wondering about that the other day. Odd I'd never actually thought about how a log pot was set up before but then over the years I have only used them rarely. A stepped/layering approach to the track makes perfect sense. I have been using them to graduate the slope of the attack on your envelope module. They still aren't quite smooth enough of course because I am fighting against the curve created by my overly enthusiastic pot impedance.
 
The C math functions are incredibly slow. I try to avoid them where possible.

To map linear potentiometer readings to an audio curve (called logarithmic, but it is in fact exponential), there is a very quick integer approximation which is precise enough for volume control purposes etc.:
Code:
y = (x * (x + 1)) >> n; // where n is the resolution and x and y are positive integers.
Thus for the 10 bit resolution (0 - 1023) cited in the first post of this thread, you'd chose n=10 which would map the linear 0 to 1023 to pseudo exponential 0 to 1023. y has to be declared to hold at least the double resolution. 20 bits are needed in this example, thus it should be an uint32_t.

Fantastic. That's the kind of starting point I was looking for. Thanks for that.
 
In this case the problem isn't added capacitance but vastly higher output impedence but the result is similar. You'd need a considerable amount of time to stabilize the voltage you're trying to read.

The code I put in my second posting is what I am using at the moment which is just five pulls on the pin in a loop. At a rough test a while back it seemed to do the trick better even than just putting in a delay. (Don't worry, makes no sense to me either but there it is.) I've got to do an efficiency test on this at some stage though to fine tune it but it's reading fast enough with very little flutter. It used to be worse but once I soldered up a perferboard prototype rather than using the aging breadboard I started on the noise became better than I have had before on these things. I even seem to be getting better results when I went from the Teensy 3.5 to the 3.6. In the normal range without a MUX to 1023 I have only needed to clean up about 3 to 5 values of noise. Once this is dropped down to 0.0 to 1.0 it has been unnoticeable most of the time. I figure that once I get this thing into a box with decent shielding and looms cut to essential lengths this might improve even more.
 
Nice... that's going in my toolbox.

I knew mine wasn't the best method but thought it was at least something that should work.

But I still think OP should reconsider the hardware. He may find that logarithmic output was contingent on the setting of the previous pin the mux was reading and now readings are being dragged down instead of up...

Especially as he indicates 'crosstalk' between pots.

If the pots are linear I can't think of any reason their output wouldn't be so I suspect it's the input impedance fighting the wimpy signal and insuffient time for the pin the stabilize.

This thread may be informative.

https://forum.pjrc.com/threads/32373-Potentiometers-affecting-each-others-when-connected-to-74HC4051

Back when I was using the 10K pots I did get intermittent cross talk at worst. It was more a curiosity than a concern at the time. I put it down to possible bad connections but changing the pots cured it. Having that go away with the 1Ms was pretty much just a pleasant bonus.
As far as the signal stabilizing though how long should it take? With my current proto-code (the skeleton of an earlier summing loop I was using to smooth noise) I just started with about 100 reads and kept trimming it down until the signal started to waver more which turned out to be at three and added a couple to give breathing room. I figured I'd work out something better later. I haven't worked out how long five quick peeks take in ms yet but I can't imagine it is much. It certainly hasn't been enough to mess with the operation of the system. I'm with you though. This setup has been doing some things oddly well. I was thinking that I was going to have to give the reads mush more time than I have so far.
 
Last edited:
You've not posted code so I can't comment on why it behaves as it does.

Averaging values will stabilize the signals but does not address the time-to-switch issue. After you set mux select pins an amount of time is required for the voltage to stabilize before an accurate sample can be read.

It's an incredibly short period of time but Teensies are very fast so if you set-then-read in the same pass thru the code you should expect to get some error from this.

If you have extra capacitance at the Teensy pin when the mux is set the amount of time goes way up.

Very large resistance pots should also need more time than smaller ones but I don't know how much. With 10K pots it's a few micro-seconds and just separating the set and read to different passes of the main loop is usually enough.

If you don't want to set up elapsedMicros timers, delayMicroseconds() can be added between the mux set and read but this is wasted processing time and may not be compatible with your synth project.

Best of luck with your project.
 
The C math functions are incredibly slow. I try to avoid them where possible.

To map linear potentiometer readings to an audio curve (called logarithmic, but it is in fact exponential), there is a very quick integer approximation which is precise enough for volume control purposes etc.:
Code:
y = (x * (x + 1)) >> n; // where n is the resolution and x and y are positive integers.
Thus for the 10 bit resolution (0 - 1023) cited in the first post of this thread, you'd chose n=10 which would map the linear 0 to 1023 to pseudo exponential 0 to 1023. y has to be declared to hold at least the double resolution. 20 bits are needed in this example, thus it should be an uint32_t.

Excellent mate! I gave it a quick test drive this morning and it did the trick very nicely. It was agonizing working out how to apply it to code taking all of 90 seconds and two sips of coffee.

For those who are curios it was simply...

long n = 10; // 10 being the shift of ten bit
long x = pot; // pot is just a variable containing the data from the pot pin
long y; // the result
y = (x * (x + 1)) >> n;
Serial.println(y);

My use of the long type wasn't thought through at all but worked (I only had 4 mins before I had to dash out the door). There is probably better efficiency in another type but I'll get to that in my next session.

I found the noise ratio came up a little to about 1% of the signal but I haven't fully treated that yet and am not that worried at this point but this neat little formula has quite a few possibilities for wider pot use and beyond.

Thanks again Theremingenieur. Looks like I'll be back to the site soon to get a few more bug fix ideas and hopefully contribute some solutions to others. No more unnecessary essays though, I promise.
 
Since variable declarations like „long” might have different sizes on different CPU architectures, I prefer declaring these with more deterministic types: since x, y and n are strictly positive, I’d use uint16_t instead of long. For n which is a low value, even uint8_t would be sufficient.
 
Something has gone wrong when even 40 pots is chewing thru battery like that.
40 x 3.3v ÷ 10KOhm = 13.2mA

So 13mA difference should not make such a dramatic difference in battery life for something like a 500 mAh lipo.

Also, crosstalk on very low frequency DC signals should be effectively zero compared with the noise. Do you mean other pots output is effected beyond the noise floor of the signals?

If so your crosstalk is from too little current trying to bring the input pin to it's new level (and/or not enough time between setting the mux channel and reading it's value).

You have multiplexing with 1MOhm pots... honestly I'm surprised this works at all let alone as well as you say.

While an output remap should be feasible I'd suggest you may want to spend more time on hardware.

FYI - feeding Teensy its own reference voltage is redundant at best. AGND is typically used to ground pots but in my experience using GND makes little difference but I'm not sure for those massive impedences. I suspect it matters less but I'm no EE.

I've always found a bit more drain from my various Arduino based projects than I liked but pretty much took it for granted but now that you mention it I am going to have to give it a decent analysis. It's got to be something to do with my build habits.
The thing that was happening with crosstalk (no longer a problem) was that setting a pot on one control could change the values passing from other nearby controls on the same mux. There were a few possibilities I though about at the time (power supply, connections or stray capacitance's etc.) but as the problem seemed to evolve away from the system I mostly forgot about it as no more than a potential future clue for future bug fixes. It could have been from going to 1Mohm pots or replacing my computer systems pretty noisy USB power with a cleaner, better conditioned and more substantial (1 Amp) external supply. I may have even simply disrupted an as yet undiscovered dry solder point somewhere but since I was thinking of getting a board etched for the final model rather than the "artistic" tangle of connections that I have now I haven't been losing that much sleep over it.
I too was surprised that 1M pots worked so well but there you are. Since the log formula we got here seems to work (fingers crossed) I will probably continue to use them on these bigger potentiometer cluster projects unless I find that I am losing too much CPU grunt on the conditioning calculations. Even if it is a build mistake I have been making there is always going to be a fairly significant battery life benefit from them.
As for the reference voltage I absolutely agree with you. It is certainly redundant and is still there simply because it does little or nothing and I hadn't gotten around to removing it after trying it as a failed hunch in the log problem. It's as good as gone anyway.
Thanks for the input. It's good to be challenged for a re-think.
 
Last edited:
Status
Not open for further replies.
Back
Top