vc USB的HID通讯类封装

        从事嵌入式方面,会点上位机会事半功倍,总体而言,一个串口,网口,usb通讯用到的比较多,这方面的资料网上也很多,但是总体而言零零碎碎,不算太齐全。

        本问讲解的是usb hid类的封装,该例程的上位机可以和圈圈的开发板配套使用。在这里,我们用到的库是hidapi.lib,这个网上有下载,到时本人也提供一份。圈圈上位机的usb hid通讯接口看起来有些零碎杂乱,对于新手而言,看起来很费劲。下面罗列一下该库的提供的接口函数。

int HID_API_EXPORT HID_API_CALL hid_init(void);
int HID_API_EXPORT HID_API_CALL hid_exit(void);
HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, wchar_t *serial_number);
int  HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length);
int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds);
int  HID_API_EXPORT HID_API_CALL hid_read(hid_device *device, unsigned char *data, size_t length);
void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device);

相信这些接口很多人一开就明白是什么意思,在这里我也简单的讲解下。

hid_init该函数相当于初始化,是初始化HIDAPI库的,用在usb通讯最前端。

hid_exit是用来释放HIDAPI一些资源,防止内存泄露,用在usb通讯关闭后。

hid_open该函数是用来打开usb设备,需要提供版本号和产品号,序列号可以提供为NULL,如果该usb设备存在,此时该函数会返回一个指针,指向hid通讯对象。如果返回值为NULL,那么表示无法建立通讯。

hid_write此函数用是用来往hid端口写数据,第一个参数是hid_oepn函数的指针,第二个参数是你要写数据的首地址,第三个表示你写的长度,返回值为已经写入多少字节。

hid_read_timeout是用来读取usb hid接口中的数据,第一个参数是hid_open函数返回的指针,第二个参数存储读取数据存放的首地址,第三个表示需要的长度,肯定不能小于你要读取内容的长度,第四个参数表示超时时间,如果多少毫秒内没读取到内容,该函数立即返回为0,如果读取发生错误,返回值为-1,读取正常,返回值为读取内容的长度。

hid_read次函数也表示读取hid中的数据,和hid_read_timeout函数唯一的区别是该函数没有超时机制,如果没有数据,此函数一直会处于阻塞状态,无法返回,当然读取内的函数一般是放在线程中去处理的,否则界面会出现卡死的现象。

继续这些接口都有了,那我又做了什么,试想你有了这写接口,你用在你程序中因为你无法确认你什么时候会收到数据,所以接受的部分需要单独一个线程处理,在这里,我就是前面的接口经过封装成类,把收到的数据通过消息发送出去,到时候使用者就只要在响应函数中处理你所需的函数。

在这里我使用的是vs2010,先在工程中把hidapi.dll hidapi.lib, hidapi.h三个文件添加到工程文件夹中,并且把头文件添加到工程中。

新建USBClass类,里面的代码如下:

#pragma once
#include "hidapi.h"					//hidapi库的头文件
#pragma comment(lib, "hidapi.lib")	//导入的库	
#define USB_RECV		WM_USER + 1	//自定义的消息
#define VEN_ID			0x8888		//版本号
#define PRO_ID			0x0001		//产品号
class USBClass
{
private:
	hid_device *usbDev;				//指向通讯接口对象的指针
	HANDLE thread;					//线程
	HANDLE hEvent;					//事件
	HWND curHwnd;					//用来存放需要接受数据的窗口句柄
	UINT8 buf[256];					//接受函数的缓冲区
	UINT16 venID, proID;			//接口的版本号,产品号
public:
	/**
	构造函数是只是把一些参数初始化
	*/
	USBClass(void)
	{	
		usbDev = NULL;
		hEvent = NULL;
		venID = VEN_ID;
		proID = PRO_ID;
	}
	/**
	析构函数主要是判断线程是否运行,如果运行设置hEvent为有信号状态,并且设置一个等待时间,方便接受线程退出。
	*/
	~USBClass(void)
	{
		if (hEvent)
		{
			SetEvent(hEvent);
			Sleep(50);
		}
		
	}
	/**
	接受线程,如果hEvent为无信号状态,那么一直处于读取内容状态,如果为有信号状态,此时线程退出
	*/
	static DWORD WINAPI RecvThread(LPVOID para)
	{
		int ret;
		USBClass *p = (USBClass *)para;
		while (WaitForSingleObject(p->hEvent, 5) != WAIT_OBJECT_0)	
		{
			ret = p->Read();
			if (ret)	//收到数据,此时发送消息
			{
				PostMessage(p->curHwnd, USB_RECV, (WPARAM)p->buf, ret);
			}
		}
		if (p->usbDev)	//线程退出时,关闭usb hid通讯接口
		{
			hid_close(p->usbDev);
			p->usbDev = NULL;
		}
		hid_exit();
		return 0;
	}
	/**
	为打开usb接口,hwnd为传入的句柄,为了把收到的数据传给该串口句柄。
	同时建立线程,事件
	*/
	BOOL Open(HWND hwnd)
	{
		hid_init();
		usbDev = hid_open(venID, proID, NULL);
		if (usbDev == NULL)
		{
			return FALSE;
		}
		curHwnd = hwnd;
		thread = CreateThread(NULL, 0, RecvThread, this, 0, 0);
		CloseHandle(thread);
		hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
		return TRUE;
	}
	/**
	判断接口是否打开
	*/
	BOOL IsOpen()
	{
		if (usbDev)
		{
			return TRUE;
		}
		return FALSE;
	}
	/**
	设置有信号,方便接受线程退出,并且要设置等待停留时间
	*/
	BOOL Close()
	{
		SetEvent(hEvent);
		Sleep(50);
		return TRUE;
	}
	/**
	往usb接口中写入数据,这里要注意写入数据的内容长度必须多加一个字节空间,需要存放
	报告ID号,在这里我一门设置为0,如果你要传送64字节,此时你要提供65个字节空间大小,
	而且你的有效数据从第一个字节开始存放。该函数返回实际写入的数据长度。
	*/
	int Write(UINT8 *data, UINT32 len)
	{
		int ret = 0;
		if (usbDev)
		{
			data[0] = 0;
			ret = hid_write(usbDev, data, len);
		}
		return ret;
	}
	/**
	读取接口中的内容,采用的是读取超时函数,使用阻塞的不太好,因为不方便线程的退出。
	*/
	int Read()
	{
		int ret = 0;
		if (usbDev)
		{
			ret = hid_read_timeout(usbDev, buf, 256, 10);
		}
		return ret;
	}
};

建立一个对话框USBExample,界面如下:

10个按钮,ID号分别为IDC_BUTTON_OPEN,IDC_BUTTON_CLOSE,IDC_BUTTON_LED1~IDC_BUTTON_LED8.

在Resource.h文件添加对应的ID号

#define IDC_BUTTON_OPEN                 1000
#define IDC_BUTTON_CLOSE                1001
#define IDC_BUTTON_LED1                 1002
#define IDC_BUTTON_LEN2                 1003
#define IDC_BUTTON_LED3                 1004
#define IDC_BUTTON_LED4                 1005
#define IDC_BUTTON_LED5                 1006
#define IDC_BUTTON_LED6                 1007
#define IDC_BUTTON_LED7                 1008
#define IDC_BUTTON_LED8                 1009

为了打印接受的数据,这里,需要在stdafx.h文件中添加一下宏定义:

#define MY_DEBUG 1
#if MY_DEBUG    
#pragma comment( linker, "/subsystem:console /entry:wWinMainCRTStartup" )    
#endif  
在USBExmapleDlg.h文件中内容如下:

// USBExampleDlg.h : 头文件
//
#pragma once
#include "USBClass.h"
// CUSBExampleDlg 对话框
class CUSBExampleDlg : public CDialogEx
{
// 构造
public:
	CUSBExampleDlg(CWnd* pParent = NULL);	// 标准构造函数
// 对话框数据
	enum { IDD = IDD_USBEXAMPLE_DIALOG };
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV 支持
// 实现
protected:
	HICON m_hIcon;
	USBClass usbCls;		//定义使用到的类对象										
	// 生成的消息映射函数
	virtual BOOL OnInitDialog();
	afx_msg void OnPaint();
	afx_msg HCURSOR OnQueryDragIcon();
	DECLARE_MESSAGE_MAP()
public:
	afx_msg void OnBnClickedButtonLed(UINT nID);//LED1~LED8 8个按钮的处理函数
	afx_msg LRESULT OnRecvAct(WPARAM wParam, LPARAM lParam);//USB接受数据的处理函数
	afx_msg void OnBnClickedButtonOpen();		//打开按钮的处理函数
	afx_msg void OnBnClickedButtonClose();		//关闭按钮的处理函数
};

在USBExmapleDlg.cpp 文件中内容如下:

// USBExampleDlg.cpp : 实现文件
//
#include "stdafx.h"
#include "USBExample.h"
#include "USBExampleDlg.h"
#include "afxdialogex.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// CUSBExampleDlg 对话框
CUSBExampleDlg::CUSBExampleDlg(CWnd* pParent /*=NULL*/)
	: CDialogEx(CUSBExampleDlg::IDD, pParent)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CUSBExampleDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CUSBExampleDlg, CDialogEx)
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_MESSAGE(USB_RECV, &CUSBExampleDlg::OnRecvAct)			//消息绑定,就是用来处理usb收到的数据
	ON_CONTROL_RANGE(BN_CLICKED, IDC_BUTTON_LED1, IDC_BUTTON_LED8, OnBnClickedButtonLed)
	ON_BN_CLICKED(IDC_BUTTON_OPEN, &CUSBExampleDlg::OnBnClickedButtonOpen)	//打开按钮的绑定
	ON_BN_CLICKED(IDC_BUTTON_CLOSE, &CUSBExampleDlg::OnBnClickedButtonClose)//关闭按钮的绑定
END_MESSAGE_MAP()


// CUSBExampleDlg 消息处理程序

BOOL CUSBExampleDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();
	// 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
	//  执行此操作
	SetIcon(m_hIcon, TRUE);			// 设置大图标
	SetIcon(m_hIcon, FALSE);		// 设置小图标
	// TODO: 在此添加额外的初始化代码
	GetDlgItem(IDC_BUTTON_OPEN)->EnableWindow(true);
	GetDlgItem(IDC_BUTTON_CLOSE)->EnableWindow(false);
	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

// 如果向对话框添加最小化按钮,则需要下面的代码
//  来绘制该图标。对于使用文档/视图模型的 MFC 应用程序,
//  这将由框架自动完成。

void CUSBExampleDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // 用于绘制的设备上下文

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// 使图标在工作区矩形中居中
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;
		// 绘制图标
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialogEx::OnPaint();
	}
}

//当用户拖动最小化窗口时系统调用此函数取得光标
//显示。
HCURSOR CUSBExampleDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

/**
这里只是打印接受的数据
*/
LRESULT CUSBExampleDlg::OnRecvAct(WPARAM wParam, LPARAM lParam)
{	
	UINT8 *str = (UINT8 *)wParam;
	int len = (int)lParam;
	printf("***USB Recv\r\n");
	for (int i = 0; i < len; i++)
	{
		printf("%d=%02x\r\n", i, str[i]);
	}
	return 0;
}

void CUSBExampleDlg::OnBnClickedButtonLed(UINT nID)
{
	// TODO: 在此添加控件通知处理程序代码
	if (usbCls.IsOpen())
	{
		UINT8 data[9];		//本来发送的数据长度只为0,需要多加一个字节空间用来存放报告ID号
		memset(data, 0, 9);
		data[1] = (nID - IDC_BUTTON_LED1 + 1);
		usbCls.Write(data, 9);
		printf("###USB Send\r\n");
		for (int i = 0; i < 9; i++)
		{
			printf("%d=%02x\r\n", i, data[i]);
		}
	}
}

void CUSBExampleDlg::OnBnClickedButtonOpen()
{
	// TODO: 在此添加控件通知处理程序代码
	if (usbCls.Open(this->m_hWnd))
	{
		GetDlgItem(IDC_BUTTON_OPEN)->EnableWindow(false);
		GetDlgItem(IDC_BUTTON_CLOSE)->EnableWindow(true);
	}
}

void CUSBExampleDlg::OnBnClickedButtonClose()
{
	// TODO: 在此添加控件通知处理程序代码
	if (usbCls.IsOpen())
	{
		usbCls.Close();
		GetDlgItem(IDC_BUTTON_OPEN)->EnableWindow(true);
		GetDlgItem(IDC_BUTTON_CLOSE)->EnableWindow(false);
	}
}
程序下载链接为: USBExample.rar



相关推荐
HID 设备读写 #include <windows.h> //! Defines the maximum length of a serial number #define SERNUM_LEN 40 //! Defines the maximum number of physical devices #define MAX_PHYS_DEVICES 6 //! \name HID Device return codes //! @{ // //! HID action/transfer was successful #define HID_DEVICE_SUCCESS 0x00 //! HID device was not found #define HID_DEVICE_NOT_FOUND 0x01 //! HID device is not opened #define HID_DEVICE_NOT_OPENED 0x02 //! HID device is allready opened #define HID_DEVICE_ALREADY_OPENED 0x03 //! Timeout occurs during transfer #define HID_DEVICE_TRANSFER_TIMEOUT 0x04 //! HID transfer failed #define HID_DEVICE_TRANSFER_FAILED 0x05 //! Invalid handle #define HID_DEVICE_HANDLE_ERROR 0x06 //! Unknown error #define HID_DEVICE_UNKNOWN_ERROR 0xFF //! @} // Enabled only when debugging HID connection issues //#define DEBUG_MODE //****************************************************************************** // //! \brief Device information structure. // //****************************************************************************** struct strHidDevice{ //! Handle for hid device HANDLE hndHidDevice; //! Indicator if device is opened BOOL bDeviceOpen; //! Timeout for GetReport requests UINT uGetReportTimeout; //! Timeout for SetReport requests UINT uSetReportTimeout; //! Asynchronous I/O structure OVERLAPPED oRead; //! Asynchronous I/O structure OVERLAPPED oWrite; //! Maximum length of InReport's WORD wInReportBufferLength; //! Maximum length of OutReport's WORD wOutReportBufferLength; //! InBuffer contains data, if InReport provides more data then the application actual need BYTE inBuffer[8192]; //! Number of current used bytes in inBuffer WORD inBufferUsed; }; //****************************************************************************** // //! A structure that tracks the number of serial numbers // //****************************************************************************** struct strTrackSerialNumbers { //! Index number DWORD deviceNum; //! Serial number of physical device char serialNum[SERNUM_LEN]; }; //****************************************************************************** // //! \addtogroup hiddevice_api //! @{ // //****************************************************************************** //****************************************************************************** // //! \brief Close a HID Device. //! //! This function will close a HID device based on the HID structure //! //! \param pstrHidDevice Structure which contains important data of an HID //! device //! //! \return Returns the error status, as one of //! \n \b HID_DEVICE_SUCCESS //! \n \b HID_DEVICE_NOT_OPENED //! \n \b HID_DEVICE_HANDLE_ERROR // //****************************************************************************** BYTE HID_Close(struct strHidDevice* pstrHidDevice); //****************************************************************************** // //! \brief Flush USB buffer for the given device //! //! \param pstrHidDevice Structure which contains important data of an HID //! device. //! //! \return Returns the error status, as one of //! \n \b HID_DEVICE_SUCCESS //! \n \b HID_DEVICE_HANDLE_ERROR //! \n \b HID_DEVICE_UNKNOWN_ERROR // //****************************************************************************** BYTE HID_FlushBuffer(struct strHidDevice* pstrHidDevice); //****************************************************************************** // //! \brief Gets the number of HID devices //! //! This function will return the number of interfaces connected with a //! specified VID, PID and serial number, if no devices are connected, //! it will return a 0 //! //! \param vid Vendor-Id of the device //! \param pid Product-Id of the device //! \param numSerNums Total number of connected physical devices //! //! \return Return the number of connected devices with the specific VID, PID, //! and serial number. // //****************************************************************************** DWORD HID_GetNumOfInterfaces(WORD vid, WORD pid, DWORD numSerNums); //****************************************************************************** // //! \brief Gets the number of serial number and serial number list //! //! Scans the HID Devices on the system for any whose VID/PID match the //! ones specified. For every one it finds, it returns that device's //! serial number in serialNumList. Every physical USB device within a //! given VID/PID space has a unique serial number; therefore, each //! item in the list corresponds with a separate physical USB device //! attached to this host; that is, different physical instances of the //! same product or design. The function returns the total number of //! serial numbers found; if none are found, it returns 0. //! //! \param vid Vendor-ID of the device //! \param pid Product-ID of the device //! \param serialNumList List of serial numbers corresponding to the passed //! VID and PID //! //! \return Returns the number of connected physical devices with the specific //! VID and PID // //****************************************************************************** DWORD HID_GetSerNums(WORD vid, WORD pid, struct strTrackSerialNumbers * serialNumList); //****************************************************************************** // //! \brief Returns the version number of a device. //! //! \param pstrHidDevice Structure which contains important data of an HID //! device. //! \param VersionNumber Pointer to USHORT variable. //! //! \return Returns the error status, as one of //! \n \b HID_DEVICE_SUCCESS //! \n \b HID_DEVICE_HANDLE_ERROR // //****************************************************************************** BYTE HID_GetVersionNumber(struct strHidDevice* pstrHidDevice, USHORT * VersionNumber); //****************************************************************************** // //! \brief Init structure with default values. //! //! It is important to call HID_Init() before calling HID_Open() to //! avoid unpredictable behavoir. //! //! \param pstrHidDevice Structure which contains important data of a HID //! device //! //! \return None // //****************************************************************************** void HID_Init(struct strHidDevice* pstrHidDevice); //****************************************************************************** // //! \brief This has to be called inside WM_ON_DEVICECHANGE notification window //! //! This function checks if the particular HID device structure is //! still connected or disconnected. //! //! \param pstrHidDevice Structure which contains important data of an HID //! device. //! //! \return Returns the error status, as one of //! \n \b HID_DEVICE_SUCCESS //! \n \b HID_DEVICE_HANDLE_ERROR // //****************************************************************************** BOOL HID_IsDeviceAffected(struct strHidDevice* pstrHidDevice); //****************************************************************************** // //! \brief Open a HID Device. //! //! This function opens the HID device associated with the HID interface //! 'deviceIndex' (0-7), on the physical device described by the VID, //! PID, and serial number. //! \param pstrHidDevice Structure which contains important data of an HID //! device //! \param vid Vendor-ID of the device //! \param pid Product-ID of the device //! \param deviceIndex Index of the device.If only one HID is connected, //! deviceIndex is 0. //! - Starts with zero //! - Maximum value is (HID_GetNumOfInterfaces() - 1) //! \param serialNumber Serial number of device to be opened. //! \param totalDevNum Total number of interfaces associated with the //! serial number //! \param totalSerNum Total number of physical devices associated with //! the VID/PID //! //! \return Returns the error status, as one of //! \n \b HID_DEVICE_SUCCESS //! \n \b HID_DEVICE_NOT_FOUND //! \n \b HID_DEVICE_ALREADY_OPENED // //****************************************************************************** BYTE HID_Open(struct strHidDevice* pstrHidDevice, WORD vid, WORD pid, DWORD deviceIndex, char serialNumber[SERNUM_LEN], DWORD totalDevNum, DWORD totalSerNum); //****************************************************************************** // //! \brief Reads a data stream from the given HID device. //! //! Prefixed report ID will be skipped. //! //! \param pstrHidDevice Structure which contains important data of an HID //! device //! \param buffer Pointer to buffer in which will be written //! \param bufferSize Number of bytes to read //! \param bytesReturned Number of actual read bytes //! //! \return Returns the error status, as one of //! \n \b HID_DEVICE_SUCCESS //! \n \b HID_DEVICE_NOT_OPENED //! \n \b HID_DEVICE_TRANSFER_TIMEOUT //! \n \b HID_DEVICE_TRANSFER_FAILED // //****************************************************************************** BYTE HID_ReadFile(struct strHidDevice* pstrHidDevice, BYTE* buffer, DWORD bufferSize, DWORD* bytesReturned); //****************************************************************************** // //! \brief Registers a device for program Windows notification. //! //! Registers the window pointed to by handle hWnd to receive //! notification when devices are added or removed from the system. //! //! \param hWnd Windows handle //! \param diNotifyHandle Device notification handle pointer address //! //! \return Returns the error status, as one of //! \n \b HID_DEVICE_SUCCESS //! \n \b HID_DEVICE_HANDLE_ERROR // //****************************************************************************** BYTE HID_RegisterForDeviceNotification(HWND hWnd, HDEVNOTIFY* diNotifyHandle); //****************************************************************************** // //! \brief Un-Registers a device from Windows notification. //! //! Un-registers the window pointed to by handle hWnd to receive //! notification when devices are added or removed from the system. //! //! \param diNotifyHandle: Device notification handle pointer address. //! //! \return Returns the error status, as one of //! \n \b HID_DEVICE_SUCCESS //! \n \b HID_DEVICE_HANDLE_ERROR // //****************************************************************************** BYTE HID_UnRegisterForDeviceNotification(HDEVNOTIFY* diNotifyHandle); //****************************************************************************** // //! \brief Writes a data stream to the given HID device. //! //! Needed report IDs will be generated automatically. //! //! \param pstrHidDevice Structure which contains important data of an HID //! device //! \param buffer Buffer which will be send //! \param bufferSize Number of bytes to send //! //! \return Returns the error status, as one of //! \n \b HID_DEVICE_SUCCESS //! \n \b HID_DEVICE_NOT_OPENED //! \n \b HID_DEVICE_TRANSFER_TIMEOUT //! \n \b HID_DEVICE_TRANSFER_FAILED // //****************************************************************************** BYTE HID_WriteFile(struct strHidDevice* pstrHidDevice, BYTE* buffer, DWORD bufferSize);
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页