Go Down

Topic: Timer1 in C and in Processing (Read 4540 times) previous topic - next topic

avelino

Hi all,

My timer interrupt code runs ok when using Processing but does not run when using C. When I run this code on my Arduino Leonardo board the led blinks:
Code: [Select]
/*
Example Timer1 Interrupt
Flash LED every second
*/

#define ledPin 13
int timer1_counter;
void setup()
{
  pinMode(ledPin, OUTPUT);

  // initialize timer1
  noInterrupts();           // disable all interrupts
  TCCR1A = 0;
  TCCR1B = 0;

  // Set timer1_counter to the correct value for our interrupt interval
  //timer1_counter = 64886;   // preload timer 65536-16MHz/256/100Hz
  //timer1_counter = 64286;   // preload timer 65536-16MHz/256/50Hz
  timer1_counter = 34286;   // preload timer 65536-16MHz/256/2Hz
 
  TCNT1 = timer1_counter;   // preload timer
  TCCR1B |= (1 << CS12);    // 256 prescaler
  TIMSK1 |= (1 << TOIE1);   // enable timer overflow interrupt
  interrupts();             // enable all interrupts
}

ISR(TIMER1_OVF_vect)        // interrupt service routine
{
  TCNT1 = timer1_counter;   // preload timer
  digitalWrite(ledPin, digitalRead(ledPin) ^ 1);
}

void loop()
{
  // your program here...
}


But when I run this C code on the same board (using the avr-gcc included in Arduino installation), the led does not blink:
Code: [Select]
#include <avr/io.h>
#include <avr/interrupt.h>

int timer1_counter;

ISR(TIMER1_OVF_vect) {
        TCNT1 = timer1_counter;   // preload timer
        PORTC ^= 0x80;
}

int main() {
        DDRC |= 0x80;
        cli();           // disable all interrupts
        TCCR1A = 0;
        TCCR1B = 0;

        // Set timer1_counter to the correct value for our interrupt interval
        //timer1_counter = 64886;   // preload timer 65536-16MHz/256/100Hz
        //timer1_counter = 64286;   // preload timer 65536-16MHz/256/50Hz
        timer1_counter = 34286;   // preload timer 65536-16MHz/256/2Hz
 
        TCNT1 = timer1_counter;   // preload timer
        TCCR1B |= (1 << CS12);    // 256 prescaler
        TIMSK1 |= (1 << TOIE1);   // enable timer overflow interrupt
sei();             // enable all interrupts
        while (true)
                ;
}


What am I doing wrong?

Thanks in advance.

Robin2

I guess that DDRC takes the place of "pinMode(ledPin, OUTPUT);" 

As far as I can see pin 13 is in Port B.
http://arduino.cc/en/Hacking/PinMapping168

Might this have anything to do with it?

Two other comments
    Both pieces of code you have posted are written in C or C++, neither is in Processing
    If it works the easy way (which is what the Arduino system is about) why bother with the hard way?

...R
Two or three hours spent thinking and reading documentation solves most programming problems.

PaulS

Code: [Select]
int main() {
The IDE supplies, when one is not present, a main function that calls init(), setup(), and loop(). While you don't need to call setup() or loop(), you do need to call init(). You don't, so don't expect the things that init() does to have been done.
The art of getting good answers lies in asking good questions.

avelino

Thank you for your responses:

Robin2: on Arduino Leonardo, pin 13 is at PORTC, bit 7. You are ok: Both pieces of code are strictly C, but I used that terms to remark that the first piece of code is more "Arduino IDE friendly" than second one (which is more "raw avr-g++ friendly"). For advanced multi-file OOP projects I prefer to use avr-g++ with makefiles than the Arduino IDE.

PaulS: I didn't know that there is an "init()" function call on supplied "main()" function. Perhaps that is the problem with my code.

Thanks!  :-)

westfw

Offhand, I don't see anything happening in init() that you aren't doing as well.
Are you sure that your compile command is correct?
If you turn on the LED in main(), does it go on to start with (and never go off)?  (are your sure it's your timer that isn't working, rather than the output code?)

nickgammon

You have a number of problems in your sketch, for example:

Code: [Select]

int timer1_counter;


That should be both volatile and unsigned int, as you are stuffing 34286 into it.

Extensive testing reveals that by omitting setup() and loop() you have omitted two important calls:

Code: [Select]

  init ();
  USBDevice.attach();


If you add those in, it works (although I'm not quite sure why):

Code: [Select]

#include <avr/io.h>
#include <avr/interrupt.h>

volatile unsigned int timer1_counter;

ISR(TIMER1_OVF_vect)        // interrupt service routine
{
  TCNT1 = timer1_counter;   // preload timer
  PORTC ^= 0x80;
}

int main ()
{
  init ();
  USBDevice.attach();

  DDRC |= 0x80;
  cli();           // disable all interrupts
  TCCR1A = 0;
  TCCR1B = 0;

  // Set timer1_counter to the correct value for our interrupt interval
  //timer1_counter = 64886;   // preload timer 65536-16MHz/256/100Hz
  //timer1_counter = 64286;   // preload timer 65536-16MHz/256/50Hz
  timer1_counter = 34286;   // preload timer 65536-16MHz/256/2Hz

  TCNT1 = timer1_counter;   // preload timer
  TCCR1B |= (1 << CS12);    // 256 prescaler
  TIMSK1 |= (1 << TOIE1);   // enable timer overflow interrupt
  sei();             // enable all interrupts
  while (true)
    ;
}
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

westfw

Since this is an "interesting" problem, I looked into it further.

It looks as thought the leonardo bootloader leaves USB turned on when it runs the user application.  As soon as you enable interrupts, the AVR gets a USB interrupt and vectors off to __bad_interupt(), which ... restarts the application.
The easiest fix is to turn off the USB peripheral using the power controller:
Code: [Select]
    PRR1 |= 1<<PRUSB;

Putting a prelude at main() that flashed the led several times before fiddling with the timer made it obvious that the code was restarting.  I tried the watchdog (no luck), and then  (noticing that __bad_interrupt caused a restart) filled in all the interrupt vectors that were present in the arduino sketch but not the C program.   With recognizable LED-flash patterns.

(bad bootloader!  No cookie for you!)


nickgammon

Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

Tom Carpenter

One really important thing to note if you are ditching the Arduino core (by having your own main()):

Interrupts on an AVR are disabled at reset. In otherwords, if you want to use interrupts, you have to make sure you add:

Code: [Select]
sei();

In your sketch to enable the global interrupt flag. This is actually done for you in the Arduino init() function which is why you don't have to do it in a normal Arduino sketch.



Edit: Never mind, just noticed that in your original sketch. I'll be quiet now.
~Tom~

avelino

Hi all,

Using
Code: [Select]
PRR1 |= 1 << PRUSB;The Arduino Leonardo is now blinking!!  :-D. (westfw, Your suspicion was ok, thank you!)

Thanks!

nickgammon


My timer interrupt code runs ok when using Processing but does not run when using C.


Your original code was C++, anyway. You are just making things harder for yourself doing it this way.

Quote
Using
Code: [Select]
PRR1 |= 1 << PRUSB;
The Arduino Leonardo is now blinking!!  :-D. (westfw, Your suspicion was ok, thank you!)


That's fine, if you aren't planning to use your Leonardo for anything to do with USB.
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

westfw

Quote
That's fine, if you aren't [using]USB.

Yep.  Note that if you DO want to use USB, you'd need a lot more code than just USBDevice.attach() !
Fortunately, you can get back to the USB bootloader via the RESET switch!
Disabling USB in the power-controller seemed much simpler than figuring out how to undo all of the USB setup done int the bootloader.  Since I came across it earlier, when investigating why the timer might not have been running, it was fresh in my mind.

(Note that this is one of the "typical" differences with ARM microcontrollers.   On an AVR, the peripherals all default to being turned on, but you can turn some of them off to save power.  On most ARM chips, you have to turn peripheral clocks ON in the equivalent of the powerdown controller before you can use them at all.  That usually includes even the general purpose IO.)

Go Up