VS2013 编译的问题

最近安装了一个 VS2013进行 UEFI 的编译,碰到了其怪的错误:

“C:\Program Files\Microsoft Visual Studio 12.0\Vc\bin\link.exe” /out:”c:\udk2015\Build\NT32IA32\DEBUG_VS2013\IA32\SecMain.exe” /base:0x10000000 /pdb:”c:\udk2015\Build\NT32IA32\DEBUG_VS2013\IA32\SecMain.pdb” /LIBPATH:”C:\Program Files\Microsoft Visual Studio 12.0\VC\\Lib” /LIBPATH:”C:\Program Files\Windows Kits\8.1\Lib\winv6.3\um\x86″ /NOLOGO /SUBSYSTEM:CONSOLE /NODEFAULTLIB /IGNORE:4086 /MAP /OPT:REF /DEBUG /MACHINE:I386 /LTCG Kernel32.lib MSVCRTD.lib Gdi32.lib User32.lib Winmm.lib Advapi32.lib /EXPORT:InitializeDriver=_ModuleEntryPoint /BASE:0x10000 /ALIGN:4096 /FILEALIGN:4096 /SUBSYSTEM:CONSOLE @c:\udk2015\Build\NT32IA32\DEBUG_VS2013\IA32\Nt32Pkg\Sec\SecMain\OUTPUT\static_library_files.lst

SecMain.lib(WinNtThunk.obj) : error LNK2001: unresolved external symbol _SetCons
oleCursorInfo@8
c:\udk2015\Build\NT32IA32\DEBUG_VS2013\IA32\SecMain.exe : fatal error LNK1120: 1
51 unresolved externals

build…
: error 7000: Failed to execute command
C:\Program Files\Microsoft Visual Studio 12.0\Vc\bin\nmake.exe /nologo t
build [c:\udk2\015\Build\NT32IA32\DEBUG_VS2013\IA32\Nt32Pkg\Sec\SecMain]

仔细研究发现是虚拟机无法找到一些 Windows SDK 的内容。后来经过研究发现错误的原因是因为我使用了错误 VS2013 的控制台。我想编译 X64的UEFI 代码,但是进入了 x86(IA32)的控制台。

tt

正确的做法是:

如果你要编译x64 的代码,那么需要进入上面红色框中的命令行模式。然后,Conf\Target.txt 中的TOOL_CHAIN_TAG 必须是VS2013;

如果你要编译IA32的代码,那么需要进入上面绿色框中的命令行模式。然后,Conf\Target.txt 中的TOOL_CHAIN_TAG 必须是VS2013x86;

否则编译器无法找到对应的SDK 文件。

简单代码打造 Arduino 整蛊设备

如果你使用的是Intel 集成显卡的话,安装好驱动之后可以使用键盘上的 Ctrl + Alt+方向键旋转屏幕。根据这个功能,我们可以尝试制作一个整蛊的硬件。原理上来说就是使用 Leonardo模拟按下这个组合键,发送给系统来实现转动。当然为了防止你的朋友们变成下面这个样子,代码使用随机转动:
image001

代码非常简单

#define KEY_LEFT_CTRL		0x80
#define KEY_LEFT_ALT		0x82
#define KEY_UP_ARROW		0xDA
#define KEY_DOWN_ARROW		0xD9
#define KEY_LEFT_ARROW		0xD8
#define KEY_RIGHT_ARROW		0xD7

void setup() {
  // put your setup code here, to run once:
  Keyboard.begin();
}

void loop() {

  delay(5000);
  
  // CTRL-ALT-LEFT
  Keyboard.press(KEY_LEFT_CTRL);
  Keyboard.press(KEY_LEFT_ALT);
  
  switch(random(3)){
    case 0:
      Keyboard.press(KEY_LEFT_ARROW);    
      break;
    case 1:
      Keyboard.press(KEY_RIGHT_ARROW);        
      break;
    case 2:
      Keyboard.press(KEY_UP_ARROW);        
      break;
    case 3:
      Keyboard.press(KEY_DOWN_ARROW);        
      break;
  }
  Keyboard.releaseAll();

}

 

运行之后的效果:

image003

image002

关于按键信息在下面这个文件中可以看到:
\hardware\arduino\sam\cores\arduino\USB\USBAPI.h
弄明白上面的原理,你还可以做一个能够遥控旋转的设备,如果你不想让别人动你的电脑就让他转起来,很快对方就会知难而退的。

Step to UEFI (93)FTDI 串口驱动

FDTI232系列芯片是最好的USB转串口芯片,驱动全,芯片本身很稳定,兼容性也很好。这次介绍一个FTDI USB串口芯片的项目,地址是

https://github.com/tianocore/tianocore.github.io/wiki/Tasks-USB-Serial-Adapter-driver#Real_UEFI_system

项目的简介如下:

“Today there are many inexpensive USB Serial adapters available, and most systems are built with USB ports available. But at the same time, the dedicated Serial port is becoming less common to find available in a system.
A serial port can still be useful for software debugging purposes. (debug trace messages)
It can also be useful in providing a secondary terminal to the UEFI system.
This task would involve writing a USB driver which interfaces with a USB Serial Adapter.
Ideally, this project should enable a driver that will attach to the USB Serial Adapter and produce the SerialIo protocol to enable the UEFI terminal to become available through the USB Serial adapter.”。

简单的说如果你的系统当前有 FTDI的USB串口,那么驱动会帮你生成一个 SerialIO 供你使用。这样你就可以在UEFI 环境中使用 FTDI 的USB 串口进行通讯。
在使用之前,请确定你的USB串口设备ID,例如:我现在使用的USB串口在 Windows下面看到的信息是这样的:
image002

image001

本文并不打算做原理上的分析,只是介绍如何编译和实验。
实验环境是 UDK2015

1.在 C:\EDK\Nt32Pkg\Nt32Pkg.dsc 文件的 [Components] 段中添加下面的内容

 

Nt32Pkg/WinNtBlockIoDxe/WinNtBlockIoDxe.inf
Nt32Pkg/WinNtSerialIoDxe/WinNtSerialIoDxe.inf
Nt32Pkg/WinNtGopDxe/WinNtGopDxe.inf
Nt32Pkg/WinNtSimpleFileSystemDxe/WinNtSimpleFileSystemDxe.inf
MdeModulePkg/Application/HelloWorld/HelloWorld.inf
#LabZ_Start
FtdiUsbSerialDxe/FtdiUsbSerialDxe.inf
#LabZ_End
#
# Network stack drivers
# To test network drivers, need network Io driver(SnpNt32Io.dll), please refer to NETWORK-IO Subproject.
#

特别说明:如果有可能,最好在你当前的项目上重新这样编译一次。比如,你打算在BayTrail上用这个驱动,那么最好再Baytrail的BIOS中重新编译一次,这样最为稳妥。
2.将FtdiUsbSerialDxe目录拷贝到你UDK 的根目录下 例如: C:\EDK\
3.使用 Build 命令编译 NT32,这次是编译为 X64 的驱动。 命令式 Build –a X64
至此,驱动程序已经编译完成。可以在Build目录下找到我们需要的驱动,名称是 FtdiUsbSerialDxe.efi。 接下来我们就可以进行实验。
实验是在实体机上进行的,让一款“酷比魔方”的平板电脑和台式机进行通讯。实验中我使用了2个USB串口设备,还有一段双母头线(因为USB转串口都是公头)。

1. 首先检查一下,当前系统中没有 SerialIo Protocol
image003

2. 加载驱动image004

3. 再次检查会发现系统中多了一个 SerialIO protocol
image005
直接 dh 进行检查会发现多了2个handle 一个是驱动,一个是新增的挂protocol
image006
4. 之后使用我们前面介绍过的串口工具【参考1】
image007
看起来在启动的时候串口上会有一些乱码

在 Windows 端启动串口工具,双方可以进行通讯。
image008

完整的代码下载
FtdiUsbSerialDxe
编译好的X64 驱动下载
FtdiUsbEFI

特别注意:本文提到的代码是在 Windows7 VS2013 UDK2015环境下编译生成。

参考:
1. http://www.lab-z.com/stu91/ Step to UEFI (91) Shell下的串口测试软件

如何重新编译 16U2 的Firmware

Arduino Uno 上16U2 芯片的作用是烧写328P Firmware。具体来说,它负责把PC上的USB转为328P能够识别的串口,然后每次刷写的时候它会先用一个Pin将328P拉到 Reset状态下,然后从串口“喂”给328P 需要的Firmware.
本文介绍的就是:如何重新编译 16U2 上面的Firmware.
1. Download WinAVR 我使用的是 20100110 的安装版
2. Download LUFA-100807 特别注意,一定要这个版本,我试验过最新的版本,居然自己的测试编译项目都无法通过
2.1 Lufa 随便解压到一个目录,我是解压到C:\WinAVR-20100110\LUFA-100807
2.2 打开 cmd 命令行模式,进入目录中,使用 make 命令编译 Lufa 的工程,这是为了测试 Lufa 是否正常
image001

3. 正式的编译
3.1 16U2 Firmware source code 在\hardware\arduino\avr\firmwares\atmegaxxu2\arduino-usbserial 中,你要把整个目录copy到 Lufa 的Project目录下。例如:
image002

3.2 命令行进入这个目录,输入 make 即可编译出新的 HEX
image003

image004

最后,把这个生成的HEX文件刷写到16U2上即可。
Note:编译时你可能遇到一些奇怪的错误信息,例如 “incompatible with avr”产生的原因是 2.2 步骤测试时生成了一些非 AVR8 格式的中间文件。你重新到 Lufa 根目录下,运行 make clean 清除测试生成的中间文件即可。
上面是重新编译 16U2的方法,下面说点关于USB的事情。
我们先插入一个 Arduino Uno,在设备管理器中查看他的一些信息。

image005

其中最重要的就是 USB 的 PID 和 VID.众所周知 USB 是一种 PNP (即插即用设备),当然,实际上这个名称和实际的效果差别很远,插上之后还需要安装驱动等等动作之后才能保证你的设备能够正常使用。所谓PnP的真正含义是:连接硬件之后能够自动识别。比如插上一块PCI卡,PC能够知道它用的资源是多少,基本功能是什么。X86电脑上的设备有 Non-Pnp 和 Pnp两种:
1. Non–PnP 方式。具体实现方法有两种,一种是直接“吸收”特定内存或者IO 端口数据。以ISA串口卡为例,当这个卡看到ISA总线上出现发往 0x3F8 IO端口的数据就知道这是在和串口通讯,它会要吸收这个信号并且转发到外面的实体上;另外一种方法是直接“写死”。比方说,内置驱动程序,强行加载。大多数 x86 上的安卓系统就是这样玩的。还有可以通过 ACPI 的 ASL 来通知系统,比如:平板电脑上挂接在 I2C总线上的设备需要用这样的方式通知Windows,当前这个设备的地址传输速度等等。
2. Pnp 方式。 PCI和USB 设备就是典型的 Pnp设备。协议本身就规定了设备如何报告ID给系统,系统用这个ID来识别需要的资源和驱动等等。
从发展上来看,Pnp 是后面才出现的,他的出现也避免了很多用户设置上的麻烦。比如,很早之前,如果系统中有很多使用某个io端口的ISA卡,用户只能一次次跳线来避免冲突反复尝试。时至今日,大多数电脑从生产出来开始,用户几乎没有打开机箱的必要(由于硬件接口的不断变化,电脑升级通常只意味着更换一台)。
继续说 USB ,每个USB设备在插入之后,都会向上报告自己的 VID (Vendor ID),PID (Product ID)。通过这两个ID, 系统就能够知道这是 X 厂商生产的 Y 设备。顺便说一句,如果你系统中出现了无法识别的USB设备,你可以直接在网上搜索 USB PID VID通常能够顺利找到对应的驱动。
上面的各种USB相关数据都能搜索到,下面讲点书本之外的: PID 和 VID 怎么来的。
在世界上有一个USB组织【参考1】,他们负责制定USB标准(在电脑界,越是开放的协议越容易流行,相反那些不愿意透露一点信息而大肆吹嘘厉害的协议很少有人支持或者说更多只是骗局而已)。这个组织会负责分配 VID。当然加入这个组织是要缴纳会费的,如果没有记错的话差不多是$2000一年(几年之前比较便宜,后来涨价了。对于个人来说看着比较多,但对于公司来说只是九牛一毛)。加入这个组织可以给自己申请一个 VID 。有了 VID 之后,可以自己随心编造 PID了。PID 范围是从 0到0xFFFF 。如果你想把你的设备加入到 Windows的 Update中(就是那个有时候找不到驱动,Windows提示网上搜索的功能),正规的 VID和PID是必不可少的。
说道这里,再说一个事情:我目前用到的最好用的USB转串口芯片是 FT232系列的,稳定,支持的驱动也很全。前几年很多人发现他们原来用的好好的基于FT232 芯片的产品忽然变得不正常。最终发现,FT232的生产厂家更新了驱动,新的驱动会“毁坏”产品上的FT232芯片(PID变成0000).新的驱动是通过Windows Update分发的。而厂商这样做的原因是“市场上充斥着山寨货和假货,通过包装很难区分真正的和假冒的FT232,但芯片的硅组成上有着巨大的差异,新的FT232驱动利用这些差异,对其重组,导致这些假冒产品无法兼容现有的驱动程序。这是一个大胆的策略来减少市场上的假冒FTDI。”
下面讲不正规的做法:
1. 你选用的USB IC,比如:你的产品用了CYPRESS公司出品的EZ-USBFX的某款IC,那么你可以直接找CYPRESS,让他们给你一个 PID;
2. 网上有一家公司【参考2】出售自己的PID。他是很多年前加入USB组织的,拿到了一个 VID。然后他就在网上出售自己的PID(6万多个足够卖一阵了)。后来他们收到 USB组织的律师函,意思是说你这样做是不对的,根据协议巴拉巴拉。然后他们的回复是,我们当年加入组织的时候,你们的协议没有这样的巴拉巴拉。然后USB组织说亲我可以给你退钱,不要这样玩。他们回复,根据我们国家的消费者相关法律,购买生效之后是不接受这样的事情的……然后他们继续卖
3. 在设计的时候,可以使用兼容Windows自带驱动的协议。比如:HID 或者 mass-Storage。这样的情况下,也不用考虑 PID和VID的问题,插上就可以用。
参考:
1.http://www.usb.org/about/
2.http://www.mcselec.com/index.php?page=shop.product_details&flypage=shop.flypage&product_id=92&option=com_phpshop&Itemid=1

Step to UEFI (92)关于 ConOut 的奇怪实验

UEFI System Table 中的 ConOut-> OutputString 能够让我们直接在屏幕上输出字符串。这里介绍一种方法,能够让我们截获并且修改这个函数输出的字符串。

首先看一下 System Table 的定义在 \MdePkg\Include\Uefi\UefiSpec.h

///

/// EFI System Table

///

typedef struct {

///

/// The table header for the EFI System Table.

///

EFI_TABLE_HEADER                  Hdr;

///

/// A pointer to a null terminated string that identifies the vendor

/// that produces the system firmware for the platform.

///

CHAR16                            *FirmwareVendor;

///

/// A firmware vendor specific value that identifies the revision

/// of the system firmware for the platform.

///

UINT32                            FirmwareRevision;

///

/// The handle for the active console input device. This handle must support

/// EFI_SIMPLE_TEXT_INPUT_PROTOCOL and EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL.

///

EFI_HANDLE                        ConsoleInHandle;

///

/// A pointer to the EFI_SIMPLE_TEXT_INPUT_PROTOCOL interface that is

/// associated with ConsoleInHandle.

///

EFI_SIMPLE_TEXT_INPUT_PROTOCOL    *ConIn;

///

/// The handle for the active console output device.

///

EFI_HANDLE                        ConsoleOutHandle;

///

/// A pointer to the EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL interface

/// that is associated with ConsoleOutHandle.

///

EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL   *ConOut;

///

/// The handle for the active standard error console device.

/// This handle must support the EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.

///

EFI_HANDLE                        StandardErrorHandle;

///

/// A pointer to the EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL interface

/// that is associated with StandardErrorHandle.

///

EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL   *StdErr;

///

/// A pointer to the EFI Runtime Services Table.

///

EFI_RUNTIME_SERVICES              *RuntimeServices;

///

/// A pointer to the EFI Boot Services Table.

///

EFI_BOOT_SERVICES                 *BootServices;

///

/// The number of system configuration tables in the buffer ConfigurationTable.

///

UINTN                             NumberOfTableEntries;

///

/// A pointer to the system configuration tables.

/// The number of entries in the table is NumberOfTableEntries.

///

EFI_CONFIGURATION_TABLE           *ConfigurationTable;

} EFI_SYSTEM_TABLE;

 

其中的ConOut 是指向  EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL  的指针

具体定义可以在 \MdePkg\Include\Protocol\SimpleTextOut.h 查到

///

/// The SIMPLE_TEXT_OUTPUT protocol is used to control text-based output devices.

/// It is the minimum required protocol for any handle supplied as the ConsoleOut

/// or StandardError device. In addition, the minimum supported text mode of such

/// devices is at least 80 x 25 characters.

///

struct _EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL {

EFI_TEXT_RESET                Reset;

EFI_TEXT_STRING               OutputString;

EFI_TEXT_TEST_STRING          TestString;

EFI_TEXT_QUERY_MODE           QueryMode;

EFI_TEXT_SET_MODE             SetMode;

EFI_TEXT_SET_ATTRIBUTE        SetAttribute;

EFI_TEXT_CLEAR_SCREEN         ClearScreen;

EFI_TEXT_SET_CURSOR_POSITION  SetCursorPosition;

EFI_TEXT_ENABLE_CURSOR        EnableCursor;

///

/// Pointer to SIMPLE_TEXT_OUTPUT_MODE data.

///

EFI_SIMPLE_TEXT_OUTPUT_MODE   *Mode;

};

我们需要的是将  EFI_TEXT_STRING OutputString; 替换为我们自己的函数。

最终的代码如下

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

EFI_SYSTEM_TABLE	myST;
EFI_SYSTEM_TABLE	*pmyST=&myST;

EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL	myConOut;

EFI_STATUS
myOut (
  IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL        *This,
  IN CHAR16                                 *String
  )
{
	//Just a experiment, add a String to the output
	CHAR16 R[40]=L"LAB-Z:";
	StrCat(R,String);
	gST->ConOut->OutputString(This,R);
	
	return EFI_SUCCESS;
}
  
int
EFIAPI
main (
  IN int Argc,
  IN CHAR16 **Argv
  )
{	
    //Create a fake EFI_SYSTEM_TABLE named myST
	memcpy(&myST,gST,sizeof(EFI_SYSTEM_TABLE));	
	//Test this EFI_SYSTEM_TABLE
	  gST->ConOut->OutputString(  gST->ConOut,L"Test of gSt 1\n\r");
	pmyST->ConOut->OutputString(pmyST->ConOut,L"Test of pmyST 2\n\r");
	
	//Create a fake EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL
	memcpy(&myConOut,gST->ConOut,sizeof(EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL));	
    //Test the fake ConOut
	pmyST->ConOut=&myConOut;
	
	//If we use pmyST->ConOut it will be an error
	pmyST->ConOut->OutputString(gST->ConOut,L"Test of myConOut 3\n\r");
	
	//Replace OutputString function with our function
	pmyST->ConOut->OutputString=&myOut;
	pmyST->ConOut->OutputString(gST->ConOut,L"Test of myConOut 4\n\r");
	
	return EFI_SUCCESS;
}

 

运行结果

image001

特别需要注意的地方,如果我们写成

pmyST->ConOut->OutputString(pmyST ->ConOut,L”Test of myConOut 3\n\r”);

那么会碰到下面这个错误

image003

产生问题的代码在  \ShellPkg\Application\Shell\ConsoleLogger.c 中,看起来是向 Handle 安装另外一个 Protocol的时候出现报错信息。

  Status = gBS->InstallProtocolInterface(&gImageHandle, &gEfiSimpleTextOutProtocolGuid, EFI_NATIVE_INTERFACE, (VOID*)&((*ConsoleInfo)->OurConOut));
  if (EFI_ERROR(Status)) {
    SHELL_FREE_NON_NULL((*ConsoleInfo)->Buffer);
    SHELL_FREE_NON_NULL((*ConsoleInfo)->Attributes);
    SHELL_FREE_NON_NULL((*ConsoleInfo));
    *ConsoleInfo = NULL;
    return (Status);
  }

  gST->ConsoleOutHandle = gImageHandle;
  gST->ConOut           = &(*ConsoleInfo)->OurConOut;

  return (Status);

 

完整的代码下载:
MySt

购买 Arduino Uno 务必注意的问题

今天在刷写Arduino Uno的时候意外损坏了一个 328P 芯片(很大可能是烧写次数太多)。具体的现象是:用 AvrDudu 这样的第三方芯片都无法识别芯片型号。刚好手上有其他的 Uno板子,于是随手换了一块。新的板子可以正常识别328P和烧写,但是我发现竟然无法烧写16u2这个芯片。正常情况下将烧录器插在 16U2 的ICSP接口之后,会对板子进行供电,板子上的灯会亮,但是这块板子插上之后板子上的灯不亮,烧写器上的灯也会灭掉。
首先打开PCB电路图进行查看。

image001

ICSP1顺序如下:

image002

和16U2对应关系如下
1– Pin17 MISO
2–Pin4 VCC
3– Pin15 SCLK
4– Pin16 MOSI
5–Pin24 Reset
6—GND
然后经过测试发现排针上 GND 竟然和 VCC 是连通的,这也是为什么每次插上烧录器灯就会灭掉,这样根本就让烧录器短路了…….
这是能够正常烧写 16U2 的板子,因为使用时间很长,右上角的插座已经掉了。

image003

这是无法刷新 16U2的板子

image004

仔细查看PCB 还是能发现有些不同的,比如:这个位置,通常这样的设计是为了让走线长度相同,而另一块板子没有这样的设计。

image005

再次查看 Arduino PCB设计可以发现正常是应该有这样的走线的。

image006

所以提醒购买Uno板子的朋友,如果你发现走线上和上图有差别,请用万用表测量16U2的刷写脚,如果有短路问题,请马上联系卖家。

如果有可能不要在设计上使用 eSPI 总线

目前 Intel 推出了 eSPI 替代 LPC 。从我目前的经验来看最好不要这样做,理由如下:

  1. eSPI  频率很快,但是通常 LPC 上通常只有 EC/UART之类的不需要快速传输的设备,所以速度快只是浪费;
  2. eSPI 目前还没有好用的调试工具。之前 EC 上的 SCI/SMI中断线被“包”进了 是SPI 总线,这样无法用示波器直观的调试。之前的 SERRIRQ 也不复存在,从 Spec 上来看,可能是因为 eSPI 速度足够快,所以无需这样的中断了;如果确实需要调试,只能用 eSPI 总线的逻辑分析仪,目前感觉还不成熟,限制颇多;
  3. 使用 LPC 总线可以用来参考的设计并不多,遇到问题甚至无法获得足够的支持

综上所述,eSPI 目前看起来优势不明显,反而存在不便调试的问题,因此,不建议在设计上使用 eSPI。 当然,如果真的有一天,Intel 抽风,完全用 eSPI 取代 LPC ,也是没有办法的事情。

Step to UEFI (91) Shell下的串口测试软件

这里【参考1】提供了一个Shell下调用 SerialIO Protocol 进行通讯的 Application。下面介绍一下如何重新编译和使用这个程序。

这里我使用UDK2014下面的 EADK作为编译环境:
1. 将代码目录copy到EADK的AppPkg\Application 下面
2. 需要在 AppPkg.dsc中加入下面的代码
[LibraryClasses] 下面加入
UefiHandleParsingLib|ShellPkg/Library/UefiHandleParsingLib/UefiHandleParsingLib.inf
3. 同样使用 build –a IA32 –p AppPkg\AppPkg.dsc 进行编译
4. 编译之后即生成了 Serial-Test.efi

我选择在 VirtualBox 中测试这个 Application。把它放在一个 ISO 之中,挂接启动到 UEFI虚拟机中,然后在 FS0: 下面即可看到这个 Application。

image001

同样,虚拟机中需要打开串口,我是采用 pipe通讯的方法在虚拟机中模拟出来com1。

image002
之后再打开putty,设置如下:
image003
双方连接之后即可进行通讯。
image004

image005

可以看到双方能够进行正常的通讯。
image006

本文提到的代码下载:
SerialTest
制作好的 ISO下载
test

特别提醒:VirtualBox 的BIOS有一些问题(至少5.0.20 r106931依然如此),无法彻底关闭Redirection功能,所以如果你要用它来实验一些串口相关内容时,需要特别注意,显示在虚拟机EFI Shell下面的东西还会发送一份到串口上。
参考:
1. https://github.com/tianocore/edk2/tree/master/OptionRomPkg/Bus/Usb/FtdiUsbSerialDxe

WinDBG 调试 Virtual Box 中的Windows 10

最近实验了一下 WinDBG 调试 VirtualBox 中的 Win10,简单介绍一下方法:

1. VirtualBox的设置(特别注意,名称上最好不要用 com1 之类的,有可能会和系统设备冲突)

windbg2

2.WinDbg的设置

windbg1

3.进入 Windows10 之后打开 debug 功能。在 Windows看起来是通过串口进行调试的

WinDBG4

4. 启动 WinDBG 进行调试

windbg3

Arduino模拟鼠标的又一种方法

Arduino 模拟鼠标目前有三种方法:
1.外部加电阻 USB母头等元件,然后烧录模拟程序,用 328P作为处理器。这种方法的缺点是:原件多,不容易调试,占用板载资源多,做出来之后基本上不能完成什么功能了(因为Usb低速设备传输要求1.5Mb/s,而328P最高只有16MHz),有兴趣的朋友可以看一下之前我做的一个锁屏的装置【参考1】;
2.直接使用Leonardo 这样主控是ATmega32u4【参考2】 的板子。这种方法的好处是:Arduino 原生库支持,资料比较多,调试方便。个人推荐初学者如果有鼠标键盘的需要可以玩这个;
3.原版的Arduino Uno 上面使用的串口芯片是 16u2,可以给这个芯片刷写上一个特殊的Firmware,它和PC端用USB鼠标或者键盘通讯,然后和 328P 使用串口通讯。
本文介绍的就是第三种方法。
在玩第三种方法的时候,你需要特别准备一个烧写器。我用的是 USBTINY 这款。

image001

本次实验的目标是将uno模拟成鼠标。参考的资料来自下面的页面:

http://hunt.net.nz/users/darran/weblog/cca39/Arduino_UNO_Mouse_HID.html

我刷写的工具是 AvrDudess 2.4,用法很简单,接线之后(建议选购下载器的时候直接选带完整线的,否则每次接线也是很麻烦的事情),按下 Detect按钮,软件需要检查到正确芯片的类型,比如,我的转接芯片是 16u2。如果无法侦测,那么请检查连线。如果折腾了很久都不行,那么请联系卖家所要驱动和刷写工具。刚开始的时候我就在这里折腾了很长时间。

image002

image003

因为串口芯片被刷掉了,所以接下来也必须使用刷写器写入编译好的Arduino 程序。
image004

输入程序,确定编译无误

/* Arduino USB Mouse HID demo */

/* Author: Darran Hunt
 * Release into the public domain.
 */

struct {
    uint8_t buttons;
    int8_t x;
    int8_t y;
    int8_t wheel;	/* Not yet implemented */
} mouseReport;

uint8_t nullReport[4] = { 0, 0, 0, 0 };

void setup();
void loop();

void setup() 
{
    Serial.begin(9600);
    delay(200);
}

/* Move the mouse in a clockwise square every 5 seconds */
void loop() 
{
    int ind;
    delay(5000);

    mouseReport.buttons = 0;
    mouseReport.x = 0;
    mouseReport.y = 0;
    mouseReport.wheel = 0;

    mouseReport.x = -2;
    for (ind=0; ind<20; ind++) {
	Serial.write((uint8_t *)&mouseReport, 4);
	Serial.write((uint8_t *)&nullReport, 4);
    }

    mouseReport.x = 0;
    mouseReport.y = -2;
    for (ind=0; ind<20; ind++) {
	Serial.write((uint8_t *)&mouseReport, 4);
	Serial.write((uint8_t *)&nullReport, 4);
    }

    mouseReport.x = 2;
    mouseReport.y = 0;
    for (ind=0; ind<20; ind++) {
	Serial.write((uint8_t *)&mouseReport, 4);
	Serial.write((uint8_t *)&nullReport, 4);
    }

    mouseReport.x = 0;
    mouseReport.y = 2;
    for (ind=0; ind<20; ind++) {
	Serial.write((uint8_t *)&mouseReport, 4);
	Serial.write((uint8_t *)&nullReport, 4);
    }
}

 

接线如下:

用 IDE 上传内容,需要一些设置,指定刷写工具
image005
然后使用 File->Upload Using Programmer 来进行上传
image006
上传成功:
image007

成功之后,用Arduino Usb口连接电脑,你的鼠标每隔一段会自动旋转一圈,同时在设备管理器中会出现一个鼠标设备:
image008

这个和16u2 Firmware source code(Descriptors.c)中定义是相同的
.VendorID = 0x03EB,
.ProductID = 0x2041,
.ReleaseNumber = 0x0000

最后,特别提醒:设计模拟USB鼠标键盘之类的程序时,一定要考虑留多加一个运行条件。比如:某个引脚设定为低时才运行,或者上电10s才运行。否则有可能出现程序正常,但是因为键盘鼠标的干扰你没有再重新刷写新的代码的机会。

本文提到的16u2特别的 Firmware 下载 Arduino-mouse-0.1 源程序 arduino-mouse-0.1.tar

参考:
1. http://www.lab-z.com/20140101/ 用 Arduino 打造一个自动锁屏装置
2. http://www.arduino.cn/thread-1205-1-1.html Arduino Leonardo 中文介绍