(请保留-> 作者: 罗冰 https://blog.csdn.net/luobing4365)
YIE002USB开发板之Windows编程
1 HID(人机接口设备)介绍
HID设备是USB规范中最早提出并支持的一种类设备,日常使用的键盘、鼠标等,都属于HID设备,是一种使用非常广泛的USB设备。
HID一开始为USB,但设计为与总线无关。它是为低延迟、低宽带的设备而设计的,在Windows 7以上,也支持蓝牙、I2C、GPIO等HID设备。
当然,我们主要还是研究跨平台的USB HID。
HID包含两个基本概念:报告描述符和报告。报告描述符描述设备支持的数据格式和含义,报告是在设备和软件客户端之间交换的实际数据。
上位机程序和HID设备通过报告来交换数据,上一篇中了解到,有三种报告类型:
输入报告(Input Report): 从HID设备发送到上位机程序的数据,通常在状态发生更改时发送;
输出报告(Output Report):从上位机程序发送到HID设备(比如键盘上的LED状态)数据;
功能报告(Feature Report):通常与配置信息相关。
在报告描述符中,可以定义每种类型的0个或多个报告,通常Input Report至少有一个。
不同的操作系统,对USB HID的支持方式不一样。Windows下有驱动Hidusb.sys支持HID传输,并提供了HID应用程序接口;UEFI的下使用EFI_USB_IO_PROTOCOL构建通信函数;而Linux下则可以使用libusb与HID设备通信。
2 Windows上位机程序UsbHID
为YIE002开发的Windows上位机,我命名为UsbHID。其界面图如图1所示。
图1 UsbHID工具界面图
UsbHID工具运行于Windows系统下, 主要用来在与YIE002进行通信。它将系统中所有能够找到的HID设备以列表的形式显示,用户选择需要通信的设备,再选择三种通信方式中的一种,即可发送数据。
USB通信中,所有的命令都是由主机发出的。USB设备根据主机发出的命令,进行相应的处理。YIE002设计的固件中,对主机发出来的数据,进行了一定的处理并进行回传,以演示双方的通信过程。
YIE002的开发板上,提供了LED灯控制以及随机数生成的功能。上位机可以通过此数据通道,控制LED灯,并获取生成的随机数。
3 Windows系统的HID API
Windows系统上提供的HID应用程序编程接口(API),大致可以分为两类:
I 获取设备属性的函数
HidD _ GetAttributes()
HidD _ GetHidGuid()
HidD _ GetIndexedString()
HidD _ GetManufacturerString()
HidD _ GetPhysicalDescriptor()
HidD _ GetPreparsedData()
HidD _ GetProductString()
HidD _ GetSerialNumberString()
HidD _ GetNumInputBuffers()
HidD _ SetNumInputBuffers()
II 数据通信的函数
ReadFile()
WriteFile()
HidD _ GetInputReport()
HidD _ SetOutputReport()
HidD _ SetFeature()
HidD _ GetFeature()
获取设备属性函数的使用方法相对简单,直接查看MSDN上的函数描述就可以了。这里主要介绍下HID设备数据通讯函数的用法。
3.1 CreateFile()、ReadFile()和WriteFile()
这三个函数的函数原型为:
HANDLE CreateFile(
LPCWSTR lpFileName, //指向路径的指针
DWORD dwDesiredAccess, //访问模式(读/写)
DWORD dwShareMode, //共享模式
LPSECURITY_ATTRIBUTES lpSecurityAttributes, //指向安全属性的指针
DWORD dwCreationDisposition, //如何创建
DWORD dwFlagsAndAttributes, //文件属性(同步或异步)
HANDLE hTemplateFile //用于复制文件句柄
);
BOOL ReadFile(
HANDLE hFile, //设备句柄,可通过CreateFile得到
LPVOID lpBuffer, //存储数据的缓冲区
DWORD nNumberOfBytesToRead, //要读取的数据长度
LPDWORD lpNumberOfBytesRead, //实际收到的数据长度
LPOVERLAPPED lpOverlapped //OVERLAPPED结构体指针
);
BOOL WriteFile(
HANDLE hFile, //设备句柄,可通过CreateFile得到
LPCVOID lpBuffer, //要发送的buffer(指针)
DWORD nNumberOfBytesToWrite, //要发送数据的长度
LPDWORD lpNumberOfBytesWritten, //实际发送数据的长度
LPOVERLAPPED lpOverlapped //OVERLAPPED结构体指针,如果设备是以
//FILE_FLAG_OVERLAPPED方式打开的话,那么这个指针就不能为NULL
);
CreateFile()用于打开HID设备,设备路径可通过函数SetupDi系列函数获取。此函数有以下需要注意的地方:
● 访问模式 系统独占设备,比如鼠标、键盘等,应将此参数设置为0,否则后续函数的操作将失败。也就是说,对独占设备只能进行查询操作,所以可使用的函数有限。
● 文件属性 此参数用来设置同步或异步模式,它主要是对后续的WriteFile()和ReadFile()有影响。此参数为0时,代表同步模式,也即数据处理完成之后才返回,否则阻塞在函数内部。
ReadFile()用于读取设备通过中断IN传输发来的输入报告,读取的数据从中断输入管道传入。其读取的数据是从HID设备驱动的缓冲区中得到的,缓冲区的大小,可通过HidD _ SetNumInputBuffers()来改变。
ReadFile()的入口参数nNumberOfBytesToRead代表要读取数据的长度(数据正文+1字节报告ID)。当然,在实际设计中,HID设备是由我们自己设计的,我们是知道设备固件的信息的。
上位机也可通过HidD _ GetPreparsedData()来取得报告的长度。此参数设置过大,不会有错误产生,上位机将受到实际读到的长度;此参数设置过小,也即小于数据长度,会返回错误。
WriteFile()通过中断OUT得到来自设备的数据,其入口参数lpBuffer的第一个元素为待发送的报告ID,而且此报告ID必须是Output Report,否则会返回错误。
3.2 HidD _ GetInputReport()和HidD _ SetOutputReport()
BOOLEAN HidD_GetInputReport(
HANDLE HidDeviceObject, //设备句柄
PVOID ReportBuffer, //存储数据的缓冲区
ULONG ReportBufferLength //缓冲区长度
);
BOOLEAN HidD_SetOutputReport(
HANDLE HidDeviceObject, //设备句柄
PVOID ReportBuffer, //存有待发送数据的缓冲区
ULONG ReportBufferLength //缓冲区长度
);
这两个函数通过Input Report和Output Report进行数据传输。主要在其它们的入口参数中,缓冲区的数据,第一个元素为Report ID,后面跟着的才是数据正文。
在固件程序中,这两个函数对应的类命令为Get Report和Set Report。
3.3 HidD _ SetFeature()和HidD _ GetFeature()
BOOLEAN HidD_GetFeature(
HANDLE HidDeviceObject, //设备句柄
PVOID ReportBuffer, //存储数据的缓冲区
ULONG ReportBufferLength //缓冲区长度
);
BOOLEAN HidD_SetFeature(
HANDLE HidDeviceObject, //设备句柄
PVOID ReportBuffer, //存有待发送数据的缓冲区
ULONG ReportBufferLength //缓冲区长度
);
与3.2介绍的两个函数类似,不过这两个函数是通过Feature Report进行数据传输的。在固件程序中,对应的类命令也是Get Report和Set Report。
了解了上述函数的基本用法后,就可以着手编写UsbHID工具了。具体的代码编写过程,在下一篇中进行介绍。