Can't get external interrupt button working

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

BreadboardHi,

 

I'm using INT0 connected to a normally open button but to my amazement it's doing exactly the opposite of what I expected: when I turn it on I get loads of interrupts but when I depress the button the interrupts stop.

 

Here's some of the initialization code:

 

  DDRD = 0b00001000;    // PD0..PD2 inputs, PD03 output, PD04 - PD07 = input
  PORTD = 0x01;         // PD0 pull up enable
  EICRA = 0x02;         // falling edge of INT0
  EIMSK = 0x01;         // INT0 enable

  set_bit( PORTD, PIND0 );
	

 

and the interrupt service routine:

 

ISR( INT0_vect )
{
  print("Button pressed\n");
}

 

I'm using the Teensy 2.0 (ATmega32U4).

 

I've noticed than when I disconnect the wire to INT0 completely no interrupt occurs. However, when I connect a wire to INT0 but nothing else, it generates interrupts. Is it picking up spurious noise?

 

What am I doing wrong?

 

This topic has a solution.

Building my dreams!

Last Edited: Mon. Aug 27, 2018 - 07:00 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I don't do C so I ought to stay out of this, but:

 

As a general rule, it is easier to read, and debounce, a user push button switch by NOT using a switch triggered interrupt.

 

Often, one has a HeartBeat timer interrupt running in the background, and every X mSec you read the status of your PB Switches, (X = 1, or 2, or 5, or 10, or whatever).

Your debounce routine decides when a valid PB Switch activation has occurred, and sets a flag for the Main Loop. 

There are many Threads on Debouncing PB Switches.

 

(Pushing and releasing a mechanical PB Switch generates many make/break pulses, not just one.)

 

If you are using a PB Switch triggered interrupt, (ill advised), then verify carefully is your interrupt set up for leading edge, trailing edge, both, or logic level to trigger the interrupt, and follow on question, is the said interrupt cleared by running the ISR, or must you clear it in software, and finally, is the interrupt auto-retriggering, (common with low level interrupt triggering!)?

 

JC

 

Last Edited: Sun. Jun 5, 2016 - 05:45 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You haven't told us what the CPU is but it looks like either an Arduino Pro Mini (AVR Mega328P) or a Pro Micro (Mega32u4).  

For the ProMINI the INT0 is on pin D2 (D0 and D1 are RxD and TxD).  On the Pro Micro INT0,1,2,3 share pins with RxD, TxD,

SDA, and SCL (used by the I2C, aka TWI and Wire.h). 

  The Pro Micro has a 'secret' external switch interrupt INT4 (which is called interrupt 6 in Arduino literature) on pin D7.

 

The internal interrupt pull-up is on pin D0 and the INT0 (D2) is floating.  This is the likely cause of all the INT0 invocations.

Pressing a switch on D0 may cause a BREAK condition on the USART line which is used by the ARduino to exchange data

with a PC or for bootloading.  This is most likely putting the CPU into a endless loop until the button is released.

Last Edited: Sun. Jun 5, 2016 - 08:02 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I always have the pushbutton generate an interrupt.  No problem there.  The interrupt handler disables the interrupt.  The "heartbeat" counts down a timer to re-enable the interrupt.  You must use a minimum count of 2 for the countdown. Of course the countdown timer must be long enough for the contact bounce to stop. 

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

DocJC wrote:

I don't do C so I ought to stay out of this, but:

 

As a general rule, it is easier to read, and debounce, a user push button switch by NOT using a switch triggered interrupt.

 

Often, one has a HeartBeat timer interrupt running in the background, and every X mSec you read the status of your PB Switches, (X = 1, or 2, or 5, or 10, or whatever).

Your debounce routine decides when a valid PB Switch activation has occurred, and sets a flag for the Main Loop. 

There are many Threads on Debouncing PB Switches.

 

(Pushing and releasing a mechanical PB Switch generates many make/break pulses, not just one.)

 

If you are using a PB Switch triggered interrupt, (ill advised), then verify carefully is your interrupt set up for leading edge, trailing edge, both, or logic level to trigger the interrupt, and follow on question, is the said interrupt cleared by running the ISR, or must you clear it in software, and finally, is the interrupt auto-retriggering, (common with low level interrupt triggering!)?

 

JC

 

 

I've read this before in several posts but I don't agree. The main reason for using interrupts is power usage. If you're polling you're wasting too much power needlessly. You'd need to poll at least 10 times a second for it to seem instantaneous. Ideally, you would want your MCU to be in sleep mode most of the time, only waking up when a user presses a button or a hardware event occurs.

 

My Teensy Alarm Clock uses polling, but it uses 5W continuously, which is way more than most mains powered alarm clocks (which use 1W typical). 

 

Building my dreams!

Last Edited: Sun. Jun 5, 2016 - 10:03 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Simonetta wrote:

You haven't told us what the CPU is but it looks like either an Arduino Pro Mini (AVR Mega328P) or a Pro Micro (Mega32u4).  

For the ProMINI the INT0 is on pin D2 (D0 and D1 are RxD and TxD).  On the Pro Micro INT0,1,2,3 share pins with RxD, TxD,

SDA, and SCL (used by the I2C, aka TWI and Wire.h). 

  The Pro Micro has a 'secret' external switch interrupt INT4 (which is called interrupt 6 in Arduino literature) on pin D7.

 

The internal interrupt pull-up is on pin D0 and the INT0 (D2) is floating.  This is the likely cause of all the INT0 invocations.

Pressing a switch on D0 may cause a BREAK condition on the USART line which is used by the ARduino to exchange data

with a PC or for bootloading.  This is most likely putting the CPU into a endless loop until the button is released.

 

I'm using Teensy 2.0 and you can clearly see here that the INT0 is on PD0, if I'm not mistaken.

Building my dreams!

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

The interrupt is used to wake the device. You still need to debounce it. Otherwise you might find due to ESD that you get false keypresses.

Have you considered that your pushbutton might be normally closed? Use a multimeter to check.

Last Edited: Sun. Jun 5, 2016 - 10:46 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Kartman wrote:

The interrupt is used to wake the device. You still need to debounce it.

Exactly.  That's why you need a countdown timer to expire before enabling the interrupt again.

 

The handiest chip for this is the megaxx9, as used on the Butterfly.  When someone hit the joystick, we would disable the joystick interrupt, and enable the LCD controller.  The LCD controller has a clock that ticks at something like 20 Hertz.  That's perfect for counting down the contact bouncing.  Later, after a couple of minutes of no joystick wiggling, we would disable the LCD controller.  Saves 40 uA, you know.

 

Last Edited: Sun. Jun 5, 2016 - 11:00 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

bigpilot wrote:

 

I've read this before in several posts but I don't agree. The main reason for using interrupts is power usage. If you're polling you're wasting too much power needlessly. You'd need to poll at least 10 times a second for it to seem instantaneous. Ideally, you would want your MCU to be in sleep mode most of the time, only waking up when a user presses a button or a hardware event occurs.

 

My Teensy Alarm Clock uses polling, but it uses 5W continuously, which is way more than most mains powered alarm clocks (which use 1W typical). 

 

 

If the schematic posted on the other site is accurate, I'd say you aren't measuring the power correctly.  The power consumed by that circuit is several hundred milliwatts at most.

Letting the smoke out since 1978

 

 

 

 

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

bigpilot wrote:

... when I turn it on I get loads of interrupts but when I depress the button the interrupts stop.

 

Sounds like noise is triggering the interrupt.  When the switch is pressed, the pin is grounded and thus not affected by any noise that is present.

 

Try adding a 1K pullup to the input pin.

 

 

 

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

Kartman wrote:
The interrupt is used to wake the device. You still need to debounce it. Otherwise you might find due to ESD that you get false keypresses. Have you considered that your pushbutton might be normally closed? Use a multimeter to check.

 

I have checked with a MM. When the button is not pressed the resistance is infinite and about 0.7 ohms when pressed.

Building my dreams!

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

Chuck99 wrote:

bigpilot wrote:

... when I turn it on I get loads of interrupts but when I depress the button the interrupts stop.

 

Sounds like noise is triggering the interrupt.  When the switch is pressed, the pin is grounded and thus not affected by any noise that is present.

 

Try adding a 1K pullup to the input pin.

 

 

But I have the internal pull-ups turned on for the pin. This should work, shouldn't it?

 

I've been measuring the voltage at the pin and it reads zero (0) volts, with the pull up enabled. I'm at a loss why this is.

Building my dreams!

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

check the obvious - verify what pin you're connected to. My job would be easy if what I expected to happen, actually did. Work through the problem from one end to the other proving everything along the way. Eventually you'll resolve the issue. Or you could guess and go around in circles.

 

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

bigpilot wrote:

But I have the internal pull-ups turned on for the pin. This should work, shouldn't it?

 

I've been measuring the voltage at the pin and it reads zero (0) volts, with the pull up enabled. I'm at a loss why this is.

 

Write a separate test program that just sets PD0 as input and enables the pull-up on PD0.  Then an infinite while() loop.  Post the source code you use.

 

Now measure the voltage on PD0.  If the voltage is not high then either you are measuring the wrong pin or something on the Teensy is interfering.

 

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

I've now based the code on an example I found on the internet but I get the same results.

 

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>   		  // _delay_ms()
#include "print.h"              // print debug output through usb port
#include "macros.h"             // set_bit(), clear_bit(), LED_ON, LED_OFF

int main(void)
{
    usb_init();			      // init usb port to send debugging information
    _delay_ms(2000);
  
    DDRD &= ~(1 << DDD0);     // Clear the PD0 pin

    PORTD |= (1 << PORTD0);    // turn On the Pull-up

    EICRA |= (1 << ISC00);    // set INT0 to trigger on ANY logic change
    EIMSK |= (1 << INT0);     // Turns on INT0

    sei();                    // turn on interrupts

    while(1)
    {
        /*main program loop here */
    }
}



ISR (INT0_vect)
{
    print("Button pressed\n");
}

 

Building my dreams!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
   DDRD &= ~(1 << DDD0);     // Clear the PD0 pin

Just for the record that comment has very little to do with what the code there achieves. When I read code like this on the internet my internal alarm bell starts to ring!

 

BTW personally I would not do anything quite so exciting as calling a print() rouitne in an ISR(). It will seriously mess up the timing of what you are trying to explore. During the time the first print() is being executed it could be that 50 more INT0_vect interrupts might have occrred and (apart from the last one that may still be latched in the INTF0 flag) will have been lost.

 

If I wanted to explore the temporal sequence of INT0 interrupts resulting from a button activation I think I'd start a timer ticking and at each interrupt simply record the current timer value into an array of values (say). Then at leisure I would print the stored array values (in main) and then reset things so another "shot" can be recorded.

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

I agree.

 

clear_bit(DDRD, DDD0);

 

would have been nicer.

 

I'm using the print() for debugging purposes, normally I would remove it after I'd get the code working.

 

Building my dreams!

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

bigpilot wrote:
...would have been nicer.

 

Cliff wasn't commenting on the style or wording, but rather the connotation of the comment.  Setting the DDR bit to 0 doesn't necessarily "clear the pin" (whatever "clear" means here).

 

bigpilot wrote:
... but I get the same results.

 

Please refresh us as to what the "same results" are.

Does print() work?  If you print Hello, World at the head of the program, does that arrive at the destination? (that will also tell us, e.g., that the AVR isn't stuck in reset or brownout or similar)

 

Are you building for the correct AVR?

 

Have you told us the voltage level right at the PD0 pin when the button is not pressed?  voltage level when it is pressed?

 

Indeed, except for the gratuitous use of the |= during initialization, it looks like a decent test program.  usb_init() is hidden from us but as PD0 is set up later that shouldn't matter much.  Unless that routine fusses with EICRA or EIMSK or similar registers...

 

 

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

If you are using interrupts, you should disable it in the ISR.  Otherwise you will get a lot of interrupts because of contact bounce.

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

steve17 wrote:

If you are using interrupts, you should disable it in the ISR.  Otherwise you will get a lot of interrupts because of contact bounce.

 

I know, but right now all I get are interrupts, even when no button is pushed.

Building my dreams!

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

How many?  As you haven't cleared the flag, you may well get one at startup.

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

The stream is continious, I haven't counted them.

 

As I understand it from the documentation, the clearing of the flag is ussually done by the ISR code.

Building my dreams!

Last Edited: Tue. Jun 7, 2016 - 01:58 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

-- show us usb_init()

-- get rid of the |= in EICRA and EIMSK setup

 

From your description, I can only think that the external interrupt is configured for low-level.

 

[edit]  Might as well show us print() as well.

 

And/or, eliminate the USB for now, and toggle a pin in the ISR and monitor that.

 

 

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.

Last Edited: Tue. Jun 7, 2016 - 02:12 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I didn't write print() but here's the source code:

 

void print_P(const char *s)
{
	char c;

	while (1) {
		c = pgm_read_byte(s++);
		if (!c) break;
		if (c == '\n') usb_debug_putchar('\r');
		usb_debug_putchar(c);
	}
} 

 

 

the rest of the source code is attached to this message.

 

I'll try monitoring the pin as you suggested. Will let you know.

 

 

Attachment(s): 

Building my dreams!

Last Edited: Thu. Jun 9, 2016 - 03:08 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

1)  No, that is not the source for print()...

2)  At a glance, the USB stuff looks "clean" with respect to your problem.  But one cannot tell without seeing all the include files.

3)  Did you get rid of the |= as I asked?

4)  So we don't even know if the interrupt pin has a clean state?

5)  Do we know that you are building for the correct processor?  (I'd guess "yes" if the USB is working.)

6)  Can you make a print function to send out the values of EICRA and EIMSK after converting to a string?

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

BTW does this thing have a bootloader? Could it be "messing with stuff" before control reaches your code?

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

clawson wrote:
Could it be "messing with stuff" before control reaches your code?

Thus why I want to get rid of the |=

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.

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

Finally got it working!

 

I needed an external pull-up resistor between de INT0 pin (PD0 on Teensy) and the VCC.

 

Relevant source code below:

 

DDRD = 0<<PD0;    // PD0 input
PORTD = 0<<PD0;   // PD0 internal pull up disable

EIMSK = 1<<INT0;                    // Enable INT0
EICRA = 0<<ISC01 | 0<<ISC00;    // Trigger INT0 on low level

ISR( INT0_vect )
{
  print("INT0 interrupt\n");
  _delay_ms(100);
}

With the internal pull-up, the wires would simply pick up too much noise and trigger the interrupt pin whenever I moved my hand near the button or the wires.

 

Thank you for your consideration. smiley

Building my dreams!

Last Edited: Mon. Aug 27, 2018 - 07:29 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I needed an external pull-up resistor between de INT0 pin (PD0 on Teensy) and the VCC.

I went back in the thread, and that was mentioned in #10.

 

With the internal pull-up, the wires would simply pick up too much noise and trigger the interrupt pin whenever I moved my hand near the button or the wires.

I find that VERY hard to believe, even with a breadboard lashup. If indeed the internal pullup is set (remember that you only posted "part of my initialization code") then if you confirm the symptoms there must be some other hard leakage, but that would not trip with "hand near the button" or similar.

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.