1

I want to make a simple circuit that when you press a pushbutton, an LED lights up stays lit until the button is pressed again. I haven't delved into interrupts yet on AVR, and was wondering whether it is possible to do in a while loop.

This is my code:

#include <avr/io.h>

#define INPUT (1 << PINB3)
#define OUTPUT (1 << PINB0)

int main(void)
{
    DDRB = 0x00;        //everything input
    PORTB = 0x00;       //no pud

    DDRB |= 0x1;        //PB0 is an output
    PORTB = 0x00;       //PB0 is low and PUD is off

    uint8_t prev = PINB;

    while(1)
    {
        if ((prev & INPUT) < (PINB & INPUT)) {      //rise edge
            if ((PORTB & OUTPUT) == 0x0) {      //if output was low
                PORTB |= OUTPUT;        //make output high
            } else {
                PORTB &= ~OUTPUT;       //make output low
            }
        }
        prev = PINB;
    }
}

The switch only partially works, I'm guessing because of the nature of the while loop and it storing the previous input really fast.

I believe the problem lies in my method of detecting a rising edge of the push-button. What would be a better method?

Blup1980
  • 6,120
  • 3
  • 26
  • 47
tgun926
  • 2,726
  • 12
  • 36
  • 59

3 Answers3

1

Your problem might be bouncing. When you press or release a button, it bounces. (see picture). This is due to mechanical bounces of the electrical contacts inside the button itself. enter image description here

You have to take this into account and reject the bounces. This can be done in software. A lot of method exists, using timer, interrupts, wait loops etc. Google the web and choose the implementation that fits your system best.

You can also have a look here: debouncing-buttons

Blup1980
  • 6,120
  • 3
  • 26
  • 47
  • Wouldn't bouncing imply that the light will flicker many times, rather than the light only turns on/off every few presses? – tgun926 Aug 04 '14 at 12:59
  • No. You may experience odd number of bounces. Thus you LED would light up and down again in some micro seconds (impossible to see because too fast) and you would think it did nothing and try again. Only with no or even number of bounce your LED would toggle. – Blup1980 Aug 04 '14 at 13:07
  • Yes, but the flicker will be faster than you can see. You could see it on a 'scope though. – JRobert Aug 06 '14 at 22:25
1

You must enable pull-up resistor for your switch input. Then connect one side of the switch to your AVR input, and other side to the GND.

The other problem, Blup1980 pointed out is bouncing, which can be ommited in software.

Maybe something like this:

#include <util/delay.h>

#define F_CPU    1000000UL // Your crystal frequency.
#define WAIT_MS  250 // Time in ms to wait for debounce.

int main(void)
{
    DDRB = (1 << PINB0); // PINB0 is output, others input.
    PORTB = (1 << PINB3); // Enable pull-up resistor on PINB3.

    while (1) {
        // Check if PINB3 is low (switch closed).
        if (!(PINB & (1 << PINB3))) {
            PORTB ^= (1 << PINB0); // Invert PINB0 status (XOR).
            _delay_ms(WAIT_MS); // Wait for debounce. 
        }
    }
}

Since you've said, you want to do this without interrupts, the only way to avoid switch bouncing in software is to _delay_ms() execution of the code for certain amount of time (you'll have to experiment with WAIT_MS).

Other solution would be to set a flag when inverting PINB3 status and use a timer to wait for some time, then generate an interrupt, which would clear that flag.

Note: if you do use timer approach, the variable(flag) would have to be defined as volatile.

Golaž
  • 1,967
  • 3
  • 33
  • 51
  • 3
    Be careful of "the only way" type language. What you present is one of the ways, and perhaps the simplest, but it is not *the only* possibility. – Chris Stratton Aug 04 '14 at 16:08
  • Awesome. Could you explain why you would use the internal pull up resistor, as opposed to an external pull down resistor? – tgun926 Aug 05 '14 at 11:20
  • One part less and usually easier PCB connections (since external would require connecting switch both to GND and VCC). – Golaž Aug 05 '14 at 14:52
  • Lower parts count, thus less expense. Pullups are built in to AVR processors; you only need to enable them. If you want a pulldown, you'll need to supply an external part. – JRobert Aug 06 '14 at 22:23
0

Not satisfied with the answers, as they don't provide the requirements of what I want.

The operation of a latch as according to the question is something like this:

latch

This implies that the pushbutton can be held down indefinitely, but the light should remain in the same state.

code

The problem I found lied in the way a rising edge was detected. In my initial code, the previous pin value was sampled, and then almost immediately after, the current value was sampled.The comparison for the rise edge requires two arithmetic calculations and a comparison, and the value of the input may change in between.

By sampling the current and previous PIN values separately, I obtained a ~90% success rate:

while (1) {
    cur = PINB;     //sample current input

    if ((prev & (1 << PINB3)) < (cur & (1 << PINB3))) {     //rise edge
        PINB |= (1 << PINB0);       //toggle led
    }   
    prev = PINB;    //sample previous input
}

By adding a short 10ms delay between sampling the inputs, a ~100% success rate was achieved:

#define F_CPU    1000000UL // Crystal frequency.
#define WAIT_MS  10 // Time in ms

#include <avr/io.h>
#include <util/delay.h>

int main(void)
{
    DDRB = (1 << PINB0); // PINB0 is output, others input.

    uint8_t cur = PINB;
    uint8_t prev = PINB;

    while (1) {
        cur = PINB;     //sample current input

        if ((prev & (1 << PINB3)) < (cur & (1 << PINB3))) {     //rise edge
            PINB |= (1 << PINB0);       //toggle led
        }   
        prev = PINB;
        _delay_ms(WAIT_MS); // Wait.
    }
}
tgun926
  • 2,726
  • 12
  • 36
  • 59
  • Well, the fact that the state could change in between the edge and the sampling IS, by definition, the problem of bounces... No toggles "on purpose" can be that fast. – Blup1980 Aug 09 '14 at 08:01
  • @Blup1980 I had a look at the output of the switch on an oscilloscope, and there was no bouncing regarding the switch. I added a low pass RC filter for safe measure (didn't have any Schmitt triggers on me), and the result was the same. I agree with you that that is probably not the reason. – tgun926 Aug 09 '14 at 08:16
  • I'll have a look at the output of the LED too. – tgun926 Aug 09 '14 at 08:21