Author Topic: Pull up vs pull down current consumption?  (Read 5736 times)

doublec4

  • Jr. Member
  • **
  • Posts: 53
Pull up vs pull down current consumption?
« on: February 19, 2018, 08:19:43 PM »
Hi all,

I am powering my project from a 2032 coin cell and I would like to ensure long battery life when dormant. It is a simple two button remote with an LED for now.

I've read that the internal pull up resistors consume small amounts of current even when no button is being pressed. Would it be better to go with external pull down resistors and pull the signal high when the button is pressed? Will this result in no current draw when dormant?

Thanks!

TomWS

  • Hero Member
  • *****
  • Posts: 1930
Re: Pull up vs pull down current consumption?
« Reply #1 on: February 19, 2018, 08:35:29 PM »
...
I've read that the internal pull up resistors consume small amounts of current even when no button is being pressed.
...
Where did you read this???  I don't believe that it is correct.  The only case where it might be true is where the input source is leaking and, with an external resistor you could have a higher resistance than the internal pullup.  HOWEVER, that is external to the chip.  There should be an immeasurable amount of current with an internal pullup and no source leakage.

Tom

doublec4

  • Jr. Member
  • **
  • Posts: 53
Re: Pull up vs pull down current consumption?
« Reply #2 on: February 19, 2018, 08:46:58 PM »
Hi Tom,

I came across this thread on the Arduino forum:

http://forum.arduino.cc/index.php?topic=363853.0

That user reported current draw when switch wasn't being used, but the pull up resistor is activated in the code. I suppose this isn't correct?

TomWS

  • Hero Member
  • *****
  • Posts: 1930
Re: Pull up vs pull down current consumption?
« Reply #3 on: February 19, 2018, 10:43:11 PM »
Hi Tom,

I came across this thread on the Arduino forum:

http://forum.arduino.cc/index.php?topic=363853.0

That user reported current draw when switch wasn't being used, but the pull up resistor is activated in the code. I suppose this isn't correct?
I would not trust that one data point.  The number (0.3mA) is way out of line with the currents we're talking about.  The key thing is to make sure that you do not have any floating inputs and, with switches that you don't need to monitor, make sure the pullup, or pulldown, matches the normal state of the switch.  One way to absolutely ensure that is to have another GPIO 'drive' the opposite side of the switch - when in lowpower state, make sure both sides of the switch are at the same voltage level (pulled up or down as required).

Tom

doublec4

  • Jr. Member
  • **
  • Posts: 53
Re: Pull up vs pull down current consumption?
« Reply #4 on: February 20, 2018, 12:26:52 AM »
Thanks Tom,

I'm still a bit of a newb so let me try and unpack what you're saying. Admittedly, I am little confused  :-\

So when you say "no floating inputs," do you mean that no pins are left without some sort of reference? Be it ground or 3.3V ? Is it good practice then to ground all of my unused pins? Would I then still set all of these unused pins as INPUT in my code?

For my remote, I do not want to constantly check the input pins for a button press... ideally I would like the Moteino to enter some kind of low power state and then use an interrupt of some sort to wake it when one of the two buttons are pressed. The normal state of my buttons are open. I'm not sure I understand what you mean by the pull up or pull down matching this normal state of my button?

In the past I have connected the other side of this button to GND, set the pin mode to INPUT_PULLUP and then was able to detect a button being pressed when the pin goes low... I have also done the opposite where I've connected the other side of the button to VCC and have added an external pull down to GND on the Arduino side of the switch, set the pin mode to INPUT and was able to detect the pin going high upon the button being pressed.

Can we maybe run through a very simple example, please? For instance, if I have a momentary push button (NO) connected to digital pin 3, can you kindly guide me through what you are suggesting? I would really appreciate it, thank you.

ChemE

  • Sr. Member
  • ****
  • Posts: 419
  • Country: us
Re: Pull up vs pull down current consumption?
« Reply #5 on: February 20, 2018, 08:26:42 AM »
For my remote, I do not want to constantly check the input pins for a button press... ideally I would like the Moteino to enter some kind of low power state and then use an interrupt of some sort to wake it when one of the two buttons are pressed. The normal state of my buttons are open. I'm not sure I understand what you mean by the pull up or pull down matching this normal state of my button?

What you are describing is a pin change interrupt which is no problem at all.  I fooled around with this a while back and while I'm hardly the first one to make it work, I do have some atomic code (no outside dependencies; I dislike them) which lets you set a pin change interrupt on any pin.  The way these work is there is one interrupt vector per port so whenever any pin on port B changes state, one interrupt vector runs.  Anytime a pin changes state on port C, its interrupt vector is run etc.

Code: [Select]
#if defined(__AVR_ATmega328P__) // Macros to define the ports and bitmasks for the pins on the 328p succinctly
  #if F_CPU==8000000    // No external oscillator on PB6 and PB7 - make them digital pins 20 and 21 respectively
    #define     InpRegFromPin(pin)      pin<8 ? 0x09 : pin<14 ? 0x03 : pin<20 ? 0x06 : 0x03 // Input registers for the ports
    #define     DirRegFromPin(pin)      pin<8 ? 0x0A : pin<14 ? 0x04 : pin<20 ? 0x07 : 0x04 // Data direction registers for the ports
    #define     DataRegFromPin(pin)     pin<8 ? 0x0B : pin<14 ? 0x05 : pin<20 ? 0x08 : 0x05// Output registers for the ports
  #elif F_CPU==16000000  // External oscillator on PB6 and PB7 - leave them alone
    #define     InpRegFromPin(pin)      pin<8 ? 0x09 : pin<14 ? 0x03 : 0x06 // Input registers for the ports
    #define     DirRegFromPin(pin)      pin<8 ? 0x0A : pin<14 ? 0x04 : 0x07 // Data direction registers for the ports
    #define     DataRegFromPin(pin)     pin<8 ? 0x0B : pin<14 ? 0x05 : 0x08 // Output registers for the ports
  #endif
  #define     BitmaskFromPin(pin)     pin<8 ? 1<<pin : pin<14 ? 1<<(pin-8) : 1<<(pin-14)
#endif

// Bit-banging macros - each adds 2 bytes of sketch size and takes 1 clock cycle to execute
#define    MakeOutput(pin)    _SFR_IO8(DirRegFromPin(pin))  |=  BitmaskFromPin(pin)           // Much faster and smaller version of pinMode(Pin, OUTPUT)
#define    MakeInput(pin)     _SFR_IO8(DirRegFromPin(pin))  &=  ~(BitmaskFromPin(pin))        // Much faster and smaller version of pinMode(Pin, INPUT)
#define    PullHigh(pin)      _SFR_IO8(DataRegFromPin(pin)) |=  BitmaskFromPin(pin)           // Much faster and smaller version of digitalWrite(Pin, HIGH)
#define    PullLow(pin)       _SFR_IO8(DataRegFromPin(pin)) &=  ~(BitmaskFromPin(pin))        // Much faster and smaller version of digitalWrite(Pin, LOW)
#define    ReadPin(pin)       _SFR_IO8(InpRegFromPin(pin))  & (BitmaskFromPin(pin)) ? 1 : 0   // One line if else statement using the format [test ? true return : false return]
#define    DigitalWrite(pin, state)   state ? PullHigh(pin) : PullLow(pin)
#define    PinMode(pin, mode)         mode ? MakeOutput(pin) : MakeInput(pin)

// ========================================= Pin Change Interrupt Macros =========================================
#define    PCINTFromPin(pin)  pin<8 ? 0x02 : pin<15 ? 0x01 : 0x00
#define    PCMaskFromPin(pin) pin<8 ? 1<<pin : pin<14 1<<(pin-8) : 1<<(pin-14)
#define    PCI_Reg(pin)       pin<8 ? 0x6D : pin<14 ? 0x6B : 0x6C
#define    PCI_FR_Msk(pin)    pin<8 ? 1<<2 : pin<14 ? 1 : 1<<1
#define    PCI_Setup(pin)     MakeInput(pin); PullHigh(pin); (*(volatile uint8_t *)((PCI_Reg(pin)) )) = BitmaskFromPin(pin); PCIFR |= PCI_FR_Msk(pin); PCICR |= PCI_FR_Msk(pin)

// ============================== Moteino Definitions ==============================
#define         REG_OPMODE                              0x01
#define         SLEEP_RADIO                             writeReg(REG_OPMODE, 0x00)
#define         SS_PIN                                  PB2    // Slave select pin
#define         SCK_PIN                                 PB5    // SPI clock pin
#define         MOSI_PIN                                PB3    // Master out slave in (MOSI) pin

// ============================== SPI Definitions ==============================
#define         SELECT                                  noInterrupts(); PORTB &= ~(1<<SS_PIN)
#define         UNSELECT                                SS_WRITE_HIGH; interrupts()
#define         SS_WRITE_HIGH                           PORTB |= 1<<SS_PIN
#define         WAIT_WHILE_SPI_BUSY                     asm volatile("nop"); while (!(SPSR & 1<<SPIF))

#define SLEEP_FOREVER cli();  \
                      SMCR = bit(SM1) | bit(SE); \
                      MCUCR = bit (BODS) | bit (BODSE); \
                      MCUCR = bit (BODS); \
                      sei(); \
                      __asm__ __volatile__ ( "sleep" "\n\t" :: );

void writeReg(uint8_t addr, uint8_t value) {  // Level 1 code - interact witht the radio's registers
  SELECT;
  SPDR = ( addr | 0x80 );
  WAIT_WHILE_SPI_BUSY;
  SPDR = ( value );
  WAIT_WHILE_SPI_BUSY;
  UNSELECT;
}

#define LED 14 /* LED power pin - ADC0 */
#define INT 5  /* Interrupt pin - Pin 4 on the Moteino */

int main(void) {          /* Setup code here */
  SLEEP_RADIO;            /* 30 bytes; takes 6uS to execute */
  PinMode(LED, OUTPUT);   /* Set up the LED */
  PCI_Setup(INT);         /* Set up a pin change interrupt */
  sei();                  /* Enable interrupts to the pin changes will work */
 
  while (1) {             /* Loop code here */
    SLEEP_FOREVER;        /* Sleep forever - only awake in the ISR */
  }                       /* End of Loop */
}                         /* End of main */

ISR (PCINT2_vect, ISR_NAKED) { /* handle pin change interrupt for D0 to D7 here */
  DigitalWrite(LED, HIGH); _delay_ms(10); DigitalWrite(LED, LOW); /* Strobe the LED */
}

TomWS

  • Hero Member
  • *****
  • Posts: 1930
Re: Pull up vs pull down current consumption?
« Reply #6 on: February 20, 2018, 08:39:38 AM »
Thanks Tom,

I'm still a bit of a newb so let me try and unpack what you're saying. Admittedly, I am little confused  :-\

So when you say "no floating inputs," do you mean that no pins are left without some sort of reference? Be it ground or 3.3V ? Is it good practice then to ground all of my unused pins? Would I then still set all of these unused pins as INPUT in my code?
No, "no floating inputs" means that the pin is either:
  • tied to an external circuit that references either ground or 3.3V,
  • Is driven by the MCU (ie, an output),
  • Is internally pulled up or pulled down.

Quote
For my remote, I do not want to constantly check the input pins for a button press... ideally I would like the Moteino to enter some kind of low power state and then use an interrupt of some sort to wake it when one of the two buttons are pressed. The normal state of my buttons are open. I'm not sure I understand what you mean by the pull up or pull down matching this normal state of my button?

In the past I have connected the other side of this button to GND, set the pin mode to INPUT_PULLUP and then was able to detect a button being pressed when the pin goes low... I have also done the opposite where I've connected the other side of the button to VCC and have added an external pull down to GND on the Arduino side of the switch, set the pin mode to INPUT and was able to detect the pin going high upon the button being pressed.

Can we maybe run through a very simple example, please? For instance, if I have a momentary push button (NO) connected to digital pin 3, can you kindly guide me through what you are suggesting? I would really appreciate it, thank you.
ChemE is correct that what you're talking about is called a 'pinchange interrupt'.  However, in your case, I would suggest that you use an existing and supported library, PinChangeInt, which is well documented, has plenty of examples, and is portable across virtually all Arduino platforms.  DAGS "Arduino PinChangeInt" and you'll find plenty of useful information.

Tom

perky

  • Hero Member
  • *****
  • Posts: 873
  • Country: gb
Re: Pull up vs pull down current consumption?
« Reply #7 on: February 20, 2018, 08:51:43 AM »
The important thing is to make sure no current is flowing through pull-up or pull-down resistors when asleep, and any digitial input buffers are either very close to or at 0V, or very close to or at VCC.

The post you referred to was about switches rather than momentary pressed buttons. With a switch to ground the pull-up would be continually taking current, so if the switch is read as 0 it is safe to disable the pull-up to stop the pull-up current because the switch is holding the input pin at 0V. If the switch was read as 1 the pull-up needs to be enabled to hold the pin at VCC.

As for whether a button should connect to 0V when pressed using a pull-up resistor, or connect to VCC when pressed using a pull-down, that makes no difference. In both cases current will only flow while the button is being pressed. Generally I use active low signals (a throwback to the days of TTL logic where there's more noise margin going from 1 to 0) so would tend to use pull-up resistors with a switch to ground.

So yes, you should ensure all digital input buffer pins are driven by something to 0V or VCC. You can do that for unused pins either by enabling the internal pull-up, enabling internal pull-down, strapping the pins to VCC or to 0V externally, or simply by enabling the output buffer on that pin and driving to 1 or 0. I tend to use the latter approach by driving all unused digital I/O output buffers to 0 as it makes the layout easier. Also if you have analogue pins for the ADC you should disable the pin's digital input buffer.

Mark.

doublec4

  • Jr. Member
  • **
  • Posts: 53
Re: Pull up vs pull down current consumption?
« Reply #8 on: February 20, 2018, 12:02:52 PM »
Thank you for all of the responses guys. Makes things more clear :)