目录
一、引用hidapi库
QT中引用库的方式分为隐式调用和显式调用,也就是动态库和静态库。下面是在工程中添加的数据
#在工程中添加库路径
LIBS += -L$$PWD/DLL/ -lhidapi
HEADERS += hidapi.h
二、创建 设备句柄
创建句柄就是方便对设备进行访问,类似描述符。
private:
hid_device *handle ;
三、初始化
初始化步骤比较简单,调用hidapi库的初始化函数就行了,不调用也可以,反正在hid_open函数也会调用。
其中值得注意的是hid_read需要调用才能读取数据,没用触发事件,在QT中一般习惯了信号和槽,但很可惜没用触发事件,我这里使用的方法是开一个线程,循环的读取hid_read函数,来判断是否有数据到达。
//1.初始化
hid_init();
//2.打开设备
handle = hid_open( 0x303a, 0x4012,NULL);
static unsigned char sendDATA[64] ;
// 3.发送数据
int res = hid_write(handle, sendDATA, sizeof(sendDATA));
// 4.读取数据
int res = hid_read(handle, sendDATA, sizeof(sendDATA));
四、函数说明
(1)hid_open 打开
打开的方式有两种,一种是hid_open以vid,pid的方式打开,还有一种的以路径的方式打开,具体看下面吧。
hid_device * hid_open(unsigned short vendor_id,
unsigned short product_id,
const wchar_t *serial_number);
/*
@参数1:VID
@参数2:PID
@参数3:设备序列号 (一般NULL即可,默认选第一个)
@返回值返回一个对象指针,NULL是打开失败
*/
//另一个打开方式 通过路径打开
hid_device * hid_open_path(const char *path);
这里要比hid_open多一个查找
(1)hid_device_info *devs = hid_enumerate(0x303a, 0x4012);
(2) hid_device * hid_handle = open_path(devs);
(2)hid_set_nonblocking 设置阻塞模式
设置阻塞模式,其实就是设置读取的时候,是否阻塞,设置成非阻塞的话,没用数据会执行后面的程序。
是否阻塞这个是我其中遇到调试一个问题,讲完函数在最后说一下注意事项。
int hid_set_nonblocking(hid_device *dev, int nonblock);
/*
@参数1:对象句柄
@参数2:阻塞模式-非阻塞模式
- 1 to enable nonblocking
- 0 to disable nonblocking.
@ 0 成功
@-1 失败
*/
(3)hid_read 读取
读取的注意事项就是申请的大小可以跟USBhid设置设置成一样或者大一些
int hid_read(hid_device *dev, unsigned char *data, size_t length);
/*
@参数1:对象句柄
@参数2:存放读取的数据地址
@参数3:长度
@返回值 读取长度 -1读取失败
*/
(4)hid_write写入
函数说明如下:
int hid_write(hid_device *dev, const unsigned char *data, size_t length);
/*
@参数1:对象句柄
@参数2:写入的数据地址
@参数3:写入长度
@返回值 写入长度 -1读取失败
*/
程序使用的发送例子如下, frm是CAN数据,自行按自定义格式就行。
注意发送的第一个字节需要跟 USB-hid设备的报告ID一致,例如我这次的hid设备就是0x03,后面的就是自定义数据了。
static unsigned char USB_BUF[64] = { 0 };
memset(USB_BUF,0,sizeof(USB_BUF));
int count = 0;
unsigned int ID = frm->ID;
USB_BUF[count++] = 0x03;
USB_BUF[count++] = 0x0;
USB_BUF[count++] = 0xff;
for(int i = 0 ; i < 4 ; i++)
{
USB_BUF[count++] = ( ID >> ((3-i)*8) )&0xFF;
}
USB_BUF[count++] = frm->RemoteFlag;
USB_BUF[count++] = frm->ExternFlag;
USB_BUF[count++] = frm->DataLen;
if(frm->DataLen < 9)
for(int i = 0 ;i < frm->DataLen ;i++)
USB_BUF[(count++)] = frm->Data[i];
int res;
// res = hid_send_feature_report(Usb_handle,USB_BUF,sizeof(USB_BUF));
res = hid_write(Usb_handle,USB_BUF,sizeof(USB_BUF));
(5)其他函数
剩下的函数用到再查吧 ,总的来说,利用库实现HID在QT中不难
主要是配合esp32和STM32的HID实现免驱通信,有时间搞一下ST的例子
<1>
struct hid_device_info * hid_enumerate(unsigned short vendor_id, unsigned short product_id);
//简单概括就是 传入需要查找的VID和PID 返回一个 对象结构体
//当VID 和PID 都是 0 的时候,则搜索全部的HID设备 ,具体的可以通过对象的next访问到下一个对象
//下面是具体的介绍
/** @brief Enumerate the HID Devices.
This function returns a linked list of all the HID devices
attached to the system which match vendor_id and product_id.
If @p vendor_id is set to 0 then any vendor matches.
If @p product_id is set to 0 then any product matches.
If @p vendor_id and @p product_id are both set to 0, then
all HID devices will be returned.
@ingroup API
@param vendor_id The Vendor ID (VID) of the types of device
to open.
@param product_id The Product ID (PID) of the types of
device to open.
@returns
This function returns a pointer to a linked list of type
struct #hid_device_info, containing information about the HID devices
attached to the system, or NULL in the case of failure. Free
this linked list by calling hid_free_enumeration().
*/
<2>
//简单概括一下就是发送特征报告的
int hid_send_feature_report(hid_device *dev,
const unsigned char *data,
size_t length);
<3>
//简单概括一下就是获取特征报告的
int hid_get_feature_report(hid_device *dev,
unsigned char *data,
size_t length);
<4>
//获取序列号字符串
int hid_get_serial_number_string(hid_device *dev,
wchar_t *string,
size_t maxlen);
五、总结
调试过程中主要是下位机的问题,想更方便看到效果, 可以买一个HID模块,本次调试中用到ch9326模块和Bus Hound进行调试。
遇到的问题:
1.刚开始调试的时候,没用考虑阻塞的问题,打开程序的时候设置的非阻塞,当手动关闭的时候再打开的时候没用设置非阻塞,导致hid_read函数阻塞导致整个ui阻塞了
2.由于新接触hid的免驱设备,在调试的时候最好还是先确保一方设备没问题,再调试,刚开始跟同事调试的时候发现,死活连接不上,原来是esp32 usb-hid描述符配置有问题导致,后面买了个新设备调试,发现可以连接上,并能发送。
由于这是第一篇文章,如果这篇文章给你一点帮助,可以先关注一下,后续持续更新QT系列和STM32系列,UI的话后面再搞一个lvgl专栏
以下是hidapi库 链接,提取码:tang