catalogue
0. 引言 1. Device Class Definition for Human Interface Devices (HID) 2. USB HID Report Descriptors 3. arduino USB Keyboard definition 4. USB Host Shield for Arduino分类 5. USB Host Shield for Arduino datasheet 6. USB Host Library Rev.2.0 7. USB Keyboard keylogger实验 8. keylogger解析HID Report Descriptor后重新封装按键识别码模拟按键
0. 引言
0x1: 键盘按键到显示的全过程
1. 插入USB设备,USB鼠标/键盘内部单片机通电后,开始进行初始化过程,产生一个外部设备中断信号,向上位机(被插的机器)发送一个HID Device Descriptor,用户表明自己的身份(设备号、版本等信息) 2. 上位机收到设备描述符后,load对应的设备驱动(第一次插入时会弹出驱动安装对话框),并注册到设备栈上,随机结束中断响应,等待新的按键事件的发生 3. 在按下键盘后,首先是物理按键接触到了电路板,然后产生了一个电脉冲信号,键盘硬件通过方波电平整流将其转换为0101的电平信号,发送到键盘内部的HID译码器 4. 键盘中的每一个键提前都是被编号的,按下的按键的电平信号经过翻译到得到一个HID编号,暂时存在键盘的寄存器里 5. 通过RS232或者USB TTL方式以串行方式向上位机(常常是PC机)发送HID信号,这个信号以一个中断的形式(通过串行数据的开始位向CPU的外设中断引脚设置中断位)告知CPU有按键按下了 6. CPU获取这个中断事件后,将转向相应的处理函数,而这个函数是内核规定的 7. 之后开始从硬件层面转向操作系统层面,操作系统中对键盘事件进行了编程,在操作系统捕获键盘事件后会将这个键盘事件放入到事件队列里,并给应用程序提供了相应的接口,应用程序(记事本)捕获到了这个事件,从封装的接口中获取键盘按键编号,再用系统接口进行字符的显示 8. 操作系统获取从比较高的软件层获取了需要显示的字符后再驱动显卡,将要显示的字符的点阵信息放到显存中,显卡从显存中获取需要显示的内容,驱动显示屏将一个个的像素点显示在屏幕上,就这样一个字符就显示出来了
0x2: GPIO
General Purpose Input Output(通用输入/输出)简称为GPIO,或总线扩展器,人们利用工业标准I2C、SMBus或SPI接口简化了I/O口的扩展。当微控制器或芯片组没有足够的I/O端口,或当系统需要采用远端串行通信或控制时,GPIO产品能够提供额外的控制和监视功能,每个GPIO端口可通过软件分别配置成输入或输出
1. 优点
1. 低功耗: GPIO具有更低的功率损耗(大约1μA,μC的工作电流则为100μA) 2. 集成IIC从机接口: GPIO内置IIC从机接口,即使在待机模式下也能够全速工作 3. 小封装: GPIO器件提供最小的封装尺寸 ― 3mm x 3mm QFN! 4. 低成本: 不用为没有使用的功能花费成本 5. 快速上市: 不需要编写额外的代码、文档,不需要任何维护工作 6. 灵活的灯光控制: 内置多路高分辨率的PWM输出 7. 可预先确定响应时间: 缩短或确定外部事件与中断之间的响应时间 8. 更好的灯光效果: 匹配的电流输出确保均匀的显示亮度 9. 布线简单: 仅需使用2条就可以组成IIC总线或3条组成SPI总线
Relevant Link:
http://baike.baidu.com/link?url=1Mlz87tzk4oDOTv2ea5foiMHW77Q9jUGNEDk9MBAg2EQBtQ-uMw1jW_4XiSaACC39q5rZpyRn3lswYGxrJbn__ http://baike.baidu.com/pic/gpio/4723219/0/7c1ed21b0ef41bd5245797d253da81cb39db3d8b?fr=lemma&ct=single#aid=0&pic=7c1ed21b0ef41bd5245797d253da81cb39db3d8b
1. Device Class Definition for Human Interface Devices (HID)
0x1: Introduction
Universal Serial Bus (USB) is a communications architecture that gives a personal computer (PC) the ability to interconnect a variety of devices using a simple fourwire cable(vcc、gun、tx、rx).
The USB is actually a two-wire serial communication link that runs at either 1.5 or 12 megabits per second (mbs). USB protocols can configure devices at startup or when they are plugged in at run time. These devices are broken into various device classes. Each device class defines the common behavior and protocols for devices that serve similar functions. Some examples of USB device classes are shown in the following table:
Device Class Example Device Display Monitor Communication Modem Audio Speakers Mass storage Hard drive Human interface Data glove
The HID class consists primarily of devices that are used by humans to control the operation of computer systems(让人通过外设操控PC). Typical examples of HID class devices include:
1. Keyboards and pointing devices 1) standard mouse devices 2) trackballs(轨迹球) 3) joysticks(操纵杆) 2. Front-panel controls 1) knobs(旋钮) 2) switches 3) buttons 4) sliders(滑块) 3. Controls that might be found on devices such as 1) telephones 2) VCR remote controls 3) games or simulation devices 3.1) data gloves 3.2) throttles 3.3) steering wheels 3.4) rudder pedals. 4. Devices that may not require human interaction but provide data in a similar format to HID class devices 1) bar-code readers(条码阅读器),超市里的条码阅读器本质上可以理解为一个BadUSB,刷了二维码或者条形码之后,通过usb hid向上位机发送按键信息 2) thermometers(温度计) 3) voltmeters(电压表)
0x2: Management Overview
Information about a USB device is stored in segments of its ROM (read-only memory). These segments are called descriptors. An interface descriptor can identify a device as belonging to one of a finite number of classes
A USB/HID class device uses a corresponding HID class driver to retrieve and route all data(需要和HID匹配的设备驱动对HID Descriptor进行解包).
The routing and retrieval of data is accomplished by examining the descriptors of the device and the data it provides.
The HID class device descriptor identifies which other HID class descriptors are present and indicates their sizes. For example, Report and Physical Descriptors.
1. Report descriptor
A Report descriptor describes each piece of data that the device generates and what the data is actually measuring.
For example, a Report descriptor defines items that describe a position or button state. Item information is used to:
1. Determine where to route input 1) send input to mouse or joystick API. 2. Allow software to assign functionality to input 1) use joystick input to position a tank.
By examining an items (collectively called the Report descriptor) the HID class driver is able to determine the size and composition of data reports from the HID class device.
2. Physical descriptor
Physical descriptor sets are optional descriptors which provide information about the part or parts of the human body used to activate the controls on a device.
All of these things can be combined to illustrate the descriptor structure.‘
0x3: Functional Characteristics
1. The HID Class
USB devices are segmented into device classes that
1. Have similar data transport requirements. 2. Share a single class driver.
A USB device may be a single class type or it may be composed of multiple classes.
For example, a telephone hand set might use features of the 1. HID 2. Audio and 3. Telephony classes. This is possible because the class is specified in the Interface descriptor and not the Device descriptor.
2. Subclass
During the early development of the HID specification, subclasses were intended to be used to identify the specific protocols of different types of HID class devices. While this mirrors the model currently in use by the industry (all devices use protocols defined by similar popular devices), it quickly became apparent that this approach was too restrictive. That is, devices would need to fit into narrowly defined subclasses and would not be able to provide any functionality beyond that supported by the subclass.
In addition, many known devices seemed to straddle multiple classifications—for example, keyboards with locators, or locators that provided keystrokes. Consequently, the HID class does not use subclasses to define most protocols. Instead, a HID class device identifies its data protocol and the type of data provided within its Report descriptor. 在多功能混合设备流行的现在,HID设备更倾向于不使用sub class来定义协议,而是将自己设备中包含的HID Devices定义统一封装在HID Report descriptor中
The Report descriptor is loaded and parsed by the HID class driver as soon as the device is detected. Protocols for existing and new devices are created by mixing data types within the Report descriptor
3. Protocols
A variety of protocols are supported HID devices, like
0 None 1 Keyboard 2 Mouse 3 - 255 Reserved
4. Interfaces
A HID class device communicates with the HID class driver using either the Control (default) pipe or an Interrupt pipe.
The Control pipe is used for: 1. Receiving and responding to requests for USB control and class data. 2. Transmitting data when polled by the HID class driver (using the Get_Report request). 3. Receiving data from the host. The Interrupt pipe are used for: 1. Receiving asynchronous (unrequested) data from the device(串口通信). 2. Transmitting low latency data to the device.
The Interrupt Out pipe is optional. If a device declares an Interrupt Out endpoint then Output reports are transmitted by the host to the device through the Interrupt Out endpoint. If no Interrupt Out endpoint is declared then Output reports are transmitted to a device through the Control endpoint, using Set_Report(Output) requests.
0x4: Operational Model
1. Device Descriptor Structure
At the topmost level, a descriptor includes two tables of information referred to as the Device descriptor and the String descriptor. A standard USB Device descriptor specifies the Product ID and other information about the device. For example, Device descriptor fields primarily include
1. Class 2. Subclass 3. Vendor 4. Product 5. Version
2. Report Descriptors
Report descriptors are composed of pieces of information. Each piece of information is called an Item.
3. Generic Item Format
An item is piece of information about the device. All items have a one-byte prefix that contains the item tag, item type, and item size.
4. Item Parser
The HID class driver contains a parser used to analyze items found in the Report descriptor. The parser extracts information from the descriptor in a linear fashion.
The parser collects the state of each known item as it walks through the descriptor, and stores them in an item state table. The item state table contains the state of individual items.
0x5: Descriptors
1. Standard Descriptors
The HID class device class uses the following standard USB descriptors
1. Device 2. Configuration 3. Interface 4. Endpoint 5. String
2. Class-Specific Descriptors
Each device class includes one or more class-specific descriptors. These descriptors differ from standard USB descriptors. A HID class device uses the following class-specific descriptors:
1. HID 2. Report 3. Physical
0x6: Report Protocol
1. Report Types
Reports contain data from one or more items. Data transfers are sent from the device to the host through the Interrupt In pipe in the form of reports.
Reports may also be requested (polled) and sent through the Control pipe or sent through an optional Interrupt Out pipe.
A report contains the state of all the items (Input, Output or Feature) belonging to a particular Report ID. The software application(设备驱动) is responsible for extracting the individual items from the report based on the Report descriptor.
All of the items’ values are packed on bit boundaries in the report (no byte or nibble alignment). However, items reporting Null or constant values may be used to byte-align values, or the Report Size may be made larger than needed for some fields simply to extend them to a byte boundary.
The bit length of an item’s data is obtained through the Report descriptor (Report Size * Report Count). Item data is ordered just as items are ordered in the Report descriptor. If a Report ID tag was used in the Report descriptor, all reports include a single byte ID prefix. If the Report ID tag was not used, all values are returned in a single report and a prefix ID is not included in that report
2. Report Format for Array Items
Each button in an array reports an assigned number called an array index. This can be translated into a keycode by looking up the array elements Usage Page and Usage. When any button transitions between open and closed, the entire list of
indices for buttons currently closed in the array is transmitted to the host.
Since only one array element can be reported in each array field, modifier keys should be reported as bitmap data (a group of 1-bit variable fields). For example, keys such as CTRL, SHIFT, ALT, and GUI keys make up the 8 bit modifier byte in a
standard keyboard report. Although these usage codes are defined in the Usage Table as E0–E7, the usage is not sent as array data. The modifier byte is defined as follows.
Bit Key 0 LEFT CTRL 1 LEFT SHIFT 2 LEFT ALT 3 LEFT GUI 4 RIGHT CTRL 5 RIGHT SHIFT 6 RIGHT ALT 7 RIGHT GUI
The following example shows the reports generated by a user typing ALT+CTRL+DEL, using a bitmap for the modifiers and a single array for all other keys.
Transition Modifier Byte Array Byte LEFT ALT down 00000100 00 RIGHT CTRL down 00010100 00 DEL down 00010100 4C DEL up 00010100 00 RIGHT CTRL up 00000100 00 LEFT ALT up 00000000 00
If there are multiple reports for this device, each report would be preceded by its unique Report ID
3. Report Example
The following Report descriptor defines an item with an Input report.
Usage Page (Generic Desktop), Usage (Mouse), Collection (Application), Usage (Pointer), Collection (Physical), Report ID (0A), ;Make changes to report 0A Usage (X), Usage (Y), Logical Minimum (-127), ;Report data values range from -127 Logical Maximum (127), ;to 127 Report Size (8), Report Count (2), Input (Data, Variable, Relative), ;Add 2 bytes of position data (X & Y) to report 0A Logical Minimum (0), ;Report data values range from -127 Logical Maximum (1), ;to 127 Report Count (3), Report Size (1), Usage Page (Button Page), Usage Minimum (1), Usage Maximum (3), Input (Data, Variable, Absolute), ;Add 2 bits (Button 1, 2 & 3) to report 0A Report Size (5), Input (Constant), ;Add 5 bits padding to byte align the report 0A End Collection, End Collection
The Input report structure for the above device would look as follows
The following table uses a keyboard with an integrated pointing device to demonstrate how to use two reports for a device with just one interface:
Relevant Link:
file:///C:/Users/zhenghan.zh/Desktop/HID1_11.pdf
2. USB HID Device Descriptors
USB HID Device Descriptors相当于设备的注册信息,是HID设备向HOST声明自己的一个数据结构,在设备插入PC时会发送给PC,用于初始化,我们在设备管理器里看到的设备信息就是这个时候发送的
3. USB HID Report Descriptors
A USB HID report descriptor is one of the descriptors that a USB host can received from a USB device. HID devices send data to the host using reports, and the descriptor tells the host how to interpret the data
The HID protocol makes implementation of devices very simple. Devices define their data packets and then present a “HID descriptor” to the host. The HID descriptor is a hard coded array of bytes that describe the device’s data packets. This includes:
1. how many packets the device supports 2. how large are the packets 3. the purpose of each byte and bit in the packet.
For example, a keyboard with a calculator program button can tell the host that the button’s pressed/released state is stored as the 2nd bit in the 6th byte in data packet number 4 (note: these locations are only illustrative and are device specific). The device typically stores the HID descriptor in ROM and does not need to intrinsically understand or parse the HID descriptor. Some mouse and keyboard hardware in the market today are implemented using only an 8-bit CPU(键盘也需要内置单片机微控制器,用于包装USB HID Descriptors)
USB HID Report Descriptors相当于设备发出的按键信息(通过中断方式),每个设备事件都会产生一个HID Report Descriptor,其中包含该事件的描述信息,例如鼠标的x/y坐标,按键识别码等等
0x1: Device Class Definition HID
The Device Class Definition for HID 1.11 is intended to supplement the USB Specification and provide HID manufacturers with the information necessary to build USB-compatible devices. It also specifies how the HID class driver should extract data from USB devices. The primary and underlying goals of the HID class definition are to:
1. be as compact as possible to save device data space 2. allow the software application to skip unknown information 3. be extensible and robust 4. support nesting and collections 5. be self-describing to allow generic software applications
Relevant Link:
http://eleccelerator.com/tutorial-about-usb-hid-report-descriptors/ http://www.usb.org/developers/hidpage#Class_Definitions http://www.usb.org/developers/hidpage/HID1_11.pdf http://www.usb.org/developers/hidpage#HID_Usage http://www.usb.org/developers/hidpage#HID http://www.usb.org/developers/hidpage/usbwire.pdf
3. arduino USB Keyboard definition
0x1: HID.H
HID.H封装了一些Host Interface Descriptor的通用数据结构
/* Copyright (c) 2015, Arduino LLC Original code (pre-library): Copyright (c) 2011, Peter Barrett Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef HID_h #define HID_h #include <stdint.h> #include <Arduino.h> #include "PluggableUSB.h" #if defined(USBCON) #define _USING_HID // HID ‘Driver‘ // ------------ #define HID_GET_REPORT 0x01 #define HID_GET_IDLE 0x02 #define HID_GET_PROTOCOL 0x03 #define HID_SET_REPORT 0x09 #define HID_SET_IDLE 0x0A #define HID_SET_PROTOCOL 0x0B #define HID_HID_DESCRIPTOR_TYPE 0x21 #define HID_REPORT_DESCRIPTOR_TYPE 0x22 #define HID_PHYSICAL_DESCRIPTOR_TYPE 0x23 // HID subclass HID1.11 Page 8 4.2 Subclass #define HID_SUBCLASS_NONE 0 #define HID_SUBCLASS_BOOT_INTERFACE 1 // HID Keyboard/Mouse bios compatible protocols HID1.11 Page 9 4.3 Protocols #define HID_PROTOCOL_NONE 0 #define HID_PROTOCOL_KEYBOARD 1 #define HID_PROTOCOL_MOUSE 2 // Normal or bios protocol (Keyboard/Mouse) HID1.11 Page 54 7.2.5 Get_Protocol Request // "protocol" variable is used for this purpose. #define HID_BOOT_PROTOCOL 0 #define HID_REPORT_PROTOCOL 1 // HID Request Type HID1.11 Page 51 7.2.1 Get_Report Request #define HID_REPORT_TYPE_INPUT 1 #define HID_REPORT_TYPE_OUTPUT 2 #define HID_REPORT_TYPE_FEATURE 3 typedef struct { uint8_t len; // 9 uint8_t dtype; // 0x21 uint8_t addr; uint8_t versionL; // 0x101 uint8_t versionH; // 0x101 uint8_t country; uint8_t desctype; // 0x22 report uint8_t descLenL; uint8_t descLenH; } HIDDescDescriptor; typedef struct { InterfaceDescriptor hid; HIDDescDescriptor desc; EndpointDescriptor in; } HIDDescriptor; class HIDSubDescriptor { public: HIDSubDescriptor *next = NULL; HIDSubDescriptor(const void *d, const uint16_t l) : data(d), length(l) { } const void* data; const uint16_t length; }; class HID_ : public PluggableUSBModule { public: HID_(void); int begin(void); int SendReport(uint8_t id, const void* data, int len); void AppendDescriptor(HIDSubDescriptor* node); protected: // Implementation of the PluggableUSBModule int getInterface(uint8_t* interfaceCount); int getDescriptor(USBSetup& setup); bool setup(USBSetup& setup); uint8_t getShortName(char* name); private: uint8_t epType[1]; HIDSubDescriptor* rootNode; uint16_t descriptorSize; uint8_t protocol; uint8_t idle; }; // Replacement for global singleton. // This function prevents static-initialization-order-fiasco // https://isocpp.org/wiki/faq/ctors#static-init-order-on-first-use HID_& HID(); #define D_HIDREPORT(length) { 9, 0x21, 0x01, 0x01, 0, 1, 0x22, lowByte(length), highByte(length) } #endif // USBCON #endif // HID_h
HID.cpp
/* Copyright (c) 2015, Arduino LLC Original code (pre-library): Copyright (c) 2011, Peter Barrett Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "HID.h" #if defined(USBCON) HID_& HID() { static HID_ obj; return obj; } int HID_::getInterface(uint8_t* interfaceCount) { *interfaceCount += 1; // uses 1 HIDDescriptor hidInterface = { D_INTERFACE(pluggedInterface, 1, USB_DEVICE_CLASS_HUMAN_INTERFACE, HID_SUBCLASS_NONE, HID_PROTOCOL_NONE), D_HIDREPORT(descriptorSize), D_ENDPOINT(USB_ENDPOINT_IN(pluggedEndpoint), USB_ENDPOINT_TYPE_INTERRUPT, USB_EP_SIZE, 0x01) }; return USB_SendControl(0, &hidInterface, sizeof(hidInterface)); } int HID_::getDescriptor(USBSetup& setup) { // Check if this is a HID Class Descriptor request if (setup.bmRequestType != REQUEST_DEVICETOHOST_STANDARD_INTERFACE) { return 0; } if (setup.wValueH != HID_REPORT_DESCRIPTOR_TYPE) { return 0; } // In a HID Class Descriptor wIndex cointains the interface number if (setup.wIndex != pluggedInterface) { return 0; } int total = 0; HIDSubDescriptor* node; for (node = rootNode; node; node = node->next) { int res = USB_SendControl(TRANSFER_PGM, node->data, node->length); if (res == -1) return -1; total += res; } // Reset the protocol on reenumeration. Normally the host should not assume the state of the protocol // due to the USB specs, but Windows and Linux just assumes its in report mode. protocol = HID_REPORT_PROTOCOL; return total; } uint8_t HID_::getShortName(char *name) { name[0] = ‘H‘; name[1] = ‘I‘; name[2] = ‘D‘; name[3] = ‘A‘ + (descriptorSize & 0x0F); name[4] = ‘A‘ + ((descriptorSize >> 4) & 0x0F); return 5; } void HID_::AppendDescriptor(HIDSubDescriptor *node) { if (!rootNode) { rootNode = node; } else { HIDSubDescriptor *current = rootNode; while (current->next) { current = current->next; } current->next = node; } descriptorSize += node->length; } int HID_::SendReport(uint8_t id, const void* data, int len) { auto ret = USB_Send(pluggedEndpoint, &id, 1); if (ret < 0) return ret; auto ret2 = USB_Send(pluggedEndpoint | TRANSFER_RELEASE, data, len); if (ret2 < 0) return ret2; return ret + ret2; } bool HID_::setup(USBSetup& setup) { if (pluggedInterface != setup.wIndex) { return false; } uint8_t request = setup.bRequest; uint8_t requestType = setup.bmRequestType; if (requestType == REQUEST_DEVICETOHOST_CLASS_INTERFACE) { if (request == HID_GET_REPORT) { // TODO: HID_GetReport(); return true; } if (request == HID_GET_PROTOCOL) { // TODO: Send8(protocol); return true; } if (request == HID_GET_IDLE) { // TODO: Send8(idle); } } if (requestType == REQUEST_HOSTTODEVICE_CLASS_INTERFACE) { if (request == HID_SET_PROTOCOL) { // The USB Host tells us if we are in boot or report mode. // This only works with a real boot compatible device. protocol = setup.wValueL; return true; } if (request == HID_SET_IDLE) { idle = setup.wValueL; return true; } if (request == HID_SET_REPORT) { //uint8_t reportID = setup.wValueL; //uint16_t length = setup.wLength; //uint8_t data[length]; // Make sure to not read more data than USB_EP_SIZE. // You can read multiple times through a loop. // The first byte (may!) contain the reportID on a multreport. //USB_RecvControl(data, length); } } return false; } HID_::HID_(void) : PluggableUSBModule(1, 1, epType), rootNode(NULL), descriptorSize(0), protocol(HID_REPORT_PROTOCOL), idle(1) { epType[0] = EP_TYPE_INTERRUPT_IN; PluggableUSB().plug(this); } int HID_::begin(void) { return 0; } #endif /* if defined(USBCON) */
0x2: Keyborad.h
Keyborad.h封装了键盘HID descriptor Report的基础数据结构,这其中就包括最重要的HID Keyboard code table(译码表)
/* Keyboard.h Copyright (c) 2015, Arduino LLC Original code (pre-library): Copyright (c) 2011, Peter Barrett This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef KEYBOARD_h #define KEYBOARD_h #include "HID.h" #if !defined(_USING_HID) #warning "Using legacy HID core (non pluggable)" #else //================================================================================ //================================================================================ // Keyboard #define KEY_LEFT_CTRL 0x80 #define KEY_LEFT_SHIFT 0x81 #define KEY_LEFT_ALT 0x82 #define KEY_LEFT_GUI 0x83 #define KEY_RIGHT_CTRL 0x84 #define KEY_RIGHT_SHIFT 0x85 #define KEY_RIGHT_ALT 0x86 #define KEY_RIGHT_GUI 0x87 #define KEY_UP_ARROW 0xDA #define KEY_DOWN_ARROW 0xD9 #define KEY_LEFT_ARROW 0xD8 #define KEY_RIGHT_ARROW 0xD7 #define KEY_BACKSPACE 0xB2 #define KEY_TAB 0xB3 #define KEY_RETURN 0xB0 #define KEY_ESC 0xB1 #define KEY_INSERT 0xD1 #define KEY_DELETE 0xD4 #define KEY_PAGE_UP 0xD3 #define KEY_PAGE_DOWN 0xD6 #define KEY_HOME 0xD2 #define KEY_END 0xD5 #define KEY_CAPS_LOCK 0xC1 #define KEY_F1 0xC2 #define KEY_F2 0xC3 #define KEY_F3 0xC4 #define KEY_F4 0xC5 #define KEY_F5 0xC6 #define KEY_F6 0xC7 #define KEY_F7 0xC8 #define KEY_F8 0xC9 #define KEY_F9 0xCA #define KEY_F10 0xCB #define KEY_F11 0xCC #define KEY_F12 0xCD // Low level key report: up to 6 keys and shift, ctrl etc at once typedef struct { uint8_t modifiers; uint8_t reserved; uint8_t keys[6]; } KeyReport; class Keyboard_ : public Print { private: KeyReport _keyReport; void sendReport(KeyReport* keys); public: Keyboard_(void); void begin(void); void end(void); size_t write(uint8_t k); size_t press(uint8_t k); size_t release(uint8_t k); void releaseAll(void); }; extern Keyboard_ Keyboard; #endif #endif
Keyborad.cpp
/* Keyboard.cpp Copyright (c) 2015, Arduino LLC Original code (pre-library): Copyright (c) 2011, Peter Barrett This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "Keyboard.h" #if defined(_USING_HID) //================================================================================ //================================================================================ // Keyboard static const uint8_t _hidReportDescriptor[] PROGMEM = { // Keyboard 0x05, 0x01, // USAGE_PAGE (Generic Desktop) // 47 0x09, 0x06, // USAGE (Keyboard) 0xa1, 0x01, // COLLECTION (Application) 0x85, 0x02, // REPORT_ID (2) 0x05, 0x07, // USAGE_PAGE (Keyboard) 0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl) 0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI) 0x15, 0x00, // LOGICAL_MINIMUM (0) 0x25, 0x01, // LOGICAL_MAXIMUM (1) 0x75, 0x01, // REPORT_SIZE (1) 0x95, 0x08, // REPORT_COUNT (8) 0x81, 0x02, // INPUT (Data,Var,Abs) 0x95, 0x01, // REPORT_COUNT (1) 0x75, 0x08, // REPORT_SIZE (8) 0x81, 0x03, // INPUT (Cnst,Var,Abs) 0x95, 0x06, // REPORT_COUNT (6) 0x75, 0x08, // REPORT_SIZE (8) 0x15, 0x00, // LOGICAL_MINIMUM (0) 0x25, 0x65, // LOGICAL_MAXIMUM (101) 0x05, 0x07, // USAGE_PAGE (Keyboard) 0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated)) 0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application) 0x81, 0x00, // INPUT (Data,Ary,Abs) 0xc0, // END_COLLECTION }; Keyboard_::Keyboard_(void) { static HIDSubDescriptor node(_hidReportDescriptor, sizeof(_hidReportDescriptor)); HID().AppendDescriptor(&node); } void Keyboard_::begin(void) { } void Keyboard_::end(void) { } void Keyboard_::sendReport(KeyReport* keys) { HID().SendReport(2,keys,sizeof(KeyReport)); } extern const uint8_t _asciimap[128] PROGMEM; #define SHIFT 0x80 const uint8_t _asciimap[128] = { 0x00, // NUL 0x00, // SOH 0x00, // STX 0x00, // ETX 0x00, // EOT 0x00, // ENQ 0x00, // ACK 0x00, // BEL 0x2a, // BS Backspace 0x2b, // TAB Tab 0x28, // LF Enter 0x00, // VT 0x00, // FF 0x00, // CR 0x00, // SO 0x00, // SI 0x00, // DEL 0x00, // DC1 0x00, // DC2 0x00, // DC3 0x00, // DC4 0x00, // NAK 0x00, // SYN 0x00, // ETB 0x00, // CAN 0x00, // EM 0x00, // SUB 0x00, // ESC 0x00, // FS 0x00, // GS 0x00, // RS 0x00, // US 0x2c, // ‘ ‘ 0x1e|SHIFT, // ! 0x34|SHIFT, // " 0x20|SHIFT, // # 0x21|SHIFT, // $ 0x22|SHIFT, // % 0x24|SHIFT, // & 0x34, // ‘ 0x26|SHIFT, // ( 0x27|SHIFT, // ) 0x25|SHIFT, // * 0x2e|SHIFT, // + 0x36, // , 0x2d, // - 0x37, // . 0x38, // / 0x27, // 0 0x1e, // 1 0x1f, // 2 0x20, // 3 0x21, // 4 0x22, // 5 0x23, // 6 0x24, // 7 0x25, // 8 0x26, // 9 0x33|SHIFT, // : 0x33, // ; 0x36|SHIFT, // < 0x2e, // = 0x37|SHIFT, // > 0x38|SHIFT, // ? 0x1f|SHIFT, // @ 0x04|SHIFT, // A 0x05|SHIFT, // B 0x06|SHIFT, // C 0x07|SHIFT, // D 0x08|SHIFT, // E 0x09|SHIFT, // F 0x0a|SHIFT, // G 0x0b|SHIFT, // H 0x0c|SHIFT, // I 0x0d|SHIFT, // J 0x0e|SHIFT, // K 0x0f|SHIFT, // L 0x10|SHIFT, // M 0x11|SHIFT, // N 0x12|SHIFT, // O 0x13|SHIFT, // P 0x14|SHIFT, // Q 0x15|SHIFT, // R 0x16|SHIFT, // S 0x17|SHIFT, // T 0x18|SHIFT, // U 0x19|SHIFT, // V 0x1a|SHIFT, // W 0x1b|SHIFT, // X 0x1c|SHIFT, // Y 0x1d|SHIFT, // Z 0x2f, // [ 0x31, // bslash 0x30, // ] 0x23|SHIFT, // ^ 0x2d|SHIFT, // _ 0x35, // ` 0x04, // a 0x05, // b 0x06, // c 0x07, // d 0x08, // e 0x09, // f 0x0a, // g 0x0b, // h 0x0c, // i 0x0d, // j 0x0e, // k 0x0f, // l 0x10, // m 0x11, // n 0x12, // o 0x13, // p 0x14, // q 0x15, // r 0x16, // s 0x17, // t 0x18, // u 0x19, // v 0x1a, // w 0x1b, // x 0x1c, // y 0x1d, // z 0x2f|SHIFT, // { 0x31|SHIFT, // | 0x30|SHIFT, // } 0x35|SHIFT, // ~ 0 // DEL }; uint8_t USBPutChar(uint8_t c); // press() adds the specified key (printing, non-printing, or modifier) // to the persistent key report and sends the report. Because of the way // USB HID works, the host acts like the key remains pressed until we // call release(), releaseAll(), or otherwise clear the report and resend. size_t Keyboard_::press(uint8_t k) { uint8_t i; if (k >= 136) { // it‘s a non-printing key (not a modifier) k = k - 136; } else if (k >= 128) { // it‘s a modifier key _keyReport.modifiers |= (1<<(k-128)); k = 0; } else { // it‘s a printing key k = pgm_read_byte(_asciimap + k); if (!k) { setWriteError(); return 0; } if (k & 0x80) { // it‘s a capital letter or other character reached with shift _keyReport.modifiers |= 0x02; // the left shift modifier k &= 0x7F; } } // Add k to the key report only if it‘s not already present // and if there is an empty slot. if (_keyReport.keys[0] != k && _keyReport.keys[1] != k && _keyReport.keys[2] != k && _keyReport.keys[3] != k && _keyReport.keys[4] != k && _keyReport.keys[5] != k) { for (i=0; i<6; i++) { if (_keyReport.keys[i] == 0x00) { _keyReport.keys[i] = k; break; } } if (i == 6) { setWriteError(); return 0; } } sendReport(&_keyReport); return 1; } // release() takes the specified key out of the persistent key report and // sends the report. This tells the OS the key is no longer pressed and that // it shouldn‘t be repeated any more. size_t Keyboard_::release(uint8_t k) { uint8_t i; if (k >= 136) { // it‘s a non-printing key (not a modifier) k = k - 136; } else if (k >= 128) { // it‘s a modifier key _keyReport.modifiers &= ~(1<<(k-128)); k = 0; } else { // it‘s a printing key k = pgm_read_byte(_asciimap + k); if (!k) { return 0; } if (k & 0x80) { // it‘s a capital letter or other character reached with shift _keyReport.modifiers &= ~(0x02); // the left shift modifier k &= 0x7F; } } // Test the key report to see if k is present. Clear it if it exists. // Check all positions in case the key is present more than once (which it shouldn‘t be) for (i=0; i<6; i++) { if (0 != k && _keyReport.keys[i] == k) { _keyReport.keys[i] = 0x00; } } sendReport(&_keyReport); return 1; } void Keyboard_::releaseAll(void) { _keyReport.keys[0] = 0; _keyReport.keys[1] = 0; _keyReport.keys[2] = 0; _keyReport.keys[3] = 0; _keyReport.keys[4] = 0; _keyReport.keys[5] = 0; _keyReport.modifiers = 0; sendReport(&_keyReport); } size_t Keyboard_::write(uint8_t c) { uint8_t p = press(c); // Keydown release(c); // Keyup return p; // just return the result of press() since release() almost always returns 1 } Keyboard_ Keyboard; #endif
4. USB Host Shield for Arduino分类
从硬件上来看,要完成解析USB数据的单片机需要由以下部分组成
2. USB接口芯片: 接收USB串行信号 USB芯片分主控芯片和设备芯片两种。通讯都是建立在主控芯片与设备芯片之间的,如果需要解析USB发送的数据信息,就需要将单片机改造为主机模式
0x1: Arduino UNO MEGA单片机 + Google ADK Android FZ0479模组(MAX3421E)
The following device classes are currently supported by the shield:
1. HID devices, such as 1) keyboards 2) mice 3) joysticks, etc. 2. game controllers 1) Sony PS3 2) Nintendo Wii 3) Xbox360 3. USB to serial converters 1) FTDI 2) PL-2303 3) ACM 4) as well as certain cell phones and GPS receivers 4. ADK-capable Android phones and tables 5. Digital cameras 1) Canon EOS 2) Powershot 3) Nikon DSLRs and P&S 4) as well as generic PTP
1. arduino meets USB Host Shield 通过 ICSP方式和 arduino单片机电路连接图
Relevant Link:
https://www.circuitsathome.com/products-page/arduino-shields/usb-host-shield-for-arduino-pro-mini https://item.taobao.com/item.htm?_u=b1itr7pff20&id=520509601533 http://www.geek-workshop.com/thread-1156-1-1.html http://microcontrollershop.com/product_info.php?products_id=4537
0x2: CH375B模组 + 51单片机
1. 电路图
1. CH375 是一个 USB 总线的通用接口芯片,支持 USB-HOST 主机方式和 USB-DEVICE/SLAVE 设备方式 2. 在本地端,CH375 具有 8 位数据总线和读、写、片选控制线以及中断输出,可以方便地挂接到单片机/DSP/MCU/MPU 等控制器的系统总线上 3. 在 USB 主机方式下,CH375 还提供了串行通讯方式,通过串行输入、串行输出和中断输出与单片机/DSP/MCU/MPU 等相连接 4. CH375 的 USB 设备方式与 CH372 芯片完全兼容,CH375 包含了 CH372 的全部功能 5. CH375 的 USB 主机方式支持常用的 USB 全速设备,外部单片机可以通过 CH375 按照相应的 USB 协议与 USB 设备通讯 6. CH375 还内置了处理 Mass-Storage 海量存储设备的专用通讯协议的固件,外部单片机可以直接以扇区为基本单位读写常用的 USB 存储设备(包括 USB 硬盘/USB 闪存盘/U 盘)
2. 特点
1. 低速和全速 USB-HOST 主机接口,兼容 USB V2.0,外围元器件只需要晶体和电容 2. 低速和全速 USB 设备接口,完全兼容 CH372 芯片,支持动态切换主机与设备方式 3. 主机端点输入和输出缓冲区各 64 字节,支持 12Mbps 全速 USB 设备和 1.5Mbps 低速设备 4. 支持 USB 设备的控制传输、批量传输、中断传输 5. 自动检测 USB 设备的连接和断开,提供设备连接和断开的事件通知 6. 内置控制传输的协议处理器,简化常用的控制传输 7. 内置固件处理海量存储设备的专用通讯协议,支持 Bulk-Only 传输协议和 SCSI、UFI、RBC 或等效命令集的 USB 存储设备(包括 USB 硬盘、USB 闪存盘、U 盘、USB 读卡器) 8. 通过 U 盘文件级子程序库实现单片机读写 USB 存储设备中的文件。 9. 并行接口包含 8 位数据总线,4 线控制 1) 读选通 2) 写选通 3) 片选输入 4) 中断输出 10. 串行接口包含串行输入、串行输出、中断输出,支持通讯波特率动态调整 11. 支持 5V 电源电压和 3.3V 电源电压,支持低功耗模式 12. 采用 SOP-28 无铅封装,兼容 RoHS,提供 SOP28 到 DIP28 的转换板,引脚基本兼容 CH374 芯片
3. 封装
4. 引脚
5. 功能说明
CH375 的 USB 主机方式支持并行接口和串行接口。在 USB 主机方式下,CH375 支持各种常用的 USB 全速设备,外部单片机(例如arduino)需要编写固件程序按照相应的 USB 协议与 USB 设备通讯。但是对于 USB 存储设备,CH375 内置了相关协议,通常情况下,外部单片机不需要编写固件程序,就可以直接通讯
并行接口
并口信号线包括
1. 8 位双向数据总线 D7 ~ D0: 通过被动并行接口,CH375 芯片可以很方便地挂接到各种 8 位单片机、DSP、MCU 的系统总线上,并且可以与多个外围器件共存 2. 读选通输入引脚 RD#: 对于类似 Intel 并口时序的单片机,CH375 芯片的 RD#引脚和 WR#引脚可以分别连接到单片机的读选通输出引脚和写选通输出引脚 3. 写选通输入引脚 WR#: 对于类似 Motorola 并口时序的单片机,CH375 芯片的 RD#引脚应该接低电平,并且 WR#引脚连接到单片机的读写方向输出引脚 R/-W 4. 片选输入引脚 CS#: 由地址译码电路驱动,用于当单片机具有多个外围器件时进行设备选择 5. 中断输出引脚 INT#: INT#输出的中断请求是低电平有效,可以连接到单片机的中断输入引脚或者普通 I/O 引脚,单片机可以使用中断方式或者查询方式获知中断请求 6. 地址输入引脚 A0: CH375 芯片占用两个地址位,当 A0 引脚为高电平时选择命令端口,可以写入新的命令,或者读出中断标志;当 A0 引脚为低电平时选择数据端口,可以读写数据
下表为并口 I/O 操作的真值表(表中 X 代表不关心此位,Z 代表 CH375 三态禁止)
串行接口
串行接口只能用于 USB 主机方式,CH375 芯片的 USB 设备方式不支持串口,串口信号线包括
1. 串行数据输入引脚 RXD: CH375 芯片的 RXD 和 TXD 可以分别连接到单片机的串行数据输出引脚和串行数据输入引脚 2. 串行数据输出引脚 TXD 3. 中断输出引脚 INT#: INT#输出的中断请求是低电平有效,用于通知单片机
通过串行接口,CH375 可以用最少的连线与单片机、DSP、MCU 进行较远距离的点对点连接
CH375 的串行数据格式是 1 个起始位、9 个数据位、1 个停止位,其中前 8 个数据位是一个字节数据,最后 1 个数据位是命令标志位。第 9 位为 0 时,前 8 位的数据被写入 CH375 芯片中,第 9 位为1 时,前 8 位被作为命令码写入 CH375 芯片中
CH375 的串行通讯波特率默认是 9600bps,单片机可以随时通过 SET_BAUDRATE 命令选择合适的通讯波特率
内部结构
CH375 芯片内部集成了
1. PLL 倍频器: PLL 倍频器用于将外部输入的 12MHz 时钟倍频到 48MHz 2. 主从 USB 接口 SIE: 作为 USB 接口 SIE 时钟。主从 USB 接口 SIE 是 USB 主机方式和 USB 设备方式的一体式 SIE,用于完成物理的 USB 数据接收和发送,自动处理位跟踪和同步、NRZI 编码和解码、位填充、并行数据与串行数据之间的转换、CRC数据校验、事务握手、出错重试、USB 总线状态检测等 3. 数据缓冲区: 数据缓冲区用于缓冲 USB 接口 SIE 收发的数据 4. 被动并行接口: 被动并行接口用于与外部单片机/DSP/MCU 交换数据 5. 异步串行接口: 异步串行接口用于代替被动并行接口与外部单片机/DSP/MCU 交换数据 6. 命令解释器(指令集): 命令解释器用于分析并执行外部单片机/DSP/MCU 提交的各种命令 7. 控制传输的协议处理器: 控制传输的协议处理器用于自动处理常用的控制传输的多个阶段,简化外部固件编程 8. 通用的固件程序: 通用的固件程序包含两组 1) 一组用于 USB 设备方式,自动处理 USB 默认端点 0 的各种标准事务等 2) 另一组用于 USB 主机方式,自动处理 Mass-Storage 海量存储设备的专用通讯协议
6. 单片机读写 U 盘文件(USB 存储设备的文件级接口)
一般情况下,单片机或嵌入式系统处理 USB 存储设备的文件系统需要实现上图左边的 4 个层次,右边是 USB 存储设备的内部结构层次。由于 CH375 不仅是一个通用的 USB-HOST 硬件接口芯片,还内置了相关的固件程序,包含了上图左边的 3 个层次(标为灰色部分),所以实际的单片机程序只需要处理 FAT 文件系统层,并且即使这一层也可以由 CH375 的 U 盘文件级子程序库实现
CH375 直接提供了数据块的读写接口,以 512 字节或者 2K 字节等的物理扇区为基本读写单位,从而将 USB 存储设备简化为一种外部数据存储器,单片机可以自由读写 USB 存储设备中的数据,也可以自由定义其数据结构
由于计算机将 USB 存储设备组织为文件系统,为了方便单片机通过 USB 移动存储设备与计算机之间交换数据,单片机也可以将 USB 存储设备组织为文件系统
CH375 以 C 语言子程序库提供了 USB 存储设备的文件级接口,这些应用层接口 API 包含了常用的文件级操作,可以移植并嵌入到各种常用的单片机程序中
CH375 的 U 盘文件级子程序库具有以下特性:支持常用的 FAT12、FAT16 和 FAT32 文件系统,磁盘容量可达 100GB 以上,支持多级子目录,支持 8.3 格式的大写字母和中文文件名,可以支持小写字
母或者长文件名,支持文件打开、新建、删除、读写以及搜索等
CH375 的文件级子程序库需要至少 600 字节的随机存储器 RAM 作为缓冲区。以普通的 MCS-51 单片机为例,文件系统的全部子程序有 4KB 到 8KB 代码,并且需要大约 80 字节的内部 RAM 和至少 512
字节的外部 RAM 作为缓冲区
文件级子程序库的所有 API 在调用后都有操作状态返回,但不一定有应答数据,主要子程序如下
1. 初始化 CH375 芯片: CH375Init 2. 查询 U 盘是否准备好: CH375DiskReady 3. 查询 U 盘容量: CH375DiskSize 4. 查询 U 盘信息(总容量及剩余容量): CH375DiskQuery 5. 打开文件: CH375FileOpen 6. 枚举或者搜索文件: CH375FileEnumer 7. 关闭文件: CH375FileClose 8. 新建文件: CH375FileCreate 9. 删除文件: CH375FileErase 10. 以扇区为单位从文件读数据: CH375FileReadX 11. 以扇区为单位向文件写数据: CH375FileWriteX 12. 以扇区为单位移动文件指针: CH375FileLocate 13. 查询文件属性(属性/日期/时间/长度): CH375FileQuery 14. 设置文件属性(属性/日期/时间/长度): CH375FileModify 15. 以字节为单位从文件读数据: CH375ByteRead 16. 以字节为单位向文件写数据: CH375ByteWrite 17. 以字节为单位移动文件指针: CH375ByteLocate
Relevant Link:
http://www.alldatasheet.com/view.jsp?Searchword=CH375B http://wenku.baidu.com/link?url=pCBsY72-BS4XthSAkNbPYcNK5_KCnJF4WYNrvppsBe5QVhBRuZkk5OgJ9qwytb-JXmkIC9VQXQ0LLW1qp0FB2clDZp87_WClRIP3Cm1-SJG http://pan.baidu.com/s/1i3IHH7f http://blog.sina.com.cn/s/blog_6d3279350100mdw6.html http://wenku.baidu.com/link?url=ASswFbsPUxB7ag9WofyKXVXcppSFEf4Q9YIGS5y1ZX5TylDfU2YKXUopQTEl2UcjOcogiAWiohL3o25y6rVdyAhva2R2F7ouyyOCoFHq2l3 file:///C:/Users/zhenghan.zh/Downloads/CH375%E4%B8%AD%E6%96%87%E6%89%8B%E5%86%8C.pdf
5. USB Host Shield for Arduino datasheet
0x1: Overview
The Arduino USB Host Shield allows you to connect a USB device to your Arduino board. The Arduino USB Host Shield is based on the MAX3421E (datasheet), which is a USB peripheral/host controller containing the digital logic and analog circuitry necessary to implement a full-speed USB peripheral or a full-/low-speed host compliant to USB specification rev 2.0. The shield is TinkerKit compatible, which means you can quickly create projects by plugging TinkerKit modules onto the board.
The following device classes are supported by the shield:
HID devices: keyboards, mice, joysticks, etc.Game controllers: Sony PS3, Nintendo Wii, Xbox360. USB to serial converters: FTDI, PL-2303, ACM, as well as certain cell phones and GPS receivers. ADK-capable Android phones and tables. Digital cameras: Canon EOS, Powershot, Nikon DSLRs and P&S, as well as generic PTP. Mass storage devices: USB sticks, memory card readers, external hard drives, etc. Bluetooth dongles.
Arduino communicates with the MAX3421E using the SPI bus (through the ICSP header).
1. This is on digital pins 10, 11, 12, and 13 on the Uno 2. pins 10, 50, 51, and 52 on the Mega. 3. On both boards 1) pin 10 is used to select the MAX3421E 2) Pins 7, 8 and 9 are used for GPX, INT and RES pins.
0x2: Summary
1. Operating Voltage: 5V 2. USB Controller: MAX3421E 3. Max Current: 500mA when Arduino is powered by a suitable power supply connected to the Arduino power jack 4. Max Current: 400mA when Arduino is powered by its USB port
The Arduino USB Host Shield can be used with the "USB Host Library for Arduino"
0x3: Power
The Arduino USB Host Shield has no power jack and is powered only when attached to an Arduino board.
0x4: Input and Output
This shield has a MALE USB port where you can attach your USB device.
The shield features several TinkerKit input/output and communication interfaces. Connecting TinkerKit modules can simplify the creation of a project or a prototype. The on-board connectors are
2 TinkerKit Inputs: IN2 and IN3 (in white), these connectors are routed to the Arduino A2 and A3 analog input pins. 2 TinkerKit Outputs: OUT5 and OUT6 (in orange), these connectors are routed to the Arduino PWM outputs on pins 5 and 6. 2 TinkerKit TWI: these connectors (4-pin in white) are routed on the Arduino TWI interface. Both connect to the same TWI interface to allow you to create a chain of TWI devices.
Relevant Link:
https://www.arduino.cc/en/uploads/Main/USBHostShield-Rev2.pdf https://www.arduino.cc/en/Main/ArduinoUSBHostShield https://www.elektormagazine.com/news/Arduino-host-shield http://hackaday.com/2009/08/21/arduino-usb-host-s/hield
6. USB Host Library Rev.2.0
0x1: How to use the library
1. Enable debugging
By default serial debugging is disabled. To turn it on simply change ENABLE_UHS_DEBUGGING to 1 in settings.h like so:
#define ENABLE_UHS_DEBUGGING 1
2. Boards
Currently the following boards are supported by the library:
1. All official Arduino AVR boards (Uno, Duemilanove, Mega, Mega 2560, Mega ADK, Leonardo etc.) 2. Arduino Due, Intel Galileo, Intel Galileo 2, and Intel Edison 3. Teensy (Teensy++ 1.0, Teensy 2.0, Teensy++ 2.0, Teensy 3.x, and Teensy LC) 4. Balanduino 5. Sanguino 6. Black Widdow 7. RedBearLab nRF51822 8. Digilent chipKIT 9. STM32F4
3. HID Libraries
HID devices are also supported by the library. However these require you to write your own driver. A few example are provided in the examples/HID directory. Including an example for the SteelSeries SRW-S1 Steering Wheel.
Relevant Link:
https://www.circuitsathome.com/category/mcu/usb http://www.hdl.co.jp/ftpdata/utl-001/AN3785.pdf http://microcontrollershop.com/product_info.php?products_id=4537 http://pub.ucpros.com/download/USBHostShield-v13.pdf?osCsid=olahq70ba39enad73t19dbi7p1 https://github.com/felis/USB_Host_Shield_2.0 https://www.maximintegrated.com/en/products/interface/controllers-expanders/MAX3421E.html
7. USB Keyboard keylogger实验
0x1: USB_desc
#include <usbhub.h> #include "pgmstrings.h" // Satisfy the IDE, which needs to see the include statment in the ino too. #ifdef dobogusinclude #include <spi4teensy3.h> #include <SPI.h> #endif USB Usb; //USBHub Hub1(&Usb); //USBHub Hub2(&Usb); //USBHub Hub3(&Usb); //USBHub Hub4(&Usb); //USBHub Hub5(&Usb); //USBHub Hub6(&Usb); //USBHub Hub7(&Usb); uint32_t next_time; void PrintAllAddresses(UsbDevice *pdev) { UsbDeviceAddress adr; adr.devAddress = pdev->address.devAddress; Serial.print("\r\nAddr:"); Serial.print(adr.devAddress, HEX); Serial.print("("); Serial.print(adr.bmHub, HEX); Serial.print("."); Serial.print(adr.bmParent, HEX); Serial.print("."); Serial.print(adr.bmAddress, HEX); Serial.println(")"); } void PrintAddress(uint8_t addr) { UsbDeviceAddress adr; adr.devAddress = addr; Serial.print("\r\nADDR:\t"); Serial.println(adr.devAddress, HEX); Serial.print("DEV:\t"); Serial.println(adr.bmAddress, HEX); Serial.print("PRNT:\t"); Serial.println(adr.bmParent, HEX); Serial.print("HUB:\t"); Serial.println(adr.bmHub, HEX); } void setup() { Serial.begin( 115200 ); #if !defined(__MIPSEL__) while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection #endif Serial.println("Start"); if (Usb.Init() == -1) Serial.println("OSC did not start."); delay( 200 ); next_time = millis() + 10000; } byte getdevdescr( byte addr, byte &num_conf ); void PrintDescriptors(uint8_t addr) { uint8_t rcode = 0; byte num_conf = 0; rcode = getdevdescr( (byte)addr, num_conf ); if ( rcode ) { printProgStr(Gen_Error_str); print_hex( rcode, 8 ); } Serial.print("\r\n"); for (int i = 0; i < num_conf; i++) { rcode = getconfdescr( addr, i ); // get configuration descriptor if ( rcode ) { printProgStr(Gen_Error_str); print_hex(rcode, 8); } Serial.println("\r\n"); } } void PrintAllDescriptors(UsbDevice *pdev) { Serial.println("\r\n"); print_hex(pdev->address.devAddress, 8); Serial.println("\r\n--"); PrintDescriptors( pdev->address.devAddress ); } void loop() { Usb.Task(); if ( Usb.getUsbTaskState() == USB_STATE_RUNNING ) { //if (millis() >= next_time) { Usb.ForEachUsbDevice(&PrintAllDescriptors); Usb.ForEachUsbDevice(&PrintAllAddresses); while ( 1 ); //stop } } } byte getdevdescr( byte addr, byte &num_conf ) { USB_DEVICE_DESCRIPTOR buf; byte rcode; rcode = Usb.getDevDescr( addr, 0, 0x12, ( uint8_t *)&buf ); if ( rcode ) { return ( rcode ); } printProgStr(Dev_Header_str); printProgStr(Dev_Length_str); print_hex( buf.bLength, 8 ); printProgStr(Dev_Type_str); print_hex( buf.bDescriptorType, 8 ); printProgStr(Dev_Version_str); print_hex( buf.bcdUSB, 16 ); printProgStr(Dev_Class_str); print_hex( buf.bDeviceClass, 8 ); printProgStr(Dev_Subclass_str); print_hex( buf.bDeviceSubClass, 8 ); printProgStr(Dev_Protocol_str); print_hex( buf.bDeviceProtocol, 8 ); printProgStr(Dev_Pktsize_str); print_hex( buf.bMaxPacketSize0, 8 ); printProgStr(Dev_Vendor_str); print_hex( buf.idVendor, 16 ); printProgStr(Dev_Product_str); print_hex( buf.idProduct, 16 ); printProgStr(Dev_Revision_str); print_hex( buf.bcdDevice, 16 ); printProgStr(Dev_Mfg_str); print_hex( buf.iManufacturer, 8 ); printProgStr(Dev_Prod_str); print_hex( buf.iProduct, 8 ); printProgStr(Dev_Serial_str); print_hex( buf.iSerialNumber, 8 ); printProgStr(Dev_Nconf_str); print_hex( buf.bNumConfigurations, 8 ); num_conf = buf.bNumConfigurations; return ( 0 ); } void printhubdescr(uint8_t *descrptr, uint8_t addr) { HubDescriptor *pHub = (HubDescriptor*) descrptr; uint8_t len = *((uint8_t*)descrptr); printProgStr(PSTR("\r\n\r\nHub Descriptor:\r\n")); printProgStr(PSTR("bDescLength:\t\t")); Serial.println(pHub->bDescLength, HEX); printProgStr(PSTR("bDescriptorType:\t")); Serial.println(pHub->bDescriptorType, HEX); printProgStr(PSTR("bNbrPorts:\t\t")); Serial.println(pHub->bNbrPorts, HEX); printProgStr(PSTR("LogPwrSwitchMode:\t")); Serial.println(pHub->LogPwrSwitchMode, BIN); printProgStr(PSTR("CompoundDevice:\t\t")); Serial.println(pHub->CompoundDevice, BIN); printProgStr(PSTR("OverCurrentProtectMode:\t")); Serial.println(pHub->OverCurrentProtectMode, BIN); printProgStr(PSTR("TTThinkTime:\t\t")); Serial.println(pHub->TTThinkTime, BIN); printProgStr(PSTR("PortIndicatorsSupported:")); Serial.println(pHub->PortIndicatorsSupported, BIN); printProgStr(PSTR("Reserved:\t\t")); Serial.println(pHub->Reserved, HEX); printProgStr(PSTR("bPwrOn2PwrGood:\t\t")); Serial.println(pHub->bPwrOn2PwrGood, HEX); printProgStr(PSTR("bHubContrCurrent:\t")); Serial.println(pHub->bHubContrCurrent, HEX); for (uint8_t i = 7; i < len; i++) print_hex(descrptr[i], 8); //for (uint8_t i=1; i<=pHub->bNbrPorts; i++) // PrintHubPortStatus(&Usb, addr, i, 1); } byte getconfdescr( byte addr, byte conf ) { uint8_t buf[ BUFSIZE ]; uint8_t* buf_ptr = buf; byte rcode; byte descr_length; byte descr_type; unsigned int total_length; rcode = Usb.getConfDescr( addr, 0, 4, conf, buf ); //get total length LOBYTE( total_length ) = buf[ 2 ]; HIBYTE( total_length ) = buf[ 3 ]; if ( total_length > 256 ) { //check if total length is larger than buffer printProgStr(Conf_Trunc_str); total_length = 256; } rcode = Usb.getConfDescr( addr, 0, total_length, conf, buf ); //get the whole descriptor while ( buf_ptr < buf + total_length ) { //parsing descriptors descr_length = *( buf_ptr ); descr_type = *( buf_ptr + 1 ); switch ( descr_type ) { case ( USB_DESCRIPTOR_CONFIGURATION ): printconfdescr( buf_ptr ); break; case ( USB_DESCRIPTOR_INTERFACE ): printintfdescr( buf_ptr ); break; case ( USB_DESCRIPTOR_ENDPOINT ): printepdescr( buf_ptr ); break; case 0x29: printhubdescr( buf_ptr, addr ); break; default: printunkdescr( buf_ptr ); break; }//switch( descr_type buf_ptr = ( buf_ptr + descr_length ); //advance buffer pointer }//while( buf_ptr <=... return ( rcode ); } /* prints hex numbers with leading zeroes */ // copyright, Peter H Anderson, Baltimore, MD, Nov, ‘07 // source: http://www.phanderson.com/arduino/arduino_display.html void print_hex(int v, int num_places) { int mask = 0, n, num_nibbles, digit; for (n = 1; n <= num_places; n++) { mask = (mask << 1) | 0x0001; } v = v & mask; // truncate v to specified number of places num_nibbles = num_places / 4; if ((num_places % 4) != 0) { ++num_nibbles; } do { digit = ((v >> (num_nibbles - 1) * 4)) & 0x0f; Serial.print(digit, HEX); } while (--num_nibbles); } /* function to print configuration descriptor */ void printconfdescr( uint8_t* descr_ptr ) { USB_CONFIGURATION_DESCRIPTOR* conf_ptr = ( USB_CONFIGURATION_DESCRIPTOR* )descr_ptr; printProgStr(Conf_Header_str); printProgStr(Conf_Totlen_str); print_hex( conf_ptr->wTotalLength, 16 ); printProgStr(Conf_Nint_str); print_hex( conf_ptr->bNumInterfaces, 8 ); printProgStr(Conf_Value_str); print_hex( conf_ptr->bConfigurationValue, 8 ); printProgStr(Conf_String_str); print_hex( conf_ptr->iConfiguration, 8 ); printProgStr(Conf_Attr_str); print_hex( conf_ptr->bmAttributes, 8 ); printProgStr(Conf_Pwr_str); print_hex( conf_ptr->bMaxPower, 8 ); return; } /* function to print interface descriptor */ void printintfdescr( uint8_t* descr_ptr ) { USB_INTERFACE_DESCRIPTOR* intf_ptr = ( USB_INTERFACE_DESCRIPTOR* )descr_ptr; printProgStr(Int_Header_str); printProgStr(Int_Number_str); print_hex( intf_ptr->bInterfaceNumber, 8 ); printProgStr(Int_Alt_str); print_hex( intf_ptr->bAlternateSetting, 8 ); printProgStr(Int_Endpoints_str); print_hex( intf_ptr->bNumEndpoints, 8 ); printProgStr(Int_Class_str); print_hex( intf_ptr->bInterfaceClass, 8 ); printProgStr(Int_Subclass_str); print_hex( intf_ptr->bInterfaceSubClass, 8 ); printProgStr(Int_Protocol_str); print_hex( intf_ptr->bInterfaceProtocol, 8 ); printProgStr(Int_String_str); print_hex( intf_ptr->iInterface, 8 ); return; } /* function to print endpoint descriptor */ void printepdescr( uint8_t* descr_ptr ) { USB_ENDPOINT_DESCRIPTOR* ep_ptr = ( USB_ENDPOINT_DESCRIPTOR* )descr_ptr; printProgStr(End_Header_str); printProgStr(End_Address_str); print_hex( ep_ptr->bEndpointAddress, 8 ); printProgStr(End_Attr_str); print_hex( ep_ptr->bmAttributes, 8 ); printProgStr(End_Pktsize_str); print_hex( ep_ptr->wMaxPacketSize, 16 ); printProgStr(End_Interval_str); print_hex( ep_ptr->bInterval, 8 ); return; } /*function to print unknown descriptor */ void printunkdescr( uint8_t* descr_ptr ) { byte length = *descr_ptr; byte i; printProgStr(Unk_Header_str); printProgStr(Unk_Length_str); print_hex( *descr_ptr, 8 ); printProgStr(Unk_Type_str); print_hex( *(descr_ptr + 1 ), 8 ); printProgStr(Unk_Contents_str); descr_ptr += 2; for ( i = 0; i < length; i++ ) { print_hex( *descr_ptr, 8 ); descr_ptr++; } } /* Print a string from Program Memory directly to save RAM */ void printProgStr(const char* str) { char c; if (!str) return; while ((c = pgm_read_byte(str++))) Serial.print(c); }
将arduino meets USB Host Shield和arduino UNO的GPIO反向扣起来
Start 01 -- Device descriptor: Descriptor Length: 12 Descriptor type: 01 USB version: 0110 Device class: 00 Device Subclass: 00 Device Protocol: 00 Max.packet size: 08 Vendor ID: 0000 Product ID: 0538 Revision ID: 0100 Mfg.string index: 00 Prod.string index: 01 Serial number index: 00 Number of conf.: 01 Configuration descriptor: Total length: 0022 Num.intf: 01 Conf.value: 01 Conf.string: 00 Attr.: A0 Max.pwr: 32 Interface descriptor: Intf.number: 00 Alt.: 00 Endpoints: 01 Intf. Class: 03 Intf. Subclass: 01 Intf. Protocol: 02 Intf.string: 00 Unknown descriptor: Length: 09 Type: 21 Contents: 110100012242000705 Endpoint descriptor: Endpoint address: 81 Attr.: 03 Max.pkt size: 0006 Polling interval: 0A Addr:1(0.0.1)
0x2: Mouse buttonlogger
#include "hidboot.h" #include "usbhub.h" // Satisfy the IDE, which needs to see the include statment in the ino too. #ifdef dobogusinclude #include <spi4teensy3.h> #include <SPI.h> #endif class MouseRptParser : public MouseReportParser { protected: void OnMouseMove (MOUSEINFO *mi); void OnLeftButtonUp (MOUSEINFO *mi); void OnLeftButtonDown (MOUSEINFO *mi); void OnRightButtonUp (MOUSEINFO *mi); void OnRightButtonDown (MOUSEINFO *mi); void OnMiddleButtonUp (MOUSEINFO *mi); void OnMiddleButtonDown (MOUSEINFO *mi); }; void MouseRptParser::OnMouseMove(MOUSEINFO *mi) { Serial.print("dx="); Serial.print(mi->dX, DEC); Serial.print(" dy="); Serial.println(mi->dY, DEC); }; void MouseRptParser::OnLeftButtonUp (MOUSEINFO *mi) { Serial.println("L Butt Up"); }; void MouseRptParser::OnLeftButtonDown (MOUSEINFO *mi) { Serial.println("L Butt Dn"); }; void MouseRptParser::OnRightButtonUp (MOUSEINFO *mi) { Serial.println("R Butt Up"); }; void MouseRptParser::OnRightButtonDown (MOUSEINFO *mi) { Serial.println("R Butt Dn"); }; void MouseRptParser::OnMiddleButtonUp (MOUSEINFO *mi) { Serial.println("M Butt Up"); }; void MouseRptParser::OnMiddleButtonDown (MOUSEINFO *mi) { Serial.println("M Butt Dn"); }; USB Usb; USBHub Hub(&Usb); HIDBoot<USB_HID_PROTOCOL_MOUSE> HidMouse(&Usb); uint32_t next_time; MouseRptParser Prs; void setup() { Serial.begin( 115200 ); #if !defined(__MIPSEL__) while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection #endif Serial.println("Start"); if (Usb.Init() == -1) Serial.println("OSC did not start."); delay( 200 ); next_time = millis() + 5000; HidMouse.SetReportParser(0, &Prs); } void loop() { Usb.Task(); }
0x3: USB Kerborad keylogger
#include "hidboot.h" #include "usbhub.h" // Satisfy the IDE, which needs to see the include statment in the ino too. #ifdef dobogusinclude #include <spi4teensy3.h> #include <SPI.h> #endif class KbdRptParser : public KeyboardReportParser { void PrintKey(uint8_t mod, uint8_t key); protected: void OnControlKeysChanged(uint8_t before, uint8_t after); void OnKeyDown (uint8_t mod, uint8_t key); void OnKeyUp (uint8_t mod, uint8_t key); void OnKeyPressed(uint8_t key); }; void KbdRptParser::PrintKey(uint8_t m, uint8_t key) { MODIFIERKEYS mod; *((uint8_t*)&mod) = m; Serial.print((mod.bmLeftCtrl == 1) ? "C" : " "); Serial.print((mod.bmLeftShift == 1) ? "S" : " "); Serial.print((mod.bmLeftAlt == 1) ? "A" : " "); Serial.print((mod.bmLeftGUI == 1) ? "G" : " "); Serial.print(" >"); PrintHex<uint8_t>(key, 0x80); Serial.print("< "); Serial.print((mod.bmRightCtrl == 1) ? "C" : " "); Serial.print((mod.bmRightShift == 1) ? "S" : " "); Serial.print((mod.bmRightAlt == 1) ? "A" : " "); Serial.println((mod.bmRightGUI == 1) ? "G" : " "); }; void KbdRptParser::OnKeyDown(uint8_t mod, uint8_t key) { Serial.print("DN "); PrintKey(mod, key); uint8_t c = OemToAscii(mod, key); if (c) OnKeyPressed(c); } void KbdRptParser::OnControlKeysChanged(uint8_t before, uint8_t after) { MODIFIERKEYS beforeMod; *((uint8_t*)&beforeMod) = before; MODIFIERKEYS afterMod; *((uint8_t*)&afterMod) = after; if (beforeMod.bmLeftCtrl != afterMod.bmLeftCtrl) { Serial.println("LeftCtrl changed"); } if (beforeMod.bmLeftShift != afterMod.bmLeftShift) { Serial.println("LeftShift changed"); } if (beforeMod.bmLeftAlt != afterMod.bmLeftAlt) { Serial.println("LeftAlt changed"); } if (beforeMod.bmLeftGUI != afterMod.bmLeftGUI) { Serial.println("LeftGUI changed"); } if (beforeMod.bmRightCtrl != afterMod.bmRightCtrl) { Serial.println("RightCtrl changed"); } if (beforeMod.bmRightShift != afterMod.bmRightShift) { Serial.println("RightShift changed"); } if (beforeMod.bmRightAlt != afterMod.bmRightAlt) { Serial.println("RightAlt changed"); } if (beforeMod.bmRightGUI != afterMod.bmRightGUI) { Serial.println("RightGUI changed"); } } void KbdRptParser::OnKeyUp(uint8_t mod, uint8_t key) { Serial.print("UP "); PrintKey(mod, key); } void KbdRptParser::OnKeyPressed(uint8_t key) { Serial.print("ASCII: "); Serial.println((char)key); }; USB Usb; //USBHub Hub(&Usb); HIDBoot<USB_HID_PROTOCOL_KEYBOARD> HidKeyboard(&Usb); uint32_t next_time; KbdRptParser Prs; void setup() { Serial.begin( 115200 ); #if !defined(__MIPSEL__) while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection #endif Serial.println("Start"); if (Usb.Init() == -1) Serial.println("OSC did not start."); delay( 200 ); next_time = millis() + 5000; HidKeyboard.SetReportParser(0, &Prs); } void loop() { Usb.Task(); }
Relevant Link:
http://files.cnblogs.com/files/LittleHann/USBHIDBootMouse.rar https://www.circuitsathome.com/mcu/arduino-usb-host-part-4-peripherals https://www.circuitsathome.com/mcu/arduino-usb-host-part-3-descriptors https://github.com/felis/USB_Host_Shield_2.0/blob/master/examples/HID/USBHIDBootKbd/USBHIDBootKbd.ino https://github.com/MD4N1/Arduino-USB-Barcode-Scanner-Reader/tree/master/Android-ArduinoTerminal/src https://www.circuitsathome.com/mcu/a-library-to-interface-arduino-with-xkeys-usb-keyboards https://www.circuitsathome.com/mcu/usb/visualizing-hid-device-reports-and-report-descriptors https://www.circuitsathome.com/mcu/adding-a-display-to-a-digital-scale-using-arduino-and-usb-host-shield
Copyright (c) 2016 LittleHann All rights reserved