打印
[RISC-V MCU 应用开发]

第八十三章、CH32V103应用教程——USB模拟鼠标键盘设备

[复制链接]
6724|8
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 RISCVLAR 于 2021-3-16 15:13 编辑

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

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

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

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

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

#define   DevEP0SIZE   0x40

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

const UINT8 KeyRepDesc[] =
{
        0x05, 0x01,         // Usage page Generatic Desktop
        0x09, 0x06,         // Usage keyboard
        0xa1, 0x01,         // Collation Application
        0x05, 0x07,         // Usafe page (key code)
        0x19, 0xe0,         // Usage Min ( E0 -->  L_CTL)
        0x29, 0xe7,         // Usage MAX ( E7 --> R_GUI )
        0x15, 0x00,         // Logical min
        0x25, 0x01,         // Logical max
        0x95, 0x08,         // Report count ( 8 )
        0x75, 0x01,         // Report size  ( 1 )
        0x81, 0x02,         // Input ( Data, Variable, Absolute )
        0x95, 0x08,         // Report count ( 8 )
        0x75, 0x01,         // Report size  ( 1 )
        0x81, 0x01,         // Input ( const )
        0x05, 0x08,         // Usage page( LED )
        0x19, 0x01,         // Usage min ( 1 )
        0x29, 0x03,         // Usage max ( 3 )
        0x95, 0x03,         // Report count ( 3 )
        0x75, 0x01,         // Report size ( 1 )
        0x91, 0x02,         // Output ( Data, Variable, Absolute )
        0x95, 0x01,         // Report count ( 1 )
        0x75, 0x05,         // Report size ( 5 )
        0x91, 0x01,         // Output ( const )
        0x05, 0x07,         // Usage page ( key code )
        0x19, 0x00,         // Usage min ( 0H )
        0x2a, 0xff, 0x00,   // Usage max ( FFH )
        0x15, 0x00,         // Logical min ( 0H )
        0x26, 0xff, 0x00,   // Logical max ( FFH )
        0x95, 0x06,         // Report count ( 6 )
        0x75, 0x08,         // Report size ( 8 )
        0x81, 0x00,         // Input ( Data, Array, Absolute )
        0xc0                // End collection
};

const UINT8  MouseRepDesc[]=
{
        0x05,0x01,
        0x09,0x02,
        0xA1,0x01,
        0x09,0x01,
        0xA1,0x00,
        0x05,0x09,
        0x19,0x01,
        0x29,0x03,
        0x15,0x00,
        0x25,0x01,
        0x95,0x03,
        0x75,0x01,
        0x81,0x02,
        0x95,0x01,
        0x75,0x05,
        0x81,0x03,
        0x05,0x01,
        0x09,0x30,
        0x09,0x31,
        0x09,0x38,
        0x15,0x81,
        0x25,0x7f,
        0x75,0x08,
        0x95,0x03,
        0x81,0x06,
        0xC0,
        0xC0
};

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

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

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

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

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

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

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

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

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

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

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

/**********************************************************/
volatile UINT8   Ready = 0;
volatile UINT8   Endp1Busy = 0;  //传输完成控制标志位
volatile UINT8   Endp2Busy = 0;
UINT8   DevConfig;
UINT8   SetupReqCode;
UINT16  SetupReqLen;
UINT8   HIDKey[8];
UINT8   HIDMouse[4];
const   UINT8 *pDescr;

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

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

/*******************************************************************************
* Function Name  : USB_DevTransProcess
* Description    : USB device transfer process.
* Input          : None
* Return         : None
*******************************************************************************/
void USB_DevTransProcess( void )
{
    UINT8  len, chtype;
    UINT8  intflag, errflag = 0;

    intflag = R8_USB_INT_FG;

    if( intflag & RB_UIF_TRANSFER )
    {
        switch ( R8_USB_INT_ST & ( MASK_UIS_TOKEN | MASK_UIS_ENDP ) )
        {
            case UIS_TOKEN_SETUP:
                R8_UEP0_CTRL = RB_UEP_R_TOG | RB_UEP_T_TOG | UEP_R_RES_ACK | UEP_T_RES_NAK;
                len = R8_USB_RX_LEN;

                if ( len == sizeof( USB_SETUP_REQ ) )
                {
                    SetupReqLen = pSetupReqPak->wLength;
                    SetupReqCode = pSetupReqPak->bRequest;
                    chtype = pSetupReqPak->bRequestType;

                    len = 0;
                    errflag = 0;

                    if ( ( pSetupReqPak->bRequestType & USB_REQ_TYP_MASK ) != USB_REQ_TYP_STANDARD )
                    {
                        switch(SetupReqCode)
                        {
                            case 0x01:    //GetReport
                                len = 1;
                                pEP0_DataBuf[0] = 0xaa;
                                break;
                            case 0x0a:
                                R8_UEP0_T_LEN = 0;
                                break;    //这个一定要有
                            case 0x09:
                                Ready = 1;
                                break;
                            default:
                                errflag = 0xFF;
                        }
                    }
                    else
                    {
                        switch( SetupReqCode )
                        {
                            case USB_GET_DESCRIPTOR:
                            {
                                switch( ((pSetupReqPak->wValue)>>8) )
                                {
                                    case USB_DESCR_TYP_DEVICE:
                                        pDescr = MyDevDescr;
                                        len = MyDevDescr[0];
                                        break;

                                    case USB_DESCR_TYP_CONFIG:
                                        pDescr = MyCfgDescr;
                                        len = MyCfgDescr[2];
                                        break;

                                    case USB_DESCR_TYP_STRING:
                                        switch( (pSetupReqPak->wValue)&0xff )
                                        {
                                            case 0:
                                                pDescr = MyLangDescr;
                                                len = MyLangDescr[0];
                                                break;

                                            case 1:
                                                pDescr = MyManuInfo;
                                                len = MyManuInfo[0];
                                                break;

                                            case 2:
                                                pDescr = MyProdInfo;
                                                len = MyProdInfo[0];
                                                break;

                                            case 3:
                                                pDescr = MyProductIDInfo;
                                                len = MyProductIDInfo[0];
                                                break;

                                            default:
                                                errflag = 0xFF;
                                                break;
                                        }
                                        break;

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

                                    default :
                                        errflag = 0xff;
                                        break;
                                }

                                if( SetupReqLen>len )   SetupReqLen = len;
                                len = (SetupReqLen >= DevEP0SIZE) ? DevEP0SIZE : SetupReqLen;
                                memcpy( pEP0_DataBuf, pDescr, len );
                                pDescr += len;
                            }
                                break;

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

                            case USB_GET_CONFIGURATION:
                                pEP0_DataBuf[0] = DevConfig;
                                if ( SetupReqLen > 1 ) SetupReqLen = 1;
                                break;

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

                            case USB_CLEAR_FEATURE:
                                if ( ( pSetupReqPak->bRequestType & USB_REQ_RECIP_MASK ) == USB_REQ_RECIP_ENDP )
                                {
                                    switch( (pSetupReqPak->wIndex)&0xff )
                                    {
                                    case 0x82:
                                        R8_UEP2_CTRL = (R8_UEP2_CTRL & ~( RB_UEP_T_TOG|MASK_UEP_T_RES )) | UEP_T_RES_NAK;
                                        break;

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

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

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

                                    default:
                                        errflag = 0xFF;
                                        break;
                                    }
                                }
                                else    errflag = 0xFF;
                                break;

                            case USB_GET_INTERFACE:
                                pEP0_DataBuf[0] = 0x00;
                                if ( SetupReqLen > 1 ) SetupReqLen = 1;
                                break;

                            case USB_GET_STATUS:
                                pEP0_DataBuf[0] = 0x00;
                                pEP0_DataBuf[1] = 0x00;
                                if ( SetupReqLen > 2 ) SetupReqLen = 2;
                                break;

                            default:
                                errflag = 0xff;
                                break;
                        }
                    }
                }
                else    errflag = 0xff;

                if( errflag == 0xff)
                {
                    R8_UEP0_CTRL = RB_UEP_R_TOG | RB_UEP_T_TOG | UEP_R_RES_STALL | UEP_T_RES_STALL;
                }
                else
                {
                    if( chtype & 0x80 )
                    {
                        len = (SetupReqLen>DevEP0SIZE) ? DevEP0SIZE : SetupReqLen;
                        SetupReqLen -= len;
                    }
                    else  len = 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;
                }
                break;

            case UIS_TOKEN_IN:
                switch( SetupReqCode )
                {
                    case USB_GET_DESCRIPTOR:
                        len = SetupReqLen >= DevEP0SIZE ? DevEP0SIZE : SetupReqLen;
                        memcpy( pEP0_DataBuf, pDescr, len );
                        SetupReqLen -= len;
                        pDescr += len;
                        R8_UEP0_T_LEN = len;
                        R8_UEP0_CTRL ^= RB_UEP_T_TOG;
                        break;

                    case USB_SET_ADDRESS:
                        R8_USB_DEV_AD = (R8_USB_DEV_AD&RB_UDA_GP_BIT) | SetupReqLen;
                        R8_UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
                        break;

                    default:
                        R8_UEP0_T_LEN = 0;
                        R8_UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
                        break;
                }
                break;

            case UIS_TOKEN_OUT:
                len = R8_USB_RX_LEN;
                break;

            case UIS_TOKEN_OUT | 1:
                if ( R8_USB_INT_ST & RB_UIS_TOG_OK )
                {
                    len = R8_USB_RX_LEN;
                    DevEP1_OUT_Deal( len );
                }
                break;

            case UIS_TOKEN_IN | 1:
                R8_UEP1_T_LEN = 0;
                Endp1Busy = 0;
                R8_UEP1_CTRL = (R8_UEP1_CTRL & ~MASK_UEP_T_RES) | UEP_T_RES_NAK;
                break;

            case UIS_TOKEN_OUT | 2:
                if ( R8_USB_INT_ST & RB_UIS_TOG_OK )
                {
                    len = R8_USB_RX_LEN;
                    DevEP2_OUT_Deal( len );
                }
                break;

            case UIS_TOKEN_IN | 2:
                R8_UEP2_T_LEN = 0;
                Endp2Busy = 0;
                R8_UEP2_CTRL = (R8_UEP2_CTRL & ~MASK_UEP_T_RES) | UEP_T_RES_NAK;
                break;

            default :
                break;
        }
        R8_USB_INT_FG = RB_UIF_TRANSFER;
    }
    else if( intflag & RB_UIF_BUS_RST )
    {
        R8_USB_DEV_AD = 0;
        R8_UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
        R8_UEP1_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK | RB_UEP_AUTO_TOG;
        R8_UEP2_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK | RB_UEP_AUTO_TOG;
        R8_USB_INT_FG |= RB_UIF_BUS_RST;
    }
    else if( intflag & RB_UIF_SUSPEND )
    {
        if ( R8_USB_MIS_ST & RB_UMS_SUSPEND ) {;}
        else{;}
        R8_USB_INT_FG = RB_UIF_SUSPEND;
    }
    else
    {
        R8_USB_INT_FG = intflag;
    }
}

void HIDValueHandle()
{
    UINT8 i;
    i = USART_ReceiveData(USART2);
    printf( "%c\n", (UINT8)i );
    switch(i)
    {
    //鼠标数据上传示例
        case 'L':                                               //左键
            HIDMouse[0] = 0x01;
            while( Endp2Busy )
            {
                ;                                               //如果忙(上一包数据没有传上去),则等待。
            }
            Endp2Busy = 1;                                      //设置为忙状态
            memcpy(pEP2_IN_DataBuf, HIDMouse, 4);
            DevEP2_IN_Deal(4);
            HIDMouse[0] = 0;                                    //抬起
            while( Endp2Busy )
            {
                ;                                               //如果忙(上一包数据没有传上去),则等待。
            }
            Endp2Busy = 1;                                      //设置为忙状态
            memcpy(pEP2_IN_DataBuf, HIDMouse, 4);
            DevEP2_IN_Deal(4);
            break;
        case 'R':                                               //右键
            HIDMouse[0] = 0x02;
            while( Endp2Busy )
            {
                ;                                               //如果忙(上一包数据没有传上去),则等待。
            }
            Endp2Busy = 1;                                      //设置为忙状态
            memcpy(pEP2_IN_DataBuf, HIDMouse, 4);
            DevEP2_IN_Deal(4);
            HIDMouse[0] = 0;                                    //抬起
            while( Endp2Busy )
            {
                ;                                               //如果忙(上一包数据没有传上去),则等待。
            }
            Endp2Busy = 1;                                      //设置为忙状态
            memcpy(pEP2_IN_DataBuf, HIDMouse, 4);
            DevEP2_IN_Deal(4);
            break;
    //键盘数据上传示例
        case 'A':                                               //A键
            HIDKey[2] = 0x04;                                   //按键开始
            while( Endp1Busy )
            {
                ;                                               //如果忙(上一包数据没有传上去),则等待。
            }
            Endp1Busy = 1;                                      //设置为忙状态
            memcpy(pEP1_IN_DataBuf, HIDKey, 8);
            DevEP1_IN_Deal(8);
            HIDKey[2] = 0;                                      //按键结束
            while( Endp1Busy )
            {
                ;                                               //如果忙(上一包数据没有传上去),则等待。
            }
            Endp1Busy = 1;                                      //设置为忙状态
            memcpy(pEP1_IN_DataBuf, HIDKey, 8);
            DevEP1_IN_Deal(8);
            break;
        case 'Q':                                               //CAP键
            HIDKey[2] = 0x39;
            while( Endp1Busy )
            {
                ;                                               //如果忙(上一包数据没有传上去),则等待。
            }
            Endp1Busy = 1;                                      //设置为忙状态
            memcpy(pEP1_IN_DataBuf, HIDKey, 8);
            DevEP1_IN_Deal(8);
            HIDKey[2] = 0;                                      //按键结束
            while( Endp1Busy )
            {
                ;                                               //如果忙(上一包数据没有传上去),则等待。
            }
            Endp1Busy = 1;                                      //设置为忙状态
            memcpy(pEP1_IN_DataBuf, HIDKey, 8);
            DevEP1_IN_Deal(8);
            break;
        default:                                                //其他
            break;
    }
}
/*******************************************************************************
* Function Name  : Set_USBConfig
* Description    : Set USB clock.
* Input          : None
* Return         : None
*******************************************************************************/
void USBHD_ClockCmd(UINT32 RCC_USBCLKSource,FunctionalState NewState)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, NewState);
    EXTEN->EXTEN_CTR |= EXTEN_USBHD_IO_EN;
    RCC_USBCLKConfig(RCC_USBCLKSource);                      //USBclk=PLLclk/1.5=48Mhz
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_USBHD,NewState);
}

/*******************************************************************************
* Function Name  : main
* Description    : Main program.
* Input          : None
* Return         : None
*******************************************************************************/
int main(void)
{
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    Delay_Init();
    USART2_Printf_Init(115200);

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

    pEP0_RAM_Addr = EP0_Databuf;
    pEP1_RAM_Addr = EP1_Databuf;
    pEP2_RAM_Addr = EP2_Databuf;

    USBHD_ClockCmd(RCC_USBCLKSource_PLLCLK_1Div5,ENABLE);
    USB_DeviceInit();
    NVIC_EnableIRQ( USBHD_IRQn );

    while(1)
    {
        printf("Ready1=%d\n",Ready);
        if(Ready)
        {
            HIDValueHandle();
        }
        Delay_Ms(500);
    }
}

/*******************************************************************************
* Function Name  : DevEP1_OUT_Deal
* Description    : Deal device Endpoint 1 OUT.
* Input          : l: Data length.
* Return         : None
*******************************************************************************/
void DevEP1_OUT_Deal( UINT8 l )
{
    ;
}

/*******************************************************************************
* Function Name  : DevEP2_OUT_Deal
* Description    : Deal device Endpoint 2 OUT.
* Input          : l: Data length.
* Return         : None
*******************************************************************************/
void DevEP2_OUT_Deal( UINT8 l )
{
    ;
}

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

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

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

498.8 KB

使用特权

评论回复

相关帖子

沙发
zealerlu| | 2021-11-20 22:09 | 只看该作者
感谢楼主~~

使用特权

评论回复
板凳
quark| | 2022-2-23 12:22 | 只看该作者
感谢

使用特权

评论回复
地板
probedog| | 2022-2-25 10:39 | 只看该作者
分享的资料挺全

使用特权

评论回复
5
onlycook| | 2022-3-7 15:31 | 只看该作者
感谢分享

使用特权

评论回复
6
gygp| | 2023-2-2 09:31 | 只看该作者
普通i/o可以模拟USB键盘吗

使用特权

评论回复
7
wwppd| | 2023-2-6 12:41 | 只看该作者
用单片机怎么制作电脑USB或者PS/2键盘

使用特权

评论回复
8
mikewalpole| | 2023-2-6 16:06 | 只看该作者
ch375 模拟HID鼠标吗?              

使用特权

评论回复
9
ingramward| | 2023-2-7 20:43 | 只看该作者
如何用单片机模拟键盘控制电脑               

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

132

主题

293

帖子

41

粉丝