三、HID 描述符
HID 设备除了支持 USB 设备的 5 种标准描述符之外,还支持 HID 设备特有的 3 种描述符。这些描述符是:1、USB 标准描述符:设备、配置、接口、端点和字符串描述符;2、HID 特有的描述符: HID 、报表(Report )和实体(Physical )描述符。从描述符的关联关系看, HID 描述符是关联于接口。所以如果一个 HID 设备有 2 个端点,设备不需要每个端点有一个 HID 描述符,具体参考如下代码:
设备描述符
struct _DEVICE_DEscriptOR_STRUCT
{
BYTE bLength; //设备描述符的字节数大小
BYTE bDescriptorType; //描述符类型编号,为0x01
WORD bcdUSB; //USB版本号
BYTE bDeviceClass; //USB分配的设备类代码,0x01~0xfe为标准设备类,0xff为厂商自定义类型,0x00不是在设备描述符中定义的,如HID
BYTE bDeviceSubClass; //USB分配的子类代码,同上,值由USB规定和分配的,HID设备此值为0
BYTE bDeviceProtocl; //USB分配的设备协议代码,同上HID设备此值为0
BYTE bMaxPacketSize0; //端点0的最大包的大小
WORD idVendor; //厂商编号
WORD idProduct; //产品编号
WORD bcdDevice; //设备出厂编号
BYTE iManufacturer; //描述厂商字符串的索引
BYTE iProduct; //描述产品字符串的索引
BYTE iSerialNumber; //描述设备序列号字符串的索引
BYTE bNumConfiguration; //可能的配置数量
}
配置描述符
struct _CONFIGURATION_DEscriptOR_STRUCT
{
BYTE bLength; //配置描述符的字节数大小
BYTE bDescriptorType; //描述符类型编号,为0x02
WORD wTotalLength; //配置所返回的所有数量的大小
BYTE bNumInterface; //此配置所支持的接口数量
BYTE bConfigurationVale; //Set_Configuration命令需要的参数值
BYTE iConfiguration; //描述该配置的字符串的索引值
BYTE bmAttribute; //供电模式的选择
BYTE MaxPower; //设备从总线提取的最大电流
}
字符描述符
struct _STRING_DEscriptOR_STRUCT
{
BYTE bLength; //字符串描述符的字节数大小
BYTE bDescriptorType; //描述符类型编号,为0x03
BYTE SomeDescriptor[36]; //UNICODE编码的字符串
}
接口描述符
struct _INTERFACE_DEscriptOR_STRUCT
{
BYTE bLength; //接口描述符的字节数大小
BYTE bDescriptorType; //描述符类型编号,为0x04
BYTE bInterfaceNunber; //接口的编号
BYTE bAlternateSetting; //备用的接口描述符编号
BYTE bNumEndpoints; //该接口使用端点数,不包括端点0
BYTE bInterfaceClass; //接口类型 HID设备此值为0x03
BYTE bInterfaceSubClass; //接口子类型 HID设备此值为0或者1
BYTE bInterfaceProtocol; //接口所遵循的协议
BYTE iInterface; //描述该接口的字符串索引值
}
端点描述符
struct _ENDPOIN_DEscriptOR_STRUCT
{
BYTE bLength; //端点描述符的字节数大小
BYTE bDescriptorType; //描述符类型编号,为0x05
BYTE bEndpointAddress; //端点地址及输入输出属性
BYTE bmAttribute; //端点的传输类型属性
WORD wMaxPacketSize; //端点收、发的最大包的大小
BYTE bInterval; //主机查询端点的时间间隔
}
四、MM32 MCU HID代码实现
本次我们采用MM32L373 miniboard作为测试开发板。为了方便大家使用MM32 MCU的HID功能,我们已经封装好全部代码,用户不需要自己配置以上的那些描述符等参数,只需要了解MM32 MCU HID的VID和PID以及如何处理HID的数据接收和发送即可。
软件资源如下:
以下为函数初始化配置及相关全局变量定义内容,代码如下:
#define USBD_POWER 0
#define USBD_MAX_PACKET0 64
#define USBD_DEVDESC_IDVENDOR 0x2F81
#define USBD_DEVDESC_IDPRODUCT 0x0001
以上是定义的MM32 MCU HID设备VID和PID,灵动微电子已经获得USB组织授权的VID和PID。当设备插入电脑上,可以查看到如上标识的HID设备,如图1所示:
图1 PC设备管理器列表
对于MM32 MCU的HID功能来说,在使用HID功能之前先调用USB初始化函数来初始化USB协议栈。
int main(void)
{
// USB Device Initialization and connect
usbd_init();
usbd_connect(__TRUE);
while (!usbd_configured()) // Wait for USB Device to configure
{
}
while (1)
{
}
}
然后就是HID数据收发处理函数,USB数据处理函数如下:
static volatile uint8_t USB_ResponseIdle;
static HID_queue HID_Cmd_queue;
void hid_send_packet()
{
uint8_t *sbuf;
int slen;
IF (HID_queue_get_send_buf(&HID_Cmd_queue, &sbuf, &slen))
{
if (slen > USBD_HID_OUTREPORT_MAX_SZ)
{
util_assert(0);
}
else
{
usbd_hid_get_report_trigger(0, sbuf, USBD_HID_OUTREPORT_MAX_SZ);
}
}
}
// USB HID Callback: when system initializes
void usbd_hid_init(void)
{
USB_ResponseIdle = 1;
HID_queue_init(&HID_Cmd_queue);
}
// USB HID Callback: when data needs to be prepared for the host
int usbd_hid_get_report(U8 rtype, U8 rid, U8 *buf, U8 req)
{
uint8_t *sbuf;
int slen;
switch (rtype)
{
case HID_REPORT_INPUT:
switch (req)
{
case USBD_HID_REQ_PERIOD_UPDATE:
break;
case USBD_HID_REQ_EP_CTRL:
case USBD_HID_REQ_EP_INT:
if (HID_queue_get_send_buf(&HID_Cmd_queue, &sbuf, &slen))
{
if (slen > USBD_HID_OUTREPORT_MAX_SZ)
{
util_assert(0);
}
else
{
memcpy(buf, sbuf, slen);
return (USBD_HID_OUTREPORT_MAX_SZ);
}
}
else if (req == USBD_HID_REQ_EP_INT)
{
USB_ResponseIdle = 1;
}
break;
}
break;
case HID_REPORT_FEATURE:
break;
}
return (0);
}
// USB HID override function return 1 if the activity is trivial or response is null
__attribute__((weak))
uint8_t usbd_hid_no_activity(U8 *buf)
{
return 0;
}
// USB HID Callback: when data is received from the host
void usbd_hid_set_report(U8 rtype, U8 rid, U8 *buf, int len, U8 req)
{
uint8_t *rbuf;
main_LED_state_t led_next_state = MAIN_LED_FLASH;
switch (rtype)
{
case HID_REPORT_OUTPUT:
if (len == 0)
{
break;
}
if (buf[0] == ID_HID_TransferAbort)
{
HID_TransferAbort = 1;
break;
}
// execute and store to HID_queue
if (HID_queue_execute_buf(&HID_Cmd_queue, buf, len, &rbuf))
{
if (usbd_hid_no_activity(rbuf) == 1)
{
//revert HID LED to default if the response is null
led_next_state = MAIN_LED_DEF;
}
if (USB_ResponseIdle)
{
hid_send_packet();
USB_ResponseIdle = 0;
}
}
else
{
util_assert(0);
}
break;
case HID_REPORT_FEATURE:
break;
}
}
void HID_queue_init(HID_queue *queue)
{
queue->recv_idx = 0;
queue->send_idx = 0;
queue->free_count = FREE_COUNT_INIT;
queue->send_count = SEND_COUNT_INIT;
}
BOOL HID_queue_get_send_buf(HID_queue *queue, uint8_t **buf, int *len)
{
if (queue->send_count)
{
queue->send_count--;
*buf = queue->USB_Request[queue->send_idx];
*len = queue->resp_size[queue->send_idx];
queue->send_idx = (queue->send_idx + 1) % HID_PACKET_COUNT;
queue->free_count++;
return (__TRUE);
}
return (__FALSE);
}
BOOL HID_queue_execute_buf(HID_queue *queue, const uint8_t *reqbuf, int len, uint8_t **retbuf)
{
uint32_t rsize;
if (queue->free_count > 0)
{
if (len > HID_PACKET_SIZE)
{
len = HID_PACKET_SIZE;
}
queue->free_count--;
memcpy(queue->USB_Request[queue->recv_idx], reqbuf, len);
rsize = HID_ExecuteCommand(reqbuf, queue->USB_Request[queue->recv_idx]);
queue->resp_size[queue->recv_idx] = rsize & 0xFFFF; //get the response size
*retbuf = queue->USB_Request[queue->recv_idx];
queue->recv_idx = (queue->recv_idx + 1) % HID_PACKET_COUNT;
queue->send_count++;
return (__TRUE);
}
return (__FALSE);
}
如上,我们只需要实现修改如下HID_ExecuteCommand可处理我们收到的收据,并且填入我们发送数据出去队列即可发送出去。
本次我们使用HID工具V1.3.3测试我们的HID功能,打开软件如图2所示:
图2 HID工具连接
点击选择HID设备,选择我们MM32 MCU的HID设备,如图3:
图3 HID工具设备选择
点击连接,发送数据,可以看到图4结果,发送两次共128字节,收到两次数据,共128字节。
图4 HID工具数据收发
以上就是MM32 MCU USB的HID功能,下一节我们继续介绍MM32 MCU USB的WINUSB功能。
|