Processing导入模型

最近玩玩 Processing , 尝试导入一个 DXF 的模型,OBJ 格式的. 使用 Processing 加载之。具体做法是这样的:

1. 下载 OBJLoader 库 【参考1】
2. 把这个库放在 Processing -> File -> Preferences -> Sketchbook location 指定的位置中,例如:

prc1

特别注意,默认目录中可能有空格或者中文,那么请你指定一个不含有中文或者空格的目录,像我一样,放在 D:\ 下面最好。就是下面这个样子

prc2

prc3

这个库可以直接去【参考1】给出的网站上下载,也可以用 Tools -> Add Tool 调出 Tool Manager,他会自动下载列表,你选择 ObjLoader 进行安装(可能是 GFW 的缘故,这个功能不稳定)

toolm

3.安装好之后,可以编译 ObjLoader 中 Examples 下面的例子,确保安装正确 (如果出现错误,并且错误不是 ObjLoader 导致的,那么请换另外的例子,有些例子用到了第三方的库)

4.最后编写自己的程序。从 Sketch -> Add Files 中导入 OBJ 模型,再输入下面的代码

import saito.objloader.*;

OBJModel model;

void setup() {
  size(600,600,P3D);
  model= new OBJModel(this);
  model.load("teapot.obj");
  model.setDrawMode(POLYGON);
  noStroke();
}  

void draw() {
  background(0);
  lights();
  pushMatrix();
  translate(width/2,height,-width);
  rotateY(map(mouseX,0,width,-PI,PI));
  rotateX(PI/4);
  scale(6.0);
  model.draw();
  popMatrix();
}  

 

最终运行结果如下

teapot

完整的代码下载

sketch_150823b

参考:

1.https://processing.org/reference/libraries/ 上提到的 OBJLoader by Tatsuya Saito and Matt Ditton .OBJ 3D model file loader

Step to UEFI (59) —– BMP 放在EFI文件中(上)

前面介绍了如何 Load 一幅 BMP 图片然后显示出来,然后就有一个问题:如何把这个图片也放在 EFI 文件中? 毕竟放在一起会显得更加专业。在【参考1】上,也有朋友问了同样的问题,博主的建议是“写个脚本直接把图片变成像素数组,直接弄,这样很简单”。下面就来实验一下。

首先,需要找一个将图片转为 C Header的工具。我找到的是 Bin2C 这个工具【参考2】. 用下面的命令即可完成转换,生成 show.h

bin2c -o show.h Untitled.bmp

转换之后 show.h 大概就是下面这个样子

showbmp2

然后,修改之前显示 BMP 的那个程序,用 include 来引用用到的 BMP 数据, 去掉 Load 文件的过程,直接使用内存地址。

#include  <Uefi.h>
#include  <Library/UefiLib.h>
#include  <Library/ShellCEntryLib.h>

#include  <stdio.h>
#include  <stdlib.h>
#include  <wchar.h>
#include  <time.h>
#include <Protocol/EfiShell.h>
#include <Library/ShellLib.h>

#include <Protocol/SimpleFileSystem.h>
#include <Protocol/BlockIo.h>
#include <Library/DevicePathLib.h>
#include <Library/HandleParsingLib.h>
#include <Library/SortLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/BaseMemoryLib.h>

#include <Protocol/LoadedImage.h>

#define MAX_FILE_SIZE (1024*1024*1024)

extern EFI_BOOT_SERVICES         *gBS;
extern EFI_SYSTEM_TABLE			 *gST;
extern EFI_RUNTIME_SERVICES 	 *gRT;

extern EFI_SHELL_ENVIRONMENT2    *mEfiShellEnvironment2;
extern EFI_HANDLE				 gImageHandle;

static EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
static EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput = NULL;

#pragma pack(1)

//Copied from  C\MdePkg\Include\Protocol\UgaDraw.h
typedef struct {
  UINT8 Blue;
  UINT8 Green;
  UINT8 Red;
  UINT8 Reserved;
} EFI_UGA_PIXEL;

/* This should be compatible with EFI_UGA_PIXEL */
typedef struct {
    UINT8 b, g, r, a;
} EG_PIXEL;

typedef struct {
    CHAR8         CharB;
    CHAR8         CharM;
    UINT32        Size;
    UINT16        Reserved[2];
    UINT32        ImageOffset;
    UINT32        HeaderSize;
    UINT32        PixelWidth;
    UINT32        PixelHeight;
    UINT16        Planes;       // Must be 1
    UINT16        BitPerPixel;  // 1, 4, 8, or 24
    UINT32        CompressionType;
    UINT32        ImageSize;    // Compressed image size in bytes
    UINT32        XPixelsPerMeter;
    UINT32        YPixelsPerMeter;
    UINT32        NumberOfColors;
    UINT32        ImportantColors;
} BMP_IMAGE_HEADER;

typedef struct {
    UINTN       Width;
    UINTN       Height;
    BOOLEAN     HasAlpha;
    EG_PIXEL    *PixelData;
} EG_IMAGE;
#pragma pack()

//const unsigned char Untitled_bmp[289654] 
#include "show.h"

VOID egFreeImage(IN EG_IMAGE *Image)
{
    if (Image != NULL) {
        if (Image->PixelData != NULL)
            FreePool(Image->PixelData);
        FreePool(Image);
    }
}

//
// Basic image handling
//

EG_IMAGE * egCreateImage(IN UINTN Width, IN UINTN Height, IN BOOLEAN HasAlpha)
{
    EG_IMAGE        *NewImage;
    
    NewImage = (EG_IMAGE *) AllocatePool(sizeof(EG_IMAGE));
    if (NewImage == NULL)
        return NULL;
    NewImage->PixelData = (EG_PIXEL *) AllocatePool(
								Width * Height * sizeof(EG_PIXEL));
    if (NewImage->PixelData == NULL) {
        FreePool(NewImage);
        return NULL;
    }

    NewImage->Width = Width;
    NewImage->Height = Height;
    NewImage->HasAlpha = HasAlpha;
    return NewImage;
}

//
// Load BMP image
//

EG_IMAGE * egDecodeBMP
	(
		IN UINT8 *FileData, 
		IN UINTN FileDataLength, 
		IN BOOLEAN WantAlpha)
{
    EG_IMAGE            *NewImage;
    BMP_IMAGE_HEADER    *BmpHeader;
    EFI_UGA_PIXEL       *BmpColorMap;
    UINTN               x, y;
    UINT8               *ImagePtr;
    UINT8               *ImagePtrBase;
    UINTN               ImageLineOffset;
    UINT8               ImageValue=0, AlphaValue;
    EG_PIXEL            *PixelPtr;
    UINTN               Index, BitIndex;

    // read and check header
    if (FileDataLength < sizeof(BMP_IMAGE_HEADER) || FileData == NULL)
        return NULL;

    BmpHeader = (BMP_IMAGE_HEADER *) FileData;
    if (BmpHeader->CharB != 'B' || BmpHeader->CharM != 'M')
        return NULL;

    if (BmpHeader->CompressionType != 0)
        return NULL;

    if (BmpHeader->BitPerPixel != 1 && BmpHeader->BitPerPixel != 4 &&
        BmpHeader->BitPerPixel != 8 && BmpHeader->BitPerPixel != 24)
        return NULL;

    // calculate parameters
    ImageLineOffset = BmpHeader->PixelWidth;
    if (BmpHeader->BitPerPixel == 24)
        ImageLineOffset *= 3;
    else if (BmpHeader->BitPerPixel == 1)
        ImageLineOffset = (ImageLineOffset + 7) >> 3;
    else if (BmpHeader->BitPerPixel == 4)
        ImageLineOffset = (ImageLineOffset + 1) >> 1;
    if ((ImageLineOffset % 4) != 0)
        ImageLineOffset = ImageLineOffset + (4 - (ImageLineOffset % 4));
    // check bounds
    if (BmpHeader->ImageOffset + 
			ImageLineOffset * BmpHeader->PixelHeight > FileDataLength)
        return NULL;
    
    // allocate image structure and buffer
    NewImage = egCreateImage(BmpHeader->PixelWidth, 
								BmpHeader->PixelHeight, WantAlpha);
    if (NewImage == NULL)
        return NULL;
    AlphaValue = WantAlpha ? 255 : 0;
    
    // convert image
    BmpColorMap = (EFI_UGA_PIXEL *)(FileData + sizeof(BMP_IMAGE_HEADER));
    ImagePtrBase = FileData + BmpHeader->ImageOffset;
    for (y = 0; y < BmpHeader->PixelHeight; y++) {
        ImagePtr = ImagePtrBase;
        ImagePtrBase += ImageLineOffset;
        PixelPtr = NewImage->PixelData + 
				(BmpHeader->PixelHeight - 1 - y) * BmpHeader->PixelWidth;
        
        switch (BmpHeader->BitPerPixel) {
            
            case 1:
                for (x = 0; x < BmpHeader->PixelWidth; x++) {
                    BitIndex = x & 0x07;
                    if (BitIndex == 0)
                        ImageValue = *ImagePtr++;
                    
                    Index = (ImageValue >> (7 - BitIndex)) & 0x01;
                    PixelPtr->b = BmpColorMap[Index].Blue;
                    PixelPtr->g = BmpColorMap[Index].Green;
                    PixelPtr->r = BmpColorMap[Index].Red;
                    PixelPtr->a = AlphaValue;
                    PixelPtr++;
                }
                break;
            
            case 4:
                for (x = 0; x <= BmpHeader->PixelWidth - 2; x += 2) {
                    ImageValue = *ImagePtr++;
                    
                    Index = ImageValue >> 4;
                    PixelPtr->b = BmpColorMap[Index].Blue;
                    PixelPtr->g = BmpColorMap[Index].Green;
                    PixelPtr->r = BmpColorMap[Index].Red;
                    PixelPtr->a = AlphaValue;
                    PixelPtr++;
                    
                    Index = ImageValue & 0x0f;
                    PixelPtr->b = BmpColorMap[Index].Blue;
                    PixelPtr->g = BmpColorMap[Index].Green;
                    PixelPtr->r = BmpColorMap[Index].Red;
                    PixelPtr->a = AlphaValue;
                    PixelPtr++;
                }
                if (x < BmpHeader->PixelWidth) {
                    ImageValue = *ImagePtr++;
                    
                    Index = ImageValue >> 4;
                    PixelPtr->b = BmpColorMap[Index].Blue;
                    PixelPtr->g = BmpColorMap[Index].Green;
                    PixelPtr->r = BmpColorMap[Index].Red;
                    PixelPtr->a = AlphaValue;
                    PixelPtr++;
                }
                break;
            
            case 8:
                for (x = 0; x < BmpHeader->PixelWidth; x++) {
                    Index = *ImagePtr++;
                    PixelPtr->b = BmpColorMap[Index].Blue;
                    PixelPtr->g = BmpColorMap[Index].Green;
                    PixelPtr->r = BmpColorMap[Index].Red;
                    PixelPtr->a = AlphaValue;
                    PixelPtr++;
                }
                break;
            
            case 24:
                for (x = 0; x < BmpHeader->PixelWidth; x++) {
                    PixelPtr->b = *ImagePtr++;
                    PixelPtr->g = *ImagePtr++;
                    PixelPtr->r = *ImagePtr++;
                    PixelPtr->a = AlphaValue;
                    PixelPtr++;
                }
                break;
            
        }
    }
    
    return NewImage;
}


int
EFIAPI
main (                                         
  IN int Argc,
  IN char **Argv
  )
{
    EFI_STATUS    Status;
    EG_IMAGE        *Image;	

	
    Status = gBS->LocateProtocol(&GraphicsOutputProtocolGuid, 
						NULL, (VOID **) &GraphicsOutput);
    if (EFI_ERROR(Status)) {
        GraphicsOutput = NULL;
		Print(L"Loading Graphics_Output_Protocol error!\n");
		return EFI_SUCCESS;
	}	
	
    Image=egDecodeBMP((UINT8 *)&Untitled_bmp, Untitled_bmp_size, FALSE);

    Print(L"Image height [%d]:width[%d]",
					Image->Height,
					Image->Width);

	GraphicsOutput->Blt(
				GraphicsOutput, 
				(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) Image->PixelData,
				EfiBltBufferToVideo,
                0, 
				0 , 
				0x100, 
				0x20, 
				Image->Width, 
				Image->Height, 0); 					
	
  return EFI_SUCCESS;
  
}

 

运行结果:

showbmp2a

Bin2C 下载(内含原始图片)

bin2c

源代码下载

GFXTest7

参考:

1.http://www.cppblog.com/djxzh/archive/2015/02/08/209766.html 补充《UEFI原理与编程》中关于Edk2的调试

2. http://sourceforge.net/projects/bin2c/

Arduino 打造一个“鼠标尺”

鼠标是目前玩电脑的必不可少的配备了。我最早接触的是机械鼠标,中间有一个滚球,带动内部的机械,配合光学器件将旋转信号转化为电子信号,通过串口或者PS2接口传输到电脑上。
image001
【参考1】

这样的鼠标缺点是定位不是很准确,并且容易脏,下面的球滚动一段就会蹭上泥垢,看起来比较恶心。
再后来出现了光电鼠标,那时候的光电鼠标必须在专用的鼠标垫上才能用,那种鼠标垫上面有网格状的东西,只有在它上面,鼠标发出光线才能正确的反馈,换句话说没有特定的鼠标垫根本无法使用。当然,那时候用电脑还是一项非常严重的事情,上机需要按照“团结紧张严肃活泼”的方针,先洗手再换鞋套之类的。
再再后来就出现了目前我们最常见到的光电鼠标,各种材质都可以使用,非常方便。原理上也发生了巨大的变化,简单的说就是现在的鼠标下面有一个类似照相机一样的东西不断给接触面拍照,移动会导致每次画面的变化,然后DSP根据画面不同计算出移动的距离再反馈给PC当前的移动距离信息。
当然,未来还可能出现更新原理的鼠标,比如:象象利用加速度传感器做的 “体感空中鼠”,等等。

我始终有一个想法:是否可以用鼠标来做一个电子尺,因为鼠标移动特定距离之后的输出也是固定的,意思是:鼠标移动10cm,输出的点位应该是固定的,比如:2000之类(DPI是定值)。反过来,如果我们知道鼠标移动了多少点,也就能确定鼠标移动了多少距离。最近接触了Arduino USB Shield ,于是打造一个鼠标尺。

材料准备
Arduino Uno
Arduino USB Shield
USB鼠标
USB充电宝
1602 LCD
导线 4根

实现原理

使用 USB Shield 来做USB HOST,在初始化的时候将鼠标切换到Boot Protocol,切换的好处是避免针对每种鼠标特别分析Descriptor,可以直接使用输出的数据(虽然是都要遵循HID协议,还真有不支持Boot Protocol的,这种情况需要单独处理了)。每次鼠标有动作,都会用Interrupt 方式通知到HOST来处理。我们根据输出的位移(相对位移),可以得知移动了多少。

#include “max3421e.h”
#include “usb.h”
#include “LiquidCrystal_I2C.h”
#include

#define DEVADDR 1
#define CONFVALUE 1
#define EP_MAXPKTSIZE 5
EP_RECORD ep_record[ 2 ]; //endpoint record structure for the mouse

void setup();
void loop();

MAX3421E Max;
USB Usb;

LiquidCrystal_I2C lcd(0x27,16,2);

double distance =0;

void setup()
{
lcd.init(); //初始化LCD
lcd.backlight(); //打开背光

Serial.begin( 115200 );
Serial.println(“Start”);
Max.powerOn();

lcd.setCursor(0,0);
lcd.print(” Distance “);

delay( 200 );
}

void loop()
{
byte rcode;
Max.Task();
Usb.Task();
if( Usb.getUsbTaskState() == USB_STATE_CONFIGURING ) {
mouse1_init();
}//if( Usb.getUsbTaskState() == USB_STATE_CONFIGURING…
if( Usb.getUsbTaskState() == USB_STATE_RUNNING ) { //状态机运行
rcode = mouse1_poll();
if( rcode ) {
Serial.print(“Mouse Poll Error: “);
Serial.println( rcode, HEX );
}//if( rcode…
}//if( Usb.getUsbTaskState() == USB_STATE_RUNNING…

}
/* 鼠标初始化 */
void mouse1_init( void )
{
byte rcode = 0; //return code
byte tmpdata;
byte* byte_ptr = &tmpdata;
/**/
ep_record[ 0 ] = *( Usb.getDevTableEntry( 0,0 )); //copy endpoint 0 parameters
ep_record[ 1 ].MaxPktSize = EP_MAXPKTSIZE;
ep_record[ 1 ].sndToggle = bmSNDTOG0;
ep_record[ 1 ].rcvToggle = bmRCVTOG0;
Usb.setDevTableEntry( 1, ep_record );
/* Configure device */
rcode = Usb.setConf( DEVADDR, 0, CONFVALUE );
if( rcode ) {
Serial.print(“Error configuring mouse. Return code : “);
Serial.println( rcode, HEX );
while(1); //stop
}//if( rcode…
rcode = Usb.getIdle( DEVADDR, 0, 0, 0, (char *)byte_ptr );
if( rcode ) {
Serial.print(“Get Idle error. Return code : “);
Serial.println( rcode, HEX );
while(1); //stop
}
Serial.print(“Idle Rate: “);
Serial.print(( tmpdata * 4 ), DEC ); //rate is returned in multiples of 4ms
Serial.println(” ms”);
tmpdata = 0;
rcode = Usb.setIdle( DEVADDR, 0, 0, 0, tmpdata ); //切换为Boot Protocol
if( rcode ) {
Serial.print(“Set Idle error. Return code : “);
Serial.println( rcode, HEX );
while(1); //stop
}
Usb.setUsbTaskState( USB_STATE_RUNNING );
return;
}
/* Poll mouse via interrupt endpoint and print result */
/* assumes EP1 as interrupt endpoint */
byte mouse1_poll( void )
{
byte rcode,i;
byte x;
byte y;
char res[12];

char buf[ 4 ] = { 0 };
/* poll mouse */
rcode = Usb.inTransfer( DEVADDR, 1, 4, buf, 1 ); //
if( rcode ) { //error
if( rcode == 0x04 ) { //NAK
rcode = 0;
}
return( rcode );
}
/* print buffer */
if( buf[ 0 ] & 0x01 ) {
distance=0;
}

//目前已经切换到 Boot Protocol, 输出的 Byte 2 3 分别是 X 和 Y
x=abs(buf[1]);
y=abs(buf[2]);

Serial.print(“X-axis: “);
Serial.print( buf[1], DEC); Serial.print(” “); Serial.println( x, DEC);
Serial.print(“Y-axis: “);
Serial.print( buf[2], DEC); Serial.print(” “); Serial.println(y , DEC);

distance=distance+sqrt(x*x+y*y); //计算移动距离
String dataString = “”;
//Double转String
dataString = dtostrf(distance, 5, 4, res);

Serial.println(dataString);

lcd.setCursor(0,1);
lcd.print(” “);
lcd.print(dataString);
lcd.print(” “);

return( rcode );
}
制作过程也就是连接过程,Shield直接插,1602 LCD四根线,GND VCC SDA SCL 接对了就好。

image003

工作时就是这个样子

image005

实际上显示的是移动了多少点,具体还要进行换算为实际的距离,每个鼠标的DPI不同,相同的点位表示的实际距离也不相同。上述程序我没有做这个转换,如果有需要在编程的时候拉一个已知长度的线段折算一下即可知。

image007

工作视频

http://www.tudou.com/programs/view/hcsKy9hKCVg/?resourceId=414535982_06_02_99

完整的代码下载
mruler

完成之后,到了老婆提问时间,我们有如下对话
她:这个是干什么的啊?
我:这个是尺
她:尺子是干什么的?
我:尺子就是测量距离的啊
她:那为什么要测量距离?
我:比如一根直线你想知道它多长,需要测量啊
她:那家里有直尺为什么不用。
(这个问题很Sharp…….我想了一会才回答)
我:万一你要测量一个非直线的物体你需要用这个啊。比如,测量一个圆。
她:我为什么要测量一个圆的周长啊
我:你能不能问点有建设性的问题?
她:唔。那我可以用直尺测量圆的直径然后根据圆周率测量周长啊。
(又是一个Sharp的问题!)
我:直径不好确定,另外,没有直接测量方便啊!
她:那你要沿着圆走一圈哎~ 这样不准
(更加Sharp的问题!!我想了一下)
我:从理论上来说,人类无法直接测量出一个圆的周长,因为Pi是无限不循环小数,周长一定也是一个无限不循环小数。人类历史上在逼近圆周率的时候就是采用……..
她:为什么要测量周长呢?
(沉思片刻)
我:比如,你要测量一个椭圆的周长
她:椭圆周长有公式,数学书上有,虽然我现在不记得但是确定有。
(长考虑中…….)
我:再举个例子,比如,你要测量一个抛物线(嗯,这个她听过,肯定也知道公式)。不~ 你要测量一个悬链线(我打赌她肯定都没有听说过)。
她:为什么我要…….
我:不说了,我去唱歌了 《1999谢谢你的爱》

参考:
1. 图片来自 http://it.21cn.com/hardware/mouse/a/2009/0428/20/6209325.shtml 蓝影PK激光!最强游戏鼠标“溜冰“测试
2. http://acc.pconline.com.cn/mouse/study_mouse/0902/1550361_1.html 让你一夜成高手!鼠标知识最全面充电宝典
3. http://www.lab-z.com/usbkb/ Arduino 控制USB设备(5)解析USB键盘的例子

Step to UEFI (58) —– 计算MD5

MD5 是目前常用的Hash算法,可以用来校验文件的完整性,或者比对密码等等。本文介绍如何在UEFI下实现计算一个文件MD5的方法。具体算法来自【参考1】,在编译过程中修正了几个关于类型转换的 Warning 。

程序结构很简单,和前一篇关于CRC32的没有多大差别。

#include  <Uefi.h>
#include  <Library/UefiLib.h>
#include  <Library/ShellCEntryLib.h>

#include  <stdio.h>
#include  <stdlib.h>
#include  <wchar.h>

#include <Protocol/EfiShell.h>
#include <Library/ShellLib.h>
#include <Library/MemoryAllocationLib.h>

#include "md5.h"

extern EFI_BOOT_SERVICES         *gBS;
extern EFI_SYSTEM_TABLE			 *gST;
extern EFI_RUNTIME_SERVICES 	 *gRT;

extern EFI_SHELL_PROTOCOL        *gEfiShellProtocol;

int
EFIAPI
main (
  IN int Argc,
  IN CHAR16 **Argv
  )
{
  EFI_FILE_HANDLE   FileHandle;
  RETURN_STATUS     Status;
  EFI_FILE_INFO     *FileInfo = NULL;
  EFI_HANDLE        *HandleBuffer=NULL;
  UINTN  			ReadSize;
  MD5_CTX			ctx;
  char				R[16];
  
  //Check if there is a parameter
  if (Argc == 1) {
	Print(L"Usage: crctest [filename]\n");
	return 0;
  }
  
  //Open the file given by the parameter
  Status = ShellOpenFileByName(Argv[1], (SHELL_FILE_HANDLE *)&FileHandle,
                               EFI_FILE_MODE_READ , 0);

  if(Status != RETURN_SUCCESS) {
        Print(L"OpenFile failed!\n");
		return EFI_SUCCESS;
      }			

  //Get file size	  
  FileInfo = ShellGetFileInfo( (SHELL_FILE_HANDLE)FileHandle);	

  //Allocate a memory buffer
  HandleBuffer = AllocateZeroPool((UINTN) FileInfo-> FileSize);
  if (HandleBuffer == NULL) {
      return (SHELL_OUT_OF_RESOURCES);   }

  ReadSize=(UINTN) FileInfo-> FileSize;
  
  //Load the whole file to the buffer
  Status = ShellReadFile(FileHandle,&ReadSize,HandleBuffer);
  
  MD5_Init(&ctx);
  MD5_Update(&ctx, HandleBuffer, ReadSize);
  MD5_Final((unsigned char *)&R, &ctx);
  
  //Output
  Print(L"File Name: %s\n",Argv[1]);
  Print(L"File Size: %d\n",ReadSize);
  Print(L"File Size: %d\n",ReadSize);
  Print(L"MD5      : %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n",
					R[0]&0xFF  ,R[1]&0xFF  ,R[2]&0xFF  ,R[3]&0xFF  ,
					R[4]&0xFF  ,R[5]&0xFF  ,R[6]&0xFF  ,R[7]&0xFF  ,
					R[8]&0xFF  ,R[9]&0xFF  ,R[0xA]&0xFF,R[0xB]&0xFF,
					R[0xC]&0xFF,R[0xD]&0xFF,R[0xE]&0xFF,R[0xF]&0xFF);
  
  FreePool(HandleBuffer);	
  return EFI_SUCCESS;
}

 

运行结果

md51

可以看到,上面的例子能够在Shell下工作正常,但是需要特别注意,我没有验证过 X64 是否正常,如果你有这个需求,请自行验证。

完整的代码下载:

MD5Test

参考:

1.http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 A portable, fast, and free implementation of the MD5 Message-Digest Algorithm (RFC 1321)

Arduino 打造一个小夜灯

玩了很久的 arduino 老婆一直抱怨没有做过什么实用的东西,这次就做个方便晚上下床的小夜灯。
从技术角度讲非常简单:一个红外遮挡开关(本来是打算用圆柱形的那种,结果买来不好用【参考1】),当收到被阻挡的信号之后,自动从暗到亮,间隔特定时间之后再从亮到暗的灭掉。灯珠选择的是 WS2811。这种是全色灯,理论上有 2^24种颜色(其实肉眼根本分别不出这么多种类,另外,你再看到什么灯吹嘘自己一千六百万种颜色,就知道是和“奥氏体304不锈钢”一样,听上去牛逼的名字而已)。Arduino搭配专门的库函数,控制这个灯还是很方便的。顺便介绍一下原理:这些灯都是WS2811芯片串联起来的,每个灯珠里面都封装着一颗LED和芯片。对外的线有2组,每组都是三根线:VCC/GND/DATA。VCC是5V,所以用起来比较方便的,直接Arduino供电就可以(前提是不要超过Arduino提供电流的上限200ma,如果你不确定最大电流最好像我一样单独供电)。信号方面,每个灯颜色是8位的R ,8位的G,8位的B,串行给出。当第一个WS2811收到信号之后,他会取下自己的RGB颜色,把剩下的信号继续传下去。这样,一根信号线即可给出全部灯珠的颜色信号。

image002

下面就动手开始做了。买来的灯是连接好的一整条,我把它拆成2个一组了。中间使用“SM2.54 接插件 2.54MM 公母壳+公母簧片 对插SM-3P”进行连接。使用这样的插接线的好处是:容易扩充和调整。缺点是:需要手工压头,感觉上我做的连接可靠性不是很高。后来每个插脚除了压合,我还用电烙铁简单焊接了一下。

image003
图片来自佐田旗舰店【参考2】

电路上比较简单,我选择 Arduino Pro Micro,编程方便,体积适中。下面是连接的示意图,特别注意WS2811灯条是有方向的。一边是输入,一边是输出。

image005

代码如下,其中的颜色可以根据具体情况进行调整,这里只是简单演示:

#include "FastLED.h"

//红外传感器(开关用)
#define  IRNear A2
//灯带用的数据Pin
#define DATA_PIN 10

//灯带上灯的数量
#define NUM_LEDS 6

//灯带颜色
CRGB leds[NUM_LEDS];


void setup() {
  //初始化灯带
  FastLED.addLeds<WS2811, DATA_PIN, RGB>(leds, NUM_LEDS);

  //初始化接近开关
  pinMode(IRNear, INPUT_PULLUP);
}

void loop() {

  Serial.println("Led start working");

  //灯带关闭状态
  for (int i = 0; i < NUM_LEDS; i++ )
  {
    leds[i] = CRGB::Black;
    FastLED.show();
  }
  //等待稳定
  delay(1000);
  
  if (0==digitalRead(IRNear)) {
     delay(200);
     if (0==digitalRead(IRNear)) {
        for (int i = 0; i < NUM_LEDS; i++ )
           {
             leds[i] = CRGB(255,83,0);
           }
        for (int j=0; j<255; j++)
           {
            FastLED.show(j);
            delay(100);
           }   
        delay(15000);    
        for (int i = 0; i < NUM_LEDS; i++ )
           {
             leds[i] = CRGB::Red;
           }        
        for (int j=255; j>10; j--)
           {
            FastLED.show(j);
            delay(100);
           } 
        for (int i = 0; i < NUM_LEDS; i++ )
           {
             leds[i] = CRGB::Black;
             FastLED.show();
           }    
        
     }//if (0==digitalRead(IRNear)) {
  }//if (0==digitalRead(IRNear)) {
}

 

组装好的灯测试是下面这样
image007

采用磁铁固定,用粘性低的胶带包裹然后固定在线上。
image009

最上面是遮挡开关,最下面是多出来的一根线。如果等不够,随时可以再添加上一些。
为了美观和可靠,后面又用纸盒把遮挡开关包裹了一下,同样在这个开关后面也有个用于固定的磁铁。

image011

床是铁的,直接粘在外面实验一下。

image013

最后安装之后就是这个样子,其实最好是让每个灯垂直照射地面,不过晚上实验表明这个作为照明已经足够了。

image015

当一只脚落在地面上,遮挡开关会触发,然后灯会缓慢亮起,到达最亮之后维持15秒,切换为红色,随后再逐渐暗下。
老婆实验之后,提出了一个问题:如果我把一直腿垂在床边,那他岂不是要一直工作?这不好吧?
我的回复是谢霆锋的 《1999谢谢你的爱》。

参考:
1. 再次提醒,买回来的东西一定要及时测试,否则卖家不认账
2. 作者已经哭死,从他们家买了三样东西,历时一周才拿到手上(熊猫慢递)。反复盘点发现他们居然少发了电线。联系卖家一直没动静。然后选择了天猫的退款。结果点的是“退货退款”……两天后,客服告诉我,要选“只退款”。等我取消上一次的请求之后惊奇的发现竟然没有办法再次申请。联系淘宝客服,对方听完我的陈述之后,告诉我去找天猫,俺们不是一家。我又拨打了天猫的热线,客服小伙听过讲述之后,很快发给我一个无法打开的链接(鬼知道为什么搞错了);没办法,又打了一次,给了一个可以打开的链接,目前正在处理中ing.最后结果是退款了,但是前后差不多用了2周。可以说,如果以后有机会在淘宝店买东西也不要在天猫店买东西,服务差别很大。

Step to UEFI (57.5) —– 计算CRC32

这里介绍如何编写一个计算CRC32的工具。在EDK的代码中,BaseTools\Source\Common 中有Crc32的例子,这里直接引用了 CRC32.h 和 CRC32.C。

代码中有很多注释,因此不做解释了。

#include  <Uefi.h>
#include  <Library/UefiLib.h>
#include  <Library/ShellCEntryLib.h>

#include  <stdio.h>
#include  <stdlib.h>
#include  <wchar.h>

#include <Protocol/EfiShell.h>
#include <Library/ShellLib.h>
#include <Library/MemoryAllocationLib.h>

#include "Crc32.h"

extern EFI_BOOT_SERVICES         *gBS;
extern EFI_SYSTEM_TABLE			 *gST;
extern EFI_RUNTIME_SERVICES 	 *gRT;

extern EFI_SHELL_PROTOCOL        *gEfiShellProtocol;

int
EFIAPI
main (
  IN int Argc,
  IN CHAR16 **Argv
  )
{
  EFI_FILE_HANDLE   FileHandle;
  RETURN_STATUS     Status;
  EFI_FILE_INFO     *FileInfo = NULL;
  EFI_HANDLE        *HandleBuffer=NULL;
  UINTN  			ReadSize;
  UINT32			CrcOut=0;
  
  //Check if there is a parameter
  if (Argc == 1) {
	Print(L"Usage: crctest [filename]\n");
	return 0;
  }
  
  //Open the file given by the parameter
  Status = ShellOpenFileByName(Argv[1], (SHELL_FILE_HANDLE *)&FileHandle,
                               EFI_FILE_MODE_READ , 0);

  if(Status != RETURN_SUCCESS) {
        Print(L"OpenFile failed!\n");
		return EFI_SUCCESS;
      }			

  //Get file size	  
  FileInfo = ShellGetFileInfo( (SHELL_FILE_HANDLE)FileHandle);	

  //Allocate a memory buffer
  HandleBuffer = AllocateZeroPool((UINTN) FileInfo-> FileSize);
  if (HandleBuffer == NULL) {
      return (SHELL_OUT_OF_RESOURCES);   }

  ReadSize=(UINTN) FileInfo-> FileSize;
  
  //Load the whole file to the buffer
  Status = ShellReadFile(FileHandle,&ReadSize,HandleBuffer);
  
  //Calculate the CRC32
  CalculateCrc32 ((UINT8*)HandleBuffer,ReadSize,&CrcOut);
  
  //Output
  Print(L"File Name: %s\n",Argv[1]);
  Print(L"File Size: %d\n",ReadSize);
  Print(L"CRC32    : %x\n",CrcOut);
  
  FreePool(HandleBuffer);	
  return EFI_SUCCESS;
}

 

运行结果 (计算自己的CRC32)

crc

为了保证正确,下载了一个CRC计算工具,可以看出,计算结果和上面的相同。

compare

完整代码下载

CRCTest

实际上除了上面的方法之外,还有更加简单的方法:直接使用 Boot Services 中的 CalculateCrc32 功能:

Capture

就是这样。

随便说两句

网站趴窝了几天,具体原因是2015第13号台风“苏迪罗”间接造成的…….

解释一下就是台风来了,有人在果壳发帖子【参考1】,然后我跟帖,外链贴了一下很早之前做的台风的GIF动画,大小是 20MB 。结果一个晚上,台风还没有来,我的网站就趴窝了。

检查日志发现因为这个帖子访问量很大,每个浏览帖子的人都要从网站走20MB,很快就用光了一个月的配额。

后来联系供应商,他也很吃惊,那么多这个月没几天就用光了。后来他给加了一些,又开启了。

上一次出现这样的情况是因为我在网站上放了2个MP3,结果被一个淘宝店铺引用到,只是那次是在月末,我删除了 MP 3第二个月就没事了。没想到这次这么严重。

参考:

1.http://www.guokr.com/post/694425/?page=3#6650529 【直播】2015年最强台风苏迪罗要来了!8月8日凌晨已登陆台湾

Arduino 控制USB设备(5)解析USB键盘的例子

下面是一个获得 USB 键盘数据的例子【参考1】。原理上说,是将键盘切换为 Boot Protocol 这样就避免了需要具体解析HID的麻烦。

/* MAX3421E USB Host controller LCD/keyboard demonstration */
//#include <Spi.h>
#include "Max3421e.h"
#include "Usb.h"

/* keyboard data taken from configuration descriptor */
#define KBD_ADDR        1
#define KBD_EP          1
#define KBD_IF          0
#define EP_MAXPKTSIZE   8
#define EP_POLL         0x0a
/**/
//******************************************************************************
//  macros to identify special charaters(other than Digits and Alphabets)
//******************************************************************************
#define BANG        (0x1E)
#define AT          (0x1F)
#define POUND       (0x20)
#define DOLLAR      (0x21)
#define PERCENT     (0x22)
#define CAP         (0x23)
#define AND         (0x24)
#define STAR        (0x25)
#define OPENBKT     (0x26)
#define CLOSEBKT    (0x27)

#define RETURN      (0x28)
#define ESCAPE      (0x29)
#define BACKSPACE   (0x2A)
#define TAB         (0x2B)
#define SPACE       (0x2C)
#define HYPHEN      (0x2D)
#define EQUAL       (0x2E)
#define SQBKTOPEN   (0x2F)
#define SQBKTCLOSE  (0x30)
#define BACKSLASH   (0x31)
#define SEMICOLON   (0x33)
#define INVCOMMA    (0x34)
#define TILDE       (0x35)
#define COMMA       (0x36)
#define PERIOD      (0x37)
#define FRONTSLASH  (0x38)
#define DELETE      (0x4c)
/**/
/* Modifier masks. One for both modifiers */
#define SHIFT       0x22
#define CTRL        0x11
#define ALT         0x44
#define GUI         0x88
/**/
/* "Sticky keys */
#define CAPSLOCK    (0x39)
#define NUMLOCK     (0x53)
#define SCROLLLOCK  (0x47)
/* Sticky keys output report bitmasks */
#define bmNUMLOCK       0x01
#define bmCAPSLOCK      0x02
#define bmSCROLLLOCK    0x04
/**/
EP_RECORD ep_record[ 2 ];  //endpoint record structure for the keyboard

char buf[ 8 ] = { 0 };      //keyboard buffer
char old_buf[ 8 ] = { 0 };  //last poll
/* Sticky key state */
bool numLock = false;
bool capsLock = false;
bool scrollLock = false;
bool line = false;

void setup();
void loop();

MAX3421E Max;
USB Usb;

void setup() {
  Serial.begin( 9600 );
  Serial.println("Start");
  Max.powerOn();
  delay( 200 );
}

void loop() {
    Max.Task();
    Usb.Task();
    if( Usb.getUsbTaskState() == USB_STATE_CONFIGURING ) { 
 //wait for addressing state
        kbd_init();
        Usb.setUsbTaskState( USB_STATE_RUNNING );
    }
    if( Usb.getUsbTaskState() == USB_STATE_RUNNING ) {  
//poll the keyboard  
        kbd_poll();
    }
}
/* Initialize keyboard */
void kbd_init( void )
{
 byte rcode = 0;  //return code
/**/
    /* Initialize data structures */
    ep_record[ 0 ] = *( Usb.getDevTableEntry( 0,0 ));  
                           //copy endpoint 0 parameters
    ep_record[ 1 ].MaxPktSize = EP_MAXPKTSIZE;
    ep_record[ 1 ].Interval  = EP_POLL;
    ep_record[ 1 ].sndToggle = bmSNDTOG0;
    ep_record[ 1 ].rcvToggle = bmRCVTOG0;
    Usb.setDevTableEntry( 1, ep_record );              
                           //plug kbd.endpoint parameters to devtable
    /* Configure device */
    rcode = Usb.setConf( KBD_ADDR, 0, 1 );                    
    if( rcode ) {
        Serial.print("Error attempting to configure keyboard. Return code :");
        Serial.println( rcode, HEX );
        while(1);  //stop
    }
    /* Set boot protocol */
    rcode = Usb.setProto( KBD_ADDR, 0, 0, 0 );
    if( rcode ) {
        Serial.print("Error attempting to configure boot protocol. Return code :");
        Serial.println( rcode, HEX );
        while( 1 );  //stop
    }
    delay(2000);
    Serial.println("Keyboard initialized");
}

/* Poll keyboard and print result */
/* buffer starts at position 2, 0 is modifier key state and 1 is irrelevant */
void kbd_poll( void )
{
 char i;
 static char leds = 0;
 byte rcode = 0;     //return code
    /* poll keyboard */
    rcode = Usb.inTransfer( KBD_ADDR, KBD_EP, 8, buf );
    if( rcode != 0 ) {
        return;
    }//if ( rcode..
    for( i = 2; i < 8; i++ ) {
     if( buf[ i ] == 0 ) {  //end of non-empty space
        break;
     }
      if( buf_compare( buf[ i ] ) == false ) {   //if new key
        switch( buf[ i ] ) {
          case CAPSLOCK:
            capsLock =! capsLock;
            leds = ( capsLock ) ? leds |= bmCAPSLOCK : leds &= ~bmCAPSLOCK;    
                      // set or clear bit 1 of LED report byte
            break;
          case NUMLOCK:
            numLock =! numLock;
            leds = ( numLock ) ? leds |= bmNUMLOCK : leds &= ~bmNUMLOCK;       
                                  // set or clear bit 0 of LED report byte
            break;
          case SCROLLLOCK:
            scrollLock =! scrollLock;
            leds = ( scrollLock ) ? leds |= bmSCROLLLOCK : leds &= ~bmSCROLLLOCK; 
                                  // set or clear bit 2 of LED report byte
            break;
          case DELETE:
            line = false;
            break;
          case RETURN:
            line =! line;
            break;  
          default:
            //Serial.print(HIDtoA( buf[ i ], buf[ 0 ] ));
            break;
        }//switch( buf[ i ...
        Serial.print(buf[ i ],HEX);
        Serial.print(' ');        
        Serial.println(buf[ 0 ],HEX);        
        rcode = Usb.setReport( KBD_ADDR, 0, 1, KBD_IF, 0x02, 0, &leds );
        if( rcode ) {
          Serial.print("Set report error: ");
          Serial.println( rcode, HEX );
        }//if( rcode ...
     }//if( buf_compare( buf[ i ] ) == false ...
    }//for( i = 2...
    for( i = 2; i < 8; i++ ) {                    //copy new buffer to old
      old_buf[ i ] = buf[ i ];
    }
}
/* compare byte against bytes in old buffer */
bool buf_compare( byte data )
{
 char i;
 for( i = 2; i < 8; i++ ) {
   if( old_buf[ i ] == data ) {
     return( true );
   }
 }
 return( false );
}

/* HID to ASCII converter. Takes HID keyboard scancode, returns ASCII code */
byte HIDtoA( byte HIDbyte, byte mod )
{
  /* upper row of the keyboard, numbers and special symbols */
  if( HIDbyte >= 0x1e && HIDbyte <= 0x27 ) {
    if(( mod & SHIFT ) || numLock ) {    //shift key pressed
      switch( HIDbyte ) {
        case BANG:  return( 0x21 );
        case AT:    return( 0x40 );
        case POUND: return( 0x23 );
        case DOLLAR: return( 0x24 );
        case PERCENT: return( 0x25 );
        case CAP: return( 0x5e );
        case AND: return( 0x26 );
        case STAR: return( 0x2a );
        case OPENBKT: return( 0x28 );
        case CLOSEBKT: return( 0x29 );
      }//switch( HIDbyte...
    }
    else {                  //numbers
      if( HIDbyte == 0x27 ) {  //zero
        return( 0x30 );
      }
      else {
        return( HIDbyte + 0x13 );
      }
    }//numbers
  }//if( HIDbyte >= 0x1e && HIDbyte <= 0x27
  /**/
  /* number pad. Arrows are not supported */
  if(( HIDbyte >= 0x59 && HIDbyte <= 0x61 ) && 
                   ( numLock == true )) {  // numbers 1-9
    return( HIDbyte - 0x28 );
  }
  if(( HIDbyte == 0x62 ) && ( numLock == true )) {  //zero
    return( 0x30 );
  }
  /* Letters a-z */
  if( HIDbyte >= 0x04 && HIDbyte <= 0x1d ) {
    if((( capsLock == true ) && ( mod & SHIFT ) == 0 ) 
          || (( capsLock == false ) && ( mod & SHIFT ))) {  
                     //upper case
      return( HIDbyte + 0x3d );
    }
    else {  //lower case
      return( HIDbyte + 0x5d );
    }
  }//if( HIDbyte >= 0x04 && HIDbyte <= 0x1d...
  /* Other special symbols */
  if( HIDbyte >= 0x2c && HIDbyte <= 0x38 ) {
    switch( HIDbyte ) {
      case SPACE: return( 0x20 ); 
      case HYPHEN:
        if(( mod & SHIFT ) == false ) {
         return( 0x2d );
        }
        else {
          return( 0x5f );
        }
      case EQUAL:
       if(( mod & SHIFT ) == false ) {
        return( 0x3d );
       }
       else {
        return( 0x2b );
       }
       case SQBKTOPEN:
         if(( mod & SHIFT ) == false ) {
          return( 0x5b );
         }
         else {
          return( 0x7b );
         }
       case SQBKTCLOSE:
         if(( mod & SHIFT ) == false ) {
          return( 0x5d );
         }
         else {
          return( 0x7d );
         } 
       case BACKSLASH:
         if(( mod & SHIFT ) == false ) {
           return( 0x5c );
         }
         else {
           return( 0x7c );
         }
       case SEMICOLON:
         if(( mod & SHIFT ) == false ) {
           return( 0x3b );
         }
         else {
           return( 0x3a );
         }
      case INVCOMMA:
        if(( mod & SHIFT ) == false ) {
          return( 0x27 );
        }
        else {
          return( 0x22 );
        }
      case TILDE:  
        if(( mod & SHIFT ) == false ) {
          return( 0x60 );
        }
        else {
          return( 0x7e );
        }
      case COMMA:   
        if(( mod & SHIFT ) == false ) {
          return( 0x2c );
        }
        else {
          return( 0x3c );
        }
      case PERIOD:
        if(( mod & SHIFT ) == false ) {
          return( 0x2e );
        }
        else {
          return( 0x3e );
        }
      case FRONTSLASH:
        if(( mod & SHIFT ) == false ) {
          return( 0x2f );
        }
        else {
          return( 0x3f );
        }
      default:
        break;
    }//switch( HIDbyte..
  }//if( HIDbye >= 0x2d && HIDbyte <= 0x38..
  return( 0 );
}

实验依然使用上一次的USB小键盘。上面的按键分布如下:

usbskb1

关于键值的介绍可以在【参考1】找到

NumLock OFF的情况下,各输出键值:

usbskb2
*输出三次62,相当于输出3个0

NumLock ON的情况下,各输出键值:
usbskb3
*输出三次62外加一个53

运行结果

kbr

本文完整代码下载 LCDkbd

参考:
1. https://github.com/felis/USB_Host_Shield/tree/master/examples/descriptor_parser USB_Host_Shield/examples/LCDkbd/ 本文原始代码
2. http://www.quadibloc.com/comp/scan.htm

重新编译EDK2工具的方法(Python部分)

之前在《重新编译EDK2工具的方法(C语言部分)》【参考1】中介绍了重新编译C语言编写的编译工具的方法。本文介绍重新编译Python工具的方法。

我们知道,每次编译时,先运行 edksetup.bat 然后使用 build 命令即可进行编译。下面先说build命令的来龙去脉。

我们运行的Build实际上是 BaseTools\bin\Win32 下面的 Build.exe 。运行 edksetup.bat 之后,会自动把这个目录加入到 Path 中。它的源代码可以在BaseTools\Source\Python下面找到。

根据BaseTools\Source\Python\MakeFile中的建议,编译需要使用 cx_Freeze 4.2.3 和 Python 2.7.2。Python是解释型语言,需要解释器才能正常执行程序,Cx_Freeze是将Python源程序转换为EXE的工具,转换之后即可脱离Python解析器单独运行(相当于把解释器打包到EXE中)。特别提醒,一定要使用上面说的这个版本, cx_Freeze版本之间差别很大,选择其他版本会有莫名的问题。这里【参考2】提供上述版本的下载,有需要的朋友可以直接抓取。

接下来是Python的安装,安装完之后,在命令行下手工输入 Python查看能否运行,如果无法运行,请在环境变量中加入Python的路径。如下图所示。

image001

加入Path后,可以在任意位置调用到Python。

image002

之后安装cx_Freeze。安装完成之后在Python27\Scripts下面会有cxfreeze.bat文件,可以运行这个批处理检查是否能正常工作。

image003

还可以编写一个简单的Python文件然后使用下面的命令生成EXE进行测试
cxfreeze hello.py –target-dir dist

最后,可以开始重新编译工具了。在toolsetup.bat 中加入下面语句指定 cxFreeze 和Python的路径。
Set PYTHON_HOME=c:\python27
Set PYTHON_FREEZE_PATH=c:\python27\Scripts

image004

编译的方法是:首先运行 edksetup.bat ,然后进入BaseTools目录,运行
toolsetup.bat ForceRebuild

image005

运行结果如下

image006

编译后生成的EXE 会直接放置到 BaseTools\Bin\Win32下面。

为了验证这个方法,我在 Build.py上加入输出字符串的语句,重新编译工具后再编译整个BIOS。可以看到执行了我加入的语句。

image007

如果你对 buid 的过程感兴趣,下面就可以慢慢分析了。

就是这样。

参考:

1. http://www.lab-z.com/%E9%87%8D%E6%96%B0%E7%BC%96%E8%AF%91edk2%E5%B7%A5%E5%85%B7%E7%9A%84%E6%96%B9%E6%B3%95%EF%BC%88c%E8%AF%AD%E8%A8%80%E9%83%A8%E5%88%86%EF%BC%89/ 重新编译EDK2工具的方法(C语言部分)》
2. Baidu 网盘链接: http://pan.baidu.com/s/1kTjDDf5 密码: 4wsf

Arduino 控制USB设备(4)解析描述符

前面一篇介绍了如何获得USB Descriptor,更麻烦的是这个数据的解读。在【参考1】给出了一个直接解析 Descriptor的例子。美中不足的是,这个例子只能在老版本的Arduino上工作(我估计是 0.22),在新版本 1.6.x 的IDE上会出现很多报错。

经过努力修改,终于可以编译通过(需要选择 Arduno Uno),程序如下:

/* MAX3421E USB Host controller configuration descriptor parser */
//#include "Spi.h"
#include "Max3421e.h"
#include "Usb.h"
#include "descriptor_parser.h"
 
#define LOBYTE(x) ((char*)(&(x)))[0]
#define HIBYTE(x) ((char*)(&(x)))[1]
#define BUFSIZE 256    //buffer size
#define DEVADDR 1

#define getReportDescr( addr, ep, nbytes, parse_func, nak_limit ) ctrlXfer( addr, ep, bmREQ_HIDREPORT, USB_REQUEST_GET_DESCRIPTOR, 0x00, HID_DESCRIPTOR_REPORT, 0x0000, nbytes, parse_func, nak_limit )
#define getReport( addr, ep, nbytes, interface, report_type, report_id, parse_func, nak_limit ) ctrlXfer( addr, ep, bmREQ_HIDIN, HID_REQUEST_GET_REPORT, report_id, report_type, interface, nbytes, parse_func, nak_limit )

/* Foeward declarations */ 
void setup();
void loop();
byte ctrlXfer( byte addr, byte ep, byte bmReqType, byte bRequest, byte wValLo, byte wValHi, unsigned int wInd, uint16_t nbytes, PARSE parse_func, uint16_t nak_limit );
void HIDreport_parse( uint8_t* buf, uint8_t* head, uint8_t* tail);

typedef struct {
  uint8_t bDescriptorType;
  uint16_t wDescriptorLength;
} HID_CLASS_DESCRIPTOR;


//typedef void (*PARSE)( int8_t*, int8_t*, int8_t );

MAX3421E Max;
USB Usb;
 
void setup()
{
  Serial.begin( 115200 );
  printProgStr(PSTR("\r\nStart"));
  Max.powerOn();
  delay( 200 );
}
 
void loop()
{
  uint8_t rcode;
  uint8_t tmpbyte = 0;

  //PARSE pf = &HIDreport_parse;
  /**/
  Max.Task();
  Usb.Task();
  if( Usb.getUsbTaskState() >= USB_STATE_CONFIGURING ) {  //state configuring or higher
  /* printing device descriptor */
    printProgStr(PSTR("\r\nDevice addressed... "));
    printProgStr(PSTR("Requesting device descriptor."));
    tmpbyte = getdevdescr( DEVADDR );                           //number of configurations, 0 if error   
    if( tmpbyte == 0 ) {
      printProgStr(PSTR("\r\nDevice descriptor cannot be retrieved. Program Halted\r\n"));
      while( 1 );           //stop
     }//if( tmpbyte
     /* print configuration descriptors for all configurations */
     for( uint8_t i = 0; i < tmpbyte; i++ ) {
       getconfdescr( DEVADDR, i );
     }   
  /* Stop */
      while( 1 );                          //stop
  }    
}

/* Prints device descriptor. Returns number of configurations or zero if request error occured */
byte getdevdescr( byte addr )
{
  USB_DEVICE_DESCRIPTOR buf;
  byte rcode;
  //Max.toggle( BPNT_0 );
  rcode = Usb.getDevDescr( addr, 0, 0x12, ( char *)&buf );
  if( rcode ) {
    printProgStr( rcode_error_msg );
    print_hex( rcode, 8 );
    return( 0 );
  }
  printProgStr(PSTR("\r\nDevice descriptor: \r\n"));
  //Descriptor length
  printProgStr( descr_len );
  print_hex( buf.bLength, 8 );
  //Descriptor type
//  printProgStr( descr_type );
//  print_hex( buf.bDescriptorType, 8 );
//  printProgStr( descrtype_parse( buf.bDescriptorType ));
  //USB Version
  printProgStr(PSTR("\r\nUSB version:\t\t"));
  Serial.print(( HIBYTE( buf.bcdUSB )), HEX );
  Serial.print(".");
  Serial.print(( LOBYTE( buf.bcdUSB )), HEX );
  //Device class
  printProgStr( class_str );
  print_hex( buf.bDeviceClass, 8 );
  printProgStr( classname_parse( buf.bDeviceClass ));
  //Device Subclass 
  printProgStr( subclass_str );
  print_hex( buf.bDeviceSubClass, 8 );
  //Device Protocol
  printProgStr( protocol_str );
  print_hex( buf.bDeviceProtocol, 8 );
  //Max.packet size
  printProgStr( maxpktsize_str );
  print_hex( buf.bMaxPacketSize0, 8 );
  //VID
  printProgStr(PSTR("\r\nVendor  ID:\t\t"));
  print_hex( buf.idVendor, 16 );
  //PID
  printProgStr(PSTR("\r\nProduct ID:\t\t"));
  print_hex( buf.idProduct, 16 );
  //Revision
  printProgStr(PSTR("\r\nRevision ID:\t\t"));
  print_hex( buf.bcdDevice, 16 );
  //Mfg.string
  printProgStr (PSTR("\r\nMfg.string index:\t"));
  print_hex( buf.iManufacturer, 8 );
  getstrdescr( addr, buf.iManufacturer );
  //Prod.string
  printProgStr(PSTR("\r\nProd.string index:\t"));
  print_hex( buf.iProduct, 8 );
  //printProgStr( str_cont );
  getstrdescr( addr, buf.iProduct );
  //Serial number string
  printProgStr(PSTR("\r\nSerial number index:\t"));
  print_hex( buf.iSerialNumber, 8 );
  //printProgStr( str_cont );
  getstrdescr( addr, buf.iSerialNumber );
  //Number of configurations
  printProgStr(PSTR("\r\nNumber of conf.:\t"));
  print_hex( buf.bNumConfigurations, 8 );
  return( buf.bNumConfigurations );
}
/* Get string descriptor. Takes device address and string index */
byte getstrdescr( byte addr, byte idx )
{
  char buf[ BUFSIZE ];
  byte rcode;
  byte length;
  byte i;
  unsigned int langid;
  if( idx == 0 ) {  //don't try to get index zero
    return( 0 );
  }
  rcode = Usb.getStrDescr( addr, 0, 1, 0, 0, buf );  //get language table length
  if( rcode ) {
    printProgStr(PSTR("\r\nError retrieving LangID table length"));
    return( rcode );
  }
  length = buf[ 0 ];      //length is the first byte
  rcode = Usb.getStrDescr( addr, 0, length, 0, 0, buf );  //get language table
  if( rcode ) {
    printProgStr(PSTR("\r\nError retrieving LangID table"));
    return( rcode );
  }
  HIBYTE( langid ) = buf[ 3 ];                            //get first langid  
  LOBYTE( langid ) = buf[ 2 ];                            //bytes are swapped to account for endiannes
  //printProgStr(PSTR("\r\nLanguage ID: "));
  //print_hex( langid, 16 );
  rcode = Usb.getStrDescr( addr, 0, 1, idx, langid, buf );
  if( rcode ) {
    printProgStr(PSTR("\r\nError retrieving string length"));
    return( rcode );
  }
  length = ( buf[ 0 ] < 254 ? buf[ 0 ] : 254 );
  printProgStr(PSTR(" Length: "));
  Serial.print( length, DEC ); 
  rcode = Usb.getStrDescr( addr, 0, length, idx, langid, buf );
  if( rcode ) {
    printProgStr(PSTR("\r\nError retrieveing string"));
    return( rcode );
  }
  printProgStr(PSTR(" Contents: "));
  for( i = 2; i < length; i+=2 ) {
    Serial.print( buf[ i ] );
  }
  return( idx );
}
/* Returns string to class name */
const char* classname_parse( byte class_number )
{
  switch( class_number ) {
    case 0x00:
      return PSTR(" Use class information in the Interface Descriptor");
    case 0x01:
      return PSTR(" Audio");
    case 0x02:
      return PSTR(" Communications and CDC Control");
    case 0x03:
      return PSTR(" HID (Human Interface Device)");
    case 0x05:
      return PSTR(" Physical");
    case 0x06:
      return PSTR(" Image");
    case 0x07:
      return PSTR(" Printer");
    case 0x08:
      return PSTR(" Mass Storage");
    case 0x09:
      return PSTR(" Hub");
    case 0x0a:
      return PSTR(" CDC-Data");
    case 0x0b:
      return PSTR(" Smart Card");
    case 0x0d:
      return PSTR(" Content Security");
    case 0x0e:
      return PSTR(" Video");
    case 0x0f:
      return PSTR(" Personal Healthcare");
    case 0xdc:
      return PSTR("Diagnostic Device");
    case 0xe0:
      return PSTR(" Wireless Controller");
    case 0xef:
      return PSTR(" Miscellaneous");
    case 0xfe:
      return PSTR(" Application Specific");
    case 0xff:
      return PSTR(" Vendor Specific");
    default:
      return unk_msg;
  }//switch( class_number
}            
/* Getting configuration descriptor */
byte getconfdescr( byte addr, byte conf )
{
  char buf[ BUFSIZE ];
  char* buf_ptr = buf;
  byte rcode;
  byte descr_length;
  byte descr_type;
  unsigned int total_length;
  printProgStr(PSTR("\r\n\nConfiguration number "));
  Serial.print( conf, HEX );
  rcode = Usb.getConfDescr( addr, 0, 4, conf, buf );  //get total length
  if( rcode ) {
    printProgStr(PSTR("Error retrieving configuration length. Error code "));
    Serial.println( rcode, HEX );
    return( 0 );
  }//if( rcode
  LOBYTE( total_length ) = buf[ 2 ];
  HIBYTE( total_length ) = buf[ 3 ];
  printProgStr(PSTR("\r\nTotal configuration length: "));
  Serial.print( total_length, DEC );
  printProgStr(PSTR(" bytes"));
  if( total_length > BUFSIZE ) {    //check if total length is larger than buffer
    printProgStr(PSTR("Total length truncated to "));
    Serial.print( BUFSIZE, DEC);
    printProgStr(PSTR("bytes"));
    total_length = BUFSIZE;
  }
  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( HID_DESCRIPTOR_HID ):
        printhid_descr( buf_ptr );
        break;
      default:
        printunkdescr( buf_ptr );
        break;
        }//switch( descr_type
    Serial.println("");    
    buf_ptr = ( buf_ptr + descr_length );    //advance buffer pointer
  }//while( buf_ptr <=...
  return( 0 );
}
/* function to print configuration descriptor */
void printconfdescr( char* descr_ptr )
{
 USB_CONFIGURATION_DESCRIPTOR* conf_ptr = ( USB_CONFIGURATION_DESCRIPTOR* )descr_ptr;
 uint8_t tmpbyte;
  printProgStr(PSTR("\r\n\nConfiguration descriptor:"));
  printProgStr(PSTR("\r\nTotal length:\t\t"));
  print_hex( conf_ptr->wTotalLength, 16 );
  printProgStr(PSTR("\r\nNumber of interfaces:\t"));
  print_hex( conf_ptr->bNumInterfaces, 8 );
  printProgStr(PSTR("\r\nConfiguration value:\t"));
  print_hex( conf_ptr->bConfigurationValue, 8 );
  printProgStr(PSTR("\r\nConfiguration string:\t"));
  tmpbyte = conf_ptr->iConfiguration;
  print_hex( tmpbyte, 8 );
  getstrdescr( DEVADDR, tmpbyte );
  printProgStr(PSTR("\r\nAttributes:\t\t"));
  tmpbyte = conf_ptr->bmAttributes;
  print_hex( tmpbyte, 8 );
  if( tmpbyte & 0x40 ) {  //D6
    printProgStr(PSTR(" Self-powered"));
  }
  if( tmpbyte & 0x20 ) { //D5
    printProgStr(PSTR(" Remote Wakeup"));
  }
  printProgStr(PSTR("\r\nMax.power:\t\t"));
  tmpbyte = conf_ptr->bMaxPower;
  print_hex( tmpbyte, 8 );
  printProgStr(PSTR(" "));
  Serial.print(( tmpbyte * 2 ), DEC);
  printProgStr(PSTR("ma"));
  return;
}
/* function to print interface descriptor */
void printintfdescr( char* descr_ptr )
{
 USB_INTERFACE_DESCRIPTOR* intf_ptr = ( USB_INTERFACE_DESCRIPTOR* )descr_ptr;
 uint8_t tmpbyte;
  printProgStr(PSTR("\r\nInterface descriptor:"));
  printProgStr(PSTR("\r\nInterface number:\t"));
  print_hex( intf_ptr->bInterfaceNumber, 8 );
  printProgStr(PSTR("\r\nAlternate setting:\t"));
  print_hex( intf_ptr->bAlternateSetting, 8 );
  printProgStr(PSTR("\r\nEndpoints:\t\t"));
  print_hex( intf_ptr->bNumEndpoints, 8 );
  printProgStr( class_str );
  tmpbyte = intf_ptr->bInterfaceClass;
  print_hex( tmpbyte, 8 );
  printProgStr(classname_parse( tmpbyte ));
  printProgStr( subclass_str );
  print_hex( intf_ptr->bInterfaceSubClass, 8 );
  printProgStr( protocol_str );
  print_hex( intf_ptr->bInterfaceProtocol, 8 );
  printProgStr(PSTR("\r\nInterface string:\t"));
  tmpbyte = intf_ptr->iInterface;
  print_hex( tmpbyte, 8 );
  getstrdescr( DEVADDR, tmpbyte );
  return;
}
/* function to print endpoint descriptor */
void printepdescr( char* descr_ptr )
{
 USB_ENDPOINT_DESCRIPTOR* ep_ptr = ( USB_ENDPOINT_DESCRIPTOR* )descr_ptr;
 uint8_t tmpbyte;
  printProgStr(PSTR("\r\nEndpoint descriptor:"));
  printProgStr(PSTR("\r\nEndpoint address:\t"));
  tmpbyte = ep_ptr->bEndpointAddress;
  print_hex( tmpbyte & 0x0f, 8 );
  printProgStr(PSTR(" Direction: "));
  ( tmpbyte & 0x80 ) ? printProgStr(PSTR("IN")) : printProgStr(PSTR("OUT"));
  printProgStr(PSTR("\r\nAttributes:\t\t"));
  tmpbyte = ep_ptr->bmAttributes;
  print_hex( tmpbyte, 8 );
  printProgStr(PSTR(" Transfer type: "));
  printProgStr((char*)pgm_read_word(&transfer_types[(tmpbyte & 0x03)]));
  if(( tmpbyte & 0x03 ) == 1 ) {  //Isochronous Transfer
    printProgStr(PSTR(", Sync Type: "));
    printProgStr((char*)pgm_read_word(&sync_types[(tmpbyte & 0x0c)]));
    printProgStr(PSTR(", Usage Type: "));
    printProgStr((char*)pgm_read_word(&usage_types[(tmpbyte & 0x30)]));
  }//if( tmpbyte & 0x01
  printProgStr( maxpktsize_str );
  print_hex( ep_ptr->wMaxPacketSize, 16 );
  printProgStr(PSTR("\r\nPolling interval:\t"));
  tmpbyte = ep_ptr->bInterval;
  print_hex( tmpbyte, 8 );
  printProgStr(PSTR(" "));
  Serial.print( tmpbyte, DEC );
  printProgStr(PSTR(" ms"));
  return;
}
/* function to print HID descriptor */
void printhid_descr( char* descr_ptr )
{
 PARSE pf = &HIDreport_parse;
 USB_HID_DESCRIPTOR* hid_ptr = ( USB_HID_DESCRIPTOR* )descr_ptr;
 uint8_t tmpbyte;
  /**/
  printProgStr(PSTR("\r\nHID descriptor:"));
  printProgStr(PSTR("\r\nDescriptor length:\t"));
  tmpbyte = hid_ptr->bLength;
  print_hex( tmpbyte, 8 );
  printProgStr(PSTR(" "));
  Serial.print( tmpbyte, DEC );
  printProgStr(PSTR(" bytes"));
  printProgStr(PSTR("\r\nHID version:\t\t"));
  Serial.print(( HIBYTE( hid_ptr->bcdHID )), HEX );
  Serial.print(".");
  Serial.print(( LOBYTE( hid_ptr->bcdHID )), HEX );
  tmpbyte = hid_ptr->bCountryCode;
  printProgStr(PSTR("\r\nCountry Code:\t\t"));
  Serial.print( tmpbyte, DEC );
  printProgStr(PSTR(" "));
  ( tmpbyte > 35 ) ? printProgStr(PSTR("Reserved")) : printProgStr((char*)pgm_read_word(&HID_Country_Codes[ tmpbyte ]));
  tmpbyte = hid_ptr->bNumDescriptors;
  printProgStr(PSTR("\r\nClass Descriptors:\t"));
  Serial.print( tmpbyte, DEC );
  //Printing class descriptors
  descr_ptr += 6; //advance buffer pointer
  for( uint8_t i = 0; i < tmpbyte; i++ ) {
    uint8_t tmpdata;
    HID_CLASS_DESCRIPTOR* hidclass_ptr = ( HID_CLASS_DESCRIPTOR* )descr_ptr;
    tmpdata = hidclass_ptr->bDescriptorType;
    printProgStr(PSTR("\r\nClass Descriptor Type:\t"));
    Serial.print( tmpdata, HEX );
    if(( tmpdata < 0x21 ) || ( tmpdata > 0x2f )) {
     printProgStr(PSTR(" Invalid"));
    }
    switch( tmpdata ) {
      case 0x21:
        printProgStr(PSTR(" HID"));
        break;
      case 0x22:
        printProgStr(PSTR(" Report"));
        break;
      case 0x23:
        printProgStr(PSTR(" Physical"));
        break;
      default:
        printProgStr(PSTR(" Reserved"));
        break;
    }//switch( tmpdata
    printProgStr(PSTR("\r\nClass Descriptor Length:"));
    Serial.print( hidclass_ptr->wDescriptorLength );
    printProgStr(PSTR(" bytes"));
    printProgStr(PSTR("\r\n\nHID report descriptor:\r\n"));
    getReportDescr( DEVADDR, 0 , hidclass_ptr->wDescriptorLength, pf, USB_NAK_LIMIT );
    descr_ptr += 3; //advance to the next record
  }//for( uint8_t i=...
  return;
}
/*function to print unknown descriptor */
void printunkdescr( char* descr_ptr )
{
  byte length = *descr_ptr;
  byte i;
  printProgStr(PSTR("\r\nUnknown descriptor:"));
  printProgStr(PSTR("Length:\t\t"));
  print_hex( *descr_ptr, 8 );
  printProgStr(PSTR("\r\nType:\t\t"));
  print_hex( *(descr_ptr + 1 ), 8 );
  printProgStr(PSTR("\r\nContents:\t"));
  descr_ptr += 2;
  for( i = 0; i < length; i++ ) {
    print_hex( *descr_ptr, 8 );
    descr_ptr++;
  }
}
/* Control-IN transfer with callback. Sets address, endpoint, fills control packet with necessary data, dispatches control packet, and initiates bulk IN transfer   */
/* Control, data, and setup stages combined from standard USB library to be able to read large data blocks. Restricted to control-IN transfers with data stage   */
/* data read and MAX3421E RECV FIFO buffer release shall be performed by parse_func callback */
/* return codes:                */
/* 00       =   success         */
/* 01-0f    =   non-zero HRSLT  */
byte ctrlXfer( byte addr, byte ep, byte bmReqType, byte bRequest, byte wValLo, byte wValHi, unsigned int wInd, uint16_t nbytes, PARSE parse_func, uint16_t nak_limit = USB_NAK_LIMIT )
{
 byte rcode;   
 SETUP_PKT sp;
 EP_RECORD* ep_rec = Usb.getDevTableEntry( addr, ep );
 byte pktsize;
 byte maxpktsize = ep_rec->MaxPktSize;
 unsigned int xfrlen = 0;
  /**/
  Max.regWr( rPERADDR, addr );                    //set peripheral address
  /* fill in setup packet */
  sp.ReqType_u.bmRequestType = bmReqType;
  sp.bRequest = bRequest;
  sp.wVal_u.wValueLo = wValLo;
  sp.wVal_u.wValueHi = wValHi;
  sp.wIndex = wInd;
  sp.wLength = nbytes;
  Max.bytesWr( rSUDFIFO, 8, ( char *)&sp );    //transfer to setup packet FIFO
  rcode = Usb.dispatchPkt( tokSETUP, ep, nak_limit );            //dispatch packet
  //Serial.println("Setup packet");   //DEBUG
  if( rcode ) {                                   //return HRSLT if not zero
      printProgStr(PSTR("\r\nSetup packet error: "));
      Serial.print( rcode, HEX );                                          
      return( rcode );
  }
  /* Data stage */
  //ep_rec->rcvToggle = bmRCVTOG1;
  Max.regWr( rHCTL, bmRCVTOG1 );  //set toggle
  while( 1 ) {                    //exited by break
    /* request data */
    rcode = Usb.dispatchPkt( tokIN, ep, nak_limit );
    if( rcode ) {
      printProgStr(PSTR("\r\nData Stage Error: "));
      Serial.print( rcode, HEX );
      return( rcode );
    }
    /* check for RCVDAVIRQ and generate error if not present */ 
    /* the only case when absense of RCVDAVIRQ makes sense is when toggle error occured. Need to add handling for that */
    if(( Max.regRd( rHIRQ ) & bmRCVDAVIRQ ) == 0 ) {
      printProgStr(PSTR("\r\nData Toggle error."));
      return ( 0xf0 );                            
    }    
    pktsize = Max.regRd( rRCVBC );  //get received bytes count
    parse_func( pktsize );          //call parse function. Parse is expected to read the FIFO completely
    Max.regWr( rHIRQ, bmRCVDAVIRQ );                    // Clear the IRQ & free the buffer
    xfrlen += pktsize;                              // add this packet's byte count to total transfer length
    /* The transfer is complete under two conditions:           */
    /* 1. The device sent a short packet (L.T. maxPacketSize)   */
    /* 2. 'nbytes' have been transferred.                       */
    if (( pktsize < maxpktsize ) || (xfrlen >= nbytes )) {      // have we transferred 'nbytes' bytes?
      break;
    }
  }//while( 1 )
  rcode = Usb.dispatchPkt( tokOUTHS, ep, nak_limit );
  if( rcode ) {   //return error
    printProgStr(PSTR("Status packet error: "));
    Serial.print( rcode, HEX );                                          
  }
  return( rcode );
}
/* Parses bitfields in main items */
void print_mainbitfield( uint8_t byte_toparse )
{
  ( byte_toparse & 0x01 ) ? printProgStr(PSTR("Constant,")) : printProgStr(PSTR("Data,"));  //bit 0
  ( byte_toparse & 0x02 ) ? printProgStr(PSTR("Variable,")) : printProgStr(PSTR("Array,"));  //bit 1
  ( byte_toparse & 0x04 ) ? printProgStr(PSTR("Relative,")) : printProgStr(PSTR("Absolute,"));  //...
  ( byte_toparse & 0x08 ) ? printProgStr(PSTR("Wrap,")) : printProgStr(PSTR("No Wrap,"));
  ( byte_toparse & 0x10 ) ? printProgStr(PSTR("Non Linear,")) : printProgStr(PSTR("Linear,"));
  ( byte_toparse & 0x20 ) ? printProgStr(PSTR("No preferred,")) : printProgStr(PSTR("Preferred State,"));
  ( byte_toparse & 0x40 ) ? printProgStr(PSTR("Null State,")) : printProgStr(PSTR("No Null Position,"));  //bit 6
  ( byte_toparse & 0x40 ) ? printProgStr(PSTR("Volatile( ignore for Input),")) : printProgStr(PSTR("Non-volatile(Ignore for Input),"));  //bit 7
}
/* HID Report Desriptor Parser Callback             */
/* called repeatedly from Control transfer function */
void HIDreport_parse( uint8_t pkt_size )
{
#define B_SIZE 0x03        //bSize bitmask
#define B_TYPE 0x0c        //bType bitmask
#define B_TAG  0xf0        //bTag bitmask
 /* parser states */
 enum STATE { ITEM_START, DATA_PARSE };
 static STATE state = ITEM_START;
 static uint8_t databytes_left = 0;
 static uint8_t prefix;              //item prefix - type and tag
 uint8_t byte_toparse;
 uint8_t bType;
 uint8_t tmpbyte;
 /**/
  while( 1 ) {
     if( pkt_size ) {
       byte_toparse = Max.regRd( rRCVFIFO );  //read a byte from FIFO
       pkt_size--;
     }
     else {
       return;                                //all bytes read
     }
     switch( state ) {
      case ITEM_START:  //start of the record
        prefix = byte_toparse >>2;        //store prefix for databyte parsing
        tmpbyte = byte_toparse & B_SIZE; 
        /* get item length */
        ( tmpbyte == 0x03 ) ? databytes_left = 4 : databytes_left = tmpbyte;
         if( databytes_left ) {
           state = DATA_PARSE;    //read bytes after prefix
         }
         printProgStr(PSTR("\r\nLength: "));
         Serial.print( databytes_left, DEC );
         /* get item type */
         bType = ( byte_toparse & B_TYPE ) >>2;
         printProgStr(PSTR("  Type: "));
         printProgStr((char*)pgm_read_word(&btypes[ bType ]));
         /* get item tag */
         printProgStr(PSTR("\t\tTag: "));
         tmpbyte = ( byte_toparse & B_TAG ) >>4 ;
         switch( bType ) {
           case 0:  //Main
             if( tmpbyte < 0x08 ) {
               printProgStr(PSTR("Invalid Tag"));
             }
             else if( tmpbyte > 0x0c ) {
               printProgStr( reserved_msg ); 
             }
             else {
               printProgStr((char*)pgm_read_word(&maintags[ tmpbyte - 8 /* & 0x03 */]));
               //Serial.print("Byte: ");
               //Serial.println( tmpbyte, HEX );
             }
             break;//case 0 Main
           case 1:  //Global
             ( tmpbyte > 0x0b ) ? printProgStr( reserved_msg ) : printProgStr((char*)pgm_read_word(&globaltags[ tmpbyte ]));
             break;//case 1 Global
           case 2:  //Local
             ( tmpbyte > 0x0a ) ? printProgStr( reserved_msg ) : printProgStr((char*)pgm_read_word(&localtags[ tmpbyte ]));
             break;//case 2 Local
           default:
             break;  
         }//switch( bType...        
         break;//case ITEM_START
       case DATA_PARSE:
         switch( prefix ) {
           case 0x20:  //Main Input
           case 0x24:  //Main Output
           case 0x2c:  //Main Feature
             /* todo: add parsing 8th bit */
             print_mainbitfield( byte_toparse );
             break;
           case 0x28:    //Main Collection
             if(( byte_toparse > 0x06 ) && ( byte_toparse < 0x80 )) {
               printProgStr( reserved_msg );
             }
             else if(( byte_toparse > 0x7f ) && ( byte_toparse <= 0xff )) {
               printProgStr(PSTR("Vendor-defined"));
             }
             else {
               printProgStr((char*)pgm_read_word(&collections[ byte_toparse ]));
             }
             break;//case 0x28 Main Collection           
           //case 0x30: //Main End Collection
           case 0x01:    //Global Usage Page
             switch( byte_toparse ) {  //see HID Usage Tables doc v.1.12 page 14
               case 0x00:              
               case 0x01:
               case 0x02:
               case 0x03:
               case 0x04:
               case 0x05:
               case 0x06:
               case 0x07:
               case 0x08:
               case 0x09:
               case 0x0a:
               case 0x0b:
               case 0x0c:
               case 0x0d:
               case 0x0e:
               case 0x0f:
               case 0x10:
                 printProgStr((char*)pgm_read_word(&usage_pages[ byte_toparse ]));
                 break;
               case 0x14:
                 printProgStr(PSTR("Alphanumeric Display"));
                 break;
               case 0x40:
                 printProgStr(PSTR("Medical Instruments"));
                 break;
               case 0x80:
               case 0x81:
               case 0x82:
               case 0x83:
                 printProgStr(PSTR("Monitor page"));
                 break;
               case 0x84:
               case 0x85:
               case 0x86:
               case 0x87:
                 printProgStr(PSTR("Power page"));
                 break;
               case 0x8c:
                 printProgStr(PSTR("Bar Code Scanner page"));
                 break;
               case 0x8d:
                 printProgStr(PSTR("Scale page"));
                 break;
               case 0x8e:
                 printProgStr(PSTR("Magnetic Stripe Reading (MSR) Devices"));
                 break;
               case 0x8f:
                 printProgStr(PSTR("Reserved Point of Sale pages"));
                 break;
               case 0x90:
                 printProgStr(PSTR("Camera Control Page"));
                 break;
               case 0x91: 
                 printProgStr(PSTR("Arcade Page"));
                 break;                
             default:
//               printProgStr(PSTR("Data: "));
//               print_hex( byte_toparse, 8 );
               //databytes_left--;
               break;           
             }//switch case 0x01:    //Global Usage Page
         }//switch( prefix ...         
         printProgStr(PSTR("  Data: "));
         print_hex( byte_toparse, 8 );
         databytes_left--;
         if( !databytes_left ) {
           state = ITEM_START;
         }
         break;
     }//switch( state...
   }//while( 1 ...
}
/* 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);
}

/* given a PROGMEM string, use Serial.print() to send it out       */
/* Some non-intuitive casting necessary:                           */
/* printProgStr(PSTR("Func.Mode:\t0x"));                           */
/* printProgStr((char*)pgm_read_word(&mtpopNames[(op & 0xFF)]));   */
void printProgStr(const char* str)
{
  if(!str) { 
    return;
  }
  char c;
  while((c = pgm_read_byte(str++))) {
    Serial.write(c);
  }
  return;
}

 

工作的照片:

usbkbs

我用USB Shield挂接了一个USB小键盘,获得的结果如下:

Start
Device addressed… Requesting device descriptor.
Device descriptor:

Descriptor Length: 12
USB version: 1.10
Class: 00 Use class information in the Interface Descriptor
Subclass: 00
Protocol: 00
Max.packet size: 08
Vendor ID: 13BA
Product ID: 0001
Revision ID: 0100
Mfg.string index: 00
Prod.string index: 00
Serial number index: 00
Number of conf.: 01

Configuration number 0
Total configuration length: 34 bytes

Configuration descriptor:
Total length: 0022
Number of interfaces: 01
Configuration value: 01
Configuration string: 00
Attributes: A0 Remote Wakeup
Max.power: 32 100ma

Interface descriptor:
Interface number: 00
Alternate setting: 00
Endpoints: 01
Class: 03 HID (Human Interface Device)
Subclass: 01
Protocol: 01
Interface string: 00

HID descriptor:
Descriptor length: 09 9 bytes
HID version: 1.10
Country Code: 0 Not Supported
Class Descriptors: 1
Class Descriptor Type: 22 Report
Class Descriptor Length:54 bytes

HID report descriptor:

Length: 1 Type: Global Tag: Usage Page Generic Desktop Controls Data: 01
Length: 1 Type: Local Tag: Usage Data: 06
Length: 1 Type: Main Tag: Collection Application (mouse, keyboard) Data: 01
Length: 1 Type: Global Tag: Usage Page LEDs Data: 08
Length: 1 Type: Local Tag: Usage Minimum Data: 01
Length: 1 Type: Local Tag: Usage Maximum Data: 03
Length: 1 Type: Global Tag: Logical Minimum Data: 00
Length: 1 Type: Global Tag: Logical Maximum Data: 01
Length: 1 Type: Global Tag: Report Size Data: 01
Length: 1 Type: Global Tag: Report Count Data: 03
Length: 1 Type: Main Tag: Output Data,Variable,Absolute,No Wrap,Linear,Preferred State,No Null Position,Non-volatile(Ignore for Input), Data: 02
Length: 1 Type: Global Tag: Report Count Data: 05
Length: 1 Type: Main Tag: Output Constant,Array,Absolute,No Wrap,Linear,Preferred State,No Null Position,Non-volatile(Ignore for Input), Data: 01
Length: 1 Type: Global Tag: Usage Page Keyboard/Keypad Data: 07
Length: 1 Type: Local Tag: Usage Minimum Data: E0
Length: 1 Type: Local Tag: Usage Maximum Data: E7
Length: 1 Type: Global Tag: Report Count Data: 08
Length: 1 Type: Main Tag: Input Data,Variable,Absolute,No Wrap,Linear,Preferred State,No Null Position,Non-volatile(Ignore for Input), Data: 02
Length: 1 Type: Global Tag: Report Size Data: 08
Length: 1 Type: Global Tag: Report Count Data: 01
Length: 1 Type: Main Tag: Input Constant,Array,Absolute,No Wrap,Linear,Preferred State,No Null Position,Non-volatile(Ignore for Input), Data: 01
Length: 1 Type: Local Tag: Usage Minimum Data: 00
Length: 1 Type: Local Tag: Usage Maximum Data: 91
Length: 2 Type: Global Tag: Logical Maximum Data: FF Data: 00
Length: 1 Type: Global Tag: Report Count Data: 06
Length: 1 Type: Main Tag: Input Data,Array,Absolute,No Wrap,Linear,Preferred State,No Null Position,Non-volatile(Ignore for Input), Data: 00
Length: 0 Type: Main Tag: End Collection

Endpoint descriptor:
Endpoint address: 01 Direction: IN
Attributes: 03 Transfer type: Interrupt
Max.packet size: 0008
Polling interval: 0A 10 ms

修改后的可以正常编译的代码下载

descriptor_parser

参考:

1. https://github.com/felis/USB_Host_Shield/tree/master/examples/descriptor_parser USB_Host_Shield/examples/descriptor_parser/