C编程一个按钮,在按下时执行一次任务(闩锁)
我对c和Raspberry Pi比较陌生,正在尝试简单的程序。我想要的是,当按下按钮时,它会打印一次,直到再次按下按钮时才会再次打印,即使按下按钮(类似于闩锁)。我想也许添加第二个while循环可以解决这个问题,但有时它仍然检测不到按钮按下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
#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;
}