Step to UEFI Shell (3) —-获得命令行参数的方法

这里介绍如何获得Shell下的命令行参数。关键代码很简单,如下:

EFI_STATUS
EFIAPI
ShellAppMain (
	IN	UINTN 	Argc,
	IN 	CHAR16	**Argv
)
{
UINTN 	i;

	Print (L"Total %d args\n",Argc);
	for (i=0;i<Argc;i++)
 	{
	  Print (L"Arg[%d]= %s \n",i,*(Argv+i));
	}
	return EFI_SUCCESS;
}

 

uefistep3

完整的代码 HelloWorld2

获得当前硬盘上全部盘符

这一系列文章是根据cutebunny 的BLOG “windows的磁盘操作” 写成的,主要是部分修改原作中的代码,使之兼容Unicode和Windows 7 64bit. 原文可以在下面的网址找到

http://cutebunny.blog.51cto.com 。 本文是参考 “windows的磁盘操作之五——获取物理磁盘上的所有逻辑分区号”写成。

程序实现了获得当前硬盘上全部盘符的功能(分区可能没有分配盘符,比如:win7一般都会隐藏起来最前面的分区)。

// emuPart.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "windows.h"

/******************************************************************************
* Function: get disk's physical number from its drive letter
*           e.g. C-->0 (C: is on disk0)
* input: letter, drive letter
* output: N/A
* return: Succeed, disk number
*         Fail, -1
******************************************************************************/
DWORD GetPhysicalDriveFromPartitionLetter(TCHAR letter)
{
    HANDLE hDevice;               // handle to the drive to be examined
    BOOL result;                 // results flag
    DWORD readed;                   // discard results
    STORAGE_DEVICE_NUMBER number;   //use this to get disk numbers
 
    TCHAR path[MAX_PATH];
    wsprintf(path, L"\\\\.\\%c:", letter);
    hDevice = CreateFile(path, // drive to open
                         GENERIC_READ | GENERIC_WRITE,    // access to the drive
                         FILE_SHARE_READ | FILE_SHARE_WRITE,    //share mode
                         NULL,             // default security attributes
                         OPEN_EXISTING,    // disposition
                         0,                // file attributes
                         NULL);            // do not copy file attribute
    if (hDevice == INVALID_HANDLE_VALUE) // cannot open the drive
    {
        fprintf(stderr, "CreateFile() Error: %ld\n", GetLastError());
        return DWORD(-1);
    }
 
    result = DeviceIoControl(
                hDevice,                // handle to device
                IOCTL_STORAGE_GET_DEVICE_NUMBER, // dwIoControlCode
                NULL,                            // lpInBuffer
                0,                               // nInBufferSize
                &number,           // output buffer
                sizeof(number),         // size of output buffer
                &readed,       // number of bytes returned
                NULL      // OVERLAPPED structure
            );
    if (!result) // fail
    {
        fprintf(stderr, "IOCTL_STORAGE_GET_DEVICE_NUMBER Error: %ld\n", GetLastError());
        (void)CloseHandle(hDevice);
        return (DWORD)-1;
    }
    printf("%d %d %d\n\n", number.DeviceType, number.DeviceNumber, number.PartitionNumber);
 
    (void)CloseHandle(hDevice);
    return number.DeviceNumber;
}

/******************************************************************************
* Function: get disk's drive letters from physical number
*           e.g. 0-->{C, D, E} (disk0 has 3 drives, C:, D: and E:)
* input: phyDriveNumber, disk's physical number
* output: letters, letters array
* return: Succeed, the amount of letters
*         Fail, -1
******************************************************************************/
DWORD GetPartitionLetterFromPhysicalDrive(DWORD phyDriveNumber, TCHAR **letters)
{
    DWORD mask;
    DWORD driveType;
    DWORD bmLetters;
    DWORD diskNumber;
    TCHAR path[MAX_PATH]; 
    TCHAR letter;
    DWORD letterNum;
    WORD i;
    TCHAR *p;
 
    bmLetters = GetLogicalDrives();
    if (0 == bmLetters)
    {
        return (DWORD)-1;
    }
 
    letterNum = 0;
    for (i = 0; i < sizeof(DWORD) * 8; i++)
    {
        mask = 0x1u << i;
        if ((mask & bmLetters) == 0)        //get one letter
        {
            continue;
        }
        letter = (TCHAR)(0x41 + i);    //ASCII change
        wsprintf(path, L"%c:\\", letter);
        driveType = GetDriveType(path);
        if (driveType != DRIVE_FIXED)
        {
            bmLetters &= ~mask;     //clear this bit
            continue;
        }
        diskNumber = GetPhysicalDriveFromPartitionLetter(letter);
        if (diskNumber != phyDriveNumber)
        {
            bmLetters &= ~mask;     //clear this bit
            continue;
        }
        letterNum++;
    }
 
    //build the result
    *letters = (TCHAR *)malloc(letterNum);
    if (NULL == *letters)
    {
        return (DWORD)-1;
    }
    p = *letters;
    for (i = 0; i < sizeof(DWORD) * 8; i++)
    {
        mask = 0x1u << i;
        if ((mask & bmLetters) == 0)
        {
            continue;
        }
        letter = (TCHAR)(0x41 + i);    //ASCII change
        *p = letter;
		printf("%c",letter);
        p++;
    }
   
    return letterNum;
}

int _tmain(int argc, _TCHAR* argv[])
{
	TCHAR	*List;

	GetPartitionLetterFromPhysicalDrive(0,&List);

	getchar();
	return 0;
}

 

运行结果如下 (注意,运行时需要管理员的权限)
emupart

最后一行给出了PhysicalDrive0 上的三个盘符

参考:
1.cutebunny 的BLOG “windows的磁盘操作” 可以在这里下载 WindowsDisk

使用 Arduino 打造一个硬件的Watchdog

我的台式机使用梅捷 SY-D2700-U3M 的主板,固化了Intel D2700 Atom CPU。具有零噪音(无风扇),节能环保,扩展丰富等等特点。唯一的问题是:经常硬盘丢失。具体是在使用中忽然BSOD,然后自动重启之后在Setup中无法找到硬盘,重启无效,必须关机一次再开机。从我的经验来看这绝非BIOS问题,并且实验确定不是硬盘问题。有人看守的情况下遇到这样的最多只是郁闷一些,但是作为下载机的时候,上述的问题就变得令人难以忍受。于是,考虑做一个硬件的 WatchDog,当出现问题的时候自动完成关闭开关,再次重启的动作。

BOM 如下:

1. Arduino UNO
2. WCH341 USB转串口
3. 面包板
4. 杜邦线+大头针
5. 继电器

说说上面这些东西,最重要的是继电器,就是下面这个东西。

1

这是两路的继电器,可以控制2组电路。性能指标是“交流电压最大250V,交流电流最大10A,直流电压最大30V,直流电流最大10A”。有4个输入针脚,分别是VCC GND,1IN 和2IN可以直连Arduino IO口。上电之后,1IN和2IN可以直接接入到VCC和GND上,应该能听到继电器“啪”的吸合的声音,此时测量受控端的电阻可以得知当前是断开还是链接。

2

再说一下喂狗的WCH341。 一般来说,Arduino的USB口可以用来取电和串口通讯。实际使用中我关闭台式机后,USB口供电也会随之切断,因此Arduino必须使用外部供电。既然使用了外部供电,串口也只能使用外部输入。刚开始选择的是FDTI 的USB转串口,一直无法正常通讯,最后测量发现它转出来的电压在 6.2v左右(TX/RX)远超过了.预期的5V。WCH341是之前玩单片机的剩下的,测量之后发现它的输出电压能够满足要求,于是就选用之。还有需要注意的是,完整的串口通讯实际上需要3跟线:TX RX和GND.

引出PowerButton的2个Pin.

3

继电器直接控制PowerButton Pin。

4

绿线接到继电器上进行控制

5

Arduino代码:

#include //引用库
/*
Simple LED sketch
*/

int led = 13; // Pin 13 当连接(相当于按下PowerButton)时点亮,断开时关闭
int cnt = 10; // Pin 10 控制继电器,高时断开,低时连接

unsigned long int elsp=0; //计算时间

void setup()
{
pinMode(led, OUTPUT); // Set pin 13 as digital out
pinMode(cnt, OUTPUT); // Set pin 10 as digital out
digitalWrite(led, LOW); // off
digitalWrite(cnt,HIGH); //Cut OFF

// Start up serial connection
Serial.begin(9600); //baud rate
Serial.flush();
elsp=millis();

wdt_enable(WDTO_8S); //这是Arduino的看门狗可以更换其他时间值
//注意,这里是8s,后面的延时不可以直接超过8s
}

void loop()
{

// Read any serial input
while (Serial.available() > 0)
{
if ((char) Serial.read() == ‘P’) {elsp=millis(); }
//当收到”P”时,重置计时器。换句话,我们用字符 “P” 来喂狗
}

if (abs(millis()-elsp)>300*1000UL) //300s = minutes
{
digitalWrite(led, HIGH); // on
//关机动作
digitalWrite(cnt,LOW); //继电器短路,相当于按下PowerButton
wdt_reset(); //重置Arduino内部看门狗
delay(6000); //这里相当于长按PowerButton 6s
wdt_reset(); //重置Arduino内部看门狗
digitalWrite(cnt,HIGH); //继电器断路,相当于松开PowerButton
//松开PowerButton 5s
delay(5000);
wdt_reset(); //重置Arduino内部看门狗
//开机上电
digitalWrite(cnt,LOW); //继电器短路,相当于按下PowerButton
delay(500); //按下500ms
digitalWrite(cnt,HIGH); //继电器断路,相当于松开PowerButton
elsp=millis();

digitalWrite(led, LOW); // off
wdt_reset(); //重置Arduino内部看门狗
}
delay(1000);
Serial.println(millis()-elsp);
wdt_reset(); //重置Arduino内部看门狗如果loop时间超过时限,系统会重启
}

6

除了Arduino的Firmware,PC端喂狗也需要一个程序。选择Delphi编写程序,实现两个功能:1.扫描COM Port找到进行通讯的端口 2.每隔一段时间就进行一次喂狗

(此处Delphi代码,Console模式)
HWDog

7

参考:

1. http://biosren.com/thread-6794-1-1.html 《改动代码提取器》

用 Arduino 打造PPT遥控器

前文提到,用Arduino模拟了USB键盘。做了一个自动锁屏幕装置外,又思考了一下用途还可以做个PPT的遥控器。为了实现遥控功能,需要搭配如下的元件。发射器和接收器是配对的。发射器上面有4个按钮,对应的接收器有D0-D3四个输出Pin.这个东西的名称叫做“2262/2272四路无线遥控套件M4非锁接收板 配四键无线遥控器”,价格不超过15元……

a

首先实验这个元件,直插面包板上,5V电是从USB口上取出来的。

b

用万用表验证,当按下A按钮时,D0会输出高电平。非锁的意思就是按一下就是一下(与之对应的是锁存,按一下后面会keep一段时间)。Enough,下面就可以继续其他。

简单的说,工作分为两步:第一步,制作Arduino模拟USB键盘;第二步,让这个模拟键盘发出左箭头和右箭头的键盘码。

使用的BOM如下:
A.USB公头(有供电和通讯能力的USB头皆可) x1
B.120欧电阻(原文建议68欧,但是我刚好没有。并联120欧充当60欧) x4
C. 2.2K欧电阻 x1
D.3.6伏稳压管 x2
E.遥控发射接收器(上面图片上的)

先说第一步,根据 《Arduino学习笔记A11 – Arduino模拟电脑键盘(基于AVR-USB的USB-HID设备)》参考[1]。使用到BOM中提到的A-D,具体电路如下

c

按照上图设计,首先用面包板进行搭建,确定键盘能够正常工作。具体怎么做一个USB键盘这里就不说了。然后加入接收板。我使用大头针来作为引脚针,砍掉上面的大头,搭配杜邦线使用。用普通的排针总有太短没插到位的感觉,大头针长度足够,粗细也适合。

d

四个输出脚,只用了2个,D0和D3,间距大方便而已。如果有朋友喜欢还可以定义更多的功能。分别插在Arduino的D8和D10上。

f

程序如下:

最后实测,可以正常遥控,蓝色的USB线是下载程序用的,真正的USB是插在笔记本左边。

g

后面再考虑一下遥控的其他用途,比如:放炮点火什么的………多说一下,使用Arduino模拟USB之后,他上面的资源就非常非常少了,个人感觉能实现的功能还是挺有限的,做起来玩玩还可以,要想投入产品是够呛的。比如:因为速度的缘故,PC识别它模拟出来的USB键盘鼠标就要挺长时间。真打算做USB设备还是考虑传统的那些C51+D12之类的比较靠谱吧。

参考:
1. http://www.geek-workshop.com/forum.php?mod=viewthread&tid=1137
2. http://www.lab-z.com/arduino-usb-keyboard-debug%E7%BB%8F%E9%AA%8C/
3. http://www.geek-workshop.com/thread-4006-1-1.html 利用Arduino上的atmega8u2制作红外遥控版PPT控制器

PuttyTel的辅助工具升级到 V2.1

最近升级了一下之前编写的一款方便串口调试的小工具 http://www.lab-z.com/puttytel%E7%9A%84%E8%BE%85%E5%8A%A9%E5%B7%A5%E5%85%B7/

V2.0 可以自己设置波特率,也可以兼容更多的能接受命令行参数的串口软件。

V2.1 上一个版本会确认当前的命令行参数,对于普通用户来说是多余的。删掉这个重新编译。另外,调整代码将 JediAPILib.inc 放在源程序的目录下。

还有就是多显示一位版本号,之前只能显示大版本,比如:2 修改之后可以显示 2.1 这样的版本号。

screen

SerialPortChooserII2.1

SPCII,SPC2

用 Arduino 打造一个自动锁屏装置

上大学的时候,当团支书,负责同学交入Party申请书之类的事情。想入Party有一关是“群众评议”,就是看看是否有反对的意见。当时我和班级上的同学说“一个好人,进入Party是追求进步,是好事;一个坏人,进入Party会让群众队伍更纯洁,因此也是好事。大家都不要反对哈”。所以我们班级在这一关从来没有过什么问题。后来有一次,我去帮同学交入Party申请书,顺便讨价还价一下(我记得有规定是交了之后必须间隔一段才能进入正式的城西,为此我会和辅导员商量这个申请书的时间多多提前一些)。辅导员不在,我就稍等了一下。期间无聊,顺便翻翻其他班级的评议,结果让我大吃一惊,真有班级同学特别反对某个人的,罗列了很多条意见。正在我看在兴头上,辅导员看到急忙过来将东西收了起来,半开玩笑的批评另外帮忙的女同学,说这样的东西怎么能让这种人看到………所以信息安全非常重要。

当你起身去上厕所或者喝水的时候,是否可曾担心屏幕上的信息被人有意或者无意的看到?或者你外出办事,是否担心有人悄悄操作你的电脑?解决这个问题最简单的办法就是给Windows设置一个密码,然后在离开的时候按下 WinKey+L 。

下面就来使用Arduino制作一个自动完成这个“人走屏锁”的装置。

简单的说,工作分为两步:第一步,制作Arduino模拟USB键盘;第二步,让这个模拟键盘发出WinKey+L的键码。

使用的BOM如下:
A.USB公头(有供电和通讯能力的USB头皆可) x1
B.120欧电阻(原文建议68欧,但是我刚好没有所以并联2个来实现60欧) x4
C. 2.2K欧电阻 x1
D.3.6伏稳压管 x2
E.红外线传感器 x1 (用来实现人体感应)

先说第一步,根据 《Arduino学习笔记A11 – Arduino模拟电脑键盘(基于AVR-USB的USB-HID设备)》参考[1]。使用到BOM中提到的A-D,具体电路如下

aa0

按照上图设计,首先用面包板进行搭建:

aa1

测试能够正常实现一个USB Keyboard的功能,具体调试可以参照《Arduino USB keyboard debug经验》。确定上述能够正常工作之后,进行简单的焊接,用大头针将Pin脚引出。

aa2

再使用纸壳做了一个盒子,将Arduino装了进去。
aa3

aa4

下一步的目标就是加入一个能够判断人体是否存在的功能了。最先想到的是人体感应模块。通常都是长得下面这个样子。

aa5

我也入手了一块,但是测试中感觉很奇怪,手放在它前面一段时间之后就没有输出了。后来再仔细阅读资料发现,这个东西应该叫做“人体运动感应模块”。是根据判断当前的红外线变化来判断是否有人体进入。如果想做到“人体感应”,还要加入更复杂的设计。
万幸,手边还有一块红外距离模块,这是用来判断一定距离内是否有遮挡的元件。探测距离可以在0-100cm以内调节。当有阻挡的时候输出低电平,反之输出高。

aa6

使用时,探头的VCC和GND同样取自USB供给。输出OUT连接到Arduino的D8。

aa7

程序如下:

#include "UsbKeyboard.h"
int KEYPIN = 8;                //使用D8作为检测输入,直接使用D1的话
//灌入电流过大,会导致死机。最好是加入限流电阻
//手边没有,就这样暂时这样了
unsigned long ElspTimer=0; 
void setup()
{
  TIMSK0 &= !(1 << TOIE0);     //这里的中断给USB使用了,所以Delay(), Millis()
//micros() delay() delayMicroseconds()统统不好用了
//取而代之的是用串口输出做的粗糙的延时
  pinMode(KEYPIN, INPUT);
  Serial.begin(9600);
}
void loop()
{
  UsbKeyboard.update();
  if(digitalRead(KEYPIN) == LOW)
  {
    ElspTimer=0;
  }
 ElspTimer++;
 Serial.println(ElspTimer);		//这里主要是为了延时使用
  if (ElspTimer>6000L)         //循环6000次,在Uno上是40s左右
    {
      UsbKeyboard.sendKeyStroke(KEY_L,MOD_GUI_LEFT); //发出WinKey+L 来锁定
      ElspTimer=0;
    }
}

 

这样,当人离开的时候,红外探头就无法检查到障碍物,会输出一个高电平。经过40s左右的延时,如果始终为高,就会模拟按下WinKey+L来锁定电脑。从而实现了人走屏锁的功能。
为了美观,最好再做个盒子之类的,我这里只是找了一个啤酒杯,露出探头和USB线。大功告成。

aa8

总结:Arduino通过IO来模拟一个低速设备能够完成一些有趣的功能,还有更多种的“玩法”等待我们去探索。

参考:
1. http://www.geek-workshop.com/forum.php?mod=viewthread&tid=1137
2. http://www.lab-z.com/arduino-usb-keyboard-debug%E7%BB%8F%E9%AA%8C/

修理电饭锅的故障

我的松下电饭锅经过四年的使用出现了一个问题:每次做饭的时候伴随着蒸汽会从排气孔向外喷水,一方面这让会让电饭煲每次都脏兮兮的,另一方面做出来的米饭下面有焦糊的情况。

网上搜索了一下,有人遇到同样的现象,解释是:在这种智能电饭煲的锅盖上,有着负责感应水汽的传感器(打开之后是有三组线,一个地,一个应该是温度传感器,还有一个不认识的线)。当出现带着水的蒸汽达到锅盖的时候,传感器会通知处理器,这时候处理器会让电饭锅加热部分断电(继电器),这也就是为什么我们在正常使用的情况下经常听到的“啪,啪”的声音。停止加热后,水汽不会溢出,然后会再次加热,这个过程反复进行,直到电饭锅中的水已经被米饭之类的完全吸收。米饭在没有液态水的状态下会达到105度(之前看到的一个原理介绍,具体的品牌有可能设定不同)。然后整个蒸饭的过程就结束了,新鲜的米饭即可出锅。

拆解了电饭锅对几根线进行了测量,发现确实有一个断掉了。

psb1

因为锅盖和传感器是一体的,只好求助于万能的淘宝。还真找到了卖家,145一套。卖家刚开始得知我是自己维修,不肯卖,后来商量了很久才同意卖给我。

psb2

拿到手就开始了安装。基本上都是塑料件,安装并没有难度,也没有出现因为尺寸差别导致的不好安装

底座:

psb3

正面

psb4

内胆,中间包裹的是一层保温材料,特地摸了一下,肯定不是石棉……话说前一段刚知道我们经常用的热宝,中间居然用石棉隔热保温,太TMD有创造力了

psb5

布线

psb6

煮饭测试,没问题,只是有轻微的水蒸气的痕迹。

psb7

于是顺利收工,不用担心喷的到处都是。

最后顺便说两句:

1.我的电饭锅是松下的,拆开之后看起来做工不错,一方面是改包裹的都包起来了,另一方面是他的各种机构件标准度很高。有做机械的朋友可以解释一下;

2.未来的维修基本上就是更换各种配件了。所谓的“芯片级”维修,除了RD工程师为了研究问题才会去干,恐怕不会有人去干,一方面是成本的问题,更换配件要远比检查省时省力(维修人力成本很高);另一方面是设计的问题,都会尽量模块话,这样对于生成以及降低成本很有帮助,可以做到外包或者使用标准件。

3.说起来电饭煲/电饭锅,很多人都会提起来当年的“三角牌”。但是这个牌子早已经不存在了。历史大约是,刚开始这是一个国有企业的牌子,后来因为质量很好口碑不错,就开始了贴牌,再后来就是卖牌子,相当于授权经营的意思吧,于是就没落了。即使现在看到“三角牌”也早就不是最初我们认识的那个品牌了。当时我用的是“美的”的电饭锅,用了一年就出了问题,总结经验是:不要买“美的” 品牌超过200的东西。为了买新的电饭锅,当时做了很多功课,最后的结论是:还是日本品牌最靠谱。如果你对生活品质有追求,不妨还是考虑买一个好点的日系的电饭锅吧。我所能知道是:你购买一个日本的产品,自己能用的爽,这个品牌在国内产生的价值比给日本的要多。即便日本人真的用这些钱去买子弹造大炮那也是孙子才需要考虑的事情。如果这笔钱捐给了gov,你是否能有孙子都是一个问题。

就是这样。

Arduino 使用I2C的1602 LCD

很多时候我们需要显示简单的字符,1602LCD就是很好的选择。通常Arduino 使用1602 LCD需要连接很多线路,需要占用很多个IO口,而是用I2C版本的1602即可省去这样的繁杂。I2C版本只需要VCC(3.3和5都可以,亮度上有些差别),GND,还有SDA(连接A4)和 SCL(连接A5)。

下面就是一个使用I2C在1602上显示的例子:

20131206613

对应的程序和库在这里

Hw

在使用的时候,你可以遇到如下问题:

1.编译不通过。请检查编译器的版本,建议使用 1.0 以上的,因为这是未来的方向。如果还有问题,请检测引用库的方式。如果你将 LiquidCrystal_I2C 放在了Library下面,可以用 #include 这样的方式来引用,或者像我代码中,直接将LiquidCrystal_I2C.cpp 和 LiquidCrystal_I2C.h 放在源文件同样的目录下,你就可以使用 #include “LiquidCrystal_I2C.h”。此外,如果出现编译错误提示在 LiquidCrystal_I2C lcd(0x27,16,2); 这行,就是没有正确引用到LiquidCrystal_I2C.h这个头文件导致的。你可以按照上述检查一下,然后重启编译器试试。

2.烧写之后无显示,首先请检查地址是否正确(地址不正确的情况下最上面可能有方块,下面看起来是空白行),最直接的方法是询问卖家,此外还可以像我之前的一篇文章提到的烧入一个软件来判定地址。其次要检查背后的电位器是否正常,避免实际上已经有数值了只是对比度不正确你看不到而已。如果仍然不行,就需要重新寻找库。这个差别有可能是如下硬件顺序导致的。

1602

可以看到 P0- P3分别对应 RS/RW/CS/P ,另外,P4-P7对应DB4-7。还有一种计是颠倒过来 P0-P3对应DB4-7的。如果存在差别,库文件初始化方式传输Command方式都需要修改的。剩下的就是检查Pin是否有电平输出,如果没有那就是I2C模块坏掉了。

当然,如果你在上面花费了超过8个小时仍然无法解决,我的建议是赶快更换一篇,这个东西不需要研究那么久,你的1602有可能压根就是坏掉的(非常不幸的是我就是因为这个问题花费了无数个夜晚…..)

我最后购买到OK的1602是在 http://arduinochina.taobao.com/?spm=a1z10.1.w1002-2071319798.3.RT0Fkv (Arduino CHINA)