Stupid question about interrupts

Go To Last Post
22 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi, I'm a total newb. I've read many tutorials though and am trying to get a stupid LED to blink on the ATTiny25 using a CTC interrupt. (I used the TIMER0_COMPB vector rather than pure CTC mode, because I want to add a delay in the beginning and do some other stuff than just blinking at one frequency later). I modified the code in the AVR timers tutorial to supposedly match the registers and bits required for the 25 versus the mega the tutorial was written for. (I also added a sleep mode between interrupts). It's still not working though. I have no clue what's wrong. I've been over and over the datasheet. Can someone tell me what is wrong with this?

// Flash LED's - A program to flash an LED at 4 Hz on the ATTiny25V
// by interrupting on timer compare CTC mode.

#include 
#include 
#include 

int main (void)
{
   ACSR |= (1 << ACD); // Turn off Analog comparator to save power

   DDRB |= (1 << 0); // Set LED as output

   TCCR0A |= (1 << WGM01); // Configure timer 1 for CTC mode

   TIMSK |= (1 << OCIE0B); // Enable CTC interrupt

   sei(); //  Enable global interrupts

   OCR0B   = 244; // Set CTC compare value to 4 Hz at 1 MHz AVR clock, with a prescaler of 1024 (976.6 Hz)

   TCCR0B |= ((1 << CS02) | (1 << CS00)); // Start timer at Fcpu/1024

   MCUCR |= (1 << SE); // Enable sleep mode (hopefully)

   set_sleep_mode(SLEEP_MODE_IDLE);

   for (;;)
   {
	  sleep_mode(); //Sleep between interrupts (hopefully)
   }
}

ISR(TIMER0_COMPB_vect)
{
   PORTB ^= (1 << 0); // Toggle the LED
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Are you trying this on real hardware or the simulator?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well, you're not setting OCR0A to any value, so presumably it's zero, and CTC uses OCR0A as the TOP value. I don't know what CTC mode does when OCR0A is zero, but I bet it doesn't do what you're hoping it does.

On another note, the AVR has a neat little feature. You can replace

   PORTB ^= (1 << 0); // Toggle the LED 

with

   PINB = (1 << 0); // Toggle the LED 

and that will toggle the pin.

Mike

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm trying it out on real hardware.

Isn't OCR0A for timer A though? I think I have it set up to use timer B and OCR0B is set to 244

If I use pinB instead of portB, how does it know which pin to turn on?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

unseenwombat wrote:
I'm trying it out on real hardware.

Isn't OCR0A for timer A though? I think I have it set up to use timer B and OCR0B is set to 244


There is no "timer A" or "timer B" on AVRs. There are timer(/counter) 0, 1 and 2, and each of them has an OCRnA and an OCRnB register. You've been accessing timer 0 according to your source code.

Quote:
If I use pinB instead of portB, how does it know which pin to turn on?

Writing a '1' bit to any PIN bit will cause that port's same PORT bit to toggle. Thus

PORTB ^= (1<<0);

has the same effect as

PINB = (1<<0);

but the latter is faster and produces smaller code.

Mike

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Okay, I tried changing it, but the LED still isn't blinking. I don't know what's wrong. Here's the code I have now:

// Flash LED's - A program to flash an LED at 4 Hz on the ATTiny25V
// by interrupting on timer compare CTC mode.

#include 
#include 
#include 

int main (void)
{
   ACSR |= (1 << ACD); // Turn off Analog comparator to save power

   DDRB |= (1 << 0); // Set LED as output

   TCCR0A |= (1 << WGM01); // Configure timer 1 for CTC mode

   TCCR0B |= ((1 << CS02) | (1 << CS00)); // Start timer at Fcpu/1024

   TIMSK |= (1 << OCIE0A); // Enable CTC interrupt

   sei(); //  Enable global interrupts

   OCR0A   = 244; // Set CTC compare value to 4 Hz at 1 MHz AVR clock, with a prescaler of 1024 (976.6 Hz)

   MCUCR |= (1 << SE); // Enable sleep mode 

   set_sleep_mode(SLEEP_MODE_IDLE);

   for (;;)
   {
	  sleep_mode(); //Sleep between interrupts 
   }
}

ISR(TIMER0_COMPA_vect)
{
   PORTB ^= (1 << 0); // Toggle the LED
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm just guessing, but you may be trapped in interrupt-hell since you start the timer and enable interrupts while OCR0A is still zero. Try this order:

   OCR0A   = 244; // Set CTC compare value to 4 Hz at 1 MHz AVR clock, with a prescaler of 1024 (976.6 Hz) 

   TCCR0A |= (1 << WGM01); // Configure timer 1 for CTC mode 

   TIMSK |= (1 << OCIE0A); // Enable CTC interrupt 

   TCCR0B |= ((1 << CS02) | (1 << CS00)); // Start timer at Fcpu/1024 

  sei(); //  Enable global interrupts 

Here you'll be setting OCR0A before starting the timer, and you won't enable interrupts until everything else is set up.

Mike

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I would forget sleep mode for now. There is no real need for it.

Quote:
I'm just guessing, but you may be trapped in interrupt-hell since you start the timer and enable interrupts while OCR0A is still zero
.
The order shouldn't really make a difference (though I agree that your order is better). With the original order the interrupt will simply fire once before OCR0A is set to the required value.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok, it makes sense that that's better, but it still doesn't flash.

I took out the sleep function, but I would like to have it in there because in the future, I'd like to put a delay before the LED starts flashing, so I'd like it to sleep then and conserve battery.

Here's the changes I made to the code.

// Flash LED's - A program to flash an LED at 4 Hz on the ATTiny25V
// by interrupting on timer compare CTC mode.

#include 
#include 
#include 

int main (void)
{
   ACSR |= (1 << ACD); // Turn off Analog comparator to save power

   DDRB |= (1 << 0); // Set LED as output

   OCR0A   = 244; // Set CTC compare value to 4 Hz at 1 MHz AVR clock, with a prescaler of 1024 (976.6 Hz)

   TCCR0A |= (1 << WGM01); // Configure timer 1 for CTC mode

   TIMSK |= (1 << OCIE0A); // Enable CTC interrupt

   TCCR0B |= ((1 << CS02) | (1 << CS00)); // Start timer at Fcpu/1024

   sei(); //  Enable global interrupts

for (;;)
   {
   }
}

ISR(TIMER0_COMPA_vect)
{
   PORTB ^= (1 << 0); // Toggle the LED
}
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Time for good news / bad news.

The good news is that your code works - I programmed it into an ATtiny25 and got 4 interrupts per second, output on PB0. It works with the sleep mode code included or removed.

So you need to look elsewhere for the problem. Try just blinking the LED in your main loop with a _delay_ms(250) and see if that shows up.

Mike

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Aha! it does work on PB0. Why though doesn't it work on the other portB pins? I see PB0 is OC0A, so that must be the reason, but I don't understand why.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What do you mean, it doesn't work on other PORTB pins? Have you set the other pin(s) to outputs via DDRB? The fact that PB0 can be assigned to OC0A doesn't apply here, since you're using the pin just as a GPIO output.

Mike

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't know. I plug the LED into PB1 through 4 and it doesn't work. I plug it into PB0 and it works. I have no idea why.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
I don't know. I plug the LED into PB1 through 4 and it doesn't work. I plug it into PB0 and it works. I have no idea why.

You did make the corresponding software changes as well?

Chuck Baird

"I wish I were dumber so I could be more certain about my opinions. It looks fun." -- Scott Adams

http://www.cbaird.org

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

unseenwombat wrote:
I don't know. I plug the LED into PB1 through 4 and it doesn't work. I plug it into PB0 and it works. I have no idea why.

Well, your code as posted doesn't set PB1-PB4 as outputs, and doesn't set any of those bits high or low. So by default they're just inputs, and inputs can't drive an LED.

Mike

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

aren't PB1 through 4 included in "DDRB?" I thought

DDRB |= (1 << 0);

ncluded those as well.

If not, how do I set them as outputs?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

unseenwombat wrote:
aren't PB1 through 4 included in "DDRB?" I thought
DDRB |= (1 << 0);

ncluded those as well.

If not, how do I set them as outputs?

They are in DDRB, but you only set bit 0. Read the tutorial at https://www.avrfreaks.net/index.p...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes, PB1 through 4 are set as outputs through DDRB, but you need to set the corresponding bit in DDRB for each output pin. Thus to use PB3 as your output:

// set PB3 as an output
DDRB |= (1<<3);    // or (1<<PB3), same thing
....
// toggle PB3
PINB = (1<<3);       // or (1<<PB3)
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Okay, I got it. I did read that tutorial, but that part was kinda over my head I guess. Thanks for explaining it, and thanks for all your help guys.

Here's something strange though, when I use "PINB" in my ISR, it turns the LED on steadily, even though it's supposed to blink. When I use "PORTB" it blinks like it's supposed to. Any idea why that is?

The same thing happens when I try to set more than one pin as output i.e.,

DDRB  |= ((1<<0)|(1<<3));
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Why don't you post the exact code - it's hard to know what's happening just from your comments.

Mike

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This is my code

// Flash LED's - A program to flash an LED at 4 Hz on the ATTiny25V
// by interrupting on timer compare CTC mode.

#include 
#include 
#include 

int main (void)
{
   ACSR  |= (1 << ACD); // Turn off Analog comparator to save power

   DDRB  |= ((1 << 0)|(1 << 3)); // Set LED as output

   OCR0A  = 244; // Set CTC compare value to 4 Hz at 1 MHz AVR clock, with a prescaler of 1024 (976.6 Hz)

   TCCR0A |= (1 << WGM01); // Configure timer 1 for CTC mode

   TIMSK  |= (1 << OCIE0A); // Enable CTC interrupt

   TCCR0B |= ((1 << CS02) | (1 << CS00)); // Start timer at Fcpu/1024

   sei(); //  Enable global interrupts

   for (;1==1;)
   {
   }
}

ISR(TIMER0_COMPA_vect)
{
   PORTB ^= ((1 << 0)|(1 << 3)); // Toggle the LEDs
}

I know if you type

DDRB = 0x18;

that will set PB3 and PB4 as outputs. (binary 00011000) So could I say

DDRB = 0x1F;

to set PB0,1,2,3 and 4 as outputs? (binary 00011111). It doesn't work when I type

DDRB = 00011111;

.

I'll have to try it when I get home.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Syntax is

DDRB = 0b00011111;

Quote:

It doesn't work when I type
DDRB = 00011111;

.

I'll have to try it when I get home.