[SOLVED] IRremote library bug on Atmega8 only. How to fix it.

1. The problem

Hi all,
making a simple IR code sender using IRremote library, an IR led and an Atmega8, I noticed a problem after sending the IR code: all time related commands (like millis(), delay(), etc.) stopped to work.
The strange thing is that the same sketch runs without any issue on a Atmega328.
After a little "googling", I found similar issues about sketches using IRremote library that runs on Atmega328 but not on Atmega8.
So after some tests and a little of reverse analysis, I found a little bug inside that library, that clears the Timer/Counter0 Overflow Interrupt Enable bit TOIE0, blocking any Timer0 based command (only if executed on Atmega8).
More in depth, inside file IRremoteInt.h (in the IRremote library folder) there is the macro TIMER_DISABLE_INTR that is used to control Timer1 (on Atmega8 IRremote uses Timer1):

...
#if defined(__AVR_ATmega8P__) || defined(__AVR_ATmega8__)
  #define TIMER_ENABLE_INTR    (TIMSK = _BV(OCIE1A))
  #define TIMER_DISABLE_INTR   (TIMSK = 0)
#else
  #define TIMER_ENABLE_INTR    (TIMSK1 = _BV(OCIE1A))
  #define TIMER_DISABLE_INTR   (TIMSK1 = 0)
#endif
...

This macro is executed on every "irsend" instance to control the IR pulses transmitted, but clears on a Atmega8 all the TIMSK bits, and so clears also TOIE0 and TOIE2 bits inside the TIMSK register (see Atmega8 datasheet).

2. How to fix it

If IRremote clears the TOIE0 bit, the cure is simple..... set it again!
So, after every execution of "irsend" I append the following code:

// Re-enable interrupt for Timer0 on Atmega8. Now time related commands work again!
// NOTE: Atmega 328 "family" is not affected.
#if defined(__AVR_ATmega8__)
  sbi(TIMSK, TOIE0);  // Set the TOIE0 (Timer/Counter0 Overflow Interrupt Enable) again.
#endif

where sbi is so defined (the standard way...):

#ifndef cbi
  #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
  #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

3. How to test it

I've attached a file: IRsend_test1.ino. This sketch (can be executed on both Atmega8 and Atmega328)
waits a few seconds blinking a led, then transmits an IR code, then fades the previous led, and print on serial monitor some Timer0 related registers, included TIMSK.
Take in account that IRremote uses Timer1 on Atmega8 and Timer2 on Atmega328, so leds must be connected to different pins on the two MCUs (see "Hardware definitions" section of the sketch).

Inside the sketch, in the "General definitions" section, it is possible to switch off and on the described workaround, setting TIMER0WKAR to 0 or 1 respectively. If TIMER0WKAR is set to 0 and the sketch runs on a Atmega8, the fading after IR code tx doesn't work anymore because the millis() command, used to timer the PWM fade, is blocked.

4. But the story doesn't end here...

So I decided to test the IRremote receiving routines too. I founded a similar bug, but related to the execution of the initialization statement "irrecv.enableIRIn()", that causes the macro TIMER_ENABLE_INTR to be executed on a Atmega8 (see the first code block of this message):

#define TIMER_ENABLE_INTR (TIMSK = _BV(OCIE1A))

so, because OCIE1A bit is on position 4 (see Atmega8 datasheet), this is coded as:

TIMSK = 0b00010000

clearing TOIE0 and TOIE2 and blocking Timer0 and Timer2 overflow interrupt again!

So, in this case it is sufficient to append only one time after the "irrecv.enableIRIn()" initialization statement the usual fix:

// Re-enable interrupt for Timer0 on Atmega8. Now time related commands work again!
// NOTE: Atmega 328 "family" is not affected.
#if defined(__AVR_ATmega8__)
  sbi(TIMSK, TOIE0);  // Set the TOIE0 (Timer/Counter0 Overflow Interrupt Enable) again.
#endif

I've attached the file IRrecv_test2.ino to test it in similar way as with the first one, comparing the behavior on Atmega8 and Atmega328 and playing with the TIMER0WKAR "switch". Note that for this sketch is mandatory the use of optiboot bootloader on the Atmega8, to leave the maximum amount of available flash memory.

5. Why this happened (probably...)

If you compare the Atmega8/8L and Atmega328 "family" datasheets, will find that the two MCUs differs not only for the amount of memory, but also (and mainly) for the hardware design.
In particular the structure of TIMSK registers is totally different.
On the Atmega328 "family" there are several TIMSK registers, each dedicated exclusively to control bits of a single Timer.
So a statement as TIMSK2 = 0 or TIMSK2 = _BV(OCIE2A) do not have any effect to other timers.
On the Atmega8, instead, there is only one TIMSK register that controls all the timers, so the above statements have the side effects described on all the other timers.
So, it may be that simply "translating" the TIMSK name in the Atmega8 way, has generated this bug.

Hoping this will help others using Atmega8/8L...

Cheers.

      • UPDATE (Oct. 2015): New library v. 2.01 should fix this.

IRsend_test1.ino (10.4 KB)

IRrecv_test2.ino (6.91 KB)

How to sork with atmega 32 plsssss