如何在Digispark(类Arduino)上检测F15按键是否被按住
解决Digispark检测主机按键状态的问题
首先得明确一个关键限制:Digispark作为USB HID从设备(模拟键盘/鼠标),它只能向主机发送按键/鼠标事件,没办法主动读取主机上的按键状态——USB HID的基础通信是单向的(设备→主机),除非你把它配置成支持双向通信的自定义HID设备。你想用F15作为程序运行的状态标识,这个思路本身没法直接实现,因为Digispark根本“看不到”主机上F15的按下状态。
不过没关系,我们有两个可行的替代方案,既能满足你的需求,又不需要复杂的硬件改动:
方案1:自定义双向HID通信(推荐,最可靠)
这个方案让Digispark支持接收来自C#程序的状态指令,彻底解决通信问题:
步骤1:修改Digispark的USB固件
你需要修改Digispark的USB描述符,让它支持自定义HID报告(实现双向通信)。找到Digispark核心库的usbconfig.h文件,替换HID报告描述符为:
#define USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH 34 const PROGMEM char usbHidReportDescriptor[USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH] = { 0x06, 0x00, 0xFF, // 自定义厂商使用页 0x09, 0x01, // 自定义使用项 0xA1, 0x01, // 应用集合 0x15, 0x00, // 逻辑最小值0 0x26, 0xFF, 0x00, // 逻辑最大值255 0x75, 0x08, // 报告长度8位 0x95, 0x01, // 报告数量1 0x09, 0x01, // 自定义使用项 0x81, 0x02, // 输入(数据、可变、绝对) 0x09, 0x01, // 自定义使用项 0x91, 0x02, // 输出(数据、可变、绝对) 0xC0 // 结束集合 };
步骤2:Digispark端代码
这段代码会监听来自C#程序的状态指令,根据指令切换旋转编码器的功能:
#include <DigiKeyboard.h> // 旋转编码器引脚(根据你的接线调整) #define ENC_A 2 #define ENC_B 3 bool programRunning = false; int lastEncA = HIGH; void setup() { pinMode(ENC_A, INPUT_PULLUP); pinMode(ENC_B, INPUT_PULLUP); lastEncA = digitalRead(ENC_A); } void loop() { DigiKeyboard.update(); // 必须调用,才能接收主机的HID报告 // 读取C#程序发送的状态(0x01=程序运行,0x00=未运行) uint8_t status = DigiKeyboard.getHIDReport(0); programRunning = (status == 0x01); // 处理旋转编码器 int currentEncA = digitalRead(ENC_A); if (currentEncA != lastEncA) { if (digitalRead(ENC_B) != currentEncA) { // 顺时针旋转 if (programRunning) { DigiKeyboard.sendKeyStroke(KEY_F13); } else { DigiKeyboard.sendKeyStroke(0, MODIFIERKEY_NONE, MEDIA_VOLUME_UP); } } else { // 逆时针旋转 if (programRunning) { DigiKeyboard.sendKeyStroke(KEY_F14); } else { DigiKeyboard.sendKeyStroke(0, MODIFIERKEY_NONE, MEDIA_VOLUME_DOWN); } } lastEncA = currentEncA; } delay(20); // 防抖 }
步骤3:C#程序端代码
用HidLibrary库(NuGet可以安装)向Digispark发送状态指令:
using HidLibrary; using System.Linq; class VolumeControllerBridge { static void Main(string[] args) { // 匹配Digispark的VID和PID(默认是0x16C0和0x05DF) var digispark = HidDevices.Enumerate(0x16C0, 0x05DF).FirstOrDefault(); if (digispark == null) { System.Console.WriteLine("未找到Digispark设备!"); return; } digispark.Open(); System.Console.WriteLine("程序已运行,按任意键退出..."); // 发送"程序运行"状态 byte[] report = new byte[2]; // 第一个字节是报告ID,固定为0 report[1] = 0x01; digispark.Write(report); System.Console.ReadKey(); // 发送"程序退出"状态 report[1] = 0x00; digispark.Write(report); digispark.Close(); } }
方案2:利用键盘LED状态(无需修改固件,简单快速)
如果不想折腾自定义HID,可以用主机的键盘LED(比如Caps Lock)作为状态标识:
- C#程序运行时,自动开启Caps Lock;退出时关闭。
- Digispark通过读取Caps Lock的LED状态,判断程序是否在运行。
Digispark端代码
#include <DigiKeyboard.h> #define ENC_A 2 #define ENC_B 3 bool programRunning = false; int lastEncA = HIGH; void setup() { pinMode(ENC_A, INPUT_PULLUP); pinMode(ENC_B, INPUT_PULLUP); lastEncA = digitalRead(ENC_A); } void loop() { DigiKeyboard.update(); // 读取Caps Lock LED状态(0x02对应Caps Lock) programRunning = (DigiKeyboard.getLEDs() & 0x02) != 0; // 旋转编码器处理逻辑 int currentEncA = digitalRead(ENC_A); if (currentEncA != lastEncA) { if (digitalRead(ENC_B) != currentEncA) { programRunning ? DigiKeyboard.sendKeyStroke(KEY_F13) : DigiKeyboard.sendKeyStroke(0, MODIFIERKEY_NONE, MEDIA_VOLUME_UP); } else { programRunning ? DigiKeyboard.sendKeyStroke(KEY_F14) : DigiKeyboard.sendKeyStroke(0, MODIFIERKEY_NONE, MEDIA_VOLUME_DOWN); } lastEncA = currentEncA; } delay(20); }
C#程序端代码
using System.Windows.Forms; // 需要引用System.Windows.Forms class VolumeControllerBridge { static void Main(string[] args) { bool originalCapsState = Control.IsKeyLocked(Keys.CapsLock); // 开启Caps Lock作为状态标识 if (!originalCapsState) { SendKeys.SendWait("{CAPSLOCK}"); } System.Console.WriteLine("程序已运行,按任意键退出..."); System.Console.ReadKey(); // 恢复原始Caps Lock状态 if (!originalCapsState) { SendKeys.SendWait("{CAPSLOCK}"); } } }
注意事项
- 方案1需要重新上传Digispark固件,可能需要重新安装驱动,但通信最可靠,不会影响用户的日常操作。
- 方案2简单,但会占用Caps Lock,可能会给用户带来小困扰,适合临时测试或个人使用。
- 旋转编码器的防抖逻辑可以根据实际情况调整延迟时间。
内容的提问来源于stack exchange,提问作者SenaCarbtc
相关产品推荐
相关产品推荐

