Avr-gcc: Timer/counter interrupts conflicting with UART?

Hi all,

I tried this question first as Avr-gcc: Timer/counter interrupts conflicting with UART?; but since that problem is a showstopper, I'd like to see if I get some comments here. Not sure if its a bug, per se, (I think it could be more, due to my lack of understanding of C++ classes and interrupts) but the content follows: :::::

Please consider the following example (tried on Arduino IDE 0022, Ubuntu 11.04, Arduino AtMEGA 2560), where I'm trying to start a timer/counter interrupt and use the Arduino Serial class at the same time:

volatile uint8_t sreg;

// main loop debug pin 
#define DBGPINM 13
// ISR debug pin 
#define DBGPINI 34


// Timer 0 interrupt routine
ISR(TIMER0_COMPA_vect, ISR_NAKED)
{
  sreg = SREG;  /* Save global interrupt flag */
  cli(); /* Disable interrupts */

  digitalWrite(DBGPINI, not((bool)digitalRead(DBGPINI)));

  SREG = sreg; /* Restore global interrupt flag */
  reti(); // must for ISR: return and enable interrupt  
}

void setup() {
  pinMode(DBGPINM, OUTPUT);
  pinMode(DBGPINI, OUTPUT);
  Serial.begin(115200);
  Serial.println("Hello from setup");
  delay(200); 
}

void loop() {
  digitalWrite(DBGPINM, HIGH);
  Serial.println("Hello from loop: A");
  
  digitalWrite(DBGPINM, LOW);
  delay(200); 
  
  digitalWrite(DBGPINM, HIGH);
#if 1 // register update part
  cli(); // disable interrupts
  GTCCR = 0b10000011; // halt timers
  // set up Timer/Counter 0
  TCCR0A = 0b00000010; // CTC; normal mode (don't use output pin)
  TCCR0B = 0b00000101; // no force output; CTC; ... and clock select: prescale 1024
  TCNT0 = 0; // init the actual counter variable
  TIMSK0 = 0b00000010; // enable (only) Compare Match A Interrupt
  OCR0A = 125; //the top value, to which the running counter is compared to

  GTCCR = 0b00000000;
  sei(); // Enable interrupts once registers have been updated
  
  digitalWrite(DBGPINM, LOW);
  delay(200); 
#endif

  digitalWrite(DBGPINM, HIGH);
  Serial.println("Hello from loop: B");
  
  digitalWrite(DBGPINM, LOW);
  delay(200);
}

As the example is, the printout via serial will be:

    Hello from setup
    Hello from loop: A
    Hello from loop: B
    Hello from loop: A
    Hello from loop: B

... and then all processing will stop (indicated by lack of action on both LED pin 13 and 34); I guess, this is what you would call a BSOD in the chip world :) Superficially, the halt happens as soon as the ISR routine kicks in for the first time.

If you take out the "register update part", then the serial printout runs forever, as expected - and also (as expected), there is no ISR running. However, if the "register update part" is left, and the two "Serial.println(..." lines are commented instead - then the program prints only "Hello from setup" - but the interrupt does run (as evidenced by pulses on pin 34).

Which seems to tell me, that you cannot run a timer ISR and the UART on the ATMega2560 at the same time - which is silly, given that I had previously used, with success, the same kind of approach on an ATMega328.

So, I'm wandering whether what I want to do (have both serial printout and pins pulsing) is fundamentally impossible with this architecture - or am I just missing something in the setup?

Thanks in advance for any answers, Cheers!

(Just wanted to note that this Serial class actually operates on a class definition in HardwareSerial.cpp in the Arduino IDE package; and this class defines the reception USART interrupt routines; thought this may be the problem - but again, I used the same approach in ATMega328, where I had seen it work.. )

Try it without using ISR_NAKED (and so you don't need to remember SREG or use cli or use sei or use reti)

also, do you need to constaintly re-initialize the timer in loop()? why not do it only once in setup()?

oh and double check that you have the right board selected, so that the compiler is indeed targetting the atmega2560

Hi @frank26080115;

Thanks for the prompt response!!

frank26080115: oh and double check that you have the right board selected, so that the compiler is indeed targetting the atmega2560

Did that, double-checked :)

frank26080115: also, do you need to constaintly re-initialize the timer in loop()? why not do it only once in setup()?

You're absolutely right - that initialization indeed started in setup; then when I saw it failing, I thought it maybe has to do with it being located there (although it should be in setup, as you note) - I tried a myriad of combinations of code placements, to see if that made a difference - and when I finally exhausted all I could try, I decided to post; and that code is simply the version I had at the time... EDIT: But it also helps to demonstrate where exactly the code fails - which arguably would be more difficult if it is kept in setup...

frank26080115: Try it without using ISR_NAKED (and so you don't need to remember SREG or use cli or use sei or use reti)

This sounds very promising, thanks - I had no (clear) idea that ISR_NAKED is related to use of SREG cli/sei/reti - will give it a shot!

Many thanks again for the prompt response,

Cheers!

Hi again,

Just to note - I just re-run the exact same code above (apart from change of debug pin) through an Arduino Diecimilla with an ATMega168 - everything works fine - interrupt function is running along with happily with the main loop (and in spite of halting and resetting everything at each execution of the main loop!) EDIT: Also tried it with an Arduino UNO with an ATMega328 - works fine too...

Which gives?: this is probably a bug, either with: avr-gcc specifically for the ATMega2560; or some wrong defines are being read for the ATMega2560 by the Arduino IDE (but then, compiler would complain, no?); or there is some register that needs to be set additionally for the ATMega2560 (which means plowing through the massive datasheet :( ); - or finally, there is maybe some sort of a "hardware" bug on the board?

Well, if anyone could think of a thing to try, please comment ...

Thanks - cheers!

Is this the code you are running on the 168 and 328 processors... http://arduino.cc/forum/index.php/topic,69017.msg509975.html#msg509975

HI @Coding Badly,

Thanks for replying!

[quote author=Coding Badly link=topic=69017.msg510223#msg510223 date=1312998293] Is this the code you are running on the 168 and 328 processors... http://arduino.cc/forum/index.php/topic,69017.msg509975.html#msg509975 [/quote]

Yup - the exact same (except for that I changed pin 34 to pin 7 for the smaller Arduinos)..

Cheers!

Hi @smd,

smd:
Thanks for replying!

Don’t thank me yet. I’m about to scold you…

// Timer 0 interrupt routine
ISR(TIMER0_COMPA_vect , ISR_NAKED ) // Remove ISR_NAKED. Even for very experienced AVR developers, this is difficult to get correct.
{
// Remove the following two lines of code. The are unnecessary.
sreg = SREG; /* Save global interrupt flag /
cli(); /
Disable interrupts */

digitalWrite(DBGPINI, not((bool)digitalRead(DBGPINI)));

// Remove the following two lines of code. They are unnecessary.
SREG = sreg; /* Restore global interrupt flag */
reti(); // must for ISR: return and enable interrupt
}

I just re-run the exact same code above (apart from change of debug pin) through an Arduino Diecimilla with an ATMega168 - everything works fine

With the ISR_NAKED option, you are lucky that it works. Get rid of that option.

Cheers!

Cheers to you as well!

Hi @Coding Badly,

Hah, no worries - I certainly don’t mind scolding, if I missed anything important, and I learn something new! :slight_smile: Besides, it’s always nice to see a thread is alive… so thanks again! :slight_smile:

Done - honestly, I don’t know much about interrupts, and I have copied that initialization some time ago from somewhere (forgotten where from) - and as it worked then (but for a different interrupt, and chip), I just kept on copying it in different projects …

Done - now the function looks like this:

...
ISR(TIMER0_COMPA_vect)
{
  digitalWrite(DBGPINI, not((bool)digitalRead(DBGPINI)));
}
...

When I run the code with this function, the ATMEGA2560 shows interrupt debug pin pulsing; however, nothing on main debug pin (13), and the serial output is:

Hello from setup
Hello from loop: A

… and that’s it, there the main loop apparently locks. I’m guessing it is some of the register setting in the “register update part” that breaks the main process…

So, I thought to look once more into that part of process, and look at this - the following piece (along with the previous simplification of the ISR) actually demonstrates both serial write and interrupts on the 2560:

#if 1 // register update part
  cli(); // disable interrupts
  GTCCR = 0b10000011; // halt timers
  // set up Timer/Counter 0
  //  TCCR0A = 0b00000010; // CTC; normal mode (don't use output pin)
  TCCR0B = 0b00000101; // no force output; CTC; ... and clock select: prescale 1024
  TCNT0 = 0; // init the actual counter variable
  TIMSK0 = 0b00000010; // enable (only) Compare Match A Interrupt
  OCR0A = 125; //the top value, to which the running counter is compared to

  GTCCR = 0b00000000;
  sei(); // Enable interrupts once registers have been updated
  
  digitalWrite(DBGPINM, LOW);
  delay(200); 
#endif

… That is, I just need to comment the TIMSK0 = 0b00000010; TCCR0A = 0b00000010; statement - and code starts working on the MEGA2560 just as on the other two chips! However, the question is why ? And also, as far as I understand - I do need to set TIMSK0 TCCR0A, in order to set CTC mode, right?

Seemingly, this TIMSK0 TCCR0A setting will kill serial.write even if it is in setup() (that is where I first noticed it) … And so I found this post for TIMSK0: atmega2560 Interrupt :: AVR Freaks:

Koshchi:
OUT only works for registers with addresses below 64 (0x40). TIMSK0 on that chip has an address of 110 (0x6E). You need to use STS instead.

… but it is from 2005, and if I press Shift+Verify in Arduino IDE to get the command line, and then modify the switch -c to -S, I get an assebly line listing, which shows that the C command TIMSK0 = 0b00000010; is correctly compiled using the STS assembly instruction:

# for stdout:
$ grep -r SKETCHNAME /tmp/console*
/tmp/console390364846081395661.tmp/stdout.txt:avr-g++ ...

$ avr-g++ -S -g -Os -w -fno-exceptions -ffunction-sections -fdata-sections -mmcu=atmega2560 -DF_CPU=16000000L -DARDUINO=22 -I/path/to/arduino-0022/hardware/arduino/cores/arduino /tmp/build6714629142643953238.tmp/SKETCHNAME.cpp -o/tmp/build6714629142643953238.tmp/SKETCHNAME.cpp.s

$ less /tmp/build6714629142643953238.tmp/SKETCHNAME.cpp.s

# shows:

/* #APP */
 ;  42 "/tmp/build6714629142643953238.tmp/isrsertest.cpp" 1
        cli
 ;  0 "" 2
        .stabn  68,0,43,.LM7-.LFBB1
.LM7:
/* #NOAPP */
...
.LM8:
        ldi r25,lo8(2)
...
.LM11:
        sts 110,r25                     <== YUP sts being used, all fine
        .stabn  68,0,49,.LM12-.LFBB1
/* #APP */
 ;  52 "/tmp/build6714629142643953238.tmp/isrsertest.cpp" 1
        sei
 ;  0 "" 2
        .stabn  68,0,54,.LM15-.LFBB1
.LM15:
/* #NOAPP */

So, that ain’t it either - but apparently, the key here is some conflict between TIMSK0 TCCR0A and the UART on an ATMEGA2560… Any tips related to how to proceed on this, are more than welcome :slight_smile:

Cheers!

EDIT: Made a mistake - should have been TCCR0A not TIMSK0 (so I shouldn’t have bothered with the above assembly listing check… but I’m leaving that part for reference)

Hola again,

Well, it seems I have somewhat of a workaround fix: basically, I avoid using Timer 0; and use Timer 2 instead (simply replace respective register names to: TIMER2_COMPA_vect, TCCR2A, TCCR2B, TCNT2, TIMSK2, OCR2A in the code above) - and then both interrupt pulses, and main loop pulses and serial writes, are visible continuously. This is fine for my current project where I need only one timer anyway - but what to do in case there is need of more?

So this is a bug, it seems, specific to Timer/Counter 0 on Arduino MEGA 2560... By using PORTA = TCCR0A;, I could see its initial value (in the setup function, at least) is 0b00000011 (incidentally, that is the same initial value for TCCR2A) - which is "fast PWM" mode; and indeed, if you consult arduino-mega2560-schematic.pdf, the output compare pin A for timer 0 (OC0A/OC1C/PCINT7) PB7 (26) - D13 is marked with "PWM". The only thing, obviously different from timer 2, is that this OC pin is also used to drive the LED (but then again, the same pin is used in the same way on the Diecimilla/Uno - and they seemingly don't have this problem).

In any case - I'd still love to hear an explanation for why this is happening :)

Cheers!

Now that the ISR is corrected, it’s time to address the reason you created this topic…

So this is a bug, it seems, specific to Timer/Counter 0

Timer 0 is used by millis, micros, delay, and delayMicroseconds (and two PWM pins). You can certainly use timer 0 for your own purposes but doing so will very likely cause those four functions to misbehave. In your case, delay completely stopped working.

Hi @Coding Badly,

Thanks a lot for this clarification:

[quote author=Coding Badly link=topic=69017.msg510728#msg510728 date=1313044984]

So this is a bug, it seems, specific to Timer/Counter 0

Timer 0 is used by millis, micros, delay, and delayMicroseconds (and two PWM pins). You can certainly use timer 0 for your own purposes but doing so will very likely cause those four functions to misbehave. In your case, delay completely stopped working. [/quote]

I was of course aware that some sort of resources must be already used on the board, but it didn't really sink in until I read your comment - probably because now I had a real problem :) And good for me to know it is not a bug - however, it is something to take into account... (although, I just now bumped into a similar freeze problem, when I try to set ADCSRA on the ATMega2560 to anything else than its default value :( I wish I knew if any of the API functions are using it as well... ;) )

Btw, is there any sort of a quick reference that mentions these kind of resource usages by the Arduino API functions?

Thanks again - much appreciated, Cheers!

Btw, is there any sort of a quick reference that mentions these kind of resource usages by the Arduino API functions?

I suspect there is but I’m not aware of one.

Bear in mind that you have the complete source code already installed on your computer. For example, wiring.c / init contains the code to initialize and use timer 0.