【媒体控制器】开源项目学习笔记(基于Arduino Micro开发板)

☑️ 首先说明:本项目基于Arduino Micro 开发板开发的,外设只用到了EC11E1534408无定位旋转编码器。

项目来源:【DIY】自制PC外设-媒体控制器,在英国_哔哩哔哩_bilibili

Github:GitHub - xuan25/HIDMediaController-ArduinoMicroPro


HID

Human interface device(HID),人体学接口设备。

HID 一般指 USB-HID 标准。

在USB协议官网上,给出了HID的文档:HID Usage Tables 1.3

关于键盘的报告描述符、鼠标的报告描述符,可以用官方网站提供的HID描述符工具(HID Descriptor tool)生成;还可以使用现成的报告描述符进行修改;HID协议和用途表文档中,也有很多现成的例子。

✔️ 关于HID的设备描述符,查阅了相关资料,做出总结。因为首次接触,难免会有错误!

    //Button
    0x05, 0x0c,                    // Usage Page (Consumer Devices)
    0x09, 0x01,                    // Usage (Consumer Control)
    
    0xa1, 0x01,                    // Collection (Application)
   
      0x85, 0x04,                    //   REPORT_ID (4)
      0x09, 0xe9,                    //   Usage (Volume Up)
      0x09, 0xea,                    //   Usage (Volume Down)
      0x09, 0xe2,                    //   Usage (Mute)            //静音
      0x09, 0xcd,                    //   Usage (Play)            //播放
      0x09, 0xb5,                    //   Usage (Next)
      0x09, 0xb6,                    //   Usage (Previous)
      0x15, 0x00,                    //   Logical Minimum (0)
      0x25, 0x01,                    //   Logical Maximum (1)
      0x75, 0x01,                    //   Report Size (1)
      0x95, 0x10,                    //   Report Count (10)
      0x81, 0x06,                    //   Input (Data, Variable, Relative)
    
    0xc0,                          // End Collection

描述符没有固定的长度,没有固定的数据类型,而是由条目(item)组成。一个条目占据一行。

短条目(大部分为短条目)构成:一个字节的前缀 + 可选的数据字节

前缀结构:

7 6 5 4 3 2 1 0
bTag bType bSize
  • bTag:表示该条目的功能。
  • bType:表示该条目的类型。0为主条目;1为全局条目;2为局部条目。
  • bSize:表示条目的数据字节数量。0~3分别表示1~4个字节。

主条目(main item)情况

数值 功能 二进制
8 input(输入) 1000 xxxx
9 output(输出) 1001 xxxx
b feature(特性) 1011 xxxx

0xa1

collection(集合) 1010 0001
0xc0 End collection(闭集合) 1100 0000

全局条目(global item)情况

数值 功能
0 Usage Page(用途页)
1 Logical Mini(逻辑最小值)
2 Logical Maxi(逻辑最大值)
3 Physical Mini(物理量最小值)
4 Physical Maxi(物理量最大值)
7 Report Size(数据域大小)
8 Report ID(报告ID)
9 Report Count(数据域数量)

局部条目(local item)情况

数值 功能
0 Usage(用途)
1 Usage Mini(用途最小值)
2 Usage Maxi(用途最大值)

 分析:

0x05, 0x0c,                    // Usage Page (Consumer Devices)
/* 0x05: 0000 0101        表示:用途页,全局条目,数据字节量为2
 * 0x0c: Consumer Page    表示:用户设备
0x09, 0x01,                    // Usage (Consumer Control)
/* 0x09: 0000 1001        表示:用途页,局部条目,数据字节量为2
 * 0x01:Consumer Control 表示:用户控制
0x85, 0x04,                    //   REPORT_ID (4)
/* 0x85:1000 0101        表示:报告ID,全局条目,数据字节量为2
0x09, 0xea,                    //   Usage (Volume Down)
/* 0x09: 0000 1001        表示:用途页,局部条目,数据字节量为2
 * 0xea:Volume Decrement 表示:音量减小
0x81, 0x06,                    //   Input (Data, Variable, Relative)
/* 0x81: 1000 0001        表示:输入,主条目,数据字节量为2

Arduino 规定的报告 ID

Arduino 将报告 ID 有些值设置为固定的。例如:  0:不可用1:鼠标2:键盘

自己编写 HID 设备时,需要设置为其他的 ID。

设置自己想要的报告描述符,可使用辅助软件生成:HID Descriptor Tool | USB-IF

关于 HID 的部分到此为止。后续如果学习到 USB 设备时再看 HID 相关知识吧!


怎么在Arduino上实现HID

翻到了 Keyboard 库和 Mouse 库。

读库文件的同时发现了都引用了 HID.h 文件:#include "HID.h"

可以仿照 Mouse 和 Keyboard 库来写一个自己的 HID 库。猜测原作者也是这么想的。

偶然发现 Arduino 社区的一篇文章,才让我发现本项目原作者是怎么写 HID 库的。

分析 Mouse 库的 HID 描述符:

static const uint8_t _hidReportDescriptor[] PROGMEM = {
  
   //  Mouse
    0x05, 0x01,     // USAGE_PAGE (Generic Desktop) //用途页为通用桌面设备
    0x09, 0x02,     // USAGE (Mouse)                //用途为鼠标
    0xa1, 0x01,     // COLLECTION (Application)     //COLLECTION1
    0x09, 0x01,     // USAGE (Pointer)              //指针设备
    0xa1, 0x00,     // COLLECTION (Physical)        //COLLECTION2
    0x85, 0x01,     // REPORT_ID (1)                //报告ID设置为1
    0x05, 0x09,     // USAGE_PAGE (Button)          //按键设备
    0x19, 0x01,     // USAGE_MINIMUM (Button 1)     //用途最小值1
    0x29, 0x03,     // USAGE_MAXIMUM (Button 3)     //用途最大值3:1,左键;2,右键;3,中键
    0x15, 0x00,     // LOGICAL_MINIMUM (0)          //逻辑最小值0
    0x25, 0x01,     // LOGICAL_MAXIMUM (1)          //逻辑最大值1(是否按下,0-1)
    0x95, 0x03,     // REPORT_COUNT (3)             //报告的数量(三个键)
    0x75, 0x01,     // REPORT_SIZE (1)              //报告的大小(1 bit)
    0x81, 0x02,     // INPUT (Data,Var,Abs)         //输入,属性为变量,数值,绝对值
    0x95, 0x01,     // REPORT_COUNT (1)             //报告的数量
    0x75, 0x05,     // REPORT_SIZE (5)              //报告的大小
    0x81, 0x03,     // INPUT (Cnst,Var,Abs)         //输入,属性为常量0
    0x05, 0x01,     // USAGE_PAGE (Generic Desktop) //用途页为通用桌面
    0x09, 0x30,     // USAGE (X)                    //用途为X轴
    0x09, 0x31,     // USAGE (Y)                    //用途为Y轴
    0x09, 0x38,     // USAGE (Wheel)                //用途为滚轮
    0x15, 0x81,     // LOGICAL_MINIMUM (-127)       //逻辑最小值-127
    0x25, 0x7f,     // LOGICAL_MAXIMUM (127)        //逻辑最大值 127
    0x75, 0x08,     // REPORT_SIZE (8)              //报告的大小
    0x95, 0x03,     // REPORT_COUNT (3)             //报告的数量
    0x81, 0x06,     // INPUT (Data,Var,Rel)         //输入,属性为变量,数值,相对值
    0xc0,           // END_COLLECTION               //END_COLLECTION2
    0xc0,           // END_COLLECTION               //END_COLLECTION1
};

查看 Mouse.cpp 文件,会发现 HID报告描述符 的声明过程如下(在Keyboard.cpp中几乎相同):

Mouse_::Mouse_(void) : _buttons(0)
{
    static HIDSubDescriptor node(_hidReportDescriptor, sizeof(_hidReportDescriptor));
    HID().AppendDescriptor(&node);
}

发送报告的函数:

void Mouse_::move(signed char x, signed char y, signed char wheel)
{
	uint8_t m[4];
	m[0] = _buttons;
	m[1] = x;
	m[2] = y;
	m[3] = wheel;
	HID().SendReport(1,m,4);
}

其中,重要的是最后一个代码

HID().SendReport(uint8_tid, const void* data, int len)  
               //ID         数据              数据长度

项目代码实现

首先看 HID 仿照 Mouse 和 Keyboard 库写的 HIDDevice 库。

HIDDevice相关

HIDDevice.cpp

基本为仿照官方库所写。

#include "HIDDevice.h"
// 自己写的 + Keyboard + Mouse 都用上了
static const uint8_t HIDDevice::_hidReportDescriptor[] PROGMEM = {
   //  Button
    0x05, 0x0c,                    // Usage Page (Consumer Devices)
    0x09, 0x01,                    // Usage (Consumer Control)
    0xa1, 0x01,                    // Collection (Application)
    0x85, 0x04,                    //   REPORT_ID (4)
    0x09, 0xe9,                    //   Usage (Volume Up)
    0x09, 0xea,                    //   Usage (Volume Down)
    0x09, 0xe2,                    //   Usage (Mute)
    0x09, 0xcd,                    //   Usage (Play)
    0x09, 0xb5,                    //   Usage (Next)
    0x09, 0xb6,                    //   Usage (Previous)
    0x15, 0x00,                    //   Logical Minimum (0)
    0x25, 0x01,                    //   Logical Maximum (1)
    0x75, 0x01,                    //   Report Size (1)
    0x95, 0x10,                    //   Report Count (10)
    0x81, 0x06,                    //   Input (Data, Variable, Relative)
    0xc0,                          // End Collection
   //  Keyboard
    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)  // 47
    0x09, 0x06,                    // USAGE (Keyboard)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x85, 0x02,                    //   REPORT_ID (2)
    0x05, 0x07,                    //   USAGE_PAGE (Keyboard)
    0x19, 0xe0,                    //   USAGE_MINIMUM (Keyboard LeftControl)
    0x29, 0xe7,                    //   USAGE_MAXIMUM (Keyboard Right GUI)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
    0x75, 0x01,                    //   REPORT_SIZE (1)
    0x95, 0x08,                    //   REPORT_COUNT (8)
    0x81, 0x02,                    //   INPUT (Data,Var,Abs)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x81, 0x03,                    //   INPUT (Cnst,Var,Abs)
    0x95, 0x06,                    //   REPORT_COUNT (6)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x65,                    //   LOGICAL_MAXIMUM (101)
    0x05, 0x07,                    //   USAGE_PAGE (Keyboard) 
    0x19, 0x00,                    //   USAGE_MINIMUM (Reserved (no event indicated))
    0x29, 0x65,                    //   USAGE_MAXIMUM (Keyboard Application)
    0x81, 0x00,                    //   INPUT (Data,Ary,Abs)
    0xc0,                          // END_COLLECTION
    //  Mouse
    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)  // 54
    0x09, 0x02,                    // USAGE (Mouse)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x09, 0x01,                    //   USAGE (Pointer)
    0xa1, 0x00,                    //   COLLECTION (Physical)
    0x85, 0x01,                    //     REPORT_ID (1)
    0x05, 0x09,                    //     USAGE_PAGE (Button)
    0x19, 0x01,                    //     USAGE_MINIMUM (Button 1)
    0x29, 0x03,                    //     USAGE_MAXIMUM (Button 3)
    0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)
    0x95, 0x03,                    //     REPORT_COUNT (3)
    0x75, 0x01,                    //     REPORT_SIZE (1)
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
    0x95, 0x01,                    //     REPORT_COUNT (1)
    0x75, 0x05,                    //     REPORT_SIZE (5)
    0x81, 0x03,                    //     INPUT (Cnst,Var,Abs)
    0x05, 0x01,                    //     USAGE_PAGE (Generic Desktop)
    0x09, 0x30,                    //     USAGE (X)
    0x09, 0x31,                    //     USAGE (Y)
    0x09, 0x38,                    //     USAGE (Wheel)
    0x15, 0x81,                    //     LOGICAL_MINIMUM (-127)
    0x25, 0x7f,                    //     LOGICAL_MAXIMUM (127)
    0x75, 0x08,                    //     REPORT_SIZE (8)
    0x95, 0x03,                    //     REPORT_COUNT (3)
    0x81, 0x06,                    //     INPUT (Data,Var,Rel)
    0xc0,                          //     END_COLLECTION
    0xc0,                          //     END_COLLECTION
     
};

HIDDevice::HIDDevice(){  
}
// 声明报告描述符
void HIDDevice::begin(){
  static HIDSubDescriptor node(_hidReportDescriptor, sizeof(_hidReportDescriptor));
  HID().AppendDescriptor(&node);
}
// 媒体控制发送报告
void HIDDevice::mediaControl(uint8_t c){
  uint8_t m[2] = {c, 0};
  HID().SendReport(4,m,2);
}
// 键盘控制发送报告
void HIDDevice::keyEvent(uint8_t modifiers, uint8_t key1, uint8_t key2, uint8_t key3, uint8_t key4, uint8_t key5, uint8_t key6){
  uint8_t m[8] = {modifiers, 0, key1, key2, key3, key4, key5, key6};
  HID().SendReport(2,m,8);
}
// 鼠标控制发送报告
void HIDDevice::mouseEvent(uint8_t buttons, uint8_t x, uint8_t y, uint8_t wheel){
  uint8_t m[4] = {buttons, x, y, wheel};
  HID().SendReport(1,m,4);
}

HIDDevice.h

#ifndef HIDDevice_H
#define HIDDevice_H

#include "arduino.h"
#include <HID.h>

#define VOLUME_INCREMENT 0x01
#define VOLUME_DECREMENT 0x02
#define MUTE 0x04
#define PLAY_PAUSE 0x08
#define NEXT 0x10
#define PREVIOUS 0x20

#define MOUSE_LEFT 0x00
#define MOUSE_RIGHT 0x01
#define MOUSE_MIDDLE 0x04


#define KEY_LEFT_CTRL 1<<0x00
#define KEY_LEFT_SHIFT 1<<0x01
#define KEY_LEFT_ALT 1<<0x02
#define KEY_LEFT_GUI 1<<0x03
#define KEY_RIGHT_CTRL 1<<0x04
#define KEY_RIGHT_SHIFT 1<<0x05
#define KEY_RIGHT_ALT 1<<0x06
#define KEY_RIGHT_GUI 1<<0x07

#define KEY_UP_ARROW    0x52
#define KEY_DOWN_ARROW    0x51
#define KEY_LEFT_ARROW    0x50
#define KEY_RIGHT_ARROW   0x4f
#define KEY_BACKSPACE   0x2a
#define KEY_TAB       0x2b
#define KEY_RETURN      0x28
#define KEY_ESC       0x29
#define KEY_INSERT      0x49
#define KEY_DELETE      0x4c
#define KEY_PAGE_UP     0x4b
#define KEY_PAGE_DOWN   0x4e
#define KEY_HOME      0x4a
#define KEY_END       0x4d
#define KEY_CAPS_LOCK   0x39
#define KEY_F1        0x3a
#define KEY_F2        0x3b
#define KEY_F3        0x3c
#define KEY_F4        0x3d
#define KEY_F5        0x3e
#define KEY_F6        0x3f
#define KEY_F7        0x40
#define KEY_F8        0x41
#define KEY_F9        0x42
#define KEY_F10       0x43
#define KEY_F11       0x44
#define KEY_F12       0x45

#define KEY_A    0x04
#define KEY_B    0x05
#define KEY_C    0x06
#define KEY_D    0x07
#define KEY_E    0x08
#define KEY_F    0x09
#define KEY_G    0x0a
#define KEY_H    0x0b
#define KEY_I    0x0c
#define KEY_J    0x0d
#define KEY_K    0x0e
#define KEY_L    0x0f
#define KEY_M    0x10
#define KEY_N    0x11
#define KEY_O    0x12
#define KEY_P    0x13
#define KEY_Q    0x14
#define KEY_R    0x15
#define KEY_S    0x16
#define KEY_T    0x17
#define KEY_U    0x18
#define KEY_V    0x19
#define KEY_W    0x1a
#define KEY_X    0x1b
#define KEY_Y    0x1c
#define KEY_Z    0x1d

class HIDDevice{
  private:static const uint8_t _hidReportDescriptor[] PROGMEM; 

  public:HIDDevice();
  public:void begin();
  public:void mediaControl(uint8_t c);
  public:void keyEvent(uint8_t modifiers, uint8_t key1, uint8_t key2, uint8_t key3, uint8_t key4, uint8_t key5, uint8_t key6);
  public:void mouseEvent(uint8_t buttons, uint8_t x, uint8_t y, uint8_t wheel);
};

#endif

其余就是检测输入的问题了。


检测输入

EC11介绍

A、B、C 用来检测旋钮转动方向。D、E 相当于普通按键。

EC11判断方法:左转:逆时针;右转:顺时针

当C接地时!AB之间的电平状态关系

逆时针(左转) 11、01、00、10
顺时针(右转) 11、10、00、01

判断方法:

A信号位于下降沿时,B是低电平,表示顺时针。B是高电平,表示逆时针。

同理:

B信号位于下降沿时,A是高电平,表示顺时针。A是低电平,表示逆时针。

Encoder.cpp(和检测A、B、C相关)

#include "Encoder.h"

int prePinA = 0;
int prePinB = 0;
int preDire = 0;
int a,b,c;

//注册 ABC引脚
Encoder::Encoder(int aPin, int bPin, int cPin){
    a=aPin;
    b=bPin;
    c=cPin;
}

//初始化C为低电平,A、B为输入端口
void Encoder::begin(){
    pinMode(c,OUTPUT);
    digitalWrite(c,LOW);

    pinMode(a,INPUT_PULLUP);
    pinMode(b,INPUT_PULLUP);

    prePinA = digitalRead(a);    //检测电平
    prePinB = digitalRead(b);
}

int Encoder::nextFrame(){
    int pinA = digitalRead(a);
    int pinB = digitalRead(b);
    int result = 0;

    //通过下一帧检测正反转
    if(pinA > prePinA)    //a up
    {
      if(pinB == 1) result = -1;//b high
      else          result = 1;//b low
    }
    else if(pinA < prePinA)    //a down
    {
      if(pinB == 1) result = 1;//b high
      else          result = -1;//b low
    }
    else if(pinB > prePinB)     //b up
    {
      if(pinA == 1) result = 1;//a high
      else          result = -1;//a low
    }
    else if(pinB < prePinB)    //b down
    {
      if(pinA == 1) result = -1;//a high
      else          result = 1;//a low
    }
    prePinA = pinA;
    prePinB = pinB;
    return result;
}
// 是再确认一次还是两次动作一次操作呢?
int Encoder::refresh(){
    int dire = nextFrame();
    if(dire == 1)
    {
      if(preDire != 1)
      {
        preDire = 1;
        return 0;
      }
      else    return 1;
    }
    else if(dire == -1)
    {
      if(preDire != -1){
        preDire = -1;
        return 0;
      }
      else    return -1;
    }
    return 0;
}

Encoder.h(和检测A、B、C相关)

#ifndef Encoder_H
#define Encoder_H

#include "arduino.h"

class Encoder{
  private:int prePinA;
  private:int prePinB;
  private:int preDire;
  private:int a,b,c;
  private:int nextFrame();

  public:Encoder(int aPin, int bPin, int cPin);
  public:void begin();
  public:int refresh();
};
#endif

MediaButton.cpp(和检测D、E相关)

#include "MediaButton.h"

int d,e;
// 注册 DE 引脚
MediaButton::MediaButton(int dPin, int ePin){
  d = dPin;
  e = ePin;
}
// 初始化引脚: d为低电平,e为检测口
void MediaButton::begin(){
  pinMode(d, OUTPUT);
  digitalWrite(d, LOW);
  pinMode(e, INPUT_PULLUP);
}

void MediaButton::refresh(HIDDevice hid1)
{
  if(!digitalRead(e))    //判断按键是否按下,如果按下 digitalRead(e) = 0;
  {
    int i = 0;
    /* 按钮长按实现播放上一曲功能 */
    while(!digitalRead(e))    //按下循环
    {
      delay(10);
      i++;
      if(i>50)    //按下超时(上一首)
      {
        hid1.mediaControl(PREVIOUS);    //#按钮长按保持#
        hid1.mediaControl(0);
        i = 0;
        while(!digitalRead(e))
        {
          if(i>1)
          {
            hid1.mediaControl(PREVIOUS);    //#按钮长按保持循环#
            hid1.mediaControl(0);
          }
          delay(500);
          i++;
        }
        return;
      }
    }
    i = 0;
    /* 按钮短按实现暂停播放功能 */
    while(digitalRead(e))    //释放循环
    {
      delay(10);
      i++;
      if(i>50)    //释放超时(暂停/播放)
      {
        hid1.mediaControl(PLAY_PAUSE);//#按钮短按#
        hid1.mediaControl(0);
        return;
      }
    }
    i = 0;
    /* 按钮双击长按实现播放下一曲功能 */
    while(!digitalRead(e))    //按下循环2
    {
      delay(10);
      i++;
      if(i>50)    //按下超时(下一首)
      {
        hid1.mediaControl(NEXT);    //#按钮双击保持#
        hid1.mediaControl(0);
        i = 0;
        while(!digitalRead(e))
        {
          if(i>1)
          {
            hid1.mediaControl(NEXT);    //#按钮双击保持循环#
            hid1.mediaControl(0);
          }
          delay(500);
          i++;
        }
        return;
      }
    }
    i = 0;  
    /* 按钮双击实现播放下一曲功能 */
    while(digitalRead(e))    //释放循环2
    {
      delay(10);
      i++;
      if(i>50)    //释放超时(下一首)
      {
        hid1.mediaControl(NEXT);    //#按钮双击#
        hid1.mediaControl(0);
        return;
      }
    }
    i = 0;  
    /* 按钮三击长按实现收藏歌曲功能 */
    while(!digitalRead(e))    //按下循环3
    {
      delay(10);
      i++; 
      if(i>50)    //按下超时(喜欢)
      {
        hid1.keyEvent(KEY_LEFT_CTRL | KEY_LEFT_ALT, KEY_L, 0, 0, 0, 0, 0);    //#按钮三击保持#
        hid1.keyEvent(0, 0, 0, 0, 0, 0, 0);
        while(!digitalRead(e))
        {
          delay(500);    //#按钮三击保持循环#
        }
        return;
      }
    }
    hid1.keyEvent(KEY_LEFT_CTRL | KEY_LEFT_ALT, KEY_L, 0, 0, 0, 0, 0);    //#按钮三击#
    hid1.keyEvent(0, 0, 0, 0, 0, 0, 0);
    return;
  }
}

这个实现方式挺有意思的,画个流程图吧

MediaButton.cpp(和检测D、E相关)

#ifndef MediaButton_H
#define MediaButton_H

#include "arduino.h"
#include "HIDDevice.h"

class MediaButton{
  private:int d,e;
  
  public:MediaButton(int dPin, int ePin);
  public:void begin();
  public:void refresh(HIDDevice hid1);
};

#endif

主函数

#include "Encoder.h"
#include "MediaButton.h"
#include "HIDDevice.h"

static int a=8,b=6,c=7,d=16,e=15;    //EC11引脚定义

Encoder enc1(a,b,c);        //创建实例
MediaButton btn1(d,e);
HIDDevice hid1;

void setup()     //初始化
{
  enc1.begin();
  btn1.begin();
  hid1.begin();
}

int total = 0;
int threshold = 5;

void loop() 
{
  int re = enc1.refresh();    //检测正反转
  total += re;    //正反转抵消问题
  if(total == threshold)
  {
    //#编码器正转#
    hid1.mediaControl(VOLUME_INCREMENT);    //增加音量
    hid1.mediaControl(0);
    total = 0;
  }
  else if(total == -threshold)
  {
    //#编码器反转#
    hid1.mediaControl(VOLUME_DECREMENT);    //减少音量
    hid1.mediaControl(0);
    total = 0;
  }

  btn1.refresh(hid1);    //检测按键
}

总结

把项目的资源百度网盘放在这里了,做个备份。

下载项目最好还是访问文章最前方的 GitHub 链接。

链接:https://pan.baidu.com/s/1nXlvSr33GzwCzQ4frGQogg
提取码:o8r7

参考:

HID设备描述符的解析

USB_HID协议基础

利用Arduino制作自己的usb hid设备


日常总结啦 ~

C++ 还是不太熟悉。但是这次学会了 带参数创建实例的方法,也可以啦 ~

Encoder enc1(a,b,c);        
MediaButton btn1(d,e);
HIDDevice hid1;

共同进步啊 ~ 小伙伴们 ~

猜你喜欢

转载自blog.csdn.net/qq_41650023/article/details/125806026
今日推荐