用示波器“看” arduino (2)

有一个网友提出一个问题“现在要进行一个0.4us的延时,发现不管怎么调都只能调到15us,根本达不到要求,我用的芯片是MEGA32U4-AU,外部晶振是16MHz,求见解!!!!!”
我试验了一下,最终的程序如下:

const int PinA =  13;      
void setup() {
  pinMode(PinA, OUTPUT);
  digitalWrite(PinA,LOW);
}

void loop() {
  PORTB = B100000; //digitalWrite(PinA,HIGH); 
  PORTB = B000000; //digitalWrite(PinA,LOW);  
    for (long zdelay=0;zdelay<9; zdelay++) {
    __asm__("nop\n\t");
  }
  PORTB = B100000; //digitalWrite(PinA,HIGH);
  PORTB = B000000; //digitalWrite(PinA,LOW);
  PORTB = B100000; //digitalWrite(PinA,HIGH);  
  PORTB = B000000;  //digitalWrite(PinA,LOW);  
}

 

这个程序运行之后的波形如下

image002

可以看到中间的延时差不多有4.1331us(我用游标对齐,右下角显示x=4.1331). 让然这个值中还包括了一个拉GPIO的指令周期,大约会有 62.1ns的影响。此外,如果要求特别精确,在使用时还要考虑周期性中断的影响。这里就不说了…….

下面我们继续实验,尝试找到循环次数和实际delay时间的关系(因为涉及到编译器优化, long int的计算和判断,直接尝试计算机器周期不可行)。
首先尝试zdelay<8,测量结果是3.7013us image004

根据上述值结合循环简单猜测一下,对于这个循环体,固定部分耗时0.2469us (比如给变量赋初始值),循环部分每次耗时0.4318us
就是 T= 0.2469 + n *0.4318
根据这个计算循环zdelay<5应该是 2.4059us测量结果是2.3722us 加入我们打算delay 100us 根据上述公式应该循环 231次 代码:

const int PinA =  13;      
void setup() {
  pinMode(PinA, OUTPUT);
  digitalWrite(PinA,LOW);
}

void loop() {
  PORTB = B100000; //digitalWrite(PinA,HIGH); 
  PORTB = B000000; //digitalWrite(PinA,LOW);  
  
  for (long zdelay=0;zdelay<231; zdelay++) {
    __asm__("nop\n\t");
  }

  PORTB = B100000; //digitalWrite(PinA,HIGH);
  PORTB = B000000; //digitalWrite(PinA,LOW);
  PORTB = B100000; //digitalWrite(PinA,HIGH);  
  PORTB = B000000;  //digitalWrite(PinA,LOW);  
}

 

实际测试结果符合理论…….
image006

最后,“极客工坊”的 sanyouhi 朋友指出,精确延时可以用写好的库来直接完成【参考1】

#define F_CPU 16000000
#include <util/delay.h>

void setup()
{
  DDRB = 0X20;
}

void loop()
{
PORTB = 0X20;
_delay_us(0.4);
PORTB = 0;
}

 

对应的头文件在 \arduino-1.6.3\hardware\tools\avr\avr\include\util\delay.h 有机会的时候再研究一下。

后记,比较有意思,如果我把代码写为下面的形式

const int PinA =  13;      
void setup() {
  pinMode(PinA, OUTPUT);
  digitalWrite(PinA,LOW);
}

void loop() {
  PORTB = B100000; //digitalWrite(PinA,HIGH); 
  for (long zdelay=0;zdelay<17; zdelay++) {
  }
  PORTB = B000000; //digitalWrite(PinA,LOW);  
  PORTB = B100000; //digitalWrite(PinA,HIGH);
  PORTB = B000000; //digitalWrite(PinA,LOW);
  PORTB = B100000; //digitalWrite(PinA,HIGH);  
  PORTB = B000000;  //digitalWrite(PinA,LOW);  
}

 

中间delay 的周期和不写 for 循环是相同的,猜测原因是编译器的自动优化,当编译器发现空循环时会自动移除循环代码。

参考:

1.http://www.geek-workshop.com/thread-24982-1-1.html

Arduino 控制USB设备(7)解析USB鼠标的例子(下)

前面实现了HP 鼠标数据的读取,下面继续研究 DELL 的鼠标。还是首先运行取得描述符的程序,结果如下:

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: 2188
Product ID: 0AE1
Revision ID: 0100
Mfg.string index: 00
Prod.string index: 01 Length: 38 Contents: USB OPTICAL MOUSE
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: 02
Interface string: 00

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

HID report descriptor:

Length: 1 Type: Global Tag: Usage Page Generic Desktop Controls Data: 01
Length: 1 Type: Local Tag: Usage Data: 02
Length: 1 Type: Main Tag: Collection Application (mouse, keyboard) Data: 01
Length: 1 Type: Global Tag: Report ID Data: 01
Length: 1 Type: Local Tag: Usage Data: 01
Length: 1 Type: Main Tag: Collection Physical (group of axes) Data: 00
Length: 1 Type: Global Tag: Usage Page Button Data: 09
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 Count Data: 03
Length: 1 Type: Global Tag: Report Size Data: 01
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 Count Data: 01
Length: 1 Type: Global Tag: Report Size Data: 05
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: Global Tag: Usage Page Generic Desktop Controls Data: 01
Length: 1 Type: Local Tag: Usage Data: 30
Length: 1 Type: Local Tag: Usage Data: 31
Length: 2 Type: Global Tag: Logical Minimum Data: 00 Data: F8
Length: 2 Type: Global Tag: Logical Maximum Data: FF Data: 07
Length: 1 Type: Global Tag: Report Size Data: 0C
Length: 1 Type: Global Tag: Report Count Data: 02
Length: 1 Type: Main Tag: Input Data,Variable,Relative,No Wrap,Linear,Preferred State,No Null Position,Non-volatile(Ignore for Input), Data: 06
Length: 1 Type: Local Tag: Usage Data: 38
Length: 1 Type: Global Tag: Logical Minimum Data: 81
Length: 1 Type: Global Tag: Logical Maximum Data: 7F
Length: 1 Type: Global Tag: Report Size Data: 08
Length: 1 Type: Global Tag: Report Count Data: 01
Length: 1 Type: Main Tag: Input Data,Variable,Relative,No Wrap,Linear,Preferred State,No Null Position,Non-volatile(Ignore for Input), Data: 06
Length: 0 Type: Main Tag: End Collection
Length: 0 Type: Main Tag: End Collection

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

同样,尝试之前的 Get_Report 方式的程序,得到的却是不停的输出的错误信息:

Setup packet error: 7
Mouse Poll Error: 7

没有办法直接了解这个错误编号的含义,最后只能用逻辑分析仪分析产生问题的原因:

usb71

可以看到当发送Get_Report之后,Device 会返回STALL 。

对比之前能够正常工作的 HP 鼠标,收到 Get_Report 后,会返回 ACK 还会继续通讯。

usb72

查看资料上说,返回STALL有可能是因为设备不支持该指令…… Windows的设备经常会出现这样的情况:可以正常工作,但是未必完整的 follow 工业标准。

上面的方法行不通,只能用中断方式来获取数据。我去掉了切换 Boot Protocol 的代码

/* Mouse communication via interrupt endpoint  */
/* Assumes EP1 as interrupt IN ep              */
#include "max3421e.h"
#include "usb.h"
 
#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;
 
void setup()
{
    Serial.begin( 115200 );
    Serial.println("Start");
    Max.powerOn();
    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 ) {  //poll the keyboard
        rcode = mouse1_poll();
        if( rcode ) {
          Serial.print("Mouse Poll Error: ");
          Serial.println( rcode, HEX );
        }//if( rcode...
    }//if( Usb.getUsbTaskState() == USB_STATE_RUNNING...
}
/* Initialize mouse */
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 );              //plug kbd.endpoint parameters to devtable
  /* 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.setIdle( DEVADDR, 0, 0, 0, tmpdata );
  if( rcode ) {
    Serial.print("Set Idle error. Return code : ");
    Serial.println( rcode, HEX );
    while(1);  //stop
  }
  
  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 );
  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;
  char buf[ 6 ] = { 0 };                          //mouse report buffer

  unsigned long int libuf[ sizeof(buf) ];
  unsigned long int x;
  unsigned long int y;

  
  /* poll mouse */
  rcode = Usb.inTransfer( DEVADDR, 1, sizeof(buf), buf, 1 );  //

    if( rcode ) {  //error
      if( rcode == 0x04 ) {  //NAK
        rcode = 0;
      }
      return( rcode );
    }
    /* print buffer */
    if( buf[ 1 ] & 0x01 ) {
      Serial.println("Button1 pressed ");
    }
    if( buf[ 1 ] & 0x02 ) {
      Serial.println("Button2 pressed ");
    }
    if( buf[ 1 ] & 0x04 ) {
      Serial.println("Button3 pressed ");
    }
    
    for (int i=0;i<sizeof(buf);i++) {
       libuf[i]=(buf[i]) & 0xff;
    }
    
    /*  
    Serial.print(libuf[0],HEX); Serial.print("  "); 
    Serial.print(libuf[1],HEX); Serial.print("  "); 
    Serial.print(libuf[2],HEX); Serial.print("  "); 
    Serial.print(libuf[3],HEX); Serial.print("  ");     
    Serial.print(libuf[4],HEX); Serial.print("  ");         
    Serial.print(libuf[5],HEX); Serial.print("  ");             
    Serial.println("");
    */
    
    Serial.print("X-axis: ");
    x=((libuf[3] & 0xF)<<8)+(libuf[2] & 0xFF );
    if (x & 0x800) {
      Serial.print("-");
      x = ((~x) & 0x7FF) +1;
    }
   
    Serial.print(x, HEX);  Serial.print("   ");
    
    Serial.print("Y-axis: ");    
    y=(((libuf[3]>>4) & 0xF) +((libuf[4] & 0xFF )<<4));
    if (y & 0x800) {
      Serial.print("-");
      y = ((~y) & 0x7FF) +1;
    }    
    Serial.print(y, HEX); Serial.print("   ");

    Serial.print("Wheel: ");    
    Serial.println(buf [5] & 0xFF, HEX);    

    return( rcode );
}

 

和前面那个程序相比,这个特别之处在于返回值多了 Report_ID ,对于我们处理数据来说,只是有效数据从 Buf[1] 开始,其他地方并无特别。

运行结果

usb73

完整代码下载

mousec

基本上,鼠标的玩法就是这些。可以用鼠标做很多好玩的东西。

Arduino 控制USB设备(6)解析USB鼠标的例子(上)

前面介绍了USB键盘的使用,这里介绍一下USB鼠标的调用。根据【参考1】的文章进行实验,这次我们的目标是:获得鼠标移动按键信息,串口输出之。

首先运行一下之前的取得描述符的工具抓取一下描述符:

Descriptor of HP Mouse
Start
Device addressed… Requesting device descriptor.
Device descriptor:

Descriptor Length: 12
USB version: 2.0
Class: 00 Use class information in the Interface Descriptor
Subclass: 00
Protocol: 00
Max.packet size: 08
Vendor ID: 046D
Product ID: C077
Revision ID: 6700
Mfg.string index: 01 Length: 18 Contents: Logitech
Prod.string index: 02 Length: 36 Contents: USB Optical Mouse
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: 31 98ma

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

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

HID report descriptor:

Length: 1 Type: Global Tag: Usage Page Generic Desktop Controls Data: 01
Length: 1 Type: Local Tag: Usage Data: 02
Length: 1 Type: Main Tag: Collection Application (mouse, keyboard) Data: 01
Length: 1 Type: Local Tag: Usage Data: 01
Length: 1 Type: Main Tag: Collection Physical (group of axes) Data: 00
Length: 1 Type: Global Tag: Usage Page Button Data: 09
Length: 1 Type: Local Tag: Usage Minimum Data: 01
Length: 1 Type: Local Tag: Usage Maximum Data: 08
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: 08
Length: 1 Type: Main Tag: Input Data,Variable,Absolute………
Length: 1 Type: Global Tag: Usage Page Generic Desktop Controls Data: 01
Length: 2 Type: Global Tag: Logical Minimum Data: 01 Data: F8
Length: 2 Type: Global Tag: Logical Maximum Data: FF Data: 07
Length: 1 Type: Global Tag: Report Size Data: 0C
Length: 1 Type: Global Tag: Report Count Data: 02
Length: 1 Type: Local Tag: Usage Data: 30
Length: 1 Type: Local Tag: Usage Data: 31
Length: 1 Type: Main Tag: Input Data,Variable,Relative………
Length: 1 Type: Global Tag: Logical Minimum Data: 81
Length: 1 Type: Global Tag: Logical Maximum Data: 7F
Length: 1 Type: Global Tag: Report Size Data: 08
Length: 1 Type: Global Tag: Report Count Data: 01
Length: 1 Type: Local Tag: Usage Data: 38
Length: 1 Type: Main Tag: Input Data,Variable,Relative………
Length: 1 Type: Global Tag: Usage Page Consumer Data: 0C
Length: 2 Type: Local Tag: Usage Data: 38 Data: 02
Length: 1 Type: Global Tag: Report Count Data: 01
Length: 1 Type: Main Tag: Input Data,Variable,Relative………
Length: 0 Type: Main Tag: End Collection
Length: 0 Type: Main Tag: End Collection

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

根据上面的结果,计算长度应该是1×8+Cx2+8×1=40 bits= 5 bytes ,修改【参考1】的代码如下:

#include "Max3421e.h"
#include "Usb.h"

 
#define DEVADDR 1
#define CONFVALUE 1
 
void setup();
void loop();
 
MAX3421E Max;
USB Usb;
 
void setup()
{
    Serial.begin( 115200 );
    Serial.println("Start");
    Max.powerOn();
    delay( 200 );
}
 
void loop()
{
 byte rcode;
    Max.Task();
    Usb.Task();
    if( Usb.getUsbTaskState() == USB_STATE_CONFIGURING ) {
        mouse0_init();
    }//if( Usb.getUsbTaskState() == USB_STATE_CONFIGURING...
    if( Usb.getUsbTaskState() == USB_STATE_RUNNING ) {  //poll the keyboard
        rcode = mouse0_poll();
        if( rcode ) {
          Serial.print("Mouse Poll Error: ");
          Serial.println( rcode, HEX );
        }//if( rcode...
    }//if( Usb.getUsbTaskState() == USB_STATE_RUNNING...
}
/* Initialize mouse */
void mouse0_init( void )
{
 byte rcode = 0;  //return code
  /**/
  Usb.setDevTableEntry( 1, Usb.getDevTableEntry( 0,0 ) );              //copy device 0 endpoint information to device 1
  /* 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...

  Usb.setUsbTaskState( USB_STATE_RUNNING );
  return;
}
/* Poll mouse using Get Report and print result */
byte mouse0_poll( void )
{
  byte rcode,i;
  char buf[ 4 ] = { 0 };      //mouse buffer
  static char old_buf[ 4 ] = { 0 };  //last poll
    /* poll mouse */
    rcode = Usb.getReport( DEVADDR, 0, 4, 0, 1, 0, buf );
    if( rcode ) {  //error
      return( rcode );
    }
    for( i = 0; i < 4; i++) {  //check for new information
      if( buf[ i ] != old_buf[ i ] ) { //new info in buffer
        break;
      }
    }
    if( i == 4 ) {
      return( 0 );  //all bytes are the same
    }
    /* print buffer */
    if( buf[ 0 ] & 0x01 ) {
      Serial.print("Button1 pressed ");
    }
    if( buf[ 0 ] & 0x02 ) {
      Serial.print("Button2 pressed ");
    }
    if( buf[ 0 ] & 0x04 ) {
      Serial.print("Button3 pressed ");
    }
    Serial.println("");
    Serial.print("X-axis: ");
    Serial.println( buf[ 1 ], DEC);
    Serial.print("Y-axis: ");
    Serial.println( buf[ 2 ], DEC);
    Serial.print("Wheel: ");
    Serial.println( buf[ 3 ], DEC);
    
    for( i = 0; i < 4; i++ ) {
      old_buf[ i ] = buf[ i ];  //copy buffer
    }
    Serial.println("");
    return( rcode );
}

 

运行结果

usb61

完整代码下载

mouse0

看起来按键和X Y都已经正确,但是 wheel 并不正常。再仔细研究代码,取出来的信息长度和描述符中的不一致。就是说,程序虽然能够运行也能够取到变化的值,但是解读有误。

再进一步研读 Descriptor
Length: 1 Type: Global Tag: Usage Page Generic Desktop Controls Data: 01
Length: 2 Type: Global Tag: Logical Minimum Data: 01 Data: F8 //最小 F801 = 1111 1000 0000 0001 = -2047
Length: 2 Type: Global Tag: Logical Maximum Data: FF Data: 07//最大 07FF = 0000 0111 1111 1111 = 2047
Length: 1 Type: Global Tag: Report Size Data: 0C //这里指出了上面数值的大小是 12位
Length: 1 Type: Global Tag: Report Count Data: 02
Length: 1 Type: Local Tag: Usage Data: 30 // X
Length: 1 Type: Local Tag: Usage Data: 31 // Y

应该是意味着2个12bits长度的数值,对应X Y 分配如下,其中 X[B] 和Y[B]应该是最高的符号位

usb62

根据这个改写程序,按照上面的表格取值再拼接起来,特别需要注意的是,我发现Arduino在不同尺寸的数据转换上似乎有一些bug,我只能把值放在 unsigned long int中然后才能进行拼接和输出的动作。

    Serial.print("X-axis: ");
    x=((libuf[2] & 0xF)<<8)+(libuf[1] & 0xFF );
    if (x & 0x800) {
      Serial.print("-");
      x = ((~x) & 0x7FF) +1;
    }
   
    Serial.println(x, HEX);
    
    Serial.print("Y-axis: ");    
    y=(((libuf[2]>>4) & 0xF) +((libuf[3] & 0xFF )<<4));
    if (y & 0x800) {
      Serial.print("-");
      y = ((~y) & 0x7FF) +1;
    }    
Serial.println(y, HEX);

 

最终,程序可以正常执行。

工作正常的程序如下:

#include "Max3421e.h"
#include "Usb.h"

 
#define DEVADDR 1
#define CONFVALUE 1
 
void setup();
void loop();
 
MAX3421E Max;
USB Usb;
 
void setup()
{
    Serial.begin( 115200 );
    Serial.println("Start");
    Max.powerOn();
    delay( 200 );
}
 
void loop()
{
 byte rcode;
    Max.Task();
    Usb.Task();
    if( Usb.getUsbTaskState() == USB_STATE_CONFIGURING ) {
        mouse0_init();
    }//if( Usb.getUsbTaskState() == USB_STATE_CONFIGURING...
    if( Usb.getUsbTaskState() == USB_STATE_RUNNING ) {  //poll the keyboard
        rcode = mouse0_poll();
        if( rcode ) {
          Serial.print("Mouse Poll Error: ");
          Serial.println( rcode, HEX );
        }//if( rcode...
    }//if( Usb.getUsbTaskState() == USB_STATE_RUNNING...
}
/* Initialize mouse */
void mouse0_init( void )
{
 byte rcode = 0;  //return code
  /**/
  Usb.setDevTableEntry( 1, Usb.getDevTableEntry( 0,0 ) );              //copy device 0 endpoint information to device 1
  /* 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...

  Usb.setUsbTaskState( USB_STATE_RUNNING );
  return;
}
/* Poll mouse using Get Report and print result */
byte mouse0_poll( void )
{
  byte rcode,i;
  char buf[ 5 ] = { 0 };      //mouse buffer
  static char old_buf[ sizeof(buf) ] = { 0 };  //last poll
  unsigned long int libuf[ sizeof(buf) ];
  unsigned long int x;
  unsigned long int y;
  
    /* poll mouse */
    rcode = Usb.getReport( DEVADDR, 0, sizeof(buf), 0, 1, 0, buf );
    if( rcode ) {  //error
      return( rcode );
    }
    for( i = 0; i < sizeof(buf); i++) {  //check for new information
      if( buf[ i ] != old_buf[ i ] ) { //new info in buffer
        break;
      }
    }
    if( i == sizeof(buf)) {
      return( 0 );  //all bytes are the same
    }
    /* print buffer */
    if( buf[ 0 ] & 0x01 ) {
      Serial.print("Button1 pressed ");
    }
    if( buf[ 0 ] & 0x02 ) {
      Serial.print("Button2 pressed ");
    }
    if( buf[ 0 ] & 0x04 ) {
      Serial.print("Button3 pressed ");
    }
    
    for( i = 0; i < sizeof(buf); i++ ) {
      old_buf[ i ] = buf[ i ];  //copy buffer
      libuf[ i] = buf[i];
    }

    //Serial.print(libuf[0],HEX); Serial.print("  "); 
    //Serial.print(libuf[1],HEX); Serial.print("  "); 
    //Serial.print(libuf[2],HEX); Serial.print("  "); 
    //Serial.print(libuf[3],HEX); Serial.print("  ");     
    //Serial.println("");
    
    Serial.print("X-axis: ");
    x=((libuf[2] & 0xF)<<8)+(libuf[1] & 0xFF );
    if (x & 0x800) {
      Serial.print("-");
      x = ((~x) & 0x7FF) +1;
    }
   
    Serial.println(x, HEX);
    
    Serial.print("Y-axis: ");    
    y=(((libuf[2]>>4) & 0xF) +((libuf[3] & 0xFF )<<4));
    if (y & 0x800) {
      Serial.print("-");
      y = ((~y) & 0x7FF) +1;
    }    
    Serial.println(y, HEX);

    Serial.print("Wheel: ");    
    Serial.println(buf [4] & 0xFF, HEX);    
    return( rcode );
}

 

完整代码下载:

mousea

补遗:在运行中,我还发现了一个奇怪的问题:当使用 Get_Report取得数据时,鼠标向一个方向移动,会出现最大值。意思是:比如,向右一直移动鼠标,输出达到 7FF 之后,继续右移,输出值会维持在 7FF 而不会变化。没有找到直接描述这个问题的资料,不过有一些类似的介绍【参考2】,上面提到USB鼠标在送出数据的时候,会有两种方式,一种是Relative ,一种是Absolute。差别在于,比如前者输出 (1,1),意思是告诉系统鼠标移动了(1,1);而后者如果输出 (1,1)则是告诉系统,鼠标移动到(1,1)。看起来,我这个鼠标描述符中提到的是Relative,但是实际输出是 Absolute。

Length: 1 Type: Global Tag: Report Size Data: 0C
Length: 1 Type: Global Tag: Report Count Data: 02
Length: 1 Type: Local Tag: Usage Data: 30
Length: 1 Type: Local Tag: Usage Data: 31
Length: 1 Type: Main Tag: Input Data,Variable,Relative………

于此可以形成对照的是 Wheel,每次滚动之后,很快会再次输出一个 0 的位置,下次再给出新的滚动值。
至于Windows 下,根据资料,系统会自动根据鼠标信息计算出一个大概范围方便使用(比如,屏幕是 1024×768,鼠标是Relative -100到+100的话,如果不做优化,从左上到右下需要做几次才行)。而我用的这个鼠标,声明自己是Relative,但是实际上发出Absolute,只是因为自己的范围很大(-7FF到+7FF),所以也不会有什么感觉。

上述只是猜测,是目前已知的最合理的解释而已。使用逻辑分析仪分析USB鼠标信息得到的结果相同。

逻辑分析仪抓取的结果,Windows使用 Interrupt 方式来和鼠标通讯

usb64

下面展示一下USB逻辑分析仪抓到的启动时的一些信息,首先是 Overview

image016

image005

image006
不清楚为什么VendorID 居然是罗技的,一种可能是因为这个是假的HP鼠标,乱写一个ID;另外的可能是HP 找罗技代工的。

image007

image008

可以看到,MaxPower确实是一个很古怪的数值:98ma

image009

image010

image011

image012

接下来是最重要的HID 描述符
image013

image014

image015

参考:
1.https://www.circuitsathome.com/communicating-arduino-with-hid-devices-part-1
2.http://www.microchip.com/forums/m482197.aspx MOUSE HID / Resolution problem

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键盘的例子

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

Step to UEFI (57) —– 只在一个有显示?

之前的很多实验都有一个奇怪的显现,开出了2个模拟窗口,只有一个上面有显示。琢磨了一下,猜测是因为只打开了一个设备上面的Protocol,于是用命令查看了一下有Graphics 的 Protocol,发现有三个(模拟环境下)

handle

之前我们的代码都是用 gBS->LocateProtocol 打开的,这个函数的意思是“是从内核中找出指定Protocol的第一个实例。”【参考1】

gBS->LocateProtocol(&GraphicsOutputProtocolGuid, NULL, (VOID **) &GraphicsOutput);

 

我们遇到问题就是【参考1】中提到的:“UEFI内核中某个Protocol的实例可能不止一个,例如每个硬盘及每个分区都有一个EFI_DISK_IO_PROTOCOL实例。LocateProtocol顺序搜索HANDLE链表,返回找到的第一个该Protocol的实例。我们可以用BootServices提供的其它函数处理HANDLE和Protocol。”

编写代码如下,枚举系统中的所有实例,然后逐个打开并且调用:

#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>



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;

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


int
EFIAPI
main (                                         
  IN int Argc,
  IN char **Argv
  )
{
    EFI_STATUS    Status;
    UINTN i;
    UINTN HandleIndex;	
	EFI_GRAPHICS_OUTPUT_BLT_PIXEL FillColor;
    UINTN HandleCount;
	EFI_HANDLE   *HandleBuffer;
    //
    // Search for the shell protocol
    //
    Status = gBS->LocateHandleBuffer(
	 ByProtocol,
      &GraphicsOutputProtocolGuid,
      NULL,
	  &HandleCount,
      &HandleBuffer
     );

    Print(L"[%d] \r\n",HandleCount);
	 
	for (HandleIndex=0; HandleIndex<HandleCount;HandleIndex++)
	{
		Status = gBS -> HandleProtocol (
							HandleBuffer[HandleIndex],
							&gEfiGraphicsOutputProtocolGuid,
							(void **)&GraphicsOutput);
		if (!(EFI_ERROR(Status))) {
			for (i=0;i<100;i++) {
				FillColor.Blue=rand() % 256;
				FillColor.Red=rand() % 256;
				FillColor.Green=rand() % 256;
	
				GraphicsOutput->Blt(GraphicsOutput, &FillColor, EfiBltVideoFill,
					0, 
					0 , 
					rand() % (GraphicsOutput->Mode->Info->HorizontalResolution-100), 
					rand() % (GraphicsOutput->Mode->Info->VerticalResolution-100), 
					100, 100, 0); 
				gBS->Stall(50000);
			} // For i
		}
		else {
		    Print(L"Error @[%d] \r\n",HandleCount);
		}
	} // For HandleIndex		

  return EFI_SUCCESS;
  
}

 

运行结果:

gfxtest6

可以看到现在两个窗口都会有显示。

完整代码下载:

GFXTest6

参考:

1.http://www.cppblog.com/djxzh/archive/2012/03/06/167106.html UEFI 实战(4) protocol

Step to UEFI (56) —– 在屏幕上显示一幅BMP

前面介绍了如何截图,如果把这个过程反过来,先是读取BMP文件到内存,然后重新排列RGB,再用Blt输出到Video上就是显示BMP的过程。

根据这个原理编写程序如下:

#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()



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




static EFI_GUID ESPGuid = { 0xc12a7328, 0xf81f, 0x11d2, 
				{ 0xba, 0x4b, 0x00, 0xa0, 0xc9, 0x3e, 0xc9, 0x3b } };

static EFI_STATUS egFindESP(OUT EFI_FILE_PROTOCOL *RootDir)
{
    EFI_STATUS          Status;

   

    return Status;
}

//
// 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;
}

EFI_STATUS LoadFile(  IN CHAR16 *FileName,
                      IN UINT8 **FileData, 
					  IN UINTN *FileDataLength)
{
    EFI_STATUS          Status;
    EFI_FILE_HANDLE     FileHandle;
    EFI_FILE_PROTOCOL   *Root;
	EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFileSystem;
	EFI_FILE_INFO     *FileInfo = NULL;
	
    Status = gBS->LocateProtocol(
						&gEfiSimpleFileSystemProtocolGuid, 
						NULL,
						(VOID **)&SimpleFileSystem);
    if (EFI_ERROR(Status)) {
		    Print(L"Cannot find EFI_SIMPLE_FILE_SYSTEM_PROTOCOL \r\n");
            return Status;	
	}

	Status = SimpleFileSystem->OpenVolume(SimpleFileSystem, &Root);
    if (EFI_ERROR(Status)) {
		    Print(L"OpenVolume error \r\n");
            return Status;	
	}

    Status = Root->Open(
							Root, 
							&FileHandle, 
							FileName,
							EFI_FILE_MODE_READ , 
							0);
    if (EFI_ERROR(Status))
	  {
        Print(L"Error Open NULL\n");
        return Status;
	  }	

	FileInfo = ShellGetFileInfo( (SHELL_FILE_HANDLE)FileHandle);	
    Print(L"Filesize [%ld] bytes\n",FileInfo-> FileSize);
	*FileDataLength=(UINTN) FileInfo->FileSize;
	
	*FileData  = AllocatePool((UINTN) FileInfo->FileSize);
    Status = FileHandle->Read(FileHandle, FileDataLength, *FileData );
    if (EFI_ERROR(Status)) {
		Print(L"Loading file error! \n");
	}
	Print(L"Get file size [%d]!\n",*FileDataLength);
    

    FileHandle->Close(FileHandle);
    
    return Status;
}


//
// 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;	
    UINT8           *FileData=NULL;
    UINTN           FileDataLength;

	
    Status = gBS->LocateProtocol(&GraphicsOutputProtocolGuid, 
						NULL, (VOID **) &GraphicsOutput);
    if (EFI_ERROR(Status)) {
        GraphicsOutput = NULL;
		Print(L"Loading Graphics_Output_Protocol error!\n");
		return EFI_SUCCESS;
	}	
    
	Status=LoadFile(L"test.bmp",&FileData,&FileDataLength);
    Print(L"Get file size [%d]!\n",FileDataLength);	
	
    Image=egDecodeBMP(FileData, FileDataLength, 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 , 
				0x20, 
				0x0, 
				Image->Width, 
				Image->Height, 0); 					
	
  return EFI_SUCCESS;
  
}

 

运行结果:

showbmp

完整代码例子:

ShowBMP

本文同样参考了之前提到的 https://github.com/chengs/UEFI 的代码,再次感谢!

If you are using Delphi TComPort VCL ……

If you are using Delphi TComPort VCL for some Arduino programs. Please set FlowControl -> ControlDTR to dtrEnable. Otherwise, you will get nothing from Serial Port.

comport

It costed me one afternoon for this issue. I worked with a Arduino Pro Micro (Leonardo). The Serial Monitor of IDE worked well. Putty worked well and ‘Serial Port Utility’ worked well. Arduino Uno was tried. It works well……. Only my Delphi program couldn’t get anything from the Serial Port. At last, I noticed the example program of TComPort worked well. But if I deleted this VCL and added again, it would fail. At last I found this Properties.

I don’t know why. All the documents said Leonardo didn’t use DTR pin.

If you have any suggestion, please let me know. Thanks a lot.

Arduino String的问题

比如我想从串口得到一个输入,然后再输出到串口上,然后写下面的程序

String comdata="";
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);

}

void loop() {
// put your main code here, to run repeatedly:
while (Serial.available() &gt; 0)
{
comdata += Serial.read();
delay(2);
}
if (comdata.length()&gt;1) {Serial.println(comdata); comdata="";}

}

 

但是惊奇的发现,结果是下面这样。”1″ 对应为 “49”两个字符;“2”对应为 “50”两个字符………

dd

如果想得到想要的结果,需要做一个强制转换

String comdata="";
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);

}

void loop() {
// put your main code here, to run repeatedly:
while (Serial.available() &gt; 0)
{

comdata +=(char)Serial.read();
delay(2);
}
if (comdata.length()&gt;1) {Serial.println(comdata); comdata="";}

}

 

这样的结果就符合预期

ee

感谢极客工坊Super169这位朋友,他给出的解释:

String class 的特性, 當 operator “+” 之後是 numeric type, 會自動把數值先轉成 字符再加上去.
“1” 的數值就是 49, 由於 Serial.read() 的結果是 int type, 當你用 comdata += Serial.read() 時, comdata 是 String, Serial.read() 是 int, 就會執行 String “+” int 的 operation. 當你輸入第一個字 “1” 時, 就會先把 “1” (int value 為 ASCII 值, 即 49) 轉成 “49”, 然後再連接上去. 之後都是一樣了.

當你加上 (char) 的轉換後, comdata += (char)Serial.read(), 就變成是 String “+” char 的 operation, 這時就不需要任何轉換而直接連上去了.

这个问题的原文可以在 http://www.geek-workshop.com/thread-14981-1-1.html 看到