我无法完全取消附加到中断的按钮。目的是使void loop()
中的语句在按下/释放按钮时仅运行一次。
通常最终发生的事情是两件事之一
这是我的确切代码:
#define interruptPin 2
#define DBOUNCE 100
volatile byte state = LOW; //ISR flag, triggers code in main loop
volatile unsigned long difference;
void setup() {
pinMode(interruptPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(interruptPin), ISR_0, CHANGE);
Serial.begin(115200);
}
void loop() {
if(state){ //If we have a valid interrupt
Serial.println(difference); //Print the time since the last ISR call
state = LOW; //Reset the flag
}
}
void ISR_0() {
static unsigned long last_interrupt = 0;
if(millis()-last_interrupt > DBOUNCE && digitalRead(interruptPin)){
difference=millis()-last_interrupt;
state = HIGH;
}
last_interrupt = millis(); //note the last time the ISR was called
}
这似乎是消除中断的一种流行方法,但是无论出于何种原因,它都不适合我。
我希望在按钮释放的第一个下降沿上digitalRead(interruptPin)
会读取low
,因此不会设置state
标志。
由于ISR更新了last_interrupt
时间,因此似乎仍然可以成功忽略第一个下降沿之后的连续弹跳。这使我相信反跳不是问题,而是digitalRead(interruptPin)
。
去弹跳似乎可以照顾到一个状态。释放按钮后,代码有时仍会将state
标志设置为HIGH
。
以下是一些示例输出:
3643
(在启动后等待约3.6秒后,我按下按钮,约1秒后释放它)
在与上述相同的情况下,输出有时看起来像这样:
3643
1018
这表明我按下了按钮,也释放了按钮。
我正在使用UNO R3和带有1k下拉电阻的瞬时触觉按钮。
我不确定这是怎么回事。我希望这很简单,只要有人愿意,任何人都可以在arduino上轻松测试一下。
答案 0 :(得分:1)
您可以使用Uno的内部上拉电阻,也可以使用按钮本身的下拉电阻。这是不正确的,您只能使用其中之一。 在if内部,您希望输入为高电平,因此请使用下拉电阻并将INPUT_PULLUP更改为INPUT。
(要弄清楚:电阻连接在输入引脚和地之间,按钮连接在+ 5V之间)
[编辑]
我认为当调用ISR时,interruptPin的状态可能会再次更改。 与(可能)峰值相比,由于digitalRead的速度较慢。
我不确定这是否是您想要的,但是下面的示例正在运行。 (我已启用LED进行测试)。 一件事:至少按住该按钮的去抖时间(100毫秒),否则将无法使用。 这并不理想,但是如果您想立即对开关做出响应,那就是您必须付出的代价。
#define interruptPin 2
#define DBOUNCE 100
volatile byte state = LOW; //ISR flag, triggers code in main loop
volatile unsigned long difference;
bool hasPrinted = false;
void setup() {
pinMode(interruptPin, INPUT);
pinMode (13, OUTPUT);
attachInterrupt(digitalPinToInterrupt(interruptPin), ISR_0, CHANGE);
Serial.begin(115200);
}
void loop() {
digitalWrite(13, state);
if (!hasPrinted) {
Serial.println(difference);
hasPrinted = true;
}
}
void ISR_0() {
static unsigned long last_interrupt = 0;
if(millis()-last_interrupt > DBOUNCE){
state = !state;
hasPrinted = false;
difference = millis()-last_interrupt;
}
last_interrupt = millis(); //note the last time the ISR was called
}
答案 1 :(得分:1)
我几次遇到按钮和编码器弹跳的情况,并且在软件中进行反跳会变得凌乱,并导致无法读取的代码或逻辑错误。
最简单的方法是添加一个0.1 uF电容器,如图所示:
Arduino具有滞后输入,如果您将10K用作上拉电阻,则可以在1ms以下进行跳动。这是我最喜欢的方法。
如果您想变得更认真,可以在互联网上找到精彩 pdf文件,其中包含许多示例和说明:A Guide to Debouncing
答案 2 :(得分:0)
我认为,正如评论中所说,主要问题是因为您没有实际的反跳。
因此,释放按钮后,它会继续弹跳,从而导致输入引脚上的逻辑电平发生变化。如果此时,当为DigitalRead()锁定引脚状态时,该状态被读取为高电平,则将满足整个条件并执行state = HIGH;
。
我不是艺术家,但是我尽力画出这个时序图:
因此,为避免这种情况,您可以使用任何简单的方法来消除抖动。最简单的方法是在稍微超时后重新读取引脚状态,该状态大于预期的最大反弹时间。 例如,如果您正在等待按下按钮并获得高逻辑电平(如果按钮连接到GND,则为低电平),只需等待约5毫秒,然后再次读取该电平。仅在级别仍为高(低)时处理按钮按下。
正如在其他答案中所述,硬件反跳也将有所帮助。您可以使用更高的电阻(实际上您不需要使用外部电阻:将按钮连接至GND并启用内部上拉电阻,约为35kOhm)。并在按钮旁边并联一个大约1nF的电容器。
答案 3 :(得分:0)
消除中断中的按钮/开关很麻烦。
使用限位开关时,我遇到了类似的情况。 敲到限位开关的那一刻,必须发生一些事情-所以要中断。
但是,当限位开关被释放时,中断也会触发,这是一个问题。我将中断设置为在下降边缘触发。
无论如何,我使用标志结束了在中断外部的反跳操作。 该代码对此进行了解释,但: 按下开关,ISR运行(执行所需的操作)并设置ISR标志。 ISR标志会阻止ISR实际执行任何操作,直到将其清除为止。 在主循环中,如果设置了ISR标志,则调用反跳功能。 反跳功能将一直等待,直到引脚/开关在所需的状态(高/低)稳定一段预定义的时间,然后清除ISR标志,使ISR再次执行操作。
#define interruptPin 2
#define DEBOUNCE_TIME 100L
volatile bool ISR_ACTIVATED = false;
volatile bool display_int_time = false;
bool debounce_started = false;
unsigned long Isr_debounce_pin_timer;
volatile unsigned long difference;
void setup() {
pinMode(interruptPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(interruptPin), ISR_0, CHANGE);
Serial.begin(112500);
}
void loop() {
if (display_int_time) {
Serial.println(difference); //Print the time since the last ISR call
// Right, done with interrupt stuff. clear the interrupt flag
display_int_time = false;
}
// Call debounce ISR routine in main loop
if (ISR_ACTIVATED)
ISR_ACTIVATED = !debounce(interruptPin, HIGH, Isr_debounce_pin_timer);
}
bool debounce(int debounce_pin, bool state, unsigned long &state_latch_start) {
// debounce_pin - what pin are we debouncing?
// state - should the stable state be HIGH or LOW
// state_latch_start - when we 'latched' on to a (possibly) stable state
static bool current_state;
// Read the required pin
current_state = digitalRead(debounce_pin);
// Is the pin at the required state, and we have not yet 'latched' it?
if ((!debounce_started) && (current_state == state)) {
// 'latch' this state (ie take note of when the pin went to the required level)
state_latch_start = millis();
debounce_started = true;
}
// Have we 'latched', but the pin has bounced
if (debounce_started && (current_state != state))
// unlatch
debounce_started = false;
// Have we latched, the pin is at the required level and enough time has passed (ie pin is stable)
if (debounce_started && (current_state == state) && (((unsigned long)(millis() - state_latch_start)) >= DEBOUNCE_TIME)) {
// cool. unlatch
debounce_started = false;
// report back that all is goood.
return(true);
}
// Blast. Either the pin is at the wrong level, or is still bouncing. Tell the boss to try again later!
return(false);
}
void ISR_0() {
static unsigned long last_interrupt = 0;
if((millis()-last_interrupt > DEBOUNCE_TIME) && digitalRead(interruptPin) && !ISR_ACTIVATED){
difference=millis()-last_interrupt;
//state = HIGH;
ISR_ACTIVATED = true;
display_int_time = true;
}
last_interrupt = millis(); //note the last time the ISR was called
}
***请注意,编辑了我的代码,将去抖时间包含在原始ISR中,以确保中断有效。我没有阅读有关您嘈杂环境的评论
答案 4 :(得分:0)
我的最终解决方案,感谢@darrob
#define interruptPin 2
#define DEBOUNCE_TIME 50L
//ISR Flags
volatile bool ISR_DEACTIVATED = false;
volatile bool display_int_time = false;
bool debounce_started = false;
unsigned long Isr_debounce_pin_timer;
int x = 0;
void setup() {
pinMode(interruptPin, INPUT);
attachInterrupt(digitalPinToInterrupt(interruptPin), ISR_0, CHANGE);
Serial.begin(112500);
}
void loop() {
//This runs every time we press the button
if (display_int_time) {
Serial.print("X "); //Print the time since the last ISR call
x++;
(x%10==0)?Serial.println(x):Serial.println();
display_int_time = false; //Done with interrupt stuff. Clear the interrupt flag
}
//Debounce for the ISR routine in main loop
if (ISR_DEACTIVATED)
//Wait until the pin settles LOW to reactivate the ISR
ISR_DEACTIVATED = !debounce(interruptPin, LOW, Isr_debounce_pin_timer);
}
//Returns TRUE if pin is stable at desired state, FALSE if bouncing or other state
bool debounce(const int debounce_pin, const bool state, unsigned long &state_latch_start) {
// debounce_pin - what pin are we debouncing?
// state - should the stable state be HIGH or LOW
// state_latch_start - when we 'latched' on to a (possibly) stable state
//If you are calling this routine to debounce multiple pins in the same loop,
// this needs to be defined outside of the function, and passed in as a separate
// parameter for each debounce item (like unsigned long &state_latch_start)
static bool current_state;
// Read the required pin
current_state = digitalRead(debounce_pin);
// Is the pin at the required state, and we have not yet 'latched' it?
if ((!debounce_started) && (current_state == state)) {
// 'latch' this state (ie take note of when the pin went to the required level)
state_latch_start = millis();
debounce_started = true;
}
// Have we 'latched', but the pin has bounced
if (debounce_started && (current_state != state))
// unlatch
debounce_started = false;
// Have we latched, the pin is at the required level and enough time has passed (ie pin is stable)
if (debounce_started && (current_state == state) && (((unsigned long)(millis() - state_latch_start)) >= DEBOUNCE_TIME)) {
// cool. unlatch
debounce_started = false;
// report back that all is goood.
return(true);
}
//Either the pin is at the wrong level, or is still bouncing. Try again later!
return(false);
}
void ISR_0() {
if(!ISR_DEACTIVATED){
ISR_DEACTIVATED = true;
display_int_time = true;
}
}