【问题标题】:Arduino - button triggers twice randomlyArduino - 按钮随机触发两次
【发布时间】:2026-01-04 14:55:01
【问题描述】:

我编写了一个小型 C++ 程序,它可以在 Arduino Uno (atmega328p) 上交叉编译和运行。它是一个基本的 MIDI 补丁更换器,它允许步进通过 32 个插槽,其中包含程序编号(以数组形式组织)。选择一个插槽后,计时器运行,当它达到 400 毫秒时,程序更改将通过串行 MIDI 协议发送到连接的合成器。但这在这种情况下并不重要。

有两个按钮用于增加和减少当前程序。当前节目以二进制形式显示,带有 5 个 LED。有 32 个程序需要单步执行,00000 代表 1,11111 代表 32。

到目前为止一切正常,我创建了一个小类,它代表一个按钮并处理去抖动。所以我不认为噪音是错误的原因。

错误/错误如下:一段时间后(我无法重现错误发生的那一刻)按钮触发了两次更改程序的动作 -按下时第一个,松开时第二个。另外,如果我按下按钮,请等待几秒钟然后释放它... 这是不需要的,但我看不出我在哪里犯了错误。

midipatcher.cpp - 入口点

#include <Arduino.h>
#include "Button.h"

int timer = 0;
int deltaTime = 0;
int currentSlot = 0;
bool waiting;
int actionDelay = 400;

Button* buttonUp;
Button* buttonDown;

int leds[] = { 9, 10, 11, 12, 13 };

byte nordLeadSlots[32] = {
  0, 1, 3, 2,
  5, 6, 9, 10,
  24, 50, 11, 12,
  25, 26, 25, 25
};

byte ms2000Slots[32] = {
  5, 6, 9, 10,
  24, 50, 11, 12,
  25, 26, 25, 25,
  0, 1, 3, 2
};

void sendSlotData() {
  Serial.write(0xC0);
  Serial.write(nordLeadSlots[currentSlot]);

  Serial.write(0xC1);
  Serial.write(ms2000Slots[currentSlot]);
}
void updateLeds() {
  for (int i = 0; i < 5; i++) {
    if (bitRead(currentSlot, i) == 1) {
        digitalWrite(leds[i], HIGH);
    } else {
        digitalWrite(leds[i], LOW);
    }
  }
}

void changeSlot(int slot) {
  if (slot > 31) {
    currentSlot = 0;
  } else if (slot < 0) {
    currentSlot = 31;
  } else {
    currentSlot = slot;
  }

  waiting = true;
  updateLeds();

  timer = millis();
}

void setup() {
  buttonUp = new Button(2);
  buttonDown = new Button(3);

  for (int i = 0; i < 5; i++) {
    pinMode(leds[i], OUTPUT);
  }

  Serial.begin(31250);
}

void loop() {
  if (buttonUp->isPressed()) {
    changeSlot(currentSlot + 1);
  }

  if (buttonDown->isPressed()) {
    changeSlot(currentSlot - 1);
  }

  deltaTime = millis() - timer;

  if (waiting == true && deltaTime > actionDelay) {
    sendSlotData();
    waiting = false;
  }
}

int main(void) {
  init();
  setup();
  while (true) {
    loop();
  }
}

Button.cpp - 关心按钮去抖动的代码:

#include <Arduino.h>
#include "Button.h"

Button::Button(int pin) {
  debounceDelay_ = 10;

  pin_ = pin;
  state_ = LOW;
  lastDebounceTime_ = 0;

  pinMode(pin_, INPUT);
}

bool Button::isPressed() {
  if (millis() - debounceDelay_ < lastDebounceTime_) return false;

  int currentState = digitalRead(pin_);

  if (currentState != state_) {
    state_ = currentState;
    if (currentState == HIGH) {
      return true;
    }
  }
  lastDebounceTime_ = millis();
  return false;
}

我不认为主类中的时间处理是问题,它只关心在选择插槽后,在发送程序更改之前必须经过的 400 毫秒。我已经注释掉了那个功能,但是问题被拒绝了。

编辑:

这是电路的截图,用fritzing制作,抱歉不是真正的电路布局。

再次编辑:不是。

因为我的知名度不够高,所以不能发截图。 图片可在此处获得:

【问题讨论】:

  • 不按时,引脚是接地还是不接地?
  • 是的,它与一个下拉电阻相连。 (这是正确的词吗?)。我编辑了原始帖子并附上了草图的屏幕截图。
  • 您找出问题所在了吗?
  • 不...我刚刚构建了该死的东西,在 30 - 40 次按钮操作之后,问题就出现了。但没问题,它只是需要更多的注意力,我必须来回走动几次,直到我得到想要的位置。哎哟。我在上周六的一次演出中试了一下,结果很好。有趣。

标签: button arduino debouncing


【解决方案1】:

您好,您可以使用低通滤波器解决这个问题。

噪声是来自周围环境的高频信号,可能是交流线路中存在无功负载时产生的电噪声。

你可以做的是在每个输入引脚上放置一个旁路电容到接地。

创建一个只允许长按状态的低通滤波器程序。 长按按钮的概念。

【讨论】:

    【解决方案2】:

    您不需要下拉电阻,您希望将开关接地并启用输入引脚上的内部上拉电阻。你可以看看我在这里整理的一个小演示系统的照片:

    https://kentindell.files.wordpress.com/2015/02/2015-02-18-15-02-16.jpg

    这里讨论了演示:

    https://kentindell.wordpress.com/2015/02/18/micrcontroller-interconnect-network-min-version-1-0/

    初始化 I/O 端口的 C 在这里:

    https://github.com/min-protocol/min/blob/master/firmware/main.c#L64

    读取输入的C代码在这里:

    https://github.com/min-protocol/min/blob/master/firmware/main.c#L85

    【讨论】: