Atmega328p Crystal and Prescaler question.

Hi All, please help me figure out the math behind building accurate timer for atmega328 chip.

So I read that chip that runs 16Mhz crystal has prescaler of 64, which means that millis() counter realistically updated (16000000/64/256=976.5625) times per second. Which means that millis() is not exactly counting millisecond, right? it basically means that in 1000 real life seconds millis() will show that only 976.6525 seconds had passed, or basically every hour it will lose 84 seconds? that is off by almost 1.5 minutes. Is that correct?

So if I want to have very precise results, I need a crystal of 16.384Mhz, in this case Timer0 will be updated precisely when 1 millisecond passed, did I get that right?

This is already accounted for in software - see wiring.c

I'm sorry but I don't quite understand, do you mean when millis() return value it is already adjusted to be exact or wiring.c is the library that does such measurement better than built-in millis() command?

So I read that chip that runs 16Mhz crystal has prescaler of 64

Yes. The timer0 overflow interrupt gets called every 1024us.

which means that millis() counter realistically updated (16000000/64/256=976.5625) times per second.

No; this is already accounted for. Effectively the ISR adds 1024 to a microseconds count, and millis() returns that value converted to milliseconds. (The code in reality works someone differently in an effort to be more efficient and avoid "divide." As DrAzzy says, you can read wiring.c for the details.
(Alas, some of the generality, subtlety, elegance, and documentation was lost when the "new" code was integrated with the arduino source code. You can read the original discussion here: Improving SIG_OVERFLOW (timer0, millis, etc) - Development - Arduino Forum

Well, I am running 2 codes right now, where I hand an interrupt to timer0 to increment "counter" value by 1.

in one version of a code I use delay(10000) command and measure counter value and it shows 9766
in second version I measure 10 seconds delay using millis() command and after millisNow-Millisold=10000 counter value also showing 9766

so that obviousely means that Timer0 interrupt called not once per millisecond but rather once per 0.9766 times per milllisecond.

Reading the original discussion now..... hopefully this is answered there too. Thanks.

when millis() return value it is already adjusted to be exact

Because of the correction for the 24 us fractional overflow, the millis() count will advance by 2 every approximately every 42 milliseconds.

void setup() {
  Serial.begin(115200);
}
void loop() {
  static unsigned long previousMillis = millis();
  if (millis() - previousMillis != 0) //did the millis count advance
  {
    if (millis() == previousMillis + 2)//did the millis count advance by 2
      {
        Serial.println(millis());
      }
    previousMillis = millis();
  }
}

Thanks, it starting to make sense now, still need to do alot of reading but, thanks. Also, it seems that new version if IDE already have corrected millis() too. I've come to this conclusion because if my code measures 1 second delay using millis() function and in return it shows only 976.5 timer updates, this means that millis() difference was precisely 1 second, otherwise timer would have returned 1000.
Well, atleast that's what it seems to me right now as I type this.

I hand an interrupt to timer0 to increment "counter" value by 1.

Obviously if you don't run the Arduino code, you don't get the Arduino corrections to make millis() come out right.

Arduino actually keeps two variables (grrr: legacy code support) :

volatile unsigned long timer0_overflow_count = 0;  // uncorrected interrupt count
volatile unsigned long timer0_millis = 0;          // correct millisecond count

You can access them directly and watch them diverge if you want...