Timing/Timer question(s)

Go To Last Post
10 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hey all,

 

I am doing a little one off thingie using an UNO.  I want to do certain things at certain times, and if I was doing this outside the Arduino world this is a piece of cake.  Set up a time to tick off some time, fire interrupt, increment variable, if statement in MAIN to compare the count to a reference...MATCH do this blah blah blah.

 

But its my understanding that in the UNO the timers are allocated for other tasks and as such are not readily(easily) available.  Am I correct?  I understand that tampering with the timers can cause havoc in various libraries and background functions so I thought better to ask before jumping into the deep end of an empty pool.

 

I have elected to use some of the ready made libraries for the two parts I am using in this application.  An OLED, and a D/A converter, as the end user is going to use the Arduino IDE for changes so I figured I would make it as easy for them as possible.

 

 

Looking at the Arduino Interrupts tutorials but those seem to be for I/O not internal, but I am searching.

 

Thanks,

JIm

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Are you dissatisfied with the millis() and micros() provided by Arduino?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jgmdesign wrote:
Am I correct? 
Not entirely. I think that by default it's just Timer 1 that is "taken" (for millis() etc). It only starts to use the other timers when you start using analogWrite() (which then uses the other timers to do PWM output).

 

Of course you can work with the timer it provides anyway. Something like:

unsigned long lastTime = 0;

void loop() {
    if ((millis() - lastTime) > 5000) {
        // do something after 5 seconds
        lastTime = millis();
    }
}

in fact this is such a common usage in Arduino that Paul Stoffregen (Mr Teensy) donated the following clever technology to the entire Arduino projects (not just for Teensyduino use):

elapsedMillis lastMillis = 0;

void loop() {
    if (lastMillis > 5000) {
        // do 5s work
        lastMillis = 0;
    }
}

the variable type "elapsedMillis" is an auto-incrementing variable. From the point you write to it, it automatically increments by one each millisecond. So you just set it to 0 then in loop() keep checking until it has exceeded some timing threshold.

 

(in AVR terms it's a bit like setting up a timer ISR and having that incrementing some kind of count variable with the ISR configured to fire every 1ms).

 

The elapsedMillis page on arduino.cc leads to: https://github.com/pfeerick/elap...

 

PS the quick way to see where Arduino may be using timers is by searching for "TCCR" in the core directory:

D:\arduino-1.8.13\hardware\arduino\avr\cores\arduino>grep TCCR *
Tone.cpp:#define TCCR2A TCCR2
Tone.cpp:#define TCCR2B TCCR2
Tone.cpp:      #if defined(TCCR0A) && defined(TCCR0B) && defined(WGM01)
Tone.cpp:        TCCR0A = 0;
Tone.cpp:        TCCR0B = 0;
Tone.cpp:        bitWrite(TCCR0A, WGM01, 1);
Tone.cpp:        bitWrite(TCCR0B, CS00, 1);
Tone.cpp:      #if defined(TCCR1A) && defined(TCCR1B) && defined(WGM12)
Tone.cpp:        TCCR1A = 0;
Tone.cpp:        TCCR1B = 0;
Tone.cpp:        bitWrite(TCCR1B, WGM12, 1);
Tone.cpp:        bitWrite(TCCR1B, CS10, 1);
Tone.cpp:      #if defined(TCCR2A) && defined(TCCR2B)
Tone.cpp:        TCCR2A = 0;
Tone.cpp:        TCCR2B = 0;
Tone.cpp:        bitWrite(TCCR2A, WGM21, 1);
Tone.cpp:        bitWrite(TCCR2B, CS20, 1);
Tone.cpp:      #if defined(TCCR3A) && defined(TCCR3B) &&  defined(TIMSK3)
Tone.cpp:        TCCR3A = 0;
Tone.cpp:        TCCR3B = 0;
Tone.cpp:        bitWrite(TCCR3B, WGM32, 1);
Tone.cpp:        bitWrite(TCCR3B, CS30, 1);
Tone.cpp:      #if defined(TCCR4A) && defined(TCCR4B) &&  defined(TIMSK4)
Tone.cpp:        TCCR4A = 0;
Tone.cpp:        TCCR4B = 0;
Tone.cpp:          bitWrite(TCCR4B, WGM42, 1);
Tone.cpp:          bitWrite(TCCR4B, CS43, 1);
Tone.cpp:        bitWrite(TCCR4B, CS40, 1);
Tone.cpp:      #if defined(TCCR5A) && defined(TCCR5B) &&  defined(TIMSK5)
Tone.cpp:        TCCR5A = 0;
Tone.cpp:        TCCR5B = 0;
Tone.cpp:        bitWrite(TCCR5B, WGM52, 1);
Tone.cpp:        bitWrite(TCCR5B, CS50, 1);
Tone.cpp:#if defined(TCCR0B)
Tone.cpp:        TCCR0B = (TCCR0B & 0b11111000) | prescalarbits;
Tone.cpp:#if defined(TCCR2B)
Tone.cpp:        TCCR2B = (TCCR2B & 0b11111000) | prescalarbits;
Tone.cpp:#if defined(TCCR1B)
Tone.cpp:        TCCR1B = (TCCR1B & 0b11111000) | prescalarbits;
Tone.cpp:#if defined(TCCR3B)
Tone.cpp:        TCCR3B = (TCCR3B & 0b11111000) | prescalarbits;
Tone.cpp:#if defined(TCCR4B)
Tone.cpp:        TCCR4B = (TCCR4B & 0b11111000) | prescalarbits;
Tone.cpp:#if defined(TCCR5B)
Tone.cpp:        TCCR5B = (TCCR5B & 0b11111000) | prescalarbits;
Tone.cpp:      #if defined(TCCR2A) && defined(WGM20)
Tone.cpp:        TCCR2A = (1 << WGM20);
Tone.cpp:      #if defined(TCCR2B) && defined(CS22)
Tone.cpp:        TCCR2B = (TCCR2B & 0b11111000) | (1 << CS22);
wiring.c:#if defined(TCCR0A) && defined(WGM01)
wiring.c:       sbi(TCCR0A, WGM01);
wiring.c:       sbi(TCCR0A, WGM00);
wiring.c:       sbi(TCCR0, CS02);
wiring.c:#elif defined(TCCR0) && defined(CS01) && defined(CS00)
wiring.c:       sbi(TCCR0, CS01);
wiring.c:       sbi(TCCR0, CS00);
wiring.c:#elif defined(TCCR0B) && defined(CS01) && defined(CS00)
wiring.c:       sbi(TCCR0B, CS01);
wiring.c:       sbi(TCCR0B, CS00);
wiring.c:#elif defined(TCCR0A) && defined(CS01) && defined(CS00)
wiring.c:       sbi(TCCR0A, CS01);
wiring.c:       sbi(TCCR0A, CS00);
wiring.c:#if defined(TCCR1B) && defined(CS11) && defined(CS10)
wiring.c:       TCCR1B = 0;
wiring.c:       sbi(TCCR1B, CS11);
wiring.c:       sbi(TCCR1B, CS10);
wiring.c:#elif defined(TCCR1) && defined(CS11) && defined(CS10)
wiring.c:       sbi(TCCR1, CS11);
wiring.c:       sbi(TCCR1, CS10);
wiring.c:#if defined(TCCR1A) && defined(WGM10)
wiring.c:       sbi(TCCR1A, WGM10);
wiring.c:#if defined(TCCR2) && defined(CS22)
wiring.c:       sbi(TCCR2, CS22);
wiring.c:#elif defined(TCCR2B) && defined(CS22)
wiring.c:       sbi(TCCR2B, CS22);
wiring.c:#if defined(TCCR2) && defined(WGM20)
wiring.c:       sbi(TCCR2, WGM20);
wiring.c:#elif defined(TCCR2A) && defined(WGM20)
wiring.c:       sbi(TCCR2A, WGM20);
wiring.c:#if defined(TCCR3B) && defined(CS31) && defined(WGM30)
wiring.c:       sbi(TCCR3B, CS31);              // set timer 3 prescale factor to 64
wiring.c:       sbi(TCCR3B, CS30);
wiring.c:       sbi(TCCR3A, WGM30);             // put timer 3 in 8-bit phase correct pwm mode
wiring.c:#if defined(TCCR4A) && defined(TCCR4B) && defined(TCCR4D) /* beginning of timer4 block for 32U4 and similar */
wiring.c:       sbi(TCCR4B, CS42);              // set timer4 prescale factor to 64
wiring.c:       sbi(TCCR4B, CS41);
wiring.c:       sbi(TCCR4B, CS40);
wiring.c:       sbi(TCCR4D, WGM40);             // put timer 4 in phase- and frequency-correct PWM mode
wiring.c:       sbi(TCCR4A, PWM4A);             // enable PWM mode for comparator OCR4A
wiring.c:       sbi(TCCR4C, PWM4D);             // enable PWM mode for comparator OCR4D
wiring.c:#if defined(TCCR4B) && defined(CS41) && defined(WGM40)
wiring.c:       sbi(TCCR4B, CS41);              // set timer 4 prescale factor to 64
wiring.c:       sbi(TCCR4B, CS40);
wiring.c:       sbi(TCCR4A, WGM40);             // put timer 4 in 8-bit phase correct pwm mode
wiring.c:#if defined(TCCR5B) && defined(CS51) && defined(WGM50)
wiring.c:       sbi(TCCR5B, CS51);              // set timer 5 prescale factor to 64
wiring.c:       sbi(TCCR5B, CS50);
wiring.c:       sbi(TCCR5A, WGM50);             // put timer 5 in 8-bit phase correct pwm mode
wiring_analog.c:                        #if defined(TCCR0) && defined(COM00) && !defined(__AVR_ATmega8__)
wiring_analog.c:                                sbi(TCCR0, COM00);
wiring_analog.c:                        #if defined(TCCR0A) && defined(COM0A1)
wiring_analog.c:                                sbi(TCCR0A, COM0A1);
wiring_analog.c:                        #if defined(TCCR0A) && defined(COM0B1)
wiring_analog.c:                                sbi(TCCR0A, COM0B1);
wiring_analog.c:                        #if defined(TCCR1A) && defined(COM1A1)
wiring_analog.c:                                sbi(TCCR1A, COM1A1);
wiring_analog.c:                        #if defined(TCCR1A) && defined(COM1B1)
wiring_analog.c:                                sbi(TCCR1A, COM1B1);
wiring_analog.c:                        #if defined(TCCR1A) && defined(COM1C1)
wiring_analog.c:                                sbi(TCCR1A, COM1C1);
wiring_analog.c:                        #if defined(TCCR2) && defined(COM21)
wiring_analog.c:                                sbi(TCCR2, COM21);
wiring_analog.c:                        #if defined(TCCR2A) && defined(COM2A1)
wiring_analog.c:                                sbi(TCCR2A, COM2A1);
wiring_analog.c:                        #if defined(TCCR2A) && defined(COM2B1)
wiring_analog.c:                                sbi(TCCR2A, COM2B1);
wiring_analog.c:                        #if defined(TCCR3A) && defined(COM3A1)
wiring_analog.c:                                sbi(TCCR3A, COM3A1);
wiring_analog.c:                        #if defined(TCCR3A) && defined(COM3B1)
wiring_analog.c:                                sbi(TCCR3A, COM3B1);
wiring_analog.c:                        #if defined(TCCR3A) && defined(COM3C1)
wiring_analog.c:                                sbi(TCCR3A, COM3C1);
wiring_analog.c:                        #if defined(TCCR4A)
wiring_analog.c:                                sbi(TCCR4A, COM4A1);
wiring_analog.c:                                cbi(TCCR4A, COM4A0);
wiring_analog.c:                        #if defined(TCCR4A) && defined(COM4B1)
wiring_analog.c:                                sbi(TCCR4A, COM4B1);
wiring_analog.c:                        #if defined(TCCR4A) && defined(COM4C1)
wiring_analog.c:                                sbi(TCCR4A, COM4C1);
wiring_analog.c:                        #if defined(TCCR4C) && defined(COM4D1)
wiring_analog.c:                                sbi(TCCR4C, COM4D1);
wiring_analog.c:                                cbi(TCCR4C, COM4D0);
wiring_analog.c:                        #if defined(TCCR5A) && defined(COM5A1)
wiring_analog.c:                                sbi(TCCR5A, COM5A1);
wiring_analog.c:                        #if defined(TCCR5A) && defined(COM5B1)
wiring_analog.c:                                sbi(TCCR5A, COM5B1);
wiring_analog.c:                        #if defined(TCCR5A) && defined(COM5C1)
wiring_analog.c:                                sbi(TCCR5A, COM5C1);
wiring_digital.c:               #if defined(TCCR1A) && defined(COM1A1)
wiring_digital.c:               case TIMER1A:   cbi(TCCR1A, COM1A1);    break;
wiring_digital.c:               #if defined(TCCR1A) && defined(COM1B1)
wiring_digital.c:               case TIMER1B:   cbi(TCCR1A, COM1B1);    break;
wiring_digital.c:               #if defined(TCCR1A) && defined(COM1C1)
wiring_digital.c:               case TIMER1C:   cbi(TCCR1A, COM1C1);    break;
wiring_digital.c:               #if defined(TCCR2) && defined(COM21)
wiring_digital.c:               case  TIMER2:   cbi(TCCR2, COM21);      break;
wiring_digital.c:               #if defined(TCCR0A) && defined(COM0A1)
wiring_digital.c:               case  TIMER0A:  cbi(TCCR0A, COM0A1);    break;
wiring_digital.c:               #if defined(TCCR0A) && defined(COM0B1)
wiring_digital.c:               case  TIMER0B:  cbi(TCCR0A, COM0B1);    break;
wiring_digital.c:               #if defined(TCCR2A) && defined(COM2A1)
wiring_digital.c:               case  TIMER2A:  cbi(TCCR2A, COM2A1);    break;
wiring_digital.c:               #if defined(TCCR2A) && defined(COM2B1)
wiring_digital.c:               case  TIMER2B:  cbi(TCCR2A, COM2B1);    break;
wiring_digital.c:               #if defined(TCCR3A) && defined(COM3A1)
wiring_digital.c:               case  TIMER3A:  cbi(TCCR3A, COM3A1);    break;
wiring_digital.c:               #if defined(TCCR3A) && defined(COM3B1)
wiring_digital.c:               case  TIMER3B:  cbi(TCCR3A, COM3B1);    break;
wiring_digital.c:               #if defined(TCCR3A) && defined(COM3C1)
wiring_digital.c:               case  TIMER3C:  cbi(TCCR3A, COM3C1);    break;
wiring_digital.c:               #if defined(TCCR4A) && defined(COM4A1)
wiring_digital.c:               case  TIMER4A:  cbi(TCCR4A, COM4A1);    break;
wiring_digital.c:               #if defined(TCCR4A) && defined(COM4B1)
wiring_digital.c:               case  TIMER4B:  cbi(TCCR4A, COM4B1);    break;
wiring_digital.c:               #if defined(TCCR4A) && defined(COM4C1)
wiring_digital.c:               case  TIMER4C:  cbi(TCCR4A, COM4C1);    break;
wiring_digital.c:               #if defined(TCCR4C) && defined(COM4D1)
wiring_digital.c:               case TIMER4D:   cbi(TCCR4C, COM4D1);    break;
wiring_digital.c:               #if defined(TCCR5A)
wiring_digital.c:               case  TIMER5A:  cbi(TCCR5A, COM5A1);    break;
wiring_digital.c:               case  TIMER5B:  cbi(TCCR5A, COM5B1);    break;
wiring_digital.c:               case  TIMER5C:  cbi(TCCR5A, COM5C1);    break;

The stuff in wiring.c is about millis() etc, the stuff in wiring_analog.c is about analogWrite() etc. Seems that "tone.cpp"  is using timers to make beeps too. So if you use any of that it may be grabbing a timer too.

Last Edited: Fri. Feb 26, 2021 - 03:12 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

kabasan wrote:

Are you dissatisfied with the millis() and micros() provided by Arduino?

 

I have no clear understanding on how they work....by my own hand as I really do not use the platform .  Will read up on it.

 

 

Cliff seems to have a nice approach to the issue.  I will try it out and see what happens.

 

Thanks to you both.

 

JIm

 

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I really like elaspedMillis as it simplifies the stuff for doing "intervals" - hope you will find it equally useful.

 

(a lot of What Paull Stoffregen does is very clever indeed!)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
I really like elaspedMillis as it simplifies the stuff for doing "intervals" - hope you will find it equally useful.

 

It looks pretty straightforward, but this part:

 

// create elapsedMillis outside loop(), to
// retain its value each time loop() runs.
#include <elapsedMillis.h>

elapsedMillis sinceTest1;
elapsedMillis sinceTest2;
elapsedMillis sinceTest3;

void setup() 
{
    Serial.begin(9600);
}

void loop() 
{
    if (sinceTest1 >= 1000) 
    {
        sinceTest1 = sinceTest1 - 1000;
        Serial.println("Test1 (1 sec)");
    }
    if (sinceTest2 >= 2700) 
    {
        sinceTest2 = sinceTest2 - 2700;
        Serial.println("             Test2 (2.7 sec)");
    }
    if (sinceTest3 >= 1300) 
    {
        sinceTest3 = sinceTest3 - 1300;
        Serial.println("                            Test3 (1.3 sec)");
    }
}

 

All well and good, but this explanation:

In this example, each interval is substracted from the elapsed time, which automatically adjusts for any latency. Imagine if if the Serial.println() for Test1 takes more than 4 ms, and during that time sinceTest2 increases to 2704. Because 2700 is substracted, it will start over at 4, so the next Test2 may print 4 ms early, aligned to the original schedule.

If you do not care about maintaining the original schedule, or the time between events must not be less than intended, you would set the variable back to zero instead of substracting.

 

Leads me to believe that I should simply reset the variable back to zero and be done with it.

 

Jim

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

From memory,  Arduino uses Timer0 for the System time e.g. micros(), millis()

 

You can use Timer1 and Timer2 for your own purposes  if you are not using Arduino PWM functions like analogWrite()

Remember to configure the Timer1/2 registers absolutely.   e.g. with =

 

The existing PWM functions don't use interrupts.

 

David.   (typed from memory.  you should check)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

jgmdesign wrote:
Leads me to believe that I should simply reset the variable back to zero and be done with it.
Just use as in my example:

elapsedMillis lastMillis = 0;

void loop() {
    if (lastMillis > 5000) {
        // do 5s work
        lastMillis = 0;
    }
}

There's no subtraction in this and once it has exceeded 5000ms I simply set it back to 0 to restart the timing period.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Assuming you don't need to use millis()(IIRC timer0) or analogWrite()(can use any of T0,T1, or T2) functions, you can do anything you want with the timers, just re-init them for your use.

 

Have fun!

 

Jim

 

Keys to wealth:

Invest for cash flow, not capital gains!

Wealth is attracted, not chased! 

Income is proportional to how many you serve!

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
  • Timer0– For Uno functions like delay(), millis(), micros() or delaymicros().
  • Timer1– For the working of servo library.
  • Timer2– For functions like tone(), notone().

 

PWM operations that use analogWrite() will also use timers.

 

If you are not using the servo library or tone(), then you can use Timer1 and Timer2 for your applications with the Arduino UNO (and the other Mega328P-based Arduinos).