Teensy 3.1 Analog to digital converter value jumps like digital IO pin hysteresis

Status
Not open for further replies.

Ed_

New member
I am getting unexpected jumps in ADC data when varying the input voltage using a potentiometer (data is pictured). I have tested with two teensy 3.1 boards and several potentiometers and other voltage sources and all show the same behavior. The analog value jumps at the same time the digital value of the pin would change (confirmed later, below). What do I have to do to get the ADC to act like a normal analog input and vary smoothly, without the gaps?
Data:
adc_data_gaps.jpg
Zoomed in:
adc_data_gaps_zoom.jpg

Hardware:
Teensy 3.1 (The mask revision is 2N36B which does have ADC related errata (pdf) but nothing like this error)
10k potentiometer from AGND to 3V, with the wiper measured on A9 (pin 23)

Software: Arduino 1.0.5, Teensyduino 1.19, Windows 8.1

Code:
Code:
const int readPin = A9;

void setup()
{
    pinMode(readPin, INPUT);
    Serial.begin(9600);
}

void loop()
{
      Serial.println(analogRead(readPin), DEC);
      Serial.send_now();

  delay(1);
}

Procedure:
Move the potentiometer around a few times and then plot the data.

Expected result:
The data should vary smoothly from end to end.

Actual Result:
There are jumps in the data at certain values based on whether the data is rising or falling.

---
Reading the pin as a digital input confirms that the jumps happen when the digital value changes.
Code:
const int readPin = A9;

void setup()
{
    pinMode(readPin, INPUT);
    Serial.begin(9600);
}

void loop()
{
      Serial.print(analogRead(readPin), DEC);
      Serial.print(", ");
      Serial.print(digitalRead(readPin), DEC);
      Serial.println("");
      Serial.send_now();

  delay(1);
}

When the data is plotted the digital transitions align with the analog jumps.
Blue is the analog read value (0-1023) and green is the digital value shifted vertically and scaled to be visible. The analog jump matches the transition, or the next reading occasionally, as expected for both the rising and falling transitions.
adc_data_gaps_digital_data_overlay.jpg
 
I see this is your first post, welcome! Doubly so for providing clear details, exact code, electrical connections etc.

Assuming your wires from Teensy to the pot are secure and not very long, the first thing I would try would be a 100nF decoupling capacitor between AGND and 3V. The second thing would be to use AREF instead of 3V. Thirdly, re-arrange the code so that analog read and USB communication are separate. Fouthly, collect a bunch of data in an array and then send it over USB in a separate operation.

Code:
const int readPin = A9;

int a;

void setup()
{
    pinMode(readPin, INPUT);
    Serial.begin(9600);
}

void loop()
{
      a = analogRead(readPin);
      delay(1);
      Serial.println(a, DEC);
      delay(1);
}

Since you are sending data over Serial (thus, USB) I assume you are powering the teensy by USB from the computer?
 
I'm unable to explain your finding of a discontinuity at the low to high logic transition point. I haven't seen that before.
 
The results when using a 100nF decoupling capacitor between AGND and 3V, and also when using AREF for power are the same. Re-arranging the code also yields the same result.

An oscilloscope measures the same jumps as the ADC. When the analog input pin on the teensy is disconnected from the signal source it measures smoothly as expected.

When measuring a sine wave the following code will produce the issue, but removing the pinMode command and uploading it makes it act normally. Switching between the two is kind of funny because the top portion of the sine wave jumps up with the little gaps in the middle.
Code:
void setup()
{
    pinMode(readPin, INPUT);
}

void loop()
{
}

The signal with pinMode (annotated to point out the jump) and without pinMode (normal):
input_jump.jpginput_ok.jpg

This seems to narrow it down to something in pinMode configuring the input differently than expected.
 
Yes, that does look like a bug. Most example code that uses analog input does not set the mode. Also the default mode for digital pins is input. However, if a pin has been used as a digital output, and then that same pin is used as an analog input, then it is necessary to set the pin as input before it can be read and that should work correctly.

I wonder if a distinct keyword, like
Code:
pinMode(pin, ANALOG_INPUT);
is needed here because for digital input you do want the threasholding around the changeover point.

Paul (creator of Teensy) is away at an event for the next few days, back on Monday I believe. There is a new version of Teensyduino (1.20) being prepared for release, release candidate 1 was out last week. So a bug fix could get rolled into rc2.
 
Last edited:
Bump for Paul to see. This does look like a bug. Advising to "not set the mode" for analog pins is good, but if a pin is ever used for digital output there is no way to set it back to analog input without triggering this bug. So the another value looks like the best option to me. For consistency with INPUT_PULLUP the value should probably be INPUT_ANALOG. This woulld be added to core_pins.h where the other definitions are.
 
Last edited:
I think this is what would be needed. In cores/teensy3/core_pins.h
Code:
#define INPUT_ANALOG	3
in cores/teensy3/pins_teensy.c change pinmode to add the new value
Code:
void pinMode(uint8_t pin, uint8_t mode)
{
	volatile uint32_t *config;

	if (pin >= CORE_NUM_DIGITAL) return;
	config = portConfigRegister(pin);

	if (mode == OUTPUT) {
		*portModeRegister(pin) = 1;
		*config = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
	} else {
		*portModeRegister(pin) = 0;
		if (mode == INPUT_ANALOG) {
			*config = PORT_PCR_MUX(0);
		} else if (mode == INPUT) {
			*config = PORT_PCR_MUX(1);
		} else {
			*config = PORT_PCR_MUX(1) | PORT_PCR_PE | PORT_PCR_PS; // pullup
		}
	}
}
 
I like the INPUT_ANALOG idea. For processors like AVRs it could have the same value as INPUT to keep code relatively portable, but it would disable the digital functions of the pin on the K20 series unlike on AVRs.

The change in voltage I was seeing is pin leakage error from the digital pins. In the reference manual it mentions
"31.6.2.2 Pin leakage error
Leakage on the I/O pins can cause conversion error if the external analog source resistance, R_AS, is high. If this error cannot be tolerated by the application, keep R_AS lower than V_REFH / (4 × I_LEAK× 2^N ) for less than 1/4 LSB leakage error, where N = 8 in 8-bit mode, 10 in 10-bit mode, 12 in 12-bit mode, or 16 in 16-bit mode." and lists the input leakage in the K20 Sub-Family Data Sheet Data Sheet in Table 4. Voltage and current operating behaviors
So, I can use the analog input as a purely analog input like pinMode(INPUT_ANALOG) or buffer the signal since the source resistance is 10kOhms.
 
Status
Not open for further replies.
Back
Top