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

Arduino类中attachInterrupt无法关联按钮按下函数的问题

Arduino类中使用attachInterrupt的问题解决

问题根源

Arduino的attachInterrupt只接受无参数、无返回值的全局函数或静态成员函数,而类的非静态成员函数会隐含一个this指针(用于访问对象的成员变量和方法),函数签名不匹配,所以无法直接作为中断服务函数(ISR)传入。

另外还有一个隐藏风险:你原代码的中断函数里调用了delay()、Serial.println()这类函数,这在ISR里是绝对禁止的——ISR要尽可能简短,不能阻塞,也不能调用依赖中断的函数(比如Serial依赖UART中断,delay依赖定时器中断),否则会导致系统异常。

解决方案

用静态成员函数作为中转ISR,配合一个指向Game对象的静态指针,让静态函数能访问到对象的成员;同时把耗时/非ISR安全的操作移到loop()中处理,用标志位触发事件。

修改后的完整代码

#include <Arduino.h>

class Game {
private:
    bool started;
    bool over;
    int winner;
    int reactionTime;
    unsigned long startTime;
    int button1Pin;
    int button2Pin;
    int led1Pin;
    int led2Pin;
    int redPin;
    int greenPin;
    int bluePin;
    int buzzerPin;
    int player1Score;
    int player2Score;
    // 新增:标记玩家按下按钮的事件标志位
    bool player1Pressed;
    bool player2Pressed;
    // 新增:静态指针,指向当前Game对象
    static Game* instance;

    // 原中断逻辑改为私有成员函数,仅处理状态和标志位
    void handlePlayer1Press() {
        if (!over && started) {
            over = true;
            winner = 1;
            reactionTime = millis() - startTime;
            player1Pressed = true;
            player1Score++;
        }
    }

    void handlePlayer2Press() {
        if (!over && started) {
            over = true;
            winner = 2;
            reactionTime = millis() - startTime;
            player2Pressed = true;
            player2Score++;
        }
    }

    // 新增:静态中转ISR函数
    static void isrButton1() {
        if (instance) {
            instance->handlePlayer1Press();
        }
    }

    static void isrButton2() {
        if (instance) {
            instance->handlePlayer2Press();
        }
    }

public:
    Game(int b1, int b2, int l1, int l2, int r, int g, int b, int bz) {
        button1Pin = b1;
        button2Pin = b2;
        led1Pin = l1;
        led2Pin = l2;
        redPin = r;
        greenPin = g;
        bluePin = b;
        buzzerPin = bz;
        started = false;
        over = false;
        winner = 0;
        reactionTime = 0;
        player1Score = 0;
        player2Score = 0;
        player1Pressed = false;
        player2Pressed = false;

        pinMode(button1Pin, INPUT_PULLUP);
        pinMode(button2Pin, INPUT_PULLUP);
        pinMode(led1Pin, OUTPUT);
        pinMode(led2Pin, OUTPUT);
        pinMode(redPin, OUTPUT);
        pinMode(greenPin, OUTPUT);
        pinMode(bluePin, OUTPUT);
        pinMode(buzzerPin, OUTPUT);

        // 初始化静态指针为当前对象
        instance = this;
        // 绑定静态中转函数到中断
        attachInterrupt(digitalPinToInterrupt(button1Pin), isrButton1, FALLING);
        attachInterrupt(digitalPinToInterrupt(button2Pin), isrButton2, FALLING);
    }

    void start() {
        Serial.println("Press any button to start the game");
        while (digitalRead(button1Pin) && digitalRead(button2Pin)) {
            // INPUT_PULLUP模式下,按钮按下为LOW,等待任意按钮触发
        }
        started = true;
        Serial.println("Game started!");
    }

    void update() {
        // 处理玩家1获胜逻辑
        if (player1Pressed) {
            player1Pressed = false;
            digitalWrite(led1Pin, HIGH);
            digitalWrite(redPin, LOW);
            digitalWrite(greenPin, HIGH);
            digitalWrite(bluePin, LOW);
            tone(buzzerPin, 440, 500);
            Serial.println("Player 1 wins!");
            Serial.print("Reaction time: ");
            Serial.print(reactionTime);
            Serial.println("ms");
            
            // 闪烁效果
            for (int i = 0; i < 5; i++) {
                digitalWrite(led1Pin, HIGH);
                digitalWrite(greenPin, HIGH);
                digitalWrite(bluePin, HIGH);
                delay(100);
                digitalWrite(led1Pin, LOW);
                digitalWrite(greenPin, LOW);
                digitalWrite(bluePin, LOW);
                delay(100);
            }
            
            delay(2000);
            resetGame();
        }

        // 处理玩家2获胜逻辑
        if (player2Pressed) {
            player2Pressed = false;
            digitalWrite(led2Pin, HIGH);
            digitalWrite(redPin, LOW);
            digitalWrite(greenPin, HIGH);
            digitalWrite(bluePin, LOW);
            tone(buzzerPin, 440, 500);
            Serial.println("Player 2 wins!");
            Serial.print("Reaction time: ");
            Serial.print(reactionTime);
            Serial.println("ms");
            
            // 闪烁效果
            for (int i = 0; i < 5; i++) {
                digitalWrite(led2Pin, HIGH);
                digitalWrite(greenPin, HIGH);
                digitalWrite(bluePin, HIGH);
                delay(100);
                digitalWrite(led2Pin, LOW);
                digitalWrite(greenPin, LOW);
                digitalWrite(bluePin, LOW);
                delay(100);
            }
            
            delay(2000);
            resetGame();
        }
    }

    void blinkRgbLed() {
        if (started && !over) {
            int randomTime = random(3000, 6000);
            digitalWrite(redPin, HIGH);
            digitalWrite(greenPin, LOW);
            digitalWrite(bluePin, LOW);
            delay(randomTime);
            digitalWrite(redPin, LOW);
            digitalWrite(greenPin, HIGH);
            digitalWrite(bluePin, LOW);
            startTime = millis();
        }
    }

    void resetGame() {
        digitalWrite(led1Pin, LOW);
        digitalWrite(led2Pin, LOW);
        digitalWrite(redPin, LOW);
        digitalWrite(greenPin, LOW);
        digitalWrite(bluePin, LOW);
        over = false;
        winner = 0;
        reactionTime = 0;
        startTime = 0;
        started = false;
        start(); // 自动进入下一轮等待
    }

    int getPlayer1Score() {
        return player1Score;
    }

    int getPlayer2Score() {
        return player2Score;
    }
};

// 初始化静态指针
Game* Game::instance = nullptr;

// 创建Game对象
Game game(2, 3, 6, 7, 9, 10, 11, 12);

void setup() {
    Serial.begin(9600);
    randomSeed(analogRead(0));
    game.start();
    delay(250);
}

void loop() {
    game.update(); // 处理按钮按下后的逻辑
    game.blinkRgbLed();
}

关键修改说明

  1. 静态指针与中转ISR:静态指针instance指向当前Game对象,静态ISR函数通过这个指针调用对象的成员函数,解决了非静态成员函数无法直接作为ISR的问题。
  2. ISR安全优化:中断函数仅做状态更新和标志位设置,所有耗时操作(闪烁、延迟、串口输出)都移到update()方法中,在loop()里执行,避免ISR阻塞系统。
  3. 游戏流程优化:重置游戏后自动调用start(),进入下一轮等待,让游戏可以连续进行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 01:54:56