万万没想到,串口的故事

周末在家进行实验,打算写个VS2008串口发送数据的小程序。结果万万没想到麻烦一大堆。

目标:用VS2008发送串口数据。

为了实现这个目标,需要有能发送和接受串口数据的设备,我有一只USB转串口线,又找到了一个USB转串口的小板子。

20140705984

下面的这个小卡在之前的《使用 Arduino 打造一个硬件的Watchdog》出场过。

20140705985

问题是这两个哥们连不上啊~当然对于每个都分别测试过 loop back.唯独连在一起的时候工作不正常。

20140705981

串口公头定义如下

c2cec3fdfc039245937957298794a4c27c1ed21b0ff405fb

4 数据终端准备好(DTR)
5 信号地线(SG)
6 数据准备好(DSR)
7 请求发送(RTS)
8 清除发送(CTS)
9 振铃指示(RI)

插上之后发现两个都是 ch341A:

dv1

他们之间无法正常通信(当然 TXD TXD GND 已经连接好),乱码,严重的乱码,发送 01 接收到的是 7F,尝试使用Putty和WCH自己的工具都是不行的,波特率之类的设定经过一遍一遍的检查,确定都已经设置为相同了。真是万万没想到啊!

本打算放弃了,忽然想起来前一段买了一个 USB转IIC的小卡,可以当作串口用,也是CH340系列的芯片。再找出我珍藏多年的25米串口线,连在一起。

20140705986

测试他和我那个USB转串口线,通过Putty可以通讯,一切正常。

dv2

终于开始调试程序,发现程序运行时出现错误,GetLastError = 2 意思是“ERROR_FILE_NOT_FOUND 2 (0x2) The system cannot find the file specified.” 【参考2】,但是实验 COM6是可以的。搜索资料,找到【参考3】,原来是我用的下面这样的形式,只给COM1-9来用…….

hComm = CreateFile( “com6”, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );

要想打开COM10及其以后的必须使用\\\\.\\COM27这样的形式。

CreateFile(
“\\\\.\\COM27”, // address of name of the communications device
fdwAccess, // access (read-write) mode
0, // share mode
NULL, // address of security descriptor
OPEN_EXISTING, // how to create
0, // file attributes
NULL // handle of file with attributes to copy
);

万万没想到啊,还有这样的限制。继续下面的调试。

继续测试发现我的程序这边发送,那边可以收到了,但是只能收到前面几个字节。我的程序端显示发送的字符串数量是正确的,但是接收到的字数却是少了一截。

xx

实验发现如果升高波特率比如使用 9600反倒没问题。于是怀疑这个问题和超时相关,查看COMMTIMEOUTS相关的资料。

// COMMTIMEOUTS对象

COMMTIMEOUTS comTimeOut;
// 接收时,两字符间最大的时延
comTimeOut.ReadIntervalTimeout = 0;
// 读取每字节的超时
comTimeOut.ReadTotalTimeoutMultiplier = 0;
// 读串口数据的固定超时
// 总超时= ReadTotalTimeoutMultiplier * 字节数+ ReadTotalTimeoutConstant
comTimeOut.ReadTotalTimeoutConstant = 0;
// 写每字节的超时
comTimeOut.WriteTotalTimeoutMultiplier = 0;
// 写串口数据的固定超时
comTimeOut.WriteTotalTimeoutConstant = 0;
// 将超时参数写入设备控制
SetCommTimeouts(hCom,&comTimeOut);

上面写为0表示会无限等待下去。但是仍然不好用。

调试又发现如果在WriteFile的时候断点调试,是可以收到的。但是我在他前面加入一个 sheep之后仍然现象相同。

最终,万万没想到,真相只有一个:我在最后设定了关闭串口 CloseHandle(hCom) ,但是如果这个时候没有完成发送就被截断(按道理,应该是同步操作,但是不知道为什么,这里不会等待完全发送)。对于我程序来说解决方法就是把关闭串口放在 pause 的后面………

// ComPortSend.cpp : main project file.
//http://forums.codeguru.com/showthread.php?442634-Serial-port-program-in-c-language
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <conio.h>


int main(int argc, char *argv[])
{
   DCB dcb;
   HANDLE hCom;
   BOOL fSuccess;
   TCHAR SPort[]=L"\\\\.\\COM27";
   char txchar[]="This is a test from www.lab-z.com!";
   DWORD iBytesWritten;

   hCom = CreateFile(SPort,
                    GENERIC_WRITE,
                    0,    // must be opened with exclusive-access
                    NULL, // no security attributes
                    OPEN_EXISTING, // must use OPEN_EXISTING
                    0,    // not overlapped I/O
                    NULL  // hTemplate must be NULL for comm devices
                    );

   if (hCom == INVALID_HANDLE_VALUE) 
   {
       // Handle the error.
       printf ("CreateFile failed with error %d.\n", GetLastError());
       return (1);
   }

   SetupComm(hCom, 32, 32);//输入输出缓冲区大小都是1024个字节


   // Build on the current configuration, and skip setting the size
   // of the input and output buffers with SetupComm.

   fSuccess = GetCommState(hCom, &dcb);

   if (!fSuccess) 
   {
      // Handle the error.
      printf ("GetCommState failed with error %d.\n", GetLastError());
      return (2);
   }

   // Fill in DCB: 300 bps, 8 data bits, no parity, and 1 stop bit.

   dcb.BaudRate = CBR_300;     // set the baud rate
   dcb.ByteSize = 8;             // data size, xmit, and rcv
   dcb.Parity = NOPARITY;        // no parity bit
   dcb.StopBits = ONESTOPBIT;    // one stop bit

   fSuccess = SetCommState(hCom, &dcb);

   if (!fSuccess) 
   {
      // Handle the error.
      printf ("SetCommState failed with error %d.\n", GetLastError());
      return (3);
   }

   WriteFile(hCom, &txchar, strlen(txchar), &iBytesWritten,NULL);
   printf ("Serial port %s successfully reconfigured. \n", "COM27");

   system("PAUSE");

   CloseHandle(hCom);

   return (0);
}

 

终于我的Putty在另外一端能够接收到完整的数据了。

代码下载 ComPortSend

后记:从我目前的认知来看,USB转串口质量最好的是 FT232 ,然后是 PL2xx(严重的问题是它没有 Win8的驱动) ,CH34x系列只能排在最后。如果成本压力不是很大,有机会应该优先考虑Ft232。

参考:
1. http://www.elecfans.com/yuanqijian/jiekou/200801247510.html
2. http://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx System Error Codes (0-499)
3. http://bbs.csdn.net/topics/80130516 用CreateFile打开串口设备,出现问题?
4. http://www.lab-z.com/%E4%BD%BF%E7%94%A8-arduino-%E6%89%93%E9%80%A0%E4%B8%80%E4%B8%AA%E7%A1%AC%E4%BB%B6%E7%9A%84watchdog/ 使用 Arduino 打造一个硬件的Watchdog

Arduino制作一个电子骰子(2)

俗话说“小赌怡情,大赌兴业”,经过考证我们能够确信这个叫做”俗话“的名人是赌场老板—-看过余华先生的《活着》对此肯定深有体会,再多说一句,我个人不觉得这部小说讲述的是一个悲剧故事,生活就是这样而已,过去是这样现在依然是这样。小说没有被禁,但是对应的电影在国内是禁片。有这样一种说法”昨晚在暨大演讲,有同学问我《活着》改编电影时什么事情印象深刻?我说这是18年前的事了,还记得当时张艺谋时常说原作里的什么细节要改动,审查才能通过。看他胸有成竹的模样,心想他如此了解xxx,对他十分钦佩。可是张艺谋拍摄完成电影后,审查还是没有通过。我不再钦佩张艺谋,我钦佩xxx了“【参考1】。

从概率的角度来说,哪怕对方比你获胜的概率只高了2%,在你”根本停不下“的情况下,蚀本只是时间问题…….

前面介绍了一个 Arduino 打造的”电子骰子“,我们下面要对它进行改装,让你”稳赢不输“。

我们再添加一个遥控设备,它在我们之前的《用 Arduino 打造PPT遥控器》中出场过,是目很容易买到价格低廉的遥控设备。

remote

和之前的相比,只是多了一个接收器。接收器上有5个Pin,这个接收器和 Arduino 的连接如下:

5V    <——> 5v (我试过如果用 3.3v的话不工作)
GND<——>  GND
D0   <——> D4
D1   <——> D5
D2   <——> D6
D3   <——> D7

按键比较少,所以只支持对应了从3到6个点数。改造之前的程序,加入检查遥控按键是否被按下的代码。

/*
日期:2014-7-2
功能:MAX7219驱动8*8点阵
作者:Z.t
IDE:1.0.5
硬件:最小系统UNO
说明:本代码主要参考了官方的示例程序
*/

//官方的库
#include "LedControl.h"

/*
Now we need a LedControl to work with.
***** These pin numbers will probably not work with your hardware *****
第一个参数:pin 12 is connected to the DataIn 
第二个参数:pin 11 is connected to the CLK 
第三个参数:pin 10 is connected to LOAD 
第四个参数:We have only a single MAX72XX.
*/
LedControl lc=LedControl(12,11,10,1);

/* we always wait a bit between updates of the display */
unsigned long delaytime=50;
int ButtonPin=3;
int PinA=4;
int PinB=5;
int PinC=6;
int PinD=7;
int Current=1;
int Actual=0xFF; 
boolean MarkStart=false;  //标记是否按键抬起

void setup() {
  /*
   The MAX72XX is in power-saving mode on startup,
   we have to do a wakeup call
   */
  lc.shutdown(0,false);
  /* Set the brightness to a medium values */
  lc.setIntensity(0,8);
  /* and clear the display */
  lc.clearDisplay(0);

  randomSeed(analogRead(0));
  pinMode(ButtonPin, INPUT);    
  pinMode(PinA, INPUT);   
  pinMode(PinB, INPUT);   
  pinMode(PinC, INPUT);   
  pinMode(PinD, INPUT);    
}

void showNum(int x) {
  /* here is the data for the characters */
  byte one[8]={     
B00000000,
B00000000,
B00000000,
B00111000,
B00111000,
B00000000,
B00000000,
B00000000};

  byte two[8]={   
B00000000,
B00000110,
B00000110,
B00000000,
B00000000,
B01100000,
B01100000,
B00000000};

  byte three[8]={   
B00000000,
B00111000,
B00111000,
B00000000,
B01100110,
B01100110,
B01100110,
B00000000};

  byte four[8]={   
B00000000,
B01100110,
B01100110,
B00000000,
B00000000,
B01100110,
B01100110,
B00000000};

  byte five[8]={   
B00000000,
B01100110,
B01100110,
B00011000,
B00011000,
B01100110,
B01100110,
B00000000};

  byte six[8]={   
B01100110,
B01100110,
B00000000,
B01100110,
B01100110,
B00000000,
B01100110,
B01100110};

switch (x) {
case 1:
  lc.setRow(0,0,one[0]);
  lc.setRow(0,1,one[1]);
  lc.setRow(0,2,one[2]);
  lc.setRow(0,3,one[3]);
  lc.setRow(0,4,one[4]);
  lc.setRow(0,5,one[5]);
  lc.setRow(0,6,one[6]);
  lc.setRow(0,7,one[7]);
  break;
case 2:
  lc.setRow(0,0,two[0]);
  lc.setRow(0,1,two[1]);
  lc.setRow(0,2,two[2]);
  lc.setRow(0,3,two[3]);
  lc.setRow(0,4,two[4]);
  lc.setRow(0,5,two[5]);
  lc.setRow(0,6,two[6]);
  lc.setRow(0,7,two[7]);
  break;
case 3:
  lc.setRow(0,0,three[0]);
  lc.setRow(0,1,three[1]);
  lc.setRow(0,2,three[2]);
  lc.setRow(0,3,three[3]);
  lc.setRow(0,4,three[4]);
  lc.setRow(0,5,three[5]);
  lc.setRow(0,6,three[6]);
  lc.setRow(0,7,three[7]);
  break;

case 4:
  lc.setRow(0,0,four[0]);
  lc.setRow(0,1,four[1]);
  lc.setRow(0,2,four[2]);
  lc.setRow(0,3,four[3]);
  lc.setRow(0,4,four[4]);
  lc.setRow(0,5,four[5]);
  lc.setRow(0,6,four[6]);
  lc.setRow(0,7,four[7]);
  break;
case 5:
  lc.setRow(0,0,five[0]);
  lc.setRow(0,1,five[1]);
  lc.setRow(0,2,five[2]);
  lc.setRow(0,3,five[3]);
  lc.setRow(0,4,five[4]);
  lc.setRow(0,5,five[5]);
  lc.setRow(0,6,five[6]);
  lc.setRow(0,7,five[7]);
  break;

case 6:
  lc.setRow(0,0,six[0]);
  lc.setRow(0,1,six[1]);
  lc.setRow(0,2,six[2]);
  lc.setRow(0,3,six[3]);
  lc.setRow(0,4,six[4]);
  lc.setRow(0,5,six[5]);
  lc.setRow(0,6,six[6]);
  lc.setRow(0,7,six[7]);
  break;
} 
}

void loop() { 
  int Next;

  if (digitalRead(PinA)==HIGH) {Actual=3;}
  if (digitalRead(PinB)==HIGH) {Actual=4;}
  if (digitalRead(PinC)==HIGH) {Actual=5;}
  if (digitalRead(PinD)==HIGH) {Actual=6;}

  if (digitalRead(ButtonPin)==LOW) {
      showNum(Current);
      do {
         Next=random(1,7);
         }  
      while (Current==Next); //因为如果两次出现相同的数字,看起来
			     //会觉得没有变,所以这里要保证生成不同
      Current=Next; 
      delay(delaytime);
    MarkStart=true;  

  }

  if ((MarkStart==true) && (digitalRead(ButtonPin)==HIGH)){  //按键抬起,生成实际显示的结果
	MarkStart=false; 
	if (Actual==0xFF) {showNum(random(1,7));}  //如果当前未收到选择,随机生成一个
	else
	  {showNum(Actual);}         //收到过选择,那么就显示
	Actual=0xFF;  
	}

}

和上一个电子骰子相比,这个接线看起来复杂多了,这是视觉差异而已,看起来多了很多线因为5V和GND用的比较多而已,所以导入面包板,都从上面取电而已。

20140703980

 

程序下载 dice2

最后这篇文章告诉大家了一个非常重要的事实就是:不要和电子的赌博机游戏,因为你根本不可能赢。如果真的有人对你这样炫耀,存在的三种可能性是:

1.他在作弊
2.机器坏了
3.他想拉你一起入伙(这个可能性最大)

参考:
1:很早之前听说过这个事情,但是一直没有找到出处。从目前能够搜索到的资料看,来源应该是余华的微博
http://tieba.baidu.com/p/1621656047

2.http://www.lab-z.com/arduinodice1/  本系列的第一篇文章

3.http://www.lab-z.com/%E7%94%A8-arduino-%E6%89%93%E9%80%A0ppt%E9%81%A5%E6%8E%A7%E5%99%A8/   用-arduino-打造ppt遥控器

制作一个电子骰子(1)

有一种说法,说赌场的英文单词 CASINO 是中国人发明的:

“据说这也是中国人发明的。十八世纪,美国从中国广东引进了大批的劳工修铁路。美国的西部牛仔就开设赌局,赚这帮广东人的血汗钱。他们白天干活,晚上赌博消磨时光。一到傍晚赌局开始的时候,这帮劳工就争相吆喝“开始了”___石毓智:也谈华人为什么好赌 http://t.cn/zYOPz3e”。

这样的解释听上去有些道理,“开始喽”发音也和 Casino 很类似…….经过考证非常遗憾实际上并不是这样,具体的来源是意大利。【参考1】

骰子是一种简单方便的赌博工具,具有体积小便于携带,低噪音,节能环保,便于广大人民群众学习裂解等等特点,记得高中时代,电视热播《赌神》《赌圣》系列,我们班上的几个同学放课后便去学校边上的小公园,模仿电影中的样子猜大小,赌注不大也觉得刺激。当然,作为一个好孩子是不会直接参加赌局的,通常只是提供用具偶尔抽水而已…….

dice

这里介绍用 arduino 做一个电子的骰子,材料清单如下:

Arduino Uno x1
Max7219 + 8×8 LED点阵 x1
按键开关 x1
杜邦线 若干

Max7219 + 8×8 LED点阵是下面这个样子,用来显示当前的点数。当然,还以用数码管,只是那样看起来太不专业了。

max7219

电路方面,连接如下:

按钮三个Pin很简单,分别连到Ardudice1ino上的 GND,3.3V 和 D2

MAX7219五个Pin连接如下

VCC  <—–> 5V
GND  <—–> GND
DIN    <—–> D12
CS     <—–> D10
CLK   <—–> D11

下面的程序核心有2部分,一部分是用来处理按下按钮时,让LED看起来在不断跳动;另外一部分是直接生成最终的结果。这样做的原因是为了保证概率上的公平以及代码的简洁。

/*
日期:2014-7-2
功能:MAX7219驱动8*8点阵
作者:Z.t
IDE:1.0.5
硬件:最小系统UNO
说明:本代码主要参考了官方的示例程序
*/

//官方的库
#include "LedControl.h"

/*
Now we need a LedControl to work with.
***** These pin numbers will probably not work with your hardware *****
第一个参数:pin 12 is connected to the DataIn
第二个参数:pin 11 is connected to the CLK
第三个参数:pin 10 is connected to LOAD
第四个参数:We have only a single MAX72XX.
*/
LedControl lc=LedControl(12,11,10,1);

/* we always wait a bit between updates of the display */
unsigned long delaytime=50;
int ButtonPin=3;
int Current=1;

void setup() {
/*
The MAX72XX is in power-saving mode on startup,
we have to do a wakeup call
*/
lc.shutdown(0,false);
/* Set the brightness to a medium values */
lc.setIntensity(0,8);
/* and clear the display */
lc.clearDisplay(0);

randomSeed(analogRead(0));
pinMode(ButtonPin, INPUT);
}

void showNum(int x) {
/* here is the data for the characters */
byte one[8]={
B00000000,
B00000000,
B00000000,
B00111000,
B00111000,
B00000000,
B00000000,
B00000000};

byte two[8]={
B00000000,
B00000110,
B00000110,
B00000000,
B00000000,
B01100000,
B01100000,
B00000000};

byte three[8]={
B00000000,
B00111000,
B00111000,
B00000000,
B01100110,
B01100110,
B01100110,
B00000000};

byte four[8]={
B00000000,
B01100110,
B01100110,
B00000000,
B00000000,
B01100110,
B01100110,
B00000000};

byte five[8]={
B00000000,
B01100110,
B01100110,
B00011000,
B00011000,
B01100110,
B01100110,
B00000000};

byte six[8]={
B01100110,
B01100110,
B00000000,
B01100110,
B01100110,
B00000000,
B01100110,
B01100110};

switch (x) {
case 1:
lc.setRow(0,0,one[0]);
lc.setRow(0,1,one[1]);
lc.setRow(0,2,one[2]);
lc.setRow(0,3,one[3]);
lc.setRow(0,4,one[4]);
lc.setRow(0,5,one[5]);
lc.setRow(0,6,one[6]);
lc.setRow(0,7,one[7]);
break;
case 2:
lc.setRow(0,0,two[0]);
lc.setRow(0,1,two[1]);
lc.setRow(0,2,two[2]);
lc.setRow(0,3,two[3]);
lc.setRow(0,4,two[4]);
lc.setRow(0,5,two[5]);
lc.setRow(0,6,two[6]);
lc.setRow(0,7,two[7]);
break;
case 3:
lc.setRow(0,0,three[0]);
lc.setRow(0,1,three[1]);
lc.setRow(0,2,three[2]);
lc.setRow(0,3,three[3]);
lc.setRow(0,4,three[4]);
lc.setRow(0,5,three[5]);
lc.setRow(0,6,three[6]);
lc.setRow(0,7,three[7]);
break;

case 4:
lc.setRow(0,0,four[0]);
lc.setRow(0,1,four[1]);
lc.setRow(0,2,four[2]);
lc.setRow(0,3,four[3]);
lc.setRow(0,4,four[4]);
lc.setRow(0,5,four[5]);
lc.setRow(0,6,four[6]);
lc.setRow(0,7,four[7]);
break;
case 5:
lc.setRow(0,0,five[0]);
lc.setRow(0,1,five[1]);
lc.setRow(0,2,five[2]);
lc.setRow(0,3,five[3]);
lc.setRow(0,4,five[4]);
lc.setRow(0,5,five[5]);
lc.setRow(0,6,five[6]);
lc.setRow(0,7,five[7]);
break;

case 6:
lc.setRow(0,0,six[0]);
lc.setRow(0,1,six[1]);
lc.setRow(0,2,six[2]);
lc.setRow(0,3,six[3]);
lc.setRow(0,4,six[4]);
lc.setRow(0,5,six[5]);
lc.setRow(0,6,six[6]);
lc.setRow(0,7,six[7]);
break;
}
}

void loop() {
int Next;
boolean MarkStart=false; //标记是否按键抬起

if (digitalRead(ButtonPin)==LOW) {
showNum(Current);
do {
Next=random(1,7);
}
while (Current==Next); //因为如果两次出现相同的数字,看起来
//会觉得没有变,所以这里要保证生成不同
Current=Next;
delay(delaytime);
MarkStart=true;
}

if ((MarkStart==true) && (digitalRead(ButtonPin)==HIGH)){ //按键抬起,生成实际显示的结果
MarkStart=false;
showNum(random(1,7));
}
}

 

 

 

 

 

程序设计上有个很有趣的地方,

之前我是使用下面的代码来做最后的显示,但是经过考虑这是不妥当的,你能说出为什么要修改吗?答案见最下面。

if ((MarkStart==true) && (digitalRead(ButtonPin)==HIGH)){ //按键抬起,生成实际显示的结果
MarkStart=false;
showNum(Next);
}

要是有个外壳能更好看一点

20140702977

程序下载 dice1

参考:
1.http://blog.sina.com.cn/s/blog_507de1780102eyec.html 程阳:赌场的英文单词 CASINO 来源自哪?

因为这样做导致结果不公平,比如我在抬起按键之前看到点数是 6,那么最后出现的结果肯定是 1-5 之间,无形之中导致了不公平…….

Delphi发送键盘消息的例子

这是一个Delphi实现对其他窗口发送按键的例子。

首先是 keyTest,这是用来测试接收按键的程序,接收键盘发出的方向键,并将其显示在窗口上,只显示上下左右四个方向键。

keytest

其次是 SensKey ,它会首先查找窗口,然后发送按键给找到的窗口。程序是Console模式的,每隔1s发送一个方向按键。

测试时,先运行 Keytest,然后运行 SendKey,就可以看到 KeyTest 的窗口被挪到最前面,然后依次收到Sendkey 程序发出来的按键。

下载: KeyT

参考:

1.晓风缠月的博客
http://blog.sina.com.cn/s/blog_63cefe150100ogp9.html delphi虚拟键值
2.东方千树
http://hi.baidu.com/fangqianshu/item/97dc6fa46c4002e915329b21 Delphi中的OnKeyDown事件等等
OnKeyPress 只能抓到数值或字母按键及 Esc键、空白键,但不含功能键(F1-F12)
OnKeyDown 能抓到所有的键(除 Tab 键)但不能分辨「对称键」的不同
OnShortCut 能抓到所有的键(含 Tab 键)且能分辨「对称键」的不同

测试黄曲霉素

黄曲霉素是目前人类非常确定的致癌物,它被发现的原因是,1960年,英国发现有10万只火鸡死于一种以前没见过的病,被称为“火鸡X病”,再后来鸭子也被波及。经过研究最大的嫌疑是饲料。这些可怜的火鸡和鸭子吃的是花生饼。花生饼是花生榨油之后剩下的残渣,富含蛋白质,是很好的禽畜饲料。科学家们很快从花生饼中找到了罪魁祸首,一种真菌产生的毒素。它被命名为“aflatoxin ”。自那以后,黄曲霉毒素就获得了科学家们的特别关照,对它的研究可能是所有的真菌毒素中最深入最广泛的。[参考1]

因此,很简单的结论:一定要避免使用发霉的花生。另外一个问题则是:如果花生产生了黄曲霉素,那么用这样的花生压榨出来的花生油是否也会受到污染?

非常不幸,对于这个问题的回答是肯定的…….因此,从某种意义上来说,为了躲避可能遇到的地沟油问题,很多人选择自己携带原料去进行压榨,但是,这样并没有办法避免黄曲霉素的污染(自己压榨的食用因为杂质多油烟点也会比较低)。

家人送过来了30多斤的花生油,为了验证是否有黄曲霉素的问题,进行了下面的实验:

淘宝上购买的检测试纸

image

根据说明,要求10ml水,配5ml油,但是因为没有适合的容积工具,只好使用台秤来粗略的测量

image_1_

摇晃震荡15分钟,然后静置一段
image_2_

滴入到检测卡上,过一段时间之后查看结果,正常,检测合格
image_3_

测试卡背面的比对说明
image_5_

参考:
1.http://baike.baidu.com/view/287205.htm?fr=aladdin

UDK2014 发布了

今天刚刚注意到 UDK2014 发布了,可以在下面的网址看到

http://sourceforge.net/apps/mediawiki/tianocore/index.php?title=UDK2014

udk2014

从 Release Note 来看,和UDK2010相比变化不大,有如下变化

1. Support Unified Extensible Firmware(UEFI) specification 2.4.
2. Support Platform Initialization(PI) specification 1.3.
3. Support OpenSSL version 0.98w and whole X509v3 extension check.
4. Support TPM 2.0.
5. Support I2C. //这个主要是平板用的
6. Support NVM Express.
7. Update ACPI5.0 tables, PCI definitions and Atapi definitions.
8. Update BaseTools Python version to 2.7.3.

用前一段写的计算频率的程序测试了一下,能够正常编译和运行。

更换ETS3125i电话机的电池

家里使用的无线电话用了几年之后感觉电池很差了,经常通话没有多久就自动关机了。taobao出售的电池要30元左右,于是自己动手更换新电池。20140501895

电池就在后盖里面,很好拆。20140501896

容量比较小,我现在也没有搞清楚,是一节600mah,还是三节加在一起600mah.font

三节串联,构成的电池。引出了三根线,正极负极还有一个多余的白色Pin. 我请教了一下专门做电池的朋友,她们家的电池是用电阻做ID的,但是这个电池看起来中间的元件像是齐纳二极管,可能是用来做4.2v限压来测试是否充满的。

rear

 

我找了3个普通的充电电池(7号的),串联起来。电极不吃锡,我用了一种网上买的点焊剂,蛮好用的。

kk

我是直接用之前电池的线路。负极出来一根线和那个白色的线是并联在一起的。

ss

包起来,装进去就好了。

end

 

前面两次使用的时候电量不太准,循环两次之后就OK了。个人感觉充满一次,至少能够维持通话1个多小时了。

 

Delphi 写的替换 // 注释小工具

Delphi 的注释方式有两种:一种是传统的 Pascal 的 {} ,另外一种是 // 的单行注释。

下面的代码能将程序中的 //abcdefg 替换为 {//abcdefg} 这样的形式

program Project6;

{$APPTYPE CONSOLE}

//必须声明 Classes 否则 try..except 无法正常捕捉到异常
uses
  SysUtils,Classes;

var
  fInput,fOutput:TextFile;
  Line:String;
begin
  writeln('   Delphi comments "//" to Pascal comments "{}"');
  writeln('             Usage "DC2PC filoname"');
  writeln('            Powered by www.lab-z.com');

  //如果没有输入文件名
  if paramCount=0 then
    begin
      writeln('Please input file name!');
      exit;
    end;

  //打开输入文件
  AssignFile(fInput,ParamStr(1));
  try
    Reset(fInput);
  Except
    writeln('Opening file '+ParamStr(1)+' error!');
    exit;
  end;

  //替换后的结果放在 输入文件名.pas 文件中
  AssignFile(fOutput,ParamStr(1)+'.pas');
  rewrite(fOutput);

  //在输入文件中查找 // 的注释
  while NOT eof(fInput) do
     begin
       readln(fInput,Line);
       if pos('//',Line)<>0 then
         begin
           insert('{',Line,pos('//',Line));
           Line:=Line+'}';
         end;
       //writeln(Line);
       writeln(fOutput,Line);
     end;

  CloseFile(fInput);
  CloseFile(fOutput);
end.

 

上述代码在实际使用中还有一点小问题,比如 writeln(“abc //efg”); 这样的代码也会被替换,不过正常的代码应该不会有很多处这样的用法,出现问题手工修改一下就好了。

制作一个PS2键盘记录器

这是一个能够记录PS2键盘发出的按键信息的演示装置。

使用的元件:

Arduino UNO                          一块
PS2 延长线(一端公头,一端母头)     一根
测试钩                               三根

首先要将PS2延长线剥开,其中有四根线,分别是 Vcc/GND/Clock/Data。我们只需要钩取其中除了Vcc之外的三根线。

psa

特别说一下这几根线的分布,在公头端和母头端看过去的Pin编号是对称的,如下图所示(网上有人说一些资料搞错了,我看了一下,是他本人搞错了公母而已)。选好线之后务必使用万用表确定接线无误。

psb

连接方面,上面图片中白色测试钩钩取的是黄色线 GND;绿色测试钩钩取的是红色Data;白色测试钩钩取的是白色线 Clock。

PS2 头和线色对应关系如下图:

psc

程序调用了PS2Keyboard library 【参考1】,用它来做PS2 协议的解码。调用的方式就是keyboard.begin(DataPin, IRQpin);  DataPin 要给出Arduino上连接到 PS2 Data的Pin脚;IRQpin 要给出Arduino上连接到 PS2 Clock的Pin脚。本例中,分别是 Pin8 和 Pin2。

psd

开始运行后, Setup()中会在串口输出一个菜单,提示如果输入 P ,那么会直接打印出 Arduino板载 EEPROM中的内容(UNO 的EEPROM不大,只有1K【参考2】。前面2个Byte用来存放一个记录当前要写入的位置。其余位置用来存储记录的键值。如果超过1K,那么返回开始处重新覆盖)。如果5秒之内没有输入 P ,那么程序会运行loop() 中的代码。先使用PS2Keyboard library 进行解码,然后每次都要从EEPROM中取出前2个Bytes组成一个指针,指示要将这个键值要存放在EEPROM中的位置。

  
#include "PS2Keyboard.h"
#include <EEPROM.h>

const int DataPin = 8;
const int IRQpin =  2;
const int MaxLength = 1024;
PS2Keyboard keyboard;
int SaveTo;

void setup() {
  boolean TimeOutMark=true;
  keyboard.begin(DataPin, IRQpin);  
  Serial.begin(9600);
  
  Serial.write("Keyboard Logger for Arduino\n");
  Serial.write("www.lab-z.com 2014.5\n");
  Serial.write("   p - for output record \n");
  
  unsigned long _time = millis();  //用于超时
  while ((millis() - _time) < 5000)
  {
  char inChar = Serial.read();
  if ((inChar == 'p') || (inChar == 'P'))
    {
       TimeOutMark=false;
       for (int i=2; i<MaxLength; i++)
         {
              char c = EEPROM.read(i) ;
              // check for some of the special keys
              if (c == PS2_ENTER) {
                    Serial.println();
              } else if (c == PS2_TAB) {
                    Serial.print("[Tab]");
              } else if (c == PS2_ESC) {
                    Serial.print("[ESC]");
              } else if (c == PS2_PAGEDOWN) {
                    Serial.print("[PgDn]");
              } else if (c == PS2_PAGEUP) {
                    Serial.print("[PgUp]");
              } else if (c == PS2_LEFTARROW) {
                    Serial.print("[Left]");
              } else if (c == PS2_RIGHTARROW) {
                    Serial.print("[Right]");
              } else if (c == PS2_UPARROW) {
                    Serial.print("[Up]");
              } else if (c == PS2_DOWNARROW) {
                    Serial.print("[Down]");
              } else if (c == PS2_DELETE) {
                    Serial.print("[Del]");
              } else {
                  // otherwise, just print all normal characters
                    Serial.print(c);
              } // End of else            
         }//End of for
     } //End of if ((inChar = 'p') || (inChar = 'P'))  
  }// End of While 
 if (TimeOutMark==true)  {Serial.write("Timeout occured!\n");  }
 else {Serial.write('\n Output End!');}
 
    EEPROM.write(0,2);
    EEPROM.write(1,0);     

} // End of Setup

void loop() {
  char c;

 if (keyboard.available()) {
     // read the next key
    c = keyboard.read();
   Serial.print(c);
    SaveTo = EEPROM.read(0) + (EEPROM.read(1) <<8);
    EEPROM.write(SaveTo,c);
    SaveTo++;
    if (SaveTo>MaxLength) {SaveTo=2;}
    EEPROM.write(0,SaveTo);
    EEPROM.write(1,(SaveTo >> 8));    
  }

  
}

 

实验中使用的是一个USB键盘,通过USB转PS2转为电脑上能使用的PS2 (资料上说现在的USB键盘内置2种协议,根据当前插入的接口自动选择输出的协议)。该键盘通过上面提到的PS2延长线接入电脑。记录结果如下:

pse

因为尺寸的原因,可以看出来这个东西只有演示的价值,如果真想做一个实用性的东西,还要选择PIC系列的小尺寸单片机加上SPI ROM………

下载
KeyLog

参考:

1.http://playground.arduino.cc/Main/PS2Keyboard

2.Arduino上的单片机自带EEPROM  328P 有 1024字节

http://wiki.geek-workshop.com/doku.php?do=export_xhtml&id=arduino:libraries:eeprom

直接测量电机转速

论坛上有个朋友提了一个问题【参考 1】 “请问大神,我想测一个上万转的转速,也就是几万hz的频率,应该用什么传感器?”

论坛上的朋友介绍说这样的通常都是用减速齿轮,降低到一个可以直接测量的范围后进行测试。但是我认为这样做比较麻烦,需要适合的减速齿轮,并且可能导致测不准的问题。我猜测应该可以使用光敏电阻加激光同,通过轴承上的小孔对光遮断和导通来完成测试,

为了验证这个想法,设计了一个简单的装置,使用了如下的元件:

1.光敏电阻(我不知道是什么材料)

2.激光头(很早之前买东西送的,功率应该是非常小的)

3.电动机(1v-6V)

4.100K 电阻一只

设备:

数字示波器一台(泰克的)

下图可以看到,用了3组电池供电,最左边是激光头 3.7v左右,中间黄色和白色夹子是给电机供电的,分别测试是 1.5 3v 和 4.5v供电最右边是和一个100K电阻串联的光敏电阻,我做了一个小纸筒包起来了,这样可以减少外界光线的干扰。转轴是一根塑料吸管,里面插入了4根牙签,这样刚好能将电机的轴卡住。同时,轴上还有一张纸片,这样转动起来之后可以阻断激光。

20140512916

测试结果如下,这是电动机使用 1.5v 供电时,测试的结果:

140512_041048

这是电动机使用 4.5v 供电时,测试的结果:

140512_041946

可以从波形,结合遮蔽的原理可以很容易计算出频率。但是对于波形为什么会有这么大的差别我并没有太好的解释,猜测是因为电压不同导致扭力的差别导致的,可以看出来当4.5v时是更接近理论波形的。

最后放一张overview的照片。

20140512917

从理论上说,我设想的直接用激光来进行转速测试是可行的。但是目前的资料来看选择硫化铅的光敏电阻应该是可行的。【参考2】

hz

参考:

1.http://www.geek-workshop.com/thread-9612-1-1.html

2.https://www.google.com.hk/url?sa=t&rct=j&q=&esrc=s&source=web&cd=11&ved=0CCkQFjAAOAo&url=%68%74%74%70%3a%2f%2f%64%6f%63%73%65%72%76%69%63%65%2e%73%68%74%76%75%2e%6f%72%67%2e%63%6e%2f%77%65%62%2f%64%6f%77%6e%6c%6f%61%64%2e%61%73%70%78%3f%46%69%6c%65%49%44%3d%35%30%61%36%32%66%62%36%2d%31%33%66%33%2d%34%66%30%39%2d%38%32%66%66%2d%62%30%32%33%31%30%33%37%34%65%31%31&ei=aWt3U9fXI9eC8gX714LoCg&usg=AFQjCNG73nRZfEuQ_6yCAEzLGo7VjaUmHA&sig2=bBUADuiJCdukBOP-BJl49g
光敏电阻的频率特性