C语言 C 编写一个按钮以在按下时执行一次任务(闩锁)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16306901/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 06:13:44  来源:igfitidea点击:

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

cbuttonraspberry-pidebouncing

提问by Marmstrong

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.

我对 c 和 Raspberry Pi 比较陌生,正在尝试简单的程序。我想要的是,当按下按钮时,它会打印一次,并且在再次按下按钮之前不会再次打印,即使按下按钮(有点像闩锁)。我想也许添加第二个 while 循环会解决这个问题,但有时它仍然没有检测到按下按钮。

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

采纳答案by jerry

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.

一个快速而肮脏的修改是定期检查主循环中的按钮,只有在它发生变化时才采取行动。由于您是 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;
}

回答by Marmstrong

Your logic is correct and this would work if buttons were perfect. But they aren't. You have to debouncethe signal of the button. Two methods to achieve that (works best when combined):

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

I. Add a capacitor between the two pins of the button (or try an even more sophisticated button debouncer circuitry), and/or

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

II. use software debouncing (pseudo-C):

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

while (1) {
    while (!button_pressed)
        ;

    printf("Button pressed!\n");


    while (elapsed_time < offset)
        ;
}

etc.

等等。

Edit: as @jerry pointed out, the above doesn't work "correctly" when the button is held. Here are a couple of more professional code snippetsyou can use to meet all the requirements.

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

回答by Clifford

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 debouncemost switches while retaining responsiveness.

以下函数以名义上 1 毫秒的间隔轮询按钮,并要求状态保持“释放”到 20 次连续轮询。这通常足以在保持响应性的同时消除大多数开关的抖动

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

将您的while(bcm2835_gpio_lev(PIN)){}循环替换为对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 循环可能如下所示:

    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 bouncelooks like. It may help you understand the problem and also to tailor the debounce to the characteristics of your particular switch.

如果您可以使用数字存储示波器,您可以直接探测开关信号以准确查看开关弹跳的样子。它可以帮助您了解问题并根据您的特定开关的特性定制去抖动。