SpenceKonde / ATTinyCore

Arduino core for ATtiny 1634, 828, x313, x4, x41, x5, x61, x7 and x8
Other
1.53k stars 301 forks source link

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

Open MD2240 opened 1 month ago

MD2240 commented 1 month ago

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 commented 1 month ago

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 commented 1 month ago

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

include

include

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 commented 1 month ago

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 commented 1 month ago

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 commented 1 month ago

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 commented 1 month ago

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

hmeijdam commented 1 month ago

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

Question posted. Who knows we can both learn something.