使用 CH567 制作一个USB 鼠标

目标:使用CH567 在 USB1 上实现微软Microsoft 5-Button Mouse with IntelliEye(TM) 鼠标。

为了更好的进行调试,首先将UART 设置为 1M的速率,这样做的好处是:调试信息能够更快的发送出来同时避免因为调试影响 USB 的时序。在 MYdebug.h 中修改如下:

#define STDIO_BAUD_RATE 1000000  //波特率为1000000BPS

使用时,将一个 USB 转串口Dongle的 RX接在CH567的PA5引脚上(UART3),共地之后使用 1 000 000波特率即可看到信息。

描述符如下:

1.Device Descriptor

Device Descriptor Microsoft 5-Button Mouse with IntelliEye(TM)

OffsetFieldSizeValueDescription
0bLength112h
1bDescriptorType101hDevice
2bcdUSB20110hUSB Spec 1.1
4bDeviceClass100hClass info in Ifc Descriptors
5bDeviceSubClass100h
6bDeviceProtocol100h
7bMaxPacketSize0108h8 bytes
8idVendor2045Eh
10idProduct20039h
12bcdDevice20300h3.00
14iManufacturer101h"Microsoft"
15iProduct103h"Microsoft 5-Button Mouse with IntelliEye(TM)"
16iSerialNumber100h
17bNumConfigurations101h

2.Configuration Descriptor

Configuration Descriptor 1 Bus Powered, 100 mA

OffsetFieldSizeValueDescription
0bLength109h
1bDescriptorType102hConfiguration
2wTotalLength20022h
4bNumInterfaces101h
5bConfigurationValue101h
6iConfiguration100h
7bmAttributes1A0hBus Powered, Remote Wakeup
4..0: Reserved...00000 
5: Remote Wakeup..1..... Yes
6: Self Powered.0...... No, Bus Powered
7: Reserved (set to one)
(bus-powered for 1.0)
1....... 
8bMaxPower132h100 mA

Interface Descriptor 0/0 HID, 1 Endpoint

OffsetFieldSizeValueDescription
0bLength109h
1bDescriptorType104hInterface
2bInterfaceNumber100h
3bAlternateSetting100h
4bNumEndpoints101h
5bInterfaceClass103hHID
6bInterfaceSubClass101hBoot Interface
7bInterfaceProtocol102hMouse
8iInterface100h

HID Descriptor

OffsetFieldSizeValueDescription
0bLength109h
1bDescriptorType121hHID
2bcdHID20110h1.10
4bCountryCode100h
5bNumDescriptors101h
6bDescriptorType122hReport
7wDescriptorLength20048h72 bytes

Endpoint Descriptor 81 1 In, Interrupt, 10 ms

OffsetFieldSizeValueDescription
0bLength107h
1bDescriptorType105hEndpoint
2bEndpointAddress181h1 In
3bmAttributes103hInterrupt
1..0: Transfer Type......11 Interrupt
7..2: Reserved000000.. 
4wMaxPacketSize20004h4 bytes
6bInterval10Ah10 ms

3.Language ID Descriptor

04 03 09 04

4.String Index 3

Microsoft 5-Button Mouse with IntelliEye(TM)

5. String Index 1

Microsoft

6.SET_CONFIGURATION

7.SET_IDLE 暂时未实现

8. GET_DESCRIPTOR REPORT_DESCRIPTOR 这里返回 HID Descriptor

Interface 0 HID Report Descriptor Mouse

Item Tag (Value)Raw Data
Usage Page (Generic Desktop)05 01 
Usage (Mouse)09 02 
Collection (Application)A1 01 
    Usage (Pointer)09 01 
    Collection (Physical)A1 00 
        Usage Page (Button)05 09 
        Usage Minimum (Button 1)19 01 
        Usage Maximum (Button 5)29 05 
        Logical Minimum (0)15 00 
        Logical Maximum (1)25 01 
        Report Size (1)75 01 
        Report Count (5)95 05 
        Input (Data,Var,Abs,NWrp,Lin,Pref,NNul,Bit)81 02 
        Report Size (3)75 03 
        Report Count (1)95 01 
        Input (Cnst,Ary,Abs)81 01 
        Usage Page (Generic Desktop)05 01 
        Usage (X)09 30 
        Usage (Y)09 31 
        Usage (Wheel)09 38 
        Logical Minimum (-127)15 81 
        Logical Maximum (127)25 7F 
        Report Size (8)75 08 
        Report Count (3)95 03 
        Input (Data,Var,Rel,NWrp,Lin,Pref,NNul,Bit)81 06 
    End CollectionC0 
    Usage Page05 FF 
    Usage09 02 
    Logical Minimum (0)15 00 
    Logical Maximum (1)25 01 
    Report Size (1)75 01 
    Report Count (1)95 01 
    Feature (Data,Var,Abs,NWrp,Lin,NPrf,NNul,NVol,Bit)B1 22 
    Report Size (7)75 07 
    Report Count (1)95 01 
    Feature (Cnst,Ary,Abs,NWrp,Lin,Pref,NNul,NVol,Bit)B1 01 
End CollectionC0 

代码是从USB1_DevCH372 修改而来的,其中的一些关于Endpoint 2 3 4 的代码并未去除。主要的修改如下:

1. void   USB1DeviceInit( void )  // 初始化USB设备 中,因为鼠标是低速设备,进行如下修改:

R8_USB1_CTRL   = UCST_LS | bUC_INT_BUSY |bUC_DMA_EN;  //设备模式,低速,

2. 用我们自己的描述符替换之前代码中的,在 case USB_GET_DESCRIPTOR: 中。因为我们是 HID设备,所以需要多一个 HID描述符,增加了处理的代码:

// GET_DESCRIPTOR REPORT_DESCRIPTOR
case USB_DESCR_TYP_REPORT:
         printf("REPORT_DESCRIPTOR Start\n");
         pDescr = (PUINT8)( &HidDescr[0] );
         len = sizeof( HidDescr );
		 printf("REPORT_DESCRIPTOR End\n");
         break;

上述完成之后,插入设备,Windows 就会认为有一个USB 鼠标插入系统。为了让鼠标有动作,还需要在Main 中添加如下代码。简单的说有如下2点:

  1. 我们使用 UsbConfig 作为鼠标配置完成的标志,只有 Windows 读取完各种必要的信息之后,进行的数据传输才是有效的;
  2. 鼠标的动作就是放在 Ep1Buffer的 512 字节中,然后给R8_UEP1_TX_CTRL1寄存器赋值即可进行发送;
int main()
{
	Interrupt_init( 1<<INT_ID_USB1 );     /* 系统总中断开启 */
	SysClkInit();           /* 初始化系统时钟 */
	mInitSTDIO_UR3();       /* 调试信息初始化 printf调用 */

	USB1DeviceInit();			/* USB1Device Init */
	printf("USB1 Device Init!\n");

	while(1) {
		if (UsbConfig!=0) {

            printf("Move mouse\n");
            Ep1Buffer[0+512]=0;
            Ep1Buffer[1+512]=0x20;
            Ep1Buffer[2+512]=0;
            Ep1Buffer[3+512]=0;
            R16_UEP1_T_LEN1=4;
            R8_UEP1_TX_CTRL1 = (R8_UEP1_TX_CTRL1 & ~ MASK_UEP_T_RES) | UEP_T_RES_ACK;
            mDelaymS(100);

            Ep1Buffer[0+512]=0;
            Ep1Buffer[1+512]=0;
            Ep1Buffer[2+512]=0x20;
            Ep1Buffer[3+512]=0;
            R16_UEP1_T_LEN1=4;
            R8_UEP1_TX_CTRL1 = (R8_UEP1_TX_CTRL1 & ~ MASK_UEP_T_RES) | UEP_T_RES_ACK;
            mDelaymS(100);

            Ep1Buffer[0+512]=0;
            Ep1Buffer[1+512]=0xE0;
            Ep1Buffer[2+512]=0;
            Ep1Buffer[3+512]=0;
            R16_UEP1_T_LEN1=4;
            R8_UEP1_TX_CTRL1 = (R8_UEP1_TX_CTRL1 & ~ MASK_UEP_T_RES) | UEP_T_RES_ACK;
            mDelaymS(100);

            Ep1Buffer[0+512]=0;
            Ep1Buffer[1+512]=0;
            Ep1Buffer[2+512]=0xE0;
            Ep1Buffer[3+512]=0;
            R16_UEP1_T_LEN1=4;
            R8_UEP1_TX_CTRL1 = (R8_UEP1_TX_CTRL1 & ~ MASK_UEP_T_RES) | UEP_T_RES_ACK;
            mDelaymS(100);

		}
		mDelaymS(5000);
	};

    return 0;
}

整体上并不复杂,但是因为缺少示例,个人感觉整体代码比较乱,如果未来对于功能进行扩展可能会遇到麻烦。

完整代码下载

CH567 USB1 上面的 CH372 例子

这个例子是 Ch567 支持包里面带的demo, 将自己模拟为 Ch372 设备。

关键代码在 ch56x_usb1dev372.c 中如下:

/*
 ============================================================================
 Name        : ch56x_usb1dev372.c
 Author      : CC
 Version     :
 Copyright   : Your copyright notice
 Description : USB1 Device mode, simulate a device of CH372
 ============================================================================
 */

#include <stdio.h>
#include <nds32_isr.h>
#include <nds32_intrinsic.h>

#include "CH568SFR.H"
#include "MYdebug.H"
#include "ch56x_usb1dev372.h"


//------ 设备模式 USB1
// 设备描述符
const UINT8    MyDevDescr[] = { 0x12, 0x01, 0x00, 0x02, 0xFF, 0x80, 0x37, THIS_ENDP0_SIZE,
                                0x48, 0x43, 0x37, 0x55,              /* 厂商ID和产品ID */
                                0x00, 0x01, 0x01, 0x02, 0x00, 0x01
                              };
// 配置描述符
const UINT8    MyCfgDescr[] = { 0x09, 0x02, 0x4a, 0x00, 0x01, 0x01, 0x00, 0x80, 0x32,    //配置描述符
                                0x09, 0x04, 0x00, 0x00, 0x08, 0xFF, 0x80, 0x37, 0x00,    //接口描述符
                                0x07, 0x05, 0x84, 0x02, 0x00, 0x02, 0x00,                //端点描述符,
                                0x07, 0x05, 0x04, 0x02, 0x00, 0x02, 0x00,
                                0x07, 0x05, 0x83, 0x02, 0x00, 0x02, 0x00,
                                0x07, 0x05, 0x03, 0x02, 0x00, 0x02, 0x00,
                                0x07, 0x05, 0x82, 0x02, 0x00, 0x02, 0x00,
                                0x07, 0x05, 0x02, 0x02, 0x00, 0x02, 0x00,
                                0x07, 0x05, 0x81, 0x02, 0x00, 0x02, 0x0A,
                                0x07, 0x05, 0x01, 0x02, 0x00, 0x02, 0x0A,
                              };
// 语言描述符
const UINT8    MyLangDescr[] = { 0x04, 0x03, 0x09, 0x04 };
// 厂家信息
const UINT8    MyManuInfo[] = { 0x0E, 0x03, 'w', 0, 'c', 0, 'h', 0, '.', 0, 'c', 0, 'n', 0 };
// 产品信息
const UINT8    MyProdInfo[] = { 0x0C, 0x03, 'C', 0, 'H', 0, '5', 0, '6', 0, 'x', 0 };

__attribute__ ((aligned(4)))UINT8    Ep0Buffer[64 + 512*2] ;              // EP1+EP4 OUT&IN, must 4字节对齐
__attribute__ ((aligned(4)))UINT8   Ep1Buffer[512*2] ;                    // EP1 IN+OUT, must 4字节对齐
__attribute__ ((aligned(4)))UINT8   Ep2Buffer[512*2] ;                    // EP2 IN+OUT, must 4字节对齐
__attribute__ ((aligned(4)))UINT8   Ep3Buffer[512*2] ;                    // EP3 IN+OUT, must 4字节对齐
#define UsbSetupBuf         ((PUSB_SETUP_REQ)Ep0Buffer)
#define UsbEp1OUTBuf        ((PUINT8)&Ep1Buffer[0])
#define UsbEp1INBuf          ((PUINT8)&Ep1Buffer[512])
#define UsbEp2OUTBuf        ((PUINT8)&Ep2Buffer[0])
#define UsbEp2INBuf         ((PUINT8)&Ep2Buffer[512])
#define UsbEp3OUTBuf        ((PUINT8)&Ep3Buffer[0])
#define UsbEp3INBuf          ((PUINT8)&Ep3Buffer[512])
#define UsbEp4OUTBuf        ((PUINT8)&Ep0Buffer[64])
#define UsbEp4INBuf         ((PUINT8)&Ep0Buffer[64+512])

UINT16  SetupLen;                    /* USB控制传输传输长度 */
UINT8V  SetupReqCode;                /* USB控制传输命令码 */
UINT8V  UsbInterCfg;                 /* USB设备接口配置 */
PUINT8  pDescr;                      /* 上传指针 */
UINT8   DevAddr;                     /* 设备地址 */
UINT8   UsbConfig = 0;               // USB配置标志

/*******************************************************************************
* Function Name  : USB1DeviceInit
* Description    : USB1 设备模式初始化
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void    USB1DeviceInit( void )  // 初始化USB设备
{
        /* 全局寄存器 */
        R8_USB1_CTRL   = bUC_CLR_ALL;       //清空中断标志和FIFO
        R8_USB1_CTRL   = UCST_HS | bUC_INT_BUSY |bUC_DMA_EN;  //设备模式,高速,
        R8_USB1_SUSPEND = 0;                //不挂起
        R8_USB1_DEV_AD = 0x00;              //设备地址0
        R8_USB1_INT_FG = 0xff;              //清除中断标志
        R8_USB1_INT_EN = bUIE_FIFO_OV | bUIE_SUSPEND | bUIE_TRANSFER | bUIE_BUS_RST;        //中断使能
        R8_UEP4_1_MOD1 = 0;
        R8_UEP2_3_MOD1 = 0;

        /* 设备端点设置 */
//ENDP0
        R8_UEP0_TX_CTRL1 = UEP_T_RES_NAK;
        R8_UEP0_RX_CTRL1 = UEP_R_RES_ACK;
        R16_UEP0_MAX_LEN1 = THIS_ENDP0_SIZE;             //端点0接收包的最大长度为64字节
        R16_UEP0_DMA1 = (UINT32)Ep0Buffer;

//ENDP1
        R8_UEP1_TX_CTRL1 = UEP_T_RES_NAK | bUEP_AUTO_TOG;
        R8_UEP1_RX_CTRL1 = UEP_R_RES_ACK| bUEP_AUTO_TOG;
        R8_UEP4_1_MOD1 |= bUEP1_TX_EN | bUEP1_RX_EN;
        R16_UEP1_MAX_LEN1 = 512;
        R16_UEP1_DMA1 = (UINT32)Ep1Buffer;

//ENDP2
        R8_UEP2_TX_CTRL1 = UEP_T_RES_NAK| bUEP_AUTO_TOG;
        R8_UEP2_RX_CTRL1 = UEP_R_RES_ACK| bUEP_AUTO_TOG;
        R8_UEP2_3_MOD1 |= bUEP2_RX_EN | bUEP2_TX_EN;
        R16_UEP2_MAX_LEN1 = 512;
        R16_UEP2_DMA1 = (UINT32)Ep2Buffer;

//ENDP3
        R8_UEP3_TX_CTRL1 = UEP_T_RES_NAK | bUEP_AUTO_TOG;
        R8_UEP3_RX_CTRL1 = UEP_R_RES_ACK| bUEP_AUTO_TOG;
        R8_UEP2_3_MOD1 |= bUEP3_RX_EN | bUEP3_TX_EN;
        R16_UEP3_MAX_LEN1 = 512;
        R16_UEP3_DMA1 = (UINT32)Ep3Buffer;

//ENDP4
        R8_UEP4_TX_CTRL1 = UEP_T_RES_NAK;        //不支持自动翻转
        R8_UEP4_RX_CTRL1 = UEP_R_RES_ACK;
        R8_UEP4_1_MOD1 |= bUEP4_RX_EN | bUEP4_TX_EN;
        R16_UEP4_MAX_LEN1 = 512;

        R8_USB1_CTRL |= bUC_DEV_PU_EN;        //设备连接
}

/*******************************************************************************
* Function Name  : USB1DevIntDeal
* Description    : USB1 设备中断处理
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void USB1DevIntDeal(void)
{
        UINT8   intstatus;
        UINT16  i, l;

        if ( R8_USB1_INT_FG & UIF_TRANSFER )    // USB传输完成
        {
                if ( R8_USB1_INT_ST & bUIS_IS_NAK )    //接收到主机IN包
                {
                        printf("NAK\n");
                }
                else
                {
                        intstatus = R8_USB1_INT_ST & (MASK_UIS_TOKEN | MASK_UIS_ENDP);

                        if(intstatus == (UIS_TOKEN_OUT|4))             /* endpoint 4  下传 */
                        {
                                if(R8_USB1_INT_ST&bUIS_TOG_OK)
                                {
                                        R8_UEP4_RX_CTRL1 = R8_UEP4_RX_CTRL1 ^ 0x08;    //手动翻转
                                        l = R16_USB1_RX_LEN;
                                        for(i=0; i<l; i++)
                                                UsbEp4INBuf[i] = ~UsbEp4OUTBuf[i];        //按位取反上传
                                        R16_UEP4_T_LEN1 = l;
                                        R8_UEP4_TX_CTRL1 = (R8_UEP4_TX_CTRL1 & ~ MASK_UEP_T_RES) | UEP_T_RES_ACK;
                                }
                        }
                        if(intstatus == (UIS_TOKEN_OUT|3))             /* endpoint 3 下传 */
                        {
                                if(R8_USB1_INT_ST&bUIS_TOG_OK)
                                {
                                        //R8_UEP3_RX_CTRL1 = R8_UEP3_RX_CTRL1 ^ 0x08;    //翻转
                                        l = R16_USB1_RX_LEN;
                                        for(i=0; i<l; i++)
                                                UsbEp3INBuf[i] = ~UsbEp3OUTBuf[i];        //按位取反上传
                                        R16_UEP3_T_LEN1 = l;
                                        R8_UEP3_TX_CTRL1 = (R8_UEP3_TX_CTRL1 & ~ MASK_UEP_T_RES) | UEP_T_RES_ACK;
                                }
                        }
                        if(intstatus == (UIS_TOKEN_OUT|2))             /* endpoint 2 下传 */
                        {
                                if(R8_USB1_INT_ST&bUIS_TOG_OK)
                                {
                                        //R8_UEP2_RX_CTRL1 = R8_UEP2_RX_CTRL1 ^ 0x08;    //翻转
                                        l = R16_USB1_RX_LEN;
                                        for(i=0; i<l; i++)
                                                UsbEp2INBuf[i] = ~UsbEp2OUTBuf[i];    //按位取反上传
                                        R16_UEP2_T_LEN1 = l;
                                        R8_UEP2_TX_CTRL1 = (R8_UEP2_TX_CTRL1 & ~ MASK_UEP_T_RES) | UEP_T_RES_ACK;
                                }
                        }
                        if(intstatus == (UIS_TOKEN_OUT|1))             /* endpoint 1 下传 */
                        {
                                if(R8_USB1_INT_ST&bUIS_TOG_OK)
                                {
                                        //R8_UEP1_RX_CTRL1 = R8_UEP1_RX_CTRL1 ^ 0x08;    //翻转
                                        l = R16_USB1_RX_LEN;
                                        for(i=0; i<l; i++)
                                                UsbEp1INBuf[i] = ~UsbEp1OUTBuf[i];        //按位取反上传
                                        R16_UEP1_T_LEN1 = l;
                                        R8_UEP1_TX_CTRL1 = (R8_UEP1_TX_CTRL1 & ~ MASK_UEP_T_RES) | UEP_T_RES_ACK;
                                }
                        }
                        if(intstatus == (UIS_TOKEN_IN|4))             /* endpoint 4 上传 */
                        {
                                R8_UEP4_TX_CTRL1 = R8_UEP4_TX_CTRL1 ^ 0x08;     //手动翻转
                                R8_UEP4_TX_CTRL1 = (R8_UEP4_TX_CTRL1 & ~ MASK_UEP_T_RES) | UEP_T_RES_NAK;
                        }
                        if(intstatus == (UIS_TOKEN_IN|3))             /* endpoint 3 上传 */
                        {
                                R8_UEP3_TX_CTRL1 = (R8_UEP3_TX_CTRL1 & ~ MASK_UEP_T_RES) | UEP_T_RES_NAK;
                        }
                        if(intstatus == (UIS_TOKEN_IN|2))             /* endpoint 2 上传 */
                        {
                                R8_UEP2_TX_CTRL1 = (R8_UEP2_TX_CTRL1 & ~ MASK_UEP_T_RES) | UEP_T_RES_NAK;
                        }
                        if(intstatus == (UIS_TOKEN_IN|1))             /* endpoint 1 上传 */
                        {
                                R8_UEP1_TX_CTRL1 = (R8_UEP1_TX_CTRL1 & ~ MASK_UEP_T_RES) | UEP_T_RES_NAK;
                        }
                        if(intstatus == (UIS_TOKEN_SETUP))             /* endpoint 0# SETUP */
                        {
                                USB1Dev_EDP0_Setup_Deal();
                        }
                        if(intstatus == (UIS_TOKEN_IN))             /* endpoint 0# IN */
                        {
                                USB1Dev_EDP0_IN_Deal();
                        }
                        if(intstatus == (UIS_TOKEN_OUT))             /* endpoint 0# OUT */
                        {
                                USB1Dev_EDP0_OUT_Deal();
                        }
                }
                R8_USB1_INT_FG = UIF_TRANSFER;
        }
        else if ( R8_USB1_INT_FG & UIF_SUSPEND )   // USB总线挂起/唤醒完成
        {
                R8_USB1_INT_FG = UIF_SUSPEND;
                printf("Suspend\n");
        }
        else if( R8_USB1_INT_FG & UIF_BUS_RST )     //USB设备复位
        {
                R8_UEP0_TX_CTRL1 = UEP_T_RES_NAK;
                R8_UEP0_RX_CTRL1 = UEP_R_RES_ACK;
                R8_UEP1_TX_CTRL1 = UEP_T_RES_NAK | bUEP_AUTO_TOG;
                R8_UEP1_RX_CTRL1 = UEP_R_RES_ACK | bUEP_AUTO_TOG;
                R8_UEP2_TX_CTRL1 = UEP_T_RES_NAK | bUEP_AUTO_TOG;
                R8_UEP2_RX_CTRL1 = UEP_R_RES_ACK | bUEP_AUTO_TOG;
                R8_UEP3_TX_CTRL1 = UEP_T_RES_NAK | bUEP_AUTO_TOG;
                R8_UEP3_RX_CTRL1 = UEP_R_RES_ACK | bUEP_AUTO_TOG;
                R8_UEP4_TX_CTRL1 = UEP_T_RES_NAK;
                R8_UEP4_RX_CTRL1 = UEP_R_RES_ACK;
                R8_USB1_DEV_AD = 0;
                DevAddr = 0;
                R8_USB1_INT_FG = UIF_BUS_RST;
                printf("Reset!\n");
        }
        else if( R8_USB1_INT_FG & UIF_FIFO_OV )     //FIFO溢出中断
        {
                R8_USB1_INT_FG = UIF_FIFO_OV;
                printf("FIFOOV!\n");
        }
}

/*******************************************************************************
* Function Name  : USB1Dev_EDP0_Setup_Deal
* Description    : USB1 设备模式端点0 setup处理
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void USB1Dev_EDP0_Setup_Deal(void)
{
        UINT16 len;
        UINT8  status, i;
        UINT8  buf[8];

        /* 获取SETUP包 */
        len = R16_USB1_RX_LEN;
        if ( len == sizeof( USB_SETUP_REQ ) )
        {
//        for(i=0; i<8; i++)    printf("%02x ", Ep0Buffer[i]);    printf("\n");

                SetupLen = (UINT16)(UsbSetupBuf->wLengthH)<<8|(UsbSetupBuf->wLengthL);        //请求数据长度
                SetupReqCode = UsbSetupBuf->bRequest;                                        //命令码

                /* 分析并处理当前的SETUP包 */
                len = 0;                                                                      // 默认为成功并且上传0长度
                status = 0;
                if (( UsbSetupBuf->bRequestType & USB_REQ_TYP_MASK ) != USB_REQ_TYP_STANDARD )  /* 非标准请求 */
                {
                        status = 0xFF;  // 操作失败
                }
                else                                                                                /* 标准请求 */
                {
                        switch( SetupReqCode )            // 请求码
                        {
                        case USB_GET_DESCRIPTOR:
                                switch( UsbSetupBuf->wValueH )
                                {
                                case 0x01:                                                                  // 设备描述符
                                        pDescr = (PUINT8)( &MyDevDescr[0] );
                                        len = sizeof( MyDevDescr );
                                        break;
                                case 0x02:                                                                  // 配置描述符
                                        pDescr = (PUINT8)( &MyCfgDescr[0] );
                                        len = sizeof( MyCfgDescr );
                                        break;
                                case 0x03:                                                                  // 字符串描述符
                                        switch( UsbSetupBuf->wValueL )
                                        {
                                        case 1:                                                                //厂商字符串描述符
                                                pDescr = (PUINT8)( &MyManuInfo[0] );
                                                len = sizeof( MyManuInfo );
                                                break;
                                        case 2:                                                                //产品字符串描述符
                                                pDescr = (PUINT8)( &MyProdInfo[0] );
                                                len = sizeof( MyProdInfo );
                                                break;
                                        case 0:                                                                //语言描述符
                                                pDescr = (PUINT8)( &MyLangDescr[0] );
                                                len = sizeof( MyLangDescr );
                                                break;
                                        default:
                                                status = 0xFF;  // 不支持的字符串描述符
                                                break;
                                        }
                                        break;
                                default:
                                        status = 0xFF;  // 不支持的描述符类型
                                        break;
                                }
                                break;
                        case USB_SET_ADDRESS:
                                DevAddr = UsbSetupBuf->wValueL;  // 暂存USB设备地址
                                break;
                        case USB_GET_CONFIGURATION:
                                buf[0] = UsbConfig;
                                pDescr = buf;
                                if ( SetupLen >= 1 ) len = 1;
                                break;
                        case USB_SET_CONFIGURATION:
                                UsbConfig = UsbSetupBuf->wValueL;
                                break;
                        case USB_CLEAR_FEATURE:
                                if ( ( UsbSetupBuf->bRequestType & USB_REQ_RECIP_MASK ) == USB_REQ_RECIP_ENDP )    // 清除端点
                                {
                                        switch( UsbSetupBuf->wIndexL )          //复位同步触发位,清除STALL
                                        {
                                        case 0x82:
                                                R8_UEP2_TX_CTRL1 = R8_UEP2_TX_CTRL1 & ~ ( MASK_UEP_T_RES ) | UEP_T_RES_NAK;
                                                break;
                                        case 0x02:
                                                R8_UEP2_RX_CTRL1 = R8_UEP2_RX_CTRL1 & ~ ( MASK_UEP_R_RES ) | UEP_R_RES_ACK;
                                                break;
                                        case 0x81:
                                                R8_UEP1_TX_CTRL1 = R8_UEP1_TX_CTRL1 & ~ ( MASK_UEP_T_RES ) | UEP_T_RES_NAK;
                                                break;
                                        case 0x01:
                                                R8_UEP1_RX_CTRL1 = R8_UEP1_RX_CTRL1 & ~ ( MASK_UEP_R_RES ) | UEP_R_RES_ACK;
                                                break;
                                        default:
                                                status = 0xFF;  // 不支持的端点
                                                break;
                                        }
                                }
                                else if (( UsbSetupBuf->bRequestType & USB_REQ_RECIP_MASK ) == USB_REQ_RECIP_DEVICE)         //清除设备
                                {
                                        /* 设备休眠 */
                                }
                                else status = 0xFF;  // 不是端点不支持
                                break;
                        case USB_SET_FEATURE:
                                if ( ( UsbSetupBuf->bRequestType & USB_REQ_RECIP_MASK ) == USB_REQ_RECIP_ENDP )    // 设置端点
                                {
                                        switch((UINT16)(UsbSetupBuf->wIndexH)<<8|(UsbSetupBuf->wIndexL))          //复位同步触发位,清除STALL
                                        {
                                        case 0x82:
                                                R8_UEP2_TX_CTRL1 |= UEP_T_RES_STALL;
                                                break;
                                        case 0x02:
                                                R8_UEP2_RX_CTRL1 |= UEP_T_RES_STALL;
                                                break;
                                        case 0x81:
                                                R8_UEP1_TX_CTRL1 |= UEP_T_RES_STALL;
                                                break;
                                        case 0x01:
                                                R8_UEP1_RX_CTRL1 |= UEP_T_RES_STALL;
                                                break;
                                        default:
                                                status = 0xFF;  // 不支持的端点
                                                break;
                                        }
                                }
                                else if (( UsbSetupBuf->bRequestType & USB_REQ_RECIP_MASK ) == USB_REQ_RECIP_DEVICE)         //设置设备
                                {
                                        /* 设备唤醒 */
                                }
                                else status = 0xFF;  // 不是端点不支持
                                break;
                        case USB_SET_INTERFACE:
                                UsbInterCfg = UsbSetupBuf->wIndexL;
                                break;
                        case USB_GET_INTERFACE:
                                buf[0] = UsbInterCfg;
                                pDescr = buf;
                                if ( SetupLen >= 1 ) len = 1;
                                break;
                        case USB_GET_STATUS:
                                if (( UsbSetupBuf->bRequestType & USB_REQ_RECIP_MASK ) == USB_REQ_RECIP_ENDP )    // 获取端点状态
                                {
                                        switch((UINT16)(UsbSetupBuf->wIndexH)<<8|(UsbSetupBuf->wIndexL))
                                        {
                                        case 0x82:
                                                buf[0] = ((R8_UEP2_TX_CTRL1&0x11) == UEP_T_RES_STALL) ? 0x01 : 0x00 ;
                                                break;
                                        case 0x02:
                                                buf[0] = ((R8_UEP2_RX_CTRL1&0x11) == UEP_T_RES_STALL) ? 0x01 : 0x00 ;
                                                break;
                                        case 0x81:
                                                buf[0] = ((R8_UEP1_TX_CTRL1&0x11) == UEP_T_RES_STALL) ? 0x01 : 0x00 ;
                                                break;
                                        case 0x01:
                                                buf[0] = ((R8_UEP1_RX_CTRL1&0x11) == UEP_T_RES_STALL) ? 0x01 : 0x00 ;
                                                break;
                                        default:
                                                status = 0xFF;  // 不支持的端点
                                                break;
                                        }
                                        buf[1] = 0x00;
                                        pDescr = buf;
                                        len = 2;
                                }
                                else if (( UsbSetupBuf->bRequestType & USB_REQ_RECIP_MASK ) == USB_REQ_RECIP_DEVICE )      // 获取设备状态
                                {
                                        buf[0] = 0x00;        //填充当前设备状态
                                        buf[1] = 0x00;
                                        pDescr = buf;
                                        len = 2;
                                }
                                break;
                        default:
                                status = 0xFF;  // 操作失败
                                break;
                        }
                }
        }
        else
        {
                status = 0xFF;      // SETUP包长度错误
        }
        if ( status == 0xFF )    // 操作失败
        {
                SetupReqCode = 0xFF;
                R8_UEP0_RX_CTRL1 = UEP_DATA1 | UEP_R_RES_STALL;
                R8_UEP0_TX_CTRL1 = UEP_DATA1 | UEP_T_RES_STALL;
        }
        else
        {
                SetupLen = (SetupLen<len) ? SetupLen : len;                                            //这次传输上传数据长度
                if(SetupLen)
                {
                        if(UsbSetupBuf->bRequestType & 0x80)                                                                     //上传数据
                        {
                                len = (SetupLen<THIS_ENDP0_SIZE) ? SetupLen : THIS_ENDP0_SIZE;        //本次数据上传长度
                                memcpy( Ep0Buffer, pDescr, len );  /* 加载上传数据 */
                                R16_UEP0_T_LEN1 = len;
                                R8_UEP0_TX_CTRL1 = UEP_DATA1 | UEP_T_RES_ACK;                    // 默认数据包是DATA1
                                SetupLen -= len;
                                pDescr += len;
                        }
                        else                                                                                                     //下传数据
                        {
                                R16_UEP0_T_LEN1 = 0;  // 虽然尚未到状态阶段,但是提前预置上传0长度数据包以防主机提前进入状态阶段
                                R8_UEP0_RX_CTRL1 = UEP_DATA1 | UEP_R_RES_ACK;                    // 默认数据包是DATA1
                        }
                }
                else
                {
                        R16_UEP0_T_LEN1 = 0;
                        R8_UEP0_TX_CTRL1 = UEP_DATA1 | UEP_T_RES_ACK;        //状态阶段
                }
        }
}

/*******************************************************************************
* Function Name  : USB1Dev_EDP0_IN_Deal
* Description    : USB1 设备模式端点0 IN处理
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void USB1Dev_EDP0_IN_Deal(void)
{
        UINT16 len;

        switch(SetupReqCode)
        {
        case USB_GET_DESCRIPTOR:
                len = (SetupLen<THIS_ENDP0_SIZE) ? SetupLen : THIS_ENDP0_SIZE;        //本次数据上传长度
                memcpy( Ep0Buffer, pDescr, len );  /* 加载上传数据 */
                R16_UEP0_T_LEN1 = len;
                R8_UEP0_TX_CTRL1 ^= UEP_DATA1;  // 翻转
                SetupLen -= len;
                pDescr += len;
                break;
        case USB_SET_ADDRESS:
                R16_UEP0_T_LEN1 = 0;
                R8_UEP0_TX_CTRL1 = UEP_DATA0 | UEP_T_RES_NAK;
                R8_USB1_DEV_AD = DevAddr;
                break;
        default:
                R16_UEP0_T_LEN1 = 0;
                R8_UEP0_TX_CTRL1 = UEP_DATA0 | UEP_T_RES_NAK;
                R8_UEP0_RX_CTRL1 = UEP_DATA0 | UEP_R_RES_ACK;
                break;
        }
}

/*******************************************************************************
* Function Name  : USB1Dev_EDP0_OUT_Deal
* Description    : USB1 设备模式端点0 OUT处理
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
UINT16 USB1Dev_EDP0_OUT_Deal(void)
{
        UINT16 len;
        len = R16_USB1_RX_LEN;
        return (len);
}

和之前的相比,修改2个位置:

1.项目名称,位于 .project 文件中:

<name>USB1_DevCH372</name>

2. 对代码进行了格式化,使用4个空格代替代码中的 Tab

国产双USB 32位双高速单片机 CH567:IDE 的安装

最近开始玩 CH567, 选择它的原因是:国产,速度足够快带有独立的2个 High Speed USB Port。这次介绍的是它的 IDE 的安装。

https://www.bilibili.com/video/BV16Y411a79g/?spm_id_from=333.337.search-card.all.click&vd_source=cf6121716e06cb669a27c10276f9c920