Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/71.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C编程一个按钮,在按下时执行一次任务(闩锁)_C_Button_Raspberry Pi_Debouncing - Fatal编程技术网

C编程一个按钮,在按下时执行一次任务(闩锁)

C编程一个按钮,在按下时执行一次任务(闩锁),c,button,raspberry-pi,debouncing,C,Button,Raspberry Pi,Debouncing,我对c和Raspberry Pi比较陌生,正在尝试简单的程序。我想要的是,当按下按钮时,它会打印一次,直到再次按下按钮时才会再次打印,即使按下按钮(类似于闩锁)。我想也许添加第二个while循环可以解决这个问题,但有时它仍然检测不到按钮按下 #include <bcm2835.h> #include <stdio.h> #define PIN RPI_GPIO_P1_11 int main() { if(!bcm2835_init()) retu

我对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;
}
#包括
#包括
#定义引脚RPI\U GPIO\U P1\U 11
int main()
{
如果(!bcm2835_init())
返回1;
bcm2835_gpio_fsel(引脚,bcm2835_gpio_fsel_输入);
而(1)
{
如果(bcm2835_gpio_lev(引脚))
{
printf(“按钮已被按下\n”);
}
而(bcm2835_gpio_lev(PIN)){}
}
bcm2835_close();
返回0;
}

您的逻辑是正确的,如果按钮是完美的,这将起作用。但事实并非如此。您必须对按钮的信号进行去抖动。实现这一目标的两种方法(组合使用时效果最佳):

I.在按钮的两个引脚之间添加电容器(或尝试),和/或

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

while(1){
当(!按下按钮时)
;
printf(“按下按钮!\n”);
while(经过的时间<偏移量)
;
}
等等


编辑:正如@jerry指出的,当按下按钮时,上面的操作“不正确”。这里有几个可以用来满足所有要求的函数。

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

用调用
waitButtonRelease()
替换
while(bcm2835\u gpio\u lev(PIN)){}
循环


如果您可以使用数字存储示波器,您可以直接探测开关信号,以查看开关反弹的确切情况。它可以帮助您了解问题,并根据您的特定交换机的特性调整去抖动。

对于这样一个简单的程序,像您这样使用繁忙循环是可以的。然而,我建议你改掉这个习惯,因为这在玩具项目之外的任何项目中都是不可接受的

有很多方法可以消除按钮的抖动,就像有人在编写代码一样。在某些情况下,在硬件上实现这一点可能是可行的,但也并非没有缺点。在任何情况下,由于这是一个编程站点,让我们假设您不能(或不想)更改硬件

一个快速而肮脏的修改是定期检查主循环中的按钮,只有在按钮发生变化时才采取行动。由于您是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;
}
#包括
#包括
#定义引脚RPI\U GPIO\U P1\U 11
//检查次数的适当值取决于按钮的“清洁”程度,以及
//您需要系统具有响应性,以及您调用helper函数的频率。那个
//最后一个取决于你的CPU有多快,以及你的电脑里还有多少其他的东西在运转
//循环。不要选择高于UINT_MAX(在limits.h中)的值
#定义按钮_去抖动_检查100
int ButtonPress()
{
静态无符号int buttonState=0;
静态字符ButtonPresessabled=1;
如果(bcm2835_gpio_lev(引脚))
{
如果(按钮状态<按钮去抖动检查)
{
buttonState++;
}
否则如果(按钮已启用)
{
ButtonPresessabled=0;
返回1;
}
}
否则如果(按钮状态>0)
{
按钮状态--;
//或者,您可以在这里将buttonState设置为0,但我更喜欢这种方式
}
其他的
{
按钮启用=1;
}
返回0;
}
int main()
{
如果(!bcm2835_init())
返回1;
bcm2835_gpio_fsel(引脚,bcm2835_gpio_fsel_输入);
而(1)
{
如果(按钮按())
{
printf(“按钮已被按下\n”);
}
//主循环代码的其余部分
}
bcm2835_close();
返回0;
}

你可能会发现谷歌搜索“按钮去Bouncing”很有帮助。这一切都是关于知道什么是技术术语,这样我就可以查找它了。谢谢你乐意帮忙-我不认为这是一个值得回答的问题,但是有一个合适的词来搜索有时真的很有帮助!如果按住按钮,此代码将重复printfindefinitely@jerry这是正确的。添加了这一点,以及指向更好的替代方案的链接。如果您将
while(经过的时间
更改为
while(经过的时间
将解决特定问题
usleep(1000)
调用应该在while循环中。否则你就不会做太多的去抖动了。我试着实现了这段代码。仍然有一点反弹,所以我改变了“按钮去盎司检查”。我发现一个很好的值大约是300(我的按钮可能不是那么“干净”)。通常情况下,是释放按钮导致反弹。(也尝试了“按钮去抖动检查”=5000的乐趣,明显的滞后,但没有反弹)。硬件解决方案是否更可靠??谢谢你的帮助!此解决方案的问题在于,
BUTTON_DEBOUNCE_CHECKS
所需的值不仅取决于开关的特性,还取决于处理器的速度以及“主外观代码的其余部分”的执行时间。它还“忙等待”-在主循环中没有其他阻塞调用时,CPU使用率将达到100%,ans会使其他进程饥饿,从而导致系统整体缓慢。如果您希望能够在监视按钮按下的同时执行其他工作,那么最好使用带有
usleep()
调用的单独线程来保持CPU负载最小化。我完全同意
button\u DEBOUNCE\u检查值的脆弱性,并在两个
#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++ ; 
        }
    }
}
void waitButtonPress()
{
    int debounce = 0 ;

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

        if( !bcm2835_gpio_lev(PIN) )
        {
            debounce = 0 ;
        }
        else
        {
            debounce++ ; 
        }
    }
}
#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 ; 
        }
    }
}
    while(1)
    {
        waitButton( true )
        printf("The button has been pressed\n");

        waitButton( false ) ;
    }
#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;
}