|
通过上一节的工作,PC机能成功发现USB设备的插入,但是PC要求我们提供此设置的驱动程序,这是因为原固件驱动定义的设备类型是“测试设备类”,此时要求用户提供自定义的设备驱动程序,如下图所示:
USB主机是通过请求设备的相关描述符来判断设备类型的,所以我们只需要修改相关描述符就能实现我们想要设备类型。描述符的配置位于Descriptor.h和Descriptor.c文件中。
下面我们来把Easy USB 51 Programer改造成一个鼠标。
第一步:修改Descriptor.c
1、找到设备描述结构体
-
- code USB_DEVICE_DESCRIPTOR DeviceDescr =
- {
- sizeof(USB_DEVICE_DESCRIPTOR),
- USB_DEVICE_DESCRIPTOR_TYPE,
- 0x00,0x01,
- USB_CLASS_CODE_TEST_CLASS_DEVICE,
- 0, 0,
- EP0_PACKET_SIZE,
- 0x71,0x04,
- 0x66,0x01,
- 0x00,0x01,
- 0, 0, 0,
- 1
- };
将其中的
- USB_CLASS_CODE_TEST_CLASS_DEVICE,
注释掉,并在其后加入下面这行代码
同时也将
修改为
2、找到接口描述符结构体
-
- {
- sizeof(USB_INTERFACE_DESCRIPTOR),
- USB_INTERFACE_DESCRIPTOR_TYPE,
- 0,
- 0,
- NUM_ENDPOINTS,
- USB_CLASS_CODE_TEST_CLASS_DEVICE,
- USB_SUBCLASS_CODE_TEST_CLASS_D12,
- USB_PROTOCOL_CODE_TEST_CLASS_D12,
- 0
- },
将其中的
- USB_CLASS_CODE_TEST_CLASS_DEVICE,
注释掉,并在其后加入下面一行代码
第二步:编译源程序并写入主控芯片
将我们的编程器插入PC机,激动人心的时刻到了,我们看到PC机能成功识别到我们插入的设备是人体工学设备(就是HID设备)了:
但是接下来却提示:
打开设备管理器我们发现人体学输入设备下面有一个USB设备出现了“!”号,这个设备就是我们的Easy USB 51 Programer,但为什么有问题呢?
看来是我们对USB设备的枚举过程不够了解,您可以先看看百合电子工作室发表的文章:USB开发基础--实例讲解USB的枚举(配置)过程和USB HID 设备类协议入门。
第三步:继续修改
《USB HID 设备类协议入门》一文中提到,除了HID的三个特定描述符组成对HID设备的解释外,5个标准描述符中与HID设备有关的部分有:
- 设备描述符中bDeviceClass、bDeviceSubClass和bDeviceProtocol三个字段的值必须为零。
- 接口描述符中bInterfaceClass的值必须为0x03,bInterfaceSubClass的值为0或1,为1表示HID设备符是一个启动设备(Boot Device,一般对PC机而言才有意义,意思是BIOS启动时能识别并使用您的HID设备,且只有标准鼠标或键盘类设备才能成为Boot Device。 bInterfaceProtocol的取值含义如下表所示:
HID接口描述符中bInterfaceProtocol的含义 |
bInterfaceProtocol的取值(十进制) |
含义 |
0 |
NONE |
1 |
鼠标 |
2 |
键盘 |
3~255 |
保留 |
所以我们还有两处没有修改到:
A、找到:
- USB_SUBCLASS_CODE_TEST_CLASS_D12,
将其注释掉并在其后加入以下代码:
B、找到:
- USB_PROTOCOL_CODE_TEST_CLASS_D12,
将其注释掉,并在其后加入以下代码:
虽然我们将接口描述符中的bInterfaceProtocol设为1(代表鼠标),但这只是针对启动设备(Boot Device)而言有才有效果(即PC机的BIOS加载后能识别和使用),但真正对HID设备的数据流格式进行描述的是报告描述符,所以 bInterfaceProtocol的取值实际意义不大。
对标准描述符的修改已经完成了,但是到目前为止还不能让我们的HID设备运行起来,因为HID设备类还有3个特定的描述符,它们分别是:HID描述符、报告描述符和实体描述符,其中实体描述符是可选描述符。
USB主机在请求HID设备的配置描述符时,设备返回的描述符为:配置描述符、接口描述符、HID描述符、端点描述符。HID描述符里包含了其附属的描述的类型和长度(如报告描述符),然后主机再根据HID描述符的信息请求其相关的描述符。所以我们现在来完成HID设备类的两必须的类特定描述符:HID描述符和报告描述符。
1、修改Descriptor.c
A、将以下代码
修改为
B、在以下代码
-
- {
- sizeof(USB_ENDPOINT_DESCRIPTOR),
- USB_ENDPOINT_DESCRIPTOR_TYPE,
- 0x81,
- USB_ENDPOINT_TYPE_INTERRUPT,
- EP1_PACKET_SIZE,0x00,
- 10
- },
前面加入代码:
-
- {
- sizeof(USB_HID_DESCRIPTOR),
- USB_HID_DESCRIPTOR_TYPE,
- SWAP16(0x0110),
- 0x21,
- NUM_SUB_DESCRIPTORS,
- USB_REPORT_DESCRIPTOR_TYPE,
- SWAP16(sizeof(MouseReportDescriptor)),
- },
其中USB_HID_DESCRIPTOR为HID描述符结构体,在Descriptor中定义;SWAP16为宏定义,作用是交换双字节数据的高字节和低字节,在Descriptor.h中定义;MouseReportDescriptor为HID报告描述符,我们可利用HID Descriptor tool来生成,这是一个标准鼠标的报告描述符。
C、在代码
-
- #define USB_PROTOCOL_CODE_TEST_CLASS_D12 0xB0
后面加入以下代码:
-
- code char MouseReportDescriptor[63] = {
- 0x05, 0x01,
- 0x09, 0x06,
- 0xa1, 0x01,
- 0x05, 0x07,
- 0x19, 0xe0,
- 0x29, 0xe7,
- 0x15, 0x00,
- 0x25, 0x01,
- 0x75, 0x01,
- 0x95, 0x08,
- 0x81, 0x02,
- 0x95, 0x01,
- 0x75, 0x08,
- 0x81, 0x03,
- 0x95, 0x05,
- 0x75, 0x01,
- 0x05, 0x08,
- 0x19, 0x01,
- 0x29, 0x05,
- 0x91, 0x02,
- 0x95, 0x01,
- 0x75, 0x03,
- 0x91, 0x03,
- 0x95, 0x06,
- 0x75, 0x08,
- 0x15, 0x00,
- 0x25, 0xFF,
- 0x05, 0x07,
- 0x19, 0x00,
- 0x29, 0x65,
- 0x81, 0x00,
- 0xc0
- };
D、将以下代码注释掉
-
- {
- sizeof(USB_ENDPOINT_DESCRIPTOR),
- USB_ENDPOINT_DESCRIPTOR_TYPE,
- 0x81,
- USB_ENDPOINT_TYPE_INTERRUPT,
- EP1_PACKET_SIZE,0x00,
- 10
- },
-
- {
- sizeof(USB_ENDPOINT_DESCRIPTOR),
- USB_ENDPOINT_DESCRIPTOR_TYPE,
- 0x1,
- USB_ENDPOINT_TYPE_INTERRUPT,
- EP1_PACKET_SIZE,0x00,
- 10
- },
E、将以下代码
-
- {
- sizeof(USB_ENDPOINT_DESCRIPTOR),
- USB_ENDPOINT_DESCRIPTOR_TYPE,
- 0x82,
- USB_ENDPOINT_TYPE_BULK,
- EP2_PACKET_SIZE,0x00,
- 10
- },
-
- {
- sizeof(USB_ENDPOINT_DESCRIPTOR),
- USB_ENDPOINT_DESCRIPTOR_TYPE,
- 0x2,
- USB_ENDPOINT_TYPE_BULK,
- EP2_PACKET_SIZE,0x00,
- 10
- }
修改为
-
- {
- sizeof(USB_ENDPOINT_DESCRIPTOR),
- USB_ENDPOINT_DESCRIPTOR_TYPE,
- 0x82,
- USB_ENDPOINT_TYPE_INTERRUPT,
- EP2_PACKET_SIZE,0x00,
- 0x01
- },
-
- {
- sizeof(USB_ENDPOINT_DESCRIPTOR),
- USB_ENDPOINT_DESCRIPTOR_TYPE,
- 0x02,
- USB_ENDPOINT_TYPE_INTERRUPT,
- EP2_PACKET_SIZE,0x00,
- 0x01
- }
2、修改Descriptor.h
A、将以下代码
-
- typedef struct _USB_DESCRIPTOR {
- USB_CONFIGURATION_DESCRIPTOR ConfigDescr;
- USB_INTERFACE_DESCRIPTOR InterfaceDescr;
- USB_ENDPOINT_DESCRIPTOR EP1_TXDescr;
- USB_ENDPOINT_DESCRIPTOR EP1_RXDescr;
- USB_ENDPOINT_DESCRIPTOR EP2_TXDescr;
- USB_ENDPOINT_DESCRIPTOR EP2_RXDescr;
- } USB_DESCRIPTOR, *PUSB_DESCRIPTOR;
修改为
-
- typedef struct _USB_DESCRIPTOR {
- USB_CONFIGURATION_DESCRIPTOR ConfigDescr;
- USB_INTERFACE_DESCRIPTOR InterfaceDescr;
- USB_HID_DESCRIPTOR HidDescr;
-
-
- USB_ENDPOINT_DESCRIPTOR EP2_TXDescr;
- USB_ENDPOINT_DESCRIPTOR EP2_RXDescr;
- } USB_DESCRIPTOR, *PUSB_DESCRIPTOR;
B、找到以下代码段:
-
- typedef struct _USB_HUB_DESCRIPTOR {
- INT8U bDescriptorLength;
- INT8U bDescriptorType;
- INT8U bNumberOfPorts;
- INT16U wHubCharacteristics;
- INT8U bPowerOnToPowerGood;
- INT8U bHubControlCurrent;
-
-
- INT8U bRemoveAndPowerMask[64];
- } USB_HUB_DESCRIPTOR, *PUSB_HUB_DESCRIPTOR;
在其后加入代码:
- #define NUM_SUB_DESCRIPTORS 1
-
-
- typedef struct _HID_DESCRIPTOR
- {
- INT8U bLength;
- INT8U bDescriptorType;
- INT16U bcdHID;
- INT8U bCountryCode;
- INT8U bNumDescriptors;
-
- INT8U bSubDescriptorType;
- INT16U wDescriptorLength;
- }USB_HID_DESCRIPTOR,*PUSB_HID_DESCRIPTOR;
C、找到以下代码:
- #define USB_POWER_DESCRIPTOR_TYPE 0x06
在其后加入
- #define USB_HID_DESCRIPTOR_TYPE 0x21 //HID描述符类型
- #define USB_REPORT_DESCRIPTOR_TYPE 0x22 //报告描述符
D、找到以下代码:
- #define __HIDDESCRIPTOR_H__
在其后加入
- #define SWAP16(x) ((((INT16U)(x))<<8)|(((INT16U)(x))>>8))
-
- extern code char MouseReportDescriptor[63];
E、将以下代码
修改为
F、将以下代码
- #define CONFIG_DESCRIPTOR_LENGTH sizeof(USB_CONFIGURATION_DESCRIPTOR) \
- + sizeof(USB_INTERFACE_DESCRIPTOR) \
- + (NUM_ENDPOINTS * sizeof(USB_ENDPOINT_DESCRIPTOR))
修改为
- #define CONFIG_DESCRIPTOR_LENGTH sizeof(USB_CONFIGURATION_DESCRIPTOR) + sizeof(USB_INTERFACE_DESCRIPTOR) + sizeof(USB_HID_DESCRIPTOR) + (NUM_ENDPOINTS * sizeof(USB_ENDPOINT_DESCRIPTOR))
3、修改Chap_9.c
以下修改内容主要与实现与HID类相关请求有关。
A、将get_descriptor函数注释掉,并修改为
- void get_descriptor(void)
- {
- INT8U bDescriptor = MSB(ControlData.DeviceRequest.wValue);
-
-
-
- if (bDescriptor == USB_DEVICE_DESCRIPTOR_TYPE)
- {
- code_transmit((INT8U *)&DeviceDescr, sizeof(USB_DEVICE_DESCRIPTOR));
- }
- else if (bDescriptor == USB_CONFIGURATION_DESCRIPTOR_TYPE)
- {
- if (ControlData.DeviceRequest.wLength > CONFIG_DESCRIPTOR_LENGTH)
- {
- ControlData.DeviceRequest.wLength = CONFIG_DESCRIPTOR_LENGTH;
- }
- code_transmit((INT8U *)&(usb_descr.ConfigDescr), ControlData.DeviceRequest.wLength);
-
-
-
- }
-
-
- else if(bDescriptor == USB_REPORT_DESCRIPTOR_TYPE)
- {
-
-
- ControlData.pData=(INT8U *)(MouseReportDescriptor);
-
-
-
-
- code_transmit((INT8U *)(MouseReportDescriptor),sizeof(MouseReportDescriptor));
- }
-
- else
- {
-
- stall_ep0();
-
- }
-
- }
B、将control_handler函数中被注销掉的以下语句取消注释以复原
C、找到以下代码
-
-
-
- code void (*StandardDeviceRequest[])(void) =
- {
- get_status,
- clear_feature,
- reserved,
- set_feature,
- reserved,
- set_address,
- get_descriptor,
- reserved,
- get_configuration,
- set_configuration,
- get_interface,
- set_interface,
- reserved,
- reserved,
- reserved,
- reserved
- };
在后面加入
-
-
-
- code void (*ClassDeviceRequest[])(void) =
- {
- reserved,
- get_report,
- get_idle,
-
- reserved,
- reserved,
- reserved,
- reserved,
- reserved,
- reserved,
- set_report,
- set_idle,
-
- reserved
- };
D、增加以下代码
-
- EPPFLAGS bEPPflags;
- 后加入
- unsigned char idle;
-
-
-
-
-
- void get_report(void)
- {
-
- }
-
-
-
-
-
- void set_report(void)
- {
- }
-
-
-
-
-
- void get_idle(void)
- {
- code_transmit(&idle,1);
- }
-
-
-
-
-
- void set_idle(void)
- {
-
- while(D12_SelectEndpoint(1)&0x01);
- single_transmit(0, 0);
- idle=ControlData.DeviceRequest.wValue;
- }
4、修改Chap_9.h
加入以下代码
- #define LSB(x) ((INT8U)(x))
-
- extern void get_idle(void);
- extern void set_idle(void);
- extern void set_report(void);
- extern void get_report(void);
第四步、再次编译程序,将程序写入主控芯片
再次怀着激动和不安的心情将我们的Easy USB 51 Programer插入电脑,这次终于实现了我们的USB鼠标了!如图所示,“USB人体学输入设备”已经没有“!”了,而且在“鼠标和其它指针设备”中多了一项“HID-compliant mouse”,这就是我们“设计”的鼠标了。
下载源程序
但是这个实例还不够生动,因为它并没有鼠标的一点功能,现在我们就来实现部分鼠标的功能。我们可以利用扩展板 EXT-BOARD-A 上的两个按键来模拟鼠标指针的左移和右移,当K1按下鼠标指针左移、K2按下时鼠标指针右移(当然您也可以用它来模拟鼠标左键和鼠标右键)。实现这个功能非常简单,只需修改main函数,其内容如下:
- main()
- {
- unsigned char i = 0;
- signed char cKeyIn[4];
-
- if (Init_D12()!=0)
- return;
-
- IT0 = 0;
-
- EX0 = 1;
- PX0 = 0;
- EA = 1;
-
-
-
- while(1)
- {
- usbserve();
- if(bEPPflags.bits.configuration)
- {
-
- if(bEPPflags.bits.ep2_rxdone )
- {
- bEPPflags.bits.ep2_rxdone = 0;
- }
-
- K1 = 1;
- K2 = 1;
-
- for(i=0;i<100;i++);
-
- if(~K1 & K2)
- {
- cKeyIn[0]=0;
- cKeyIn[1]=-1;
- cKeyIn[2]=0;
- cKeyIn[3]=0;
-
- D12_WriteEndpoint(5,4,cKeyIn);
- }
- else if(K1 & ~K2)
- {
- cKeyIn[0]=0;
- cKeyIn[1]=1;
- cKeyIn[2]=0;
- cKeyIn[3]=0;
-
- D12_WriteEndpoint(5,4,cKeyIn);
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- }
- }
- }
USB鼠标各个键的定义由报告描述符定义,此实例中的报告描述符描述了4个字节,第一个字节表示按键,第二个字节表示x轴(即鼠标左右移动,0表示不动,正值表示往右移,负值表示往左移),第三个字节表示y轴(即鼠标上下移动,0表示不动,正值表示往下移动,负值表示往上移动),第四个字节表示鼠标滚轮(正值为往上滚动,负值为往下滚动)。
USB鼠标报告各位(字节)用法及含义 |
字节 |
位 |
用法及含义 |
0 |
0 |
Button1 |
1 |
Button2 |
2 |
Button3 |
3 |
Button4 |
4 |
Button5 |
5~7 |
Not Used |
1 |
8~15 |
指针X轴方向位移,相对量(若此值为1,指针在原来基础上向右移动一个像素,若为-1则向左移动一个像素) |
2 |
16~23 |
指针Y轴方向位移,相对量(若此值为1,指针在原来基础上向下移动一个像素,若为-1则向上移动一个像素) |
3 |
24~31 |
滚轮 |
利用EXT-BOARD-A上的K1和K2键来控制鼠标指针
下载源程序
|
|