Step to UEFI (110)在 Shell 下面使用 Unicode 的制表符

在DOS时代,可以用Ascii制表符做出好看的界面。

tabm

同样的 Unicode 中也有制表符【参考1】

tabn

下面的代码就是枚举输出UEFI 下面定义的全部制表符。

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


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

int
EFIAPI
main (
  IN int Argc,
  IN CHAR16 **Argv
  )
{
	CHAR16 ChrSide[2] = {0,0};
	
ChrSide[0]=BOXDRAW_HORIZONTAL;
Print(L"BOXDRAW_HORIZONTAL [%s]\n",ChrSide);                 
ChrSide[0]=BOXDRAW_VERTICAL;
Print(L"BOXDRAW_VERTICAL [%s]\n",ChrSide);                   
ChrSide[0]=BOXDRAW_DOWN_RIGHT;
Print(L"BOXDRAW_DOWN_RIGHT [%s]\n",ChrSide);                 
ChrSide[0]=BOXDRAW_DOWN_LEFT;
Print(L"BOXDRAW_DOWN_LEFT [%s]\n",ChrSide);                  
ChrSide[0]=BOXDRAW_UP_RIGHT;
Print(L"BOXDRAW_UP_RIGHT [%s]\n",ChrSide);                   
ChrSide[0]=BOXDRAW_UP_LEFT;
Print(L"BOXDRAW_UP_LEFT [%s]\n",ChrSide);                    
ChrSide[0]=BOXDRAW_VERTICAL_RIGHT;
Print(L"BOXDRAW_VERTICAL_RIGHT [%s]\n",ChrSide);             
ChrSide[0]=BOXDRAW_VERTICAL_LEFT;
Print(L"BOXDRAW_VERTICAL_LEFT [%s]\n",ChrSide);              
ChrSide[0]=BOXDRAW_DOWN_HORIZONTAL;
Print(L"BOXDRAW_DOWN_HORIZONTAL [%s]\n",ChrSide);            
ChrSide[0]=BOXDRAW_UP_HORIZONTAL;
Print(L"BOXDRAW_UP_HORIZONTAL [%s]\n",ChrSide);              
ChrSide[0]=BOXDRAW_VERTICAL_HORIZONTAL;
Print(L"BOXDRAW_VERTICAL_HORIZONTAL [%s]\n",ChrSide);        
ChrSide[0]=BOXDRAW_DOUBLE_HORIZONTAL;
Print(L"BOXDRAW_DOUBLE_HORIZONTAL [%s]\n",ChrSide);          
ChrSide[0]=BOXDRAW_DOUBLE_VERTICAL;
Print(L"BOXDRAW_DOUBLE_VERTICAL [%s]\n",ChrSide);            
ChrSide[0]=BOXDRAW_DOWN_RIGHT_DOUBLE;
Print(L"BOXDRAW_DOWN_RIGHT_DOUBLE [%s]\n",ChrSide);          
ChrSide[0]=BOXDRAW_DOWN_DOUBLE_RIGHT;
Print(L"BOXDRAW_DOWN_DOUBLE_RIGHT [%s]\n",ChrSide);          
ChrSide[0]=BOXDRAW_DOUBLE_DOWN_RIGHT;
Print(L"BOXDRAW_DOUBLE_DOWN_RIGHT [%s]\n",ChrSide);          
ChrSide[0]=BOXDRAW_DOWN_LEFT_DOUBLE;
Print(L"BOXDRAW_DOWN_LEFT_DOUBLE [%s]\n",ChrSide);           
ChrSide[0]=BOXDRAW_DOWN_DOUBLE_LEFT;
Print(L"BOXDRAW_DOWN_DOUBLE_LEFT [%s]\n",ChrSide);           
ChrSide[0]=BOXDRAW_DOUBLE_DOWN_LEFT;
Print(L"BOXDRAW_DOUBLE_DOWN_LEFT [%s]\n",ChrSide);           
ChrSide[0]=BOXDRAW_UP_RIGHT_DOUBLE;
Print(L"BOXDRAW_UP_RIGHT_DOUBLE [%s]\n",ChrSide);            
ChrSide[0]=BOXDRAW_UP_DOUBLE_RIGHT;
Print(L"BOXDRAW_UP_DOUBLE_RIGHT [%s]\n",ChrSide);            
ChrSide[0]=BOXDRAW_DOUBLE_UP_RIGHT;
Print(L"BOXDRAW_DOUBLE_UP_RIGHT [%s]\n",ChrSide);            
ChrSide[0]=BOXDRAW_UP_LEFT_DOUBLE;
Print(L"BOXDRAW_UP_LEFT_DOUBLE [%s]\n",ChrSide);             
ChrSide[0]=BOXDRAW_UP_DOUBLE_LEFT;
Print(L"BOXDRAW_UP_DOUBLE_LEFT [%s]\n",ChrSide);             
ChrSide[0]=BOXDRAW_DOUBLE_UP_LEFT;
Print(L"BOXDRAW_DOUBLE_UP_LEFT [%s]\n",ChrSide);             
ChrSide[0]=BOXDRAW_VERTICAL_RIGHT_DOUBLE;
Print(L"BOXDRAW_VERTICAL_RIGHT_DOUBLE [%s]\n",ChrSide);      
ChrSide[0]=BOXDRAW_VERTICAL_DOUBLE_RIGHT;
Print(L"BOXDRAW_VERTICAL_DOUBLE_RIGHT [%s]\n",ChrSide);      
ChrSide[0]=BOXDRAW_DOUBLE_VERTICAL_RIGHT;
Print(L"BOXDRAW_DOUBLE_VERTICAL_RIGHT [%s]\n",ChrSide);      
ChrSide[0]=BOXDRAW_VERTICAL_LEFT_DOUBLE;
Print(L"BOXDRAW_VERTICAL_LEFT_DOUBLE [%s]\n",ChrSide);       
ChrSide[0]=BOXDRAW_VERTICAL_DOUBLE_LEFT;
Print(L"BOXDRAW_VERTICAL_DOUBLE_LEFT [%s]\n",ChrSide);       
ChrSide[0]=BOXDRAW_DOUBLE_VERTICAL_LEFT;
Print(L"BOXDRAW_DOUBLE_VERTICAL_LEFT [%s]\n",ChrSide);       
ChrSide[0]=BOXDRAW_DOWN_HORIZONTAL_DOUBLE;
Print(L"BOXDRAW_DOWN_HORIZONTAL_DOUBLE [%s]\n",ChrSide);     
ChrSide[0]=BOXDRAW_DOWN_DOUBLE_HORIZONTAL;
Print(L"BOXDRAW_DOWN_DOUBLE_HORIZONTAL [%s]\n",ChrSide);     
ChrSide[0]=BOXDRAW_DOUBLE_DOWN_HORIZONTAL;
Print(L"BOXDRAW_DOUBLE_DOWN_HORIZONTAL [%s]\n",ChrSide);     
ChrSide[0]=BOXDRAW_UP_HORIZONTAL_DOUBLE;
Print(L"BOXDRAW_UP_HORIZONTAL_DOUBLE [%s]\n",ChrSide);       
ChrSide[0]=BOXDRAW_UP_DOUBLE_HORIZONTAL;
Print(L"BOXDRAW_UP_DOUBLE_HORIZONTAL [%s]\n",ChrSide);       
ChrSide[0]=BOXDRAW_DOUBLE_UP_HORIZONTAL;
Print(L"BOXDRAW_DOUBLE_UP_HORIZONTAL [%s]\n",ChrSide);       
ChrSide[0]=BOXDRAW_VERTICAL_HORIZONTAL_DOUBLE;
Print(L"BOXDRAW_VERTICAL_HORIZONTAL_DOUBLE [%s]\n",ChrSide); 
ChrSide[0]=BOXDRAW_VERTICAL_DOUBLE_HORIZONTAL;
Print(L"BOXDRAW_VERTICAL_DOUBLE_HORIZONTAL [%s]\n",ChrSide); 
ChrSide[0]=BOXDRAW_DOUBLE_VERTICAL_HORIZONTAL;
Print(L"BOXDRAW_DOUBLE_VERTICAL_HORIZONTAL [%s]\n",ChrSide); 
ChrSide[0]=BLOCKELEMENT_FULL_BLOCK;
Print(L"BLOCKELEMENT_FULL_BLOCK [%s]\n",ChrSide);            
ChrSide[0]=BLOCKELEMENT_LIGHT_SHADE;
Print(L"BLOCKELEMENT_LIGHT_SHADE [%s]\n",ChrSide);           
ChrSide[0]=GEOMETRICSHAPE_UP_TRIANGLE;
Print(L"GEOMETRICSHAPE_UP_TRIANGLE [%s]\n",ChrSide);         
ChrSide[0]=GEOMETRICSHAPE_RIGHT_TRIANGLE;
Print(L"GEOMETRICSHAPE_RIGHT_TRIANGLE [%s]\n",ChrSide);      
ChrSide[0]=GEOMETRICSHAPE_DOWN_TRIANGLE;
Print(L"GEOMETRICSHAPE_DOWN_TRIANGLE [%s]\n",ChrSide);       
ChrSide[0]=GEOMETRICSHAPE_LEFT_TRIANGLE;
Print(L"GEOMETRICSHAPE_LEFT_TRIANGLE [%s]\n",ChrSide);       
ChrSide[0]=ARROW_LEFT;
Print(L"ARROW_LEFT [%s]\n",ChrSide);                         
ChrSide[0]=ARROW_UP;
Print(L"ARROW_UP [%s]\n",ChrSide);                           
ChrSide[0]=ARROW_RIGHT;
Print(L"ARROW_RIGHT [%s]\n",ChrSide);                        
ChrSide[0]=ARROW_DOWN;
Print(L"ARROW_DOWN [%s]\n",ChrSide);                         

  return EFI_SUCCESS;
}

 

上述代码运行结果:

tab4

tab3

tab2

tab1

完整的代码下载:

tabtest

使用上面的代码,绘制的一个方框:

tab5

参考:
1. https://en.wikipedia.org/wiki/Box-drawing_character

给Arduino 设计一个序列号

之前看到很多朋友询问过如何实现“序列号”的功能。就是对于每一个 Arduino 都有独立的编码,这样可以实现一些身份识别之类的功能。
最容易实现的就是每次都修改程序中的定义。但是这样用起来耗时很长,并且必须有源程序才行,琢磨了一下,提出一种软件的实现方法,欢迎大家一起讨论。
本文是纯软件的序列号实现,因此通用性会很好,这里使用 Arduino Uno 来实现。
第一个问题是:如何在命令行下进行刷写。首先将IDE中的上传调试功能打开

image001

这样你可以看到IDE具体使用了什么上传命令(Arduino IDE 只是一个外壳,具体的编译和上传是由其他程序实现的。IDE只是调用和重定向了运行的结果)

image002

具体的话,上面图片中的烧写代码如下:

C:\Users\labz\Documents\arduino\hardware\tools\avr/bin/avrdude -CC:\Users\ labz \Documents\arduino\hardware\tools\avr/etc/avrdude.conf -v -patmega328p -carduino -PCOM18 -b115200 -D -Uflash:w:C:\Users\ labz \AppData\Local\Temp\build3ee88a1b99cef46fdbee27900bbb7d73.tmp/Blink.ino.hex:i

知道了这一行命令,我们可以直接在CMD窗口中输入这个命令从而完成上传的动作。
第二步,直接修改生成的 HEX文件。这里制作一个带有序列号的DEMO。我们定义程序的序列号为 20170101,从串口输出这个编号以便检查,代码如下:

String sign="LABZ";
String number="20170101";

// the setup function runs once when you press reset or power the board
void setup() {
  Serial.begin(9600);
  // initialize digital pin 13 as an output.
  pinMode(13, OUTPUT);
  Serial.print(number);
}

// the loop function runs over and over again forever
void loop() {
  digitalWrite(13, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(2000);              // wait for a second
  digitalWrite(13, LOW);    // turn the LED off by making the voltage LOW
  delay(1000);              // wait for a second
}

 

运行结果如下
image003

我们的目标就是直接用工具修改这个序列号,然后上传到 Arduino 上。同样是根据前面的命令行,我们可以知道上传的HEX文件位置:
image004

用文本编辑软件即可打开这个文件,搜索 4C 41 42 5A (ASCII:LABZ, 这就是为什么我们要在代码中定义String Sign,它能帮助我们定位后面的序列号的位置。其实不用这个作为定位手段直接搜索代码中定义的序列号也是可以的)。

仔细查看,后面的数据 32 30 31 37 30 36 就是我们的序列号 “2017”。之后又遇到的问题是,这个HEX文件是有简单的格式的。根据【参考1】,我们可以得知每行最后一个数值是这一行的校验和。例如

:100CE400FB000F019B014C41425A00323031373036
image005

其中的最后一个 36 是校验和计算方法如下:

10+0C+E4+00+FB+00+0F+01+9B+01+4C+41+42+5A+00+32+30+31+37+30=0x4CA 校验和的意思是加上一个值之后最后2位会变成00,0xCA+0x36=0x100

假设,我们的目标是将序列号修改为 19731023 = 30 39 37 33 31 30 32 33

经过分析,我们修改如下
:100CE400FB000F019B014C41425A0031393733312B
:040CF4003032330067
其中最后的校验和是通过前面的数值计算得到的。当然,更简单的办法是根据第一步,直接烧写,烧录程序会提醒你的校验和不匹配。例如,我使用错误的检验和,0x36 在烧写的时候收到提示,当前计算结果为 0x2B:

image006

修改完成之后,再次查看,序列号就是我新定义的了。
image007

可以看出,使用手工修改还是比较麻烦的,但是掌握了方法,编写自动化的工具也就是分分钟的事情了。
参考:
1. http://www.manysj.com/thread-13501-1-1.html HEX文件格式详解

HSTI 的一个问题

这一切的起因是 MS WHQL测试的要求,为了确保 Connected Standby 功能,需要验证 secure 方面的设定(我目前还很难想象二者之间的关系,也许是在一睡一醒之间会放松警惕,所以MS要求我们特别注意?)

这个测试被称作System.Fundamentals.Firmware.CS.UEFISecureBoot.Provisioning 【参考1】。

为了完成这个测试 MS (我相信这种事情肯定不止MS一个人干的)引入新功能: HSTI Hardware Security Test Interface。

关于这个功能,在【参考2】有如下解释:
“HSTI helps avoid misconfiguration of security features on devices running Windows. Thus, HSTI provides better assurance of compliance with Windows Hardware Security Requirements. HSTI aims to simplify the interface for designing tests to ensure compliance, reducing the effort required to comply with Windows Hardware Security Requirements. The results of HSTI tests will be consumed by Windows Certification Tests and can be used to verify that devices have been properly configured to enable supported security features. These tests may be used to identify unsecure engineering devices in the field; for example, engineering devices which may contain unsecure test keys. The results of these tests may be used by the Windows operating system to display a watermark (or similar indicator) on unsecured devices. The IHV will develop reference security designs for their platforms that comply with the Windows Compatibility Requirements. In addition, IHVs and IBVs will also implement programmatic tests that verify proper enablement of the reference security implementations and report the results via the Hardware Security Test Interface. These tests are delivered to OEMs & ODMs as compiled modules (not source) and should work without modification. If an OEM/ODM deviates from reference security designs, these test modules may report failures, and the OEM/ODM will need to contact Microsoft to review the modifications and implement an additional HSTI instance that reports these exceptions. OEMs should be able to leverage these security modules with no modification required by following the reference design and documentation. OEMs who wish to add additional security modules, or modify the behavior of any security module, must undergo a design review with Microsoft. Silicon suppliers and IBVs who support Connected Standby systems must implement the platform independent interfaces for querying the respective hardware and firmware security states of their reference platforms. These implementations must be delivered as compiled modules. It is recommended that these modules be signed, and that a signature check is performed when they are run. The purpose is to query the hardware and firmware designs and states to report proper security provisioning of the platform. If an OEM wants to provide an alternate implementation of HSTI tested security features the OEM may provide additional tests. OEM security checks must at least fully cover one IHV or IBV security test. Before use, OEMs must submit to a design review by Microsoft and are subject to the same Documentation and Tool disclosure requirements as other HSTI test providers. Upon approval from Microsoft, the OEM may include security tests that extend upon the IHV and IBV tests. Note that OEM attestation is not required as part of the HSTI design. HSTI is not a list of requirements for OEMs; it is an interface to guarantee effective programmatic security testing of firmware, hardware, and configuration parameters. Silicon and firmware suppliers should make available to Microsoft all necessary security-related reference documentation and tools that they provide to OEMs. This documentation and tools should be made available no later than they are provided to Windows OEMs. This should include, but is not limited to, all documentation and tools related to fusing, installing and updating firmware, firmware and boot recovery, hardware diagnostics, firmware diagnostics, & boot diagnostics. This documentation and tools provided should be fully sufficient to perform HSTI checks in a lab environment.”

相信各位和我一样,看完了之后依然一头雾水。

根据我的理解,具体动作如下:BIOS 自己检查安全项目,将结果存放在内存中 HSTI Table,这也是双方协商好确定的 Windows 接口,通过它,Windows可以得知当前系统上的安全设定,比如Boot Guard 是否打开,SPI 是否已经设置保护等等。对于WHQL 来说只是读取这个Table而已。

实例:我拿到的测试报告有下面的Fail项目:

Error 0x00060002 Platform Security Specification – CPU Security Configuration – Pre-production silicon in use

查看代码,是可以在你的 codebase 中找到这个字符串的定义的,比如:

#define LABZ_ERROR_CODE_2 L”0x00060002″
#define LABZ_ERROR_STRING_2 L” Pre-production silicon in use\r\n”

然后再检查引用这个字符串的代码:

DEBUG ((DEBUG_INFO, ” 2. Sample Part \n”));

if ((ProcessorReadMsr64 (CpuX, MSR_LABZ_INFO) & BIT21) != 0) {
DEBUG ((DEBUG_INFO, “Fail: This is a sample part\n”));
HstiErrorString = BuildHstiErrorString (LABZ_ERROR_CODE_2 ,HSTI_CPU_SECURITY_CONFIGURATION, LABZ_ERROR_STRING_2 );
Result = FALSE;
FreePool (HstiErrorString);
}

就是说,BIOS 根据ProcessorReadMsr64 (CpuX, MSR_LABZ_INFO) & BIT21!=0这个条件判断当前系统是否为 Sample。如果是的话,就将这个ERROR 记录下来存放在HSTI 中。

原理就是这样,如果单纯的为了通过测试,可以去修改BIOS,修改自检的动作。比如,修改上面的条件,让BIOS不去记录这个错误从而通过WHQL。特别注意,一个错误可能是很多不同条件检查出来的结果。比如,检查 EC access, CSME access 的时候都会报告下面这个错误

Error 0x0001000A Platform Security Specification – SPI Flash Configuration – SPI Region Access Rights Invalid

下面再介绍如何检查修改是否生效:

1. 最最稳妥的就是拿去重做WHQL测试;
2. 如果细心的话,会注意到上面的代码是有Debug信息输出的,可以对照UART输出的DEBUG信息检查,如果修改后,如果没有这个信息,那么就是正确的了。推荐使用这个方法,查看Log对于定位错误的位置非常有帮助;
3. 需要检查你的codebase是否为最新版,很多检查寄存器的值并非不正确,只是你手中的版本没有包含这个值而已;
4. 因为HSTI是UEFI 定义的结构,还可以在UEFI 下面进行读取。这里推荐jyao1 编写的一个工具【参考3】,能够在Shell下展示这个结构体的信息。我重新在UDK2015上编译了一次,非常好用,这里送上代码和X64的EFI。
5. 如果有问题,直接问Intel。 Insyde 对此不清楚。

上面提到的完整代码和X64的EFI 程序下载 hstiwsmtdump

就是这样,祝好运。

参考:
1. https://msdn.microsoft.com/en-us/library/windows/hardware/mt712332.aspx
2. https://firmwaresecurity.com/2015/08/03/microsoft-windows-hsti-hardware-security-test-interface/ 这里提到的出处是 MSDN,但是原始界面已经不复存在了
3. https://github.com/jyao1/EdkiiShellTool/tree/master/EdkiiShellToolPkg

特别声明:本文是一篇小说,不可作为技术指导以及参考,非专业人士请不要用来检测使用的机器。

Arduino I2C GPIO模块

Arduino Uno 大概是用途最广泛的型号了,美中不足的是它的GPIO 有限,这里介绍一个GPIO的扩展模块。基于 PCA9555芯片的 GPIO模块。
image002
简单的说,这个模块是将I2C转换为 GPIO,一共支持16个GPIO。板子上通过跳线的方式可以选择当前的地址0x20-0x27一共8个,理论上可以级联出来8(个模块)*16(个GPIO)=128个GPIO。

image004
特地我去查了一下电气特性【参考1】:
电源支持2.3到5.5v(正常工作范围),IO 输入5V,支持每一个输出最大50mA,输入时每个最大20mA。电源供电最大输入 160mA(这意味着不能同时有3以上的 GPIO输出50mA,在使用的时候一定要特别注意,最好把它当作只能提供电平的控制模块)。
简单实验这个模块,用4个LED,负极通过一个1K电阻接地,虽然这样会让LED看起来很暗,但是我能够确定不会有过流的问题。然后用扩展版上的 P0.6,P0.7 ,P1.6和P1.7连接LED正极进行供电控制。

运行的代码来自 DFRobot【参考2】(看起来他们在很久之前做过一块类似的板子,但是目前已经停产了)。当I2C地址选择三个Pin都为0时,模块地址是0x20。另外,I2C访问部分,我修改为Wire.write 之前的Wire.send 函数已经取消了。

/****************************************************************************** 
Test Program for the 12C PCA9555 Board part number DFR0013 IIC TO GPIO module from dfrobot.com
16 outputs that I used to drive this relay board made in Bulgaria
http://www.denkovi.com/product/21/16-relay-board-for-your-pic-avr-project-12v.html
it's a great little expansion board that can be used to drive LEDs or anything you want.
made by peter@testelectronics.com
January 07th 2011
My biggest problem was figuring out the I2C address of the PCA9555.
If there are no jumpers the address is 1 0 0 '1 1 1'
Jumpers make the address 1 0 0 '0 0 0'. This is opposite of what I expected.
******************************************************************************/ 

#include <Wire.h> 

//  with no jumpers the full address is   1 0 0 1 1 1    1 0 0 A2 A1 A0  0x27 is the default address for the DFR0013 board with no jumpers.
#define PCA9555 0x20 // 0x27 is default address for the DFR0013 board with no jumpers.
                     // 0x20 is address for the DFR0013 board with all jumpers.
// COMMAND BYTE TO REGISTER RELATIONSHIP FROM PCA9555 DATA SHEET
// At reset, the device's ports are inputs with a high value resistor pull-ups to VDD
// If relays turning on during power up are a problem. Add a pull down resistor to each relay transistor base.

#define IN_P0 0x00 // Read Input port0
#define IN_P1 0x01 // Read Input port1
#define OUT_P0 0x02 // Write Output port0
#define OUT_P1 0x03 // Write Output port1
#define INV_P0 0x04 // Input Port Polarity Inversion port0 if B11111111 is written input polarity is inverted
#define INV_P1 0x05 // Input Port Polarity Inversion port1 if B11111111 is written input polarity is inverted
#define CONFIG_P0 0x06 // Configuration port0 configures the direction of the I/O pins 0 is output 1 is input
#define CONFIG_P1 0x07 // Configuration port1 configures the direction of the I/O pins 0 is output 1 is input

#define PAUSE 200

void setup()
{
  Wire.begin(PCA9555); // join i2c bus (address optional for master) tried to get working
  write_io (CONFIG_P0, B00000000); //defines all pins on Port0 are outputs
  write_io (CONFIG_P1, B00000000); //defines all pins on Port1 are outputs  
  write_io (OUT_P0, B00000000); //clears all relays
  write_io (OUT_P1, B00000000); //clears all relays
  delay (PAUSE);
}


void loop()
{
  write_io (OUT_P0, B00000001);
  delay (PAUSE);
  write_io (OUT_P0, B00000010);
  delay (PAUSE);
  write_io (OUT_P0, B00000100);
  delay (PAUSE);
  write_io (OUT_P0, B00001000);
  delay (PAUSE);
  write_io (OUT_P0, B00010000);
  delay (PAUSE);
  write_io (OUT_P0, B00100000);
  delay (PAUSE);
  write_io (OUT_P0, B01000000);
  delay (PAUSE);
  write_io (OUT_P0, B10000000);
  delay (PAUSE);
  write_io (OUT_P0, B00000000);
 
  
  write_io (OUT_P1, B00000001);
  delay (PAUSE);
  write_io (OUT_P1, B00000010);
  delay (PAUSE);
  write_io (OUT_P1, B00000100);
  delay (PAUSE);
  write_io (OUT_P1, B00001000);
  delay (PAUSE);
  write_io (OUT_P1, B00010000);
  delay (PAUSE);
  write_io (OUT_P1, B00100000);
  delay (PAUSE);
  write_io (OUT_P1, B01000000);
  delay (PAUSE);
  write_io (OUT_P1, B10000000);
  delay (PAUSE);
  write_io (OUT_P1, B00000000);
  
}
 
 
 void write_io(int command, int value)
{
  Wire.beginTransmission(PCA9555);
  Wire.write(command),Wire.write(value);
  Wire.endTransmission();
}

 

最终工作的样子:
image006

最后补充一些:
1. 这个模块在淘宝价格20左右,DFRobot新的替代品的价格在65左右;
2. 模块做工不错,看起来挺舒服,应该是机器焊接的(老婆偶然看到,竟然觉得挺精致)
3. Github 上有人提供了一个库,有兴趣的朋友可以试试。如果能够简化编程,都值得实验 https://github.com/nicoverduin/PCA9555

参考:
1. http://www.nxp.com/documents/data_sheet/PCA9555.pdf
2. http://wiki.dfrobot.com.cn/index.php/(SKU:DFR0013)IIC_TO_GPIO%E6%A8%A1%E5%9D%97

Step to UEFI (108)引用Protocol GUID的方法

每一个 Protocol 都有一个GUID,这是Protocol 正式的名称。编写一个Application,标准的做法是:

1. 在Application 的inf 文件, [Packages] 节中引用 MdePkg.dec , 实际上你用到的UEFI 标准的 Protocol GUID 在这个文件中都有定义;[Protocols] 节中定义你用到的 ProtocolGuid;

2.在Application的 c 文件中 extern 一下代码中用到的 GUID;

当然,这样的做法看起来不如直接在代码中使用 EFI_GUID 定义直接易懂,之前我写的很多代码为了省事采用的就是在 Application中重新定义的方法。

下面列出常用的 Protocol 的定义:

EFI_GUID gEfiLoadedImageProtocolGuid = {0x5B1B31A1, 0x9562,0x11D2,
        { 0x8E, 0x3F,0x0, 0xA0, 0xC9, 0x69,0x72, 0x3B } };
EFI_GUID gEfiLoadedImageDevicePathProtocolGuid = {0xBC62157E, 0x3E33,0x4FEC,
        { 0x99, 0x20,0x2D, 0x3B, 0x36, 0xD7,0x50, 0xDF } };
EFI_GUID gEfiSimpleTextOutProtocolGuid = {0x387477C2, 0x69C7,0x11D2,
        { 0x8E, 0x39,0x0, 0xA0, 0xC9, 0x69,0x72, 0x3B } };
EFI_GUID gEfiDevicePathProtocolGuid = {0x9576E91, 0x6D3F,0x11D2,
        { 0x8E, 0x39,0x0, 0xA0, 0xC9, 0x69,0x72, 0x3B } };
EFI_GUID gEfiSimplePointerProtocolGuid = {0x31878C87, 0xB75,0x11D5,
        { 0x9A, 0x4F,0x0, 0x90, 0x27, 0x3F,0xC1, 0x4D } };
EFI_GUID gEfiAbsolutePointerProtocolGuid = {0x8D59D32B, 0xC655,0x4AE9,
        { 0x9B, 0x15,0xF2, 0x59, 0x4, 0x99,0x2A, 0x43 } };
EFI_GUID gEfiSerialIoProtocolGuid = {0xBB25CF6F, 0xF1D4,0x11D2,
        { 0x9A, 0xC,0x0, 0x90, 0x27, 0x3F,0xC1, 0xFD } };
EFI_GUID gEfiEdidDiscoveredProtocolGuid = {0x1C0C34F6, 0xD380,0x41FA,
        { 0xA0, 0x49,0x8A, 0xD0, 0x6C, 0x1A,0x66, 0xAA } };
EFI_GUID gEfiEdidActiveProtocolGuid = {0xBD8C1056, 0x9F36,0x44EC,
        { 0x92, 0xA8,0xA6, 0x33, 0x7F, 0x81,0x79, 0x86 } };
EFI_GUID gEfiEdidOverrideProtocolGuid = {0x48ECB431, 0xFB72,0x45C0,
        { 0xA9, 0x22,0xF4, 0x58, 0xFE, 0x4,0xB, 0xD5 } };
EFI_GUID gEfiLoadFileProtocolGuid = {0x56EC3091, 0x954C,0x11D2,
        { 0x8E, 0x3F,0x0, 0xA0, 0xC9, 0x69,0x72, 0x3B } };
EFI_GUID gEfiLoadFile2ProtocolGuid = {0x4006C0C1, 0xFCB3,0x403E,
        { 0x99, 0x6D,0x4A, 0x6C, 0x87, 0x24,0xE0, 0x6D } };
EFI_GUID gEfiTapeIoProtocolGuid = {0x1E93E633, 0xD65A,0x459E,
        { 0xAB, 0x84,0x93, 0xD9, 0xEC, 0x26,0x6D, 0x18 } };
EFI_GUID gEfiDiskIoProtocolGuid = {0xCE345171, 0xBA0B,0x11D2,
        { 0x8E, 0x4F,0x0, 0xA0, 0xC9, 0x69,0x72, 0x3B } };
EFI_GUID gEfiBlockIoProtocolGuid = {0x964E5B21, 0x6459,0x11D2,
        { 0x8E, 0x39,0x0, 0xA0, 0xC9, 0x69,0x72, 0x3B } };
EFI_GUID gEfiUnicodeCollationProtocolGuid = {0x1D85CD7F, 0xF43D,0x11D2,
        { 0x9A, 0xC,0x0, 0x90, 0x27, 0x3F,0xC1, 0x4D } };
EFI_GUID gEfiPciRootBridgeIoProtocolGuid = {0x2F707EBB, 0x4A1A,0x11D4,
        { 0x9A, 0x38,0x0, 0x90, 0x27, 0x3F,0xC1, 0x4D } };
EFI_GUID gEfiPciIoProtocolGuid = {0x4CF5B200, 0x68B8,0x4CA5,
        { 0x9E, 0xEC,0xB2, 0x3E, 0x3F, 0x50,0x2, 0x9A } };
EFI_GUID gEfiScsiPassThruProtocolGuid = {0xA59E8FCF, 0xBDA0,0x43BB,
        { 0x90, 0xB1,0xD3, 0x73, 0x2E, 0xCA,0xA8, 0x77 } };
EFI_GUID gEfiScsiIoProtocolGuid = {0x932F47E6, 0x2362,0x4002,
        { 0x80, 0x3E,0x3C, 0xD5, 0x4B, 0x13,0x8F, 0x85 } };
EFI_GUID gEfiExtScsiPassThruProtocolGuid = {0x143B7632, 0xB81B,0x4CB7,
        { 0xAB, 0xD3,0xB6, 0x25, 0xA5, 0xB9,0xBF, 0xFE } };
EFI_GUID gEfiIScsiInitiatorNameProtocolGuid = {0x59324945, 0xEC44,0x4C0D,
        { 0xB1, 0xCD,0x9D, 0xB1, 0x39, 0xDF,0x7, 0xC } };
EFI_GUID gEfiUsbIoProtocolGuid = {0x2B2F68D6, 0xCD2,0x44CF,
        { 0x8E, 0x8B,0xBB, 0xA2, 0xB, 0x1B,0x5B, 0x75 } };
EFI_GUID gEfiUsbHcProtocolGuid = {0xF5089266, 0x1AA0,0x4953,
        { 0x97, 0xD8,0x56, 0x2F, 0x8A, 0x73,0xB5, 0x19 } };
EFI_GUID gEfiUsb2HcProtocolGuid = {0x3E745226, 0x9818,0x45B6,
        { 0xA2, 0xAC,0xD7, 0xCD, 0xE, 0x8B,0xA2, 0xBC } };
EFI_GUID gEfiDebugSupportProtocolGuid = {0x2755590C, 0x6F3C,0x42FA,
        { 0x9E, 0xA4,0xA3, 0xBA, 0x54, 0x3C,0xDA, 0x25 } };
EFI_GUID gEfiDebugPortProtocolGuid = {0xEBA4E8D2, 0x3858,0x41EC,
        { 0xA2, 0x81,0x26, 0x47, 0xBA, 0x96,0x60, 0xD0 } };
EFI_GUID gEfiDecompressProtocolGuid = {0xD8117CFE, 0x94A6,0x11D4,
        { 0x9A, 0x3A,0x0, 0x90, 0x27, 0x3F,0xC1, 0x4D } };
EFI_GUID gEfiAcpiTableProtocolGuid = {0xFFE06BDD, 0x6107,0x46A6,
        { 0x7B, 0xB2,0x5A, 0x9C, 0x7E, 0xC5,0x27, 0x5C } };
EFI_GUID gEfiEbcProtocolGuid = {0x13AC6DD1, 0x73D0,0x11D4,
        { 0xB0, 0x6B,0x0, 0xAA, 0x0, 0xBD,0x6D, 0xE7 } };
EFI_GUID gEfiSimpleNetworkProtocolGuid = {0xA19832B9, 0xAC25,0x11D3,
        { 0x9A, 0x2D,0x0, 0x90, 0x27, 0x3F,0xC1, 0x4D } };
EFI_GUID gEfiNetworkInterfaceIdentifierProtocolGuid = {0xE18541CD, 0xF755,0x4F73,
        { 0x92, 0x8D,0x64, 0x3C, 0x8A, 0x79,0xB2, 0x29 } };
EFI_GUID gEfiNetworkInterfaceIdentifierProtocolGuid_31 = {0x1ACED566, 0x76ED,0x4218,
        { 0xBC, 0x81,0x76, 0x7F, 0x1F, 0x97,0x7A, 0x89 } };
EFI_GUID gEfiPxeBaseCodeProtocolGuid = {0x3C4E603, 0xAC28,0x11D3,
        { 0x9A, 0x2D,0x0, 0x90, 0x27, 0x3F,0xC1, 0x4D } };
EFI_GUID gEfiPxeBaseCodeCallbackProtocolGuid = {0x245DCA21, 0xFB7B,0x11D3,
        { 0x8F, 0x1,0x0, 0xA0, 0xC9, 0x69,0x72, 0x3B } };
EFI_GUID gEfiBisProtocolGuid = {0xB64AAB0, 0x5429,0x11D4,
        { 0x98, 0x16,0x0, 0xA0, 0xC9, 0x1F,0xAD, 0xCF } };
EFI_GUID gEfiManagedNetworkServiceBindingProtocolGuid = {0xF36FF770, 0xA7E1,0x42CF,
        { 0x9E, 0xD2,0x56, 0xF0, 0xF2, 0x71,0xF4, 0x4C } };
EFI_GUID gEfiManagedNetworkProtocolGuid = {0x7AB33A91, 0xACE5,0x4326,
        { 0xB5, 0x72,0xE7, 0xEE, 0x33, 0xD3,0x9F, 0x16 } };
EFI_GUID gEfiArpServiceBindingProtocolGuid = {0xF44C00EE, 0x1F2C,0x4A00,
        { 0xAA, 0x9,0x1C, 0x9F, 0x3E, 0x8,0x0, 0xA3 } };
EFI_GUID gEfiArpProtocolGuid = {0xF4B427BB, 0xBA21,0x4F16,
        { 0xBC, 0x4E,0x43, 0xE4, 0x16, 0xAB,0x61, 0x9C } };
EFI_GUID gEfiDhcp4ServiceBindingProtocolGuid = {0x9D9A39D8, 0xBD42,0x4A73,
        { 0xA4, 0xD5,0x8E, 0xE9, 0x4B, 0xE1,0x13, 0x80 } };
EFI_GUID gEfiDhcp4ProtocolGuid = {0x8A219718, 0x4EF5,0x4761,
        { 0x91, 0xC8,0xC0, 0xF0, 0x4B, 0xDA,0x9E, 0x56 } };
EFI_GUID gEfiTcp4ServiceBindingProtocolGuid = {0x720665, 0x67EB,0x4A99,
        { 0xBA, 0xF7,0xD3, 0xC3, 0x3A, 0x1C,0x7C, 0xC9 } };
EFI_GUID gEfiTcp4ProtocolGuid = {0x65530BC7, 0xA359,0x410F,
        { 0xB0, 0x10,0x5A, 0xAD, 0xC7, 0xEC,0x2B, 0x62 } };
EFI_GUID gEfiIp4ServiceBindingProtocolGuid = {0xC51711E7, 0xB4BF,0x404A,
        { 0xBF, 0xB8,0xA, 0x4, 0x8E, 0xF1,0xFF, 0xE4 } };
EFI_GUID gEfiIp4ProtocolGuid = {0x41D94CD2, 0x35B6,0x455A,
        { 0x82, 0x58,0xD4, 0xE5, 0x13, 0x34,0xAA, 0xDD } };
EFI_GUID gEfiIp4ConfigProtocolGuid = {0x3B95AA31, 0x3793,0x434B,
        { 0x86, 0x67,0xC8, 0x7, 0x8, 0x92,0xE0, 0x5E } };
EFI_GUID gEfiIp4Config2ProtocolGuid = {0x5B446ED1, 0xE30B,0x4FAA,
        { 0x87, 0x1A,0x36, 0x54, 0xEC, 0xA3,0x60, 0x80 } };
EFI_GUID gEfiUdp4ServiceBindingProtocolGuid = {0x83F01464, 0x99BD,0x45E5,
        { 0xB3, 0x83,0xAF, 0x63, 0x5, 0xD8,0xE9, 0xE6 } };
EFI_GUID gEfiUdp4ProtocolGuid = {0x3AD9DF29, 0x4501,0x478D,
        { 0xB1, 0xF8,0x7F, 0x7F, 0xE7, 0xE,0x50, 0xF3 } };
EFI_GUID gEfiMtftp4ServiceBindingProtocolGuid = {0x2FE800BE, 0x8F01,0x4AA6,
        { 0x94, 0x6B,0xD7, 0x13, 0x88, 0xE1,0x83, 0x3F } };
EFI_GUID gEfiMtftp4ProtocolGuid = {0x78247C57, 0x63DB,0x4708,
        { 0x99, 0xC2,0xA8, 0xB4, 0xA9, 0xA6,0x1F, 0x6B } };
EFI_GUID gEfiAuthenticationInfoProtocolGuid = {0x7671D9D0, 0x53DB,0x4173,
        { 0xAA, 0x69,0x23, 0x27, 0xF2, 0x1F,0xB, 0xC7 } };
EFI_GUID gEfiHashServiceBindingProtocolGuid = {0x42881C98, 0xA4F3,0x44B0,
        { 0xA3, 0x9D,0xDF, 0xA1, 0x86, 0x67,0xD8, 0xCD } };
EFI_GUID gEfiHashProtocolGuid = {0xC5184932, 0xDBA5,0x46DB,
        { 0xA5, 0xBA,0xCC, 0xB, 0xDA, 0x9C,0x14, 0x35 } };
EFI_GUID gEfiHiiFontProtocolGuid = {0xE9CA4775, 0x8657,0x47FC,
        { 0x97, 0xE7,0x7E, 0xD6, 0x5A, 0x8,0x43, 0x24 } };
EFI_GUID gEfiHiiStringProtocolGuid = {0xFD96974, 0x23AA,0x4CDC,
        { 0xB9, 0xCB,0x98, 0xD1, 0x77, 0x50,0x32, 0x2A } };
EFI_GUID gEfiHiiImageProtocolGuid = {0x31A6406A, 0x6BDF,0x4E46,
        { 0xB2, 0xA2,0xEB, 0xAA, 0x89, 0xC4,0x9, 0x20 } };
EFI_GUID gEfiHiiConfigRoutingProtocolGuid = {0x587E72D7, 0xCC50,0x4F79,
        { 0x82, 0x9,0xCA, 0x29, 0x1F, 0xC1,0xA1, 0xF } };
EFI_GUID gEfiHiiConfigAccessProtocolGuid = {0x330D4706, 0xF2A0,0x4E4F,
        { 0xA3, 0x69,0xB6, 0x6F, 0xA8, 0xD5,0x43, 0x85 } };
EFI_GUID gEfiFormBrowser2ProtocolGuid = {0xB9D4C360, 0xBCFB,0x4F9B,
        { 0x92, 0x98,0x53, 0xC1, 0x36, 0x98,0x22, 0x58 } };
EFI_GUID gEfiDeviceIoProtocolGuid = {0xAF6AC311, 0x84C3,0x11D2,
        { 0x8E, 0x3C,0x0, 0xA0, 0xC9, 0x69,0x72, 0x3B } };
EFI_GUID gEfiUgaDrawProtocolGuid = {0x982C298B, 0xF4FA,0x41CB,
        { 0xB8, 0x38,0x77, 0xAA, 0x68, 0x8F,0xB8, 0x39 } };
EFI_GUID gEfiUgaIoProtocolGuid = {0x61A4D49E, 0x6F68,0x4F1B,
        { 0xB9, 0x22,0xA8, 0x6E, 0xED, 0xB,0x7, 0xA2 } };
EFI_GUID gEfiDriverConfigurationProtocolGuid = {0x107A772B, 0xD5E1,0x11D4,
        { 0x9A, 0x46,0x0, 0x90, 0x27, 0x3F,0xC1, 0x4D } };
EFI_GUID gEfiDriverConfiguration2ProtocolGuid = {0xBFD7DC1D, 0x24F1,0x40D9,
        { 0x82, 0xE7,0x2E, 0x9, 0xBB, 0x6B,0x4E, 0xBE } };
EFI_GUID gEfiSimpleTextInputExProtocolGuid = {0xDD9E7534, 0x7762,0x4698,
        { 0x8C, 0x14,0xF5, 0x85, 0x17, 0xA6,0x25, 0xAA } };
EFI_GUID gEfiIp6ServiceBindingProtocolGuid = {0xEC835DD3, 0xFE0F,0x617B,
        { 0xA6, 0x21,0xB3, 0x50, 0xC3, 0xE1,0x33, 0x88 } };
EFI_GUID gEfiIp6ProtocolGuid = {0x2C8759D5, 0x5C2D,0x66EF,
        { 0x92, 0x5F,0xB6, 0x6C, 0x10, 0x19,0x57, 0xE2 } };
EFI_GUID gEfiIp6ConfigProtocolGuid = {0x937FE521, 0x95AE,0x4D1A,
        { 0x89, 0x29,0x48, 0xBC, 0xD9, 0xA,0xD3, 0x1A } };
EFI_GUID gEfiMtftp6ServiceBindingProtocolGuid = {0xD9760FF3, 0x3CCA,0x4267,
        { 0x80, 0xF9,0x75, 0x27, 0xFA, 0xFA,0x42, 0x23 } };
EFI_GUID gEfiMtftp6ProtocolGuid = {0xBF0A78BA, 0xEC29,0x49CF,
        { 0xA1, 0xC9,0x7A, 0xE5, 0x4E, 0xAB,0x6A, 0x51 } };
EFI_GUID gEfiDhcp6ServiceBindingProtocolGuid = {0x9FB9A8A1, 0x2F4A,0x43A6,
        { 0x88, 0x9C,0xD0, 0xF7, 0xB6, 0xC4,0x7A, 0xD5 } };
EFI_GUID gEfiDhcp6ProtocolGuid = {0x87C8BAD7, 0x595,0x4053,
        { 0x82, 0x97,0xDE, 0xDE, 0x39, 0x5F,0x5D, 0x5B } };
EFI_GUID gEfiUdp6ServiceBindingProtocolGuid = {0x66ED4721, 0x3C98,0x4D3E,
        { 0x81, 0xE3,0xD0, 0x3D, 0xD3, 0x9A,0x72, 0x54 } };
EFI_GUID gEfiUdp6ProtocolGuid = {0x4F948815, 0xB4B9,0x43CB,
        { 0x8A, 0x33,0x90, 0xE0, 0x60, 0xB3,0x49, 0x55 } };
EFI_GUID gEfiTcp6ServiceBindingProtocolGuid = {0xEC20EB79, 0x6C1A,0x4664,
        { 0x9A, 0xD,0xD2, 0xE4, 0xCC, 0x16,0xD6, 0x64 } };
EFI_GUID gEfiTcp6ProtocolGuid = {0x46E44855, 0xBD60,0x4AB7,
        { 0xAB, 0xD,0xA6, 0x79, 0xB9, 0x44,0x7D, 0x77 } };
EFI_GUID gEfiVlanConfigProtocolGuid = {0x9E23D768, 0xD2F3,0x4366,
        { 0x9F, 0xC3,0x3A, 0x7A, 0xBA, 0x86,0x43, 0x74 } };
EFI_GUID gEfiEapProtocolGuid = {0x5D9F96DB, 0xE731,0x4CAA,
        { 0xA0, 0xD,0x72, 0xE1, 0x87, 0xCD,0x77, 0x62 } };
EFI_GUID gEfiEapManagementProtocolGuid = {0xBB62E663, 0x625D,0x40B2,
        { 0xA0, 0x88,0xBB, 0xE8, 0x36, 0x23,0xA2, 0x45 } };
EFI_GUID gEfiFtp4ServiceBindingProtocolGuid = {0xFAAECB1, 0x226E,0x4782,
        { 0xAA, 0xCE,0x7D, 0xB9, 0xBC, 0xBF,0x4D, 0xAF } };
EFI_GUID gEfiFtp4ProtocolGuid = {0xEB338826, 0x681B,0x4295,
        { 0xB3, 0x56,0x2B, 0x36, 0x4C, 0x75,0x7B, 0x9 } };
EFI_GUID gEfiIpSecConfigProtocolGuid = {0xCE5E5929, 0xC7A3,0x4602,
        { 0xAD, 0x9E,0xC9, 0xDA, 0xF9, 0x4E,0xBF, 0xCF } };
EFI_GUID gEfiDriverHealthProtocolGuid = {0x2A534210, 0x9280,0x41D8,
        { 0xAE, 0x79,0xCA, 0xDA, 0x1, 0xA2,0xB1, 0x27 } };
EFI_GUID gEfiDeferredImageLoadProtocolGuid = {0x15853D7C, 0x3DDF,0x43E0,
        { 0xA1, 0xCB,0xEB, 0xF8, 0x5B, 0x8F,0x87, 0x2C } };
EFI_GUID gEfiUserCredentialProtocolGuid = {0x71EE5E94, 0x65B9,0x45D5,
        { 0x82, 0x1A,0x3A, 0x4D, 0x86, 0xCF,0xE6, 0xBE } };
EFI_GUID gEfiUserManagerProtocolGuid = {0x6FD5B00C, 0xD426,0x4283,
        { 0x98, 0x87,0x6C, 0xF5, 0xCF, 0x1C,0xB1, 0xFE } };
EFI_GUID gEfiAtaPassThruProtocolGuid = {0x1D3DE7F0, 0x807,0x424F,
        { 0xAA, 0x69,0x11, 0xA5, 0x4E, 0x19,0xA4, 0x6F } };
EFI_GUID gEfiFirmwareManagementProtocolGuid = {0x86C77A67, 0xB97,0x4633,
        { 0xA1, 0x87,0x49, 0x10, 0x4D, 0x6,0x85, 0xC7 } };
EFI_GUID gEfiIpSecProtocolGuid = {0xDFB386F7, 0xE100,0x43AD,
        { 0x9C, 0x9A,0xED, 0x90, 0xD0, 0x8A,0x5E, 0x12 } };
EFI_GUID gEfiIpSec2ProtocolGuid = {0xA3979E64, 0xACE8,0x4DDC,
        { 0xBC, 0x7,0x4D, 0x66, 0xB8, 0xFD,0x9, 0x77 } };
EFI_GUID gEfiKmsProtocolGuid = {0xEC3A978D, 0x7C4E,0x48FA,
        { 0x9A, 0xBE,0x6A, 0xD9, 0x1C, 0xC8,0xF8, 0x11 } };
EFI_GUID gEfiBlockIo2ProtocolGuid = {0xA77B2472, 0xE282,0x4E9F,
        { 0xA2, 0x45,0xC2, 0xC0, 0xE2, 0x7B,0xBC, 0xC1 } };
EFI_GUID gEfiStorageSecurityCommandProtocolGuid = {0xC88B0B6D, 0xDFC,0x49A7,
        { 0x9C, 0xB4,0x49, 0x7, 0x4B, 0x4C,0x3A, 0x78 } };
EFI_GUID gEfiUserCredential2ProtocolGuid = {0xE98ADB03, 0xB8B9,0x4AF8,
        { 0xBA, 0x20,0x26, 0xE9, 0x11, 0x4C,0xBC, 0xE5 } };
EFI_GUID gPcdProtocolGuid = {0x11B34006, 0xD85B,0x4D0A,
        { 0xA2, 0x90,0xD5, 0xA5, 0x71, 0x31,0xE, 0xF7 } };
EFI_GUID gEfiTcgProtocolGuid = {0xF541796D, 0xA62E,0x4954,
        { 0xA7, 0x75,0x95, 0x84, 0xF6, 0x1B,0x9C, 0xDD } };
EFI_GUID gEfiHiiPackageListProtocolGuid = {0x6A1EE763, 0xD47A,0x43B4,
        { 0xAA, 0xBE,0xEF, 0x1D, 0xE2, 0xAB,0x56, 0xFC } };
EFI_GUID gEfiDriverFamilyOverrideProtocolGuid = {0xB1EE129E, 0xDA36,0x4181,
        { 0x91, 0xF8,0x4, 0xA4, 0x92, 0x37,0x66, 0xA7 } };
EFI_GUID gEfiIdeControllerInitProtocolGuid = {0xA1E37052, 0x80D9,0x4E65,
        { 0xA3, 0x17,0x3E, 0x9A, 0x55, 0xC4,0x3E, 0xC9 } };
EFI_GUID gEfiDiskIo2ProtocolGuid = {0x151C8EAE, 0x7F2C,0x472C,
        { 0x9E, 0x54,0x98, 0x28, 0x19, 0x4F,0x6A, 0x88 } };
EFI_GUID gEfiAdapterInformationProtocolGuid = {0xE5DD1403, 0xD622,0xC24E,
        { 0x84, 0x88,0xC7, 0x1B, 0x17, 0xF5,0xE8, 0x2 } };
EFI_GUID gEfiShellDynamicCommandProtocolGuid = {0x3C7200E9, 0x5F,0x4EA4,
        { 0x87, 0xDE,0xA3, 0xDF, 0xAC, 0x8A,0x27, 0xC3 } };
EFI_GUID gEfiDiskInfoProtocolGuid = {0xD432A67F, 0x14DC,0x484B,
        { 0xB3, 0xBB,0x3F, 0x2, 0x91, 0x84,0x93, 0x27 } };

 

Step to UEFI (107)取得USB DISK 的序列号

继续前面的话题,这次研究如何取得一个USB DISK的信息。最先想到的还是使用 DISK INFO PROTOCOL,不过到了实际编写代码的时候发现:我根本不知道返回值是什么格式。在代码中搜索了几次 gEfiDiskInfoUsbInterfaceGuid 发现无人使用。之后为了弄清楚格式,直接创建了一个256bytes大小的内存,还是用 identify 来获得返回值,最后发现:返回值是空的。因此,也就是说虽然 USB DISK上有安装这个 protocol,但是根本就是一个空的而已。
再调整思路,首先用 DISK INFO PROTOCOL取得USB DISK 的 Handle ,然后,在这个Handle上打开 USBIO,EFI_USB_IO_PROTOCOL.UsbGetStringDescriptor 可以用来取得描述符中相关的字符串【参考1】,具体结构可以在 \EdkCompatibilityPkg\Foundation\Include\IndustryStandard\usb.h 找到:

typedef struct {
  UINT8           Length;
  UINT8           DescriptorType;
  UINT16          BcdUSB;
  UINT8           DeviceClass;
  UINT8           DeviceSubClass;
  UINT8           DeviceProtocol;
  UINT8           MaxPacketSize0;
  UINT16          IdVendor;
  UINT16          IdProduct;
  UINT16          BcdDevice;
  UINT8           StrManufacturer;
  UINT8           StrProduct;
  UINT8           StrSerialNumber;
  UINT8           NumConfigurations;
} EFI_USB_DEVICE_DESCRIPTOR;

 

最后,用这个 函数来取得需要的字符串。

完整的代码:

#include  <Uefi.h>
#include  <Library/UefiLib.h>
#include  <Library/ShellCEntryLib.h>
#include  <Library/ShellCEntryLib.h>
#include  <Protocol/DiskInfo.h>
#include  <Library/BaseMemoryLib.h>
#include  <Protocol/IdeControllerInit.h>
#include <Library/MemoryAllocationLib.h>
#include  <Protocol/UsbIo.h>

extern EFI_BOOT_SERVICES         *gBS;
extern EFI_HANDLE				 gImageHandle;

EFI_GUID gEfiDiskInfoProtocolGuid = { 0xD432A67F, 0x14DC, 0x484B, 
					{ 0xB3, 0xBB, 0x3F, 0x02, 0x91, 0x84, 0x93, 0x27 }};
EFI_GUID gEfiDiskInfoUsbInterfaceGuid   = { 0xCB871572, 0xC11A, 0x47B5, 
					{ 0xB4, 0x92, 0x67, 0x5E, 0xAF, 0xA7, 0x77, 0x27 }};

EFI_GUID  gEfiUsbIoProtocolGuid   = { 0x2B2F68D6, 0x0CD2, 0x44CF, 
					{ 0x8E, 0x8B, 0xBB, 0xA2, 0x0B, 0x1B, 0x5B, 0x75 }};					

int
EFIAPI
main (
  IN int Argc,
  IN CHAR16 **Argv
  )
{
    EFI_STATUS 	Status;
    UINTN 		HandleIndex, NumHandles;
    EFI_HANDLE 	*ControllerHandle = NULL;
	EFI_DISK_INFO_PROTOCOL	*DiskInfoProtocol;	
	EFI_USB_IO_PROTOCOL 			*USBIO;
	EFI_USB_DEVICE_DESCRIPTOR     DeviceDescriptor;
	CHAR16                       *Manufacturer;
	CHAR16                       *Product;
	CHAR16                       *SerialNumber;
	
    Status = gBS->LocateHandleBuffer(
            ByProtocol,
            &gEfiDiskInfoProtocolGuid,
            NULL,
            &NumHandles,
            &ControllerHandle);
	
    for (HandleIndex = 0; HandleIndex < NumHandles; HandleIndex++) {
        Status = gBS->OpenProtocol(
                ControllerHandle[HandleIndex],
                &gEfiDiskInfoProtocolGuid, 
                (VOID**)&DiskInfoProtocol,
                gImageHandle,
                NULL,
                EFI_OPEN_PROTOCOL_GET_PROTOCOL
                );		
        if (EFI_ERROR(Status)) {
            continue;
        } 

		//We only deal with USB
		if (!(CompareGuid (
				&DiskInfoProtocol->Interface, 
				&gEfiDiskInfoUsbInterfaceGuid))) {	
				continue;
			}	
			
        Status = gBS->OpenProtocol(
                ControllerHandle[HandleIndex],
                &gEfiUsbIoProtocolGuid, 
                (VOID**)&USBIO,
                gImageHandle,
                NULL,
                EFI_OPEN_PROTOCOL_GET_PROTOCOL
                );		
        if (EFI_ERROR(Status)) {
			Print(L"ERROR : Open USBIO fail.\n");
            continue;
        } 
		
		Status = USBIO->UsbGetDeviceDescriptor
							(USBIO, &DeviceDescriptor);     
		if (EFI_ERROR(Status))
		{
			Print(L"ERROR : Get Device Descriptor fail.\n");
			return 0;
		}		
		
		Print(L"VendorID = %04X\nProductID = %04X\n", 
                              DeviceDescriptor.IdVendor, 
                              DeviceDescriptor.IdProduct);  

		Status = USBIO->UsbGetStringDescriptor (
                    USBIO,
                    0x0409, 			// English
                    DeviceDescriptor.StrManufacturer,
                    &Manufacturer
                    );
		if (EFI_ERROR (Status)) {
				Manufacturer = L"";
		}

		Status = USBIO->UsbGetStringDescriptor (
                    USBIO,
                    0x0409, 			// English
                    DeviceDescriptor.StrProduct,
                    &Product
                    );
		if (EFI_ERROR (Status)) {
				Product = L"";	}
				
				
		Status = USBIO->UsbGetStringDescriptor (
                    USBIO,
                    0x0409, 			// English
                    DeviceDescriptor.StrSerialNumber,
                    &SerialNumber
                    );
		if (EFI_ERROR (Status)) {
				SerialNumber = L"";}
		
		Print(L"     Manufacturer :  %s\n",Manufacturer);
		Print(L"     Product      :  %s\n",Product);
		Print(L"     Serial Number:  %s\n",SerialNumber);		
						  
							  
	}
  return EFI_SUCCESS;
}

 

运行结果:
ditu1

完整的代码和程序下载:
diskinfousb

另外,还可以直接枚举 USBIO 然后检查对应的 Class发现是USB MASS Storage 即是U盘,然后再重复上面取得字符串信息的动作。

参考:
1. UEFI Spec 2.4 P835 EFI_USB_IO_PROTOCOL.UsbGetStringDescriptor()

DFRobot SIM808 模块评测 GPS

人类对于世界的了解的越多,能够掌握的工具和方法就越多。

曾经读过方舟子写的《相对论有没有用?》【参考1】,让我大吃一惊的是日常用到的GPS就是相对论使用的典型:

“GPS是靠美国空军发射的24颗GPS卫星来定位的(此外还有几颗备用卫星),每颗卫星上都携带着原子钟,它们计时极为准确,误差不超过十万亿分之一,即每天的误差不超过10纳秒(1纳秒等于10亿分之一秒),并不停地发射无线电信号报告时间和轨道位置。这些GPS卫星在空中的位置是精心安排好的,任何时候在地球上的任何地点至少都能见到其中的4颗。GPS导航仪通过比较从4颗GPS卫星发射来的时间信号的差异,计算出所在的位置。

GPS卫星以每小时14000千米的速度绕地球飞行。根据狭义相对论,当物体运动时,时间会变慢,运动速度越快,时间就越慢。因此在地球上看GPS卫星,它们携带的时钟要走得比较慢,用狭义相对论的公式可以计算出,每天慢大约7微秒。

GPS卫星位于距离地面大约2万千米的太空中。根据广义相对论,物质质量的存在会造成时空的弯曲,质量越大,距离越近,就弯曲得越厉害,时间则会越慢。受地球质量的影响,在地球表面的时空要比GPS卫星所在的时空更加弯曲,这样,从地球上看,GPS卫星上的时钟就要走得比较快,用广义相对论的公式可以计算出,每天快大约45微秒。

在同时考虑了狭义相对论和广义相对论后,GPS卫星时钟每天还要快上大约38微秒,这似乎微不足道,但是如果我们考虑到GPS系统必须达到的时间精度是纳秒级的,这个误差就非常可观了(38微秒等于38000纳秒)。如果不校正的话,GPS系统每天将会累积大约10千米的定位误差,是没有用的。为此,在GPS卫星发射前,要先把其时钟的走动频率调慢100亿分之4.465,把10.23兆赫调为10.22999999543兆赫。此外,GPS卫星的运行轨道并非完美的圆形,与地面的距离和运行速度会有所变化,如果轨道偏心率为0.02,时间就会有46纳秒的误差。由于地球的自转,GPS导航仪在地球表面上的位移也会产生误差,例如当GPS导航仪在赤道上,而GPS卫星在地平线上时,由于位移产生的误差将会达到133纳秒。GPS导航仪在定位时还必须根据相对论进行计算纠正这些误差。

我手中的 DFRobot的SIM808 Shield支持GPS功能,这次我们就实验一下这个功能,GPS取得当前的时间和经纬度之后,结果显示在1602液晶上。使用到的硬件包括:

1. Arduino Uno 1块

2. DFRobot 的SIM808 Shield 1块

3. 18650 电池组 2块

4. 1602 LCD 一块

5. 亚克力切割的外壳 一对

特别注意的是,必须使用外接电源,USB端口供电不足以驱动模块,特别注意:一定要接上 GPS 天线,否则无法收到信号。

硬件准备妥当之后,先实验直接发送 AT 命令,这个实验不需要SIM卡:

1. 板上开关放置于3号位置

2. 下载 blink 程序(需要不占用 的程序)

3. 外接电源(我用的是2节18650,目前有7.5v左右)

4. 板上开关放置于2号位置

5. 用IDE自带的串口工具,输入 AT 可以看到自动回复 OK,这说明模块本身是正常的

6. 输入AT+CGNSPWR=1命令(打开GPS电源)AT+CGNSTST=1命令(开始从串口接收GPS数据)

7. 下面就可以看到获得的GPS数据了
image001

8. 结束的时候,使用AT+CGNSPWR=0命令关断GPS电源。

image002

我们在地图上看一下这个位置,很准

image003
软件方面需要下载DFRobot_SIM808的库,在https://github.com/DFRobot/DFRobot_SIM808。本文末尾提供了打包好的库文件。可以使用 Sketch->Include library->Add .zip library直接添加。

代码如下:

#include <LiquidCrystal_I2C.h>

#include <DFRobot_sim808.h>

 

DFRobot_SIM808 sim808(&Serial);

 

// Set the LCD address to 0x3F for a 16 chars and 2 line display

LiquidCrystal_I2C lcd(0x3f, 16, 2);

 

void setup() {

    Serial.begin(9600);

 

    // initialize the LCD

    lcd.begin();

 

    // Turn on the blacklight and print a message.

    lcd.backlight();

  

    //************* Turn on the GPS power************

    if( sim808.attachGPS())

      {

        Serial.println("Open the GPS power success");

        lcd.print("power success");

      }

    else

      {    

        Serial.println("Open the GPS power failure");

        lcd.print("power failure");        

      }

}

 

void loop() {

     char s[20];    

     //************** Get GPS data *******************

     if (sim808.getGPS()) {

      Serial.print(sim808.GPSdata.hour);

      Serial.print(":");

      Serial.print(sim808.GPSdata.minute);

      Serial.print(":");

      Serial.println(sim808.GPSdata.second);

      Serial.print("latitude :");

      Serial.println(sim808.GPSdata.lat);

      Serial.print("longitude :");

      Serial.println(sim808.GPSdata.lon);

 

      lcd.clear(); 

      lcd.print("GPS     bbdebbbbbbbhhh             b        "); 

      lcd.print(sim808.GPSdata.hour);  

      lcd.print(":"); 

      lcd.print(sim808.GPSdata.minute);  

      lcd.print(":"); 

      lcd.print(sim808.GPSdata.second);

      

 

      lcd.setCursor(0,1);

      lcd.print("(");

      dtostrf(sim808.GPSdata.lat,3,2,s);

      lcd.print(s);

      lcd.print(",");

      dtostrf(sim808.GPSdata.lon,3,2,s);

      lcd.print(s);

      lcd.print(")");

 

      //************* Turn off the GPS power ************

      sim808.detachGPS();

    }

 

  }

 

最终运行结果:

image004

image005

最后说点关于GPS 好玩的事情:

第一件事情:我在昆山打工的时候,有一天瞎溜达,走到一座大桥下,看到上面铭牌上刻着经纬度。那是很久以前的事情,久远到 google还没有被封,手机还没有 GPS 功能。我很好奇的记下了经纬度。回去宿舍在google map上查找这个经纬度,非常疑惑的看着坐标飞到了太平洋中。朱多年来心中疑惑一直没有解开:这样的标注是为了“乱了敌人锻炼了群众”吗?

第二件事情: 1983年9月1日清晨(UTC时间为8月31日傍晚),大韩航空007号班机进入苏联领空,遭苏联空军Su-15拦截机击落于库页岛西南方的公海。误入的原因是机长操作失误,没有切换到正确的导航模式【参考2】。这件事情之后美国宣布开放部份的GPS功能给民间使用。

参考:

1. http://view.news.qq.com/a/20090422/000029.htm 方舟子:相对论有没有用?

借用臧克家的一句话来描述方舟子“有的人死了,他还活着;有的人或者,他已经敏感字了”。

2. http://baike.baidu.com/item/%E5%A4%A7%E9%9F%A9%E8%88%AA%E7%A9%BA007%E5%8F%B7%E7%8F%AD%E6%9C%BA%E7%A9%BA%E9%9A%BE 大韩航空007号班机空难

如果非说苏联伟大的话,那么伟大的原因一定是它一次次将人类从自己的魔爪下解救出来。

Step to UEFI (106)取得AHCI SATA 的序列号

继续前面的话题,现在尝试直接输出AHCI HDD 的信息。资料上标明AHCI和IDE HDD输出的信息格式是相同的,所以这里会一同处理。

原理上:找到 DISK INFO PROTOCOL 后,判断 GUID 是否为 IDE和 AHCI 的,如果是,那么用Identify 来取得型号信息。返回构体EFI_ATAPI_IDENTIFY_DATA,具体定义在下面这个文件中:

\EdkCompatibilityPkg\Foundation\Framework\Protocol\IdeControllerInit\IdeControllerInit.h 中。

typedef struct {
    UINT16  config;             // General Configuration
    UINT16  obsolete_1;
    UINT16  specific_config;
    UINT16  obsolete_3;   
    UINT16  retired_4_5[2];
    UINT16  obsolete_6;   
    UINT16  cfa_reserved_7_8[2];
    UINT16  retired_9;
    CHAR8   SerialNo[20];       // ASCII 
    UINT16  retired_20_21[2];
    UINT16  obsolete_22;
    CHAR8   FirmwareVer[8];     // ASCII 
    CHAR8   ModelName[40];      // ASCII 
    UINT16  multi_sector_cmd_max_sct_cnt;
    UINT16  reserved_48;
    UINT16  capabilities_49;
    UINT16  capabilities_50;
    UINT16  obsolete_51_52[2];   
    UINT16  field_validity;
    UINT16  obsolete_54_58[5];
    UINT16  mutil_sector_setting;
    UINT16  user_addressable_sectors_lo;
    UINT16  user_addressable_sectors_hi;
    UINT16  obsolete_62;
    UINT16  multi_word_dma_mode;
    UINT16  advanced_pio_modes;
    UINT16  min_multi_word_dma_cycle_time;
    UINT16  rec_multi_word_dma_cycle_time;
    UINT16  min_pio_cycle_time_without_flow_control;
    UINT16  min_pio_cycle_time_with_flow_control;
    UINT16  reserved_69_74[6];
    UINT16  queue_depth;
    UINT16  reserved_76_79[4];
    UINT16  major_version_no;
    UINT16  minor_version_no;
    UINT16  cmd_set_support_82;
    UINT16  cmd_set_support_83;
    UINT16  cmd_feature_support;
    UINT16  cmd_feature_enable_85;
    UINT16  cmd_feature_enable_86;
    UINT16  cmd_feature_default;
    UINT16  ultra_dma_select;
    UINT16  time_required_for_sec_erase;
    UINT16  time_required_for_enhanced_sec_erase;
    UINT16  current_advanced_power_mgmt_value;
    UINT16  master_pwd_revison_code;
    UINT16  hardware_reset_result;
    UINT16  current_auto_acoustic_mgmt_value;
    UINT16  reserved_95_99[5];
    UINT16  max_user_lba_for_48bit_addr[4];
    UINT16  reserved_104_126[23];
    UINT16  removable_media_status_notification_support;
    UINT16  security_status;
    UINT16  vendor_data_129_159[31];
    UINT16  cfa_power_mode;
    UINT16  cfa_reserved_161_175[15];
    UINT16  current_media_serial_no[30];
    UINT16  reserved_206_254[49];
    UINT16  integrity_word;
} EFI_ATAPI_IDENTIFY_DATA;

 

代码如下:

#include  <Uefi.h>
#include  <Library/UefiLib.h>
#include  <Library/ShellCEntryLib.h>
#include  <Library/ShellCEntryLib.h>
#include  <Protocol/DiskInfo.h>
#include  <Library/BaseMemoryLib.h>
#include  <Protocol/IdeControllerInit.h>

extern EFI_BOOT_SERVICES         *gBS;

extern EFI_HANDLE				 gImageHandle;

EFI_GUID gEfiDiskInfoProtocolGuid = { 0xD432A67F, 0x14DC, 0x484B, 
					{ 0xB3, 0xBB, 0x3F, 0x02, 0x91, 0x84, 0x93, 0x27 }};
EFI_GUID gEfiDiskInfoAhciInterfaceGuid  = { 0x9e498932, 0x4abc, 0x45af, 
					{ 0xa3, 0x4d, 0x02, 0x47, 0x78, 0x7b, 0xe7, 0xc6 }};
EFI_GUID gEfiDiskInfoIdeInterfaceGuid   = { 0x5E948FE3, 0x26D3, 0x42B5, 
					{ 0xAF, 0x17, 0x61, 0x02, 0x87, 0x18, 0x8D, 0xEC }};

int
EFIAPI
main (
  IN int Argc,
  IN CHAR16 **Argv
  )
{
    EFI_STATUS Status;
    UINTN HandleIndex, NumHandles;
    EFI_HANDLE *ControllerHandle = NULL;
	EFI_DISK_INFO_PROTOCOL	*DiskInfoProtocol;
	UINT32                       BufferSize;	
    EFI_ATAPI_IDENTIFY_DATA      IdentifyData;	
	UINT32				i;
	
    Status = gBS->LocateHandleBuffer(
            ByProtocol,
            &gEfiDiskInfoProtocolGuid,
            NULL,
            &NumHandles,
            &ControllerHandle);
	
    for (HandleIndex = 0; HandleIndex < NumHandles; HandleIndex++) {
        Status = gBS->OpenProtocol(
                ControllerHandle[HandleIndex],
                &gEfiDiskInfoProtocolGuid, 
                (VOID**)&DiskInfoProtocol,
                gImageHandle,
                NULL,
                EFI_OPEN_PROTOCOL_GET_PROTOCOL
                );		
        if (EFI_ERROR(Status)) {
            continue;
        } 

		//We only deal with AHCI and IDE
		if (!(CompareGuid (
				&DiskInfoProtocol->Interface, 
				&gEfiDiskInfoAhciInterfaceGuid)||
			(CompareGuid (
				&DiskInfoProtocol->Interface,
			    &gEfiDiskInfoIdeInterfaceGuid)
			))) {	
				continue;
			}	

		BufferSize   = sizeof (EFI_ATAPI_IDENTIFY_DATA);
		Status = DiskInfoProtocol->Identify (
                         DiskInfoProtocol,
                         &IdentifyData,
                         &BufferSize
                         );			
		
		Print(L"Model Name :");
		for (i=0;i<40;i=i+2) {
			Print(L"%c%c",
				IdentifyData.ModelName[i+1],
				IdentifyData.ModelName[i]);
		}
		Print(L"\n");
	}

  return EFI_SUCCESS;
}

 

在 KabyLake HDK 板子上运行上述代码,结果如下:
ahci1

细心的朋友可能注意到,输出并不是直接输出序列号,而是有一个顺序上的调整:
Print(L”%c%c”, IdentifyData.ModelName[i+1], IdentifyData.ModelName[i]);
原因是,刚开始我试验的是直接顺序输出,但是发现结果是下面这样的:
ahci2

开始以为是 CHAR 对 CHAR16转换上的问题,后来查阅资料【参考1】,发现这里的行医比较特别。排列是 2/1/4/3/6/5……. 这样的:
ahci3

所以,修改代码手工做一次反转就可以了。
完整的代码下载:
diskinfoahci

参考:
1. http://www.t13.org/Documents/UploadedDocuments/docs2013/d2161r5-ATAATAPI_Command_Set_-_3.pdf

Windows常见开发包

https://msdn.microsoft.com/windows/hardware/commercialize/kits-and-tools-overview

Windows SDK for Windows 10 用来编写 Application的头文件库文件和工具 Windows SDK for Windows 10 contains headers, libraries, and tools you can use when you create apps that run on Windows operating systems.

WDK 10 用来编写Driver的头文件库文件和工具 WDK 10 contains the tools to build, test, debug, and deploy drivers for Windows 10.

Enterprise WDK (EWDK) 将上面2个放在一起的 The Enterprise WDK (EWDK) is a kit that large organizations can use as an alternative to downloading and installing the SDK and WDK individually on each computer.

Windows symbols 符号文件,用来调试 Windows的 Symbol files make it easier to debug your code.

Windows Hardware Lab Kit (HLK) for Windows 10 用来测试运行 Windows 设备的工具 Windows Hardware Lab Kit (HLK) for Windows 10

HLK supplemental test content 测试多媒体时可能需要的音频视频文件 Some tests, like graphics and multimedia tests, require additional files for testing.

ADK for Windows 10 (Windows Assessment and Deployment Kit) OEM和OEM用来制作安装镜像的工具
Download the Windows ADK to install tools and documentation for OEMs and ODMs to customize Windows 10 images, assess the quality and performance of systems or components, and to deploy Windows operating systems to new computers.

A device for UEFI Application testing

In the last posts, as the NT32 environment has too much limitations. We have to test my applications on the real system. I use a USB disk for these kinds of thing. After compiling, I have to copy my application to this disk. Then unplug and plug again to the testing system (I use Kabylake HDK now). After that I can test my application on the real system. If some bugs are found, I have to repeat this action. As you can see it’s really inconvenience.
For this purpose, I begin to study the “matrix switch IC” carefully. After all, I find that the common switching IC in the market could only support 300Mhz. While the USB 2.0 device requires 480Mhz. No one know what will happen if I make one. It’s not a good idea for me to DIY. At last, I begin to search the solution on Taobao.com. Soon, I find this one:

su

It has a mechanical switcher inside. And the most important thing: it’s very cheap. Only 7.8 Yuan (less than $2).
I buy one USB switcher and 2 USB cables (1.5m for 3Yuan).
The usage is that:
1st plug a USB disk in this switcher.
2nd Every time the complication is completed, copy the application to this USB Disk.
3rd Switch to the working PC and make your experiment in real system.

I have used this device for a period, it works well.