第五章 HID设备
5.1 HID介绍
为简化USB设备的开发过程,USB提出了设备类的概念。所有设备类都必须支持标准USB描述符和标准USB设备请求。如果有必要,设备类还可以自行定义其专用的描述符和设备请求,这分别被称为设备类定义描述符和设备类定义请求。另外,一个完整的设备类还将指明其接口和端点的使用方法,如接口所包含端点的个数、端点的最大数据包长度等。
HID设备类就是设备类的一类,HID是Human Interface Device缩写,人机交互设备,例如键盘、鼠标与游戏杆等。不过HID设备并不一定要有人机接口,只要符合HID类别规范的设备都是HID设备。
HID设备既可以是低速设备也可以是全速设备,其典型的数据传输类型为中断IN传输,即它适用于主机接收USB设备发来的小量到中等量的数据。HID具有以下的功能特点:适用于传输少量或中量的数据;传输的数据具有突发性;传输的最大速率有限制;无固定的传输率。
HID设备类除支持标准USB描述符外(设备描述符、配置描述符、接口描述符、端点描述符和字符串描述符),还自行定义了3种类描述符,分别为HID描述符(主要用于识别HID设备所包含的其他类描述符)、报告描述符(提供HID设备和主机间交换数据的格式)和物理描述符。一个HID设备只能支持一个HID描述符;可以支持一个或多个报告描述符;物理描述符是可选的,大多数HID设备不需要使用它。
5.2 HID类描述符
除了标准USB描述符外,HID设备自行定义了三种描述:HID描述符、报告描述符、物理描述符,物理描述符不常用,不在此讲解。三种描述分别定义为:
#define USB_HID_DTYPE_HID 0x21
#define USB_HID_DTYPE_REPORT 0x22
#define USB_HID_DTYPE_PHYSICAL 0x23
HID描述符,提供HID设备的相关信息。HID描述符描述符如下表:
偏移量 |
域 |
大小 |
值 |
描述 |
0 |
bLength |
1 |
数字 |
字节数。 |
1 |
bDescriptorType |
1 |
常量 |
配置描述符类型 |
2 |
bcdHID |
2 |
数字 |
版本号(BCD码) |
4 |
bCountryCode |
1 |
数字 |
国家语言代码 |
5 |
bNumDescriptors |
1 |
数字 |
描述符个数 |
6 |
bType |
1 |
索引 |
下一个描述符类型 |
7 |
wLength |
2 |
位图 |
下一个描述符长度 |
表1. HID描述符符
C语言HID描述符结构体为:
typedef struct
{
//HID描述符长度
unsigned char bLength;
// USB_HID_DTYPE_HID (0x21).
unsigned char bDescriptorType;
//HID协议版本号
unsigned short bcdHID;
//国家语言
unsigned char bCountryCode;
//描述符个数,至少为1
unsigned char bNumDescriptors;
//下一个描述符类型
unsigned char bType;
//下一个描述符长度
unsigned short wLength;
}
tHIDDescriptor;
例如:定义一个实际的HID设备描述符。
static const tHIDDescriptor g_sKeybHIDDescriptor =
{
9, // bLength
USB_HID_DTYPE_HID, // bDescriptorType
0x111, // bcdHID (version 1.11 compliant)
USB_HID_COUNTRY_US, // bCountryCode (not localized)
1, // bNumDescriptors
USB_HID_DTYPE_REPORT, // Report descriptor
sizeof(Report) // Size of report descriptor
};
国家语言定义:
#define USB_HID_COUNTRY_NONE 0x00
#define USB_HID_COUNTRY_ARABIC 0x01
#define USB_HID_COUNTRY_BELGIAN 0x02
#define USB_HID_COUNTRY_CANADA_BI 0x03
#define USB_HID_COUNTRY_CANADA_FR 0x04
#define USB_HID_COUNTRY_CZECH_REPUBLIC 0x05
#define USB_HID_COUNTRY_DANISH 0x06
#define USB_HID_COUNTRY_FINNISH 0x07
#define USB_HID_COUNTRY_FRENCH 0x08
#define USB_HID_COUNTRY_GERMAN 0x09
#define USB_HID_COUNTRY_GREEK 0x0A
#define USB_HID_COUNTRY_HEBREW 0x0B
#define USB_HID_COUNTRY_HUNGARY 0x0C
#define USB_HID_COUNTRY_INTERNATIONAL_ISO 0x0D
#define USB_HID_COUNTRY_ITALIAN 0x0E
#define USB_HID_COUNTRY_JAPAN_KATAKANA 0x0F
#define USB_HID_COUNTRY_KOREAN 0x10
#define USB_HID_COUNTRY_LATIN_AMERICAN 0x11
#define USB_HID_COUNTRY_NETHERLANDS 0x12
#define USB_HID_COUNTRY_NORWEGIAN 0x13
#define USB_HID_COUNTRY_PERSIAN 0x14
#define USB_HID_COUNTRY_POLAND 0x15
#define USB_HID_COUNTRY_PORTUGUESE 0x16
#define USB_HID_COUNTRY_RUSSIA 0x17
#define USB_HID_COUNTRY_SLOVAKIA 0x18
#define USB_HID_COUNTRY_SPANISH 0x19
#define USB_HID_COUNTRY_SWEDISH 0x1A
#define USB_HID_COUNTRY_SWISS_FRENCH 0x1B
#define USB_HID_COUNTRY_SWISS_GERMAN 0x1C
#define USB_HID_COUNTRY_SWITZERLAND 0x1D
#define USB_HID_COUNTRY_TAIWAN 0x1E
#define USB_HID_COUNTRY_TURKISH_Q 0x1F
#define USB_HID_COUNTRY_UK 0x20
#define USB_HID_COUNTRY_US 0x21
#define USB_HID_COUNTRY_YUGOSLAVIA 0x22
#define USB_HID_COUNTRY_TURKISH_F 0x23
在中国使用较多的是美国语言USB_HID_COUNTRY_US。
USB HID设备是通过报告来给传送数据的,报告有输入报告和输出报告。输入报告(Input)是USB设备发送给主机的,例如USB鼠标将鼠标移动和鼠标点击等信息返回给电脑,键盘将按键数据数据返回给电脑等;输出报告(Output)是主机发送给USB设备的,例如键盘上的数字键盘锁定灯和大写字母锁定灯等。报告是一个数据包,里面包含的是所要传送的数据。输入报告是通过中断输入端点输入的,而输出报告有点区别,当没有中断输出端点时,可以通过控制输出端点0发送,当有中断输出端点时,通过中断输出端点发出。
而报告描述符,是描述一个报告以及报告里面的数据是用来干什么用的。通过它,USB HOST可以分析出报告里面的数据所表示的意思。它通过控制输入端点0返回,主机使用获取报告描述符命令来获取报告描述符,注意这个请求是发送到接口的,而不是到设备。一个报告描述符可以描述多个报告,不同的报告通过报告ID来识别,报告ID在报告最前面,即第一个字节。当报告描述符中没有规定报告ID时,报告中就没有ID字段,开始就是数据。更详细的说明请参看USB HID协议。
下面以一个实例介绍鼠标报告描述符:
static const unsigned char g_pucMouseReportDescriptor[]=
{
UsagePage(USB_HID_GENERIC_DESKTOP), //通用桌面
Usage(USB_HID_MOUSE), //HID鼠标
Collection(USB_HID_APPLICATION), //应用集合,以EndCollection结束
Usage(USB_HID_POINTER), //指针设备
Collection(USB_HID_PHYSICAL), //集合
UsagePage(USB_HID_BUTTONS), //按键
UsageMinimum(1), //最小值
UsageMaximum(3), //最大值
LogicalMinimum(0), //逻辑最小值
LogicalMaximum(1), //逻辑最大值
ReportSize(1), //Report大小为1bit
ReportCount(3), //Report 3个位
Input(USB_HID_INPUT_DATA | USB_HID_INPUT_VARIABLE |
USB_HID_INPUT_ABS), //发送给主机的报告格式
//剩余5位填满
ReportSize(5),
ReportCount(1),
Input(USB_HID_INPUT_CONSTANT | USB_HID_INPUT_ARRAY |
USB_HID_INPUT_ABS),
UsagePage(USB_HID_GENERIC_DESKTOP), //通用桌面
Usage(USB_HID_X),
Usage(USB_HID_Y),
LogicalMinimum(-127),
LogicalMaximum(127),
ReportSize(8),
ReportCount(2),
Input(USB_HID_INPUT_DATA | USB_HID_INPUT_VARIABLE |
USB_HID_INPUT_RELATIVE),
ReportSize(8),
ReportCount(MOUSE_REPORT_SIZE - 3),
Input(USB_HID_INPUT_CONSTANT | USB_HID_INPUT_ARRAY |
USB_HID_INPUT_ABS),
EndCollection,
EndCollection,
};
UsagePage常用参数:
#define USB_HID_GENERIC_DESKTOP 0x01
#define USB_HID_BUTTONS 0x09
#define USB_HID_USAGE_POINTER 0x0109
#define USB_HID_USAGE_BUTTONS 0x0509
#define USB_HID_USAGE_LEDS 0x0508
#define USB_HID_USAGE_KEYCODES 0x0507
#define USB_HID_X 0x30
#define USB_HID_Y 0x31
Usage常用参数:
#define USB_HID_X 0x30
#define USB_HID_Y 0x31
#define USB_HID_POINTER 0x01
#define USB_HID_MOUSE 0x02
#define USB_HID_KEYBOARD 0x06
Collection常用参数:
#define USB_HID_APPLICATION 0x00
#define USB_HID_PHYSICAL 0x01
Input常用参数:
#define USB_HID_INPUT_DATA 0x0000
#define USB_HID_INPUT_CONSTANT 0x0001
#define USB_HID_INPUT_ARRAY 0x0000
#define USB_HID_INPUT_VARIABLE 0x0002
#define USB_HID_INPUT_ABS 0x0000
#define USB_HID_INPUT_RELATIVE 0x0004
#define USB_HID_INPUT_NOWRAP 0x0000
#define USB_HID_INPUT_WRAP 0x0008
#define USB_HID_INPUT_LINEAR 0x0000
#define USB_HID_INPUT_NONLINEAR 0x0010
#define USB_HID_INPUT_PREFER 0x0000
#define USB_HID_INPUT_NONPREFER 0x0020
#define USB_HID_INPUT_NONULL 0x0000
#define USB_HID_INPUT_NULL 0x0040
#define USB_HID_INPUT_BITF 0x0100
#define USB_HID_INPUT_BYTES 0x0000
Output常用参数:
#define USB_HID_OUTPUT_DATA 0x0000
#define USB_HID_OUTPUT_CONSTANT 0x0001
#define USB_HID_OUTPUT_ARRAY 0x0000
#define USB_HID_OUTPUT_VARIABLE 0x0002
#define USB_HID_OUTPUT_ABS 0x0000
#define USB_HID_OUTPUT_RELATIVE 0x0004
#define USB_HID_OUTPUT_NOWRAP 0x0000
#define USB_HID_OUTPUT_WRAP 0x0008
#define USB_HID_OUTPUT_LINEAR 0x0000
#define USB_HID_OUTPUT_NONLINEAR x0010
#define USB_HID_OUTPUT_PREFER 0x0000
#define USB_HID_OUTPUT_NONPREFER 0x0020
#define USB_HID_OUTPUT_NONULL 0x0000
#define USB_HID_OUTPUT_NULL 0x0040
#define USB_HID_OUTPUT_BITF 0x0100
#define USB_HID_OUTPUT_BYTES 0x0000
报告描述符使用复杂,可从Http://www.usb.org下载HID Descriptor tool(如图1所示)来生成。HID Descriptor tool是由USB官方编写的专用报告符生成工具。有关报告符具体内容请参阅Http://www.usb.org网上的HID Usage Tables文档。使用USB库函数开发USBHID设备可以不用考虑报告符,在库中已经定义。
<ignore_js_op>
如图1
HID Descriptor tool界面简洁,操作方便,集结了所有HID设备报告定义,如键盘、鼠标、操作秆、手写设备等,对于报告符不熟悉的开发者相当方便。
5.3 USB键盘
在USB库中已经定义好USB键盘的数据类型、API,开发USB键盘非常快捷方便。相关定义和数据类型放在“usbdhidkeyb.h”中。
5.3.1 数据类型
usbdhidkeyb.h中已经定义好USB键盘使用的所有数据类型和函数,下面介绍USB键盘使用的数据类型。
#define KEYB_MAX_CHARS_PER_REPORT 6
KEYB_MAX_CHARS_PER_REPORT定义USB键盘一次发送6个数据给主机,如果每次发送的数据多于定义的6个,将会通过USBDHIDKeyboardKeyStateChange()函数将返回发送数据太多的错误:KEYB_ERR_TOO_MANY_KEYS。KEYB_MAX_CHARS_PER_REPORT这个值的定义由键盘报告决定。
typedef enum
{
//状态还没有定义
HID_KEYBOARD_STATE_UNCONFIGURED,
//空闲状态,没有按键按下或者没有等待数据。
HID_KEYBOARD_STATE_IDLE,
//等待主机发送数据
HID_KEYBOARD_STATE_WAIT_DATA,
//等待数据发送
HID_KEYBOARD_STATE_SEND
}
tKeyboardState;
tKeyboardState,定义键盘状态,用于在USB键盘工作时,保存键盘状态。
#define KEYB_IN_REPORT_SIZE 8
#define KEYB_OUT_REPORT_SIZE 1
KEYB_IN_REPORT_SIZE、KEYB_OUT_REPORT_SIZE定义键盘IN报告符、OUT报告符长度。
typedef struct
{
// 指示当前USB设备是否配置成功。
unsigned char ucUSBConfigured;
// USB键盘使用的子协议:USB_HID_PROTOCOL_BOOT 或者USB_HID_PROTOCOL_REPORT
// 将会传给设备描述符和端点描述符
unsigned char ucProtocol;
// 键盘LED灯当前状态
volatile unsigned char ucLEDStates;
// 记录有几个键按下
unsigned char ucKeyCount;
// 中断IN端点状态.
volatile tKeyboardState eKeyboardState;
// 指示当前是否有键状态改变
volatile tBoolean bChangeMade;
// 用于接收OUT报告
unsigned char pucDataBuffer[KEYB_OUT_REPORT_SIZE];
// 用于收送IN报告,保存最新一次报告
unsigned char pucReport[KEYB_IN_REPORT_SIZE];
// 按下键的usage代码
unsigned char pucKeysPressed[KEYB_MAX_CHARS_PER_REPORT];
// 为IN报告定义时间。
tHIDReportIdle sReportIdle;
// HID设备实例,保存键盘HID信息。
tHIDInstance sHIDInstance;
// HID设备驱动
tUSBDHIDDevice sHIDDevice;
}
tHIDKeyboardInstance;
tHIDKeyboardInstance,键盘实例。在tHIDInstance和tUSBDHIDDevice的基础上,用于保存全部USB键盘的配置信息,包括描述符、callback函数、按键事件等。
typedef struct
{
//VID
unsigned short usVID;
//PID
unsigned short usPID;
//设备最大耗电量
unsigned short usMaxPowermA;
//电源属性
unsigned char ucPwrAttributes;
//函数指针,处理返回事务
tUSBCallback pfnCallback;
//Callback第一个入口参数
void *pvCBData;
//指向字符串描述符集合
const unsigned char * const *ppStringDescriptors;
//字符串描述符个数 (1 + (5 * (num languages)))
unsigned long ulNumStringDescriptors;
//键盘实例,保存USB键盘的相关信息。
tHIDKeyboardInstance *psPrivateHIDKbdData;
}
tUSBDHIDKeyboardDevice;
tUSBDHIDKeyboardDevice,USB键盘类。定义了VID、PID、电源属性、字符串描述符等,还包括了一个USB键盘实例。其它HID设备描述符、配置信息通过API函数储入tHIDKeyboardInstance定义的USB键盘实例中。
5.3.2 API函数
在USB键盘API库中定义了7个函数,完成USB键盘初始化、配置及数据处理。下面为usbdhidkeyb.h中定义的API函数:
void *USBDHIDKeyboardInit(unsigned long ulIndex, const tUSBDHIDKeyboardDevice *psDevice);
void *USBDHIDKeyboardCompositeInit(unsigned long ulIndex,
const tUSBDHIDKeyboardDevice *psDevice);
unsigned long USBDHIDKeyboardKeyStateChange(void *pvInstance, unsigned char ucModifiers,
unsigned char ucUsageCode, tBoolean bPressed);
void USBDHIDKeyboardTerm(void *pvInstance);
void *USBDHIDKeyboardSetCBData(void *pvInstance, void *pvCBData);
void USBDHIDKeyboardPowerStatusSet(void *pvInstance, unsigned char ucPower);
tBoolean USBDHIDKeyboardRemoteWakeupRequest(void *pvInstance);
void *USBDHIDKeyboardInit(unsigned long ulIndex,
const tUSBDHIDKeyboardDevice *psDevice);
作用:初始化键盘硬件、协议,把其它配置参数填入psDevice的键盘实例中。
参数:ulIndex,USB模块代码,固定值:USB_BASE0。psDevice,USB键盘类。
返回:指向配置后的tUSBDHIDKeyboardDevice。
void *USBDHIDKeyboardCompositeInit(unsigned long ulIndex,
const tUSBDHIDKeyboardDevice *psDevice);
作用:初始化键盘协议,本函数在USBDHIDKeyboardInit中已经调用。
参数:ulIndex,USB模块代码,固定值:USB_BASE0。psDevice,USB键盘类。
返回:指向配置后的tUSBDHIDKeyboardDevice。
unsigned long USBDHIDKeyboardKeyStateChange(void *pvInstance,
unsigned char ucModifiers,
unsigned char ucUsageCode, tBoolean bPressed);
作用:键盘状态改变,并发送报告给主机。
参数:pvInstance,指向tUSBDHIDKeyboardDevice,本函数将修改其按键状态等。ucModifiers,功能按键代码。ucUsageCode,普通按键代码。bPressed,是否加入到报告中并发送给主机。
返回:程序错误代码。
void USBDHIDKeyboardTerm(void *pvInstance);
作用:结束usb键盘。
参数:pvInstance,指向tUSBDHIDKeyboardDevice。
返回:无。
void *USBDHIDKeyboardSetCBData(void *pvInstance, void *pvCBData);
作用:修改tUSBDHIDKeyboardDevice中的pvCBData指针。
参数:pvInstance,指向tUSBDHIDKeyboardDevic。pvCBData,数据指针,用于替换tUSBDHIDKeyboardDevice中的pvCBData指针。
返回:以前tUSBDHIDKeyboardDevice的pvCBData的指针。
void USBDHIDKeyboardPowerStatusSet(void *pvInstance, unsigned char ucPower);
作用:设置键盘电源模式。
参数:pvInstance,指向tUSBDHIDKeyboardDevic。ucPower,电源工作模式,USB_STATUS_SELF_PWR或者USB_STATUS_BUS_PWR。
返回:无。
tBoolean USBDHIDKeyboardRemoteWakeupRequest(void *pvInstance);
作用:唤醒请求。
参数:pvInstance,指向tUSBDHIDKeyboardDevic。
返回:是否成功唤醒。
这些API中使用最多是USBDHIDKeyboardInit和USBDHIDKeyboardPowerStatusSet两个函数,在首次使用USB键盘时,要初始化设备,使用USBDHIDKeyboardInit完成USB键盘初始化、打开USB中断、枚举设备、描述符补全等;USBDHIDKeyboardPowerStatusSet设置按键状态,并通过报告发送给主机,这是键盘与主机进行数据通信最主要的接口函数,使用频率最高。
5.3.3 USB 键盘开发
USB键盘开发只需要4步就能完成。如图2所示,键盘设备配置(主要是字符串描述符)、callback函数编写、USB处理器初始化、按键处理。
<ignore_js_op>
图2
第一步:键盘设备配置(主要是字符串描述符),按字符串描述符标准完成串描述符配置,进而完成键盘设备配置。
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_gpio.h"
#include "inc/hw_types.h"
#include "driverlib/debug.h"
#include "driverlib/gpio.h"
#include "driverlib/interrupt.h"
#include "driverlib/sysctl.h"
#include "driverlib/systick.h"
#include "driverlib/usb.h"
#include "usblib/usblib.h"
#include "usblib/usbhid.h"
#include "usblib/usb-ids.h"
#include "usblib/device/usbdevice.h"
#include "usblib/device/usbdhid.h"
#include "usblib/device/usbdhidkeyb.h"
//声明函数原型
unsigned long KeyboardHandler(void *pvCBData,
unsigned long ulEvent,
unsigned long ulMsgData,
void *pvMsgData);
//****************************************************************************
// 语言描述符
//****************************************************************************
const unsigned char g_pLangDescriptor[] =
{
4,
USB_DTYPE_STRING,
USBShort(USB_LANG_EN_US)
};
//****************************************************************************
// 制造商 字符串 描述符
//****************************************************************************
const unsigned char g_pManufacturerString[] =
{
(17 + 1) * 2,
USB_DTYPE_STRING,
'T', 0, 'e', 0, 'x', 0, 'a', 0, 's', 0, ' ', 0, 'I', 0, 'n', 0, 's', 0,
't', 0, 'r', 0, 'u', 0, 'm', 0, 'e', 0, 'n', 0, 't', 0, 's', 0,
};
//****************************************************************************
//产品 字符串 描述符
//****************************************************************************
const unsigned char g_pProductString[] =
{
(16 + 1) * 2,
USB_DTYPE_STRING,
'K', 0, 'e', 0, 'y', 0, 'b', 0, 'o', 0, 'a', 0, 'r', 0, 'd', 0, ' ', 0,
'E', 0, 'x', 0, 'a', 0, 'm', 0, 'p', 0, 'l', 0, 'e', 0
};
//****************************************************************************
// 产品 序列号 描述符
//****************************************************************************
const unsigned char g_pSerialNumberString[] =
{
(8 + 1) * 2,
USB_DTYPE_STRING,
'1', 0, '2', 0, '3', 0, '4', 0, '5', 0, '6', 0, '7', 0, '8', 0
};
//*****************************************************************************
// 设备接口字符串描述符
//*****************************************************************************
const unsigned char g_pHIDInterfaceString[] =
{
(22 + 1) * 2,
USB_DTYPE_STRING,
'H', 0, 'I', 0, 'D', 0, ' ', 0, 'K', 0, 'e', 0, 'y', 0, 'b', 0,
'o', 0, 'a', 0, 'r', 0, 'd', 0, ' ', 0, 'I', 0, 'n', 0, 't', 0,
'e', 0, 'r', 0, 'f', 0, 'a', 0, 'c', 0, 'e', 0
};
//*****************************************************************************
// 设备配置字符串描述符
//*****************************************************************************
const unsigned char g_pConfigString[] =
{
(26 + 1) * 2,
USB_DTYPE_STRING,
'H', 0, 'I', 0, 'D', 0, ' ', 0, 'K', 0, 'e', 0, 'y', 0, 'b', 0,
'o', 0, 'a', 0, 'r', 0, 'd', 0, ' ', 0, 'C', 0, 'o', 0, 'n', 0,
'f', 0, 'i', 0, 'g', 0, 'u', 0, 'r', 0, 'a', 0, 't', 0, 'i', 0,
'o', 0, 'n', 0
};
//*****************************************************************************
// 字符串描述符集合,一定要按这个顺序排列,因为在描述符中已经定义好描述索引。
//*****************************************************************************
const unsigned char * const g_pStringDescriptors[] =
{
g_pLangDescriptor,
g_pManufacturerString,
g_pProductString,
g_pSerialNumberString,
g_pHIDInterfaceString,
g_pConfigString
};
#define NUM_STRING_DESCRIPTORS (sizeof(g_pStringDescriptors) / \
sizeof(unsigned char *))
//*****************************************************************************
//键盘实例,键盘配置并为键盘设备信息提供空间
//*****************************************************************************
tHIDKeyboardInstance g_KeyboardInstance;
//*****************************************************************************
//键盘设备配置
//*****************************************************************************
const tUSBDHIDKeyboardDevice g_sKeyboardDevice =
{
USB_VID_STELLARIS, //自行定义VIP.
USB_PID_KEYBOARD, //自行定义PID.
500,
USB_CONF_ATTR_SELF_PWR | USB_CONF_ATTR_RWAKE,
KeyboardHandler,
(void *)&g_sKeyboardDevice,
g_pStringDescriptors,
NUM_STRING_DESCRIPTORS,
&g_KeyboardInstance
};
第二步:完成Callback函数。Callback函数用于处理按键事务。可能是主机发出,也可能是状态信息。USB键盘设备中包含了以下事务:USB_EVENT_CONNECTED、USB_EVENT_DISCONNECTED、USBD_HID_KEYB_EVENT_SET_LEDS、USB_EVENT_SUSPEND、USB_EVENT_RESUME、USB_EVENT_TX_COMPLETE。如下表:
名称 |
说明 |
USB_EVENT_CONNECTED |
USB设备已经连接到主机 |
USB_EVENT_DISCONNECTED |
USB设备已经与主机断开 |
USBD_HID_KEYB_EVENT_SET_LEDS |
USB键盘有LED灯设置,查询功能按键再确定LED |
USB_EVENT_SUSPEND |
挂起 |
USB_EVENT_RESUME |
唤醒 |
USB_EVENT_TX_COMPLETE |
发送完成 |
表2. USB键盘事务
根据以上事务编写Callback函数:
unsigned long KeyboardHandler(void *pvCBData, unsigned long ulEvent,
unsigned long ulMsgData, void *pvMsgData)
{
switch (ulEvent)
{
case USB_EVENT_CONNECTED:
{
//连接成功时,点亮LED1
GPIOPinWrite(GPIO_PORTF_BASE,0x10,0x10);
break;
}
case USB_EVENT_DISCONNECTED:
{
//断开连接时,LED1灭
GPIOPinWrite(GPIO_PORTF_BASE,0x10,0x00);
break;
}
case USB_EVENT_TX_COMPLETE:
{
//发送完成时,LED2亮
GPIOPinWrite(GPIO_PORTF_BASE,0x20,0x20);
break;
}
case USB_EVENT_SUSPEND:
{
//发送完成时,LED2亮
GPIOPinWrite(GPIO_PORTF_BASE,0x20,0x0);
break;
}
case USB_EVENT_RESUME:
{
break;
}
case USBD_HID_KEYB_EVENT_SET_LEDS:
{
//HID_KEYB_CAPS_LOCK灯
GPIOPinWrite(GPIO_PORTF_BASE,0x80,((char)ulMsgData &
HID_KEYB_CAPS_LOCK)?0 : 0x80);
break;
}
default:
{
break;
}
}
return (0);
}
第三步:系统初始化,配置内核电压、系统主频、使能端口、配置按键端口、LED控制等,本例中使用4个按键模拟普通键盘,使用4个LED进行指示。原理图如图3所示:
<ignore_js_op>
图3
系统初始化:
//设置系统内核电压 与 主频
SysCtlLDOSet(SYSCTL_LDO_2_75V);
SysCtlClockSet(SYSCTL_XTAL_8MHZ | SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN ); //使能端口
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE,0xf0);
GPIOPinTypeGPIOInput(GPIO_PORTF_BASE,0x0f);
HWREG(GPIO_PORTF_BASE+GPIO_O_PUR) |= 0x0f;
//初始化键盘设备
USBDHIDKeyboardInit(0, &g_sKeyboardDevice);
第四步:按键处理。主要使用USBDHIDKeyboardPowerStatusSet设置按键状态,并通过报告发送给主机。
while(1)
{
USBDHIDKeyboardKeyStateChange((void *)&g_sKeyboardDevice, HID_KEYB_CAPS_LOCK,
HID_KEYB_USAGE_A,
(GPIOPinRead(GPIO_PORTF_BASE, 0x0f) & GPIO_PIN_0)
? false : true);
USBDHIDKeyboardKeyStateChange((void *)&g_sKeyboardDevice, 0,
HID_KEYB_USAGE_DOWN_ARROW,
(GPIOPinRead(GPIO_PORTF_BASE, 0x0f) & GPIO_PIN_1)
? false : true);
USBDHIDKeyboardKeyStateChange((void *)&g_sKeyboardDevice, 0,
HID_KEYB_USAGE_UP_ARROW,
(GPIOPinRead(GPIO_PORTF_BASE, 0x0f) & GPIO_PIN_2)
? false : true);
USBDHIDKeyboardKeyStateChange((void *)&g_sKeyboardDevice, 0,
HID_KEYB_USAGE_ESCAPE,
(GPIOPinRead(GPIO_PORTF_BASE, 0x0f) & GPIO_PIN_3)
? false : true);
SysCtlDelay(SysCtlClockGet()/3000);
}
使用上面四步就完成USB键盘开发,与普通USB键盘没有什么差别。由于在这个例子中使用的是Demo开发板,只模拟了四个按键,但功能与普通USB键盘一样。USB键盘开发时要加入两个lib: usblib.lib和DriverLib.lib,在启动代码中加入USB0DeviceIntHandler中断服务函数。USB键盘源码如下:
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_gpio.h"
#include "inc/hw_types.h"
#include "driverlib/debug.h"
#include "driverlib/gpio.h"
#include "driverlib/interrupt.h"
#include "driverlib/sysctl.h"
#include "driverlib/systick.h"
#include "driverlib/usb.h"
#include "usblib/usblib.h"
#include "usblib/usbhid.h"
#include "usblib/usb-ids.h"
#include "usblib/device/usbdevice.h"
#include "usblib/device/usbdhid.h"
#include "usblib/device/usbdhidkeyb.h"
//声明函数原型
unsigned long KeyboardHandler(void *pvCBData,
unsigned long ulEvent,
unsigned long ulMsgData,
void *pvMsgData);
//****************************************************************************
// 语言描述符
//****************************************************************************
const unsigned char g_pLangDescriptor[] =
{
4,
USB_DTYPE_STRING,
USBShort(USB_LANG_EN_US)
};
//****************************************************************************
// 制造商 字符串 描述符
//****************************************************************************
const unsigned char g_pManufacturerString[] =
{
(17 + 1) * 2,
USB_DTYPE_STRING,
'T', 0, 'e', 0, 'x', 0, 'a', 0, 's', 0, ' ', 0, 'I', 0, 'n', 0, 's', 0,
't', 0, 'r', 0, 'u', 0, 'm', 0, 'e', 0, 'n', 0, 't', 0, 's', 0,
};
//****************************************************************************
//产品 字符串 描述符
//****************************************************************************
const unsigned char g_pProductString[] =
{
(16 + 1) * 2,
USB_DTYPE_STRING,
'K', 0, 'e', 0, 'y', 0, 'b', 0, 'o', 0, 'a', 0, 'r', 0, 'd', 0, ' ', 0,
'E', 0, 'x', 0, 'a', 0, 'm', 0, 'p', 0, 'l', 0, 'e', 0
};
//****************************************************************************
// 产品 序列号 描述符
//****************************************************************************
const unsigned char g_pSerialNumberString[] =
{
(8 + 1) * 2,
USB_DTYPE_STRING,
'1', 0, '2', 0, '3', 0, '4', 0, '5', 0, '6', 0, '7', 0, '8', 0
};
//*****************************************************************************
// 设备接口字符串描述符
//*****************************************************************************
const unsigned char g_pHIDInterfaceString[] =
{
(22 + 1) * 2,
USB_DTYPE_STRING,
'H', 0, 'I', 0, 'D', 0, ' ', 0, 'K', 0, 'e', 0, 'y', 0, 'b', 0,
'o', 0, 'a', 0, 'r', 0, 'd', 0, ' ', 0, 'I', 0, 'n', 0, 't', 0,
'e', 0, 'r', 0, 'f', 0, 'a', 0, 'c', 0, 'e', 0
};
//*****************************************************************************
// 设备配置字符串描述符
//*****************************************************************************
const unsigned char g_pConfigString[] =
{
(26 + 1) * 2,
USB_DTYPE_STRING,
'H', 0, 'I', 0, 'D', 0, ' ', 0, 'K', 0, 'e', 0, 'y', 0, 'b', 0,
'o', 0, 'a', 0, 'r', 0, 'd', 0, ' ', 0, 'C', 0, 'o', 0, 'n', 0,
'f', 0, 'i', 0, 'g', 0, 'u', 0, 'r', 0, 'a', 0, 't', 0, 'i', 0,
'o', 0, 'n', 0
};
//*****************************************************************************
// 字符串描述符集合
//*****************************************************************************
const unsigned char * const g_pStringDescriptors[] =
{
g_pLangDescriptor,
g_pManufacturerString,
g_pProductString,
g_pSerialNumberString,
g_pHIDInterfaceString,
g_pConfigString
};
#define NUM_STRING_DESCRIPTORS (sizeof(g_pStringDescriptors) / \
sizeof(unsigned char *))
//*****************************************************************************
//键盘实例,键盘配置并为键盘设备信息提供空间
//*****************************************************************************
tHIDKeyboardInstance g_KeyboardInstance;
//*****************************************************************************
//键盘设备配置
//*****************************************************************************
const tUSBDHIDKeyboardDevice g_sKeyboardDevice =
{
USB_VID_STELLARIS,
USB_PID_KEYBOARD,
500,
USB_CONF_ATTR_SELF_PWR | USB_CONF_ATTR_RWAKE,
KeyboardHandler,
(void *)&g_sKeyboardDevice,
g_pStringDescriptors,
NUM_STRING_DESCRIPTORS,
&g_KeyboardInstance
};
//*****************************************************************************
//键盘callback函数
//*****************************************************************************
unsigned long KeyboardHandler(void *pvCBData, unsigned long ulEvent,
unsigned long ulMsgData, void *pvMsgData)
{
switch (ulEvent)
{
case USB_EVENT_CONNECTED:
{
//连接成功时,点亮LED1
GPIOPinWrite(GPIO_PORTF_BASE,0x10,0x10);
break;
}
case USB_EVENT_DISCONNECTED:
{
//断开连接时,LED1灭
GPIOPinWrite(GPIO_PORTF_BASE,0x10,0x00);
break;
}
case USB_EVENT_TX_COMPLETE:
{
//发送完成时,LED2亮
GPIOPinWrite(GPIO_PORTF_BASE,0x20,0x20);
break;
}
case USB_EVENT_SUSPEND:
{
//发送完成时,LED2亮
GPIOPinWrite(GPIO_PORTF_BASE,0x20,0x0);
break;
}
case USB_EVENT_RESUME:
{
break;
}
case USBD_HID_KEYB_EVENT_SET_LEDS:
{
//HID_KEYB_CAPS_LOCK灯
GPIOPinWrite(GPIO_PORTF_BASE,0x80,((char)ulMsgData & HID_KEYB_CAPS_LOCK)?0 : 0x80);
break;
}
default:
{
break;
}
}
return (0);
}
//*****************************************************************************
//主函数
//*****************************************************************************
int main(void)
{
//系统初始化。
SysCtlLDOSet(SYSCTL_LDO_2_75V);
SysCtlClockSet(SYSCTL_XTAL_8MHZ | SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN );
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE,0xf0);
GPIOPinTypeGPIOInput(GPIO_PORTF_BASE,0x0f);
HWREG(GPIO_PORTF_BASE+GPIO_O_PUR) |= 0x0f;
USBDHIDKeyboardInit(0, &g_sKeyboardDevice);
while(1)
{
USBDHIDKeyboardKeyStateChange((void *)&g_sKeyboardDevice, HID_KEYB_CAPS_LOCK,
HID_KEYB_USAGE_A,
(GPIOPinRead(GPIO_PORTF_BASE, 0x0f) & GPIO_PIN_0)
? false : true);
USBDHIDKeyboardKeyStateChange((void *)&g_sKeyboardDevice, 0,
HID_KEYB_USAGE_DOWN_ARROW,
(GPIOPinRead(GPIO_PORTF_BASE, 0x0f) & GPIO_PIN_1)
? false : true);
USBDHIDKeyboardKeyStateChange((void *)&g_sKeyboardDevice, 0,
HID_KEYB_USAGE_UP_ARROW,
(GPIOPinRead(GPIO_PORTF_BASE, 0x0f) & GPIO_PIN_2)
? false : true);
USBDHIDKeyboardKeyStateChange((void *)&g_sKeyboardDevice, 0,
HID_KEYB_USAGE_ESCAPE,
(GPIOPinRead(GPIO_PORTF_BASE, 0x0f) & GPIO_PIN_3)
? false : true);
SysCtlDelay(SysCtlClockGet()/3000);
}
}
5.4 USB鼠标
在USB库中已经定义好USB鼠标的数据类型、API,开发USB鼠标非常快捷方便。相关定义和数据类型放在“usbdhidmouse.h”中。
5.4.1 数据类型
usbdhidmouse.h中已经定义好USB鼠标使用的所有数据类型和函数,下面介绍USB鼠标使用的数据类型。
#define MOUSE_REPORT_SIZE 3
MOUSE_REPORT_SIZE定义鼠标IN报告的大小,可用于判断IN报告是否正确,如果超出3个字节会返回错误代码。
typedef enum
{
//状态还没有定义
HID_MOUSE_STATE_UNCONFIGURED,
//空闲状态,没有按键按下或者没有等待数据。
HID_MOUSE_STATE_IDLE,
//等待主机发送数据
HID_MOUSE_STATE_WAIT_DATA,
//等待数据发送
HID_MOUSE_STATE_SEND
}
tMouseState;
tMouseState,定义USB鼠标状态,USB鼠标正常工作时,保存鼠标工作状态。
typedef struct
{
// 指示当前USB设备是否配置成功。
unsigned char ucUSBConfigured;
// USB键盘使用的子协议:USB_HID_PROTOCOL_BOOT 或者USB_HID_PROTOCOL_REPORT
// 将会传给设备描述符和端点描述符
unsigned char ucProtocol;
// 用于收送IN报告,保存最新一次报告
unsigned char pucReport[MOUSE_REPORT_SIZE];
// 中断IN端点状态.
volatile tMouseState eMouseState;
// 为IN报告定义时间。
tHIDReportIdle sReportIdle;
// HID设备实例,保存键盘HID信息。
tHIDInstance sHIDInstance;
// HID设备驱动
tUSBDHIDDevice sHIDDevice;
}
tHIDMouseInstance;
tHIDMouseInstance,USB鼠标实例。在tHIDInstance和tUSBDHIDDevice的基础上,用于保存全部USB鼠标的配置信息,包括描述符、callback函数、按键事件等。
typedef struct
{
//VID
unsigned short usVID;
//PID
unsigned short usPID;
//设备最大耗电量
unsigned short usMaxPowermA;
//电源属性
unsigned char ucPwrAttributes;
//函数指针,处理返回事务
tUSBCallback pfnCallback;
//Callback第一个入口参数
void *pvCBData;
//指向字符串描述符集合
const unsigned char * const *ppStringDescriptors;
//字符串描述符个数 (1 + (5 * (num languages)))
unsigned long ulNumStringDescriptors;
//鼠标实例,保存USB鼠标的相关信息。
tHIDMouseInstance *psPrivateHIDMouseData;
}
tUSBDHIDMouseDevice;
tUSBDHIDMouseDevice,USB鼠标类。定义了VID、PID、电源属性、字符串描述符等,还包括了一个USB鼠标实例。其它HID设备描述符、配置信息通过API函数储入tHIDMouseInstance定义的USB鼠标实例中。
#define MOUSE_ERR_TX_ERROR 2
MOUSE_ERR_TX_ERROR,USB鼠标API函数USBDHIDMouseStateChange返回的错误代码。
5.4.2 API函数
在USB鼠标API库中定义了7个函数,完成USB键盘初始化、配置及数据处理。下面为usbdhidkeyb.h中定义的API函数:
void *USBDHIDMouseInit(unsigned long ulIndex,
const tUSBDHIDMouseDevice *psDevice);
void *USBDHIDMouseCompositeInit(unsigned long ulIndex,
const tUSBDHIDMouseDevice *psDevice);
unsigned long USBDHIDMouseStateChange(void *pvInstance, char cDeltaX,
char cDeltaY,
unsigned char ucButtons);
void USBDHIDMouseTerm(void *pvInstance);
void *USBDHIDMouseSetCBData(void *pvInstance, void *pvCBData);
void USBDHIDMousePowerStatusSet(void *pvInstance,
unsigned char ucPower);
tBoolean USBDHIDMouseRemoteWakeupRequest(void *pvInstance);
void *USBDHIDMouseInit(unsigned long ulIndex,
const tUSBDHIDMouseDevice *psDevice);
作用:初始化鼠标硬件、协议,把其它配置参数填入psDevice的鼠标实例中。
参数:ulIndex,USB模块代码,固定值:USB_BASE0。psDevice,USB鼠标类。
返回:指向配置后的tUSBDHIDMouseDevice。
void *USBDHIDMouseCompositeInit(unsigned long ulIndex,
const tUSBDHIDMouseDevice *psDevice);
作用:初始化鼠标协议,本函数在USBDHIDMouseInit中已经调用。
参数:ulIndex,USB模块代码,固定值:USB_BASE0。psDevice,USB鼠标类。
返回:指向配置后的tUSBDHIDMouseDevice。
unsigned long USBDHIDMouseStateChange(void *pvInstance, char cDeltaX,
char cDeltaY,
unsigned char ucButtons);
作用:鼠标状态改变,并发送报告给主机。
参数:pvInstance,指向tUSBDHIDMouseDevice,本函数将修改其X、Y、按键状态等。cDeltaX,X值。cDeltaY,Y值。ucButtons,鼠标按键。
返回:程序错误代码。
void USBDHIDMouseTerm(void *pvInstance);
作用:结束usb鼠标。
参数:pvInstance,指向tUSBDHIDMouseDevice。
返回:无。
void *USBDHIDMouseSetCBData(void *pvInstance, void *pvCBData);
作用:修改tUSBDHIDMouseDevice中的pvCBData指针。
参数:pvInstance,指向tUSBDHIDMouseDevice。pvCBData,数据指针,用于替换tUSBDHIDMouseDevice中的pvCBData指针。
返回:以前tUSBDHIDMouseDevice的pvCBData的指针。
void USBDHIDMousePowerStatusSet(void *pvInstance,
unsigned char ucPower);
作用:设置鼠标电源模式。
参数:pvInstance,指向tUSBDHIDMouseDevice。ucPower,电源工作模式,USB_STATUS_SELF_PWR或者USB_STATUS_BUS_PWR。
返回:无。
tBoolean USBDHIDMouseRemoteWakeupRequest(void *pvInstance);
作用:唤醒请求。
参数:pvInstance,指向tUSBDHIDMouseDevice。
返回:是否成功唤醒。
这些API中使用最多是USBDHIDMouseInit和USBDHIDMouseStateChange两个函数,在首次使用USB鼠标时,要初始化USB设备,使用USBDHIDMouseInit完成USB鼠标初始化、打开USB中断、枚举设备、描述符补全等;USBDHIDMouseStateChange设置鼠标X、Y、按键状态,并通过IN报告发送给主机,这是USB鼠标与主机进行数据通信最主要的接口函数,使用频率最高。
5.4.3 USB 鼠标开发
USB鼠标开发只需要4步就能完成。如图2所示,鼠标设备配置(主要是字符串描述符)、callback函数编写、USB处理器初始化、X\Y\按键处理。
<ignore_js_op>
图2
第一步:鼠标设备配置(主要是字符串描述符),按字符串描述符标准完成串描述符配置,进而完成鼠标设备配置。
#include "inc/hw_types.h"
#include "driverlib/usb.h"
#include "usblib/usblib.h"
#include "usblib/usbhid.h"
#include "usblib/usb-ids.h"
#include "usblib/device/usbdevice.h"
#include "usblib/device/usbdhid.h"
#include "usblib/device/usbdhidmouse.h"
#include "usb_mouse_structs.h"
//声明函数原型
unsigned long MouseHandler(void *pvCBData,
unsigned long ulEvent,
unsigned long ulMsgData,
void *pvMsgData);
//****************************************************************************
// 语言描述符
//****************************************************************************
const unsigned char g_pLangDescriptor[] =
{
4,
USB_DTYPE_STRING,
USBShort(USB_LANG_EN_US)
};
//****************************************************************************
// 制造商 字符串 描述符
//****************************************************************************
const unsigned char g_pManufacturerString[] =
{
(17 + 1) * 2,
USB_DTYPE_STRING,
'T', 0, 'e', 0, 'x', 0, 'a', 0, 's', 0, ' ', 0, 'I', 0, 'n', 0, 's', 0,
't', 0, 'r', 0, 'u', 0, 'm', 0, 'e', 0, 'n', 0, 't', 0, 's', 0,
};
//****************************************************************************
//产品 字符串 描述符
//****************************************************************************
const unsigned char g_pProductString[] =
{
(13 + 1) * 2,
USB_DTYPE_STRING,
'M', 0, 'o', 0, 'u', 0, 's', 0, 'e', 0, ' ', 0, 'E', 0, 'x', 0, 'a', 0,
'm', 0, 'p', 0, 'l', 0, 'e', 0
};
//****************************************************************************
// 产品 序列号 描述符
//****************************************************************************
const unsigned char g_pSerialNumberString[] =
{
(8 + 1) * 2,
USB_DTYPE_STRING,
'1', 0, '2', 0, '3', 0, '4', 0, '5', 0, '6', 0, '7', 0, '8', 0
};
//*****************************************************************************
// 设备接口字符串描述符
//*****************************************************************************
const unsigned char g_pHIDInterfaceString[] =
{
(19 + 1) * 2,
USB_DTYPE_STRING,
'H', 0, 'I', 0, 'D', 0, ' ', 0, 'M', 0, 'o', 0, 'u', 0, 's', 0,
'e', 0, ' ', 0, 'I', 0, 'n', 0, 't', 0, 'e', 0, 'r', 0, 'f', 0,
'a', 0, 'c', 0, 'e', 0
};
//*****************************************************************************
// 设备配置字符串描述符
//*****************************************************************************
const unsigned char g_pConfigString[] =
{
(23 + 1) * 2,
USB_DTYPE_STRING,
'H', 0, 'I', 0, 'D', 0, ' ', 0, 'M', 0, 'o', 0, 'u', 0, 's', 0,
'e', 0, ' ', 0, 'C', 0, 'o', 0, 'n', 0, 'f', 0, 'i', 0, 'g', 0,
'u', 0, 'r', 0, 'a', 0, 't', 0, 'i', 0, 'o', 0, 'n', 0
};
//*****************************************************************************
// 字符串描述符集合
//*****************************************************************************
const unsigned char * const g_pStringDescriptors[] =
{
g_pLangDescriptor,
g_pManufacturerString,
g_pProductString,
g_pSerialNumberString,
g_pHIDInterfaceString,
g_pConfigString
};
#define NUM_STRING_DESCRIPTORS (sizeof(g_pStringDescriptors) / \
sizeof(unsigned char *))
//*****************************************************************************
//鼠标实例,鼠标配置并为鼠标设备信息提供空间
//*****************************************************************************
tHIDMouseInstance g_sMouseInstance;
//*****************************************************************************
//鼠标设备配置
//*****************************************************************************
const tUSBDHIDMouseDevice g_sMouseDevice =
{
USB_VID_STELLARIS, //开发者自己定义
USB_PID_MOUSE, //开发者自己定义
500,
USB_CONF_ATTR_SELF_PWR,
MouseHandler,
(void *)&g_sMouseDevice,
g_pStringDescriptors,
NUM_STRING_DESCRIPTORS,
&g_sMouseInstance
};
第二步:完成Callback函数。Callback函数用于处理X、Y、按键事务。可能是主机发出,也可能是状态信息。USB鼠标设备中包含了以下事务:USB_EVENT_CONNECTED、USB_EVENT_DISCONNECTED、USB_EVENT_SUSPEND、USB_EVENT_RESUME、USB_EVENT_TX_COMPLETE。如下表:
名称 |
说明 |
USB_EVENT_CONNECTED |
USB设备已经连接到主机 |
USB_EVENT_DISCONNECTED |
USB设备已经与主机断开 |
USB_EVENT_SUSPEND |
挂起 |
USB_EVENT_RESUME |
唤醒 |
USB_EVENT_TX_COMPLETE |
发送完成 |
表2. USB鼠标事务
根据以上事务编写Callback函数:
unsigned long MouseHandler(void *pvCBData, unsigned long ulEvent,
unsigned long ulMsgData, void *pvMsgData)
{
switch (ulEvent)
{
case USB_EVENT_CONNECTED:
{
//连接成功时,点亮LED1
GPIOPinWrite(GPIO_PORTF_BASE,0x10,0x10);
break;
}
case USB_EVENT_DISCONNECTED:
{
//断开连接时,LED1灭
GPIOPinWrite(GPIO_PORTF_BASE,0x10,0x00);
break;
}
case USB_EVENT_TX_COMPLETE:
{
//发送完成时,LED2亮
GPIOPinWrite(GPIO_PORTF_BASE,0x20,0x20);
break;
}
case USB_EVENT_SUSPEND:
{
//发送完成时,LED2亮
GPIOPinWrite(GPIO_PORTF_BASE,0x20,0x0);
break;
}
case USB_EVENT_RESUME:
{
break;
}
default:
{
break;
}
}
return (0);
}
第三步:系统初始化,配置内核电压、系统主频、使能端口、配置按键端口、LED控制等,本例中使用4个按键控制鼠标移动,使用4个LED进行指示动作。原理图如图3所示:
<ignore_js_op>
图3
系统初始化:
//设置系统内核电压 与 主频
SysCtlLDOSet(SYSCTL_LDO_2_75V);
SysCtlClockSet(SYSCTL_XTAL_8MHZ | SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN ); //使能端口
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE,0xf0);
GPIOPinTypeGPIOInput(GPIO_PORTF_BASE,0x0f);
HWREG(GPIO_PORTF_BASE+GPIO_O_PUR) |= 0x0f;
//初始化鼠标设备
USBDHIDMouseInit (0, &g_sMouseDevice);
第四步:X、Y、按键处理。主要使用USBDHIDMouseStateChange设置X、Y、按键状态,并通过报告发送给主机。
while(1)
{
ulTemp = (~GPIOPinRead(GPIO_PORTF_BASE, 0x0f)) & 0x0f;
switch(ulTemp)
{
case 0x01:
x = x + 1;
key = 0;
break;
case 0x02:
x = x - 1 ;
key = 0;
break;
case 0x04:
y = y + 1;
key = 0;
break;
case 0x08:
y = y - 1;
key = 0;
break;
case 0x03:
key = 1;
break;
case 0x0c:
key = 2;
break;
case 0x09:
key = 4;
break;
default:
key = 0;
break;
}
if(ulTemp)
USBDHIDMouseStateChange((void *)&g_sMouseDevice,x,y,key);
SysCtlDelay(SysCtlClockGet()/30);
}
使用上面四步就完成USB鼠标开发,与普通USB鼠标一样操作。由于在这个例子中使用的是Demo开发板,只能用四个按键,模拟鼠标移动。USB鼠标开发时也要加入两个lib: usblib.lib和DriverLib.lib,在启动代码中加入USB0DeviceIntHandler中断服务函数。程序运行进如下图:
<ignore_js_op>
从图中可以看出USB鼠标枚举成功,在“设备管理器”中可以看到“USB人体学输入设备”,而且可以“鼠标和其它指针设备”中找到“HID-compliant mouse”,如下图。其中有一个就是上面开发的USB鼠标,查看“属性”可以看到下图:其中VID和PID都是之前配置的。
<ignore_js_op>
<ignore_js_op>
USB鼠标源码如下:
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_gpio.h"
#include "inc/hw_types.h"
#include "driverlib/debug.h"
#include "driverlib/gpio.h"
#include "inc/hw_types.h"
#include "driverlib/usb.h"
#include "inc/hw_sysctl.h"
#include "driverlib/sysctl.h"
#include "usblib/usblib.h"
#include "usblib/usbhid.h"
#include "usblib/usb-ids.h"
#include "usblib/device/usbdevice.h"
#include "usblib/device/usbdhid.h"
#include "usblib/device/usbdhidmouse.h"
//声明函数原型
unsigned long MouseHandler(void *pvCBData,
unsigned long ulEvent,
unsigned long ulMsgData,
void *pvMsgData);
//****************************************************************************
// 语言描述符
//****************************************************************************
const unsigned char g_pLangDescriptor[] =
{
4,
USB_DTYPE_STRING,
USBShort(USB_LANG_EN_US)
};
//****************************************************************************
// 制造商 字符串 描述符
//****************************************************************************
const unsigned char g_pManufacturerString[] =
{
(17 + 1) * 2,
USB_DTYPE_STRING,
'T', 0, 'e', 0, 'x', 0, 'a', 0, 's', 0, ' ', 0, 'I', 0, 'n', 0, 's', 0,
't', 0, 'r', 0, 'u', 0, 'm', 0, 'e', 0, 'n', 0, 't', 0, 's', 0,
};
//****************************************************************************
//产品 字符串 描述符
//****************************************************************************
const unsigned char g_pProductString[] =
{
(13 + 1) * 2,
USB_DTYPE_STRING,
'M', 0, 'o', 0, 'u', 0, 's', 0, 'e', 0, ' ', 0, 'E', 0, 'x', 0, 'a', 0,
'm', 0, 'p', 0, 'l', 0, 'e', 0
};
//****************************************************************************
// 产品 序列号 描述符
//****************************************************************************
const unsigned char g_pSerialNumberString[] =
{
(8 + 1) * 2,
USB_DTYPE_STRING,
'1', 0, '2', 0, '3', 0, '4', 0, '5', 0, '6', 0, '7', 0, '8', 0
};
//*****************************************************************************
// 设备接口字符串描述符
//*****************************************************************************
const unsigned char g_pHIDInterfaceString[] =
{
(19 + 1) * 2,
USB_DTYPE_STRING,
'H', 0, 'I', 0, 'D', 0, ' ', 0, 'M', 0, 'o', 0, 'u', 0, 's', 0,
'e', 0, ' ', 0, 'I', 0, 'n', 0, 't', 0, 'e', 0, 'r', 0, 'f', 0,
'a', 0, 'c', 0, 'e', 0
};
//*****************************************************************************
// 设备配置字符串描述符
//*****************************************************************************
const unsigned char g_pConfigString[] =
{
(23 + 1) * 2,
USB_DTYPE_STRING,
'H', 0, 'I', 0, 'D', 0, ' ', 0, 'M', 0, 'o', 0, 'u', 0, 's', 0,
'e', 0, ' ', 0, 'C', 0, 'o', 0, 'n', 0, 'f', 0, 'i', 0, 'g', 0,
'u', 0, 'r', 0, 'a', 0, 't', 0, 'i', 0, 'o', 0, 'n', 0
};
//*****************************************************************************
// 字符串描述符集合
//*****************************************************************************
const unsigned char * const g_pStringDescriptors[] =
{
g_pLangDescriptor,
g_pManufacturerString,
g_pProductString,
g_pSerialNumberString,
g_pHIDInterfaceString,
g_pConfigString
};
#define NUM_STRING_DESCRIPTORS (sizeof(g_pStringDescriptors) / \
sizeof(unsigned char *))
//*****************************************************************************
//键盘实例,键盘配置并为键盘设备信息提供空间
//*****************************************************************************
tHIDMouseInstance g_sMouseInstance;
//*****************************************************************************
//键盘设备配置
//*****************************************************************************
const tUSBDHIDMouseDevice g_sMouseDevice =
{
USB_VID_STELLARIS,
USB_PID_MOUSE,
500,
USB_CONF_ATTR_SELF_PWR,
MouseHandler,
(void *)&g_sMouseDevice,
g_pStringDescriptors,
NUM_STRING_DESCRIPTORS,
&g_sMouseInstance
};
//*****************************************************************************
//键盘callback函数
//*****************************************************************************
unsigned long MouseHandler(void *pvCBData, unsigned long ulEvent,
unsigned long ulMsgData, void *pvMsgData)
{
switch (ulEvent)
{
case USB_EVENT_CONNECTED:
{
//连接成功时,点亮LED1
GPIOPinWrite(GPIO_PORTF_BASE,0x10,0x10);
break;
}
case USB_EVENT_DISCONNECTED:
{
//断开连接时,LED1灭
GPIOPinWrite(GPIO_PORTF_BASE,0x10,0x00);
break;
}
case USB_EVENT_TX_COMPLETE:
{
//发送完成时,LED2亮
GPIOPinWrite(GPIO_PORTF_BASE,0x20,0x20);
break;
}
case USB_EVENT_SUSPEND:
{
//发送完成时,LED2亮
GPIOPinWrite(GPIO_PORTF_BASE,0x20,0x0);
break;
}
case USB_EVENT_RESUME:
{
break;
}
default:
{
break;
}
}
return (0);
}
//*****************************************************************************
//主函数
//*****************************************************************************
int main(void)
{
//系统初始化。
unsigned long x=0,y=0,key=0,ulTemp=0;
SysCtlLDOSet(SYSCTL_LDO_2_75V);
SysCtlClockSet(SYSCTL_XTAL_8MHZ | SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN );
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE,0xf0);
GPIOPinTypeGPIOInput(GPIO_PORTF_BASE,0x0f);
HWREG(GPIO_PORTF_BASE+GPIO_O_PUR) |= 0x0f;
USBDHIDMouseInit(0, &g_sMouseDevice);
while(1)
{
ulTemp = (~GPIOPinRead(GPIO_PORTF_BASE, 0x0f)) & 0x0f;
switch(ulTemp)
{
case 0x01:
x = x + 1;
key = 0;
break;
case 0x02:
x = x - 1 ;
key = 0;
break;
case 0x04:
y = y + 1;
key = 0;
break;
case 0x08:
y = y - 1;
key = 0;
break;
case 0x03:
key = 1;
break;
case 0x0c:
key = 2;
break;
case 0x09:
key = 4;
break;
default:
key = 0;
break;
}
if(ulTemp)
USBDHIDMouseStateChange((void *)&g_sMouseDevice,x,y,key);
SysCtlDelay(SysCtlClockGet()/30);
}
}
5.5 小结
经过本章节介绍,读者对HID设备有初步了解,如果想了解更深层次的HID设备类,可能参考官方数据手册。本章主要介绍了HID设备类的结构与编程、USB库函数编程、USB键盘开发与USB鼠标开发。当然这些代码都是简单的实现功能,真正的产口还需要在这基础之上进一步完善与优化。
第五章 HID设备的更多相关文章
- 精通Web Analytics 2.0 (7) 第五章:荣耀之钥:度量成功
精通Web Analytics 2.0 : 用户中心科学与在线统计艺术 第五章:荣耀之钥:度量成功 我们的分析师常常得不到我们应得的喜欢,尊重和资金,因为我们没有充分地衡量一个黄金概念:成果.因为我们 ...
- 《Linux内核设计与实现》课本第五章学习笔记——20135203齐岳
<Linux内核设计与实现>课本第五章学习笔记 By20135203齐岳 与内核通信 用户空间进程和硬件设备之间通过系统调用来交互,其主要作用有三个. 为用户空间提供了硬件的抽象接口. 保 ...
- Android深度探索--HAL与驱动开发----第五章读书笔记
第五章主要学习了搭建S3C6410开发板的测试环境.首先要了解到S3C6410是一款低功耗.高性价比的RISC处理器它是基于ARMI1内核,广泛应用于移动电话和通用处理等领域. 开发板从技术上说与我们 ...
- (转)iOS Wow体验 - 第五章 - 利用iOS技术特性打造最佳体验
本文是<iOS Wow Factor:Apps and UX Design Techniques for iPhone and iPad>第五章译文精选,其余章节将陆续放出.上一篇:Wow ...
- perl5 第五章 文件读写
第五章 文件读写 by flamephoenix 一.打开.关闭文件二.读文件三.写文件四.判断文件状态五.命令行参数六.打开管道 一.打开.关闭文件 语法为open (filevar, file ...
- CSS3秘笈复习:十三章&十四章&十五章&十六章&十七章
第十三章 1.在使用浮动时,源代码的顺序非常重要.浮动元素的HTML必须处在要包围它的元素的HTML之前. 2.清楚浮动: (1).在外围div的底部添加一个清除元素:clear属性可以防止元素包围浮 ...
- Python第五章__模块介绍,常用内置模块
Python第五章__模块介绍,常用内置模块 欢迎加入Linux_Python学习群 群号:478616847 目录: 模块与导入介绍 包的介绍 time &datetime模块 rando ...
- PJSUA2开发文档--第五章 帐户(号)Accounts
第五章 帐户(号) 帐户提供正在使用该应用程序的用户的身份(或身份).一个帐户有一个与之相关的SIP统一资源标识符(URI).在SIP术语中,该URI用作该人的记录地址( Address of Rec ...
- 《linux内核设计与实现》第五章
第五章 系统调用 一.与内核通信 系统调用在用户空间进程和硬件设备之间添加了一个中间层.作用: 为用户空间提供了一种硬件的抽象接口. 系统调用保证了系统的稳定和安全. 每个进程都运行在虚拟系统中,而在 ...
随机推荐
- 如何让Div层悬浮在Flash Object对象之上(转载)
今天有个用户,门户右上角的倒三角登陆小按钮在他的电脑上无法显示,他用的笔记本屏幕较小,宽度正好显示出页面内容,经查看,门户页眉使用的为flash对象. 大家都知道,如果想让某个图片或者Div层悬浮在别 ...
- 安装Numpy和matplotlib
(1)测试程序 这是我从网上(http://www.open-open.com/lib/view/open1393488232380.html)找到的一个使用Numpy和matplotlib的 ...
- Android数据存储方式之SharedPreferences
Android平台给我们提供了一个SharedPreferences类,它是一个轻量级的存储类,特别适合用于保存软件配置参数.使用SharedPreferences保存数据,其背后是用xml文件存放数 ...
- CocoaLumberjack+XcodeColor(输出带有颜色的日志)在安装过程中遇到的问题
在安装的时候遇到了各种坑,(在这里用到的pch文件的使用以及解决无法引入的问题,可以参考上午的文章) 一(XcodeColor的安装).在github上下载XcodeClolor的插件,并且安装,Xc ...
- ERROR 2003 (HY000): Can't connect to MySQL server on 'localhost' (10061)
http://wandering192.iteye.com/blog/758954 谢谢作者
- SQL SERVER删除列,报错."由于一个或多个对象访问此列,ALTER TABLE DROP COLUMN ... 失败"
队友给我修改数据的语句.总是执行失败.很纳闷. 如下图: 仔细看了下这个列,并没有什么特殊.如下图: 但其确实有个约束: 'DF__HIS_DRUG___ALL_I__04E4BC85' . 为什么有 ...
- C++ IO 详细用法
http://www.cnblogs.com/keam37/ keam所有 转载请注明出处 本文将分别从<iostream>,<sstream>,<fstream> ...
- 九度OJ 1534 数组中第K小的数字 -- 二分查找
题目地址:http://ac.jobdu.com/problem.php?pid=1534 题目描述: 给定两个整型数组A和B.我们将A和B中的元素两两相加可以得到数组C. 譬如A为[1,2],B为[ ...
- Eclipse配置CAS client
1.新建一个Maven项目 2.Next,选择 3.输入group id 和 artifact id --> Finish 4.项目创建完成的目录结构 编辑pom.xml文件,写上依赖 注意把 ...
- Spring MVC中Ajax实现二级联动
今天写项目遇到了二级联动,期间遇到点问题,写个博客记录一下. 后台Controller: @RequestMapping("/faultType") @ResponseBody p ...