[RISC-V MCU 应用开发] 第八十三章、CH32V103应用教程——USB模拟鼠标键盘设备

[复制链接]
 楼主| RISCVLAR 发表于 2021-3-16 15:14 | 显示全部楼层 |阅读模式
本帖最后由 RISCVLAR 于 2021-3-16 15:13 编辑

CH32V103应用教程——USB模拟鼠标键盘设备

本章教程主要使用CH32V103 USB模拟鼠标键盘设备。

1、USB简介及相关函数介绍
关于USB具体介绍,可参考前面章节。

2、硬件设计
本章教程主要进行USB模拟鼠标键盘实验,仅需用到开发板USB口。

3软件设计
本章程序全在主函数中进行,具体程序如下:
main.c文件
  1. /********************************** (C) COPYRIGHT *******************************
  2. * File Name          : main.c
  3. * Author             : WCH
  4. * Version            : V1.0.0
  5. * Date               : 2019/10/15
  6. * Description        : Main program body.
  7. *******************************************************************************/
  8. #include "debug.h"
  9. #include "usart.h"
  10. #include "string.h"

  11. #define   DevEP0SIZE   0x40

  12. /* Device Descriptor */
  13. const UINT8  MyDevDescr[] =
  14. {
  15.         0x12,              //设备描述符长度,18字节
  16.         0x01,              //描述符类型,0x01为设备描述符
  17.         0x10, 0x01,        //本设备所使用USB版本协议,因为是小端结构,所以低字节在前,即USB1.1版本为0x10,0x01,USB2.0为0x00,0x02
  18.         0x00,              //类代码,此处不在设备描述符中定义设备类,而在接口描述符中定义设备类。对于大多数标准的USB设备类,该字段通常设置为0,而在接口描述符中的bInterfaceClass中指定接口所实现的功能
  19.         0x00,              //子类代码,当类代码bDeviceClass为0时,下面的子类代码bDeviceSubClass也必须为0。
  20.         0x00,              //设备所使用的协议,协议代码由USB协会规定。当该字段为0时,表示设备不使用类所定义的协议。
  21.         DevEP0SIZE,        //端点0的最大包长,可以取值8、16、32、64,此处为64字节
  22.         0x86, 0x1A,        //厂商ID
  23.         0xe1, 0xe6,        //产品设备ID
  24.         0x00, 0x01,        //设备版本号
  25.         0x01,              //描述厂商的字符串索引值。当该值为0时,表示没有厂商字符串
  26.         0x02,              //描述产品的字符串索引值。当该值为0时,表示没有产品字符串
  27.         0x03,              //描述设备的序列号字符串索引值。当该值为0时,表示没有序列号字符串
  28.         0x01,              //可能的配置数,通常为1
  29. };

  30. const UINT8 KeyRepDesc[] =
  31. {
  32.         0x05, 0x01,         // Usage page Generatic Desktop
  33.         0x09, 0x06,         // Usage keyboard
  34.         0xa1, 0x01,         // Collation Application
  35.         0x05, 0x07,         // Usafe page (key code)
  36.         0x19, 0xe0,         // Usage Min ( E0 -->  L_CTL)
  37.         0x29, 0xe7,         // Usage MAX ( E7 --> R_GUI )
  38.         0x15, 0x00,         // Logical min
  39.         0x25, 0x01,         // Logical max
  40.         0x95, 0x08,         // Report count ( 8 )
  41.         0x75, 0x01,         // Report size  ( 1 )
  42.         0x81, 0x02,         // Input ( Data, Variable, Absolute )
  43.         0x95, 0x08,         // Report count ( 8 )
  44.         0x75, 0x01,         // Report size  ( 1 )
  45.         0x81, 0x01,         // Input ( const )
  46.         0x05, 0x08,         // Usage page( LED )
  47.         0x19, 0x01,         // Usage min ( 1 )
  48.         0x29, 0x03,         // Usage max ( 3 )
  49.         0x95, 0x03,         // Report count ( 3 )
  50.         0x75, 0x01,         // Report size ( 1 )
  51.         0x91, 0x02,         // Output ( Data, Variable, Absolute )
  52.         0x95, 0x01,         // Report count ( 1 )
  53.         0x75, 0x05,         // Report size ( 5 )
  54.         0x91, 0x01,         // Output ( const )
  55.         0x05, 0x07,         // Usage page ( key code )
  56.         0x19, 0x00,         // Usage min ( 0H )
  57.         0x2a, 0xff, 0x00,   // Usage max ( FFH )
  58.         0x15, 0x00,         // Logical min ( 0H )
  59.         0x26, 0xff, 0x00,   // Logical max ( FFH )
  60.         0x95, 0x06,         // Report count ( 6 )
  61.         0x75, 0x08,         // Report size ( 8 )
  62.         0x81, 0x00,         // Input ( Data, Array, Absolute )
  63.         0xc0                // End collection
  64. };

  65. const UINT8  MouseRepDesc[]=
  66. {
  67.         0x05,0x01,
  68.         0x09,0x02,
  69.         0xA1,0x01,
  70.         0x09,0x01,
  71.         0xA1,0x00,
  72.         0x05,0x09,
  73.         0x19,0x01,
  74.         0x29,0x03,
  75.         0x15,0x00,
  76.         0x25,0x01,
  77.         0x95,0x03,
  78.         0x75,0x01,
  79.         0x81,0x02,
  80.         0x95,0x01,
  81.         0x75,0x05,
  82.         0x81,0x03,
  83.         0x05,0x01,
  84.         0x09,0x30,
  85.         0x09,0x31,
  86.         0x09,0x38,
  87.         0x15,0x81,
  88.         0x25,0x7f,
  89.         0x75,0x08,
  90.         0x95,0x03,
  91.         0x81,0x06,
  92.         0xC0,
  93.         0xC0
  94. };

  95. /* Configration Descriptor */
  96. const UINT8  MyCfgDescr[] =
  97. {
  98.         //配置描述符
  99.         0x09,              //配置描述符长度,标准USB配置描述符长度为9字节
  100.         0x02,              //描述符类型,配置描述符为0x02
  101.         0x3b, 0x00,        //配置描述符集合总长度,59字节
  102.         0x02,              //该配置所支持的接口数,2个接口
  103.         0x01,              //表示该配置的值
  104.         0x00,              //描述该配置的字符串的索引值,0x00表示没有字符串
  105.         0xA0,              //描述设备的一些属性,如供电方式和唤醒等,0xA0表示设备总线供电且支持远程唤醒
  106.         0x32,              //设备需要从总线获取的最大电流量,0x32表示最大电流100ma

  107.         //键盘
  108.         //接口描述符,接口描述符不能单独返回,必须附着在配置描述符后一并返回
  109.         0x09,              //接口描述符长度,标准的USB接口描述符长度为9字节
  110.         0x04,              //描述符类型,接口描述符为0x04
  111.         0x00,              //该接口的编号,从0开始,此处为0x00
  112.         0x00,              //该接口的备用编号,通常设置为0
  113.         0x01,              //该接口所使用的端点数,0x01表示使用1个端点。如果该字段为0,则表示没有非0端点,只使用默认的控制端点
  114.         0x03,              //该接口所使用的类,0x03为HID类
  115.         0x01,              //该接口所使用的子类
  116.         0x01,              //该接口所使用的协议
  117.         0x00,              //该接口的字符串的索引值,0x00表示没有字符串

  118.         //HID类描述符,它是一个类描述符,应该跟在接口描述符后面
  119.         0x09,              //该描述符长度,它的大小与该描述符中下级描述符的个数有关。例如只有一个下级描述符时,总长度为9
  120.         0x21,              //描述符类型,HID描述符为0x21
  121.         0x10,0x01,         //HID协议的版本号,这里参看的HID协议是USB HID1.1协议,因此这里为0x0110
  122.         0x00,              //国家代码,是设备所适用的国家。通常我们的键盘是美式键盘,代码为33,即0x21,但此处设置为0x00
  123.         0x01,              //下级描述符的数量,该值至少为1,即至少要有一个报告描述符。下级描述符可以是报告描述符或物理描述符
  124.         0x22,              //下级描述符的类型,报告描述符的编号为0x22,物理描述符编号为0x23
  125.         sizeof(KeyRepDesc)&0xFF, 0x00, //下级描述符的长度

  126.         //端点描述符,端点描述符不能单独返回,必须附着在配置描述符后一并返回
  127.         0x07,              //端点描述符长度,标准的USB端点描述符长度为7字节
  128.         0x05,              //描述符类型,端点描述符为0x05
  129.         0x81,              //该端点的地址,0x81表示端点1作为输入,最高位D7为该端点的传输方向,1为输入,0为输出。D3-D0为端点号,可设置为0-7,D6-4保留,设为0.
  130.         //关于端点属性,最低两位D1-0表示该端点的传输类型,0为控制传输,1为等时传输,2为批量传输,3为中断传输。
  131.         0x03,              //该端点的属性,此处为中断传输方式
  132.         DevEP0SIZE, 0x00,  //该端点支持的最大包长度,此处设置为64字节
  133.         0x0a,              //端点的查询时间。对于中断端点,表示查询的帧间隔数

  134.         //鼠标
  135.         //接口描述符
  136.         0x09,              //接口描述符长度,9字节
  137.         0x04,              //描述符类型,接口描述符为0x04
  138.         0x01,              //该接口的编号,从0开始,此处为0x01
  139.         0x00,              //该接口的备用编号
  140.         0x01,              //该接口所使用的端点数,0x01表示使用1个端点
  141.         0x03,              //该接口所使用的类,0x03为HID类
  142.         0x01,              //该接口所使用的子类
  143.         0x02,              //该接口所使用的协议
  144.         0x00,              //该接口的字符串的索引值,0x00表示没有字符串

  145.         //HID类描述符
  146.         0x09,              //该描述符长度,它的大小与该描述符中下级描述符的个数有关。例如只有一个下级描述符时,总长度为9
  147.         0x21,              //描述符类型,HID描述符为0x21
  148.         0x10,0x01,         //HID协议的版本号,这里参看的HID协议是USB HID1.1协议,因此这里为0x0110
  149.         0x00,              //国家代码,是设备所适用的国家。通常我们的键盘是美式键盘,代码为33,即0x21,但此处设置为0x00
  150.         0x01,              //下级描述符的数量,该值至少为1,即至少要有一个报告描述符。下级描述符可以是报告描述符或物理描述符
  151.         0x22,              //下级描述符的类型,报告描述符的编号为0x22,物理描述符编号为0x23
  152.         sizeof(MouseRepDesc)&0xFF,0x00,  //下级描述符的长度

  153.         //端点描述符
  154.         0x07,              //端点描述符长度,7字节
  155.         0x05,              //描述符类型,端点描述符为0x05
  156.         0x82,              //该端点的地址,0x82表示端点2作为输入
  157.         0x03,              //该端点的属性
  158.         DevEP0SIZE, 0x00,  //该端点支持的最大包长度,64字节
  159.         0x0a,              //端点的查询时间
  160. };

  161. const UINT8  MyProductIDInfo[] = {0x0E,0x03,'K',0,'&',0,'M',0,'1',0,'0',0,'3',0};

  162. /* Language Descriptor */
  163. const UINT8  MyLangDescr[] = { 0x04, 0x03, 0x09, 0x04 };

  164. /* Manufactor Descriptor */
  165. const UINT8  MyManuInfo[] = { 0x0E, 0x03, 'w', 0, 'c', 0, 'h', 0, '.', 0, 'c', 0, 'n', 0 };

  166. /* Product Information */
  167. const UINT8  MyProdInfo[] = { 0x0C, 0x03, 'C', 0, 'H', 0, '5', 0, '5', 0, '9', 0 };

  168. /**********************************************************/
  169. volatile UINT8   Ready = 0;
  170. volatile UINT8   Endp1Busy = 0;  //传输完成控制标志位
  171. volatile UINT8   Endp2Busy = 0;
  172. UINT8   DevConfig;
  173. UINT8   SetupReqCode;
  174. UINT16  SetupReqLen;
  175. UINT8   HIDKey[8];
  176. UINT8   HIDMouse[4];
  177. const   UINT8 *pDescr;

  178. /* Endpoint Buffer */
  179. __attribute__ ((aligned(4))) UINT8 EP0_Databuf[64];     //ep0(64)
  180. __attribute__ ((aligned(4))) UINT8 EP1_Databuf[64+64];  //ep1_out(64)+ep1_in(64)
  181. __attribute__ ((aligned(4))) UINT8 EP2_Databuf[64+64];  //ep2_out(64)+ep2_in(64)

  182. void USBHD_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast")));

  183. /*******************************************************************************
  184. * Function Name  : USB_DevTransProcess
  185. * Description    : USB device transfer process.
  186. * Input          : None
  187. * Return         : None
  188. *******************************************************************************/
  189. void USB_DevTransProcess( void )
  190. {
  191.     UINT8  len, chtype;
  192.     UINT8  intflag, errflag = 0;

  193.     intflag = R8_USB_INT_FG;

  194.     if( intflag & RB_UIF_TRANSFER )
  195.     {
  196.         switch ( R8_USB_INT_ST & ( MASK_UIS_TOKEN | MASK_UIS_ENDP ) )
  197.         {
  198.             case UIS_TOKEN_SETUP:
  199.                 R8_UEP0_CTRL = RB_UEP_R_TOG | RB_UEP_T_TOG | UEP_R_RES_ACK | UEP_T_RES_NAK;
  200.                 len = R8_USB_RX_LEN;

  201.                 if ( len == sizeof( USB_SETUP_REQ ) )
  202.                 {
  203.                     SetupReqLen = pSetupReqPak->wLength;
  204.                     SetupReqCode = pSetupReqPak->bRequest;
  205.                     chtype = pSetupReqPak->bRequestType;

  206.                     len = 0;
  207.                     errflag = 0;

  208.                     if ( ( pSetupReqPak->bRequestType & USB_REQ_TYP_MASK ) != USB_REQ_TYP_STANDARD )
  209.                     {
  210.                         switch(SetupReqCode)
  211.                         {
  212.                             case 0x01:    //GetReport
  213.                                 len = 1;
  214.                                 pEP0_DataBuf[0] = 0xaa;
  215.                                 break;
  216.                             case 0x0a:
  217.                                 R8_UEP0_T_LEN = 0;
  218.                                 break;    //这个一定要有
  219.                             case 0x09:
  220.                                 Ready = 1;
  221.                                 break;
  222.                             default:
  223.                                 errflag = 0xFF;
  224.                         }
  225.                     }
  226.                     else
  227.                     {
  228.                         switch( SetupReqCode )
  229.                         {
  230.                             case USB_GET_DESCRIPTOR:
  231.                             {
  232.                                 switch( ((pSetupReqPak->wValue)>>8) )
  233.                                 {
  234.                                     case USB_DESCR_TYP_DEVICE:
  235.                                         pDescr = MyDevDescr;
  236.                                         len = MyDevDescr[0];
  237.                                         break;

  238.                                     case USB_DESCR_TYP_CONFIG:
  239.                                         pDescr = MyCfgDescr;
  240.                                         len = MyCfgDescr[2];
  241.                                         break;

  242.                                     case USB_DESCR_TYP_STRING:
  243.                                         switch( (pSetupReqPak->wValue)&0xff )
  244.                                         {
  245.                                             case 0:
  246.                                                 pDescr = MyLangDescr;
  247.                                                 len = MyLangDescr[0];
  248.                                                 break;

  249.                                             case 1:
  250.                                                 pDescr = MyManuInfo;
  251.                                                 len = MyManuInfo[0];
  252.                                                 break;

  253.                                             case 2:
  254.                                                 pDescr = MyProdInfo;
  255.                                                 len = MyProdInfo[0];
  256.                                                 break;

  257.                                             case 3:
  258.                                                 pDescr = MyProductIDInfo;
  259.                                                 len = MyProductIDInfo[0];
  260.                                                 break;

  261.                                             default:
  262.                                                 errflag = 0xFF;
  263.                                                 break;
  264.                                         }
  265.                                         break;

  266.                                     case USB_DESCR_TYP_REPORT:
  267.                                         if(((pSetupReqPak->wIndex)&0xff) == 0)          //接口0报表描述符
  268.                                         {
  269.                                             pDescr = KeyRepDesc;                        //数据准备上传
  270.                                             len = sizeof(KeyRepDesc);
  271.                                         }
  272.                                         else if(((pSetupReqPak->wIndex)&0xff) == 1)     //接口1报表描述符
  273.                                         {
  274.                                             pDescr = MouseRepDesc;                      //数据准备上传
  275.                                             len = sizeof(MouseRepDesc);
  276.                                             Ready = 1;                                  //如果有更多接口,该标准位应该在最后一个接口配置完成后有效
  277.                                         }
  278.                                         else len = 0xff;                                //本程序只有2个接口,这句话正常不可能执行
  279.                                         break;

  280.                                     default :
  281.                                         errflag = 0xff;
  282.                                         break;
  283.                                 }

  284.                                 if( SetupReqLen>len )   SetupReqLen = len;
  285.                                 len = (SetupReqLen >= DevEP0SIZE) ? DevEP0SIZE : SetupReqLen;
  286.                                 memcpy( pEP0_DataBuf, pDescr, len );
  287.                                 pDescr += len;
  288.                             }
  289.                                 break;

  290.                             case USB_SET_ADDRESS:
  291.                                 SetupReqLen = (pSetupReqPak->wValue)&0xff;
  292.                                 break;

  293.                             case USB_GET_CONFIGURATION:
  294.                                 pEP0_DataBuf[0] = DevConfig;
  295.                                 if ( SetupReqLen > 1 ) SetupReqLen = 1;
  296.                                 break;

  297.                             case USB_SET_CONFIGURATION:
  298.                                 DevConfig = (pSetupReqPak->wValue)&0xff;
  299.                                 break;

  300.                             case USB_CLEAR_FEATURE:
  301.                                 if ( ( pSetupReqPak->bRequestType & USB_REQ_RECIP_MASK ) == USB_REQ_RECIP_ENDP )
  302.                                 {
  303.                                     switch( (pSetupReqPak->wIndex)&0xff )
  304.                                     {
  305.                                     case 0x82:
  306.                                         R8_UEP2_CTRL = (R8_UEP2_CTRL & ~( RB_UEP_T_TOG|MASK_UEP_T_RES )) | UEP_T_RES_NAK;
  307.                                         break;

  308.                                     case 0x02:
  309.                                         R8_UEP2_CTRL = (R8_UEP2_CTRL & ~( RB_UEP_R_TOG|MASK_UEP_R_RES )) | UEP_R_RES_ACK;
  310.                                         break;

  311.                                     case 0x81:
  312.                                         R8_UEP1_CTRL = (R8_UEP1_CTRL & ~( RB_UEP_T_TOG|MASK_UEP_T_RES )) | UEP_T_RES_NAK;
  313.                                         break;

  314.                                     case 0x01:
  315.                                         R8_UEP1_CTRL = (R8_UEP1_CTRL & ~( RB_UEP_R_TOG|MASK_UEP_R_RES )) | UEP_R_RES_ACK;
  316.                                         break;

  317.                                     default:
  318.                                         errflag = 0xFF;
  319.                                         break;
  320.                                     }
  321.                                 }
  322.                                 else    errflag = 0xFF;
  323.                                 break;

  324.                             case USB_GET_INTERFACE:
  325.                                 pEP0_DataBuf[0] = 0x00;
  326.                                 if ( SetupReqLen > 1 ) SetupReqLen = 1;
  327.                                 break;

  328.                             case USB_GET_STATUS:
  329.                                 pEP0_DataBuf[0] = 0x00;
  330.                                 pEP0_DataBuf[1] = 0x00;
  331.                                 if ( SetupReqLen > 2 ) SetupReqLen = 2;
  332.                                 break;

  333.                             default:
  334.                                 errflag = 0xff;
  335.                                 break;
  336.                         }
  337.                     }
  338.                 }
  339.                 else    errflag = 0xff;

  340.                 if( errflag == 0xff)
  341.                 {
  342.                     R8_UEP0_CTRL = RB_UEP_R_TOG | RB_UEP_T_TOG | UEP_R_RES_STALL | UEP_T_RES_STALL;
  343.                 }
  344.                 else
  345.                 {
  346.                     if( chtype & 0x80 )
  347.                     {
  348.                         len = (SetupReqLen>DevEP0SIZE) ? DevEP0SIZE : SetupReqLen;
  349.                         SetupReqLen -= len;
  350.                     }
  351.                     else  len = 0;

  352.                     R8_UEP0_T_LEN = len;
  353.                     R8_UEP0_CTRL = RB_UEP_R_TOG | RB_UEP_T_TOG | UEP_R_RES_ACK | UEP_T_RES_ACK;
  354.                 }
  355.                 break;

  356.             case UIS_TOKEN_IN:
  357.                 switch( SetupReqCode )
  358.                 {
  359.                     case USB_GET_DESCRIPTOR:
  360.                         len = SetupReqLen >= DevEP0SIZE ? DevEP0SIZE : SetupReqLen;
  361.                         memcpy( pEP0_DataBuf, pDescr, len );
  362.                         SetupReqLen -= len;
  363.                         pDescr += len;
  364.                         R8_UEP0_T_LEN = len;
  365.                         R8_UEP0_CTRL ^= RB_UEP_T_TOG;
  366.                         break;

  367.                     case USB_SET_ADDRESS:
  368.                         R8_USB_DEV_AD = (R8_USB_DEV_AD&RB_UDA_GP_BIT) | SetupReqLen;
  369.                         R8_UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
  370.                         break;

  371.                     default:
  372.                         R8_UEP0_T_LEN = 0;
  373.                         R8_UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
  374.                         break;
  375.                 }
  376.                 break;

  377.             case UIS_TOKEN_OUT:
  378.                 len = R8_USB_RX_LEN;
  379.                 break;

  380.             case UIS_TOKEN_OUT | 1:
  381.                 if ( R8_USB_INT_ST & RB_UIS_TOG_OK )
  382.                 {
  383.                     len = R8_USB_RX_LEN;
  384.                     DevEP1_OUT_Deal( len );
  385.                 }
  386.                 break;

  387.             case UIS_TOKEN_IN | 1:
  388.                 R8_UEP1_T_LEN = 0;
  389.                 Endp1Busy = 0;
  390.                 R8_UEP1_CTRL = (R8_UEP1_CTRL & ~MASK_UEP_T_RES) | UEP_T_RES_NAK;
  391.                 break;

  392.             case UIS_TOKEN_OUT | 2:
  393.                 if ( R8_USB_INT_ST & RB_UIS_TOG_OK )
  394.                 {
  395.                     len = R8_USB_RX_LEN;
  396.                     DevEP2_OUT_Deal( len );
  397.                 }
  398.                 break;

  399.             case UIS_TOKEN_IN | 2:
  400.                 R8_UEP2_T_LEN = 0;
  401.                 Endp2Busy = 0;
  402.                 R8_UEP2_CTRL = (R8_UEP2_CTRL & ~MASK_UEP_T_RES) | UEP_T_RES_NAK;
  403.                 break;

  404.             default :
  405.                 break;
  406.         }
  407.         R8_USB_INT_FG = RB_UIF_TRANSFER;
  408.     }
  409.     else if( intflag & RB_UIF_BUS_RST )
  410.     {
  411.         R8_USB_DEV_AD = 0;
  412.         R8_UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
  413.         R8_UEP1_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK | RB_UEP_AUTO_TOG;
  414.         R8_UEP2_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK | RB_UEP_AUTO_TOG;
  415.         R8_USB_INT_FG |= RB_UIF_BUS_RST;
  416.     }
  417.     else if( intflag & RB_UIF_SUSPEND )
  418.     {
  419.         if ( R8_USB_MIS_ST & RB_UMS_SUSPEND ) {;}
  420.         else{;}
  421.         R8_USB_INT_FG = RB_UIF_SUSPEND;
  422.     }
  423.     else
  424.     {
  425.         R8_USB_INT_FG = intflag;
  426.     }
  427. }

  428. void HIDValueHandle()
  429. {
  430.     UINT8 i;
  431.     i = USART_ReceiveData(USART2);
  432.     printf( "%c\n", (UINT8)i );
  433.     switch(i)
  434.     {
  435.     //鼠标数据上传示例
  436.         case 'L':                                               //左键
  437.             HIDMouse[0] = 0x01;
  438.             while( Endp2Busy )
  439.             {
  440.                 ;                                               //如果忙(上一包数据没有传上去),则等待。
  441.             }
  442.             Endp2Busy = 1;                                      //设置为忙状态
  443.             memcpy(pEP2_IN_DataBuf, HIDMouse, 4);
  444.             DevEP2_IN_Deal(4);
  445.             HIDMouse[0] = 0;                                    //抬起
  446.             while( Endp2Busy )
  447.             {
  448.                 ;                                               //如果忙(上一包数据没有传上去),则等待。
  449.             }
  450.             Endp2Busy = 1;                                      //设置为忙状态
  451.             memcpy(pEP2_IN_DataBuf, HIDMouse, 4);
  452.             DevEP2_IN_Deal(4);
  453.             break;
  454.         case 'R':                                               //右键
  455.             HIDMouse[0] = 0x02;
  456.             while( Endp2Busy )
  457.             {
  458.                 ;                                               //如果忙(上一包数据没有传上去),则等待。
  459.             }
  460.             Endp2Busy = 1;                                      //设置为忙状态
  461.             memcpy(pEP2_IN_DataBuf, HIDMouse, 4);
  462.             DevEP2_IN_Deal(4);
  463.             HIDMouse[0] = 0;                                    //抬起
  464.             while( Endp2Busy )
  465.             {
  466.                 ;                                               //如果忙(上一包数据没有传上去),则等待。
  467.             }
  468.             Endp2Busy = 1;                                      //设置为忙状态
  469.             memcpy(pEP2_IN_DataBuf, HIDMouse, 4);
  470.             DevEP2_IN_Deal(4);
  471.             break;
  472.     //键盘数据上传示例
  473.         case 'A':                                               //A键
  474.             HIDKey[2] = 0x04;                                   //按键开始
  475.             while( Endp1Busy )
  476.             {
  477.                 ;                                               //如果忙(上一包数据没有传上去),则等待。
  478.             }
  479.             Endp1Busy = 1;                                      //设置为忙状态
  480.             memcpy(pEP1_IN_DataBuf, HIDKey, 8);
  481.             DevEP1_IN_Deal(8);
  482.             HIDKey[2] = 0;                                      //按键结束
  483.             while( Endp1Busy )
  484.             {
  485.                 ;                                               //如果忙(上一包数据没有传上去),则等待。
  486.             }
  487.             Endp1Busy = 1;                                      //设置为忙状态
  488.             memcpy(pEP1_IN_DataBuf, HIDKey, 8);
  489.             DevEP1_IN_Deal(8);
  490.             break;
  491.         case 'Q':                                               //CAP键
  492.             HIDKey[2] = 0x39;
  493.             while( Endp1Busy )
  494.             {
  495.                 ;                                               //如果忙(上一包数据没有传上去),则等待。
  496.             }
  497.             Endp1Busy = 1;                                      //设置为忙状态
  498.             memcpy(pEP1_IN_DataBuf, HIDKey, 8);
  499.             DevEP1_IN_Deal(8);
  500.             HIDKey[2] = 0;                                      //按键结束
  501.             while( Endp1Busy )
  502.             {
  503.                 ;                                               //如果忙(上一包数据没有传上去),则等待。
  504.             }
  505.             Endp1Busy = 1;                                      //设置为忙状态
  506.             memcpy(pEP1_IN_DataBuf, HIDKey, 8);
  507.             DevEP1_IN_Deal(8);
  508.             break;
  509.         default:                                                //其他
  510.             break;
  511.     }
  512. }
  513. /*******************************************************************************
  514. * Function Name  : Set_USBConfig
  515. * Description    : Set USB clock.
  516. * Input          : None
  517. * Return         : None
  518. *******************************************************************************/
  519. void USBHD_ClockCmd(UINT32 RCC_USBCLKSource,FunctionalState NewState)
  520. {
  521.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, NewState);
  522.     EXTEN->EXTEN_CTR |= EXTEN_USBHD_IO_EN;
  523.     RCC_USBCLKConfig(RCC_USBCLKSource);                      //USBclk=PLLclk/1.5=48Mhz
  524.     RCC_AHBPeriphClockCmd(RCC_AHBPeriph_USBHD,NewState);
  525. }

  526. /*******************************************************************************
  527. * Function Name  : main
  528. * Description    : Main program.
  529. * Input          : None
  530. * Return         : None
  531. *******************************************************************************/
  532. int main(void)
  533. {
  534.     NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  535.     Delay_Init();
  536.     USART2_Printf_Init(115200);

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

  539.     pEP0_RAM_Addr = EP0_Databuf;
  540.     pEP1_RAM_Addr = EP1_Databuf;
  541.     pEP2_RAM_Addr = EP2_Databuf;

  542.     USBHD_ClockCmd(RCC_USBCLKSource_PLLCLK_1Div5,ENABLE);
  543.     USB_DeviceInit();
  544.     NVIC_EnableIRQ( USBHD_IRQn );

  545.     while(1)
  546.     {
  547.         printf("Ready1=%d\n",Ready);
  548.         if(Ready)
  549.         {
  550.             HIDValueHandle();
  551.         }
  552.         Delay_Ms(500);
  553.     }
  554. }

  555. /*******************************************************************************
  556. * Function Name  : DevEP1_OUT_Deal
  557. * Description    : Deal device Endpoint 1 OUT.
  558. * Input          : l: Data length.
  559. * Return         : None
  560. *******************************************************************************/
  561. void DevEP1_OUT_Deal( UINT8 l )
  562. {
  563.     ;
  564. }

  565. /*******************************************************************************
  566. * Function Name  : DevEP2_OUT_Deal
  567. * Description    : Deal device Endpoint 2 OUT.
  568. * Input          : l: Data length.
  569. * Return         : None
  570. *******************************************************************************/
  571. void DevEP2_OUT_Deal( UINT8 l )
  572. {
  573.     ;
  574. }

  575. /*******************************************************************************
  576. * Function Name  : USB_IRQHandler
  577. * Description    : This function handles USB exception.
  578. * Input          : None
  579. * Return         : None
  580. *******************************************************************************/
  581. void USBHD_IRQHandler (void)
  582. {
  583.     USB_DevTransProcess();
  584. }
main.c文件中本人对各种描述符都进行了注释,便于大家理解,有不懂地方可以参考《圈圈教你玩USB》。关于USB设备传输过程,可结合应用手册关于USB寄存器介绍进行理解学习。

4下载验证
将编译好的程序下载到开发板并复位,打开串口调试助手,串口打印如下:
图片1.png
用公对公USB线将开发板与电脑连接起来,打开设备管理器可以看到端口中多了一个键盘设备和鼠标设备,如图所示:
图片2.png 图片3.png
串口调试助手发送L,显示如下:
图片4.png

CH32V103 USB模拟鼠标键盘设备.rar

498.8 KB, 下载次数: 321

zealerlu 发表于 2021-11-20 22:09 | 显示全部楼层
感谢楼主~~
quark 发表于 2022-2-23 12:22 | 显示全部楼层
感谢
probedog 发表于 2022-2-25 10:39 | 显示全部楼层
分享的资料挺全
onlycook 发表于 2022-3-7 15:31 来自手机 | 显示全部楼层
感谢分享
gygp 发表于 2023-2-2 09:31 | 显示全部楼层
普通i/o可以模拟USB键盘吗
wwppd 发表于 2023-2-6 12:41 | 显示全部楼层
用单片机怎么制作电脑USB或者PS/2键盘
mikewalpole 发表于 2023-2-6 16:06 | 显示全部楼层
ch375 模拟HID鼠标吗?              
ingramward 发表于 2023-2-7 20:43 | 显示全部楼层
如何用单片机模拟键盘控制电脑               
您需要登录后才可以回帖 登录 | 注册

本版积分规则

133

主题

296

帖子

44

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