Issues with voltage measurement

midam

New member
I noticed that there is a slight "problem" if using pinMode(Pin,INPUT).
I am using a voltage divider to measure 12V on a Teensy 3.5.

I know similar issues has been discussed before in

https://forum.pjrc.com/threads/69367-Issues-with-voltage-measurement-on-Teensy-4-0?highlight=pinmode+input+problems
and
https://forum.pjrc.com/threads/67341-T3-2-AnalogRead()-gives-wrong-values?highlight=voltage+measurement

but it does not really solve the problem.

The circuit looks like this:
514208cace395fd711000000.png
R1 = 4MOhm, R2 = 1MOhm, Vin= 12V, Vout should be roughly 2.4V
Applying Vout to A0 - A9 while using pinMode(Pin,INPUT) for each pin increases the measured Voltage up to close to 3V
Applying Vout to A10, A11, A25 and A26 with the same settings does not mess up the measurement.
If R1 = 1kOhm, and R2 is 1kOhm the error is just a couple of mV on the A0-A9

Those high resistor values are needed for a low power application.
I measured Vout directly on the divider as well as on the pins, no difference...

Code:
void setup() {
  // initialize the pin as an input.

  pinMode (A0,INPUT);
  pinMode (A1,INPUT);
  pinMode (A2,INPUT);
  pinMode (A3,INPUT);
  pinMode (A4,INPUT);
  pinMode (A5,INPUT);
  pinMode (A6,INPUT);
  pinMode (A7,INPUT);
  pinMode (A8,INPUT);
  pinMode (A9,INPUT);
  pinMode (A10,INPUT);
  pinMode (A11,INPUT);
  pinMode (A25,INPUT);
  pinMode (A26,INPUT);
}

void loop() {
}

As mentioned in other threads just not to use the pinMode function for any analog read solves this problem, but what causes it?
I am using the ResponsiveAnalogRead library and had to find out the hard way(again) that I need to comment the pinMode(pin, INPUT ); in the ResponsiveAnalogRead.cpp file to make sure everything works as intended.
Is there no fix or anything to make sure that declaring any analog pin as input will not mess up the measurements?
For now I am happy to use the "custom" ResponsiveAnalogRead library since I do not use this in any other projects, but in the long run it would be nice to use this library on all platforms without the need to manipulate this.

In my opinion declaring any analog pin as INPUT should not influence the measurement. Using INPUT_PULLUP or INPUT_PULLDOWN works as expected, why does INPUT then do stupid things?:confused:


Cheers
 
...this problem, but what causes it?

pinMode( x, INPUT ) connects the pin to digital input circuitry inside the Teensy. This does load the pin slightly as you have found.

In my opinion declaring any analog pin as INPUT should not influence the measurement. Using INPUT_PULLUP or INPUT_PULLDOWN works as expected, why does INPUT then do stupid things?

My opinion is that for a microcontroller you want your programming language to do exactly what you say and nothing more or less. As an example of a language that tries to protect the user from mistakes you get JAVA, where you spend all your programming effort writing try / catch code rather than addressing the needed elements of the design.

Why use a library to do analog reads? You might find Analog Read Averaging to work better than a library if that is the feature you use the library for.
https://forum.pjrc.com/threads/17146-What-exactly-does-analogReadAveraging-does.
 
Thanks rcarr for this.

pinMode( x, INPUT ) connects the pin to digital input circuitry inside the Teensy. This does load the pin slightly as you have found.
Is pinMode meant to do this? And if so why does other boards do not behave like this? I thought declaring pins in the setup is the normal procedure.

I was also expecting, that pinMode(x, INPUT) makes sure that the pin is just an input neither get pulled high, down or get charged through it.

I found that the ResponsiveAnalogRead library delivers the most accurate readings and reduces the noise quite good.
This is not really about the library use but I thought I include it here since this is kind of a hidden problem.

The teensy documentation claims also that by default all pins are declared as input. https://www.pjrc.com/teensy/td_digital.html
But clearly this is not the case since using the pinMode(x, INPUT) changes readings dramatically if used with a voltage divider.

I am happy with my solution and knowing that using the pinMode() will change things, but I thought I just post it here so other might not spend days on finding the cause in the circuit.
Maybe pinMode(x,INPUT) should just give the same result as not declaring it in the first place, but this is just my opinion.
 
And if so why does other boards do not behave like this?[...]
I was also expecting, that pinMode(x, INPUT) ...
That's what datasheets and manuals are for. Other boards use different controllers. Expectations do not help... and it's nothing that PJRC can influence - NXP produces the chip.
 
When one does not use pinMode() for a pin before doing an analogRead() on it on Teensy 4.x, the pad state is IOMUXC_PAD_DSE(6) | IOMUXC_PAD_PKE | IOMUXC_PAD_SPEED(2) according to the reference manual. In comparison, pinMode(pin, INPUT) sets the pad state to IOMUXC_PAD_DSE(7). IOMUX_PAD_DSE() sets the output drive strength (0 disabled, 7 maximum), IOMUXC_PAD_PKE enables the pad keeper (unless combined with IOMUXC_PAD_PUS, which causes the pull-up/down circuitry to be enabled instead), and IOMUXC_PAD_SPEED() sets the (output) slew rate (0 slowest, 3 fastest).

(In commit 576c748... in Jan 2, 2019, Paul explicitly enabled the output driver at maximum strength, IOMUX_PAD_DSE(7), for all input modes, with the message that this is "needed for direct I/O libs". Before that, INPUT state corresponded to IOMUXC_PAD_DSE(0). So, there might be more to this than appears on the surface. A LOT of time and effort has gone into Teensy development, after all!)

If there really is a difference on Teensy 4.x in ADC readings depending on whether one has a pinMode(pin, INPUT) for that specific pin or not, then I would add
Code:
#define INPUT_ADC 6
to teensy4/core_pins.h, and lines
Code:
		} else if (mode == INPUT_ADC) {
			*(p->pad) = IOMUXC_PAD_PKE;
to teensy4/digital.c pinMode() function, just before the line mentioning INPUT_PULLUP (line 150 right now). I would also check if IOMUXC_PAD_DSE(1..7) and/or IOMUXC_PAD_SPEED(1..3) affect the ADC readings. (There are 27 additional possibilities with IOMUXC_PAD_PKE, and an additional 28 ones without. So lots to test. At minimum, the shown one, plus the initial default of IOMUXC_PAD_DSE(6) | IOMUXC_PAD_PKE | IOMUXC_PAD_SPEED(2).)

In the code, then, I would use pinMode(pin, INPUT_ADC), and read say a hundred thousand samples. (The base comparison set would be the same without the pinMode() call.)
In addition to the mean (average), I'd also look at the median and the most common reading. When comparing the different settings, I would look at mean, median, and the most common reading, but also check if the distributions differ in any way other than random noise. Comparing their FFTs, perhaps?
 
Thank you for your answers so far.

I know that there is a lot of time and work in the whole Teensy development and I am happy that those exists.
I thought it was just good to mention this since there are still people like me out there how are not that deep in programming otherwise we might not even use such "ready to use boards" but would design our own board from scratch.

I was mostly confused why out of the sudden I had different readings after changing the code slightly. That`s why I wrote "the hard way" earlier since I discovered this a few years ago already.
Eventually one will have the same issue as I had and will find the solution in here.
 
When one does not use pinMode() for a pin before doing an analogRead() on it on Teensy 4.x, the pad state is IOMUXC_PAD_DSE(6) | IOMUXC_PAD_PKE | IOMUXC_PAD_SPEED(2) according to the reference manual. In comparison, pinMode(pin, INPUT) sets the pad state to IOMUXC_PAD_DSE(7). IOMUX_PAD_DSE() sets the output drive strength (0 disabled, 7 maximum), IOMUXC_PAD_PKE enables the pad keeper (unless combined with IOMUXC_PAD_PUS, which causes the pull-up/down circuitry to be enabled instead), and IOMUXC_PAD_SPEED() sets the (output) slew rate (0 slowest, 3 fastest).

(In commit 576c748... in Jan 2, 2019, Paul explicitly enabled the output driver at maximum strength, IOMUX_PAD_DSE(7), for all input modes, with the message that this is "needed for direct I/O libs". Before that, INPUT state corresponded to IOMUXC_PAD_DSE(0). So, there might be more to this than appears on the surface. A LOT of time and effort has gone into Teensy development, after all!)

If there really is a difference on Teensy 4.x in ADC readings depending on whether one has a pinMode(pin, INPUT) for that specific pin or not, then I would add
Code:
#define INPUT_ADC 6
to teensy4/core_pins.h, and lines
Code:
		} else if (mode == INPUT_ADC) {
			*(p->pad) = IOMUXC_PAD_PKE;
to teensy4/digital.c pinMode() function, just before the line mentioning INPUT_PULLUP (line 150 right now). I would also check if IOMUXC_PAD_DSE(1..7) and/or IOMUXC_PAD_SPEED(1..3) affect the ADC readings. (There are 27 additional possibilities with IOMUXC_PAD_PKE, and an additional 28 ones without. So lots to test. At minimum, the shown one, plus the initial default of IOMUXC_PAD_DSE(6) | IOMUXC_PAD_PKE | IOMUXC_PAD_SPEED(2).)

In the code, then, I would use pinMode(pin, INPUT_ADC), and read say a hundred thousand samples. (The base comparison set would be the same without the pinMode() call.)
In addition to the mean (average), I'd also look at the median and the most common reading. When comparing the different settings, I would look at mean, median, and the most common reading, but also check if the distributions differ in any way other than random noise. Comparing their FFTs, perhaps?

@PaulStoffregen Is it useful to add this code to Teensyduino? Would seem to stop a lot of confusion about Analogue Input.
 
@PaulStoffregen Is it useful to add this code to Teensyduino?

I think I remember that INPUT_DISABLE does the trick.
And as the code looks like this:
Code:
		} else { // INPUT_DISABLE
			*(p->pad) = IOMUXC_PAD_DSE(7) | IOMUXC_PAD_HYS;
		}
You can just add your INPUT_ADC:
Code:
#define INPUT_ADC INPUT_DISABLE //or any other unused random number ....
;-)
Would seem to stop a lot of confusion about Analogue Input.
As you see...no..it already exists.
The problem is a lack of clear documentation that you can actually find when you go to google.

Teensy has a lot of features... but... hard to find, or invisible.
 
Yes, I miss the towel, too. But the TARDIS would be a possible continuation now.
Code:
_______(_@_)_______
| POLICE      BOX |
|_________________|
 | _____ | _____ |
 | |###| | |###| |
 | |###| | |###| | 
 | _____ | _____ |
 | || || | || || |
 | ||_|| | ||_|| |
 | _____ |$_____ |
 | || || | || || |
 | ||_|| | ||_|| |
 | _____ | _____ |
 | || || | || || | 
 | ||_|| | ||_|| | 
 |       |       | 
 *****************

It's bigger on the inside.
 
Is it useful to add this code to Teensyduino?
We actually do need to check first, that it indeed does make a measurable difference. The code is trivial; I'm more worried about how to ensure reliable interpretation of the results, since I'm down to one Teensy 4.0 and one 4.1 myself (others already tied in other forever projects), plus one MicroMod. One or two or three samples does not a reliable dataset make, you see.

As to the entire problem at hand, I always use an op-amp buffer very much like Paul described in #6 in the other thread; and similarly an instrumentation amplifier for measuring current (by the voltage drop over a very small resistor, a fraction of an ohm). There is nothing specific to Teensys in this, because all MCUs have the same "problems" with ADC sampling.
 
Yes, I miss the towel, too. But the TARDIS would be a possible continuation now.
Code:
_______(_@_)_______
| POLICE      BOX |
|_________________|
 | _____ | _____ |
 | |###| | |###| |
 | |###| | |###| | 
 | _____ | _____ |
 | || || | || || |
 | ||_|| | ||_|| |
 | _____ |$_____ |
 | || || | || || |
 | ||_|| | ||_|| |
 | _____ | _____ |
 | || || | || || | 
 | ||_|| | ||_|| | 
 |       |       | 
 *****************

It's bigger on the inside.

That's brilliant!! Go on DO IT, only don't make the text italic.
Looks like it's shivering
 
Back
Top