[单片机芯片]CH32V203的USB1 HID库调试经验分享

背景:      

今天要水的帖子是关于使用CH32V203替代旧STM32F103实现USB霍尔摇杆的项目。关于调试经验啥的,主要分享CH32V203的USB使用。这个项目最开始是使用迈来芯的方案,但是价格30RMB/PCS,也是芯片涨价时候涨上去的,后来替换了同厂不同方案,这个方案芯片对磁场灵敏度很高,高得离谱,迫不得已更换了磁铁,后来总算满足了,2021年时候发现了国产替代方案,矽睿科技的QMC磁力计方案,本来想推给老板更换的,然而老板觉得不成熟,时至今日,老板实在用不起国外的方案了,机会来了,我对WCH的CH32V RISC-V系列MCU比较熟悉,今天我们就开始国产化替代过程吧!


关于硬件
      由于需要兼容STM32的USB,所以这里使用了PA11(DM)和PA12(DP),对应CH32V203的USB1,我看USB Device 的实现跟STM32 的HAL库居然有点一致了,之前一致是在中断回调函数中枚举的。其实我更喜欢第二种方式,可能效率会更高。不过USB2好像还是原来的方式。



软件设计

我是在懒得新建工程,移植USB Library了,干脆直接在官方工程上改,我用的是EXAM\USB\USBD\CompositeKM,USBD目录的工程用的USB1口对应PA11(DM)和PA12(DP),大家不要搞错了,我就搞错过,还寄了芯片给WCH的FAE调试,后来发现我自己用错IO了。

1.时钟初始化配置,
初始化需要配置RCC_USBCLKConfig,以及RCC_APB1PeriphClockCmd,而在例程中是使用Set_USBConfig调用实现的,如下代码:

void Set_USBConfig( )

{

        RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_Div3);

        RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB, ENABLE);                          

}

大家都知道,USB 设备一般都是使用48MHz的时钟,而例程的PLLCLK是144Mhz,所以这里是3分频,另外需要使能一下APB1的外设时钟。

2.USB端点配置、描述符配置标准请求的实现等。

端点地址、大小,描述符,各类请求的实现是通过以下函数实现:

void USB_Init(void)

{

  pInformation = &Device_Info;

  pInformation->ControlState = 2;

  pProperty = &Device_Property;

  pUser_Standard_Requests = &User_Standard_Requests;

  pProperty->Init();

}

如果你定位到pInformation,pProperty,以及pUser_Standard_Requests的定义,可以发现,
pInformation的定义其实是DEVICE_INFO这样子的,这其实是一些USB请求事务信息。

typedef struct _DEVICE_INFO

{

  uint8_t USBbmRequestType;       /* bmRequestType */

  uint8_t USBbRequest;            /* bRequest */

  uint16_t_uint8_t USBwValues;         /* wValue */

  uint16_t_uint8_t USBwIndexs;         /* wIndex */

  uint16_t_uint8_t USBwLengths;        /* wLength */



  uint8_t ControlState;           /* of type CONTROL_STATE */

  uint8_t Current_Feature;

  uint8_t Current_Configuration;   /* Selected configuration */

  uint8_t Current_Interface;       /* Selected interface of current configuration */

  uint8_t Current_AlternateSetting;/* Selected Alternate Setting of current

                                     interface*/



  ENDPOINT_INFO Ctrl_Info;

}DEVICE_INFO;

而pProperty的定义更复杂以下,DEVICE_PROP定义如下,如果说DEVICE_INFO是数据结构的话,我觉得DEVICE_PROP更像是实现,因为里面都是一些函数指针,和一些必要的指针和变量,囊括了USB 设备的枚举过程:端点的配置,各种请求类,相关描述符的获取。

typedef struct _DEVICE_PROP

{

  void (*Init)(void);        /* Initialize the device */

  void (*Reset)(void);       /* Reset routine of this device */

  void (*Process_Status_IN)(void);

  void (*Process_Status_OUT)(void);



  RESULT (*Class_Data_Setup)(uint8_t RequestNo);



  RESULT (*Class_NoData_Setup)(uint8_t RequestNo);



  RESULT  (*Class_Get_Interface_Setting)(uint8_t Interface, uint8_t AlternateSetting);



  uint8_t* (*GetDeviceDescriptor)(uint16_t Length);

  uint8_t* (*GetConfigDescriptor)(uint16_t Length);

  uint8_t* (*GetStringDescriptor)(uint16_t Length);



  void* RxEP_buffer;

   

  uint8_t MaxPacketSize;



}DEVICE_PROP;


在这里我们先来改描述符的获取吧,

  uint8_t* (*GetDeviceDescriptor)(uint16_t Length);

  uint8_t* (*GetConfigDescriptor)(uint16_t Length);

  uint8_t* (*GetStringDescriptor)(uint16_t Length);

这三个指针函数是获取设备描述符,配置描述符集合,字符串描述符。

设备描述符:以下是我雷蛇鼠标的: 


配置描述符集合包含了配置描述符,接口描述符,HID描述符(如果是HID),端点描述符:


上图只是接口0的,共有三个接口,其实都是同理可得。
HID每个接口几乎都有自己的报表描述符。

在代码中如何实现的呢?
Device_Property中的USBD_Data_Setup里面有个USBD_GetReportDescriptor请求,这是获取报表描述符的。

ONE_DESCRIPTOR Report_Descriptor[3] =

{

        {(uint8_t*)USBD_JoystickRepDesc, USBD_SIZE_REPORT_DESC_JS},

        {(uint8_t*)USBD_MouseRepDesc, USBD_SIZE_REPORT_DESC_MS},

        {(uint8_t*)USBD_VendorRepDesc, USBD_SIZE_REPORT_DESC_VD},

};

如果设备描述符、配置描述符、接口描述符、HID描述符、端点描述符、报表描述符没有问题的话,是不是就一定可以枚举成功了呢,是不是就可以高枕无忧了呢,然而事实并非如此。还需要注意一下端点地址和大小的配置。
端点地址和缓存大小其实也是在Device_Property中的USBD_Reset复位中的,

void USBD_Reset(void)

{

  pInformation->Current_Configuration = 0;

  pInformation->Current_Feature = USBD_ConfigDescriptor[7];

  pInformation->Current_Interface = 0;



  SetBTABLE(BTABLE_ADDRESS);



    SetEPType(ENDP0, EP_CONTROL);

    SetEPTxStatus(ENDP0, EP_TX_STALL);

    SetEPRxAddr(ENDP0, ENDP0_RXADDR);

    SetEPTxAddr(ENDP0, ENDP0_TXADDR);

    Clear_Status_Out(ENDP0);

    SetEPRxCount(ENDP0, Device_Property.MaxPacketSize);

    SetEPRxValid(ENDP0);

    _ClearDTOG_RX(ENDP0);

    _ClearDTOG_TX(ENDP0);



      SetEPType(ENDP1, EP_INTERRUPT);

      SetEPRxAddr(ENDP1, ENDP1_RXADDR);

      SetEPTxAddr(ENDP1, ENDP1_TXADDR);

      SetEPTxStatus(ENDP1, EP_TX_NAK);

      Clear_Status_Out(ENDP1);

      SetEPRxCount(ENDP1, Device_Property.MaxPacketSize);

      SetEPRxValid(ENDP1);

      _ClearDTOG_TX(ENDP1);

      _ClearDTOG_RX(ENDP1);



      SetEPType(ENDP2, EP_INTERRUPT);

      SetEPTxAddr(ENDP2, ENDP2_TXADDR);

      SetEPTxStatus(ENDP2, EP_TX_NAK);

      _ClearDTOG_TX(ENDP2);

      _ClearDTOG_RX(ENDP2);



      SetEPType(ENDP3, EP_INTERRUPT);

      SetEPTxAddr(ENDP3, ENDP3_TXADDR);

      SetEPTxStatus(ENDP3, EP_TX_NAK);

      _ClearDTOG_TX(ENDP3);

      _ClearDTOG_RX(ENDP3);





    SetDeviceAddress(0);



    bDeviceState = ATTACHED;

}

地址是偏移地址,需要根据端点描述符中的最大Size来配置并偏移。

如果你的设备成功枚举,这时候HID还不能动的,需要实现端点数据的上报,如下:

uint8_t USBD_ENDPx_DataUp( uint8_t endp, uint8_t *pbuf, uint16_t len )

{

        if( endp == ENDP1 )

        {

                if (USBD_Endp1_Busy)

                {

                        return USB_ERROR;

                }

                USB_SIL_Write( EP1_IN, pbuf, len );

                USBD_Endp1_Busy = 1;

                SetEPTxStatus( ENDP1, EP_TX_VALID );

                

        }

    else if( endp == ENDP2 )

        {

                if (USBD_Endp2_Busy)

                {

                        return USB_ERROR;

                }

                USB_SIL_Write( EP2_IN, pbuf, len );

                USBD_Endp2_Busy = 1;

                SetEPTxStatus( ENDP2, EP_TX_VALID );

        }

    else if( endp == ENDP3 )

    {

        if (USBD_Endp3_Busy)

        {

            return USB_ERROR;

        }

        USB_SIL_Write( EP3_IN, pbuf, len );

        USBD_Endp3_Busy = 1;

        SetEPTxStatus( ENDP3, EP_TX_VALID );

    }

        else

        {

                return USB_ERROR;

        }

        return USB_SUCCESS;

}

依葫芦画瓢,根据端点不同改一下即可实现不同的端点数据上报。
最后,希望大家样机调试成功!!!
---------------------
作者:lilijin1995
链接:https://bbs.21ic.com/icview-3271062-1-1.html
来源:21ic.com
此文章已获得原创/原创奖标签,著作权归21ic所有,任何人未经允许禁止转载。

 

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 32单片机USB调试助手是一种用于调试32单片机USB接口的工具。它通常由硬件和软件两部分组成。 硬件部分包括USB接口电路和适配器电路。USB接口电路是将单片机的IO口与USB接口相连接的电路,它负责将单片机的数据传输到计算机上。适配器电路是用来保证单片机与电脑之间的信号兼容的电路。 软件部分则是通过编写相关的程序代码实现对USB接口的调试。这些程序代码通常能够实现串口转USB功能,将单片机的串口信号转换为USB信号,从而实现单片机与计算机之间的数据传输。 通过32单片机USB调试助手,可以方便地对单片机上的USB接口进行调试,包括发送和接收数据。通过调试助手提供的接口,可以将需要调试的信号接入到单片机上,通过软件部分进行调试和观察数据的传输情况。 使用32单片机USB调试助手可以大大简化开发过程,提高调试效率。它可以帮助工程师快速发现和排除在单片机与电脑之间的通信问题,使开发过程更加高效和可靠。 总而言之,32单片机USB调试助手是一款用于调试32单片机USB接口的工具,它结合了硬件和软件部分的设计,能够帮助工程师快速定位和解决通信问题,提高开发效率。 ### 回答2: 32单片机USB调试助手是一种用于32位单片机开发的工具,它可以帮助开发人员通过USB接口进行调试和功能测试。该助手通常由硬件和软件两部分组成。 硬件部分包括一个USB转串口芯片和一些外围电路。USB转串口芯片可以将单片机的串口信号转换为USB信号,并与计算机进行通信。外围电路可以提供电源和信号的隔离,保护单片机不受到计算机的干扰。 软件部分主要是USB驱动程序和调试工具。USB驱动程序可以将计算机上的USB接口与单片机进行连接,实现数据的传输和通信。调试工具可以帮助开发人员对单片机程序进行调试和监测,例如查看变量数值、调用函数、跟踪程序执行等。 使用32单片机USB调试助手可以带来很多好处。首先,它可以简化调试的过程,减少开发时间和成本。通过USB接口进行调试,相较于传统的串口调试可以更方便地连接和操作。其次,它可以提高调试的精度和可靠性。USB接口的传输速度更快,信号传输更稳定,有助于发现和解决潜在的问题。最后,它还可以提供更多的功能和扩展性。32单片机USB调试助手通常有丰富的调试功能,如监测和追踪程序执行、实时查看变量值等,同时还可以通过软件升级和固件更新进行功能的扩展和升级。 总而言之,32单片机USB调试助手是一种方便、高效且可靠的开发工具,能够帮助开发人员更好地进行32位单片机的开发和调试工作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值