OCR0A & ATmega328P

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

Hi guys-

 

I've got an issue I hope someone can help me out with. I have an ISR that temporarily unmasks another ISR (to keep it from firing where it's not wanted). The ultimate goal is for INT0 to enable OCIE0A, reset Timer 0, and once the timer matches OCR0A, have it fire the COMPA ISR. Which is all working...ALMOST perfectly.

 

What is actually happening is that the COMPA ISR is firing, but it doesn't appear to wait for an OCR0A match. I can set OCR0A for whatever random value I'd like & the COMPA ISR always fires 2.4us behind the INT0 ISR. It doesn't matter what value OCR0A is set for, what prescaler, where OCR0A is written in the program, or if I sei() and cli() during the interrupt routine...it's almost like something else is promptly setting OCF0A, or writing 0 to OCR0A without my knowledge.

 

Any ideas of where to look? I've been at this for 4 hours & gotten no where. ATmega328P. The pin toggles are so I can see whats happening on my o-scope.

 

Thanks in advance,

Todd

 

ISR's:

ISR(INT0_vect){					// Trigger (falling edge)			
	PORTB =(1<<PORTB5);			// Turn pin on ----TROUBLESHOOTING
	PORTB = (0<<PORTB5);			// Turn pin off ---TROUBLESHOOTING
        TIMSK0 = (1<<OCIE0A);			// Enable COMPA
 	TCNT0 =0x00;				// If not reset here, COMPA fires all over the place
	return;
	}		
	
ISR(TIMER0_COMPA_vect){				// OCR0A interrupt = total timing delay 		
	PORTB = (1<<PORTB5);			// Turn pin on ---TROUBLESHOOTING	
	PORTB = (0<<PORTB5);			// Stop pin off ---TROUBLESHOOTING
	TIMSK0 = (0<<OCIE0A);			// Disable COMPA	
	return;
	}

 

 

This topic has a solution.
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Since TIMER0 keeps running while you're waiting for the external interrupt to trigger INT0, the compare match interrupt flag will get set.  Until you enable that interrupt, the flag stays set.  As soon as you enable the interrupt, the pending flag forces the ISR to run immediately.

 

You need to clear the flag before enabling the interrupt.  You should also reset the counter first:

ISR(INT0_vect){                 // Trigger (falling edge)			
    PORTB =(1<<PORTB5);         // Turn pin on ----TROUBLESHOOTING
    PORTB = (0<<PORTB5);        // Turn pin off ---TROUBLESHOOTING
    TCNT0 =0x00;                // If not reset here, COMPA fires all over the place
    TIFR0 = (1<<OCF0A);         // Clear any previous interrupt flag
    TIMSK0 = (1<<OCIE0A);       // Enable COMPA
    return;
    }		

 

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"Read a lot.  Write a lot."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
TIMSK0 = (1<<OCIE0A);			// Enable COMPA
 ...
TIMSK0 = (0<<OCIE0A);			// Disable COMPA	

While these two lines might work for the moment, the do not really do what the comments say. The first line sets OCIE0A, but it also clears all other bits in the register. The second clears all bits in the register, not just OCIE0A. These lines are more properly written:

TIMSK0 |= (1<<OCIE0A);			// Enable COMPA
...
TIMSK0 &= ~(1<<OCIE0A);			// Disable COMPA	

The same goes for all similar lines in the code.

Regards,
Steve A.

The Board helps those that help themselves.

Last Edited: Thu. Jan 29, 2015 - 05:25 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Okay...I'll twiddle with it & see what happens. I did get it working by stopping and starting the timer instead of messing with TIMSK0, but it seems a little jittery & inconsistent.

 

Edit: You guys are right. It was that the flag had already been set when the interrupt was enabled.  I also get the point of &= and |= as well, I'll clean the code up as I go :-) Kinda still have some green spots with C code, and I'm really green on the AVR... I appreciate the help.

 

Thanks guys!!!

 

Todd

Last Edited: Thu. Jan 29, 2015 - 05:45 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
You guys are right. It was that the flag had already been set when the interrupt was enabled.  I also get the point of &= and |= as well, I'll clean the code up as I go :-)
Be aware that while @Koshchi is quite correct, you need to treat TIFRn differently.  Those registers contain only interrupt flags.  Interrupt flags are unaffected by writing them to '0', and are cleared by writing them to  '1'.  As such, a line like:

TIFR0 |= (1<<OCF0A);

... will have the unintended effect of clearing all interrupt flags in that register.  The reason is simple:  TIFR0 gets written with the same value it had before (plus OCF0A), so any flags that were set will read as '1' and will therefore be written to '1'.

 

The proper way to clear a single flag in a register containing only interrupt flags like TIFR0 is:

TIFR0 = (1<<OCF0A);

This will write only OCF0A as a '1', and the remaining flag bits will be writting to '0', leaving them unchanged.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"Read a lot.  Write a lot."

"We see a lot of arses on handlebars around here." - [J Ekdahl]