购买 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 中文介绍

Step to UEFI (90) 给Shell 加入一个 command

上一次【参考1】介绍的屏幕旋转项目中还带有 Shell Command的内容。就是说可以把他调用驱动的Application”包”到Shell 中。这次介绍一下具体的实现。
源代码放置的位置目录和上一次实验相同。之后进行下面的步骤:
1.在 C:\EDK\ShellPkg\ShellPkg.dsc的 增加内容:

  ShellPkg/Application/Shell/Shell.inf {
    <LibraryClasses>
      NULL|ShellPkg/Library/UefiShellLevel2CommandsLib/UefiShellLevel2CommandsLib.inf
      NULL|ShellPkg/Library/UefiShellLevel1CommandsLib/UefiShellLevel1CommandsLib.inf
      NULL|ShellPkg/Library/UefiShellLevel3CommandsLib/UefiShellLevel3CommandsLib.inf
!ifndef $(NO_SHELL_PROFILES)
      NULL|ShellPkg/Library/UefiShellDriver1CommandsLib/UefiShellDriver1CommandsLib.inf
      NULL|ShellPkg/Library/UefiShellInstall1CommandsLib/UefiShellInstall1CommandsLib.inf
      NULL|ShellPkg/Library/UefiShellDebug1CommandsLib/UefiShellDebug1CommandsLib.inf
      NULL|ShellPkg/Library/UefiShellNetwork1CommandsLib/UefiShellNetwork1CommandsLib.inf
!ifdef $(INCLUDE_DP)
      NULL|ShellPkg/Library/UefiDpLib/UefiDpLib.inf
!endif #$(INCLUDE_DP)
!endif #$(NO_SHELL_PROFILES)
##LABZDEBUG_Start
      NULL|GopRotatePkg/Library/GopRotateShellCommandLib/GopRotateShellCommandLib.inf
##LABZDEBUG_End	  
  }

 

2. 使用编译命令 重新编译 Shell。具体方法还可以在【参考2】看到

build by build -a IA32 -p ShellPkg\ShellPkg.dsc -b RELEASE

3.正常编译之后shell.efi 可以在这个目录中找到 C:\EDK\Build\Shell\RELEASE_MYTOOLS\IA32

4.从C:\EDK\Nt32Pkg\Nt32Pkg.fdf) 可以看到,NT32Pkg 用的是 FullShell

5.用生成的Shell .efi 替换C:\EDK\EdkShellBinPkg\FullShell\Ia32中的 Shell_Full.efi

6.用 Build 重新编译Nt32 项目,然后再用 Build run 运行模拟器

7.在模拟器中先加载Driver Load GopRotate.efi

8.枚举一下当前Shell中有 GraphicsOutput Protocol支持的 Device Handle。模拟器中有两个设备,分别对应2个窗口
image002

9.设置其中一个旋转,效果如下
image003

10.再旋转另外一个
image005

下面是重新编译通过的 shell 有兴趣的朋友可以直接使用

Shell_Full

参考:

1. http://www.lab-z.com/stu88/ Step to UEFI (88) 一个转屏驱动
2. http://www.lab-z.com/how2buildshell/ Step to UEFI (35) —– How to build Shell.efi

Step to UEFI (89) 内存访问

初学 Watcom C的时候遇到一个问题“如何访问指定的内存”。当时为了这个问题花费了不少功夫,最后才发现直接用指针就可以进行访问,因为过于简单以至于网上都没有人问过…….
最近看 UEFI 编程,同样也遇到了这个问题,我去查找了 mm命令的source code,看到了它使用了PCI Root Bridge I/O Protocol 这个Protocol,然后就去研究之。同样越研究越迷糊,最终发现虽然这个Protocol提供了内存访问函数,但是本质上依然使用指针来直接访问。出于保护模式下的内存,任何其他方法都有“脱了裤子放屁—–多此一举”之嫌。
参考 mm 源程序,很快写出代码:

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

#include <Library/MemoryAllocationLib.h>
#include  <Protocol/DeviceIo.h>

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

typedef enum {
  EfiPciWidthUint8,
  EfiPciWidthUint16,
  EfiPciWidthUint32,
  EfiPciWidthUint64
} DUMMY;

VOID
ReadMem (
  EFI_IO_WIDTH  Width,
  UINT64        Address,
  UINTN         Size,
  VOID          *Buffer
  )
{
  do {
    if (Width == EfiPciWidthUint8) {
      *(UINT8 *) Buffer = *(UINT8 *) (UINTN) Address;
      Address -= 1;
    } else if (Width == EfiPciWidthUint16) {
      *(UINT16 *) Buffer = *(UINT16 *) (UINTN) Address;
      Address -= 2;
    } else if (Width == EfiPciWidthUint32) {
      *(UINT32 *) Buffer = *(UINT32 *) (UINTN) Address;
      Address -= 4;
    } else if (Width == EfiPciWidthUint64) {
      *(UINT64 *) Buffer = *(UINT64 *) (UINTN) Address;
      Address -= 8;
    } else {
      Print(L"Can't read memory at %X",Width);
      break;
    }
    //
    //
    //
    Size--;
  } while (Size > 0);
}

int
EFIAPI
main (
  IN int Argc,
  IN CHAR16 **Argv
  )
{
	CHAR8		*Mem1;
	UINT32		Buffer;

	Mem1=AllocatePool(4);
	*(Mem1+0)='L'; 
	*(Mem1+1)='A';
	*(Mem1+2)='B';
	*(Mem1+3)='Z';
	Print(L"Memory Address: %X\n",Mem1);
	Print(L"%X\n",*Mem1);
	Print(L"%X\n",*(Mem1+1));
	Print(L"%X\n",*(Mem1+2));
	Print(L"%X\n",*(Mem1+3));
	
	ReadMem(EfiPciWidthUint32, (UINT64)Mem1, 1, &Buffer);
	
	Print(L"Read[%X]=%X\n",Mem1,Buffer);
	
	FreePool(Mem1);
	
  return EFI_SUCCESS;
}

 

在NT32模拟器中实验时发现,模拟环境中不是所有的内存空间都是可以直接访问的。稍有不慎就会得到错误信息,模拟器也会随之崩溃。于是,代码是创建一个4 Bytes长的内存空间,写入一些字符,然后再 ReadMem 读取出来,这样做能够保证访问的内存是可以被正常操作的。

运行结果:

ma

参考的 mm.c 来自EfiShell 1.06\Shell\mm\mm.c。
mm

本文提到的完整代码下载
ReadMEM

UDK2015 + Windows 7

UDK2015 出来一段时间了,之前的文章也介绍过【参考1】。只是有一个严重的问题: UDK2015 下面 C编写的工具是高版本的 Visual Studio 编译的,并且没有设置对于 XP 的兼容,于是 XP 下面无法直接使用这些工具。一种解决的办法是重新编译用来build的工具,另外一种就是更换你的操作系统为 Windows 7。

周末花了一点时间在虚拟机中安装了一个 Windows7 然后配置了 Vs2008和 UDK2015 ,于是目前可以做到虚拟机下面的 UDK2015 的编译了。有需要的朋友可以直接下载,安装 Virtual Box 之后就可以直接使用。

链接:http://pan.baidu.com/s/1jIfdzhk 密码:x04z

2016/06/10 补充

今天尝试在上面安装 EADK 发现直接编译不过,后来比较了一下文件发现 UDK2015 ShellPkg中的一些Pkg被删掉了,AppPkg.dsc 中有用到。我尝试从 UDk2014 中直接比较补充了那几个 Pkg,编译能够通过。不过这样的话,建议有需要的朋友同时安装 UDK2014。

参考:
1.http://www.lab-z.com/udk2015/ UDK2015来了

Step to UEFI (88) 一个转屏驱动

这次介绍一个通过驱动程序旋转屏幕的项目,地址是https://github.com/apop2/GopRotate 。项目的简介是“A EDK2 Package that supplies a UEFI driver that will bind on top of Graphics Output Devices and rotate any BLT operations by 0, 90, 180 or 270 degrees.”。

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

实验环境是 UDK2014
1.在 C:\EDK\Nt32Pkg\Nt32Pkg.dsc 文件的 [Components] 段中添加下面的内容

  MdeModulePkg/Application/VariableInfo/VariableInfo.inf
  MdeModulePkg/Universal/PlatformDriOverrideDxe/PlatformDriOverrideDxe.inf
##LABZDebug_Start  
  GopRotatePkg/GopRotate/GopRotate.inf
##LABZDebug_End
###################################################################################################
#
# BuildOptions Section - Define the module specific tool chain flags that should be used as
#                        the default flags for a module. These flags are appended to any 

 

2.将 GopRotatePkg 目录拷贝到你UDK 的根目录下 例如: C:\EDK\
3.使用 Build 命令编译 NT32
4.使用 build run 运行模拟器
至此,驱动程序已经编译完成。下面要编译使用这个驱动的 Application。
5.将GopRot 按照一个普通的Application编译
编译完成后可以进行实验了。
6.使用 load goprotate.efi 加载驱动
image001

7.输入 goprot.efi 2 进行测试。
运行之前的屏幕是这样的:
image003
运行之后屏幕就变成这样了
image005

完整的代码下载

前面提到的驱动项目完整代码
GopRotatePkg

调用驱动的应用程序代码
GopRot