Author Topic: How to initialise INPUT_PULLUP before Interrupt runs in Arduino?  (Read 4877 times)

0 Members and 1 Guest are viewing this topic.

Offline skillz21Topic starter

  • Frequent Contributor
  • **
  • Posts: 311
  • Country: au
I have an Arduino sketch that involves something like this. There is a button, and because I'm too lazy to create a voltage divider by adding an extra resistor at the breadboard, I just did "pinMode(buttonPin, INPUT_PULLUP);". Easy peasy. In my project, I have an interrupt that runs off this button, FALLING. So when it is pressed, the pin goes low and the interrupt is triggered. Unfortunately, for some reason, the interrupt gets triggered when the code starts to run, at the beginning. Thinking about this with my noob reasoning, I can only conclude that the interrupt runs even before the part that makes the button pin an INPUT_PULLUP, therefore triggering it falsely.

To test this, in the interrupt function, I added an if statement that goes like this:

  if(millis() > 1)
  {                         
    someBoolean = true;
  }

instead of my original

    someBoolean = true;

And what do you know, that actually solved the issue. Now, I could just leave this as it is, but I don't like leaving a part of the code and commenting that I don't understand why it has to be there. Plus I'm curious to know whether there is a fix to this other than what I did. Also, I could be completely wrong in why this is happening, if so, please do let me know.
 

Online iMo

  • Super Contributor
  • ***
  • Posts: 5570
  • Country: va
Re: How to initialise INPUT_PULLUP before Interrupt runs in Arduino?
« Reply #1 on: March 22, 2019, 12:07:13 pm »
Use "attachInterrupt()" after some small delay.
It also could be you do "attachInterrupt" before the "pinMode".
« Last Edit: March 22, 2019, 12:09:07 pm by imo »
Readers discretion is advised..
 

Offline skillz21Topic starter

  • Frequent Contributor
  • **
  • Posts: 311
  • Country: au
Re: How to initialise INPUT_PULLUP before Interrupt runs in Arduino?
« Reply #2 on: March 22, 2019, 12:18:48 pm »
Use "attachInterrupt()" after some small delay.
It also could be you do "attachInterrupt" before the "pinMode".
I do attatchInterrupt() after the pinMode. Is there no other way to get rid of this issue than just use a delay? whether it be at attachInterrupt() or the actual function?
 

Online iMo

  • Super Contributor
  • ***
  • Posts: 5570
  • Country: va
Re: How to initialise INPUT_PULLUP before Interrupt runs in Arduino?
« Reply #3 on: March 22, 2019, 12:28:37 pm »
The internal pullups are rather week. It could be the longer wires to your button pick a noise.
You have to overcome your laziness and add a 10k pullup and 10nF against gnd :)
Readers discretion is advised..
 

Offline skillz21Topic starter

  • Frequent Contributor
  • **
  • Posts: 311
  • Country: au
Re: How to initialise INPUT_PULLUP before Interrupt runs in Arduino?
« Reply #4 on: March 22, 2019, 12:44:18 pm »
The internal pullups are rather week. It could be the longer wires to your button pick a noise.
You have to overcome your laziness and add a 10k pullup and 10nF against gnd :)
You're probably right. But do you know why adding a delay just gets rid of the problem altogether?
 

Offline rstofer

  • Super Contributor
  • ***
  • Posts: 9963
  • Country: us
Re: How to initialise INPUT_PULLUP before Interrupt runs in Arduino?
« Reply #5 on: March 22, 2019, 03:41:26 pm »
You should post your code...

You could also wrap part of the setup() code in noInterrupts(), interrupts() { or cli(), sei() }

I usually set the pin state before setting the pin mode.  In other words, set the pin high first even though you intend to use it as an input.
 

Offline skillz21Topic starter

  • Frequent Contributor
  • **
  • Posts: 311
  • Country: au
Re: How to initialise INPUT_PULLUP before Interrupt runs in Arduino?
« Reply #6 on: March 22, 2019, 10:58:23 pm »
You should post your code...

You could also wrap part of the setup() code in noInterrupts(), interrupts() { or cli(), sei() }

I usually set the pin state before setting the pin mode.  In other words, set the pin high first even though you intend to use it as an input.
Here's a shortened version of my code.


volatile int buttonPin = 21;         
volatile bool gotPressed= false;

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(buttonPin), Button, FALLING);
}

void loop() {
if(gotPressed)
  {
      //Does stuff
    gotPressed = false;
  }
}

void Button() { 
  if(millis() > 1)
  {
    gotPressed = true;
  }
}


At the moment, this works, and does not trigger the interrupt at the very start. I alse tried surrounding the pinMode() in noInterrupts() and interrupts. The problem didn't go away. How do you set the pin state HIGH before pinMode()?
 

Offline mjkuwp

  • Supporter
  • ****
  • Posts: 260
  • Country: us
  • mechanical engineering defector
    • The Mz Lab
Re: How to initialise INPUT_PULLUP before Interrupt runs in Arduino?
« Reply #7 on: March 23, 2019, 12:26:34 am »
interrupts can be pretty sensitive and sometimes they cause too much trouble when they are used for a feature that doesn't really call for them.  That said  -  I wrote something you could try.  There is a bit of a delay added before the interrupts are enabled.

if I were doing this I would likely do this by polling, not an interrupt and there would be a state machine to help with de-bouncing.

Code: [Select]
volatile int buttonPin = 21;         
volatile bool gotPressed= false;

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);
  digitalWrite(buttonPin, HIGH );
  noInterrupts();
  attachInterrupt(digitalPinToInterrupt(buttonPin), Button, FALLING);
  delay(1);
  interrupts();
}

void loop() {
if(gotPressed)
  {
      //Does stuff
    gotPressed = false;
  }
}

void Button() { 
 
    gotPressed = true;
 
}
 

Offline rstofer

  • Super Contributor
  • ***
  • Posts: 9963
  • Country: us
Re: How to initialise INPUT_PULLUP before Interrupt runs in Arduino?
« Reply #8 on: March 23, 2019, 12:49:59 am »
I would swap the order of the digitalWrite and PinMode statements.

Like this:

  digitalWrite(buttonPin, HIGH );
  pinMode(buttonPin, INPUT_PULLUP);

 

Offline skillz21Topic starter

  • Frequent Contributor
  • **
  • Posts: 311
  • Country: au
Re: How to initialise INPUT_PULLUP before Interrupt runs in Arduino?
« Reply #9 on: March 23, 2019, 01:32:04 am »
I would swap the order of the digitalWrite and PinMode statements.

Like this:

  digitalWrite(buttonPin, HIGH );
  pinMode(buttonPin, INPUT_PULLUP);
That didn't do anything.
 

Offline skillz21Topic starter

  • Frequent Contributor
  • **
  • Posts: 311
  • Country: au
Re: How to initialise INPUT_PULLUP before Interrupt runs in Arduino?
« Reply #10 on: March 23, 2019, 01:34:52 am »
interrupts can be pretty sensitive and sometimes they cause too much trouble when they are used for a feature that doesn't really call for them.  That said  -  I wrote something you could try.  There is a bit of a delay added before the interrupts are enabled.

if I were doing this I would likely do this by polling, not an interrupt and there would be a state machine to help with de-bouncing.

Code: [Select]
volatile int buttonPin = 21;         
volatile bool gotPressed= false;

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);
  digitalWrite(buttonPin, HIGH );
  noInterrupts();
  attachInterrupt(digitalPinToInterrupt(buttonPin), Button, FALLING);
  delay(1);
  interrupts();
}

void loop() {
if(gotPressed)
  {
      //Does stuff
    gotPressed = false;
  }
}

void Button() { 
 
    gotPressed = true;
 
}

I tried it, but it didn't do anything. How would you do it by polling? I the code is running a delay for example, and I pressed the button, it wouldn't register the click right?
 

Offline mjkuwp

  • Supporter
  • ****
  • Posts: 260
  • Country: us
  • mechanical engineering defector
    • The Mz Lab
Re: How to initialise INPUT_PULLUP before Interrupt runs in Arduino?
« Reply #11 on: March 23, 2019, 01:45:58 am »
"didn't do anything"

well, to be fair the code doesn't do anything : )  it is empty code. 

you are correct in that a button press that occurs in hardware while the code is in delay will not get noticed in a polling situation.  A couple of ways to address that.

1. don't use delay
2. write a state machine that looks for the button to be pressed, starts a timer and checks that it is still pressed X number of milliseconds later while never having been un-pressed during that time.


or, better yet look at this:

https://www.arduino.cc/en/tutorial/debounce

 

Offline Ian.M

  • Super Contributor
  • ***
  • Posts: 13216
Re: How to initialise INPUT_PULLUP before Interrupt runs in Arduino?
« Reply #12 on: March 23, 2019, 03:27:22 am »
The weak internal pullup needs time to charge the stray capacitance of the pin & button wiring to reach a valid logic '1' level.  Therefore a delay between pinMode(buttonPin, INPUT_PULLUP); and attachInterrupt(digitalPinToInterrupt(buttonPin), Button, FALLING); is essential if you want to avoid a spurious interrupt. 

1 ms is excessive - somewhere between 10 and 100 us should be sufficient.  If you've got a  scope, write a test program that outputs '0' on the pin then switches to INPUT_PULLUP mode, in a loop with a 1ms delay.  With the switch wiring connected, probe the pin and measure  the risetime (to 90%) and triple that time to get a reasonable delay to use in your actual code.

Also, its a good idea to clear any pending pin change interrupts after you've set up the interrupt on the pin, before enabling interrupts.  Reading the pins followed by PCIFR=7; should do that (assuming an  ATmega328P based Arduino, see datasheet section 17.2.5. Pin Change Interrupt Flag Register).

There's a tutorial on AVR pin change interrupts at http://microchipdeveloper.com/8avr:pin-change-interrupts
The Arduino libraries hide some of the complexity but the hardware and registers are the same.

N.B.  Pin interrupts are a *HORRIBLE* way of handling user buttons, and you risk an interrupt storm if the buttons don't have 100% effective hardware debouncing.    Jack Ganssle's article on debouncing is worth a read :  http://www.ganssle.com/debouncing.htm
« Last Edit: March 23, 2019, 05:52:56 am by Ian.M »
 

Offline skillz21Topic starter

  • Frequent Contributor
  • **
  • Posts: 311
  • Country: au
Re: How to initialise INPUT_PULLUP before Interrupt runs in Arduino?
« Reply #13 on: March 23, 2019, 05:01:06 am »
The weak internal pullup needs time to charge the stray capacitance of the pin & button wiring to reach a valid logic '1' level.  Therefore a delay between pinMode(buttonPin, INPUT_PULLUP); and attachInterrupt(digitalPinToInterrupt(buttonPin), Button, FALLING); is essential if you want to avoid a spurious interrupt. 

1 ms is excessive - somewhere between 10 and 100 us should be sufficient.  If you've got a  scope, write a test program that outputs '0' on the pin then switches to INPUT_PULLUP mode, in a loop with a 1ms delay.  With the switch wiring connected, probe the pin and measure  the risetime (to 90%) and triple that time to get a reasonable delay to use in your actual code.

Also, its a good idea to clear any pending pin change interrupts after you've set up the interrupt on the pin, before enabling interrupts.  Reading the pins followed by PCIFR=7; should do that (assuming an  ATmega328P based Arduino, see datasheet section 17.2.5. Pin Change Interrupt Flag Register).

There's a tutorial on AVR pin change interrupts at http://microchipdeveloper.com/8avr:pin-change-interrupts
The Arduino libraries hide some of the complexity but the hardware and registers are the same.

N.B.  Interrupts are a *HORRIBLE* way of handling user buttons, and you risk an interrupt storm if the buttons don't have 100% effective hardware debouncing.    Jack Ganssle's article on debouncing is worth a read :  http://www.ganssle.com/debouncing.htm

Instead of using an interrupt, is there any other way for the Arduino to recognise a button press and change a variable? It wouldn't actually do this if there is another piece of code running, right?
 

Offline Ian.M

  • Super Contributor
  • ***
  • Posts: 13216
Re: How to initialise INPUT_PULLUP before Interrupt runs in Arduino?
« Reply #14 on: March 23, 2019, 05:52:06 am »
Instead of making the button trigger an interrupt, poll the button from a timer ISR (which avoids any risk of an interrupt storm), and implement one of the software debouncing algorithms from part 2 of J. Ganssle's article.

N.B. The Timer 0 overflow interrupt is used for various Arduino sytem functions involving timekeeing, and (unless thing have changed since I last looked at the Arduino libraries), its very difficult to patch in your own code in that ISR.   If you want to run your button code on a Timer 1 interrupt (approx. every milisecond), you'll need to use the timer's Compare A interrupt.   Set OCR0A to 0 and enable OCIE0A to interrupt immediately after the system overflow routine.
 

Offline Psi

  • Super Contributor
  • ***
  • Posts: 10385
  • Country: nz
Re: How to initialise INPUT_PULLUP before Interrupt runs in Arduino?
« Reply #15 on: March 23, 2019, 10:34:14 am »
You could also add code to the interrupt handler to block any code running until millis() returns  >100  (100ms after starting).
But this would be a dirty dirty hack.
Best to do it the proper way.

If this project was not being done in Arduino i doubt you would have this issue.
The micro starts up with all interrupts disabled and you have to turn each of them on and also enable the global interrupt system as well. Which you normally only do after everything has been setup. I suspect Arduino is turning this on behind the scenes before any of your code runs and this is causing your problem.
It's either that, or as others have said you need a delay after setting the pull up and before you attach the interrupt to account for slow rise time from pullup.
It's ok to have hard delay functions as part of setting up hardware in setup().
It's only bad to have them inside loop()

If you add the delay and it doesn't help try this, it will block any interrupts from happening until after both lines run.
cli(); // global interrupt disable
<your code to enable pullup>
<your code to attach interrupt>
sei(); //global interrupt enable
« Last Edit: March 23, 2019, 10:40:35 am by Psi »
Greek letter 'Psi' (not Pounds per Square Inch)
 

Offline KL27x

  • Super Contributor
  • ***
  • Posts: 4108
  • Country: us
Re: How to initialise INPUT_PULLUP before Interrupt runs in Arduino?
« Reply #16 on: March 23, 2019, 07:12:18 pm »
If it works on a falling edge, it would probably not matter.* If it triggers on an input low, then of course you need some delay. The chip can execute a number of instructions during the rise time of the input pin. There's a discrete amount of time between turning on the pullup before the voltage rises above the logic set point, depending on the capacitance of the pin circuitry and the value of the internal pullup.

*if the interrupt were activated properly, with appropriate flags cleared. That might be hidden from you to some extent.

If speed really matters, what you can do to speed things up is make the input pin output high for a tick, then change it to input with internal pullup activated. Yes, that is ok even connected directly to a grounded button, IMO.
« Last Edit: March 23, 2019, 07:26:07 pm by KL27x »
 

Offline rstofer

  • Super Contributor
  • ***
  • Posts: 9963
  • Country: us
Re: How to initialise INPUT_PULLUP before Interrupt runs in Arduino?
« Reply #17 on: March 24, 2019, 05:36:14 pm »
Maybe page 59 and 60 of the User Manual will provide a hint.  Among other things, there is no pull-up until the input port is written high. I suspect this will be the user's responsibility.  See the schematic where PUD must be 0, the data direction must be 0 and the port value must be 1.  Top 3 input NAND gate with 2 inverted inputs.

http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf

The PUD bit in MCUCR must be zero.  If not, it disables ALL pull-ups. Zero is the default value according to 11.2.2 so everything should be fine unless something messed with it.  Maybe there is a way to read the register and verify that the library code hasn't changed it.

On the Arduino Mega 2560, there is (or was) a problem using interrupt on change with pins 20 and 21.  I scanned it, I didn't read it.

https://forum.arduino.cc/index.php?topic=158434.0

It might be worth changing to a different pin, just for giggles.



 

Offline janoc

  • Super Contributor
  • ***
  • Posts: 3925
  • Country: de
Re: How to initialise INPUT_PULLUP before Interrupt runs in Arduino?
« Reply #18 on: March 24, 2019, 06:27:41 pm »
If the entire point of this exercise is button debouncing, then just use one of the many Arduino libraries for that - e.g. Bounce2:
https://github.com/thomasfredericks/Bounce2/

But I do recommend reading the Jack Ganssle's series on this topic in order to understand the subject (it is pretty essential when it comes to handling user input with digital logic) and to possibly implement a better debouncer than what is in the library above.

 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf