好玩的电子仪器(1)

本系列文章将会介绍一些电子工程师用到的“奇怪”设备。
对于我这样的Firmware工程师来说,硬件充满了神秘,而对于硬件工程师来说,射频更加神秘。
这几天组里的WIFI工程师在摆弄一台机器,好奇之下,我和他聊了一下,他介绍说这是一台无线信号分析器。能够捕获和分析无线信号。
image002

好奇之下,我第二天拿了一个玩具的遥控器给他请他帮忙分析。
image004

他先是询问了一下大概的频率,我说不清楚可能是 315M 的。他就设定扫描100-400M 的频率范围,我一边拨动开关让遥控器工作起来,设备就开始扫描和记录这个范围内的无线信号。很快,我们就在 108Mhz 上看到了信号。之后他又设定了 0-200Mhz作为扫描范围。结果发现,真正的信号是 54Mhz,刚才看到的108Mhz只是一个倍频。结果就是下面这个图片。

image006

后来偶然间我把遥控器翻了过来,上面贴着标签,赫然写着“54Mhz”,看起来这个遥控器频率确实是 54Mhz。
之后我又和他聊了一下,我问他是否有可能用这个设备分析出具体的信号,然后我可以用其他发射器做一个遥控器。他说无法做到,因为这个设备只能用来扫描载波的频率,而实际上有着多种多样的调制方式。比如,我们可以用这个设备来看到2.4G 鼠标和USB3.0 在频率段上冲突,但是具体的冲突方式是无法看到的。
我们实验室是开放的,在刚才的测试过程中还乱入了一个100Mhz 的信号(箭头所示)。真正的分析应该是在铁皮包裹的无线电屏蔽室完成的。比如,之前我在的一家公司,产线生产出来的蓝牙模块要到这样屏蔽室来完成最后的调整和测试。
image008

最后再补充一个八卦的故事,讲讲这个屏蔽室的故事。前面说了,我之前工作的一家公司为了测试蓝牙,弄了个屏蔽室。这个房间很小,建在公司一个大办公室的中间的,10个平方,或者说30个立方更合适,估计有厨房或者厕所那么大。这种专业性很强的事情只能找专业的公司做,台湾人也有相互扶助的传统,找了另外一家台湾公司。建好了之后,承建方发现最大的问题是收不上来钱,每次要钱财务总是说等研发验收或者托词主管不在。

貌似拖欠款项是我之前那家公司的传统,原因是拖欠的货款就是自己的利润,打个比方:拖欠1万,存在余额宝,每天就是八毛多,老板可以多吃一个鸡蛋。如果拖欠10万,那么老板的早餐就有着落了。我在的时候遇到过几次元件供应商因为收不上钱而停止供货的事情。老板就会压着研发换物料。据说之前还发生过逢年过节,供应商开着卡车堵大门的事情,只是我没有碰到过,想必也很热闹。

后来,承建屏蔽室的哥们怒了,直接打电话给财务,说我叔叔是“立委”的 xxx,如果我明天还看不到钱,我就召开记者发布会让你们上《苹果日报》。财务接电话的不是台湾人,琢磨半天“立痿”是什么毛病。后来和他老板汇报了一下,老板急忙查了一下,真有这么个人,也甭管是三叔二叔还是表叔,自己理亏,还是把钱还了吧。第二天,财务主管主动打电话过去主动道歉,跪舔一番,欠款如数奉上,这个事情就了结了。

再后来,还是这个屏蔽室,测试蓝牙,买了一套软件能够自动扫频率和调教模块。结果又是欠对方钱,双方扯皮,只是对方早有防备,软件中加了一个什么逻辑锁,试用期结束后不能自动测试。老板灵机一动,找一批操作工三班倒,简单培训后就上岗,用设备纯手工测试,节省环保还智能……….

有诗为证:

小小公司真奇妙,
老板看着似土豪。
怎料长欠不愿还,
欲想追债只能闹。

自动解压ZIP的批处理

比如,我在一个目录下有大量的 zip 文件,我想解压之,就可以使用下面的批处理

for %%i in (*.zip) do 7z x %%i -o%%~ni

其中:

for %%i in (*.zip) 是枚举目录下的全部 zip 文件名

7z x %%i 是按照完整路径解压的意思

-o%%~ni 是解压到文件名相同的目录的意思

比如,我有一个zip文件名称是 labz.zip 那么运行之后,会生成一个 labz 的目录,然后将文件内容解压进去

Pin13 上LED亮度减半的问题

网上有朋友提了一个问题“arduino怎么把13号引脚的亮度减小一半”。自然而然的想法是用PWM做,查一下资料,非常不幸 Uno的Pin13不是PWM。

pin13

我们只能采用模拟PWM的方式来做。首先想到的是直接在 Timer0 的ISR (在\hardware\arduino\avr\cores\arduino\wiring.c)上加入内容。但是实验结果非常糟糕,肉眼能够看到LED在闪烁。于是,需要自己设置一个快速的中断,然后在里面写拉高低的代码。
最终找到tsaiwn 编写的代码【参考1】。直接在上面修改:
设定的亮度有十级别,0是最亮,9是最暗。

// 控制 LED 亮滅, 每秒閃爍 5000 次: 亮 0.0001 秒滅 0.0001 秒 …
// Prescaler 用 64
volatile int ggyy = 1; // 使用這當 Flag 給 ISR 使用 !
int ledPin =13;

unsigned char myPWM=0;
unsigned char myPCounter=0;
/// For Prescaler == 64
/// 1 秒 / (16 000 000 / 64) = 1/250000 = 0.000004 sec / per cycle
/// 0.1 sec / 0.000004 sec -1 = 25000 -1 = 24999
/// 0.0001 sec / 0.000004 sec -1 = 25 -1 = 24
const int myTOP = 24; // 0.0001 sec when Prescaler == 64
///// Interrupt Service Routine for TIMER1 CTC on OCR1A as TOP
/// 注意以下名稱是有意義的, 不可亂改 !
ISR(TIMER1_COMPA_vect)
{
//digitalWrite(ledPin, ggyy); // ggyy 是 0 或 1
//ggyy = 1 – ggyy; // 給下次進入 ISR 用
myPCounter++;
if ((myPCounter % 10) >= myPWM)
{
digitalWrite(ledPin, HIGH);
}
else
{
digitalWrite(ledPin, LOW);
}

}
void setup( ) {
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW); // turn Off the LED
cli(); // 禁止中斷
TCCR1A = 0;
TCCR1B = (1<

【入门指南】图解如何使用 USBTinyIsp

每一个Arduino 的玩家都应该准备一个 ISP 下载器,有了它能让你的 Uno 玩出很多花样。这里就介绍一下如何使用 USBTinyISP。本文将介绍使用这款工具的三种刷写方式:

  • 第三方软件刷写 328P BootLoader的方法
  • Arduino IDE 刷写 328P BootLoader的方法
  • Arduino IDE 刷写328P上面程序的方法

首先,你要有一个USBTinyISP烧录器。下面的是购买自淘宝的 OCROBOT 出品,价格是 45元,属于较贵的。
image001

这款需要安装驱动才能工作,淘宝很多出售的号称不需要驱动程序,我也曾经入手过一个,一直无法使用。因此,建议购买之前要和卖家沟通好,索要驱动,特别的如果卖家要求关闭数字签名才能安装驱动也请不要购买。这样做会让你的系统暴露在风险之中,另外,64位Windows由于本身的特性无法关闭数字签名功能。

插入之后安装驱动,驱动来自【参考1】。安装之后是下面这样(特别提醒,如果插入之后出现无法获得USB Descriptor 的提示,那么请更换一个USB端口,产生这个问题的原因可能是某些USB3.0端口和这个卡上的控制芯片有兼容问题)

image002

image004

image003

image005

Arduino UNO 上有2个烧录口,一个是给 USB 转串口芯片使用的,一个是给 328P 使用的。通常我们需要烧录的是328P 【参考】。也就是下图中的橘色标记口。
image006

直接将USBTinyISP的头插入这个位置即可。需要注意的是:

  1. 建议切断Uno上的外部供电之后进行烧写;
  2. 插入是有方向的,插入之后无论正反,板子上的灯都会亮。但是反插无需担心烧毁

至此,硬件部分准备完毕,下面就是如何使用软件进行烧写了。

  • 第三方软件Avrdudess刷写 328P BootLoader的方法

运行 Avrdudess,先选择 USBTinyISP

image007
然后使用右侧Detect 按钮进行芯片识别。正常情况下,主芯片应该是328P,USB转串口芯片应该是16U2或者8U2如果没有识别,应该是插线反了导致的,请调换方向重新插入
image008
最后,选择要烧写的文件,再使用GO 按钮即可进行烧录
image009
特别提醒:不要调整右侧 Fuses 选项。

二.      Arduino IDE 刷写 328P BootLoader的方法

  1. Tools->Board->选择你的板子型号,比如:Uno
  2. Tools->Programmer->USBTinyISP 选择烧写器的型号

image0103.Tools->BurnBootloader 直接烧写 328P 的Bootloader

image011

烧写成功的话,会有下面的提示信息

image012

  • Arduino IDE 刷写328P上面程序的方法

某些情况下,我们需要直接烧写328P上面的程序,方法如下:

  1. IDE中保证你的代码能够正常编译通过
  2. IDE中选择板子型号和烧写器型号和上面介绍的步骤一样
  3. 使用File->Upload Using Programmer

image013
运行成功后有下面类似提示

image014

本文首发 http://www.arduino.cn/thread-21619-1-1.html

文章中提到的工具和驱动也可以在上面的网址找到

参考:

  1. https://learn.adafruit.com/usbtinyisp/drivers 其实国内大多数 Arduino 产品都是仿造国外的而已,软件和Firmware一直是软肋。
  2. https://www.lab-z.com/ardfim/

Step to UEFI (94)Source Level Debug

直接进行 Source Level的 Debug 对于学习过是非常有用,本文就介绍一下如何在 NT32Pkg的模拟环境下进行 Source Level Debug 的方法。使用的环境是 Windows7 + UDK2015+VS2013。调试的目标是 UDK2015 AppPkg 自带的 Hello。

在 Hello.INF 中加入下面的编译参数:

[BuildOptions]
MSFT:DEBUG_*_IA32_DLINK_FLAGS = /EXPORT:InitializeDriver=$(IMAGE_ENTRY_POINT) /ALIGN:4096 /FILEALIGN:4096 /SUBSYSTEM:CONSOLE

 

此外,在代码中加入_asm int 3 ,当编译器遇到这个命令时,会自动停止准备调试

INTN
EFIAPI
ShellAppMain (
  IN UINTN Argc,
  IN CHAR16 **Argv
  )
{
  _asm int 3  
  Print(L"Hello there fellow Programmer.\n");
  Print(L"Welcome to the world of EDK II.\n");

  return(0);
}

 

上述修改完成后,正常编译

Build –a IA32 –p AppPkg\AppPkg.dsc

编译完成之后,在NT32 模拟环境中运行 hello.efi, 会出现下面的错误信息,选择使用 VS2013 调试
image001

然后就可以看到带着源码的调试界面了
image003

我试验了一下,感觉还不错。

一般情况下BIOS工程师不会进行源码级别的调试,最直接的原因是:这样的调试过于复杂,必须额外的硬件支持,要么需要 AMI Debugger ,要么需要 XDP 之类的高级工具,单纯的准备就需要花费很多时间。

自从有了UEFI 的架构,串口 Debug 信息是 BIOS 工程师最忠实的伙伴。

【注释】:本文根据《UEFI 原理与编程》一书 P59 3.4 调试 UEFI 写成。只是这本书上给出来的方法我试验并不好用,疑惑之间搜索到了该书作者在他的Blog上提到这个问题,试验之后完全没有问题:

http://www.cppblog.com/djxzh/archive/2015/02/08/209766.html
“书中讲到了如何利用_asm int 3 调试代码。
_asm int 3需要配合Nt32Pkg使用。也就是说通过Nt32Pkg编译出的.efi文件才能够调试。
如果你带_asm int 3语句的工程是通过非Nt32Pkg编译出来的(例如AppPkg),在SecMain模拟器中调试会导致断点停在Image.c文件如下代码中
Image->Status = Image->EntryPoint (ImageHandle, Image->Info.SystemTable);
在模拟器控制台会输出
WARNING: No source level debug
表明SecMain在加载你的模块时没有成功加载调试符号。
解决方案
在.inf文件中添加如下代码
[BuildOptions]
MSFT:DEBUG_*_IA32_DLINK_FLAGS = /EXPORT:InitializeDriver=$(IMAGE_ENTRY_POINT) /ALIGN:4096 /FILEALIGN:4096 /SUBSYSTEM:CONSOLE

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