电脑重启后,但注册的HID设备没有重启,导致设备失效不能工作,请问如何解决?

我的芯片用的是CH549L,注册成HID设备下的第0x0C项consumer类型应用。目的是通过旋钮调节电脑音量。

正常使用都没有问题。但是有一个BUG是,部分时候电脑关机以后重启,这时候设备就不能工作了,必须要重新插拔一下HID设备才行。

通过大量测试后初步判断,是因为电脑重启时,整个过程中电脑的USB口没有断电,以至于重新开机以后,HID设备并没有重启。电脑这边枚举的USB设备的数据已经清空了,但是我这个HID设备却还记着之前枚举的信息,这时候电脑没有检测到重新插入的USB设备的信号,就不会自动的重新枚举。导致旧的信息不能用了,以至于失效。

我尝试了一个简单的办法,就是人工判断设备失效以后,按一下一下我的板上的一个按钮,然后在程序里直接goto到程序的最开始,直接重新初始化,但是不能够解决这个问题。

所以我想问问,有没有什么能够检测到USB通信失效了的变量啊之类的?还有检测到失效以后,又如何通过软件重新让电脑枚举我的设备?

----------------------------

我的代码主要修改自给的demo代码里面的“CompositeKM.C”文件。上面附上我的描述符,USB中断服务函数没敢改。

/*设备描述符*/

UINT8C DevDesc[] = {

    0x12,  // bLength。描述符长度(18字节,十六进制为0x12),就是标志描述符数据结构的长度。

    0x01,  // bDescriptorType。描述符类型。0x01为设备描述符

    0x00,0x02,  // bcdUSB。设备使用的USB协议版本。表示形式0xJJMN版本JJ.M.N(JJ-主要版本号,M-次要版本号,N-次要版本)。

                // 例子:如果是USB2.0,写成:0200H。如果是USB1.1,写成。0110H 如果是USB3.11,写成:0311H。

                // 注意是小端模式,所以低字节写在前面

    0x00,  // bDeviceClass。类代码。表示设备类型。为0时指示用接口描述符来标识类别。详见https://www.usb.org/defined-class-codes

    0x00,  // bDeviceSubClass。子类型。

    0x00,  // bDeviceProtocol。设备使用的协议。

    THIS_ENDP0_SIZE,  // bMaxPackeSize0。端点一次最大传多少个字节。

    0x86,0x1a,  // idVender。厂商ID。

    0xe1,0xe6,  // idProduct。产品ID。

    0x00,0x01,  // bcdDevice。产品版本号。

    0x01,  // iManufacturer。描述厂商的字符串的索引。

    0x02,  // iProduct。描述产品的字符串的索引。

    0x00,  // iSerialNumber。描述产品序列号字符串的索引.

    0x01  // bNumConfigurations。指示设备有多少个配置

    };


/*字符串描述符*/

// 语言ID描述符

UINT8C  MyLangDescr[] = {

    0x04,  // bLength。描述符长度。

    0x03,  // bDescriptorType。描述符类型。语言ID描述符也是字符串描述符,类型为0x03。

    0x09, 0x04  // wLANGID[0]。要支持的语言ID号。

    // wLANGID[n]。有可能会支持多种语言。但是这里没写了。

    };

// 厂家信息描述符

UINT8C  MyManuInfo[] = {

    0x0E,  // bLength。描述符长度。

    0x03,  // bDescriptorType。描述符类型。字符串描述符类型为0x03。

    'C', 0, 'S', 0, 'Y', 0, '.', 0, 'U', 0, 'S', 0, 'B', 0  // bString。UNICODE编码的字符串。

    };

// 产品信息描述符

UINT8C  MyProdInfo[] = {

    0x0C,  // bLength。描述符长度。

    0x03,  // bDescriptorType。描述符类型。字符串描述符类型为0x03。

    'A', 0, 'u', 0, 'd', 0, 'i', 0, 'o', 0  // bString。UNICODE编码的字符串。

    };


/*HID类报表描述符*/

UINT8C ConsumerRepDesc[] =

{

    0x05,0x0C,  // Usage Page (Consumer)

    0x09,0x01,  // Usage(Consumer Control)

    0xA1,0x01,  // Collection (Application),                Main Items —— Collection —— Application

        0x15,0x00,  // Logical Minimum (0),                     Global Items —— Logical Minimum —— 0

        0x25,0x01,  // Logical Maximum (1),                     Global Items —— Logical Maximum —— 1

        0x75,0x01,  // Report Size (1),                         Global Items —— Report Size —— 1

        0x95,0x01,  // Report Count (1),                        Global Items —— Report Count —— 1


        0x09,0xCD,  // Usage(Play/Pause),开始暂停

        0x81,0x06,  // Input (Data, Value, Relative),

        0x09,0xB5,  // Usage(Scan Next Track),下一曲

        0x81,0x06,  // Input (Data, Value, Relative),

        0x09,0xB6,  // Usage(Scan Previous Track),上一曲

        0x81,0x06,  // Input (Data, Value, Relative),

        0x09,0xE2,  // Usage(Mute),静音

        0x81,0x06,  // Input (Data, Value, Relative),

        0x09,0xE9,  // Usage(Volume Increment),音量+

        0x81,0x06,  // Input (Data, Value, Relative),

        0x09,0xEA,  // Usage(Volume Decrement),音量-

        0x81,0x06,  // Input (Data, Value, Relative),

        0x09,0xB3,  // Usage(Fast Forward),快进

        0x81,0x02,  // Input (Data, Value, Absolute),

        0x09,0xB4,  // Usage(Rewind),倒带

        0x81,0x02,  // Input (Data, Value, Absolute),



    0xC0  // End Collection,                                Main Items —— End Collection

};


/*配置描述符集合(必须按照顺序)*/

UINT8C CfgDesc[] =

{

    // 标准配置描述符

    0x09,  // bLength。配置本描述符的长度。

    0x02,  // bDescriptorType。描述符类型。配置描述符为0x02。

    0x3b,0x00,  // wTotalLength。配置描述符集合总长度。

    0x01,  // bNumInterfaces。当前配置下面有多少个接口。

    0x01,  // bConfigurationValue。当前配置的标识。一个USB设备可能有多个配置,但是当前只能选择一种配置。

    0x00,  // iConfiguration。描述该配置的字符串的索引值。如果没有字符串,那这个值就是0。

    0xE0,  // bmAttributes。在这个配置下,设备的一些特性。

                // D7是保留位,默认为1;

                // D6表示供电方式,0是自供电,1是总线供电;

                // D5表示是否支持远程唤醒,为1表示设备支持远程唤醒;

                // D4~D0保留,默认为0。

    0x32,  // bMaxPower。配置设备需要的电流。单位是2ma。如果一个设备耗电量100ma,那么本字节设置为0x32即可。


    // 接口描述符(Consumer)

    0x09,  // bLength。配置本描述符的长度。

    0x04,  // bDescriptorType。描述符类型。接口描述符为0x04。

    0x00,  // bInterfaceNumber。接口编号。如果一个配置有多个接口的话,那么每个接口的编号都有一个独立的编号,编号从0开始递增。

    0x00,  // bAlternateSetting。备用接口编号。一般很少用,设置为0。

    0x01,  // bNumEndpoints。该接口使用的端点个数。

    0x03,  // bInterfaceClass。接口类。当设备描述符设备类型bDeviceClass为0时,也就是指示用接口描述符来标识类别。

    0x01,  // bInterfaceSubClass。接口子类。

    0x01,  // bInterfaceProtocol。接口协议。

    0x00,  // iInterface。此接口的字符串索引值。没有的话一般为0.

   

    // HID类描述符

    0x09,  // bLength。配置本描述符的长度。

    0x21,  // bDescriptorType。描述符类型。HID描述符为0x21。

    0x11,0x01,  // bcdHID。HID设备所遵循的HID版本号,为4位16进制的BCD码。1.0即0x0100,1.1即0x0101,2.0即0x0200。

    0x00,  // bCountryCode。HID设备国家/地区代码。

    0x01,  // bNumDescriptor。HID设备支持的下级描述符的数量。由于HID设备至少需要包括一个报告描述符,故其值至小为0x01,一般的HID设备也为1,也就是有一个报告描述符,物理描述符很少用到。

    0x22,  // bDescriptorTyep。下级描述符的类型。下级描述符第1个必须是报告描述符,所以这里存放报告描述符类型,报告描述符的类型为0x22。

    sizeof(ConsumerRepDesc)&0xFF,sizeof(ConsumerRepDesc)>>8,  // wDescriptorLength。下级描述符的长度

   

    // 端点描述符(Consumer)

    0x07,  // bLength。配置本描述符的长度。

    0x05,  // bDescriptorType。描述符类型。端点描述符为0x05。

    0x81,  // bEndpointAddress。

                // Bit 3…0: 端点编号;

                // Bit 6…4: 保留,默认为0;

                // Bit 7:如果是控制端点可以忽略,因为控制端点有两个方向,否则一般表示数据传输方向,0 = OUT endpoint   1 = IN endpoint。

    0x03,  // bmAttributes。Bits 1..0: 表示传输类型

                // 00 = Control-控制传输

                // 01 = Isochronous-同步传输

                // 10 = Bulk-批量传输

                // 11 = Interrupt-中断传输

                // Bits 7..2: 还没讲

    ENDP1_IN_SIZE,0x00,  // wMaxPackeSize(双字节)。表示当前配置下此端点能够接收或发送的最大数据包的大小。

    0x0a  // bInterval。查询时间。就是主机多久和设备通讯一次。低速和全速称为帧,一个值代表1ms。高速称为微帧,一个值代表125us。

};


有几个还需要确认的点:1、可以在USB中断函数中处理控制传输(回传描述符)的地方加个printf,看一下在开机时电脑是否有做枚举,通常电脑重启之后会重新枚举的。

2、在相同电脑环境下,其余的键盘鼠标能不能正常工作,是否有条件对USB数据线进行抓包,看一下关机到开机这个过程中您的代码行为和键鼠行为的差异。

3、你说的当出现这种问题的时候goto到开头重新运行,需要先将USB_CTRL清零,延时一会,在做USB的初始化,目的是撤销数据线上的上拉电阻,然后重新生效。此时是否能够正常工作起来。


4、解决这个问题,可以尝试使用SOF中断,通常电脑关机后总线会处于挂起,USB中断中会进入suspend,以这个时间节点等后续是否有SOF中断,如果超过若干SOF周期还是没有产生SOF中断,可以判断电脑处于关掉或者不和你通讯的状态,这个时候可以尝试执行3中的操作。具体逻辑可以测试后修改。


遇到了同样的问题,官方源码坑好多,难趟啊


只有登录才能回复,可以选择微信账号登录