Qt基础 hid设备通信

C++USB通信。使用hidapi库与控制板通信。C++与hid设备通信。hidapi库的配置与使用。

  • 前言
  • 一、HID是什么?
  • 二、使用步骤
    • 1.引入库
    • 2.获取USB-HID设备
    • 3.打开和关闭USB-HID设备
    • 4.发送和接收报告
    • 5.发送和接收数据
  • 总结


前言

这篇文章主要是记录C++与USB-hid设备(即免驱动的usb设备,鼠标、键盘就是hid设备)进行通信,它也是一种USB设备,只不过不需要安装驱动,如果要与非USB-HID设备通信请参考libusb。
libusb首页
github地址


一、HID是什么?

USB-HID是Universal Serial Bus-Human Interface Device的缩写,由其名称可以了解HID设备是直接与人交互的设备,例如键盘、鼠标与游戏杆等。

二、使用步骤

1.引入库

代码如下(示例):
从github上下载hidapi
地址:https://github.com/signal11/hidapi
这个项目是C语言编写的跨平台的项目,实现源码分别在各个平台文件夹下,各平台均使用同一头文件,该头文件是hidapi文件夹下的hidapi.h文件。源码文件以windows为例,其文件夹下可以找到hid.c的文件。
Qt基础 hid设备通信_第1张图片
将头文件和源文件添加到C++项目中
Qt基础 hid设备通信_第2张图片
然后include使用就可以了

#include "hidapi.h"//USB-HID通信所需头文件,该头文件来自第三方,参考链接:http://github.com/signal11/hidapi
#pragma comment (lib,"setupapi.lib")//hidapi所需的lib环境,没有此文件会导致链接错误

    
    
      
      
      
      
  • 1
  • 2

我这里由于动态库编译时会出现链接错误,因此加上下面那一行。

2.获取USB-HID设备

代码如下(示例):

	if (hid_init())//初始化函数,实际上不调用它hid_enumerate和下面的hid_open也会自动调用
		return -1;
	hid_device_info* Hids, * HidsCopy;//一个用于接收设备信息的单链表,另一个用来遍历,该结构体使用unicode编码,所以下面都要用unicode处理方式
	Hids = hid_enumerate(0x154F, 0x4304);//获取vid为0x154F,pid为0x4304的HID设备链表,这里如果都是0就是获取所有的HID设备
	HidsCopy = Hids;
	LPCWSTR wpSerialNumber = L"\0";//用来去重,一个hid设备一般有多个端点,因此会读到多个
	while (HidsCopy)
	{
		if (CompareStringW(LOCALE_INVARIANT, NORM_LINGUISTIC_CASING, wpSerialNumber, -1, HidsCopy->serial_number, -1) != CSTR_EQUAL)//去重
		{
			cout << HidsCopy->serial_number;
			wpSerialNumber = HidsCopy->serial_number;//我这里只需要序列号,实际上这个结构体还有很多数据,可以参考官方的实例
		}
		HidsCopy = HidsCopy->next;
	}
	hid_free_enumeration(Hids);//释放设备链表

    
    
      
      
      
      
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

3.打开和关闭USB-HID设备

/*********************************************************
函数名称: OpenUSB(LPCTSTR PscSerialNumber)
函数功能: 打开USB-HID设备
输入参数:
	参数1:  LPCTSTR PscSerialNumber  - HID设备的序列号
返回值:
	成功 - 0
	失败 - -1
********************************************************/
int OpenUSB(LPCTSTR PscSerialNumber)
{
	hid_device *handle = hid_open(0x154F, 0x4304, PscSerialNumber);//打开指定vid、pid、序列号的设备
	if (!handle)
	{
		LOG("无法打开USB设备");
		hid_exit();
		return -1;
	}
	// 将hid_read()函数设置为非阻塞。
	if (hid_set_nonblocking(handle, 1) != 0)// 1启用非阻塞  0禁用非阻塞。
	{
		hid_close(handle);
		hid_exit();
		LOG("设置非阻塞失败,错误原因:%s", hid_error(handle));
		return -1;
	}
	return 0;
}

    
    
      
      
      
      
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
//关闭USB-HID设备
/*********************************************************
函数名称: CloseUSB(hid_device*& handle)
函数功能: 关闭USB-HID设备
输入参数:
	参数1:  hid_device*& handle  - 操作句柄
********************************************************/
bool CloseUSB(hid_device*& handle)
{
	hid_close(handle);
	handle = nullptr;//这里将指针置空,增强安全性
	hid_exit();
}

    
    
      
      
      
      
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

4.发送和接收报告

/*********************************************************
函数名称: SendMsg(hid_device* Usb, char* Send)
函数功能: 向USB-HID设备发送报告
输入参数:
	参数1:  hid_device* Usb	 - USB-HID设备操作句柄
	参数2:  char* Send	 - 要发送的数据,第一位不要放数据,要留给报告编号
返回值:
	成功 - true
	失败 - false
********************************************************/
bool SendMsg(hid_device* Usb, char* Send)
{
	Send[0] = 0x01;//报告编号,上位机向HID写数据时,每个包传输的第一个byte为写数据report ID,上、下位机必须一致。
	//USB通信报告长度最短为32,加上一字节开头报告编号,最短为33。
	int nActualLength = hid_send_feature_report(Usb, Send, 33);
	if (nActualLength != 33)
	{
		LOG("发送指令失败,错误代码:%hx", hid_error(Usb));
		return false;
	}
	return true;
}

    
    
      
      
      
      
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
/*********************************************************
函数名称: ReceiveMsg(hid_device* Usb, byte* pReceiveBuf, size_t nLength)
函数功能: 接收来自USB-HID设备应答的报告
输入参数:
	参数1:  hid_device* Usb    - USB-HID设备操作句柄
	参数2:  size_t nLength     - 应答缓冲区长度
输出参数:
	参数1:  byte* pReceiveBuf  - 应答数据缓冲区
返回值:
	实际接收到数据的长度
********************************************************/
int ReceiveMsg(hid_device* Usb, byte* pReceiveBuf, size_t nLength)
{
	//接收报告必须将接收缓冲区首字节改为与设备一致才能通信成功
	pReceiveBuf[0] = 0x01;
	int nRecvLen = hid_get_feature_report(Usb, pReceiveBuf, 64);//这里必须设置接收长度,并且不管有没有数据,,执行成功了返回的就是这里设置的值加上一字节报告ID,也就是65,实际上缓冲区里面全是0x00
	if (nRecvLen != 0)
	{
		//HID通信会自动补齐,如果没有数据就全部都是0x00
		if (pReceiveBuf[0] == 0x00)
			nRecvLen = 0;//由于第一个字节设置为了0x01,所以第一个字节是0x00就认为没有收到数据。
		else if (pReceiveBuf[1] == 0x1b)//判断实际接收到的数据长度,否则接收报告长度固定返回65
		{
		//由于公司的协议里帧头是0x1b,帧头后面两位就是帧长度,因此可以通过解析帧长度获得实际接收的数据长度
			nRecvLen = (pReceiveBuf[2] << 8) | pReceiveBuf[3];
		}
	}
	return nRecvLen;
}

    
    
      
      
      
      
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

另外:
参考资料了解到
HID一般使用的是中断传输方式。中断传输的最大包长根据USB设备的速率决定。低速设备(LS):最大包长8字节;全速设备(FS):最大包长64字节;高速设备(HS):最大包长1024字节。如果使用的是全速设备,却要传输256字节,可以采用拆包传输的方式处理,也就是拆成4个64字节的包来传输。这在HID通信中也是一种常用的方式。

作者:大雨哈哈
链接:https://www.zhihu.com/question/282658997/answer/1545044186

因此还是要根据自己的hid设备类型来判断发送和接收的报告长度,如果长度过大就需要分包处理。

5.发送和接收数据

发送数据可以通过hid_write(hid_device *device, const unsigned char *data, size_t length)
接收数据通过hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds)
或hid_read(hid_device *device, unsigned char *data, size_t length)
由于公司的下位机只需要report来通信,所以这里作者并没有实现。不过用法与上述大致是相同的。

总结

以上就是hidapi库的配置方法和使用,仅作为学习记录参考。
如果本文章有误,请指出,感激不尽!

你可能感兴趣的:(QT基础,qt,驱动开发)