Timer1 impacts Timer0 on Arduino Nano ?

Hello !

A new strange behavior with a Nano.
My goal is to reach 1 micro second with the Timer1.

That works, but if i try to read millis(), it returns 0 !

Is someone can help me to understand this ?

//===============================================================================================================
// ARDUINO
//===============================================================================================================
#include <wiring.c>
//---------------------------------------------------------------------------------------------------------------
void initTimer() {
  sei();
#if defined(TCCR0A) && defined(WGM01)
  sbi(TCCR0A, WGM01);
  sbi(TCCR0A, WGM00);
#endif

  // set timer 0 prescale factor to 64
#if defined(TCCR0B) && defined(CS01) && defined(CS00)
  // this combination is for the standard 168/328/1280/2560
  sbi(TCCR0B, CS01);
  sbi(TCCR0B, CS00);
#else
  #error Timer 0 prescale factor 64 not set correctly
#endif

  // enable timer 0 overflow interrupt
#if defined(TIMSK) && defined(TOIE0)
  sbi(TIMSK, TOIE0);
#elif defined(TIMSK0) && defined(TOIE0)
  sbi(TIMSK0, TOIE0);
#else
  #error  Timer 0 overflow interrupt not set correctly
#endif
}
//---------------------------------------------------------------------------------------------------------------
//#define  TIMER1_COUNTER   49536     // = 65536-16MHz/  1/   1kHz --> 16000 /  1000µs // works
//#define  TIMER1_COUNTER   63936     // = 65536-16MHz/  1/  10kHz -->  1600 /   100µs // works
//#define  TIMER1_COUNTER   65376     // = 65536-16MHz/  1/ 100kHz -->   160 /    10µs // works
//#define  TIMER1_COUNTER   65456     // = 65536-16MHz/  1/ 200kHz -->    80 /     5µs // works
//#define  TIMER1_COUNTER   65472     // = 65536-16MHz/  1/ 250kHz -->    64 /     4µs // works
//#define  TIMER1_COUNTER   65504     // = 65536-16MHz/  1/ 500kHz -->    32 /     2µs // works
//#define  TIMER1_COUNTER   65510     // = 65536-16MHz/  1/ 615Hz -->     26 / 1.625µs // works
//#define  TIMER1_COUNTER   65515     // = 65536-16MHz/  1/ 762kHz -->    21 /1.3125µs // works
//#define  TIMER1_COUNTER   65516     // = 65536-16MHz/  1/ 800kHz -->    20 /  1.25µs // works
#define  TIMER1_COUNTER   65517     // = 65536-16MHz/  1/ 842kHz -->    19 /1.1875µs // ISSUE !!!
//#define  TIMER1_COUNTER   65518     // = 65536-16MHz/  1/ 889kHz -->    18 /1.1250µs // ISSUE !!!
//#define  TIMER1_COUNTER   65519     // = 65536-16MHz/  1/ 941kHz -->    17 /1.0625µs // ISSUE !!!
//#define  TIMER1_COUNTER   65520     // = 65536-16MHz/  1/   1MHz -->    16 /     1µs // ISSUE !!! --> My target

volatile uint32_t   tickms = 0;
//---------------------------------------------------------------------------------------------------------------
void myIRQTimer() {
  noInterrupts();           // disable all interrupts
  TCCR1A = 0;               // initialize timer1 
  TCCR1B = (1 << CS10);     // 1 prescaler 

  TCNT1 = TIMER1_COUNTER;   // preload timer
  TIMSK1 |= (1 << TOIE1);   // enable timer overflow interrupt
  interrupts();             // enable all interrupts
}
//---------------------------------------------------------------------------------------------------------------
ISR(TIMER1_OVF_vect) {      // interrupt service routine
  tickms++;
  TCNT1 = TIMER1_COUNTER;   // preload timer
}
//---------------------------------------------------------------------------------------------------------------
int main () {
  initTimer();          // Start only with Timer0
  myIRQTimer();         // Configure Timer1
  Serial.begin(115200);
  Serial.print(F("Timer1 Bug ?\n"));

  for (;;) { Serial.println(millis()); }
}
//---------------------------------------------------------------------------------------------------------------

Thanks for your help.
Patrick

That's because you are using a main() function. This not only bypasses loop() and setup(), but also ANY initialization. Aka, also any initialization of Timer0 for millis().

PS, don't forget to disable interrupts when reading tickms or you might be in for some random surprises :wink:

Hello septillion,

No no i've already check with setup() and loop() functions instead of main() and still the same behavior, exactly the same.

void setup() {
  myIRQTimer();         // Configure Timer1
  Serial.begin(115200);
  Serial.print(F("Timer1 Bug ?\n"));
}

void loop() {
  Serial.println(millis());
}

Another idea ?

Please post complete programs only. Not only is it you with a question (so why give use, the volunteers, the job of copy-pasting), it also reduces the change of error because we end up with slightly different code.

Hi, as requested :

So, when i compile with timer1 from 65517 to 65520 (and more i guess), millis() return 0.
Below, it works correctly.

Is anyone can help me to understand this behavior ?
Patrick

//===============================================================================================================
// ARDUINO
//===============================================================================================================
//#define  TIMER1_COUNTER   49536     // = 65536-16MHz/  1/   1kHz --> 16000 /  1000µs // works
//#define  TIMER1_COUNTER   63936     // = 65536-16MHz/  1/  10kHz -->  1600 /   100µs // works
//#define  TIMER1_COUNTER   65376     // = 65536-16MHz/  1/ 100kHz -->   160 /    10µs // works
//#define  TIMER1_COUNTER   65456     // = 65536-16MHz/  1/ 200kHz -->    80 /     5µs // works
//#define  TIMER1_COUNTER   65472     // = 65536-16MHz/  1/ 250kHz -->    64 /     4µs // works
//#define  TIMER1_COUNTER   65504     // = 65536-16MHz/  1/ 500kHz -->    32 /     2µs // works
//#define  TIMER1_COUNTER   65510     // = 65536-16MHz/  1/ 615Hz -->     26 / 1.625µs // works
//#define  TIMER1_COUNTER   65515     // = 65536-16MHz/  1/ 762kHz -->    21 /1.3125µs // works
//#define  TIMER1_COUNTER   65516     // = 65536-16MHz/  1/ 800kHz -->    20 /  1.25µs // works
#define  TIMER1_COUNTER   65517     // = 65536-16MHz/  1/ 842kHz -->    19 /1.1875µs // ISSUE !!!
//#define  TIMER1_COUNTER   65518     // = 65536-16MHz/  1/ 889kHz -->    18 /1.1250µs // ISSUE !!!
//#define  TIMER1_COUNTER   65519     // = 65536-16MHz/  1/ 941kHz -->    17 /1.0625µs // ISSUE !!!
//#define  TIMER1_COUNTER   65520     // = 65536-16MHz/  1/   1MHz -->    16 /     1µs // ISSUE !!! --> My target

volatile uint32_t   tickms = 0;
//---------------------------------------------------------------------------------------------------------------
void myIRQTimer() {
  noInterrupts();           // disable all interrupts
  TCCR1A = 0;               // initialize timer1 
  TCCR1B = (1 << CS10);     // 1 prescaler 

  TCNT1 = TIMER1_COUNTER;   // preload timer
  TIMSK1 |= (1 << TOIE1);   // enable timer overflow interrupt
  interrupts();             // enable all interrupts
}
//---------------------------------------------------------------------------------------------------------------
ISR(TIMER1_OVF_vect) {      // interrupt service routine
  tickms++;
  TCNT1 = TIMER1_COUNTER;   // preload timer
}
//---------------------------------------------------------------------------------------------------------------
void setup() {
  myIRQTimer();         // Configure Timer1
  Serial.begin(115200);
  Serial.print(F("Timer1 Bug ?\n"));
}

void loop() {
  Serial.println(millis());
}
//---------------------------------------------------------------------------------------------------------------

I think that the high frequency Timer1 interrupts are blocking the Timer0 interrupt to advance the millis(). I don't fully understand the interrupt latency, and why the Serial print is actually working as well, but if you make the Timer1 OVF ISR slightly shorter, by typing tickms uint16_t, millis() is running. If you comment the tickms++ out of the ISR the code also works at full speed.

//===============================================================================================================
// ARDUINO
//===============================================================================================================
//#define  TIMER1_COUNTER   49536     // = 65536-16MHz/  1/   1kHz --> 16000 /  1000µs // works
//#define  TIMER1_COUNTER   63936     // = 65536-16MHz/  1/  10kHz -->  1600 /   100µs // works
//#define  TIMER1_COUNTER   65376     // = 65536-16MHz/  1/ 100kHz -->   160 /    10µs // works
//#define  TIMER1_COUNTER   65456     // = 65536-16MHz/  1/ 200kHz -->    80 /     5µs // works
//#define  TIMER1_COUNTER   65472     // = 65536-16MHz/  1/ 250kHz -->    64 /     4µs // works
//#define  TIMER1_COUNTER   65504     // = 65536-16MHz/  1/ 500kHz -->    32 /     2µs // works
//#define  TIMER1_COUNTER   65510     // = 65536-16MHz/  1/ 615Hz -->     26 / 1.625µs // works
//#define  TIMER1_COUNTER   65515     // = 65536-16MHz/  1/ 762kHz -->    21 /1.3125µs // works
//#define  TIMER1_COUNTER   65516     // = 65536-16MHz/  1/ 800kHz -->    20 /  1.25µs // works
//#define  TIMER1_COUNTER   65517     // = 65536-16MHz/  1/ 842kHz -->    19 /1.1875µs // ISSUE !!!
//#define  TIMER1_COUNTER   65518     // = 65536-16MHz/  1/ 889kHz -->    18 /1.1250µs // ISSUE !!!
//#define  TIMER1_COUNTER   65519     // = 65536-16MHz/  1/ 941kHz -->    17 /1.0625µs // ISSUE !!!
#define  TIMER1_COUNTER   65520     // = 65536-16MHz/  1/   1MHz -->    16 /     1µs // ISSUE !!! --> My target

//volatile uint32_t   tickms = 0;
volatile uint16_t   tickms = 0;
//---------------------------------------------------------------------------------------------------------------
void myIRQTimer() {
  noInterrupts();           // disable all interrupts
  TCCR1A = 0;               // initialize timer1
  TCCR1B = (1 << CS10);     // 1 prescaler

  TCNT1 = TIMER1_COUNTER;   // preload timer
  TIMSK1 |= (1 << TOIE1);   // enable timer overflow interrupt
  interrupts();             // enable all interrupts
}
//---------------------------------------------------------------------------------------------------------------
ISR(TIMER1_OVF_vect) {      // interrupt service routine
  tickms++;//ifthis is commented it also runs at top speed
  TCNT1 = TIMER1_COUNTER;   // preload timer
}
//---------------------------------------------------------------------------------------------------------------
void setup() {
  myIRQTimer();         // Configure Timer1
  Serial.begin(115200);
  Serial.print(F("Timer1 Bug ?\n"));
}

void loop() {
  Serial.println(millis());
}
//---------------------------------------------------------------------------------------------------------------

Hi Cattledog,

Thanks to spend a few your time to test really my code and for your workaround.

So, for my software, i'll remove tickms completly and will use millis() - or micros() - and that should be great.

Again a big thanks for your help.
Patrick

BRIOT_Patrick:
Hello !

A new strange behavior with a Nano.
My goal is to reach 1 micro second with the Timer1.

That works, but if i try to read millis(), it returns 0 !

I know this has already been solved, but I am curious, if you have achieved a perfect microsecond that does not drift and is interrupt driven, why have a second timer dedicated to essentially the same function? Why not set up a millis()-esque function that is not interrupt-driven, but also does not drift and is based on your new micros function?

Perehama:
I know this has already been solved, but I am curious, if you have achieved a perfect microsecond that does not drift and is interrupt driven, why have a second timer dedicated to essentially the same function? Why not set up a millis()-esque function that is not interrupt-driven, but also does not drift and is based on your new micros function?

Hello,
It's because in my final software a program run continiously to emulate another old 8bits processor and i need to sampling sound to a loud speaker with a custom analog output.

Hello all !

This morning i was a little frustrated and so i've searched deeper to find a solution.

And i found one !
I don't understand why i need to re-activate the interrupt explicitly, but that is the solution.
If someone can explain it would be perfect.

However, the corrected code below :

//===============================================================================================================
// ARDUINO
//===============================================================================================================
#include <wiring.c>
//---------------------------------------------------------------------------------------------------------------
void initTimer() {
  sei();
#if defined(TCCR0A) && defined(WGM01)
  sbi(TCCR0A, WGM01);
  sbi(TCCR0A, WGM00);
#endif

  // set timer 0 prescale factor to 64
#if defined(TCCR0B) && defined(CS01) && defined(CS00)
  // this combination is for the standard 168/328/1280/2560
  sbi(TCCR0B, CS01);
  sbi(TCCR0B, CS00);
#else
  #error Timer 0 prescale factor 64 not set correctly
#endif

  // enable timer 0 overflow interrupt
#if defined(TIMSK) && defined(TOIE0)
  sbi(TIMSK, TOIE0);
#elif defined(TIMSK0) && defined(TOIE0)
  sbi(TIMSK0, TOIE0);
#else
  #error  Timer 0 overflow interrupt not set correctly
#endif
}
//---------------------------------------------------------------------------------------------------------------
//#define  TIMER1_COUNTER   49536     // = 65536-16MHz/  1/   1kHz --> 16000 /  1000µs // works
//#define  TIMER1_COUNTER   63936     // = 65536-16MHz/  1/  10kHz -->  1600 /   100µs // works
//#define  TIMER1_COUNTER   65376     // = 65536-16MHz/  1/ 100kHz -->   160 /    10µs // works
//#define  TIMER1_COUNTER   65456     // = 65536-16MHz/  1/ 200kHz -->    80 /     5µs // works
//#define  TIMER1_COUNTER   65472     // = 65536-16MHz/  1/ 250kHz -->    64 /     4µs // works
//#define  TIMER1_COUNTER   65504     // = 65536-16MHz/  1/ 500kHz -->    32 /     2µs // works
//#define  TIMER1_COUNTER   65510     // = 65536-16MHz/  1/ 615Hz -->     26 / 1.625µs // works
//#define  TIMER1_COUNTER   65515     // = 65536-16MHz/  1/ 762kHz -->    21 /1.3125µs // works
//#define  TIMER1_COUNTER   65516     // = 65536-16MHz/  1/ 800kHz -->    20 /  1.25µs // works
//#define  TIMER1_COUNTER   65517     // = 65536-16MHz/  1/ 842kHz -->    19 /1.1875µs // ISSUE !!!
//#define  TIMER1_COUNTER   65518     // = 65536-16MHz/  1/ 889kHz -->    18 /1.1250µs // ISSUE !!!
//#define  TIMER1_COUNTER   65519     // = 65536-16MHz/  1/ 941kHz -->    17 /1.0625µs // ISSUE !!!
#define  TIMER1_COUNTER   65520     // = 65536-16MHz/  1/   1MHz -->    16 /     1µs // ISSUE !!! --> My target

volatile uint32_t   tickms = 0;
//---------------------------------------------------------------------------------------------------------------
void myIRQTimer() {
  TCCR1A = 0;               // initialize timer1
  TCCR1B = 0;               // stop the timer1
  TIFR1 |= (1 << TOV1);     // clear the overflow interrupt flag by writing 1 to it

  TCNT1 = TIMER1_COUNTER;   // preload timer
  TCCR1B |= (1 << CS10);    // 1 prescaler
  TIMSK1 |= (1 << TOIE1);   // enable timer overflow interrupt
}
//---------------------------------------------------------------------------------------------------------------
ISR(TIMER1_OVF_vect) {      // interrupt service routine
  tickms++;
  TCNT1 = TIMER1_COUNTER;   // preload timer
  sei();                    // <=== THIS has solved the issue !!!!!
}
//---------------------------------------------------------------------------------------------------------------
int main () {
  initTimer();          // Start only with Timer0
  myIRQTimer();         // Configure Timer1
  Serial.begin(115200);
  Serial.print(F("Timer1 Bug ?\n"));

  for (;;) { 
    Serial.println(millis());
  }
}
//---------------------------------------------------------------------------------------------------------------

And by the way, that works too with setup() and loop() ! :wink:

I don't understand why i need to re-activate the interrupt explicitly, but that is the solution.
If someone can explain it would be perfect.

I think you are fooling yourself, and the fact that millis() values prints does not mean that they are accurate, that Timer0 overflow interrupts are properly executed, and that the microsecond tickms count is accurate.

The time taken to enter and exit an interrupt is on the order of 2.625 microseconds. See the discussion of interrupt latency in Nick Gammon's tutorial on interrupts.
https://gammon.com.au/interrupts

If you just focus on the entry latency of 1.4375 us you will see that by the time you set the TCNT1 value up near the top for the next microsecond tick, the microsecond has already passed.

If you reenable the interrupts within the ISR you may be reducing some of the exit latency, but overall, the interrupt entry and execution are longer than the next microsecond.

The AT328 is just not fast enough to have a Timer interrupt based 1 us tick.

Clearly, i was wrong.
Thank you Cattledog for these clarifications and the interesting link to read.

Now i need to find a solution to work on my emulation to sampling sound maybe with 2us.
I need to do some tests.

Patrick

BRIOT_Patrick:
Hello,
It's because in my final software a program run continiously to emulate another old 8bits processor and i need to sampling sound to a loud speaker with a custom analog output.

You should elaborate more on this, because so far this whole thread has been solving solving for Y instead of X. Cattledog has already posted that it takes longer than a microsecond to enter the interrupt. If you go for 2 microseconds, all you will be able to do is enter and exit the interrupt, maybe. micros() is already quite accurate to a resolution of 4 microseconds. The older 8-bit chip would likely also not be faster or more efficient in code than the ATmega328P. So, what are you really wanting to do?