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

如何在不修改Keypad库时中断waitPress()循环以响应ISR?

问题描述

我在Arduino项目中使用的Keypad库包含如下waitPress()函数:

void Keypad::waitPress()
// Wait for any key to be pressed.
 { while (scan() == 0);
 }

该函数在单一条件下运行正常,但我的主代码中存在一个ISR(中断服务程序),它可能在waitPress()调用期间被触发:

static void isr_zero(void) {
    if (isr_change_flag == 0)
    {
      isr_dest = 0;
      isr_change_flag = 1;
    }
  }

由于Keypad::waitPress是死循环,它不会响应ISR触发,会持续循环直到有按键按下,这不符合需求,我需要ISR触发时能中断该循环。

限制条件:

  • 无法修改库的任何代码
  • scan()不是公共函数,无法重写waitPress()(这原本是最简单的方法)
  • goto因作用域限制无法使用

我曾考虑在ISR中调用处理函数,但知道在ISR中挂载长运行函数并非良好实践,尽管这在技术上可行。

可行解决方案

方法1:利用setjmp和longjmp实现上下文跳转

这是最直接的方法,能在ISR触发时强制跳出waitPress()的死循环:

  1. 定义全局跳转缓冲区,保存调用waitPress()前的程序上下文
  2. 在调用waitPress()前用setjmp()记录当前执行位置
  3. 在ISR中调用longjmp()直接跳回记录的位置,中断循环

示例代码:

#include <setjmp.h>

jmp_buf exit_waitpress_buf;

// 修改后的ISR
static void isr_zero(void) {
    if (isr_change_flag == 0)
    {
      isr_dest = 0;
      isr_change_flag = 1;
      // 跳回setjmp保存的上下文
      longjmp(exit_waitpress_buf, 1);
    }
}

// 主程序调用逻辑
void loop() {
    // setjmp首次调用返回0,从longjmp跳转回来时返回非0值
    if (setjmp(exit_waitpress_buf) == 0) {
        keypad.waitPress(); // ISR触发时会直接跳回此处
    } else {
        // 处理ISR触发后的逻辑
        handle_isr_event();
    }
}

注意:longjmp会直接改变执行流,使用时要确保全局变量状态安全,避免资源泄漏。

方法2:硬件模拟按键按下

如果硬件支持,可以在ISR中模拟按键输入,让waitPress()自然退出:

  1. 选择键盘矩阵中一个闲置引脚,或复用现有按键引脚
  2. 在ISR中短暂切换引脚电平,模拟按键按下,让scan()检测到输入
  3. 退出waitPress()后,处理ISR事件并恢复引脚状态

示例代码:

// 假设模拟按键连接到PIN2
#define SIMULATE_KEY_PIN 2

static void isr_zero(void) {
    if (isr_change_flag == 0)
    {
      isr_dest = 0;
      isr_change_flag = 1;
      // 模拟按键按下
      pinMode(SIMULATE_KEY_PIN, OUTPUT);
      digitalWrite(SIMULATE_KEY_PIN, LOW);
      delayMicroseconds(10); // 短暂保持确保scan()能检测到
      digitalWrite(SIMULATE_KEY_PIN, HIGH);
      pinMode(SIMULATE_KEY_PIN, INPUT_PULLUP); // 恢复引脚状态
    }
}

// 主程序逻辑
void loop() {
    keypad.waitPress();
    // 判断是否是ISR触发的模拟按键
    if (isr_change_flag) {
        handle_isr_event();
        isr_change_flag = 0;
    } else {
        // 处理真实按键输入
        handle_real_key();
    }
}

这个方法完全利用库的原有逻辑,不会修改程序执行流,但需要硬件支持引脚操作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 17:35:13