Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

System Clock change from 1MHz to higher internal clock creates skewed ADC results- Prescaler? #858

Open
MD2240 opened this issue May 14, 2024 · 7 comments

Comments

@MD2240
Copy link

MD2240 commented May 14, 2024

Hello, I'm having an issue with ADC results when changing to a higher internal clock on an ATiny84a. I'm using the internal 1.1v reference, and a resistor divider to an analog input pin to monitor 9v battery voltage. All works perfectly with a 1MHz system clock. My project is requiring a faster clock, and trying 4 and 8 MHZ skews the ADC results. Instead of 1.1v = 1023, it now goes down to approx .98v = 1023 @ 8MHz. Am I missing something obvious here? I'm really more of a hardware guy, not code. Do I need to manually set the ADC prescale? Using ATtiny84a, Arduino Uno as ISP, Release core, no bootloader(burning bootloader at clock changes). Appreciate any insight!

@hmeijdam
Copy link
Contributor

You may want to post code, showing how you set reference, read the ADC etc. Best is a minimal sketch, but just enough to demonstrate your issue.

@MD2240
Copy link
Author

MD2240 commented May 14, 2024

I"ve done some more testing and have some results to share. First, here is the relevant code. Project is a sensor tester with an ssd1306 display and battery monitor.
``#include <SoftwareSerial.h>
#include <TinyWireM.h>
#include <Tiny4kOLED.h>

#define rxPin 7
#define txPin 8

SoftwareSerial mySerial(rxPin, txPin);

int battVoltPin = A3;

void setup()
{
pinMode(battVoltPin, INPUT);
analogReference(INTERNAL1V1);
mySerial.begin(9600);
}
void loop()
{
analogBattValue = analogRead(battVoltPin);
mySerial.print(analogBattValue);
mySerial.println();
}

At 1 Mhz clock, voltage measured at pin = 1.077 volts to keep at 1023 ADC
At 4 Mhz clock, voltage measured at pin = 1.009 volts to keep at 1023 ADC
at 8 Mhz clock, voltage measured at pin = .947 volts to keep at 1023 ADC
Now, I'm using an adjustable DC power supply and changing the input voltage to get these measurements. Voltage divider resistor values are 6640 ohm and 50k ohm. Goal is to measure from 9.1 volts to 6.5 volts. At 9.1 volts at 1MHz ADC is 1023 and voltage at the pin is 1.077 which is in spec(between 1.0-1.2 bandgap). The math puts the differences in the .060's volts range between the different clocks. I can adjust my resistors to compensate as I don't really need full range, but i'd like to figure this issue out and have it correct. Appreciate any insight!

@hmeijdam
Copy link
Contributor

hmeijdam commented May 23, 2024

I tried slightly different and kept the voltage at the same value just under the reference voltage. I also get different values at different clock speeds,

Sketch

/* Possible values to compute a shifting average in order to smooth the recieved pulse witdh */
#define AVG_WITH_1_VALUE        0
#define AVG_WITH_2_VALUES       1
#define AVG_WITH_4_VALUES       2
#define AVG_WITH_8_VALUES       3
#define AVG_WITH_16_VALUES      4

#define AVERAGE_LEVEL          AVG_WITH_8_VALUES  /* Choose here the average level among the above listed values */
/* Higher is the average level, more the system is stable (jitter suppression), but lesser is the reaction */

/* Macro for average */
#define AVERAGE(ValueToAverage,LastReceivedValue,AverageLevelInPowerOf2)  ValueToAverage=(((ValueToAverage)*((1<<(AverageLevelInPowerOf2))-1)+(LastReceivedValue))/(1<<(AverageLevelInPowerOf2)))
// AVERAGE(  xxxxx ,  yyyyy  , AVERAGE_LEVEL) ;

#define battVoltPin A3
uint16_t adc_average;

void setup()
{
pinMode(battVoltPin, INPUT);
analogReference(INTERNAL1V1);
delay(5);// allow internal reference to settle.
Serial.begin(9600); // TX pin is PA1 on Attiny84a
adc_average = analogRead(battVoltPin); // initail value for averaging
}
void loop()
{
AVERAGE(adc_average, analogRead(battVoltPin), AVERAGE_LEVEL);
Serial.print("CPU speed = ");
Serial.print(F_CPU/1000000);
Serial.print(" MHz, ADC value: ");
Serial.println(adc_average);
delay(500);
}

Result

CPU speed = 1 MHz, ADC value: 988
CPU speed = 1 MHz, ADC value: 988
CPU speed = 1 MHz, ADC value: 988
CPU speed = 1 MHz, ADC value: 988
CPU speed = 1 MHz, ADC value: 987
CPU speed = 1 MHz, ADC value: 987
CPU speed = 1 MHz, ADC value: 988
CPU speed = 1 MHz, ADC value: 987
CPU speed = 1 MHz, ADC value: 987
CPU speed = 1 MHz, ADC value: 988
CPU speed = 1 MHz, ADC value: 989
CPU speed = 1 MHz, ADC value: 989
CPU speed = 1 MHz, ADC value: 989

CPU speed = 4 MHz, ADC value: 1013
CPU speed = 4 MHz, ADC value: 1013
CPU speed = 4 MHz, ADC value: 1014
CPU speed = 4 MHz, ADC value: 1014
CPU speed = 4 MHz, ADC value: 1014
CPU speed = 4 MHz, ADC value: 1014
CPU speed = 4 MHz, ADC value: 1014
CPU speed = 4 MHz, ADC value: 1014
CPU speed = 4 MHz, ADC value: 1014
CPU speed = 4 MHz, ADC value: 1014
CPU speed = 4 MHz, ADC value: 1013

CPU speed = 8 MHz, ADC value: 1005
CPU speed = 8 MHz, ADC value: 1006
CPU speed = 8 MHz, ADC value: 1006
CPU speed = 8 MHz, ADC value: 1005
CPU speed = 8 MHz, ADC value: 1005
CPU speed = 8 MHz, ADC value: 1005
CPU speed = 8 MHz, ADC value: 1005
CPU speed = 8 MHz, ADC value: 1005
CPU speed = 8 MHz, ADC value: 1004
CPU speed = 8 MHz, ADC value: 1004
CPU speed = 8 MHz, ADC value: 1004
CPU speed = 8 MHz, ADC value: 1004

No explanation yet. I am curious if it will give different result when using the ADC directly instead of the analogRead() function.

@hmeijdam
Copy link
Contributor

hmeijdam commented May 23, 2024

Using the ADC directly gives still a difference in measurement with different clock-speeds.

/* Possible values to compute a shifting average */
#define AVG_WITH_1_VALUE        0
#define AVG_WITH_2_VALUES       1
#define AVG_WITH_4_VALUES       2
#define AVG_WITH_8_VALUES       3
#define AVG_WITH_16_VALUES      4

#define AVERAGE_LEVEL          AVG_WITH_8_VALUES  /* Choose here the average level among the above listed values */
/* Higher is the average level, more the system is stable (jitter suppression), but lesser is the reaction */

/* Macro for average */
#define AVERAGE(ValueToAverage,LastReceivedValue,AverageLevelInPowerOf2)  ValueToAverage=(((ValueToAverage)*((1<<(AverageLevelInPowerOf2))-1)+(LastReceivedValue))/(1<<(AverageLevelInPowerOf2)))
// AVERAGE(  xxxxx ,  yyyyy  , AVERAGE_LEVEL) ;

#define battVoltPin A3
uint16_t adc_average;

void setup()
{
ADMUX = _BV(REFS1) | _BV(MUX0) | _BV(MUX1);; //internal 1.1V reference and PA3 channel
ADCSRA |= _BV(ADEN); // Enable ADC
delay(5);// allow internal reference to settle.
Serial.begin(9600);
adc_average = analogReadDirect(); // initail value for averaging
}
void loop()
{
AVERAGE(adc_average, analogReadDirect(), AVERAGE_LEVEL);
Serial.print("CPU speed = ");
Serial.print(F_CPU/1000000);
Serial.print(" MHz, ADC value: ");
Serial.println(adc_average);
delay(500);
}

uint16_t analogReadDirect(){
  ADCSRA |= _BV(ADSC); // Start conversion
  while (bit_is_set(ADCSRA,ADSC)); // measuring, wait until conversion complete
  return ADC;  
}

@hmeijdam
Copy link
Contributor

hmeijdam commented May 23, 2024

And also with prescaling the ADC clock to be always 125KHz I get the same differences in ADC value with different clock speeds. I am out of clues now.

/* Possible values to compute a shifting average in order to smooth the recieved pulse witdh */
#define AVG_WITH_1_VALUE        0
#define AVG_WITH_2_VALUES       1
#define AVG_WITH_4_VALUES       2
#define AVG_WITH_8_VALUES       3
#define AVG_WITH_16_VALUES      4

#define AVERAGE_LEVEL          AVG_WITH_8_VALUES  /* Choose here the average level among the above listed values */
/* Higher is the average level, more the system is stable (jitter suppression), but lesser is the reaction */

/* Macro for average */
#define AVERAGE(ValueToAverage,LastReceivedValue,AverageLevelInPowerOf2)  ValueToAverage=(((ValueToAverage)*((1<<(AverageLevelInPowerOf2))-1)+(LastReceivedValue))/(1<<(AverageLevelInPowerOf2)))
// AVERAGE(  xxxxx ,  yyyyy  , AVERAGE_LEVEL) ;

#define battVoltPin A3
uint16_t adc_average;

void setup()
{
ADMUX = _BV(REFS1) | _BV(MUX0) | _BV(MUX1);; //internal 1.1V reference and PA3 channel
#if (F_CPU == 8000000)
ADCSRA = _BV(ADPS2) | _BV(ADPS1); // Set ADC prescaler to 64 125KHz ADC clock at 8MHz CPU
#elif (F_CPU == 4000000)
ADCSRA = _BV(ADPS2) | _BV(ADPS0); // Set ADC prescaler to 32 125KHz ADC clock at 4MHz CPU
#elif (F_CPU == 1000000)
ADCSRA = _BV(ADPS1) | _BV(ADPS0); // Set ADC prescaler to 8  125KHz ADC clock at 1MHz CPU
#else
#ifndef F_CPU
#error "F_CPU not defined"
#else
#error "F_CPU defined as an unsupported value for ADC prescaler test"
#endif
#endif
ADCSRA |= _BV(ADEN); // Enable ADC 
delay(5);// allow internal reference to settle.
Serial.begin(9600);
adc_average = analogReadDirect(); // initail value for averaging
}
void loop()
{
AVERAGE(adc_average, analogReadDirect(), AVERAGE_LEVEL);
Serial.print("CPU speed = ");
Serial.print(F_CPU/1000000);
Serial.print(" MHz, ADC value: ");
Serial.println(adc_average);
delay(500);
}

uint16_t analogReadDirect(){
  ADCSRA |= _BV(ADSC); // Start conversion
  while (bit_is_set(ADCSRA,ADSC)); // measuring, wait until conversion complete
  return ADC;  
}

Output

CPU speed = 1 MHz, ADC value: 992
CPU speed = 1 MHz, ADC value: 992
CPU speed = 1 MHz, ADC value: 992
CPU speed = 1 MHz, ADC value: 992
CPU speed = 1 MHz, ADC value: 992
CPU speed = 1 MHz, ADC value: 992
CPU speed = 1 MHz, ADC value: 992
CPU speed = 1 MHz, ADC value: 992
CPU speed = 1 MHz, ADC value: 992
CPU speed = 1 MHz, ADC value: 992
CPU speed = 1 MHz, ADC value: 992
CPU speed = 1 MHz, ADC value: 992
CPU speed = 1 MHz, ADC value: 992
CPU speed = 1 MHz, ADC value: 992
CPU speed = 1 MHz, ADC value: 992
CPU speed = 1 MHz, ADC value: 992
CPU speed = 4 MHz, ADC value: 1012
CPU speed = 4 MHz, ADC value: 1012
CPU speed = 4 MHz, ADC value: 1012
CPU speed = 4 MHz, ADC value: 1012
CPU speed = 4 MHz, ADC value: 1011
CPU speed = 4 MHz, ADC value: 1011
CPU speed = 4 MHz, ADC value: 1011
CPU speed = 4 MHz, ADC value: 1011
CPU speed = 4 MHz, ADC value: 1011
CPU speed = 4 MHz, ADC value: 1011
CPU speed = 4 MHz, ADC value: 1011
CPU speed = 4 MHz, ADC value: 1011
CPU speed = 4 MHz, ADC value: 1011
CPU speed = 4 MHz, ADC value: 1011
CPU speed = 4 MHz, ADC value: 1011
CPU speed = 4 MHz, ADC value: 1011
CPU speed = 4 MHz, ADC value: 1011
CPU speed = 4 MHz, ADC value: 1011
CPU speed = 4 MHz, ADC value: 1011
CPU speed = 8 MHz, ADC value: 1004
CPU speed = 8 MHz, ADC value: 1004
CPU speed = 8 MHz, ADC value: 1004
CPU speed = 8 MHz, ADC value: 1004
CPU speed = 8 MHz, ADC value: 1004
CPU speed = 8 MHz, ADC value: 1004
CPU speed = 8 MHz, ADC value: 1004
CPU speed = 8 MHz, ADC value: 1004
CPU speed = 8 MHz, ADC value: 1004
CPU speed = 8 MHz, ADC value: 1004
CPU speed = 8 MHz, ADC value: 1003

Let me post this sketch on the Arduino forum. That has quite a big audience and maybe someone picks it up.

@MD2240
Copy link
Author

MD2240 commented May 23, 2024

Appreciate you looking into this! Much of this is over my head, but I'm surprised no one else has noticed this.

@hmeijdam
Copy link
Contributor

https://forum.arduino.cc/t/different-adc-results-with-different-cpu-clockspeeds-attiny84a/1263347

Question posted. Who knows we can both learn something.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants