external interrupt initially firing with input pullups

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

I'm running into a problem where my logic change external interrupts are firing once when first enabled at program startup. This only happens when I enable pullups on the corresponding input pins beforehand. I thought that I went through the necessary procedure outlined by the datasheet to avoid initially triggering the interrupts, but I guess not. Here is some sample code that narrows it down:

#include 
#include 
#include 
#include "uart.h"

#define HOME_SENSOR_PORT	PORTD
#define HOME_SENSOR_DDR		DDRD
#define HOME_SENSOR_PIN		PIND
#define HOME_SENSOR_AZ		PD0
#define HOME_SENSOR_ELEV	PD1

#define clear_bit(a,z) (a &= ~_BV(z))
#define set_bit(a,z) (a |= _BV(z))

#define clear_2_bits(a,y,z) (a &= ~( _BV(y) | _BV(z) ))
#define set_2_bits(a,y,z) (a |= _BV(y) | _BV(z))

uint8_t value, value2;

//azimuth motor home sensor external interrupt
ISR(INT0_vect) {
	value++;
}

ISR(INT1_vect) {
	value2++;
}

int main(void) {
	uart_setup1(9600);
	
	value = 0;
	value2 = 0;
	
	cli();
	
	//try to avoid triggering an initial interrupt when setting up external interrupts (this doesn't seem to work)
	clear_2_bits(EIMSK, INT0, INT1);
	//set the data directions for the home sensors (both as inputs)
	clear_2_bits(HOME_SENSOR_DDR, HOME_SENSOR_AZ, HOME_SENSOR_ELEV);
	//enable pullup resistors
	set_2_bits(	HOME_SENSOR_PORT, HOME_SENSOR_AZ, HOME_SENSOR_ELEV);
	
	//setup the external interrupts, triggered on any lotical change, that count motor revolutions / detect home positions
	set_2_bits(EICRA, ISC00, ISC10);
	clear_2_bits(EICRA, ISC01, ISC11);
	//enable the external interrupts
	set_2_bits(EIMSK, INT0, INT1);
	
	//clear the interrupt flags
	clear_2_bits(EIFR, INTF0, INTF1);
	
	sei();
	
	
	while(1) {
		transmit_uint(UART_TERM, value, true);
		transmit_uint(UART_TERM, value2, true);
		uart_newline(UART_TERM);
	}
	return 0;
}

The values I receive on the UART from my atmega64 are both set to 1 when running on real hardware. However, the AVR Simulator reports value and value2 as zero while transmitting. My guess is that I'm triggering a software interrupt somehow, but I have no idea how to stop it from happening. Any ideas?

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

declare "value" and "value2" as volatile.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
 //clear the interrupt flags
   clear_2_bits(EIFR, INTF0, INTF1);

This does not clear the interrupt flags. Interrupt flags are cleared by writing a 1 to them, not a 0. Also as a general rule, do not do a read/modify/write operation to do this (such as EIFR |= 1<<INTF0) as this will clear all pending interrutps, not just the one that you intend. In your case this behavior wouldn't matter since you want to do that anyways. The line should be:

EIFR = (1<<INTF0) | (1<<INTF1);

Regards,
Steve A.

The Board helps those that help themselves.

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

glitch wrote:
declare "value" and "value2" as volatile.

Good suggestion, but still the same result. (Looking at my original piece of code, I really did have the variable set to volatile... I just forgot in this test example.)

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

Koshchi wrote:

 //clear the interrupt flags
   clear_2_bits(EIFR, INTF0, INTF1);

This does not clear the interrupt flags. Interrupt flags are cleared by writing a 1 to them, not a 0. Also as a general rule, do not do a read/modify/write operation to do this (such as EIFR |= 1<<INTF0) as this will clear all pending interrutps, not just the one that you intend. In your case this behavior wouldn't matter since you want to do that anyways. The line should be:

EIFR = (1<<INTF0) | (1<<INTF1);

Ah, good point. Looking in the datasheet, it says: "Alternatively, the flag can be cleared by writing a logical one to it." Unfortunately, trying your fix did not solve the problem. I used your line:

EIFR = (1<<INTF0) | (1<<INTF1); 

to clear the interrupt flags instead of my original way, both before and after external interrupts are enabled, with no luck.

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

You need to set the pullups first to create the "false" trigger(s), then (after perhaps doing something else first to allow them to actually trigger) clear the IF bits and then enable the interrupt source.

think about your code sequence as posted. Assume the line(s) happen to be logic-low when you start your AVR. You clear the triggers, enable the external interrupts, then turn on the pullups. What do you expect? The lines will go high (unless externally being held low) and you get a trigger.

In a "real" program with those lines connected to something, you want to set your AVR I/O pin directions and pullups (and maybe init some subsystems). Then you want to sit and wait for as long as the app allows--I usually use 100ms, and longer in some apps. Why? 'Cause external devices/subsystems may take longer than your AVR to get powered up and present proper levels to the AVR. A good example is character LCDs--chances are init will fail if the AVR immediately tries to talk to them after coming out of a power-on reset.

Lee

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

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

theusch wrote:
You need to set the pullups first to create the "false" trigger(s), then (after perhaps doing something else first to allow them to actually trigger) clear the IF bits and then enable the interrupt source.

think about your code sequence as posted. Assume the line(s) happen to be logic-low when you start your AVR. You clear the triggers, enable the external interrupts, then turn on the pullups. What do you expect? The lines will go high (unless externally being held low) and you get a trigger.

In a "real" program with those lines connected to something, you want to set your AVR I/O pin directions and pullups (and maybe init some subsystems). Then you want to sit and wait for as long as the app allows--I usually use 100ms, and longer in some apps. Why? 'Cause external devices/subsystems may take longer than your AVR to get powered up and present proper levels to the AVR. A good example is character LCDs--chances are init will fail if the AVR immediately tries to talk to them after coming out of a power-on reset.

Lee

Sweet, Lee. You solved it. Just had to wait for the false triggering to occur before clearing the flags. Here is the working source code now:

#include 
#include 
#include 
#include "uart.h"

#define HOME_SENSOR_PORT	PORTD
#define HOME_SENSOR_DDR		DDRD
#define HOME_SENSOR_PIN		PIND
#define HOME_SENSOR_AZ		PD0
#define HOME_SENSOR_ELEV	PD1

#define clear_bit(a,z) (a &= ~_BV(z))
#define set_bit(a,z) (a |= _BV(z))

#define clear_2_bits(a,y,z) (a &= ~( _BV(y) | _BV(z) ))
#define set_2_bits(a,y,z) (a |= _BV(y) | _BV(z))

volatile uint8_t value, value2;

//azimuth motor home sensor external interrupt
ISR(INT0_vect) {
	value++;
}

ISR(INT1_vect) {
	value2++;
}

int main(void) {
	uart_setup1(9600);
	
	value = 0;
	value2 = 0;
	
	//set the data directions for the home sensors
	clear_2_bits(HOME_SENSOR_DDR, HOME_SENSOR_AZ, HOME_SENSOR_ELEV);
	//enable pullup resistors
	set_2_bits(	HOME_SENSOR_PORT, HOME_SENSOR_AZ, HOME_SENSOR_ELEV);
	//delay to allow a false trigger
	for (uint8_t i=0; i<50; i++) {
		_delay_loop_2(0xffff);
	}
	//clear the interrupt flags
	EIFR = (1<<INTF0) | (1<<INTF1);
	
	cli();
	
	//make sure we won't accidentally trigger an interrupt while setting external interrupt options
	clear_2_bits(EIMSK, INT0, INT1);
	
	//setup the external interrupts, triggered on any lotical change, that count motor revolutions / detect home positions
	EICRA = _BV(ISC00) | _BV(ISC10);
	
	//enable the external interrupts
	set_2_bits(EIMSK, INT0, INT1);
	
	sei();
	
	
	while(1) {
		transmit_uint(UART_TERM, value, true);
		transmit_uint(UART_TERM, value2, true);
		uart_newline(UART_TERM);
	}
	return 0;
}

Not sure exactly how long I should delay when waiting for the false trigger, but this little loop (runnning at 16 MHz) seems to do the trick.