新版本高效跳闪-1

75 2025-08-22 08:55

单次按动版本

#include <Arduino.h>
#include <BleKeyboard.h>

#define PIN_BTN   4          // 按键 → GND(下降沿触发)
#define PIN_LED   16

BleKeyboard bleKeyboard("21's Knob", "luowei", 88);

// 按键消息队列
static QueueHandle_t btnQueue;

// 中断回调(Arduino 标准写法,不需要 attachInterruptArg)
void IRAM_ATTR onBtn() {
  static uint32_t last = 0;
  uint32_t now = xTaskGetTickCountFromISR();
  if (now - last < 2) return;   // 2 tick ≈ 2 ms 防抖
  last = now;
  uint8_t dummy = 1;
  xQueueSendFromISR(btnQueue, &dummy, NULL);
}

void setup() {
  pinMode(PIN_BTN, INPUT_PULLUP); 
  pinMode(PIN_LED, OUTPUT);
  attachInterrupt(digitalPinToInterrupt(PIN_BTN), onBtn, FALLING);

  btnQueue = xQueueCreate(8, sizeof(uint8_t));

  // NimBLE 已在库内启用,无需手动设置连接间隔;
  bleKeyboard.begin();
}

void loop() {
  uint8_t dummy;
  if (xQueueReceive(btnQueue, &dummy, portMAX_DELAY) == pdTRUE) {
    if (bleKeyboard.isConnected()) {
      bleKeyboard.write(KEY_LEFT_CTRL);
      delay(30); // 36跳得高
      bleKeyboard.print(" ");
    }
    digitalWrite(PIN_LED, HIGH);
    vTaskDelay(pdMS_TO_TICKS(50));
    digitalWrite(PIN_LED, LOW);
  }
}

 

}这段程序引入队列有什么好处?是否必要?

 

在这段程序里引入 FreeRTOS 队列(btnQueue)的主要目的是把中断里的“事件”转发给 loop() 上下文,从而避免在中断里直接调用任何可能阻塞、耗时或引发竞态的代码。好处可以概括为:
  1. 解耦中断与业务逻辑
    中断只负责“通知”发生了按键事件,不直接执行 BleKeyboard.write() 等耗时操作;真正的按键处理放在 loop(),既简洁又安全。
  2. 避免在中断里使用非 ISR 安全函数
    BleKeyboard.write() 最终会调用 NimBLE 的 API,这些 API 不是为中断环境设计的;放在中断里运行可能会触发断言或产生不可预期的行为。
  3. 自带消抖与缓存
    如果按键机械抖动导致中断多次触发,队列可以把这些事件暂存起来,随后由 loop() 按顺序逐一处理(虽然示例里中断里已经有 2 tick 去抖,但仍可防极端情况)。
  4. 便于后续扩展
    以后如果需要在按键事件中附带更多信息(单击/双击/长按类型、时间戳等),直接改成传结构体即可;而不用把更多变量塞进全局、再加 volatile 保护。
是否“必要”?
  • 从“代码能跑”角度:不是绝对必要。把 BleKeyboard.write() 直接放进中断,大多数场景也能“看起来”正常工作,因为一次按键中断里只发两个 HID 报文,耗时 <1 ms,ISR 结束前 BLE 协议栈大概率还没机会触发更高优先级中断。
  • 从“稳健、可维护”角度:非常推荐。一旦以后增加更多逻辑(比如连发、组合键、长按检测、低功耗休眠等),直接在中断里写代码会变得脆弱,队列/事件机制能让代码结构清晰且易于扩展。
一句话总结
队列不是“不写就烧不进去”的必要,而是“想写得稳健、优雅、可扩展”的必要。

 

全部评论

·