0

I'm using the Max7219 (8 digits, 7bits) led display to try and make a timer. I have downloaded an example code from the following site to set the led digits:

https://www.brainy-bits.com/arduino-countdown-timer-using-the-max7219-display/

However the original code simply loops as fast as possible and decriment the timer so it is not a true time. I tried to add a 10 ms delay to make it decriment 10ms on the right most digit so the 3rd digit to the right would be seconds, however I found this to be inaccurate and taking slightly longer than 1 second .

My question is is it possible to make a timer with 1ms accuracy using led display and if so how do I do it?

/* CountDown Timer version 2 using Max7219

Created by Yvan / https://Brainy-Bits.com
This code is in the public domain...
You can: copy it, use it, modify it, share it or just plain ignore it!
Thx!

*/

#define Max7219DIN 7  // Pin 7 connected to DIN (DataIN)
#define Max7219CLK 6  // Pin 6 connected to CLK
#define Max7219CS 5   // Pin 5 connected to CS

#define Buzzer 8  // Pin 8 connected to Buzzer +positive pin
int BuzzTrigger=0;  // Variable to store buzzer trigger value

#include "LedControl.h"  // LedControl Library created by Eberhard Fahle at http://playground.arduino.cc/Main/LedControl

LedControl lc=LedControl(Max7219DIN,Max7219CLK,Max7219CS,1);   // Last number represent the number of Max7219 Modules connected 

long int countnumber=24001000 ; // Countdown timer start value HH:MM:SSSS

// Variables to store individual numbers
int  firstnum=0;
int  secondnum=0;
int  thirdnum=0;
int  fournum=0;
int  fivenum=0;
int  sixnum=0;
int  sevennum=0;
int  eightnum=0;

void setup() {
    lc.shutdown(0,false);  // Wake up the display
    lc.setIntensity(0,7); // Set Brightness 0-15
    lc.clearDisplay(0);  // Clear display
    pinMode(Buzzer, OUTPUT);
    digitalWrite(Buzzer, LOW);  //Buzzer Off at startup


}

void loop() {

    for (countnumber; countnumber != -1; countnumber--) {
        String mystring = String(countnumber); // Transform Counter Int to String for manipulation

        // Convert number to a time value
        for (int z = 0; z < 6; z++) {

            if (mystring.substring(z) == "999999") {
                countnumber = (countnumber - 400000);
            }

            if (mystring.substring(z) == "9999") {
                countnumber = (countnumber - 4000);
            }
        }

        //  Display number on Display depending on number of digits remaining
        if (countnumber > 9999999) {
            firstnum = ((countnumber / 10000000) % 10);
            secondnum = countnumber / 1000000 % 10;
            thirdnum = countnumber / 100000 % 10;
            fournum = countnumber / 10000 % 10;
            fivenum = countnumber / 1000 % 10;
            sixnum = countnumber / 100 % 10;
            sevennum = countnumber / 10 % 10;
            eightnum = countnumber % 10;

            lc.setDigit(0, 7, firstnum, false);
            lc.setDigit(0, 6, secondnum, false);
            lc.setDigit(0, 5, thirdnum, false);
            lc.setDigit(0, 4, fournum, false);
            lc.setDigit(0, 3, fivenum, false);
            lc.setDigit(0, 2, sixnum, false);
            lc.setDigit(0, 1, sevennum, false);
            lc.setDigit(0, 0, eightnum, false);
            delay(10); //<<<<<<<<<<< I ADDED THIS
        } else {
            if (countnumber > 999999) {
                //      firstnum = ((countnumber/10000000)%10);
                secondnum = countnumber / 1000000 % 10;
                thirdnum = countnumber / 100000 % 10;
                fournum = countnumber / 10000 % 10;
                fivenum = countnumber / 1000 % 10;
                sixnum = countnumber / 100 % 10;
                sevennum = countnumber / 10 % 10;
                eightnum = countnumber % 10;

                lc.setChar(0, 7, '-', false);
                lc.setDigit(0, 6, secondnum, false);
                lc.setDigit(0, 5, thirdnum, false);
                lc.setDigit(0, 4, fournum, false);
                lc.setDigit(0, 3, fivenum, false);
                lc.setDigit(0, 2, sixnum, false);
                lc.setDigit(0, 1, sevennum, false);
                lc.setDigit(0, 0, eightnum, false);
                delay(10); //<<<<<<<<<<< I ADDED THIS
            } else {
                if (countnumber > 99999) {
                    //          firstnum = ((countnumber/10000000)%10);
                    //          secondnum = countnumber/1000000%10;
                    thirdnum = countnumber / 100000 % 10;
                    fournum = countnumber / 10000 % 10;
                    fivenum = countnumber / 1000 % 10;
                    sixnum = countnumber / 100 % 10;
                    sevennum = countnumber / 10 % 10;
                    eightnum = countnumber % 10;

                    lc.setChar(0, 7, '-', false);
                    lc.setChar(0, 6, '-', false);
                    lc.setDigit(0, 5, thirdnum, false);
                    lc.setDigit(0, 4, fournum, false);
                    lc.setDigit(0, 3, fivenum, false);
                    lc.setDigit(0, 2, sixnum, false);
                    lc.setDigit(0, 1, sevennum, false);
                    lc.setDigit(0, 0, eightnum, false);
                    delay(10); //<<<<<<<<<<< I ADDED THIS
                } else {
                    if (countnumber > 9999) {
                        //             firstnum = ((countnumber/10000000)%10);
                        //             secondnum = countnumber/1000000%10;
                        //             thirdnum = countnumber/100000%10;
                        fournum = countnumber / 10000 % 10;
                        fivenum = countnumber / 1000 % 10;
                        sixnum = countnumber / 100 % 10;
                        sevennum = countnumber / 10 % 10;
                        eightnum = countnumber % 10;

                        lc.setChar(0, 7, '-', false);
                        lc.setChar(0, 6, '-', false);
                        lc.setChar(0, 5, '-', false);
                        lc.setDigit(0, 4, fournum, false);
                        lc.setDigit(0, 3, fivenum, false);
                        lc.setDigit(0, 2, sixnum, false);
                        lc.setDigit(0, 1, sevennum, false);
                        lc.setDigit(0, 0, eightnum, false);
                        delay(10);//<<<<<<<<<<< I ADDED THIS
                    } else {
                        if (countnumber > 999) {
                            //              firstnum = ((countnumber/10000000)%10);
                            //              secondnum = countnumber/1000000%10;
                            //              thirdnum = countnumber/100000%10;
                            //              fournum = countnumber/10000%10;
                            fivenum = countnumber / 1000 % 10;
                            sixnum = countnumber / 100 % 10;
                            sevennum = countnumber / 10 % 10;
                            eightnum = countnumber % 10;

                            lc.setChar(0, 7, '-', false);
                            lc.setChar(0, 6, '-', false);
                            lc.setChar(0, 5, '-', false);
                            lc.setChar(0, 4, '-', false);
                            lc.setDigit(0, 3, fivenum, false);
                            lc.setDigit(0, 2, sixnum, false);
                            lc.setDigit(0, 1, sevennum, false);
                            lc.setDigit(0, 0, eightnum, false);
                            delay(10);//<<<<<<<<<<< I ADDED THIS
                        } else {
                            //              firstnum = ((countnumber/10000000)%10);
                            //              secondnum = countnumber/1000000%10;
                            //              thirdnum = countnumber/100000%10;
                            //              fournum = countnumber/10000%10;
                            //              fivenum = countnumber/1000%10;
                            sixnum = countnumber / 100 % 10;
                            sevennum = countnumber / 10 % 10;
                            eightnum = countnumber % 10;
a
                            lc.setChar(0, 7, '-', false);
                            lc.setChar(0, 6, '-', false);
                            lc.setChar(0, 5, '-', false);
                            lc.setChar(0, 4, '-', false);
                            lc.setChar(0, 3, '-', false);
                            lc.setDigit(0, 2, sixnum, false);
                            lc.setDigit(0, 1, sevennum, false);
                            lc.setDigit(0, 0, eightnum, false);
                            delay(10);//<<<<<<<<<<< I ADDED THIS
                        }

                    }
                }
            }
        }


    }
}

new implementation using millis

#define Max7219DIN 7  // Pin 7 connected to DIN (DataIN)
#define Max7219CLK 6  // Pin 6 connected to CLK
#define Max7219CS 5   // Pin 5 connected to CS


#include "LedControl.h"  // LedControl Library created by Eberhard Fahle at http://playground.arduino.cc/Main/LedControl

LedControl lc=LedControl(Max7219DIN,Max7219CLK,Max7219CS,1);   // Last number represent the number of Max7219 Modules connected 

unsigned long countnumber;

// Variables to store individual numbers
int  firstnum=0;
int  secondnum=0;
int  thirdnum=0;
int  fournum=0;
int  fivenum=0;
int  sixnum=0;
int  sevennum=0;
int  eightnum=0;

void setup() {
  lc.shutdown(0,false);  // Wake up the display
  lc.setIntensity(0,7); // Set Brightness 0-15
  lc.clearDisplay(0);  // Clear display
  Serial.begin(9600);
}

void loop() {

  countnumber = millis();

  firstnum = countnumber / 10000000 % 10;
  secondnum = countnumber / 1000000 % 10;
  thirdnum = countnumber / 100000 % 10;
  fournum = countnumber / 10000 % 10;
  fivenum = countnumber / 1000 % 10;
  sixnum = countnumber / 100 % 10;
  sevennum = countnumber / 10 % 10;
  eightnum = countnumber % 10;

  lc.setDigit(0, 7, firstnum, false);
  lc.setDigit(0, 6, secondnum, false);
  lc.setDigit(0, 5, thirdnum, false);
  lc.setDigit(0, 4, fournum, false);
  lc.setDigit(0, 3, fivenum, false);
  lc.setDigit(0, 2, sixnum, false);
  lc.setDigit(0, 1, sevennum, false);
  lc.setDigit(0, 0, eightnum, false); 

}
  • You code also takes some time to execute. So your loop takes 10ms, plus the time the code takes. To make it 10ms independent of the amount of code, you store the value of millis() at the beginning of the loop, and them at the end of the loop, wait till the difference between millis() and the stored millis value is greater that, or equal to 10. – Gerben Oct 31 '19 at 10:20
  • Requiring “1 ms accuracy” doesn't make any sense if you don't specify over what time span you want that accuracy. Getting 1 ms accuracy is trivial if you are measuring a 100 ms time interval. It is far from trivial if you are measuring a 1 year interval. The accuracy you can get on an Arduino out of the box depends on the measured interval, on whether your Arduino has a crystal or a resonator, and on whether you are willing to calibrate it. Please read jorisvr.nl/article/arduino-frequency to get a general idea on what “accuracy” can mean in respect to Arduino timing. – Edgar Bonet Oct 31 '19 at 11:07
  • @EdgarBonet what about over 5min trivial or no? – bakalolo Oct 31 '19 at 21:19
  • 1 ms ÷ 5 min = 3.33 ppm. Not trivial but doable. You won't get that accuracy out of the Arduino's internal clock unless it is a crystal and you calibrate it against a reliable reference. Easiest solution would probably be using a high accuracy RTC, like the DS3231, which integrates a temperature-compensated crystal oscillator (TCXO). The ubiquitous DS1307 will likely not be accurate enough. Use either the 32kHz or the SQW output and one of your MCU's counters. Please, tag your question with the type of Arduino you are using. – Edgar Bonet Oct 31 '19 at 23:01
1

You generally cannot rely on a processor for correct time keeping without a real time clock somewhere in your circuit. However, the best way of doing things at certain times, is to call millis() which will return the number of milliseconds since the program started. With that number you can determine how long something has taken (eg. 1 second) and then do something in response. You can, for example, print out how long your loop is taking by setting a long integer to millis() at the start of the sketch and then subtracting it from mills() at the end of the loop and printing out the result.

void loop() {
   long start_time = millis();
   delay(500);
   long end_time = mills();
   Serial.println(end_time - start_time);
}
| improve this answer | |
  • What do you rekon is the accuracy without real time clock? within 20ms? – bakalolo Oct 31 '19 at 7:21
  • 1
    From this article I understand that millis is incremented each 1.024ms, but if the error approaches 1ms then value is automatically corrected. Therefore in span of 20 ms error could accumulate to be close to half of a millisecond. – Filip Franik Oct 31 '19 at 7:47
  • 1
    @bakalolo Try micros()? – ElectronSurf Oct 31 '19 at 8:36
0

You might think that using the BlinkWithoutDelay example with a 1 ms period would produce a reasonably accurate count down timer, but it really doesn’t. If you run this sketch, you’ll see the result of a 10 second test. It shows an elapsed time of 10240 ms.

// 2024 storage, 251 memory.
// Times up. millis() = 10240
unsigned long countDownTime = 10000;   // Time in ms.
unsigned long displayUpdateDelay = 50; // Time in ms.
unsigned long loopDelay = 1;           // Time in ms.
unsigned long currentMillis = 0;
unsigned long previousMillisLoopDelay = 0;
unsigned long previousMillisDisplayUpdate = 0;
byte doOnce = 0;

void setup(){
  Serial.begin(9600);
  Serial.println("Start countdown timer...");
}

void loop(){

  currentMillis = millis();

  if(currentMillis - previousMillisLoopDelay >= loopDelay){
    previousMillisLoopDelay = currentMillis;
    countDownTime--;
    if(countDownTime == 0){
      doOnce = 1;
      Serial.print("Times up. millis() = ");
      Serial.println(currentMillis);
    }
  }

  if(currentMillis - previousMillisDisplayUpdate >= displayUpdateDelay){
    previousMillisDisplayUpdate = currentMillis;
    if(doOnce == 0){
      displayTimeRemaining();
    }
  }

}

void displayTimeRemaining(){
  Serial.println(countDownTime);
}

If you change the method of updating the "previousMillis" variable, you can improve the results significantly. Here is another sketch which at least gives a correct mills count, even after 60 seconds. The only way to know for sure how accurate the results are would be to compare the Arduino count down timer with one that is know to be very accurate.

// 2032 storage, 251 memory.
// Times up. millis() = 10000. 60 second test = 60000.
unsigned long countDownTime = 10000;   // Time in ms.
unsigned long displayUpdateDelay = 50; // Time in ms.
unsigned long loopDelay = 1;           // Time in ms.
unsigned long currentMillis = 0;
unsigned long previousMillisLoopDelay = 0;
unsigned long previousMillisDisplayUpdate = 0;
byte doOnce = 0;

void setup(){
  Serial.begin(9600);
  Serial.println("Start countdown timer...");
}

void loop(){

  currentMillis = millis();

  if(currentMillis - previousMillisLoopDelay >= loopDelay){
    previousMillisLoopDelay += loopDelay;
    countDownTime--;
    if(countDownTime == 0){
      doOnce = 1;
      Serial.print("Times up. millis() = ");
      Serial.println(currentMillis);
    }
  }

  if(currentMillis - previousMillisDisplayUpdate >= displayUpdateDelay){
    previousMillisDisplayUpdate += displayUpdateDelay;
    if(doOnce == 0){
      displayTimeRemaining();
    }
  }

}

void displayTimeRemaining(){
  Serial.println(countDownTime);
}

Here is a link to a good library for your display: HCMAX7219 library Github.

EDIT

This sketch incorporates the code from the previous sketches, except I've used the HCMAX7219 library instead of the code from brainy-bits.com.

EDIT 2

Here's an updated sketch that displays a count down timer in HHMMSSmm format, with leading zeros. If you use / build an Arduino with a good crystal oscillator, this sketch might work quite well.

// 2376 bytes storage, 154 bytes memory.
// MODULE.....UNO/NANO.......MEGA
// VCC..........+5V..........+5V
// GND..........GND..........GND
// CS (LOAD)....10...........10
// DIN..........11...........51
// CLK..........13...........52
#include <HCMAX7219.h>

const byte CS_PIN = 10;
unsigned long countDownTime = (86400UL * 1000UL) - 10UL; // Time in ms.
unsigned long displayUpdateDelay = 25; // Time in ms.
unsigned long loopDelay = 1;           // Time in ms.
unsigned long currentMillis = 0;
unsigned long previousMillisLoopDelay = 0;
unsigned long previousMillisDisplayUpdate = 0;
byte hours, minutes, seconds;
unsigned int milliseconds;
byte doOnce = 0;

HCMAX7219 HCMAX7219(CS_PIN);

void setup(){

  // Set display brightness. Valid values are 0 (min) to 15 (max).
  HCMAX7219.Intensity(2, 0); // 0 is the first MAX7219 module.

}

void loop(){

  currentMillis = millis();
  HCMAX7219.Clear();

  if(currentMillis - previousMillisLoopDelay >= loopDelay){
    previousMillisLoopDelay += loopDelay;
    countDownTime--;
    if(countDownTime == 0){
      doOnce = 1;
      HCMAX7219.print7Seg("TIMES UP", 8);
      HCMAX7219.Refresh();
    }
  }

  if(currentMillis - previousMillisDisplayUpdate >= displayUpdateDelay){
    previousMillisDisplayUpdate += displayUpdateDelay;
    if(doOnce == 0){
      displayTimeRemaining();
    }
  }

}

void displayTimeRemaining(){

  milliseconds = (countDownTime % 1000UL) / 10UL;
  seconds = (countDownTime / 1000UL) % 60UL;
  minutes = (countDownTime / (1000UL * 60UL)) % 60UL;
  hours = (countDownTime / (1000UL * 60UL * 60UL)) % 24UL;

  if(hours < 10){
    HCMAX7219.print7Seg("0", 8);
    HCMAX7219.print7Seg(hours, 7);
  }
  else{HCMAX7219.print7Seg(hours, 8);}

  if(minutes < 10){
    HCMAX7219.print7Seg("0", 6);
    HCMAX7219.print7Seg(minutes, 5);
  }
  else{HCMAX7219.print7Seg(minutes, 6);}

  if(seconds < 10){
    HCMAX7219.print7Seg("0", 4);
    HCMAX7219.print7Seg(seconds, 3);
  }
  else{HCMAX7219.print7Seg(seconds, 4);}

  if(milliseconds < 10){
    HCMAX7219.print7Seg("0", 2);
    HCMAX7219.print7Seg(milliseconds, 1);
  }
  else{HCMAX7219.print7Seg(milliseconds, 2);}

  HCMAX7219.Refresh();
}
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.