如何在不修改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()的死循环:
- 定义全局跳转缓冲区,保存调用
waitPress()前的程序上下文 - 在调用
waitPress()前用setjmp()记录当前执行位置 - 在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()自然退出:
- 选择键盘矩阵中一个闲置引脚,或复用现有按键引脚
- 在ISR中短暂切换引脚电平,模拟按键按下,让
scan()检测到输入 - 退出
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
相关产品推荐
相关产品推荐

