5

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;
}
4

3 回答 3

2

您的逻辑是正确的,如果按钮是完美的,这将起作用。但他们不是。你必须去抖动按钮的信号。实现这一目标的两种方法(结合使用时效果最佳):

I. 在按钮的两个引脚之间添加一个电容器(或尝试更复杂的按钮去抖动电路),和/或

二、使用软件去抖动(伪 C):

while (1) {
    while (!button_pressed)
        ;

    printf("Button pressed!\n");


    while (elapsed_time < offset)
        ;
}

等等

编辑:正如@jerry 指出的那样,当按住按钮时,上述内容不能“正确”工作。这里有几个更专业的代码片段,您可以使用它们来满足所有要求。

于 2013-04-30T18:51:18.167 回答
1

The following function polls the button at nominally 1 millisecond intervals and required that the state remain "releases" to 20 consecutive polls. That will typically be sufficient to debounce most switches while retaining responsiveness.

Replace your while(bcm2835_gpio_lev(PIN)){} loop with a call to waitButtonRelease().

#include <unistd.h>
#define DEBOUNCE_MILLISEC 20

void waitButtonRelease()
{
    int debounce = 0 ;

    while( debounce < DEBOUNCE_MILLISEC )
    {
        usleep(1000) ;

        if( bcm2835_gpio_lev(PIN) )
        {
            debounce = 0 ;
        }
        else
        {
            debounce++ ; 
        }
    }
}

You may find it necessary also to debounce button presses as well as releases. That is done in the same way, but counting the opposite state:

void waitButtonPress()
{
    int debounce = 0 ;

    while( debounce < DEBOUNCE_MILLISEC )
    {
        usleep(1000) ;

        if( !bcm2835_gpio_lev(PIN) )
        {
            debounce = 0 ;
        }
        else
        {
            debounce++ ; 
        }
    }
}

Or perhaps a single function to debounce either state:

#include <stdbool.h>

void waitButton( bool state )
{
    int debounce = 0 ;

    while( debounce < DEBOUNCE_MILLISEC )
    {
        usleep(1000) ;

        if( bcm2835_gpio_lev(PIN) == state )
        {
            debounce++ ;
        }
        else
        {
            debounce = 0 ; 
        }
    }
}

Given this last function, your main while loop might look like:

    while(1)
    {
        waitButton( true )
        printf("The button has been pressed\n");

        waitButton( false ) ;
    }

If you have access to a digital storage oscilloscope, you might probe the switch signal directly to see exactly what the switch bounce looks like. It may help you understand the problem and also to tailor the debounce to the characteristics of your particular switch.

于 2013-04-30T19:31:05.370 回答
1

对于像这样的简单程序,使用您所做的繁忙循环就可以了。但是,我建议改掉这个习惯,因为除了玩具项目之外,这通常是不可接受的。

有多少人编写代码,就有多少方法可以消除按钮的抖动。在某些情况下,在硬件中执行此操作可能是可行的方法,但并非没有缺点。无论如何,由于这是一个编程站点,我们假设您不能(或不想)更改硬件。

一个快速而肮脏的修改是定期检查主循环中的按钮,只有在它发生变化时才采取行动。由于您是 C 和嵌入式编程的新手,因此我将避免使用计时器和中断,但知道一旦您了解了它们,您就可以使代码更易于理解和维护。

#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;
}
于 2013-04-30T19:44:49.500 回答