Simple Timer / Interrupt code not behaving

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

Hi 

 

I have just put together a simple project in Atmel Studio to run on a ATMega16A.

 

I want to detect a positive transition on INT0 pin and output a single pulse on all PortB pins of a programmable width. When a rising edge is detected, PortB pins go HI, TIMER1 counter is reset and after the compare matches, PortB pins go LO again.

 

The code seems to work mostly but I always get two pulses generated instead of one. Note after the first 400ms pulse there is another that immediately follows. I have configured the INT0 ISR so that immediately the INT0 interrupt is disabled until the pulse is over, so I am not clear why this is triggering twice. I thought it might be contact bounce as I am just touching a wire to generate the pulse, but the first command in the ISR is to disable the interrupt so I expect bounce to have subsided by the time the pulse is complete.

 

 

 

 

My code is as follows:

 

/*
 * AOI_LED_Controller.c
 *
 * Created: 20/10/2014 2:01:11 PM
 */ 
#include <avr/io.h>
#include <inttypes.h>           //short forms for various integer types
#include <avr/interrupt.h>      //file to be included if using interrupts
#define sbi(x,y) x |= _BV(y) //set bit - using bitwise OR operator
#define cbi(x,y) x &= ~(_BV(y)) //clear bit - using bitwise AND operator
#define tbi(x,y) x ^= _BV(y) //toggle bit - using bitwise XOR operator
#define is_high(x,y) (x & _BV(y) == _BV(y)) //check if the y'th bit of register 'x' is high ... test if its AND with 1 is 1
#define XTAL            4608000L        // Crystal frequency in Hz
#define PRESCALER        64                // Prescaler setting
#define COUNT_PER_MS    XTAL / 1000     //How many timer ticks for 1ms to expire
#define PULSE_LENGTH    400    // SET THIS AS REQUIRED!!  How many ms wide should the pulse be    
#define TICKS_REQUIRED    (PULSE_LENGTH * COUNT_PER_MS) / PRESCALER   //How many timer ticks needed to generate the desired pulse width
static volatile uint8_t led;     // use volatile when variable is accessed from interrupts

ISR(TIMER1_COMPA_vect)        // handler for Output Compare 1 overflow interrupt (pulse has reached full width)
{    // Pulse timer has reached max. Now set up to wait for another trigger signal on INT0
    TCNT1 = 0x00;            // Reset the counter to 0
    PORTB = 0x00;            // Reset port B all pins
    cbi(TIMSK,OCIE1A);        // disable Output Compare 1 overflow interrupt until an INT0 interrupt has been triggered
    sbi(GICR,INT0);            // Enable INT0 again
}
ISR(INT0_vect) //Handler for external interrupt 0
{    //External trigger on INT0 received, start timer counting
    cbi(GICR,INT0);                    //Firstly disable external interrupt INT0 until pulse timer expires
    cbi(GIFR,INTF0);                //Clear interrupt flag for INT0
    TCNT1 = 0x00;                    // Reset the counter to 0
    TCCR1A |= 0;
    TCCR1B = _BV(CS10) | _BV(CS11) | _BV(WGM12); // prescaler=64, clear timer/counter on compare match. CTC mode, OCR1A provides max value. //
    OCR1A = TICKS_REQUIRED - 1;        // set number of ticks required for specified pulse length
    sbi(TIMSK,OCIE1A);                // enable Output Compare 1 overflow interrupt
    PORTB = 0xff;                    // Turn on all port B pins
}
void Setup_INT0()  //Initialization for external interrupt INT0 (on pin 11)
{
    sbi(MCUCR,ISC00);    //Set INT0 to fire on rising edge only
    sbi(MCUCR,ISC01);
    cbi(GICR,INT0);        //Disable INT0 specifically, Enable it later when required
}


int main(void)
{
    DDRB  = 0xff;               // use all pins on PortB for output
    PORTB = 0x00;               // and turn all pins on
    
    Setup_INT0();                // Initialize external interrupt INT0
    cbi(TIMSK,TOIE1);            // Disable overflow interrupt
    sei();                        //Enable global interrupt bit
    sbi(GICR,INT0);                //Enable INT0
    
    for (;;) {}                 // loop forever
}

 

Could anybody please let me know if I have done something wrong? I didn't think this was going to be a tough problem but I am stumped. Thanks for any advice!

This topic has a solution.

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

You need to reset the interrupt flag for the external int before re-enabling the interrupt. You do this by writing a '1' bit to the flag. The actual register and bit name escapes me at the moment.

basically there is a latch that is set when the input edge is detected. If the interrupt is enabled then the isr gets  called. You then disable the interrupt but the latch captures the bounce. After you re-enable the interrupt the isr gets called as the latch was previously set. Thus you need to manually reset that latch before you re-enable the interrupt. 

Last Edited: Wed. Oct 29, 2014 - 03:11 AM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You "clear" an interrupt flag by writing a one to the flag bit, I know it's not logical, but that is how AVR's work. 

From the datasheet:   The flag is cleared when the interrupt routine is executed.
Alternatively, the flag can be cleared by writing a logical one to it.

Be sure to clear the flag before you re-enable the interrupt!

Hope that helps.

 

JC

Last Edited: Wed. Oct 29, 2014 - 03:27 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks very much Kartman and ki0bk you guys are both legends! That solved my problem.

Both answers were very helpful but I better boost the new guy's score.

All the best.

Last Edited: Wed. Oct 29, 2014 - 06:11 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The score means nothing here as opposed to likes of stack exchange. This website does some freaky stuff at times - the posts got mixed up.

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

You "clear" an interrupt flag by writing a one to the flag bit, I know it's not logical, but that is how AVR's work. 

It is actually perfectly logical, it is just not intuitive.

Regards,
Steve A.

The Board helps those that help themselves.

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

Koshchi wrote:

You "clear" an interrupt flag by writing a one to the flag bit, I know it's not logical, but that is how AVR's work. 

It is actually perfectly logical, it is just not intuitive.

I am SO ready to have this discussion. smiley  (I agree with you, BTW)