Electronic – arduino – My own millis() function not accurate in the arduino

arduinoc

I wrote a program myself to implement the millis() and delay() function without the arduino library. I included a counting variable which counts every second and send its value every second via serial port. What I found is its value runs away from the true value by almost 2 seconds every 3 minutes. Is there anything wrong with my code? Or is that Serial.print() the culprit which may lag that routine? How much time does that Serial.print() takes to execute?

Here is the code:

Edit: I edited the code like this, but the count on the arduino still lag around 4seconds after 4 minutes. It lags 13 seconds after 10 minutes ie, it counts only 587 seconds after actual 600 seconds.

Edit 2: Here is my updated code. Still there is lag in the timing. I get a lag of around 6 seconds in 5 minutes.

#include "Arduino.h"
#include <avr/io.h>
#include <avr/interrupt.h>


void toggle_led(void);

unsigned long volatile millis_count = 0;
volatile char state = 1;
unsigned long volatile current_count = 0;
unsigned int volatile count = 0;

ISR(TIMER0_COMPA_vect) {        //Timer interrupt ISR

    millis_count++;
    if (millis_count - current_count == 1000) {
        current_count = millis_count;
        toggle_led();
        Serial.println(count++);
    }

}


int main(void) {

    init();

    TCCR0B = 0b11;  //Timer settings for interrupt at every millisecond
    OCR0A = 249;
    TIMSK0 |= 0b10;

    sei();
    Serial.begin(9600);
    DDRB |= 1<<5;

    for (;;) {



    }
}

void toggle_led(void) {

    PORTB ^= (1<<5);
}

Best Answer

Your delay will be the length of the newdelay() function plus the time it takes to send the serial data.

You have:

  1. Send the count through serial
  2. wait 1000ms
  3. toggle LED.

Each of those steps takes time.

To get an exact 1000ms time you can either trigger the serial sending from a 1 second interrupt, or you can examine the millisecond count within your loop and send the serial data when the milliseconds loop through 1000:

unsigned long lastmil;

lastmil == newmillis();
for(;;)
{
    if(((newmillis()%1000) == 0) && (lastmil != newmillis()))
    {
        lastmil = newmillis();
        Serial.println(count++);
        toggle_led();
    }
}

(another way to stop it looping multiple times for a single millisecond would be to add a short delay inside the if(..) to ensure that the routine takes at least 1ms)

The Serial.println() function will take a varying amount of time depending on:

  1. The baud rate in use
  2. The number of characters sent

At 9600 baud you are sending 9600 symbols per second. With "8N1" format that's 10 symbols per byte. So for a string of 3 digits, plus the carriage return and line feed, that will be 10 symbols × 5 characters, which is 50 symbols.

9600 baud is 0.0001041670.000104167s per symbol (or 104µS per symbol), so 50 symbols will be 0.00520835s (or 5.20835ms).

That is the actual transmission time. You then also have to add on to that the time taken to actually do the formatting of the data and the calling of the serial routines. These all take an unspecified amount of time. To find this out you will need to know the assembly code the routine compiles into then find the number of clock cycles each instruction takes and total them up.

Related Topic