C#网络编程

自定义 HID 设备

作者:陈广 日期:2019-6-16


鼠标和键盘做完后,我们已经对 HID 设备有所了解,但大多数时候,我们希望使用 HID 设备象 UART 一样与上位机进行通信。本文我们就来讲讲如何定义自己的 HID 设备,并向上位机发送或接收上位机的数据。

定义一个自定义 HID 设备

首先进行 STM32CubeMX 设置,步骤按照 这篇文章 所述。或者您之前按照我的文章操作,还留有鼠标或键盘的程序,则删除 STM32CubeMX 生成项目文件夹的所有文件的,只保留 STM32CubeMX 文件(后缀名:.ioc)。双击 .ioc 文件打开 STM32CubeMX 进行设置。

无论是重新做还是打开旧的 STM32CubeMX 文件,自定义 HID 设备不同之处就在一个地方。打开【Middleware】选项卡下的【USB_DEVICE】,在右边窗口的【Class For FS IP】组合框中选择【Custom Human Interface Device Class(HID)】项。如下图所示:

图 1:自定义 HID 设备的设置

打开生成的代码文件,可以看到,上两篇文章中生成的【usbd_desc.c】文件已经没有,而是生成了两个新的文件【usbd_customhid.c】和【usbd_custom_hid_if.c】,如下图所示:

图 2:自定义 HID 设备的生成代码

打开【usbd_customhid.c】文件,可以观察到,所有指定设备类型的地方都被指定为0x00,即非键盘鼠标设备,如下图所示:

图 3:设备编码

打开【usbd_desc.c】文件,找到 VID 和 PID 宏定义,将其更改为与做鼠标和键盘时的值不一样即可。

图 4:更改 VID 和 PID

生成自定义 HID 设备报告描述符

在【usbd_customhid.c】中不再有报告描述符了,去哪了呢?打开【usbd_custom_hid_if.c】文件,找到存放描述符的地方CUSTOM_HID_ReportDesc_FS,不过里面只有两个数字,需要自行生成设备描述符。

图 5:设备描述符声明位置

接下来打开 HID Descriptor Tool 软件,生成我们自己的设备描述符。我们模拟 UART 设备,每次可以向上位机发送一个字节的数据,也可以从上位机接收一个字节的数据。生成代码如下图所示:

图 6:编写设备描述符

这里声明了一个字节的,范围为 0~255 的发送数据,和一个字节的接收数据。将其另存为.h文件,打开后,注意数组长度,拷贝数组中的代码,将其粘贴至【usbd_custom_hid_if.c】文件中的CUSTOM_HID_ReportDesc_FS中,如下图所示:

图 7:完成设备描述符

此时数组长度已经不对,需要更改,鼠标右键单击表示数组长度的宏USBD_CUSTOM_HID_REPORT_DESC_SIZE,在弹出菜单中选择【Go To Definition ...】项,找到此宏的声明地点,将其值改为29

在【usbd_custom_hid_if.c】文件中,存放了发送和接收的相关代码。发送代码处于注释状态,如下图所示,需要解放出来。

图 8:数据发送函数

可以看到,这里只是简单地调用【usbd_customhid.c】文件中的USBD_CUSTOM_HID_SendReport函数,但此使用此函数时不再需要输入设备参数。

光释放还不行,还需要在头文件中声明它,以方便其它文件调用。编译一次代码,然后找到【usbd_custom_hid_if.h】文件并打开,在其中声明USBD_CUSTOM_HID_SendReport_FS函数,如下图所示:

图 9:声明发送函数

注意,不要拷贝static关键字,回到【usbd_customhid.c】文件中,将USBD_CUSTOM_HID_SendReport_FS函数前面的static关键字删除。

向上位机发送数据

一系列准备工作做完后,终于可以向上位机发送数据了。打开【main.c】文件,首先包含usbd_custom_hid_if.h头文件。

#include "usbd_custom_hid_if.h"

最后,写代码定时向上位机发送数据(0 ~ 255)

/* USER CODE BEGIN 2 */
uint8_t report;
/* USER CODE END 2 */

/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
	for(report=0; report<=255; report++)
	{
		USBD_CUSTOM_HID_SendReport_FS(&report, 1);
		HAL_Delay(500);
	}
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */

为验证数据是否已经成功发送,先上网下载一个 HID 工具,请访问以下网址:

https://www.cnblogs.com/wenziqi/archive/2012/05/29/2523637.html

下载多功能调试助手。打开后,选择屏幕上方的【USB调试】选项卡,单击【查找USB】按钮,在【当前USB HID设备数】组合框中选择你的设备,然后单击【打开USB】按钮,可看到数据已成功发送至上位机,如下图所示:

图 10:在上位机查看数据

重定向printf

我们知道,一般在单片机编程时,为了方便调试,都会将printf函数重定向到 UART 输出。下面我们来演示如何将printf重定向到 HID 输出。

如果 HID 只用于重定向printf,会省很多事,首先不需要在 main.c 文件中引入 HID 头文件,可以删除刚才添加的头文件代码,留着也没问题:

#include "usbd_custom_hid_if.h"

删除刚才在 usbd_custom_hid_if.h 文件中添加的函数声明:

int8_t USBD_CUSTOM_HID_SendReport_FS(uint8_t *report, uint16_t len);

重新注释掉我们刚才在 usbd_custom_hid_if.c 文件中解放的USBD_CUSTOM_HID_SendReport_FS函数。不再需要这个函数了。

在 usbd_custom_hid_if.c 文件中找到代码extern USBD_HandleTypeDef hUsbDeviceFS;,在其下方插入新代码:

#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif 
PUTCHAR_PROTOTYPE
{
	USBD_CUSTOM_HID_SendReport(&hUsbDeviceFS, (uint8_t *)&ch, 1);
	HAL_Delay(3);
	return ch;
}

插入效果如下图所示:

图 11:插入重定向代码

main.c 文件中之前在main()函数中编写的代码更改如下:

/* USER CODE END 2 */
int i = 0;
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
	HAL_Delay(2000);
	printf("Hello World = %d\r\n", i++);
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */

运行效果如下图所示:

图 12:printf 使用效果

经实验,发送 HID 数据时,必须延时3~5毫秒,否则上位机这边接收不完全,这样导致传输速度变慢。可以考虑更改报告描述符,传送 128 字节数据,将整个字符串传至上位机,不过处理起来会麻烦很多,就这样吧。

从上位机接收数据

接下来讲解如何从上位机接收数据,开发板上一盏灯都没有,只能将收到的数据原封不动地返回了。

首先将刚才写进 main.c 文件中main()函数的方法的代码删除。

然后打开 usbd_custom_hid_if.c 文件,找到CUSTOM_HID_OutEvent_FS函数,只要 HID 接收到数据,就会自动执行此函数,我们在函数里面将接收到的数据返回即可。更改代码如下:

static int8_t CUSTOM_HID_OutEvent_FS(uint8_t event_idx, uint8_t state)
{
  /* USER CODE BEGIN 6 */
  //接收到的数据放在 hhid 里面
  USBD_CUSTOM_HID_HandleTypeDef *hhid = (USBD_CUSTOM_HID_HandleTypeDef *)hUsbDeviceFS.pClassData;
  //将数据原封不动地返回
  USBD_CUSTOM_HID_SendReport(&hUsbDeviceFS, (hhid->Report_buf), 1);

  return (USBD_OK);
  /* USER CODE END 6 */
}

运行效果如下图所示:

图 13:接收数据

需要注意,如果报告描述符定义接收的数据长度超过 2,则需要更改相应的接收缓冲宏的尺寸,打开 usbd_conf.h 文件,找到下面这句代码:

#define USBD_CUSTOMHID_OUTREPORT_BUF_SIZE     2

这里定义了接收缓冲的尺寸为 2,由于大于我定义的 1 个字节,所以刚才没有改。如果你在报告描述符中定义的接收数据长度超过 2,则需要将其修改为对应的数字。

好 HID 系列写完了,我的 ST25 开发板的元件准备今天到货,设计的板子没接 UART,只接下 USB,研究到这里正好为合适。明天开始 NFC 的研究。

;

© 2018 - IOT小分队文章发布系统 v0.3