You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Arduino中断计数异常:Serial.print位置影响可靠性问询

问题分析:串口打印位置对干簧管计数的影响

咱们先直接点出核心原因:两种代码的计数差异,本质是中断服务程序(ISR)的中断屏蔽特性,加上串口输出的阻塞性共同导致的。下面具体拆解细节:

1. 第一种代码(Serial.print在loop中)的虚高计数

你的初始代码里,loop()一直在重复打印counter的值。虽然115200波特率看起来不算低,但串口输出是典型的阻塞式操作——每发送一个字符都需要等待硬件完成传输,整个打印语句的执行会占用不少CPU时间。

而这段时间里,全局中断是完全开启的:干簧管弹跳会产生多个FALLING电平边缘,每一个边缘都会触发sensorISR(),导致counter被多次累加(一次弹跳通常会触发3-5次中断)。这就是20升水得到120次计数的原因——大部分都是弹跳带来的无效触发。

2. 第二种代码(Serial.print在ISR中)的计数下降

当你把串口打印移到sensorISR()里后,情况发生了关键变化:

  • Arduino的ISR执行时,会自动禁用全局中断(默认行为,防止中断嵌套引发系统不稳定)。也就是说,在Serial.print执行的漫长过程中,即使干簧管还在弹跳,新的FALLING边缘也无法触发新的中断。
  • 所以一次弹跳过程中,只有第一次FALLING边缘会触发ISR,后续的弹跳脉冲都会被屏蔽,counter只加1。这就是计数降到40的原因——这个数值更接近真实的开关闭合次数(当然仍可能有少量漏记,但比之前的虚高合理很多)。

重要提醒:别在ISR里做串口操作

虽然第二种代码让计数更准确,但在ISR中调用Serial.print是非常糟糕的实践:

  • ISR的设计原则是尽可能短小、快速,串口输出的阻塞会让ISR执行时间过长,可能导致你后续要加的另外2个传感器中断被延迟甚至丢失。
  • 正确的做法是:在ISR里只做最必要的操作(比如给volatile变量加1,或设置一个标志位),然后在loop()里检查标志位,再进行串口打印或其他耗时操作,同时配合弹跳消抖(硬件滤波或时间判断均可)。

给你一个简单的消抖示例(核心逻辑):

const int sensorPin = 2;
volatile int counter = 0;
volatile unsigned long lastInterruptTime = 0;
const unsigned long debounceDelay = 10; // 10ms消抖阈值

void setup() {
  Serial.begin(115200);
  pinMode(sensorPin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(sensorPin), sensorISR, FALLING);
}

void loop() {
  // 用静态变量避免重复打印,减少阻塞
  static int lastPrintedCounter = -1;
  if (counter != lastPrintedCounter) {
    Serial.print("Counter: ");
    Serial.println(counter);
    lastPrintedCounter = counter;
  }
}

void sensorISR() {
  unsigned long currentTime = micros();
  // 只有距离上次中断超过消抖阈值,才计数
  if (currentTime - lastInterruptTime >= debounceDelay * 1000) {
    counter++;
    lastInterruptTime = currentTime;
  }
}

这样既避免了ISR里的耗时操作,又能有效过滤弹跳的无效计数,同时串口打印也不会频繁阻塞主循环。

内容的提问来源于stack exchange,提问作者Paulo Borges

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:57:39