Two timers work in unexpected ways.

I’m just getting familiar with the Arduino system and I have a problem with timers, which I don’t understand. I know that you can fade in and out an LED with a timer(PWM) very nicely, but here I am concerned with how two timers behave that should actually trigger slightly different.

I have a Mega2560, it has 6 timers. I use timers 3&4, but I have tried 1&3 as well. This had the same success.

Both timers are configured exactly the same.
Both timers have an exact copy of the ISR. The only difference is that one timer addresses port 11 and the other port 12.

I have now made a LED between port 11 and 12. This results in beats when the timers do not run exactly the same speed.

I use for
Timer3 an OCR3A of 99 and
Timer4 an OCR4A of 100.

This causes the LED to fade up and down slowly.

Then I swap the times every 5 seconds. Actually the same beat should appear, but that doesn’t happen to me. In the swapped assignment, the LED then remains at a brightness as if both counters were running EXACTLY the same. Or it slowly becomes light and stays light or it slowly becomes dark and stays dark. Only at the next time the times are changed it runs again.

This happens with me in all pairings.
time3=time4-1 if time4 < approx. 600

If the times are longer it works as expected (starts flickering of course). It also works if the difference between the two times is at least 2.

Why?

I made a Video of it and the sketch is completely under here.

#define ledPin3 12
#define ledPin4 11
  const int time3 = 99;
  const int time4 = 100;
void setup()
{
  pinMode(ledPin3, OUTPUT);  // Ausgabe LED festlegen
  pinMode(ledPin4, OUTPUT);  // Ausgabe LED festlegen

  noInterrupts();           // Alle Interrupts temporär abschalten


  // Timer 3
  TCCR3A = 0;
  TCCR3B = 0;
  TCNT3 = 0;                // Register mit 0 initialisieren
  OCR3A = time3;            // Output Compare Register vorbelegen 
  TCCR3B |= (1 << CS32);    // 256 als Prescale-Wert spezifizieren
  TIMSK3 |= (1 << OCIE3A);  // Timer Compare Interrupt aktivieren
  
  // Timer 4
  TCCR4A = 0;
  TCCR4B = 0;
  TCNT4 = 0;                // Register mit 0 initialisieren
  OCR4A = time4;            // Output Compare Register vorbelegen 
  TCCR4B |= (1 << CS42);    // 256 als Prescale-Wert spezifizieren
  TIMSK4 |= (1 << OCIE4A);  // Timer Compare Interrupt aktivieren

  interrupts();             // alle Interrupts scharf schalten

}
// Hier kommt die selbstdefinierte Interruptbehandlungsroutine 
// für den Timer Compare Interrupt

ISR(TIMER3_COMPA_vect)        
{
  TCNT3 = 0;                // Register mit 0 initialisieren   
  digitalWrite(ledPin3, digitalRead(ledPin3) ^ 1); // LED ein und aus
}
ISR(TIMER4_COMPA_vect)        
{
  TCNT4 = 0;                // Register mit 0 initialisieren   
  digitalWrite(ledPin4, digitalRead(ledPin4) ^ 1); // LED ein und aus
}

void loop()
{
    OCR3A = time3;            // Output Compare Register vorbelegen 
    OCR4A = time4;            // Output Compare Register vorbelegen 
    delay (5000);
    OCR3A = time4;            // Output Compare Register vorbelegen 
    OCR4A = time3;            // Output Compare Register vorbelegen 
    delay (5000);
}

Edit-Reason: Typo

It doesn’t appear you are setting your timers to a mode that uses OCRxA as the ‘TOP’ which is mode 4 or 12. There are is also the TimerOne, TimerTwo, etc. libraries that take care of all the register adjusting for you so you do not have to worry about these details.

You missed "TIMSK3 |= (1 << OCIE3A);"
OCIE3A → Counter3 Output Compare A Match Interrupt Enable

The two timers are set absolutely correctly. When I measure a single timer with the oscilloscope, the times of each timer are completely ok.

I have another idea:
Can the Interupts interrupt each other? What happens if one ISR is still running and then a second ISR has to be started? Or does the second ISR wait until the first one is ready? After all, the problem only occurs when both LED’s should change state at almost the same time AND the time difference is very small (1 tick)

In both cases, this could stop one ISR from " outrunning " the other.

What else is the Arduino doing ?
When you have no delay() in the Arduino loop(), then you can use millis() with analogWrite() to make whatever you want and have many of them independant of each other.

Like I said, I'm just getting up to speed. This is a test of the behavior of several timers/ISRs at once. It has no purpose except to find out what exactly works and where I need to be careful.

Later I will probably work with the FreeRTOS library and use multitasking. Once you get a taste for it you don't want to miss it.

But NOW it's all about finding out where the limits of the small processors are and if/how you can avoid running into traps.

If I understand why these timers react this way, I won't be surprised later on, when I can't get a bigger project running because of something like this.

bitboy0:
Can the Interupts interrupt each other?

Not on the Mega2560. When an second interrupt occurs during the execution of an ISR servicing the first, the second interrupt is latched and serviced when the current interrupt completes.

Thanks! I guess that explains it. If both interrupts occur at the same time, one has to wait. The timer can't be reset to ZERO as long as the ISR is not running and so it can't overtake the other timer.

The interrupts are latched. Is it possible that interrupts are lost whose triggers are no longer present after the release (e.g. a short pulse at an input)?

bitboy0:
The interrupts are latched. Is it possible that interrupts are lost whose triggers are no longer present after the release (e.g. a short pulse at an input)?

You can’t lose a latched interrupt. You can however miss one.

Latches are like flags, so if the flag is set, and the interrupt tries to set the flag again, then there’s (usually*) no record that the flag has been “set twice”.

This is a problem when people code delays in an interrupt routine, or try to cram too much code in one, or have interrupts happening at too fast a rate to be processed.

  • sometimes on some chips an interrupt flag will indicate the state of a buffer (e.g. characters received from UART). So in that case the “UART received interrupt flag” will indicate the state of the buffer (empty vs character available). So it’s possible to have multiple “character available” interrupts pending, up to the limit of the buffer. That’s a special case though, and doesn’t apply to timers.

bitboy0:
Like I said, I'm just getting up to speed. This is a test of the behavior of several timers/ISRs at once. It has no purpose except to find out what exactly works and where I need to be careful.

Later I will probably work with the FreeRTOS library and use multitasking. Once you get a taste for it you don't want to miss it.

But NOW it's all about finding out where the limits of the small processors are and if/how you can avoid running into traps.

If I understand why these timers react this way, I won't be surprised later on, when I can't get a bigger project running because of something like this.

I don't need interrupts or messing with chip timers or any OS to write fast tasking code on Arduino. I have a series of sketches just to show using and combining tasks. One task is a loop counter to show sketch "health", you want to keep it fast, 50KHz or more by breaking steps down if necessary. It's funny how often the sketch becomes event-driven state machines.

I have yet to see a help-you-along tasker that doesn't have more overhead than it's worth.
AVR interrupts have over 80 cycles overhead, 5 usecs gone. Use only to catch brief events.

Arduino provides 2 uint32_t counters that count up time since startup. One is millis() that may be up to +/-1ms (plus system clock drift) but can time intervals up to 49.7-some days. The other is micros() with granularity of 4 and rollover at around 71 minutes -- if close matters use micros(). Up to the interval, end time - start time = elapsed time with end time usually being now.
With variables holding time-marks, each one becomes a timer.
The micros and millis clocks run off a chip timer. Don't mess them up.

As far as getting full good info, explore these blogs: (not my site)
https://gammon.com.au/forum/bbshowpost.php?bbtopic_id=123

One is all about interrupts. The SPI bus article shows how to make slave Arduinos as well as master.
The minimal board article 2nd example chip is "The Mighty" ATmega1284P with 16K RAM, 40 pins and 2 UARTs.

You don't even need a clock to run these, they can run 3V3 on the 8MHz internal oscillator, fully contained except for a bypass cap or 2.

Do this with a PIC! O'Baka / One Chip Arduino

@pcbbc
You got me wrong. I realize that I can't solve interrupts by myself. But the system does when an ISR is ready. And only then can new ISR be processed. That's what I learned from the question at the beginning. And of course ISR should be as short as possible. So that the probability of losing something is reduced. On other systems I liked to work with queues. An interrupt writes e.g. only the data that has just arrived and can be terminated immediately. The main program then takes care of the processing. Except of course that immediate processing is unavoidable.

@GoForSmoke
Multitasking certainly costs a lot of performance. But it brings very real possibilities.

One task polls a keyboard matrix (one line per call), one creates the menu on the 20*4 LCD and scrolls longer menu key texts evenly. One takes care of the communication and data processing with a serial device and the main task independently collects information from queues, gives instructions in queues and doesn't have to care about timing.

If the program is not too complicated, it can be implemented in a conventional way in many cases. But I do not like it.

I love the on-board computer of the Apollo missions: Hardly any memory, you could watch the CPU processing and ROM was "crocheted" by hand ... but the multitasking served them well and without it the first moon landing would probably have failed.

But I think my original question is well answered by now.
Thanks for that.

bitboy0:
You missed "TIMSK3 |= (1 << OCIE3A);"
OCIE3A → Counter3 Output Compare A Match Interrupt Enable

That enables an interrupt, it does not set the mode of the timers (WGM3:0 bits). I am not sure, maybe by default they start up in Mode 4 but I thought they defaulted to Mode 0

bitboy0, you entered the Arduino world with a embedded computer mindset and started to use timers and interrupts.

Arduino was developed at a university for fast prototyping and to learn programming. It was not intended to be used as a embedded environment. Almost every device with a microcontroller or processor has some kind of embedded system, but the Arduino world is not really a part of that :frowning: Although lately Arduino is trying to move in a professional direction as well.

When using a Arduino, it turns out that interrupts can often be avoided. It is often even better to avoid interrupts. We, as Arduino-enthousiasts, all know that. Using timers is only needed for specific tasks and there are libraries that might be of help.
Scanning keys is something slow and can be done in the loop() without interrupts. That's what we all do.
I learned that with the Arduino, straightforward down-to-earth coding without extra abstraction layers, works best. When using the Arduino functions, it is also easier to change to another board.

You don't have to follow the Arduino common way to do things, you can of course use timers and interrupts if you want to. But we don't.

Have a look at the ESP32 in Arduino-compatible mode. That combines FreeRTOS with Arduino. I think you will like that a lot :stuck_out_tongue:

Thank you for this objective and well-founded statement.
I understand what you want to say and I can appreciate it. It is possible that with the Arduino I did not quite find what I was looking for.

A friend also recommended the ESP32 to me today ... it already has two cores and FreeRTOS is already included.

Not every project needs all features or multitasking. As it looks like you can do small tasks very fast with the Arduino and that's what it was designed for.

I will now see how far I get with which method. I have understood that only very few people use the Arduino with FreeRTOS and I also take the warnings about possible problems with timers and interrupts seriously. I am still a beginner with C++ and will have enough questions that have their place here.

Thanks!

most OS's get by with just one timer despite supporting many on each task.

fading multiple LEDs doesn't seem to require an RTOS, just sensible programming keeping track of the time until the next event.

bitboy0:
@GoForSmoke
Multitasking certainly costs a lot of performance. But it brings very real possibilities.

One task polls a keyboard matrix (one line per call), one creates the menu on the 20*4 LCD and scrolls longer menu key texts evenly. One takes care of the communication and data processing with a serial device and the main task independently collects information from queues, gives instructions in queues and doesn’t have to care about timing.

If the program is not too complicated, it can be implemented in a conventional way in many cases. But I do not like it.

But you don’t need an OS to do that.

Consider software button debounce. I have tasks that watch button(s) different ways and updates state/history bytes for other tasks to read. The latest reads the pin twice/ms to make a history of 8 reads, the pin is stable change detected if history is 127 or 128. It really cuts the cycles to debounce and only takes < 4ms after the last bounce… compare to wait 20 to 50ms after 1st change detect and assume the pin is stable.

Some other task acts on history == 128.

If I read a button matrix, my task only reads 1 button per loop() pass.

These techniques go back to engineers and MCU’s like the 8051 and then moved into RT gaming in the 80’s.

// add-a-sketch_button 2018 by GoForSmoke @ Arduino.cc Forum
// Free for use, Apr 30/2018 by GFS. Compiled on Arduino IDE 1.6.9.
// Update May 6/2018, Aug 11, 2018

/*  Button Debounce Example

  --- for this example connect a button between pin 7 and GND
  --- or stick a jumper in pin 7 and while holding the board steady
  --- tap the free end onto the grounded USB port box to press.
  --- Press and hold the Button to toggle led13 blinking.

  Yes I'm using a 16 bit micros timer to time fractions of millis as micros.
  The button reader only reads 1 button per call so as to not block void loop().
  Each button has a history byte that holds the last 8 reads with 256 possible
  states but only 4 of them being significant.
  0 is the button held down
  255 is the button left up
  127 is the buton changing from up to down, button just released.
  128 is the button changing from down to up, button just pressed.
  everything else is to be ignored as bounce.
*/

// button vars
byte buttonPin = 7;
byte buttonHistory;
word markButtonTime;        // 16-bit micros timers
const word waitButtonTime = 500; // micros

// added sketch task, on-off blinker vars
byte ledState, ledPin = 13; // use byte for small values, int cost 2 bytes
word startBlink, waitBlink; // 16 bit millis is good to time 65.535 seconds

void buttonTask()   // yup, this is my new button debounce compromise method.
{ // read twice per milli, bits 0 to 6 all same sets the state
  if ( word( micros()) - markButtonTime >= waitButtonTime ) // read occaisoinally
  {
    buttonHistory <<= 1; // if you don't know <<= look it up in the Arduino Reference
    // keep a browser open to that page when you use the IDE.
    buttonHistory += digitalRead( buttonPin ); // read history streams through buttonHistory
    markButtonTime = micros(); // gets the low 16 bits of micros(), time to 60 ms + margin
  }
  /*        buttonHistory bits read as values:
    0 is button held down, 8 reads of 0 in a row
    255 is button left up, 8 reads of 1 in a row
    127 is button changing from up to down, 7 1's and 1 0 = just released
    128 is button changing from down to up, 7 0's and 1 1 = just pressed.
    everything else is to be ignored as bounce.
    Understand that 7 same bits in a row counts as pin state stable.
  */
}

void OnOffBlinker() // only blinks if there's a wait time, can be switched on/off
{
  if ( waitBlink > 0 ) // this is the on/off switch
  {
    // word( millis()) gets the low 16 bits of the 32-bit millis() return.
    if ( word( millis()) - startBlink >= waitBlink ) // difference in time by subtracting start from end
    {
      ledState = !ledState;  // ! is NOT: not_0/true becomes 0/false else 0 becomes 1.
      digitalWrite( ledPin, ledState ); // the led changes state.
      startBlink += waitBlink; // next blink starts when it should, where diff > wait.
    }
  }
  else if ( ledState > 0 ) // waitBlink == 0 turns blinking off
  {
    digitalWrite( ledPin, ledState = LOW ); //  make sure the led is OFF
  } // yes, you can set a variable during calculation in C, the write here is LOW.
}

void setup()
{
  Serial.begin( 115200 );
  while ( Serial.available )  Serial.read();  // empties garbage chars from the serial output buffer
  
  Serial.println( F( "\n  Button Debounce Example, free by GoForSmoke\n" ));
  Serial.println( F( "\n-- for this example connect a button between pin 7 and GND" ));
  Serial.println( F( "--- or stick a jumper in pin 7 and while holding the board steady" ));
  Serial.println( F( "--- tap the free end onto the grounded USB port box to press." ));

  pinMode( buttonPin, INPUT_PULLUP );
  pinMode( ledPin, OUTPUT );

  buttonHistory = 255;
  waitBlink = 500;
};


void loop()
{
  buttonTask();
  /*
    0 is the button held down
    255 is the button left up
    127 is the buton changing from up to down, button just released.
    128 is the button changing from down to up, button just pressed.
    everything else is to be ignored as bounce.
  */

  switch ( buttonHistory ) 
  {
    case 128 : // pin is HIGH in bit 7, LOW for 7 reads, up to down detected
      buttonHistory = 0; // change detected, make it into no change now
      Serial.print( F( "press detected     " ));
      Serial.println( millis());
      if ( waitBlink == 0 ) // toggle action tied to button press
      {
        waitBlink = 500; // makes the blinking start
        startBlink = millis(); // gets the low 16 bits
      }
      else
      {
        waitBlink = 0; // makes the blinking stop
      }
      break;
    case 127 : // pin is LOW in bit 7, HIGH for 7 reads, down to up detected
      buttonHistory = 255; // change detected, make it into no change now
      Serial.print( F( "release detected   " ));
      Serial.println( millis());
      break;
  }

  OnOffBlinker();
}

This is the loop counter. Add it to the button sketch and see how responsive they are.

// add-a-sketch_loop_counter 2018 by GoForSmoke @ Arduino.cc Forum
// Free for use, Apr 29/2018 by GFS. Compiled on Arduino IDE 1.6.9.
// This sketch counts times that loop has run each second and prints it.
// It uses the void LoopCounter() function that does not block other code.

#define microsInOneSecond 1000000UL

void setup()
{
  Serial.begin( 115200 );
  Serial.println( F( "\n\n\n  Loop Counter, free by GoForSmoke\n" ));
  Serial.println( F( "This sketch counts times that loop has run each second and prints it." ));
}

void LoopCounter() // tells the average response speed of void loop()
{ // inside a function, static variables keep their value from run to run
  static unsigned long count, countStartMicros; // only this function sees these

  count++; // adds 1 to count after any use in an expression, here it just adds 1.
  if ( micros() - countStartMicros >= microsInOneSecond ) // 1 second
  {
    countStartMicros += microsInOneSecond; // for a regular second
    Serial.println( count ); // 32-bit binary into decimal text = many micros
    count = 0; // don't forget to reset the counter 
  }
}

void loop()  // runs over and over, see how often with LoopCounter()
{
  LoopCounter(); // the function runs as a task, the optimizer will inline the code.
}

Copy the #define and the function to another sketch and see how it averages.

You don’t need an OS to slice time when you can break a task down into small steps run by a state machine.

When you keep loop count >= 50KHz, even 1000 small steps add up quick.

  1. Gammon Forum : Electronics : Microprocessors : How to do multiple things at once ... like cook bacon and eggs ← tasking Arduino 1-2-3 … I am not Nick.
  2. Gammon Forum : Electronics : Microprocessors : How to process incoming serial data without blocking ← techniques howto
  3. Gammon Forum : Electronics : Microprocessors : Interrupts

Your sketch can sense ongoing process events in time.
Your sketch can make events to control it over time.

While un-blocking a delay-ridden (> 30 delays) greenhouse control program I came up with how to use 1 timer to replace many delays.

// add-a-sketch_un-delay 2018 by GoForSmoke @ Arduino.cc Forum
// Free for use, Apr 30/18 by GFS. Compiled on Arduino IDE 1.6.9.
// This sketch shows a general method to get rid of delays in code.
// You could upgrade code with delays to work with add-a-sketch.

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

const byte ledPin = 13;
unsigned long delayStart, delayWait;

void setup()
{
  Serial.begin( 115200 );
  Serial.println( F( "\n\n\n  Un-Delay Example, free by GoForSmoke\n" ));
  Serial.println( F( "This sketch shows how to get rid of delays in code.\n" ));

  pinMode( ledPin, OUTPUT );
};


/* The section of the original sketch with delays:
 * 
 * digitalWrite( ledPin, HIGH );   --  0
 * delay( 500 );
 * digitalWrite( ledPin, LOW );    --  1
 * delay( 250 );
 * digitalWrite( ledPin, HIGH );   --  2
 * delay( 250 );
 * digitalWrite( ledPin, LOW );    --  3
 * delay( 250 );
 * digitalWrite( ledPin, HIGH );   --  4
 * delay( 1000 );
 * digitalWrite( ledPin, LOW );    --  5
 * delay( 1000 );
 */

byte blinkStep; // state tracking for BlinkPattern() below

void BlinkPattern()
{
  // This one-shot timer replaces every delay() removed in one spot.  
  // start of one-shot timer
  if ( delayWait > 0 ) // one-shot timer only runs when set
  {
    if ( millis() - delayStart < delayWait )
    {
      return; // instead of blocking, the undelayed function returns
    }
    else
    {
      delayWait = 0; // time's up! turn off the timer and run the blinkStep case
    }
  }
  // end of one-shot timer

  // here each case has a timed wait but cases could change Step on pin or serial events.
  switch( blinkStep )  // runs the case numbered in blinkStep
  {
    case 0 :
    digitalWrite( ledPin, HIGH );
    Serial.println( F( "Case 0 doing something unspecified here at " ));
    Serial.println( delayStart = millis()); // able to set a var to a value I pass to function
    delayWait = 500; // for the next half second, this function will return on entry.
    blinkStep = 1;   // when the switch-case runs again it will be case 1 that runs
    break; // exit switch-case

    case 1 :
    digitalWrite( ledPin, LOW );
    Serial.println( F( "Case 1 doing something unspecified here at " ));
    Serial.println( delayStart = millis());
    delayWait = 250;
    blinkStep = 2;
    break;

    case 2 :
    digitalWrite( ledPin, HIGH );
    Serial.println( F( "Case 2 doing something unspecified here at " ));
    Serial.println( delayStart = millis());
    delayWait = 250;
    blinkStep = 3;
    break;

    case 3 :
    digitalWrite( ledPin, LOW );
    Serial.println( F( "Case 3 doing something unspecified here at " ));
    Serial.println( delayStart = millis());
    delayWait = 250;
    blinkStep = 4;
    break;

    case 4 :
    digitalWrite( ledPin, HIGH );
    Serial.println( F( "Case 4 doing something unspecified here at " ));
    Serial.println( delayStart = millis());
    delayWait = 1000;
    blinkStep = 5;
    break;

    case 5 :
    digitalWrite( ledPin, LOW );
    Serial.print( F( "Case 5 doing something unspecified here at " ));
    Serial.println( delayStart = millis());
    delayWait = 1000;
    blinkStep = 0;
    break;
  }
}


void loop()  // runs over and over, see how often
{            
  BlinkPattern();
}

It is more code to do the same thing, yes but it does more. It allows non-blocking tasks to be added.

My topic should not be a basic discussion about operating systems or the sense of two timers to control one LED.

I was only interested in understanding how interrupts and timers work in these small processors.

:wink:

Maybe once you can recognize timers being used where beginners would use interrupts you will get better understanding of how it's done and not what you think.

I gave you the address to the full lesson on interrupts including the overhead.
http://gammon.com.au/interrupts

YES the examples are SIMPLE. They are LIMITED. They're supposed to be. They're supposed to get one or two simple ideas across but for some reason some people just get stuck where they're at and see nothing else.

What basic discussion about OS's? On small machines they SUCK. But show me up and put up code to beat that runs on whatever magic OS you care to bring and I'll try to beat it with half as much code.

You don't need an OS to run tasks on Arduino... and to be clear about this, Arduinos can run motors and other things at the same time without an OS. Take a 3Gz PC and slap an MTOS on it and run a motor with that. Ooops.

I’m surprised. Did I write anything bad?
The topic is just far from my original question and I just wanted to end this branch of the discussion in a friendly way.

I just wanted to understand the limitations of the timers and interrupts.I now know how to use timers and interrupts and what problems to expect.

And I’ve said this more than once without attacking anyone. I thanked nicely for all the good contributions, also for those who wrote more than I wanted to know.

So please calm down a little.