Menu
CoddyTech

用中断统计按键次数

本课属于 Coddy Arduino 学习旅程的“无需 delay 的计时”单元。第 15 课,共 18 课。

正如你在“输入”单元中看到的,按钮会抖动:按下和松开时,触点会接触、分开,然后再次接触,持续约 1 毫秒。中断会检测到每一次接触,因此使用 FALLING 的 ISR 每检测到一次就加 1,会把一次点击计数好几次。

ISR 不能用 delay() 来消抖:delay() 需要时钟自己的中断,而 ISR 运行时中断会暂停。改用时钟来处理。记住上次计数的按下操作发生的时间,并忽略紧接着发生的下落沿:

unsigned long lastPress = 0;

void onPress() {
  unsigned long now = millis();
  if (now - lastPress >= 200) {
    presses = presses + 1;
    lastPress = now;
  }
}

在 ISR 中读取 millis() 没问题:它会给出 ISR 开始运行的时间。选择一个比一次点击持续时间长的时间窗口,这样松开时的抖动就会落在窗口内;同时,窗口要短于两次快速点击之间的间隔。对人来说,200 毫秒合适。

challenge icon

挑战

中等

这个计数器会把每次点击计数好几次。在 onPress() 中对它进行消抖:

  • 添加一个 unsigned long lastPress 变量
  • 只有自 lastPress 起至少经过 200 ms 后,才对一次下降沿计数,然后将时间存入 lastPress

按下 运行,点击几次按钮并观察计数,然后点击 检查。

亲手试试

const int buttonPin = 2;

volatile int presses = 0;
int shown = 0;

void onPress() {
  // 引脚每次从高电平变为低电平都会计数,抖动也算:在这里消除抖动。
  presses = presses + 1;
}

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);
  Serial.begin(9600);
  attachInterrupt(digitalPinToInterrupt(buttonPin), onPress, FALLING);
}

void loop() {
  // 每当计数发生变化时,就打印出来。
  if (presses != shown) {
    shown = presses;
    Serial.println(shown);
  }
}
quiz icon自我测试

这节课包含一个小测验。开始学习即可作答,并记录你的进度。

无需 delay 的计时中的所有课

自己练习: Arduino 在线编辑器