Step to UEFI (39) —– 编写一个GetCursorPosition函数

我们知道,可以使用 ConOut 下面的 SetCursorPosition 来设置光标输出的位置【参考1】。

setcursorposition

但是,找了一圈也没有找到 GetCursorPosition ,我怎么知道当前光标在哪呢?查查C手册,通常使用conio.h 中的 wherex,wherey 来完成这个功能。问题是, clib 连这个头文件都没有……..

经过一番查找,在【参考2】中有人提到可以使用 Mode 中的 CursorColumn 和 CursorRow 来完成这个功能。于是,编写简单的程序验证一下,程序非常简单,随机生成一个位置显示X,然后在它后面输出当前这个X的位置:

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

#include  <stdio.h>
#include  <stdlib.h>
#include  <wchar.h>
#include <Protocol/EfiShell.h>
#include <Library/ShellLib.h>

#include <Protocol/SimpleFileSystem.h>
#include <Protocol/BlockIo.h>
#include <Library/DevicePathLib.h>
#include <Library/HandleParsingLib.h>
#include <Library/SortLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/BaseMemoryLib.h>

extern EFI_SYSTEM_TABLE			 *gST;

/**
  Get the position of cursor
**/
VOID GetCursorPosition(
	IN	UINTN	*x,
	IN	UINTN	*y
)
{
	*x = gST->ConOut->Mode->CursorColumn;
	*y = gST->ConOut->Mode->CursorRow;
	
	return;
}

int
EFIAPI
main (                                         
  IN int Argc,
  IN char **Argv
  )
{
	UINTN wherex;
	UINTN wherey;
	UINTN i;
	
	for (i=0; i<5; i++)
	  {
		gST -> ConOut -> SetCursorPosition(gST -> ConOut,rand()%70,rand() % 20);
		GetCursorPosition(&wherex,&wherey);
		Print(L"x[%02d]:[%02d] ",wherex, wherey);
		
	  }
  
  return EFI_SUCCESS;
}

 

运行结果:

Getcur

完整代码下载:

GetCursor

参考:

1.UEFI Spec 2.4 P460
2.http://biosren.com/viewthread.php?tid=4164&highlight=%B9%E2%B1%EA EFI下对应wherex()作用的FUN是哪个?该怎么用?

为什么我们需要客服人员

说点好玩的事情。我刚装机器的时候,安装了一个 ghost 版本的 win7 ,内置了QQ电脑管家,没想到一开机的时候这个软件就会崩溃。万般无奈,卸载之。然后发了一个说说。之后马上有客服加我QQ,后来又有回访的人加了我的QQ。 后来就没有了。

元旦的时候我又发了一次,提到这个事情,发表关于创业公司对大公司挑战的一点感想。其中有关键字,于是又有人加我,随后有下面的对话。

====================================================================
sunshine 2015/1/9 9:47:33
hi 请问您是?

Zt: 之前我遇到过电脑管家的问题

sunshine 2015/1/9 10:45:47
那目前呢?用管家有什么问题吗?

Zt:然后发了说说,你们就加我了.问题是启动的时候报错

sunshine 2015/1/9 10:46:31
是有崩溃的弹窗吗?

Zt:是的,我装的是 ghost 版本的

sunshine 2015/1/9 10:47:16
那下次碰到这个问题的时候 请点弹窗里的“错误详情” 里面会有2个文件,麻烦发给我一下。

Zt: 我可以给你找一下ghost文件,你们找个虚拟机试试

sunshine 2015/1/9 10:49:02
在正式环境下用吧
sunshine 2015/1/9 10:49:44
虚拟机我们自己也有 而且也有用户也在用 崩溃的情况还是很少见的 可能会有触发条件,需要有dump,就是崩溃文件。

Zt: 作为普通用户你觉得有多少人明白 dump文件。作为专业人士,你觉得有多少人用软件遇到问题,会用 windump 去调试?所以,这样的话,我还是换用其他软件了。

sunshine 2015/1/9 10:54:35
崩溃(crash)的时候错误详情里就是dump文件,如果用户点了窗口里的上报,也有会上报记录,我们的开发人员如果发现这个crash率异常升高,会去会查dump必要的话也会回访用户进行联系,但如果没有任何信息,我们也没办法去跟进。

sunshine 2015/1/9 10:55:40
您如果有重现问题,可以及时把这个文件发给我们看看,我们来查询下原因

sunshine 2015/1/9 10:57:03
并不是说我们不用ghost,不用虚拟机,我们版本发布之前会做各种兼容性测试,也会有用户帮我们体验,这部分用户也有很多是用虚拟机的,如果有重现的crash我们都会去跟进,但这些crash的原因并不一定都是一样的。

Zt: 谢谢 理解

====================================================================

事情不大,对我也没有什么影响。qq电脑管家唯一值得称赞的地方就是卸载做的很好。卸载程序运行之后几乎没有残留。

针对这个事情,我觉得值得更深入的思考。

对于腾讯来说,这个并非主营业务,所以毫无影响,最多是给 ghost装机的钱打了水漂(这个是很有中国特色的事情)。客户则会留下腾讯在安全领域毫无实力的印象。这都不是什么大问题。

进一步思考,我觉得这是不能直接让技术人员面对客户的问题,原因有两点:

1.技术人员工资通常比客服高,如果直接让技术人员面对客户,成本比较高

2.技术人员的思维就是纯技术性的。作为技术人员,我也直接面对过客户。深知无法重现的问题确实无法解决。这样的事情不会有耐心去复现。

3.不可能解决所有客户的问题。让大部分人满意或者说让重要的人满意就可以了。我在上一家公司,从老板那里学到的一个很重要的原则是:“客户不是上帝,只有大客户才是”

所以,客服人员之类的还是非常必要的。

Step to UEFI (38) —– SetTimer 设定定时器(下)

上一篇是直接输出一个计数器,我们还可以让他直接打印当前的时间,需要修改的代码不多,替换Timeout函数即可:

/**
  The callback function for the timer event used to get map.

  @param[in] Event    The event this function is registered to.
  @param[in] Context  The context registered to the event.
**/
VOID
EFIAPI
Timeout (
  IN EFI_EVENT      Event,
  IN VOID           *Context
  )
{
  EFI_STATUS  Status;
  EFI_TIME   ET;

  Status = gRT->GetTime(&ET, NULL);
  
  Print(L"%02d:%02d:%02d\r\n",ET.Hour,ET.Minute,ET.Second);
  return ;
}

 

settimes1

可以看到符合我们的期望。

接下来,我们之前的文章提到CLIB中也有时间相关的函数,我们尝试直接使用ctime函数。改动很小

/**
  The callback function for the timer event used to get map.

  @param[in] Event    The event this function is registered to.
  @param[in] Context  The context registered to the event.
**/
VOID
EFIAPI
Timeout (
  IN EFI_EVENT      Event,
  IN VOID           *Context
  )
{
  time_t t;

  time(&t);
 
  printf("%s\n",ctime(&t));
  
  return ;
}

 

但是会导致TPL错误

2b51136803959fa789bb6da4793609ad

b723a4c7633c0b1b141d33777dcc3942

错误原因不详。我请 HZZZ 帮忙看了一下,他发现如果我们使用默认的Shell_Full.efi就会出现问题,但是如果使用代码重新编译一个出来就不会有同样的问题(意思是:我们能够确定这是Shell本身的问题,但是没有会出现问题的Shell代码,因此无从得知Root Cause)。会出现下面的问题

timertest3

对于这个问题,HZZZ Debug的结果是:新编译出来的Shell不支持 SE2 这个Protocol…….

因此,如果想写一个兼容性强的程序,最好直接使用 UefiShellLib 提供的 ShellGetExecutionBreakFlag 函数。这个函数会自动判断当前有哪个Protocol,然后调用存在的功能。

做一个蓝牙小车

起因是这样的:一个壕朋友希望我帮忙给他做个小车。具体要求是:能用蓝牙控制,负重500g左右。经过考察,他决定在 DFRobot上选一款,毕竟DFRobot在国内也算首屈一指。根据他的需求,最开始选定的是下面这个:

image001

下订单的时候发现没有货。官方的解释是:春节期间都被洋人买光了……剩下的2个轮子有货的型号的太小不符合要求,最后选的是四个轮子的,就是下面这样的:

image002

上面像眼睛一样的是超声波距离传感器,传感器下面有个舵机,能够带动它进行不同方向的扫描。

下了订单付款之后,第二天就收到了,包装超级好,让我大吃一惊。双层包装,然后全部的小车套件放在里面的塑料工具箱中,用之前的话说叫做“高端大气上档次”。激动过后,先盘点下零件,很惊讶的发现其中竟然有两个巨大的轮子,更惊讶的是只有两个轮子。然后急忙找来装箱单,他们错发了个更昂贵的型号给我……。很无语,急忙拨打客服电话,接电话的是一个妹子,我简单说了下情况,她表示目前是18:20,他们已经下班了,让我明天9:30之后再打电话……第二天上午,打电话过去,问明情况之后,对方表示是他们搞错了,让我快递退回。不知道是不是因为发错了贵的所以才这么痛快。我又问是不是要顺风快递,对方略微迟疑,然后说除了顺风之外的所有都可以,理由是上海市内都是隔天到达。打完电话第二天是周日,赶紧给他们发了快递。周一他们顺利收到,又给我重新发了一个。收到之后第一件事情:打开数轮子

image003

包装比前面那个差多了,没有工具箱。如果让我评价做工最好的配件,我首选那个橘色的USB线。柔软,颜色鲜艳,手感很好。跑到他们官网看了一下,这个数据线要22元。

image004

接下来就开始了安装,首先是底盘和四个直流电机。所有的螺丝在最后组装完成之前都不要拧紧。

image005

没想到的是:安装直流电机后需要焊接电线,我以为能高级一些免烙铁。焊接的时候始终担心不小心弄坏了触点。四个电机,八个点,还好,没问题。

image006

底板的盖,也是电池盒安装的地方。需要5节电池,很奇怪的数字,如果以后用充电电池还不太好一次性充满。

image007

装上盖板就是这个样子,电池盒是在小车下方,面向地面。

image008

上层只有控制板需要安装一下,拧螺丝之类即可。安装简单,牢固度是比较差的。

image009

控制板是这样的(Romeo 三合一Arduino兼容控制器)

image010

本质上他是一个Arduino(ATmega328P),14 通道数字 I/O,6 PWM 通道 8通道10位模拟输入,3组I2C,自带蓝牙,带5个按键,能够直接带动2组直流电机……..
装上电池打开开关就是这个样子:

image011

然后我就开始研究如何蓝牙控制了。很快,我就陷入了迷茫:无论如何都无法让我的笔记本搜索到他的蓝牙设备。无法配对,控制更是无从谈起。我之前用过 HC05 的透传模块,感觉很容易就上手了,没想到这个折腾了很久都没有办法跨出去第一步。上DFRobot的论坛搜索,用AT命令调整各种参数,结果都一样,就是找不到设备。最终在一个帖子中找到了不是很确实的说法:控制板上的蓝牙很高级是蓝牙4.0,如果你的蓝牙Host太矬,是无法找到这个控制板的。想想估计我开发的笔记本很可能不支持蓝牙4.0。第二天,又找了一个平板电脑,肯定支持蓝牙4.0,果真很快找到了设备。配对之后又让我陷入了迷茫:没有出现串口设备啊。只好再搜索。找到一句话,大意是,如果你想用串口透传,那么请购买另外一个USB蓝牙配对的东东……。

心中权衡了一下时间成本和金钱,放弃之,换上我之前的 HC05模块,连接串口 RX/TX ,很快就能控制起来了。最终的样子是这样:

image012

估计了一下,有点不划算。控制板上的资源我只用了2个PWM 2个GPIO?其余的都没用上啊。

程序很简单,就是控制直流电机的转动,基本上就是网站上的Demo例子:

int E1 = 5;     //定义M1使能端
int E2 = 6;     //定义M2使能端
int M1 = 4;    //定义M1控制端
int M2 = 7;    //定义M1控制端
void stop(void){                 //停止
       digitalWrite(E1,LOW);   
       digitalWrite(E2,LOW);      
}  
 
void advance(char a,char b){           //前进
       analogWrite (E1,a);             //PWM调速
       digitalWrite(M1,HIGH);    
       analogWrite (E2,b);    
       digitalWrite(M2,HIGH);
}  
void back_off (char a,char b) {          //后退
       analogWrite (E1,a);
       digitalWrite(M1,LOW);   
       analogWrite (E2,b);    
       digitalWrite(M2,LOW);
}
void turn_L (char a,char b) {           //左转
       analogWrite (E1,a);
       digitalWrite(M1,HIGH);    
       analogWrite (E2,b);    
       digitalWrite(M2,LOW);
       delay(100);
}
void turn_R (char a,char b) {           //右转
       analogWrite (E1,a);
       digitalWrite(M1,LOW);    
       analogWrite (E2,b);    
       digitalWrite(M2,HIGH);
       delay(100);       
}

void setup(void) { 
    int i;
    for(i=6;i<=9;i++)
    pinMode(i, OUTPUT);  
    Serial.begin(9600);      //设置串口波特率
} 

void loop(void) { 
   if(Serial.available()>0){
     char val = Serial.read();
     if(val!=-1){
          switch(val){
             case 'w'://前进
                     advance (255,255);   //PWM调速
                     break;
             case 's'://后退
                     back_off (255,255);
                     break;
             case 'a'://左转
                     turn_L (255,255);
                     break;       
             case 'd'://右转
                     turn_R (255,255);
                     break;          
            }     
          delay(40); 
       }
      else stop();  
   }
   stop();
}

 

PC端控制软件的话,我用Putty 工具,打开串口 WASD 这几个键就能控制前后左右移动。

最后吐槽一下:

1.这个东西要比想象中复杂,我以为买了套件安装会简单多。

2.做工还是有点粗糙,行走直线的时候,你会发现这几个轮子会歪转起来不是完整的圆形,我尝试很多次调整,没什么效果。应该是直流电机和轮子连接上的问题。

3.极客工坊上有朋友说DFRobot发错东西不是一次了,他买个舵机也发错了。如果你对实效要求很高,比如:期望今天下订单,周末用来安装调试,那么一定要小心,搞不好拿到手才发现东西不对。

4.说明做的不好。我估算了一下,组装差不多花了10个小时。这个时间有点多,如果是初学者或者缺少工具的人会遇到很多意想不到的困难。如果能说明一下需要用到的工具就好了。安装说明是在线的,是中英文混合的。这个很讨厌,混合之后就有一个衔接的问题,让人头晕。历史也已经证明,中英文混合是很犯忌讳的事情。比如:有上级问一个人,“你叫什么名字?”“他回答:“Ni Fake”。上级很恼火,“还拽英文?”他一听,以为还要介绍自己的英文名,脱口而出“Fuck Ni”……后面的事情我们都知道了。

5.控制板的设计上还有点问题,有一个螺丝孔位我无法找到合适的螺丝。配件自带螺丝头过大,无法正常拧进去,我只好换了一个自己的螺丝,勉强凑合。

image013

期望 DFRobot 能够努力改进产品,也为国内客户提供更好的服务。有一种说法是国内的Arduino生产厂家不愿在国内进行销售,主要是客户少事情多,然后仿造的太多了根本赚不到什么钱…….

实验 MPU-6050模块

在使用之前,需要特别注意的是:MPU-6050 本身只支持到 3.3V,如果你需要供电5V那么要特别确认一下你的板子是否有转换。我使用的下面这个模块上面带降压的,所以可以直接接5V.

T1OIRvXBXfXXXXXXXX_!!0-item_pic

另外,连接GND和VCC时一定要注意不能搞反了,否则转换芯片会急剧发热(推荐你第一次上电的时候留心这个芯片)。

首推极客工坊的文章【参考1】。但是可能是版本太老的缘故,在编译时(1.5.0)会出现core.a的错误。

随后查了一下,Arduino官方网站上有介绍和示例代码【参考2】:

// MPU-6050 Short Example Sketch
// By Arduino User JohnChi
// August 17, 2014
// Public Domain
#include<Wire.h>
const int MPU=0x68;  // I2C address of the MPU-6050
int16_t AcX,AcY,AcZ,Tmp,GyX,GyY,GyZ;
void setup(){
  Wire.begin();
  Wire.beginTransmission(MPU);
  Wire.write(0x6B);  // PWR_MGMT_1 register
  Wire.write(0);     // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);
  Serial.begin(9600);
}
void loop(){
  Wire.beginTransmission(MPU);
  Wire.write(0x3B);  // starting with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU,14,true);  // request a total of 14 registers
  AcX=Wire.read()<<8|Wire.read();  // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)     
  AcY=Wire.read()<<8|Wire.read();  // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
  AcZ=Wire.read()<<8|Wire.read();  // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
  Tmp=Wire.read()<<8|Wire.read();  // 0x41 (TEMP_OUT_H) & 0x42 (TEMP_OUT_L)
  GyX=Wire.read()<<8|Wire.read();  // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
  GyY=Wire.read()<<8|Wire.read();  // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
  GyZ=Wire.read()<<8|Wire.read();  // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L)
  Serial.print("AcX = "); Serial.print(AcX);
  Serial.print(" | AcY = "); Serial.print(AcY);
  Serial.print(" | AcZ = "); Serial.print(AcZ);
  Serial.print(" | Tmp = "); Serial.print(Tmp/340.00+36.53);  //equation for temperature in degrees C from datasheet
  Serial.print(" | GyX = "); Serial.print(GyX);
  Serial.print(" | GyY = "); Serial.print(GyY);
  Serial.print(" | GyZ = "); Serial.println(GyZ);
  delay(333);
}

 

我使用的是 Arduino Pro Micro (这并非Arduino官方出品,是一种类似 Arduino Leonardo 的兼容产品) , 电路非常简单,使用面包板即可

6050

运行结果

6050r

取得值本身很简单,但是如果想解出正确的姿态还是蛮复杂的。

参考:

1.http://www.geek-workshop.com/thread-1017-1-1.html arduino学习笔记37 – Arduino Uno + MPU6050首例整合性6轴演示实验

2.http://playground.arduino.cc/Main/MPU-6050 MPU-6050 Accelerometer + Gyro

二维码对联

前一段看张鸣老师的书,上面提到对联是一种很重要的传统文化表现形式,尤其是春节时要四处张贴的春联。据说很多地区的大学生春节回家之后还要承担起为乡亲们题写春联的重任。母亲说当年他们插队时,逢年也会给老乡写春联。内容上通常是毛语录,比如:“金猴奋起千钧棒,玉宇澄清万里埃”或者“四海翻腾云水怒 五洲震荡风雷激”,一类的。

每年春节上街我都会觉得这是越来越重要的传统——至少从春联的价格上来看是这样。让我编点春联是没问题的,至于写的话因为现在提笔忘字以及本人的字都会被老婆笑话,还是免谈了。

作为一个专业的工程技术人员,灵机一动,想到了既然我只能编不能写可以用眼下最时髦的二维码技术来生成。有好奇心的人就会去扫一扫,然后就能看到了。具体做法如下:

首先,找个二维码生成软件。选择最高的误码率,简单的理解这个就是你能够覆盖生成二维码的比率。比如:25%的误码率意味着你可以覆盖画面上25%的区域仍然能够正常扫出。

错误修正容量

L水平 7%的字码可被修正

M水平 15%的字码可被修正

Q水平 25%的字码可被修正

H水平 30%的字码可被修正

【参考 1】

image001

【参考1】

我随便选了一个软件,二维码大师,最高误码率30%,然后选择最高分辨率。文本内容中输入上联“壹零做法神通广大不服来扫我”,输入过程中就自动生成二维码了,具体在下面三个地方设置和输入。

image002

选择保存为一个BMP文件。比如上面“壹零做法神通广大不服来扫我”,生成的结果就是下面的图片。

image003

然后修改这个图片即可,我最喜欢用的就是Windows的画笔工具。

image004

修改之后结果

image005

特别的,修改之后一定要用刚才的软件校验一下,避免做完了根本无法识别的状况。

image006

同样做法,再做一下下联“纵横成形网挂众多若信任联”。

image007
上面这个对联是我父亲拟的,横批就是“二维码”。

最后多说两句,大多数情况下人们只是注意上下问对仗是否工整而不大在意内容本身,好比,有女生给班主任写条子,”老师,我怀了男朋友得孩子”。老师看到之后大吃一惊“唉,现在的学生啊,真是的!都上初中了,怎么还分不清楚‘的地得’的用法?”。我随口拟了一个联儿,念给父亲听,他听了片刻说“对的比较工整,这是一个传统的联吧?”

image008

image009

参考:

1. http://zh.wikipedia.org/wiki/QR%E7%A2%BC QR码

Arduino 显示奶瓶温度

有个网友提出一个需求“做一个能够显示温度的奶瓶。奶瓶泡好奶后放在底座上,可直接给婴儿喝的绿灯亮,过烫红灯亮,过凉蓝灯亮。”这是他的一个作业,然后我就帮着做了一下。整体思路就是直接使用上一次做的那个红外温度计的方案,我想这样的方案恐怕在网上都是独一无二的吧。

使用到的基本元件有:Arduino Uno + LED + 红外温度传感器(上面有一个激光头用于瞄准)

使用红外温度传感器能够获得下列优点:

1.无需接触,不必考虑卫生的问题
2.准确度在可以接受的范围内
3.速度快,对准之后马上显示
4.兼容性好,各种型号的奶瓶都可以

缺点:

1.贵
2.有人说无法精确得知瓶中奶的温度,测量的是奶瓶温度而已

milkb2

milkb1

#include <Arduino.h>
#include <Wire.h>
#include "TN901.h"  
TN901 tn;          
int RedLed=10;  //Red Led Pin10
int GreenLed=7;  //Green Led Pin10
int BigRedLed=4;  //Big Led Pin4
void setup()
{
    Serial.begin(9600);
    tn.Init(13,12,11);  //初 始 化 data clk ack
    pinMode(RedLed,OUTPUT);
    digitalWrite(RedLed,LOW);
    pinMode(GreenLed,OUTPUT);
    digitalWrite(GreenLed,LOW);    
    pinMode(BigRedLed,OUTPUT);
    digitalWrite(BigRedLed,LOW);      
}
void loop()
{
   tn.Read();
   SerialValue();
   delay(2000);
}

void SerialValue()
{
   String s;
   s=">O"+String(tn.OT, DEC)+"E"+String(tn.ET,DEC)+"<";
   Serial.println(s);
   if (tn.OT>5000) { // Temp too high
         digitalWrite(RedLed,HIGH);
         digitalWrite(GreenLed,LOW);
         digitalWrite(BigRedLed,HIGH);        
   }
   else {
     if (tn.OT>2100) {// Temp is OK
         digitalWrite(RedLed,LOW);
         digitalWrite(GreenLed,HIGH);
         digitalWrite(BigRedLed,HIGH);        
         }
    else { //Temp is too Low      
         digitalWrite(RedLed,HIGH);
         digitalWrite(GreenLed,HIGH);
         digitalWrite(BigRedLed,LOW);
     
    }
   }  
}

 

设计上我们使用了2个LED,一个是小LED双色红色和绿色,一个是单独的大个红色的(手边只有这两种LED,更好的设计应该换个颜色的LED便于区分)。温度传感器是红外温度传感器。

温度区间:

>50 小LED的红色亮起 表示温度过高

50-20 小LED的绿色亮起 表示温度适合

<20 大红色LED亮起 表示温度过低 工作时的视频 首先展示一下电路,然后我们有一瓶大瓶冷水;放在上面的时候温度过低,大红色 LED亮起;然后我们换成奶瓶,其中是热水,温度过高,小红色LED亮起;然后我们将大瓶子的水兑到小瓶子中,温度会变得适中,于是小LED绿色会亮起。

这个方案对于作业来说应该是足够了。就是这样。

窗花

前几天看《小说月报》,一篇讲述的是研究生毕业的漂亮女硕士,为了完成女导师的愿望,毅然决然选择从政的故事。只不过这是主旋律小说,必须有光明的尾巴:一直期望她能成为自己“李万姬”的老领导患了癌症,向革命老前辈们报到前说出了所有的事情,于是全部矛盾迎刃而解。最后女主角镀金完成,换了岗位,抱得男朋友满载而归。比较有意思的是故事中有一个情节,讲的是他们那里支柱企业是无纺布生产厂,受到经济危机影响,贷款出了问题,然后女主角想办法筹集资金,拖欠了一下当地干部和教师的工资,又下达集资的命令。看到资金的来源,我觉得这篇小说还真是写实。确实,对于教师这个职业,没有好处的很多时候不会想起来你是公务员,但是要是有需要体现精神的时候又会被推到前面。

又想起来很久之前的一个科幻小说,讲述人类发送太阳能电池到太空作为新能源,但是太空中有尘埃,每隔一段就需要清理。发送博士之类风险太大薪水又不能太低,最后作为特产,农民工就被送到太空,从事这份高工资的工作,也算是人上人了。小说还真是传承社会精神面貌的镜子。回头我找本书好好研究下宋朝的生活,顺便批判一下封建社会的腐朽的生活方式。

我这里说的窗花并非那种传统手工图样,而是凝结在窗户上的水汽形成的美丽图样。

0f53c8d303a1add716cbc1cbef375ee8

我的一位同学,是小学老师,前一段她每天早晨都会在朋友圈中晒出办公室窗花的照片。听起来很浪漫,现实中意味着寒冷—-办公室没有暖气和空调。听起来难以置信,不过据她所说就是这样。此外,因为种种安全上的考量严禁各种非生物能源转换,意思是除了蹦跳多穿衣服多吃东西之外严禁使用电器。于是她便只能望着美丽的窗花偶发感慨聊以自慰了。

以前我在的公司也有过这样的事情:大冬天为了省电不开空调,HW工程师冻的手都没有办法握稳电烙铁。具体是这样的,冬季是我国江南地区传统的用电高峰,企业在用电上会有一些限制,于是老板就在空调上打起来主意。最大的问题是老板的房间是在整栋楼的中间,好比上下各有一层脂肪,大约是感受不到多少寒冷,而我们研发非常不幸的处在顶楼,不开空调很冷啊。我们找了一楼的总务很多次,总务总是指着墙上的一直温度计辩解还没有降低到18度,由拒绝开,我甚至一直在怀疑,他那只温度计是特殊定制的,显示值会比正常的温度高那么两三度。再后来终于开放中央空调,但是还想了一个非常奇特的规则:整点的时候供电10分钟。意思是,比如九点到九点十分,中央空调是有电的,但是需要手工用遥控器打开之。于是,每逢快到整点,研发人员就像神经病一样相互提醒马上准备开空调,能暖和一段。偶然忘记了大家更是垂足顿胸。

时间一长,大家都受不鸟了。就开始研究解决方法了。做HW的工程师夏天的时候可以做几只风扇并联起来,但是冬天的时候没有太合适的工具取暖,热风枪吹起来只有外焦里生的效果。电烙铁的加热区域也太小,没办法满足四肢的需要…….通常 HW 搞不定的就要靠SW了。于是,我旁边的妹子提出:太TMD冷了,自己买个暖风机,然后我也附议了一下,一起买吧。忽然想起来那次似乎是我第一次在京东上购物。根据自己的体积,我挑选了一个2KW的暖风机,旁边的妹子挑选了一个2.5KW的。头天下午的订单,第二天就送货上门签收交易完成,那也是我第一次被京东的配送速度震撼到。用起来感觉不错,是风暖型的,中午熄灯之后吹上去暖洋洋的,我恨不得搬着一张床钻到桌子下面去。等我们用了一段,看看没啥动静,办公室的人也都一窝蜂的去买,到了最后几乎是人手一只。连研发的老大也悄悄买了一个低调的塞在座位下面:高调的理念通常只适用在别人身上,真正到了自己这边舒服一些比什么都强。大约也是到了这个岁数所谓天降大人于私人也不那么重要。没有大志终究比大痔或者冻疮强。

再后来,总务想管来着,羞羞答答的提出了除了费电之外的全部理由,比如:不安全,不环保等等。只是研发没人鸟。想必总务也是有所顾忌的,一方面大冬天不给开空调本来就不占理;另一方面也许是实在讲不过研发的人吧。再后来也就不了了之,我们吹着暖风迎来了春天………..

事后,我曾经认真的总结,这个事情之所以能成,很大程度上是总务对我们有所顾忌,也就睁一只眼闭一只眼了;更高一点层次来说,研发人员算是稀缺,上面也不愿意太过分。再后面发生太过分的事情,研发3个月走了一半的。

从另外一个角度来说,我同学那样的小学老师,生活是很稳定的,一辈子不用担心你会被开除或者因为某些变故失去工作,而这种稳定付出的代价就是自由。

Step to UEFI (26) —– Shell下Reboot System

研究一下如何 RESETSYSTEM,直接使用 RUNTIMESERVICES 是很好的选择。首先是查看资料 UEFI 2.4 是不二的选择。

7.5.1 Reset System

具体参数解释:EFI_RESET_TYPE 有: EfiResetCold,EfiResetWarm, EfiResetShutdown,和EfiResetPlatformSpecific。Cold是系统级别的完全重启。 Warm也是系统级别的重启,但主要是CPU级别的重启。(具体的话,以前确实有Warm Reset和Cold Reset的差别,但是我感觉实际BIOS设计上,这两个并不是区分的特别清晰。传统上的CPU的Warm Reset有可能导致几百次重启之后系统挂掉,而测试人员根本不会听取你关于他们差别的解释,于是最好的办法仍然是都用Cold Reset来解决)。Shutdown的话,就是关机了。EfiResetPlatformSpecific从解释上来看好像是将一个EFI_GUID指定的Reset类型存放在ResetData中(类似记录重启原因吗?)

EfiResetPlatformSpecific 太复杂,这里只是研究一下简单的 Reset 最后编写程序如下

 

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

#include  <stdio.h>
#include  <stdlib.h>
#include <wchar.h>

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

/***
  Demonstrates basic workings of the main() function by displaying a
  welcoming message.

  Note that the UEFI command line is composed of 16-bit UCS2 wide characters.
  The easiest way to access the command line parameters is to cast Argv as:
      wchar_t **wArgv = (wchar_t **)Argv;

  @retval  0         The application exited normally.
  @retval  Other     An error occurred.
***/
int
EFIAPI
main (
  IN int Argc,
  IN char **Argv
  )
{
  gRT -> ResetSystem(EfiResetCold,EFI_SUCCESS,0,NULL);
  return EFI_SUCCESS;
}

运行之后系统就重启了。

下载   MainRST