Monday, April 11, 2022

[SOLVED] C program a button to perform a task once when pressed (latch)

Issue

I am relatively new to c and the Raspberry Pi and am trying simple programs. What I would like is when the button is pressed it printfs once and doesn't printf again until the button is pressed again, even if the button is held down (sort of a latch). I thought maybe adding the second while loop in would fix this, but sometimes it still doesn't detect a button press.

#include <bcm2835.h>
#include <stdio.h>
#define PIN RPI_GPIO_P1_11

int main()
{
    if(!bcm2835_init())
        return 1;

    bcm2835_gpio_fsel(PIN, BCM2835_GPIO_FSEL_INPT);

    while(1)
    {
        if(bcm2835_gpio_lev(PIN))
        {
            printf("The button has been pressed\n");
        }

       while(bcm2835_gpio_lev(PIN)){}
    }

    bcm2835_close();
    return 0;
}

Solution

For a simple program like this, using busy loops like you've done is fine. However, I'd suggest getting out of the habit because it's often unacceptable in anything more than a toy project.

There are as many ways to debounce a button as there are people writing code. Doing it in hardware may be the way to go in some cases, but it's not without its drawbacks. In any case, since this is a programming site, let's assume you can't (or don't want to) change the hardware.

A quick and dirty modification is to periodically check the button in the main loop and only act if it has changed. Since you're new to C and and embedded programming, I'll avoid timers and interrupts, but know that you can make the code more understandable and maintainable once you learn about them.

#include <bcm2835.h>
#include <stdio.h>
#define PIN RPI_GPIO_P1_11

// A decent value for the number of checks varies with how "clean" your button is, how 
// responsive you need the system to be, and how often you call the helper function. That
// last one depends on how fast your CPU is and how much other stuff is going on in your
// loop. Don't pick a value above UINT_MAX (in limits.h)
#define BUTTON_DEBOUNCE_CHECKS 100

int ButtonPress()
{
    static unsigned int buttonState = 0;
    static char buttonPressEnabled = 1;

    if(bcm2835_gpio_lev(PIN))
    {
        if(buttonState < BUTTON_DEBOUNCE_CHECKS)
        {
            buttonState++;
        }
        else if(buttonPressEnabled)
        {
            buttonPressEnabled = 0;
            return 1;
        }
    }
    else if(buttonState > 0 )
    {
        buttonState--;
        // alternatively you can set buttonState to 0 here, but I prefer this way
    }
    else
    {
        buttonPressEnabled = 1;
    }

    return 0;
}

int main()
{
    if(!bcm2835_init())
        return 1;

    bcm2835_gpio_fsel(PIN, BCM2835_GPIO_FSEL_INPT);

    while(1)
    {
        if(ButtonPress())
        {
            printf("The button has been pressed\n");
        }

        // the rest of your main loop code
    }

    bcm2835_close();
    return 0;
}


Answered By - jerry
Answer Checked By - Robin (WPSolving Admin)