Qtusb通讯
一、前言
Qt通讯方式有很多,如Tcp/Ip、串口等,但对Usb通讯支持较弱,此篇主要描述Qt与plc设备通过usb进行通讯的解决方法;
开发环境:Qt5.5、VS2013
优势:支持热插拔usb线
二、实现
1、采用线程,通过hidapi方式读写usb信息
1)hidapi源码下载地址:https://github.com/signal11/hidapi
2)定义usb描述符
hid_device *m_Handle;
3)线程中,m_Handle默认为空,定时1秒检测是否有接入usb
1void SerialThread::run() 2{ 3while(m_IsRun) 4 { 5 m_Mutex.lock(); 6if(NULL == m_Handle) 7 openUsb(); 89if(m_StartSend)
10 sendData();
11 m_Mutex.unlock();
12
13if(NULL == m_Handle)
14 msleep(1000);
15else
16 exec();
17 }
18 }
线程读写
4)根据指定pid、vid,打开usb口
1void SerialThread::openUsb() 2{ 3int result = hid_init(); 45if(0 != result)
6 emit funcSig(SERIAL_FIND, QVariantList() << 1);
7else
8 {
9//打开pid=0x1FC9,vid=0x00A2的usb
10 m_Handle = hid_open(0x1FC9, 0x00A2, NULL);
11if(NULL == m_Handle)
12 {
13//判断是否第一次open,第一次需报错
14if(m_IsSendError)
15 {
16 m_IsSendError = false;
17 emit funcSig(SERIAL_OPEN, QVariantList() << 1);
18 }
19 }
20else
21 {
22 hid_set_nonblocking(m_Handle, 1);//非阻塞方式
23 }
24 }
25 }
打开usb
5)使用函数hid_read/hid_write读写usb口,例如:写入开始测试
1void SerialThread::sendData() 2{ 3int len = 0; 4int write = PACKET_LEN + 1; 5 unsigned char buf[PACKET_LEN + 2] = {0}; 67 buf[0] = 0x00;
8
9 buf[1] = 0xeb;
10 buf[2] = 0x90;
11 buf[3] = 0x02;
12 buf[4] = 0xff;
13
14 buf[5] = 0xff;
15 buf[6] = 0x03;
16 buf[7] = 0xff;
17 buf[8] = 0x00;
18
19 m_StartSend = false;
20 len = hid_write(m_Handle, buf, write);
21if(write == len)
22 emit funcSig(SERIAL_SEND, QVariantList() << 0);
23else
24 emit funcSig(SERIAL_SEND, QVariantList() << 1);
25 }
写usb口
6)读取usb口数据
1void SerialThread::readUsb() 2{ 3 unsigned char buf[ONE_PACKET_LEN + 1] = {0}; 45//读取usb数据
6int len = hid_read(m_Handle, buf, ONE_PACKET_LEN);
7if(len > 0)
8 {
9//存入缓存
10for(int index = 0;index < len;index++)
11 m_ByteArray.append(buf[index]);
12//长度大于等于一条指令长度时,进行解析
13if(m_ByteArray.length() >= ONE_PACKET_LEN)
14 {
15 parseData();
16 m_ByteArray.remove(0, ONE_PACKET_LEN);
17 }
18 }
19 }
读取usb
2、解决粘包
1)读取到的数据,先存在m_ByteArray中
2)当m_ByteArray的长度大于等于一条指令长度时进行解析
3)解析时注意先把char转为16进制,再进行数值提取,如下提取第5为数据
QByteArray(1, bytes.at(4)).toHex().toInt(&ok, 16);
3、usb热插拔
1)QWidget对象中注册usb事件
1void Widget::registerDevice() 2{ 3const GUID GUID_DEVINTERFACE_LIST[] = { 4 { 0xA5DCBF10, 0x6530, 0x11D2, { 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED } }, //USB设备的GUID5 { 0x53f56307, 0xb6bf, 0x11d0, { 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b } }};
6
7 HDEVNOTIFY hDevNotify;
8 DEV_BROADCAST_DEVICEINTERFACE NotifacationFiler;
9 ZeroMemory(&NotifacationFiler,sizeof(DEV_BROADCAST_DEVICEINTERFACE));
10 NotifacationFiler.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
11 NotifacationFiler.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
12
13for (int i = 0; i < sizeof(GUID_DEVINTERFACE_LIST)/sizeof(GUID); i++)
14 {
15 NotifacationFiler.dbcc_classguid = GUID_DEVINTERFACE_LIST[i];
16 hDevNotify = RegisterDeviceNotification((HANDLE)this->winId(), &NotifacationFiler, DEVICE_NOTIFY_WINDOW_HANDLE);
17if (!hDevNotify)
18 qCritical() << QStringLiteral("注册失败!");
19 }
20 }
注册Usb事件
2)继承QWidget的nativeEvent事件,原型如下
bool nativeEvent(const QByteArray &eventType, void *message, long *result);
1bool Widget::nativeEvent(const QByteArray &eventType, void *message, long *result) 2{ 3 Q_UNUSED(eventType); 4 Q_UNUSED(result); 56 MSG *msg = reinterpret_cast<MSG *>(message);
7
8int msgType = msg->message;
9if (WM_DEVICECHANGE == msgType)
10 {
11 PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR)msg->lParam;
12switch (msg->wParam)
13 {
14case DBT_DEVICEARRIVAL:
15 {
16if (DBT_DEVTYP_VOLUME == lpdb->dbch_devicetype)
17 {
18 PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb;
19if (0 == lpdbv->dbcv_flags)
20 m_TipQlb->setText("已检测到USB设备插入");
21//else if (DBTF_MEDIA == lpdbv->dbcv_flags)
22//qDebug() << "CD_Arrived.";
23 }
24elseif (DBT_DEVTYP_DEVICEINTERFACE == lpdb->dbch_devicetype)
25 {
26 PDEV_BROADCAST_DEVICEINTERFACE pDevInf = (PDEV_BROADCAST_DEVICEINTERFACE)lpdb;
27 QString name = QString::fromWCharArray(pDevInf->dbcc_name);
28
29 checkUsb(name);
30 }
31 }
32break;
33case DBT_DEVICEREMOVECOMPLETE:
34if (DBT_DEVTYP_VOLUME == lpdb->dbch_devicetype)
35 {
36 PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME)lpdb;
37if (0 == lpdbv->dbcv_flags)
38 m_TipQlb->setText("USB设备已拔出");
39
40if (DBTF_MEDIA == lpdbv->dbcv_flags)
41 m_TipQlb->setText("CD_Removed.");
42 }
43break;
44 }
45 }
46
47returnfalse;
48 }
插拔Usb检测
3)检测到指定的pid、vid后,重新打开usb
1void Widget::checkUsb(const QString &name) 2{ 3int errorCode = 0; 45if (name.contains("USB#"))
6 {
7 QStringList listAll = name.split("#");
8 QStringList listID = listAll.at(1).split("&");
9 QString vid = listID.at(0).right(4);
10 QString pid = listID.at(1).right(4);
11if(0 == vid.compare("1FC9", Qt::CaseInsensitive) &&
120 == pid.compare("00A2", Qt::CaseInsensitive))
13 {
14 errorCode = m_SerialThread->resetOpen();
15if(0 == errorCode)
16 m_TipQlb->setText("重新打开USB,成功");
17elseif(1000 == errorCode)
18 m_TipQlb->setText("初始化USB,失败");
19else
20 m_TipQlb->setText("重新打开USB,失败");
21 }
22 }
23 }
检查插入的usb
4)线程中执行打开操作,创建新的usb描述符
1int SerialThread::resetOpen() 2{ 3int errorCode = 0; 4int result = hid_init(); 56if(0 != result)
7 errorCode = 1000;
8else
9 {
10 m_Handle = hid_open(0x1FC9, 0x00A2, NULL);
11if(NULL == m_Handle)
12 errorCode = 1001;
13else
14 hid_set_nonblocking(m_Handle, 1);
15 }
16
17return errorCode;
18 }
重新打开usb
5)此时可继续读写usb口
以上是 Qtusb通讯 的全部内容, 来源链接: utcz.com/z/519973.html