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



  • 3
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
赋所有源代码,开发工具vs2010 framework3.5 baidu搜索c# HidUsb都是大同小异案例,而且拿下来基本不能用。大都是围绕public static extern int CreateFile(省略众多参数..);发现没有,copy下来测试基本都是用不了的。 原因很简单:windows不允许你用程序随便就去访问硬件设备。所以在此把之前做过的基于C#开发读写HidUsb设备的项目整理成一个简单的小案例,分享给大家,开发环境VS2010。 该案例重点在public static extern SafeFileHandle CreateFile(string lpFileName, uint dwDesiredAccess, int dwShareMode, IntPtr lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, int hTemplateFile); 看着貌似也是用到CreateFile这个函数,其实并不然,注意到没有"SafeFileHandle",这就是重点! 这样windows是允许程序访问外接hidusb设备的。 当然具体如何运用这个函数现在已经不是您应该 关心的了,因为我已经为您把它封装成一个类,您只要调用相应的方法就OK. 例: //第一步:获取HidUsb设备信息 List slist = new List(); UsbHidDevice usbhid = new UsbHidDevice(); usbhid.GetDeviceList(ref slist); //HidUsb设备信息包含在List数据集中 注:当获取到HidUsb设备信息为:\\?\hid#vid_0e2c&pid;_0112#6&1b44c403;&0&0000;#{4d1e55b2-f16f-11cf-88cb-001111000030}, 注意该字符串里的“vid_0e2c”和“pid_0112”部分,那么: vid为0e2c, pid为:0112 //第二步:创建一个HidUsb设备访问实例 UsbHidDevice Device = new UsbHidDevice(vid, pid); //第三步:连接HidUsb设备 Boolean connBool = Device.Connect(); //第四步:实现数据接收事件 Device.DataReceived += new UsbHidDevice.DataReceivedDelegate(Device_DataReceived); //当HidUsb设备返回信息时触发此事件 void Device_DataReceived(byte[] data) { //处理接收到的数据逻辑 } //第五步:向Hid设备发送数据"0xa0 00 0x12 0x9 0x22" string txt = "0xa0 00 0x12 0x9 0x22"; //把数据转换为字节数组 byte[] data = ConvertHelper.StringToByte(txt2); byte bt = 0; CommandMessage cmdMsg = new CommandMessage(bt, data); Boolean sbool = Device.SendMessage(cmdMsg); //发送数据 //第六步:释放所有资源 Device.Dispose();
vc导入excel封装类是指在vc(Visual C++)编程环境中,通过封装类的方式实现对Excel文件的导入功能。 首先,需要引入Microsoft Excel的相关函数库,例如使用MFC(Microsoft Foundation Class)库中的COleVariant类和COleDispatchDriver类。这些类提供了对Excel应用程序对象和工作簿对象的封装,可以方便地进行Excel文件的操作。 在导入Excel文件之前,需要先创建一个Excel应用程序对象,可以使用COleDispatchDriver类的CreateDispatch方法来创建。然后,通过应用程序对象的Workbooks属性获取工作簿集合,并使用Add方法创建一个新的工作簿对象。 接下来,可以使用工作簿对象的Open方法打开待导入的Excel文件,并通过工作簿对象的Sheets属性获取工作表集合,再使用索引或名称来获取具体的工作表对象。 对于每个工作表对象,可以使用其Range属性获取指定单元格范围的数值。例如,可以使用Range对象的GetValue方法获取单元格的数值,并将其存储到自定义的数据结构中,以便后续使用。 最后,完成Excel文件的导入后,需要调用工作簿对象的Close方法关闭文件,并释放相关资源。同时,还需要调用应用程序对象的Quit方法来退出Excel应用程序。 总结起来,实现vc导入Excel封装类需要引入Microsoft Excel的相关函数库,并通过封装类提供的方法来创建应用程序对象、打开Excel文件、获取工作表对象以及获取单元格范围的数值等操作。最后,记得关闭文件并退出Excel应用程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值