C&C++   发布时间:2022-04-13  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了CH582m模拟JoyStick使用USB与电脑通信大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。

本程序改编自沁恒官网2022年1月更新的CH583EVT中的CompoundDev例程。这里只贴了main.c中的程序。
能够实现①直接接电脑,在设备管理器中能够查到被电脑识别为HID-compliant game contorller;
②在CH582m单片机中自定义了回包内容,通过USB抓包工具可以抓到单片机模拟的JoyStick与电脑主机正常通信。

#include "CH58x_common.h"

/*设备、配置、接口、类、端点和报表描述符见我前一篇随笔,这里就省略不写了*/

uint8_t DevConfig; //存放设备的配置
uint8_t SetupReqCode; //这个值会在收到SETUP包时赋值。端口0用于控制传输,此变量只在控制传输中使用
uint16_t SetupReqLen; //这个值会在收到SETUP包时赋值。端口0用于控制传输,此变量只在控制传输中使用
const uint8_t *pDescr; //在控制传输的数据阶段,此地址中的数据会被拷贝到RAM中,由DMA发送
          //在前面加了const表示(*pDescr)是常量不可赋值,pDescr可以赋值
uint8_t Report_Value = 0x00; //USB设备所用协议,在收到SETUP包时赋值
uint8_t Idle_Value = 0x00; //设备空闲时间,在收到SETUP包时赋值
uint8_t USB_SleepStatus = 0x00; // USB睡眠状态

//JoyStick中断传输中上传给主机4字节的数据
uint8_t HIDJoyStick[4] = {0x0, 0x0, 0x0, 0x0};
/******** 用户自定义分配端点RAM ****************************************/
__attribute__((aligned(4))) uint8_t EP0_Databuf[64 + 64 + 64]; //ep0(64)+ep4_out(64)+ep4_in(64) //端点4的缓存也在这里
__attribute__((aligned(4))) uint8_t EP1_Databuf[64 + 64]; //ep1_out(64)+ep1_in(64)
__attribute__((aligned(4))) uint8_t EP2_Databuf[64 + 64]; //ep2_out(64)+ep2_in(64)
__attribute__((aligned(4))) uint8_t EP3_Databuf[64 + 64]; //ep3_out(64)+ep3_in(64)

/*********************************************************************
* @fn USB_DevTransProcesswch
*
* @brief USB 传输处理函数
*
* @return none
*/
void USB_DevTransProcess(void) //USB设备传输中断处理
{
  uint8_t len, chtype; //len用于拷贝函数,chtype用于存放数据传输方向、命令的类型、接收的对象等信息
  uint8_t intflag, errflag = 0; //intflag用于存放标志寄存器值,errflag用于标记是否支持主机的指令

  intflag = R8_USB_INT_FG; //取得中断标识寄存器的值

  if(intflag & RB_UIF_TRANSFER) //判断_INT_FG中的USB传输完成中断标志位。若有传输完成中断了,进if语句
  {
    if((R8_USB_INT_ST & MASK_UIS_TOKEN) != MASK_UIS_TOKEN) // 非空闲 //判断中断状态寄存器中的5:4位,查看令牌的PID标识。若这两位不是11(表示空闲),进if语句
    {
      switch(R8_USB_INT_ST & (MASK_UIS_TOKEN | MASK_UIS_ENDp)) //取得令牌的PID标识和设备模式下的3:0位的端点号。主机模式下3:0位是应答PID标识位
      // 分析操作令牌和端点号
      { //端点0用于控制传输。以下的端点0的IN和OUT令牌相应程序,对应控制传输的数据阶段和状态阶段。
        case UIS_TOKEN_IN: //令牌包的PID为IN,5:4位为10。3:0位的端点号为0。IN令牌:设备给主机发数据。_UIS_:USB中断状态
        { //端点0为双向端点,用作控制传输。 “|0”运算省略了
          switch(SetupReqCodE)//这个值会在收到SETUP包时赋值。在后面会有SETUP包处理程序,对应控制传输的设置阶段。
          {

            case USB_GET_DESCRIPTOR: //USB标准命令,主机从USB设备获取描述
              len = SetupReqLen >= DevEP0SIZE ? DevEP0SIZE : SetupReqLen; // 本次包传输长度。最长为64字节,超过64字节的分多次处理,前几次要满包。
              memcpy(pEP0_DataBuf, pDescr, len);//memcpy:内存拷贝函数,从(二号位)地址拷贝(三号位)字符串长度到(一号位)地址中
          //DMA直接与内存相连,会检测到内存的改写,而后不用单片机控制就可以将内存中的数据发送出去。如果只是两个数组互相赋值,不涉及与DMA匹配的物理内存,就无法触发DMA。
              SetupReqLen -= len; //记录剩下的需要发送的数据长度
              pDescr += len; //更新接下来需要发送的数据的起始地址,拷贝函数用
              R8_UEP0_T_LEN = len; //端点0发送长度寄存器中写入本次包传输长度
              R8_UEP0_CTRL ^= RB_UEP_T_TOG; // 同步切换。IN方向(对于单片机就是T方向)的PID中的DATA0和DATA1切换
              break; //赋值完端点控制寄存器的握手包响应(ACK、NAK、STALL),由硬件打包成符合规范的包,DMA自动发送

            case USB_SEt_address: //USB标准命令,主机为设备设置一个唯一地址,范围0~127,0为默认地址
              R8_USB_DEV_AD = (R8_USB_DEV_AD & RB_UDA_GP_BIT) | SetupReqLen;
                //7位地址+最高位的用户自定义地址(默认为1),或上“包传输长度”(这里的“包传输长度”在后面赋值成了地址位)
              R8_UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
                //R响应OUT事务ACK,T响应IN事务NAK。这个CASE分支里是IN方向,当DMA相应内存中,单片机没有数据更新时,回NAK握手包。
              break; //一般程序里的OUT事务,设备会回包给主机,不响应NAK。

            case USB_SET_FEATURE: //USB标准命令,主机要求启动一个在设备、接口或端点上的特征
              break;

            default:
              R8_UEP0_T_LEN = 0; //状态阶段完成中断或者是强制上传0长度数据包结束控制传输(数据字段长度为0的数据包,包里SYNC、PID、EOP字段都有)
              R8_UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
                //R响应OUT事务ACK,T响应IN事务NAK。这个CASE分支里是OUT方向,当DMA相应内存中更新了数据且单片机验收正常时,回ACK握手包。
              break;
          }
        }
        break;

        case UIS_TOKEN_OUT: //令牌包的PID为OUT,5:4位为00。3:0位的端点号为0。OUT令牌:主机给设备发数据。
        { //端点0为双向端点,用作控制传输。 “|0”运算省略了
          len = R8_USB_RX_LEN; //读取当前USB接收长度寄存器中存储的接收的数据字节数 //接收长度寄存器为各个端点共用,发送长度寄存器各有各的
        }
        break;

        case UIS_TOKEN_OUT | 1: //令牌包的PID为OUT,端点号为1
        {
          if(R8_USB_INT_ST & RB_UIS_TOG_OK) //硬件会判断是否正确的同步切换数据包,同步切换正确,这一位自动置位
          { // 不同步的数据包将丢弃
            R8_UEP1_CTRL ^= RB_UEP_R_TOG; //OUT事务的DATA同步切换。设定一个期望值。
            len = R8_USB_RX_LEN; //读取接收数据的字节数
            DevEP1_OUT_Deal(len); //发送长度为len的字节,自动回ACK握手包。自定义的程序。
          }
        }
        break;

        case UIS_TOKEN_IN | 1: //令牌包的PID为IN,端点号为1
          R8_UEP1_CTRL ^= RB_UEP_T_TOG; //IN事务的DATA切换一下。设定将要发送的包的PID。
          R8_UEP1_CTRL = (R8_UEP1_CTRL & ~MASK_UEP_T_RES) | UEP_T_RES_NAK; //当DMA中没有由单片机更新数据时,将T响应IN事务置为NAK。更新了就发出数据。
          break;

    }
    R8_USB_INT_FG = RB_UIF_TRANSFER; //写1清零中断标志
  }

  if(R8_USB_INT_ST & RB_UIS_SETUP_ACT) // Setup包处理
  {
    R8_UEP0_CTRL = RB_UEP_R_TOG | RB_UEP_T_TOG | UEP_R_RES_ACK | UEP_T_RES_NAK;
      //R响应OUT事务期待为DATA1(DMA收到的数据包的PID要为DATA1,否则算数据错误要重传)和ACK(DMA相应内存中收到了数据,单片机验收正常)
      //T响应IN事务设定为DATA1(单片机有数据送入DMA相应内存,以DATA1发送出去)和NAK(单片机没有准备好数据)。
    SetupReqLen = pSetupReqPak->wLength; //数据阶段的字节数 //pSetupReqPak:将端点0的RAM地址强制转换成一个存放结构体的地址,结构体成员依次紧凑排列
    SetupReqCode = pSetupReqPak->brequest; //命令的序号
    chtype = pSetupReqPak->brequestType; //包含数据传输方向、命令的类型、接收的对象等信息

    len = 0;
    errflag = 0;
    if((pSetupReqPak->brequestType & USB_REQ_TYP_MASK) != USB_REQ_TYP_STANDARD) //判断命令的类型,如果不是标准请求,进if语句
    {
      /* 非标准请求 */
      /* 其它请求,如类请求,产商请求等 */
      if(pSetupReqPak->brequestType & 0x40) //取得命令中的某一位,判断是否为0,不为零进if语句
      {
        /* 厂商请求 */
      }
      else if(pSetupReqPak->brequestType & 0x20) //取得命令中的某一位,判断是否为0,不为零进if语句
      { //判断为HID类请求
        switch(SetupReqCodE) //判断命令的序号
        {
          case DEF_USB_SET_IDLE: /* 0x0A: SET_IDLE */ //主机想设置HID设备特定输入报表的空闲时间间隔
            Idle_Value = EP0_Databuf[3];//这个一定要有
            break; 

          case DEF_USB_SET_REPORT: /* 0x09: SET_REPORT */ //主机想设置HID设备的报表描述符
            break;

          case DEF_USB_SET_PROTOCOL: /* 0x0B: SET_PROTOCOL */ //主机想设置HID设备当前所使用的协议
            Report_Value = EP0_Databuf[2];
            break;

          case DEF_USB_GET_IDLE: /* 0x02: GET_IDLE */ //主机想读取HID设备特定输入报表的当前的空闲比率
            EP0_Databuf[0] = Idle_Value;
            len = 1;
            break;

          case DEF_USB_GET_PROTOCOL: /* 0x03: GET_PROTOCOL */ //主机想获得HID设备当前所使用的协议
            EP0_Databuf[0] = Report_Value;
            len = 1;
            break;

          default:
            errflag = 0xFF;
        }
      }
    }
    else //判断为标准请求
    {
      switch(SetupReqCodE) //判断命令的序号
      {
        case USB_GET_DESCRIPTOR: //主机想获得标准描述符
        {
          switch(((pSetupReqPak->wvalue) >> 8)) //右移8位,看原来的高8位是否为0,为1表示方向为IN方向,则进s-case语句
          {
            case USB_DESCR_TYP_DEVICE: //不同的值代表不同的命令。主机想获得设备描述符
            {
              pDescr = MyDevDescr; //将设备描述符字符串放在pDescr地址中,“获得标准描述符”这个case末尾会用拷贝函数发送
              len = MyDevDescr[0]; //协议规定设备描述符的首字节存放字节数长度。拷贝函数会用到len参数
            }
            break;

            case USB_DESCR_TYP_CONFIG: //主机想获得配置描述符
            {
              pDescr = MyCfgDescr; //将配置描述符字符串放在pDescr地址中,之后会发送
              len = MyCfgDescr[2]; //协议规定配置描述符的第三个字节存放配置信息的总长
            }
            break;

            case USB_DESCR_TYP_HID: //主机想获得人机接口类描述符。此处结构体中的wIndex与配置描述符不同,意为接口号。
              switch((pSetupReqPak->wIndeX) & 0xff) //取低八位,高八位抹去
              {
              /* 选择接口 */
                case 0:
                  pDescr = (uint8_t *)(&MyCfgDescr[18]); //接口1的类描述符存放位置,待发送
                  len = 9;
                  break;

                default://就用了一个接口,出现其他值就不对了
                  errflag = 0xff;
                  break;
              }
            break;

            case USB_DESCR_TYP_REPORT: //主机想获得设备报表描述符
            {
              if(((pSetupReqPak->wIndeX) & 0xff) == 0) //接口0报表描述符
              {
                pDescr = JoyStickRepDesc; //数据准备上传
                len = sizeof(JoyStickRepdesc);
              }
              else
                len = 0xff; //本程序只有1个接口,这句话正常不可能执行
            }
            break;

            case USB_DESCR_TYP_StriNG: //主机想获得设备字符串描述符
            {
              switch((pSetupReqPak->wvalue) & 0xff) //根据wValue的值传递字符串信息
              {
              default:
                errflag = 0xFF; // 不支持的字符串描述符
                break;    //没有写字符串描述符,非必要
              }
            }
            break;

            default:
              errflag = 0xff;
              break;
          }
          if(SetupReqLen > len)
            SetupReqLen = len; //实际需上传总长度
          len = (SetupReqLen >= DevEP0SIZE) ? DevEP0SIZE : SetupReqLen; //最大长度为64字节
          memcpy(pEP0_DataBuf, pDescr, len); //拷贝函数
          pDescr += len;
        }
        break;

        case USB_SEt_address: //主机想设置设备地址
          SetupReqLen = (pSetupReqPak->wvalue) & 0xff; //将主机分发的位设备地址暂存在SetupReqLen中
        break; //控制阶段会赋值给设备地址参数

        case USB_GET_CONFIGURATION: //主机想获得设备当前配置
          pEP0_DataBuf[0] = DevConfig; //将设备配置放进RAM
          if(SetupReqLen > 1)
            SetupReqLen = 1; //将数据阶段的字节数置1。因为DevConfig只有一个字节
          break;

        case USB_SET_CONFIGURATION: //主机想设置设备当前配置
          DevConfig = (pSetupReqPak->wvalue) & 0xff; //取低八位,高八位抹去
          break;

        case USB_CLEAR_FEATURE: //关闭USB设备的特征/功能。可以是设备或是端点层面上的。
        {
          if((pSetupReqPak->brequestType & USB_REQ_RECIP_MASK) == USB_REQ_RECIP_ENDp) //判断是不是端点特征(清除端点停止工作的状态)
          {
            switch((pSetupReqPak->wIndeX) & 0xff) //取低八位,高八位抹去。判断索引
            { //16位的最高位判断数据传输方向,0为OUT,1为IN。低位为端点号。
              case 0x81: //清零_TOG和_T_RES这三位,并将后者写成_NAK,响应IN事务NAK表示无数据返回
                R8_UEP1_CTRL = (R8_UEP1_CTRL & ~(RB_UEP_T_TOG | MASK_UEP_T_RES)) | UEP_T_RES_NAK;
                break;
              case 0x01: //清零_TOG和_R_RES这三位,并将后者写成_ACK,响应OUT事务ACK表示接收正常
                R8_UEP1_CTRL = (R8_UEP1_CTRL & ~(RB_UEP_R_TOG | MASK_UEP_R_RES)) | UEP_R_RES_ACK;
                break;
              default:
                errflag = 0xFF; // 不支持的端点
                break;
            }
          }
          else if((pSetupReqPak->brequestType & USB_REQ_RECIP_MASK) == USB_REQ_RECIP_DEVICE) //判断是不是设备特征(用于设备唤醒)
          {
            if(pSetupReqPak->wValue == 1) //唤醒标志位为1
            {
              USB_SleepStatus &= ~0x01; //最低位清零
            }
          }
          else
          {
            errflag = 0xFF;
          }
        }
        break;

        case USB_SET_FEATURE: //开启USB设备的特征/功能。可以是设备或是端点层面上的。
          if((pSetupReqPak->brequestType & USB_REQ_RECIP_MASK) == USB_REQ_RECIP_ENDp) //判断是不是端点特征(使端点停止工作)
          {
            /* 端点 */
            switch(pSetupReqPak->wIndeX) //判断索引
            { //16位的最高位判断数据传输方向,0为OUT,1为IN。低位为端点号。
              case 0x81: //清零_TOG和_T_RES三位,并将后者写成_STALL,根据主机指令停止端点的工作
                R8_UEP1_CTRL = (R8_UEP1_CTRL & ~(RB_UEP_T_TOG | MASK_UEP_T_RES)) | UEP_T_RES_STALL;
                break;
              case 0x01: //清零_TOG和_R_RES三位,并将后者写成_STALL,根据主机指令停止端点的工作
                R8_UEP1_CTRL = (R8_UEP1_CTRL & ~(RB_UEP_R_TOG | MASK_UEP_R_RES)) | UEP_R_RES_STALL;
                break;
              default:
              /* 不支持的端点 */
                errflag = 0xFF; // 不支持的端点
                break;
            }
          }
          else if((pSetupReqPak->brequestType & USB_REQ_RECIP_MASK) == USB_REQ_RECIP_DEVICE) //判断是不是设备特征(使设备休眠)
          {
            if(pSetupReqPak->wValue == 1)
            {
              USB_SleepStatus |= 0x01; //设置睡眠
            }
          }
          else
          {
            errflag = 0xFF;
          }
          break;

        case USB_GET_INTERFACE: //主机想获得接口当前工作的选择设置值
          pEP0_DataBuf[0] = 0x00;
          if(SetupReqLen > 1)
            SetupReqLen = 1; //将数据阶段的字节数置1。因为待传数据只有一个字节
          break;

        case USB_SET_INTERFACE: //主机想激活设备的某个接口
          break;

        case USB_GET_STATUS: //主机想获得设备、接口或是端点的状态
          if((pSetupReqPak->brequestType & USB_REQ_RECIP_MASK) == USB_REQ_RECIP_ENDp) //判断是否为端点状态
          {
          /* 端点 */
            pEP0_DataBuf[0] = 0x00;
            switch(pSetupReqPak->wIndeX)
            { //16位的最高位判断数据传输方向,0为OUT,1为IN。低位为端点号。
              case 0x81: //判断_TOG和_T_RES三位,若处于STALL状态,进if语句
                if((R8_UEP1_CTRL & (RB_UEP_T_TOG | MASK_UEP_T_RES)) == UEP_T_RES_STALL)
                {
                  pEP0_DataBuf[0] = 0x01; //返回D0为1,表示端点被停止工作了。该位由SET_FEATURE和CLEAR_FEATURE命令配置。
                }
                break;

              case 0x01: //判断_TOG和_R_RES三位,若处于STALL状态,进if语句
                if((R8_UEP1_CTRL & (RB_UEP_R_TOG | MASK_UEP_R_RES)) == UEP_R_RES_STALL)
                {
                  pEP0_DataBuf[0] = 0x01;
                }
                break;
            }
          }
          else if((pSetupReqPak->brequestType & USB_REQ_RECIP_MASK) == USB_REQ_RECIP_DEVICE) //判断是否为设备状态
          {
            pEP0_DataBuf[0] = 0x00;
            if(USB_SleepStatus) //如果设备处于睡眠状态
            {
              pEP0_DataBuf[0] = 0x02; //最低位D0为0,表示设备由总线供电,为1表示设备自供电。 D1位为1表示支持远程唤醒,为0表示不支持。
            }
            else
            {
              pEP0_DataBuf[0] = 0x00;
            }
          }
          pEP0_DataBuf[1] = 0; //返回状态信息的格式为16位数,高八位保留为0
          if(SetupReqLen >= 2)
          {
            SetupReqLen = 2; //将数据阶段的字节数置2。因为待传数据只有2个字节
          }
          break;

        default:
          errflag = 0xff;
          break;
        }
      }
      if(errflag == 0xff) // 错误或不支持
      {
        R8_UEP0_CTRL = RB_UEP_R_TOG | RB_UEP_T_TOG | UEP_R_RES_STALL | UEP_T_RES_STALL; // STALL
      }
      else
      {
        if(chtype & 0x80) // 上传。最高位为1,数据传输方向为设备向主机传输。
        {
          len = (SetupReqLen > DevEP0SIZE) ? DevEP0SIZE : SetupReqLen;
          SetupReqLen -= len;
        }
      else
        len = 0; // 下传。最高位为0,数据传输方向为主机向设备传输。
      R8_UEP0_T_LEN = len;
      R8_UEP0_CTRL = RB_UEP_R_TOG | RB_UEP_T_TOG | UEP_R_RES_ACK | UEP_T_RES_ACK; // 默认数据包是DATA1
      }

      R8_USB_INT_FG = RB_UIF_TRANSFER; //写1清中断标识
    }
  }


  else if(intflag & RB_UIF_BUS_RST) //判断_INT_FG中的总线复位标志位,为1触发
  {
    R8_USB_DEV_AD = 0; //设备地址写成0,待主机重新分配给设备一个新地址
    R8_UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK; //把端点0的控制寄存器,写成:接收响应响应ACK表示正常收到,发送响应NAK表示没有数据要返回
    R8_UEP1_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
    R8_UEP2_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
    R8_UEP3_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
    R8_USB_INT_FG = RB_UIF_BUS_RST; //写1清中断标识
  }
  else if(intflag & RB_UIF_SUSPEND) //判断_INT_FG中的总线挂起或唤醒事件中断标志位。挂起和唤醒都会触发此中断
  {
    if(R8_USB_MIS_ST & RB_UMS_SUSPEND) //取得杂项状态寄存器中的挂起状态位,为1表示USB总线处于挂起态,为0表示总线处于非挂起态
    {
      ;
    } // 挂起 //当设备处于空闲状态超过3ms,主机会要求设备挂起(类似于电脑休眠)
    else //挂起或唤醒中断被触发,又没有被判断为挂起
    {
      ;
    } // 唤醒
    R8_USB_INT_FG = RB_UIF_SUSPEND; //写1清中断标志
  }
  else
  {
    R8_USB_INT_FG = intflag; //_INT_FG中没有中断标识,再把原值写回原来的寄存器
  }
}

/*********************************************************************
* @fn DevHIDJoyStickReport
*
* @brief 上报JoyStick数据
*
* @return none
*/
void DevHIDJoyStickReport(uint8_t jsdata0,uint8_t jsdata1,uint8_t jsdata2,uint8_t jsdata3)
{
  HIDJoyStick[0] = jsdata0;
  HIDJoyStick[1] = jsdata1;
  HIDJoyStick[2] = jsdata2;
  HIDJoyStick[3] = jsdata3;
  memcpy(pEP1_IN_DataBuf, HIDJoyStick, sizeof(HIDJoyStick));
  DevEP1_IN_Deal(sizeof(HIDJoyStick));
}


/*********************************************************************
* @fn DevWakeup
*
* @brief 设备模式唤醒主机
*
* @return none
*/
void DevWakeup(void)
{
  R16_PIN_ANALOG_IE &= ~(RB_PIN_USB_DP_PU); //清空标识,该位为0,表示由RB_UC_DEV_PU_EN控制D+线路上是否上拉,但是睡眠模式或者下电模式下不起作用。写1强制使能上拉。
  R8_UDEV_CTRL |= RB_UD_LOW_SPEED; //选择低速模式
  mDelaymS(2);
  R8_UDEV_CTRL &= ~RB_UD_LOW_SPEED; //清空低速模式标识,即选择全速模式
  R16_PIN_ANALOG_IE |= RB_PIN_USB_DP_PU; //RB_PIN_USB_DP_PU可以不受睡眠影响,强制启用D+引脚的上拉
}

/*********************************************************************
* @fn DebugInit
*
* @brief 调试初始化
*
* @return none
*/
void DebugInit(void) //初始化串口1,用于在串口助手显示debug信息
{
  GPIOA_SetBits(GPIO_Pin_9);
  GPIOA_ModeCfg(GPIO_Pin_8, GPIO_ModeIN_PU);
  GPIOA_ModeCfg(GPIO_Pin_9, GPIO_ModeOut_PP_5mA);
  UART1_DefInit();
}

/*********************************************************************
* @fn main
*
* @brief 主函数
*
* @return none
*/
int main()
{
  SetSysClock(CLK_sourcE_PLL_60MHz);

  DebugInit(); //配置串口1用来prinft来debug
  printf("start\n");

  pEP0_RAM_Addr = EP0_Databuf; //配置缓存区64字节。CH582m所有端点的最大数据包长度都是64字节
  pEP1_RAM_Addr = EP1_Databuf;

  USB_DeviceInit();

  PFIC_EnableIRQ(USB_IRQn); //启用中断向量

  while(1)
  {  //对于鼠标来说按下某个键会将回包的某个字节写值,松开鼠标会将该值清零表示不再按下这个键。
    //对于JoyStick来说可能也是如此,不过用单片机模拟JoyStick与电脑通信的目的已经达到,英文手册太长,就不查手册找表示释放按键的值了。
    mDelaymS(1000);
    DevHIDJoyStickReport(0x05, 0x10, 0x20, 0x11);
    mDelaymS(1000);
    DevHIDJoyStickReport(0x0A, 0x15, 0x25, 0x22);
    mDelaymS(1000);
    DevHIDJoyStickReport(0x0E, 0x1A, 0x2A, 0x44);
    mDelaymS(1000);
    DevHIDJoyStickReport(0x10, 0x1E, 0x2E, 0x88);
  }
}

/*********************************************************************
* @fn DevEP1_OUT_Deal
*
* @brief 端点1数据处理
*
* @return none
*/
void DevEP1_OUT_Deal(uint8_t l)
{ /* 用户可自定义 */
  uint8_t i;

  for(i = 0; i < l; i++)
  {
    pEP1_IN_DataBuf[i] = ~pEP1_OUT_DataBuf[i]; //将OUT缓存(对应单片机接收)按位取反,赋值给IN缓存(对应单片机发送)。//自定义的操作
  }
  DevEP1_IN_Deal(l); //单片机通过端点1发送长度l字节的数据,自动回ACK握手包。此函数配置完了控制寄存器。
}

/*********************************************************************
* @fn USB_IRQHandler
*
* @brief USB中断函数
*
* @return none
*/
__INTERRUPT
__HIGH_CODE
void USB_IRQHandler(void) /* USB中断服务程序,使用寄存器组1 */
{
  USB_DevTransProcess();
}

大佬总结

以上是大佬教程为你收集整理的CH582m模拟JoyStick使用USB与电脑通信全部内容,希望文章能够帮你解决CH582m模拟JoyStick使用USB与电脑通信所遇到的程序开发问题。

如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。
标签: