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();

8

9if(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();

4

5if(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};

6

7 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};

4

5//读取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设备的GUID

5 { 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);

5

6 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;

4

5if (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();

5

6if(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

回到顶部