1
\$\begingroup\$

I'm trying to read a pushbutton press and turn a led in case its pressed. I have the following code. I have a 1k resistance with the LED and I'm connecting the push button directly to RD0. But the led is always on! If I disconnect vcc it kinda "works". What am I doing wrong?

#define _XTAL_FREQ 8000000

#pragma config FOSC = INTRC_CLKOUT, WDTE = OFF, PWRTE = ON      
#pragma config MCLRE = ON, CP = OFF, CPD = OFF, BOREN = OFF      
#pragma config IESO = ON, FCMEN = ON, LVP = OFF        

#include <xc.h>
#include <pic16f887.h>

void main() {
    PORTD = 0;
    TRISD = 0;
    TRISD0 = 1;
    RD1 = 0;
    OSCCON = 0x70;

    while (1) {
        if (RD0) {
            RD1 = 1;
        } else {
            RD1 = 0;
        }
    }
}

schematic

simulate this circuit – Schematic created using CircuitLab

\$\endgroup\$
3
  • \$\begingroup\$ Can you use the built-in schematic editor to post the schematic of what you're doing? \$\endgroup\$ Commented Nov 24, 2017 at 4:16
  • \$\begingroup\$ @ThreePhaseEel Added, sorry if It's really bad, I'm starting to learn :) \$\endgroup\$
    – aram
    Commented Nov 24, 2017 at 4:47
  • \$\begingroup\$ and I'm connecting the push button directly to RD0 A 4k7 pullup resistor is needed. \$\endgroup\$ Commented Nov 24, 2017 at 5:04

2 Answers 2

5
\$\begingroup\$

When the button isn't being pressed, what state should RD0 read? High or low? Look at the schematic for a minute and think about it.

If you're unsure, that's good. Because the 16F887 is unsure as well. When the button isn't being pressed, RD0 is actually floating. Nothing is driving it up or down. Unfortunately, there is a no "meh" option for digital electronics. The MCU must choose high or low. Absent a driving force on RD0, the pin will act like a tiny antenna and may randomly change state with the local electric field around it. In your case, it sounds like it's sticking high. The electrostatic field around your hand may actually change the pin state if you bring your hand near the MCU.

The solution is to put a pull-down resistor on RD0. Any reasonably large value of resistance will work (4.7k, 10k, and 100k are very common values). This will provide a driving force to 0V when the button isn't being pressed. When the button is pressed the resistor will appear negligible and the pin will see 5V.

\$\endgroup\$
3
  • \$\begingroup\$ "The solution is to put a pull-down resistor on RD0" - or just activate the internal pull-up and instead make the button connect to ground instead of vdd and invert the logic in the software. - Either use a couple of lines of code, or physically add a pull-down resistor. \$\endgroup\$ Commented Nov 24, 2017 at 8:50
  • 1
    \$\begingroup\$ Not sure Port D on the 16f887 has internal pull-ups. The button input would have to be moved to Port B. \$\endgroup\$
    – Dan Laks
    Commented Nov 24, 2017 at 10:37
  • 1
    \$\begingroup\$ Hah, I googled quickly before I commented to see if 16F887 even had pull-ups, I just quickly looked if there was something that implied that the chip had some pull-ups, which I found. I assumed all port's had that. Now after some further research I see that you're correct, only Port B has that luxurious mode. Which is weird in my opinion, but a fact. So your answer is on point, though... I'd recommend the questioner to use pull-up on Port B instead, or at least say that it's another solution, either way, you've earned a +1 vote. \$\endgroup\$ Commented Nov 24, 2017 at 10:46
2
\$\begingroup\$

There's some fundamental things that are needed whenever you wish to read a button:

  • The MCU port needs to be configured as input. This means that the pin has to be driven to a known state externally, in order to read it reliably.
  • In your circuit, the MCU pin is left floating when the switch is not pressed. This means that you cannot reliably read from the port.
  • In order to drive the pin to a known state, you can use a pull resistor to either connect it to 5V or ground as default state.
  • The switch will override the default state set by the pull resistor, since when it is pressed, it will short the pin to 5V.
  • Industry de facto standard values for pull resistors are 4k7 or 10k. Modern microcontrollers have built-in pull resistors internally, as part of the port hardware. They can then be activated through software, by writing to a register.
  • Once that is sorted, you have to "de-bounce" the signal. There's an electro-mechanical phenomenon occurring when a button is pressed or released, it will not do so cleanly, but "bounce up and down" for a short while. You can observe this with an oscilloscope.
  • Code reading buttons without implementing some form of de-bouncing will behave erratically and the values cannot be trusted.
  • De-bouncing can be done either in electronics with a RC filter, or more commonly in software. In software, it can be done in many ways, the simplest form is to read the pin, then read it again after a short while (10-20ms).
  • It is a good idea to have some form of series resistor after the button, to prevent ESD discharge into the MCU. Particularly true when small things meet big fingers, such as when using tacticle SMD switches or jumpers.

Next issue: You need to change the resistor to a suitable series resistor if you want any form of light from the LED. LEDs typically have a forward voltage of 1.5V or so. If your supply is 5V, there will be roughly 5 - 1.5 = 3.5V over the LED. With 10k in series, Ohm's law gives 350uA. Optimal current to drive a LED is typically 20mA, although 5mA or so is usually fine.

Also check the MCU datasheet and ensure that the MCU is actually capable of burning/sinking the needed current for the LED.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.