Reading high impedance analog inputs quickly

I've been wracking my brain all night with a problem, and I think I've found a solution, but I thought I'd run it by you.

I have two resistive touch switches in my circuit. The two analog pins they're connected to are pulled down via 1M resistors, and one of the contacts is connected to +5v. When you bridge the two contacts with your finger, you pull the analog pins up because your finger has less resistance than the pulldown resistor.

Sounds good on paper, but when first tried doing this, I was getting all kinds of crazy readings. GrumpyMike explained that the analog pins are multiplexed... that there's only one ADC to read voltages on the microcontroller, and it just switches it between the pins. And because my pulldown resistor was so large, it couldn't pull the ADC down quickly enough to get a proper reading. The solution he explained, was to read the pin twice, with a delay between. The first read would switch the multiplexer to the appropriate pin and allow it time to pull it down. The second would read the actual value.

The problem is, I can't have my program sitting there doing nothing for 40 milliseconds. I have other pins I need to update. I tried using the millisecond timer to perform the same task while still being able to do other stuff, but it worked terribly, and I had no idea why, since I wasn't accessing any of the other analog pins while I was waiting for the pins to discharge.

So I started looking for a hardware solution. And the solution I came up with after many false starts is deceptively simple:

I connected one analog pin to ground via a 10K pulldown.

Now why would I do this?

Well, when I ws having problems reading these switches in the first place, GrumpyMike explained to me that the analog pins are all connected to a multiplexer which switches them to a single ADC in the microcontroller. And the reason I was getting readings that were all over the place was because my 1M pulldowns weren't pulling the ADC's input down fast enough.

The solution he offered to this problem was to add those delays. Read the pin once, wait for it to settle down, then read it again.

But I didn't have time for that. And my attempts to borrow time by switching to the pin, doing stuff, then going back to read it had failed.

So I wondered if there wasn't some way I could pull that input down faster, as if I had 10K resistors on my touch switches?

That's when it occured to me that I could attach the 10K pulldown to a spare analog pin I had. Then, because internally it was connected to the same thing as the pins my high impedance switches were conneced to, I could simply read it to switch the multiplexer to it briefly, pull the ADC input down to near 0v, and then switch to the pins I wanted to read which could be pulled up faster than they could be pulled down because skin has a lower resistance than the pulldown I'd stuck on the pin.

The results I got from this change were immediate. Rather than values that were all over the place, I now got very clean readings, even when reading the pins with no delay at all.

As far as I know, what I've done shouldn't harm the Ardunino at all. It's just a pulldown resistor. I will need to do more tests to see if I get any better results if I add a small delay or read the ports twice, but the raw numbes I'm getting are so good I don't think I'll need to do much more than a running average to smooth them out a little more.

So what do you think? Good technique?

Very good. :slight_smile:

But that's the trick. You're not pulling against 10K.

I've got the 1M pulldowns and the touch switches on A0 and A1 I've got the 10K pulldown on A2.

And I read the pins like so:

analogRead(2); a0 = analogRead(0); analogRead(2); a1 = analogRead(1);

So the 10K is connected only for a moment... Which because it is relatively low impedance, is long enough for it to pull the ADC down to 0v.

Then I connect the pin with the switch and the 1M pulldown. And the 1M pulldown is enough to keep the pin at 0V.

Until you touch the switch that is.

Once you do that, you're now effectively connecting a pullup whose reisstance is much less than 1M.

How much less? I'm not sure. But it seems to work.

Generally with a dry finger I can get readings of around 256 if I apply pressure. I think that means I'm 3/4 of the way to 0V, which would put the resistance of my finger around 750K.

Anyway, the theory of operation here is that my finger can do a better job pulling up the ADC than the 1M pulldown can do pulling it down, and that 1M pulldown may have a long way to go to get to 0V. Also, by doing this, I'm starting at a solid base of 0V, and the only way to go is up, and if I'm not touching the switch it shouldn't rise at all with that 1M pulldown holding it down.

Here's my actual code btw:

const int analogLeftTouch = 0;
const int analogRightTouch = 1;
const int analogLeftServoTrim = 4;
const int analogRightServoTrim = 5;

const int analogDelay = 1; // The number of milliseconds to pause between analog readings.

analogRead(2); // 10K pulldown
tmp = analogRead(analogLeftTouch); delay(analogDelay); a0 = (a0 + analogRead(analogLeftTouch)) / 2;
tmp = analogRead(analogRightTouch); delay(analogDelay); a1 = (a1 + analogRead(analogRightTouch)) / 2;     
a4 = analogRead(analogLeftServoTrim);
a5 = analogRead(analogRightServoTrim);

As you can see, I'm doing a little more than simply reading the touch switches after reading the pin with the 10K pulldown. I'm also doing a running average, and switching to the pin then waiting for 1ms before reading it.

The above isn't necessary to get good values though. I'm not even sure if I need the delay, that's just the state the code was in when I went to bed this morning.

But anyway, if you take out the code that reads the pin with the 10K pulldown on it, everything goes pear shaped and the values read from the touch switches end up all over the place. That is, unless those delays are more like 20-40ms. But even then, the values are still kinda noisy, and I tried all sorts of schemes to debounce them, including writing a kind of software schmitt trigger to consider the switch on when it rises above a certain value and off when it goes below another. Nothing worked.

With the above setup however, all I need to do is check to see if the reading I get from the touch switch pins is higher than 8, and they work beautifully. They're extremely responsive, and they happily stay off when not being touched. There's a little bouncing if if try to brush the touch switches as lightly as possible, but simply placing a finger between the contacts with little to no pressure gives a solid trigger.

So what do you think? Good technique?

Excellent, that is quite ingenious I am quite chuffed you thought of that.