[单片机芯片] 基于RISC-V CH32V103的鼠标键盘摇杆手柄Joystick学习开发

[复制链接]
 楼主| lilijin1995 发表于 2022-3-30 11:21 | 显示全部楼层 |阅读模式
本帖最后由 lilijin1995 于 2022-6-7 16:29 编辑

#申请原创#   @21小跑堂 @21小跑堂 @21小跑堂
基于RISC-V CH32V103的鼠标键盘摇杆手柄Joystick学习开发



资料还在持续更新中,评估学习板已经上架,欢迎入手体验,

链接:https://pan.baidu.com/s/1418gLVJf8eAh52CYGSNUaQ?pwd=fkr6
提取码:fkr6
入手链接:https://item.taobao.com/item.htm?ft=t&id=632428000614
在线文档:https://www.cnblogs.com/Li-Share/p/15898572.html
B站视频教程:https://www.bilibili.com/video/BV17r4y1z758/
第一部分、硬件概述

1.1 实物概图
1966993-20220217232157176-1998694330.jpg
如上图所示,配置了8个6*6轻触按键,一个摇杆(Joystick),搭载一颗WS2812B灯珠;
并将UART1串口,编程接口(SWD),外接Joystick接口,microUSB接口引出;
左边是RKJXV1224005摇杆电位器,右边和下方是8颗66的轻触按键,右上方是5050封装的WS2812B灯珠,中间是microusb母座,
H3是SWD烧录接口,烧录程序接口,H2是串口,H1是外接摇杆模块的接口;

1.2 Gamepad原理图

Gamepad原理图如图1.2所示,如看不清可打开Doc目录下的PDF文档查阅
   1966993-20220217233348812-1046753294.jpg
第二部分、软件工具
2.1 软件概述


在 /Software 目录下是常用的工具软件:
  1. Dt2_4:配置USB设备Report描述符的工具;
  2. USBHID调试助手/呀呀USB: USB调试工具,相当于串口调试助手功能;
  3. BUSHound:总线调试工具;
  4. USBlyzer:一款专业的USB协议分析软件
  5. MounRiver: 编译器;2.2 MounRiver软件入门
大家访问以下链接:
http://mounriver.com/help

第三部分、实战训练3.1

实例Eg1_GamePad
本节我们目标是实现GamePad的功能,枚举成XY轴的平面坐标和8个按键的USB HID类设备。  
3.1.1硬件设计
    1966993-20211214224348752-1607121797.png
如上图是Joystick原理图,其中VRX1与VRY1是摇杆的电位器输出的电压信号(ADC检测);
SW1则是按键,右侧H1是外接的Joystick口,供接joystick模块使用;
   1966993-20211214224447009-112297073.png
如上图是KEY原理图,我们只要配置8个GPIO作为输入去检测按键信号;  

3.1.2 软件设计

首先是工程树,我们打开工程,可以看到Project Explorer下Gamepad目录如下图
1966993-20220220194934598-1178092890.png
其中
  • Binaries: 二进制文件;
  • Includes: 包含的头文件;
  • Core:内核文件,存放core_riscv内核文件;
  • Debug: 存放串口打印和延迟函数相关的文件;
  • myBSP: 我们自己编写的驱动文件;
  • obj: 编译的生成的obj文件;        
  • Peripheral: 这是MCU厂商提供外设相关驱动;
  • Startup: ch32v103的启动文件;
  • User: ch32v103的配置文件,中断相关文件,main函数等;


工程目录这里只做一次介绍,后面的样例目录大同小异。我们先打开startup_ch32v10x.S启动文件,我们看到如下代码
  jal  SystemInit
    la t0, main
定位到SystemInit
  1. <font face="仿宋, 仿宋_GB2312">void SystemInit (void)
  2. {
  3.   RCC->CTLR |= (uint32_t)0x00000001;
  4.   RCC->CFGR0 &= (uint32_t)0xF8FF0000;
  5.   RCC->CTLR &= (uint32_t)0xFEF6FFFF;
  6.   RCC->CTLR &= (uint32_t)0xFFFBFFFF;
  7.   RCC->CFGR0 &= (uint32_t)0xFF80FFFF;
  8.   RCC->INTR = 0x009F0000;   
  9.   SetSysClock();
  10. }</font>

关于RCC寄存器的配置,请各位自行查阅用户手册;我们接着打开SetSysClock函数
  1. static void SetSysClock(void)
  2. {
  3. #ifdef SYSCLK_FREQ_HSE
  4.   SetSysClockToHSE();
  5. #elif defined SYSCLK_FREQ_24MHz
  6.   SetSysClockTo24();
  7. #elif defined SYSCLK_FREQ_48MHz
  8.   SetSysClockTo48();
  9. #elif defined SYSCLK_FREQ_56MHz
  10.   SetSysClockTo56();  
  11. #elif defined SYSCLK_FREQ_72MHz
  12.   SetSysClockTo72();
  13. #endif

  14. /* If none of the define above is enabled, the HSI is used as System clock
  15.   * source (default after reset)
  16.     */
  17. }</font>

由于我们定义了SYSCLK_FREQ_72MHz,SetSysClockTo72这个函数设置了系统时钟为72M;
接下来我们来看看main函数,如下
  1. int main(void)
  2. {
  3.     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  4.     Delay_Init();
  5.     USART_Printf_Init(115200);

  6.     printf("SystemClk:%d\r\n",SystemCoreClock);
  7.     printf("USBHD Device Test\r\n");

  8.     pEP0_RAM_Addr = EP0_Databuf;
  9.     pEP1_RAM_Addr = EP1_Databuf;
  10.     pEP2_RAM_Addr = EP2_Databuf;

  11.     USBHD_ClockCmd(RCC_USBCLKSource_PLLCLK_1Div5,ENABLE);
  12.     USB_DeviceInit();
  13.     NVIC_EnableIRQ( USBHD_IRQn );
  14.     ADC_DMA_CONF();
  15.     KEY_INIT();

  16.     while(1)
  17.     {
  18.         printf("X=%d,Y=%d\r\n",ADC_ConvertedValue[0],ADC_ConvertedValue[1]);
  19.         if(Ready)
  20.         {
  21.             Gp_SendReport();
  22.         }
  23.     }
  24. }</font>

NVIC_PriorityGroupConfig是配置优先级分组的,Delay_Init初始化延迟函数;USART2_Printf_Init初始化串口打印,
pEP0_RAM_Addr = EP0_Databuf;
pEP1_RAM_Addr = EP1_Databuf;
pEP2_RAM_Addr = EP2_Databuf;
主要配置端点0~2的缓存Ram;
USBHD_ClockCmd(RCC_USBCLKSource_PLLCLK_1Div5,ENABLE);配置系统时钟1.5分频,即48M;
USB_DeviceInit(),是对usb设备进行初始化;
ADC_DMA_CONF主要对ADC DMA进行配置;
KEY_INIT是对按键所对应的GPIO进行初始化;
接着是Gp_SendReport,主要是讲处理并上报坐标和按键数据;
  1. void Gp_SendReport(void)
  2. {   
  3.     memset(Joystick_Buf,0,3);
  4.     Ytemp=ADC_ConvertedValue[0];
  5.     Xtemp=ADC_ConvertedValue[1];
  6.     if(Xtemp>Xmax)
  7.         Xtemp=Xmax;
  8.     if(Xtemp<Xmin)
  9.         Xtemp=Xmin;
  10.     if(Ytemp>=Ymax)
  11.         Ytemp=Ymax;
  12.     if(Ytemp<=Ymin)
  13.         Ytemp=Ymin;
  14.     printf("Xmax=%x,Xcen=%x,Xmin=%x\r\n",Xmax,Xtemp,Xmin);
  15.     printf("Ymax=%x,Ycen=%x,Ymin=%x\r\n",Ymax,Ytemp,Ymin);
  16.     //根据坐标极点确定坐标(两点直线方程)
  17.     X=((Xtemp-Xmin)*255)/(Xmax-Xmin);
  18.     Y=((Ytemp-Ymin)*255)/(Ymax-Ymin);
  19.     Joystick_Buf[0]=X;
  20.     Joystick_Buf[1]=Y;
  21.     Joystick_Buf[2]=Key_Scan();
  22.     Delay_Ms(10);
  23.     while( Endp1Busy )//如果忙(上一包数据没有传上去),则等待。
  24.     { ; }
  25.     Endp1Busy = 1;                                      //设置为忙状态
  26.     memcpy(pEP1_IN_DataBuf, Joystick_Buf, 3);
  27.     DevEP1_IN_Deal(3);
  28. }   </font>

最后我们再来看看USBHD_IRQHandler,我们在这个函数值调用了USB_DevTransProcess。
3.1.3 下载验证


我们把固件程序下载进去可以,打开“设备与打印机”可以看到USB设备枚举成了一个Gamepad,如下图。
   1966993-20220322204154611-1542025732.png
右键打开游戏控制器后,点击属性得到下图所示界面
   1966993-20220322204210677-159114354.png
我们可以摇Joystick和按按键可以发现上图游戏控制器界面也跟着响应。

3.2 实例Eg2_Mouse

本节我们目标是实现模拟鼠标的功能,枚举一个具有XY,左右中键以及滚轮上下的功能;
3.2.1硬件设计

同上一章节

3.2.2 软件设计

在上一章节的基础上,我们在USB_DevTransProcess中找到报告描述符的获取,并修改为如下内容
  1. case USB_DESCR_TYP_REPORT:
  2. if(((pSetupReqPak->wIndex)&0xff) == 0)     //接口0报表描述符
  3. {
  4.     pDescr = MouseRepDesc;                      //数据准备上传
  5.     len = sizeof(MouseRepDesc);
  6.     Ready = 1;                                  //如果有更多接口,该标准位应该在最后一个接口配置完成后有效
  7. }
  8. else len = 0xff;                                //本程序只有2个接口,这句话正常不可能执行
  9. break;</font>

另外鼠标的报告描述符MouseRepDesc如下
  1. const UINT8  MouseRepDesc[]=
  2. {
  3.             0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
  4.             0x09, 0x02,                    // USAGE (Mouse)
  5.             0xa1, 0x01,                    // COLLECTION (Application)
  6.             0x09, 0x01,                    //   USAGE (Pointer)
  7.             0xa1, 0x00,                    //   COLLECTION (Physical)
  8.             0x05, 0x09,                    //     USAGE_PAGE (Button)
  9.             0x19, 0x01,                    //     USAGE_MINIMUM (Button 1)
  10.             0x29, 0x03,                    //     USAGE_MAXIMUM (Button 3)
  11.             0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
  12.             0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)
  13.             0x95, 0x03,                    //     REPORT_COUNT (3)
  14.             0x75, 0x01,                    //     REPORT_SIZE (1)
  15.             0x81, 0x02,                    //     INPUT (Data,Var,Abs)
  16.             0x95, 0x01,                    //     REPORT_COUNT (1)
  17.             0x75, 0x05,                    //     REPORT_SIZE (5)
  18.             0x81, 0x03,                    //     INPUT (Cnst,Var,Abs)
  19.             0x05, 0x01,                    //     USAGE_PAGE (Generic Desktop)
  20.             0x09, 0x30,                    //     USAGE (X)
  21.             0x09, 0x31,                    //     USAGE (Y)
  22.             0x09, 0x38,                    //     USAGE (Wheel)
  23.             0x15, 0x81,                    //     LOGICAL_MINIMUM (-127)
  24.             0x25, 0x7f,                    //     LOGICAL_MAXIMUM (127)
  25.             0x75, 0x08,                    //     REPORT_SIZE (8)
  26.             0x95, 0x03,                    //     REPORT_COUNT (3)
  27.             0x81, 0x06,                    //     INPUT (Data,Var,Rel)
  28.             0xc0,                          //     END_COLLECTION
  29.             0xc0                           // END_COLLECTION
  30. };</font>

然后是报文数据的处理如下:
  1. //处理并上报数据
  2. void Gp_SendReport(void)
  3. {   
  4.     memset(Joystick_Buf,0,4);


  5.     Ytemp=ADC_ConvertedValue[0];
  6.     Xtemp=ADC_ConvertedValue[1];

  7.     if(Xtemp>Xmax)
  8.         Xtemp=Xmax;
  9.     if(Xtemp<Xmin)
  10.         Xtemp=Xmin;

  11.     if(Ytemp>=Ymax)
  12.         Ytemp=Ymax;
  13.     if(Ytemp<=Ymin)
  14.         Ytemp=Ymin;
  15.    
  16.     printf("Xmax=%x,Xcen=%x,Xmin=%x\r\n",Xmax,Xtemp,Xmin);
  17.     printf("Ymax=%x,Ycen=%x,Ymin=%x\r\n",Ymax,Ytemp,Ymin);
  18.     //根据坐标极点确定坐标(两点直线方程)
  19.     X=((Xtemp-Xmin)*255)/(Xmax-Xmin);
  20.     Y=((Ytemp-Ymin)*255)/(Ymax-Ymin);

  21.     if(X>(X_BASE+20))
  22.     {
  23.         Joystick_Buf[1]=((X-X_BASE)>>DIV)+1;
  24.     }
  25.     if(X<(X_BASE-20))
  26.     {
  27.         Joystick_Buf[1]=(u8)-(((X_BASE-X)>>DIV)+1);
  28.     }
  29.     if(Y>(Y_BASE+20))
  30.     {
  31.         Joystick_Buf[2]=((Y-Y_BASE)>>DIV)+1;;;
  32.     }
  33.     if(Y<(Y_BASE-20))
  34.     {
  35.         Joystick_Buf[2]=(u8)-(((Y_BASE-Y)>>DIV)+1);
  36.     }
  37.     Key_Handle(Joystick_Buf);

  38.     Delay_Ms(5);

  39.     while( Endp1Busy )
  40.     {
  41.         ;                                               //如果忙(上一包数据没有传上去),则等待。
  42.     }
  43.     Endp1Busy = 1;                                      //设置为忙状态
  44.     memcpy(pEP1_IN_DataBuf, Joystick_Buf, 4);
  45.     DevEP1_IN_Deal(4);

  46. }   </font>

其中X_BASE为摇杆中点, if(X>(X_BASE+20))就是摇杆左摇动;故而((X-X_BASE)>>DIV)+1计算赋值给我们我们X+坐标;其他方向同理,另外Key_Handle的代码如下,主要是为了处理按键与滚轮值
  1. void Key_Handle(uint8_t* kv)
  2. {
  3.     if((LFKEY)==Bit_RESET)
  4.     {
  5.         kv[0]|=0x01;
  6.     }
  7.     if((RGKEY)==Bit_RESET)
  8.     {
  9.         kv[0]|=0x02;
  10.     }
  11.     if(SW1!=Bit_RESET)
  12.     {
  13.         kv[0]|=0x04;
  14.     }
  15.     if((UPKEY)==Bit_RESET)
  16.     {
  17.         if(c_tick++>5)
  18.         {
  19.             kv[3]=1;
  20.             c_tick=0;
  21.         }
  22.     }
  23.     if((DNKEY)==Bit_RESET)
  24.     {
  25.         if(c_tick++>5)
  26.         {
  27.             kv[3]=(u8)-1;
  28.             c_tick=0;
  29.         }
  30.     }
  31. }</font>

3.2.3 下载验证
我们把固件程序下载进去可以,打开“设备与打印机”可以看到USB设备枚举成了一个“LD Mouse”,如下图。
   1966993-20220325210229360-775600955.png
我们打开一个网页,摇动摇杆鼠标指针跟着动;右边上下左右键,左右代表鼠标左右键,上下代表滚轮;然后摇杆中键代表鼠标中键;

3.3 实例Eg3_KeyBoard


本节我们目标是实现模拟键盘的功能,枚举一个具有Shift键+1~8键的模拟键盘功能;
3.3.1硬件设计
同第一章节

3.3.2 软件设计
在上一章节的基础上,我们在USB_DevTransProcess中找到报告描述符的获取,并修改为如下内容
  1. case USB_DESCR_TYP_REPORT:
  2. if(((pSetupReqPak->wIndex)&0xff) == 0)     //接口0报表描述符
  3. {
  4.     pDescr = KeyboardRepDesc;                      //数据准备上传
  5.     len = sizeof(KeyboardRepDesc);
  6.     Ready = 1;             //如果有更多接口,该标准位应该在最后一个接口配置完成后有效
  7. }
  8. else len = 0xff;                                //本程序只有2个接口,这句话正常不可能执行
  9. break;</font>

另外Keyboard的报告描述符KeyboardRepDesc如下
  1. const UINT8  MouseRepDesc[]=
  2. {
  3.         0x05, 0x01, // USAGE_PAGE (Generic Desktop)
  4.         0x09, 0x06, // USAGE (Keyboard)
  5.         0xa1, 0x01, // COLLECTION (Application)
  6.         0x05, 0x07, // USAGE_PAGE (Keyboard)
  7.         0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)
  8.         0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)
  9.         0x15, 0x00, // LOGICAL_MINIMUM (0)
  10.         0x25, 0x01, // LOGICAL_MAXIMUM (1)
  11.         0x75, 0x01, // REPORT_SIZE (1)
  12.         0x95, 0x08, // REPORT_COUNT (8)
  13.         0x81, 0x02, // INPUT (Data,Var,Abs)
  14.         0x95, 0x01, // REPORT_COUNT (1)
  15.         0x75, 0x08, // REPORT_SIZE (8)
  16.         0x81, 0x03, // INPUT (Cnst,Var,Abs)
  17.         0x95, 0x05, // REPORT_COUNT (5)
  18.         0x75, 0x01, // REPORT_SIZE (1)
  19.         0x05, 0x08, // USAGE_PAGE (LEDs)
  20.         0x19, 0x01, // USAGE_MINIMUM (Num Lock)
  21.         0x29, 0x05, // USAGE_MAXIMUM (Kana)
  22.         0x91, 0x02, // OUTPUT (Data,Var,Abs)
  23.         0x95, 0x01, // REPORT_COUNT (1)
  24.         0x75, 0x03, // REPORT_SIZE (3)
  25.         0x91, 0x03, // OUTPUT (Cnst,Var,Abs)
  26.         0x95, 0x06, // REPORT_COUNT (6)
  27.         0x75, 0x08, // REPORT_SIZE (8)
  28.         0x15, 0x00, // LOGICAL_MINIMUM (0)
  29.         0x25, 0xFF, // LOGICAL_MAXIMUM (255)
  30.         0x05, 0x07, // USAGE_PAGE (Keyboard)
  31.         0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
  32.         0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
  33.         0x81, 0x00, // INPUT (Data,Ary,Abs)
  34.         0xC0        // END_COLLECTION
  35. };</font>

然后是报文数据的处理如下:
  1. uint8_t Keyboad_Buf[8]={0};
  2. uint8_t lastshift=0,currentshift=0;
  3. uint8_t lastkeycode[8]={0},currentkeycode[8]={0};
  4. static uint8_t KdataFL=0;
  5. //处理并上报数据
  6. void Keyboard_Handle(void)
  7. {   
  8.     memset(Keyboad_Buf,0,8);
  9.     uint8_t i=0;uint8_t idx=2;

  10.     if(SW1==1)
  11.     {
  12.         currentshift|=0x02;
  13.         Keyboad_Buf[0]=currentshift;
  14.     }else{

  15.         currentshift&=(~0x02);
  16.         Keyboad_Buf[0]=currentshift;
  17.     }

  18.     if(UPKEY==0)
  19.     {
  20.         currentkeycode[0]=CODE1;
  21.     }else{
  22.         currentkeycode[0]=0x00;
  23.     }
  24.     if(DNKEY==0)
  25.     {
  26.         currentkeycode[1]=CODE2;
  27.     }else{
  28.         currentkeycode[1]=0x00;
  29.     }
  30.     if(LFKEY==0)
  31.     {
  32.         currentkeycode[2]=CODE3;
  33.     }else{
  34.         currentkeycode[2]=0x00;
  35.     }
  36.     if(RGKEY==0)
  37.     {
  38.         currentkeycode[3]=CODE4;
  39.     }else{
  40.         currentkeycode[3]=0x00;
  41.     }
  42.     if(BKKEY==0)
  43.     {
  44.         currentkeycode[4]=CODE5;
  45.     }else{
  46.         currentkeycode[4]=0x00;
  47.     }
  48.     if(MDKEY==0)
  49.     {
  50.         currentkeycode[5]=CODE6;
  51.     }else{
  52.         currentkeycode[5]=0x00;
  53.     }
  54.     if(STKEY==0)
  55.     {
  56.         currentkeycode[6]=CODE7;
  57.     }else{
  58.         currentkeycode[6]=0x00;
  59.     }
  60.     if(TBKEY==0)
  61.     {
  62.         currentkeycode[7]=CODE8;
  63.     }else{
  64.         currentkeycode[7]=0x00;
  65.     }

  66.     for(i=0;i<8;i++)
  67.     {
  68.         if(currentkeycode!=lastkeycode)
  69.         {
  70.             Keyboad_Buf[idx]=currentkeycode;
  71.             if(++idx>=8)
  72.             {
  73.                 idx=2;
  74.             }
  75.             KdataFL=1;
  76.         }else{
  77.             Keyboad_Buf[idx]=0x00;
  78.         }
  79.     }
  80.     if(currentshift!=lastshift)
  81.     {
  82.         KdataFL=1;
  83.     }

  84.     if(KdataFL!=0)
  85.     {
  86.         KdataFL=0;
  87.         while( Endp1Busy )
  88.         {
  89.             ;                                               //如果忙(上一包数据没有传上去),则等待。
  90.         }
  91.         Endp1Busy = 1;                                      //设置为忙状态
  92.         memcpy(pEP1_IN_DataBuf, Keyboad_Buf, 8);
  93.         DevEP1_IN_Deal(8);

  94.     }
  95.     Delay_Ms(5);
  96.     memcpy(lastkeycode,currentkeycode,8);
  97.     lastshift=currentshift;
  98. }</font>

最后是main函数,只改了while中的Keyboard_Handle();
  1. /*******************************************************************************
  2. * Function Name  : main
  3. * Description    : Main program.
  4. * Input          : None
  5. * Return         : None
  6. *******************************************************************************/
  7. int main(void)
  8. {
  9.     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  10.     Delay_Init();
  11.     USART_Printf_Init(115200);

  12.     printf("SystemClk:%d\r\n",SystemCoreClock);
  13.     printf("USBHD Device Test\r\n");

  14.     pEP0_RAM_Addr = EP0_Databuf;
  15.     pEP1_RAM_Addr = EP1_Databuf;
  16.     pEP2_RAM_Addr = EP2_Databuf;

  17.     USBHD_ClockCmd(RCC_USBCLKSource_PLLCLK_1Div5,ENABLE);
  18.     USB_DeviceInit();
  19.     NVIC_EnableIRQ( USBHD_IRQn );
  20.     ADC_DMA_CONF();
  21.     KEY_INIT();

  22.     while(1)
  23.     {
  24.         printf("X=%d,Y=%d\r\n",ADC_ConvertedValue[0],ADC_ConvertedValue[1]);
  25.         if(Ready)
  26.         {
  27.             Keyboard_Handle();
  28.         }
  29.     }
  30. }


3.3.3 下载验证
我们把固件程序下载进去可以,打开“设备与打印机”可以看到USB设备枚举成了一个“LD Keyboard”,如下图。
   1966993-20220329215629544-759735995.png
我们打开一个键盘测试网页,地址如下:
https://keyboard.bmcx.com/
按摇杆按键SW1即为shift键按下,其他键分别对应主键盘的1~8;shift+1-8键也可以组合;




Litthins 发表于 2022-3-30 13:49 | 显示全部楼层
看起来很不错,我最近也打算做一个。
andygirl 发表于 2022-4-2 17:21 | 显示全部楼层
这个好啊~
caigang13 发表于 2022-4-3 10:44 来自手机 | 显示全部楼层
不错,空了试试。
wyz6 发表于 2022-4-24 16:53 | 显示全部楼层
楼主,资料怎么下不了 了?我想看看手柄的描述符资料,哪里可以看到?
 楼主| lilijin1995 发表于 2022-4-26 19:55 | 显示全部楼层
wyz6 发表于 2022-4-24 16:53
楼主,资料怎么下不了 了?我想看看手柄的描述符资料,哪里可以看到?

链接更新了,可以下了
benjaminka 发表于 2022-5-28 15:23 | 显示全部楼层
使用什么通信的呢?
saservice 发表于 2022-5-28 17:33 | 显示全部楼层
板子很给力呢。   
isseed 发表于 2022-5-29 08:45 | 显示全部楼层
这个板子做的好看。  
 楼主| lilijin1995 发表于 2022-5-30 09:16 | 显示全部楼层
benjaminka 发表于 2022-5-28 15:23
使用什么通信的呢?

USB hid
 楼主| lilijin1995 发表于 2022-5-30 09:17 | 显示全部楼层
isseed 发表于 2022-5-29 08:45
这个板子做的好看。

谢谢
 楼主| lilijin1995 发表于 2022-5-30 09:17 | 显示全部楼层

谢谢
您需要登录后才可以回帖 登录 | 注册

本版积分规则

56

主题

165

帖子

8

粉丝
快速回复 在线客服 返回列表 返回顶部