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

上面几篇文章介绍的都是虚拟的电子骰子,这篇文章将会介绍用Arduino来玩实体的骰子。相比前面几篇材料更复杂,牵涉到的配备更多。当然,所有的技术问题都能找到合理的解释,暂时无法回答的问题是“这个东西能干什么?”

首先介绍使用到的材料:

1. 电动骰子机(toabao购买,电池驱动,上面有个按钮,按动时自动旋转骰子)
2. 继电器 (我们用它来控制上面骰子机的开关)
3. 电池夹(用来给骰子机供电)
4. Arduino UNO (最基础的型号足够用的,如果你有其他的型号肯定也没问题)
5. 摄像头 (我选用的是 罗技的 U-VQN42,这是很老的型号,以至于目前没有Win7的驱动)
6. 电脑 (我们用摄像头采集图像,然后使用Intel 的OpenCV来进行图像的识别处理)

可以看出这个实验涉及到的内容远远比之前的复杂,设计这个实验大约花费了2周左右,实做将近两天。

从流程上来说,功能过程如下:

Drawing1

硬件部分的介绍:
介绍Arduino的连接:

Untitled Sketch_bb

虽然骰子机内部是要安装2节电池的,但是我发现我用一节五号充电电池也能让它工作的很好,考虑到外壳强度的问题,也就直接选择一节电池了。Arduino在这里就是发挥骰子机开关的功能。

此外的建议是:连接继电器的常断开端口,这样,当断电时骰子机不会转动。

image003

骰子机原来的是亚克力外壳,因为反光的问题,在测试时结果不理想,所以重新用纸做了一个。这里特别感谢严小姐,她用灵巧的双手帮我完成了这个工作。可以看到,目前整个摄像头可以伸进去,视界中只有地盘和白色的区域,这对于识别是非常有利的。还建议使用一个外部固定光源来进行补光之类的设定。

image004

软件部分:

1. Arduino程序。

#define LED_PIN 13

void setup()
{
Serial.begin(9600);
pinMode(LED_PIN, OUTPUT);
//Default
digitalWrite(LED_PIN, HIGH);
}

void loop()
{
char c;

while (Serial.available() > 0)
{
c=Serial.read();
if (‘b’==c)
{
digitalWrite(LED_PIN, LOW);
}
if (‘s’==c)
{
digitalWrite(LED_PIN, HIGH);
}
}
}

实验中 Arduino 的程序非常简单,测试时可以使用 Putty 这样的工具打开对应串口,直接发送命令测试。

2. PC端程序。

因为要使用OpenCV,所以选择了VS2008,本打算使用Delphi,无奈他的OpenCV不成熟。

这个程序可能分成两部分,一部分是串口通讯,只需要发送部分【参考1】,另外一部分是OpenCV的识别。算法上来自【参考2】,他给出了一种非常简单的算法:首先对图像进行Canny边缘提取,然后使用FloodFill对于包络范围进行填充,同时会返回填充区域的面积,利用面积就可以判断识别出来的是否为骰子的点(例如:1点的时候面积可能在200左右,2点的话,每个点可能在50左右),对于处在合理面积中的识别区域就判断为点数。

#include “stdafx.h”
#include <opencv2/opencv.hpp>
#include <opencv/cv.h>
#include <opencv/cxcore.h>
#include “opencv2/highgui/highgui.hpp”
#include

using namespace cv;

int p1=49, p2=258; //Paramters for Canny
Mat framepic; //framepic 存放摄像头抓取图像
Mat framegray; // framepic 灰度化后的图像
Mat canny; //canny 存放摄像头处理后的图像
VideoCapture cap(0);
int count[7]={0,0,0,0,0,0,0};
int counter=0;
int retry=0;

void on_trackbar(int, void*) {
Canny(framegray, canny, p1, p2);
imshow(“canny”, canny);
}

void delayshow(int dSecond){
time_t sTime=time(NULL);

while (difftime(time(NULL),sTime)<dSecond)
{
cap>>framepic;
cvtColor(framepic,framegray, CV_BGR2GRAY);
GaussianBlur(framegray, framegray,Size(7,7),1.5,1.5);
Canny(framegray, canny, p1, p2);
imshow(“canny”, framegray);
imshow(“Preview”,canny);
int c=waitKey(33);
if(c==27) break;
}
}

int main(int argc, const char** argv)
{
//==============================================
//code for COM port communication Begin
//==============================================
DCB dcb;
HANDLE hCom;
BOOL fSuccess;
TCHAR SPort[]=L”\\\\.\\COM7″;

char txcharstart[]=”b”; //b char for Start shaking dice
char txcharstop []=”s”; //s char for Stop shaking dice

DWORD iBytesWritten;

//Prepare COM part communication
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 (“[Error] CreateFile failed with error %d.\n”, GetLastError());
return (1);
}

SetupComm(hCom, 32, 32);

// 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 (“[Error] GetCommState failed with error %d.\n”, GetLastError());
return (2);
}

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

dcb.BaudRate = CBR_9600; // 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 (“[Errror] SetCommState failed with error %d.\n”, GetLastError());
return (3);
}
//==============================================
//code for COM port communication End
//==============================================

srand(time(NULL));

int dTime;
if(!cap.isOpened())
return -1;

cap>>framepic;
cvtColor(framepic,framegray, CV_BGR2GRAY);
Canny(framegray, canny, p1, p2);
imshow(“canny”, framegray);
imshow(“Preview”, framegray);

//You may have to find the P1 and P2 value for your environment
createTrackbar(“p1″,”canny”,&p1,1000,on_trackbar);
createTrackbar(“p2″,”canny”,&p2,1000,on_trackbar);

//Loop shaking dice and count
for(;;)
{
//Shake start
printf (“[Message] Send start command \n”);
if (FALSE==WriteFile(hCom, &txcharstart, strlen(txcharstart), &iBytesWritten,NULL)) {
printf(“[Error]: %d”, GetLastError());
retry=0;
while ((TRUE==WriteFile(hCom, &txcharstart, strlen(txcharstart), &iBytesWritten,NULL)) ||
(retry>5))
{
printf(“[Error]: %d”, GetLastError());
retry++;
}
}

dTime=rand()%2+1;

//Shake the dice for a random seconds
printf(“Shake delay %d \n”,dTime);
delayshow(dTime);

//Shake stop
printf (“[Message] Send stop command \n”);
if (FALSE==WriteFile(hCom, &txcharstop, strlen(txcharstop), &iBytesWritten,NULL)) {
printf(“[Error]: %d”, GetLastError());
retry=0;
while ((TRUE==WriteFile(hCom, &txcharstop, strlen(txcharstop), &iBytesWritten,NULL)) ||
(retry>5))
{
printf(“[Error]: %d”, GetLastError());
retry++;
}
}

//It delay 5 seconds for dice stopping
delayshow(5);

//==============================================
//code dice point recognize start
//==============================================
cap>>framepic;
cvtColor(framepic,framegray, CV_BGR2GRAY);
GaussianBlur(framegray, framegray,Size(7,7),1.5,1.5);
Canny(framegray, canny, p1, p2);
imshow(“canny”, framegray);
imshow(“Preview”,canny);

int num = 0;
for(int y=0;y<canny.size().height;y++)
{
uchar *row = canny.ptr(y);
for(int x=0;x<canny.size().width;x++)
{
if(row[x] <= 128)
{
int area = floodFill(canny, Point(x,y), CV_RGB(0,0,160));
printf(“filling %d, %d gray, area is %d\n”, x, y, area);
if(area>37 && area < 300) num++;
}
}
}
printf(“number is %d\n”, num);
//==============================================
//code dice point recognize end
//==============================================
counter++;
count[num]++;
printf(“=====================================================\n”);
printf(“Total=%4d| One | Two | Three | Four | Five | Six |\n”,counter);
printf(” | %4d|%4d | %4d | %4d | %4d | %4d|\n”,count[1],count[2],count[3],count[4],count[5],count[6]);
printf(“=====================================================\n”);
printf(“Show result delay 3s\n”);
delayshow(3);
int c=waitKey(33);
if(c==27) break;
}

system(“PAUSE”);
CloseHandle(hCom);

return 0;
}

运行结果就是下面这个样子。Console 显示统计结果,旁边一个显示灰度化后的摄像头内容,另外一个显示边缘提取后的结果。

image005

我只做了一个多小时,收集的结果不多,看起来结果并不平均,不知道如果做个一万次之类的会是什么结果。

运行时的视频(刚开始放在Tudou的时候,审核没有通过,估计是因为名称中有“骰子”。后来申诉了一下)

http://www.tudou.com/programs/view/TtqRy-KRr-8/?resourceId=414535982_06_02_99

其实更好的方案是记录下每次的运行结果,因为我发现这个东西每次之间似乎有相关性。比如说这次出现六点,那么下次出现六点的可能性似乎大于 1/6。猜测和骰子机的摇骰子方式有相关性。

最后的最后,吐槽一下电脑的问题:第一个没想到是前面说过的罗技摄像头居然不支持Windows 7,如果你也想做同样的实验需要购买摄像头不妨考虑一下买个二手的微软摄像头,只是外观差一些,效果和支持应该比罗技的好多了;第二个是梅捷 SY-D2700-U3M 的主板,BIOS中居然没有设计风扇温控功能【参考4 提到的Watchdog就是为他设计的】。最近天气热了,硬盘丢失的问题更是频频出现,鲁大师检测CPU温度50左右,主板温度在60左右就会出现这个现象。当我连接一个风扇到上面的时候,5000RPM让人难以忍受。下一个实验就是如何做一个能够调节转速的风扇了。

参考:
参考1:VS2008串口发送参考代码
http://www.lab-z.com/%E4%B8%87%E4%B8%87%E6%B2%A1%E6%83%B3%E5%88%B0%EF%BC%8C%E4%B8%B2%E5%8F%A3%E7%9A%84%E6%95%85%E4%BA%8B/
参考2:超简单的opencv骰子点数识别,效果居然还不错 http://chen-xiao.com/?p=250
参考3:OpenCV的imshow无法正常显示视频http://haibuo1981.iteye.com/blog/1401764
非常感谢作者解决了困惑我一下午的问题,如果你用 cap >> img 这样之后,想再通过显示imShow到屏幕上(特别是在一个循环中),必须使用waitKey(xx)给程序足够的时间来更新画面。
参考4:使用 Arduino 打造一个硬件的Watchdog
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/

Step to UEFI (11)—- 让程序中断运行的方法

前面介绍了 Pasue 一下的方法,这里再介绍一下让程序停止执行的方法。 和 DOS 不同,Shell中的程序通常都像吃过炫迈口香糖那样—-根本停不下来…..要想能够中止运行,可以通过 Shell Environment 2 特别设置一下。

下面的代码设置了 ESC 为Break 键,运行中可以使用它来终止运行,这里的代码和前面 PauseTest的长得几乎一样:

//
// PauseTest.c
//
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/ShellLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/UefiApplicationEntryPoint.h>
#include <Library/BaseMemoryLib.h>

#define SHELL_INTERFACE_PROTOCOL \
  { \
    0x47c7b223, 0xc42a, 0x11d2, 0x8e, 0x57, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b \
  }
EFI_GUID        ShellInterfaceProtocol  = SHELL_INTERFACE_PROTOCOL;
EFI_GUID		SEGuid = EFI_SE_EXT_SIGNATURE_GUID;
//
// The shell environment is provided by a driver.  The shell links to the
// shell environment for services.  In addition, other drivers may connect
// to the shell environment and add new internal command handlers, or
// internal protocol handlers.
//
#define SHELL_ENVIRONMENT_INTERFACE_PROTOCOL \
  { \
    0x47c7b221, 0xc42a, 0x11d2, 0x8e, 0x57, 0x0, 0xa0, 0xc9, 0x69, 0x72, 0x3b \
  }
EFI_GUID        ShellEnvProtocol = SHELL_ENVIRONMENT_INTERFACE_PROTOCOL;

#define EFI_OUTPUT_PAUSE    0x00000002

typedef struct {
  SHELLENV_EXECUTE          Execute;  // Execute a command line
  SHELLENV_GET_ENV          GetEnv;   // Get an environment variable
  SHELLENV_GET_MAP          GetMap;   // Get mapping tables
  SHELLENV_ADD_CMD          AddCmd;   // Add an internal command handler
  SHELLENV_ADD_PROT         AddProt;  // Add protocol info handler
  SHELLENV_GET_PROT         GetProt;  // Get the protocol ID
  SHELLENV_CUR_DIR          CurDir;
  SHELLENV_FILE_META_ARG    FileMetaArg;
  SHELLENV_FREE_FILE_LIST   FreeFileList;

  //
  // The following services are only used by the shell itself
  //
  SHELLENV_NEW_SHELL        NewShell;
  SHELLENV_BATCH_IS_ACTIVE  BatchIsActive;

  SHELLENV_FREE_RESOURCES   FreeResources;
} EFI_SHELL_ENVIRONMENT;

EFI_SHELL_INTERFACE             *SI;
EFI_SHELL_ENVIRONMENT           *SE;
EFI_SHELL_ENVIRONMENT2          *SE2;

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

//Copy from \Shell\Library\Misc.c
BOOLEAN
GrowBuffer (
  IN OUT EFI_STATUS   *Status,
  IN OUT VOID         **Buffer,
  IN UINTN            BufferSize
  )
/*++

Routine Description:

  Helper function called as part of the code needed
  to allocate the proper sized buffer for various 
  EFI interfaces.

Arguments:

  Status      - Current status

  Buffer      - Current allocated buffer, or NULL

  BufferSize  - Current buffer size needed
    
Returns:
    
  TRUE - if the buffer was reallocated and the caller 
  should try the API again.

--*/
{
  BOOLEAN TryAgain;

  //
  // If this is an initial request, buffer will be null with a new buffer size
  //
  if (NULL == *Buffer && BufferSize) {
    *Status = EFI_BUFFER_TOO_SMALL;
  }
  //
  // If the status code is "buffer too small", resize the buffer
  //
  TryAgain = FALSE;
  if (*Status == EFI_BUFFER_TOO_SMALL) {

    if (*Buffer) {
      FreePool (*Buffer);
    }

    *Buffer = AllocateZeroPool (BufferSize);

    if (*Buffer) {
      TryAgain = TRUE;
    } else {
      *Status = EFI_OUT_OF_RESOURCES;
    }
  }
  //
  // If there's an error, free the buffer
  //
  if (!TryAgain && EFI_ERROR (*Status) && *Buffer) {
    FreePool (*Buffer);
    *Buffer = NULL;
  }

  return TryAgain;
}

//Copy from \Shell\Library\Handle.c
EFI_STATUS
LocateHandle (
  IN EFI_LOCATE_SEARCH_TYPE       SearchType,
  IN EFI_GUID                     * Protocol OPTIONAL,
  IN VOID                         *SearchKey OPTIONAL,
  IN OUT UINTN                    *NoHandles,
  OUT EFI_HANDLE                  **Buffer
  )
/*++

Routine Description:

  Function returns an array of handles that support the requested protocol 
  in a buffer allocated from pool.

Arguments:

  SearchType           - Specifies which handle(s) are to be returned.
  Protocol             - Provides the protocol to search by.   
                         This parameter is only valid for SearchType ByProtocol.
  SearchKey            - Supplies the search key depending on the SearchType.
  NoHandles            - The number of handles returned in Buffer.
  Buffer               - A pointer to the buffer to return the requested array of 
                         handles that support Protocol.

Returns:
  
  EFI_SUCCESS           - The result array of handles was returned.
  EFI_NOT_FOUND         - No handles match the search. 
  EFI_OUT_OF_RESOURCES - There is not enough pool memory to store the matching results.

--*/
{
  EFI_STATUS  Status;
  UINTN       BufferSize;

  //
  // Initialize for GrowBuffer loop
  //
  Status      = EFI_SUCCESS;
  *Buffer     = NULL;
  BufferSize  = 50 * sizeof (EFI_HANDLE);

  //
  // Call the real function
  //
  while (GrowBuffer (&Status, (VOID **) Buffer, BufferSize)) {
    Status = gBS->LocateHandle (
                  SearchType,
                  Protocol,
                  SearchKey,
                  &BufferSize,
                  *Buffer
                  );
  }

  *NoHandles = BufferSize / sizeof (EFI_HANDLE);
  if (EFI_ERROR (Status)) {
    *NoHandles = 0;
  }

  return Status;
}

INTN
CompareGuidx (
  IN EFI_GUID     *Guid1,
  IN EFI_GUID     *Guid2
  )
/*++

Routine Description:

  Compares to GUIDs

Arguments:

  Guid1 - guid to compare
  Guid2 - guid to compare

Returns:
  =  0  if Guid1 == Guid2
  != 0  if Guid1 != Guid2 

--*/
{
  INT32 *g1;
  INT32 *g2;
  INT32 r;

  //
  // Compare 32 bits at a time
  //
  g1  = (INT32 *) Guid1;
  g2  = (INT32 *) Guid2;

  r   = g1[0] - g2[0];
  r |= g1[1] - g2[1];
  r |= g1[2] - g2[2];
  r |= g1[3] - g2[3];

  return r;
}
// Copy from \Shell\Library\Init.c
EFI_STATUS
LibInitializeShellApplication (
  IN EFI_HANDLE                   ImageHandle,
  IN EFI_SYSTEM_TABLE             *SystemTable
  )
{
  EFI_STATUS  Status;
  EFI_HANDLE  *HandleBuffer;
  UINTN       HandleNum;
  UINTN       HandleIndex;
  EFI_GUID         SESGuid         = EFI_SE_EXT_SIGNATURE_GUID;
  
  //
  // Connect to the shell interface
  //
  Status = gBS->HandleProtocol (ImageHandle, &ShellInterfaceProtocol, (VOID *) &SI);
  if (EFI_ERROR (Status)) {
    Print (L"InitShellApp: Application not started from Shell\n");
    gBS->Exit (ImageHandle, Status, 0, NULL);
  }

  //
  // Connect to the shell environment
  //
  Status = gBS->HandleProtocol (
                ImageHandle,
                &ShellEnvProtocol,
                (VOID *) &SE2
                );
  if (EFI_ERROR (Status) || !(CompareGuid (&SE2->SESGuid, &SESGuid) == 0 &&
    (SE2->MajorVersion > EFI_SHELL_MAJOR_VER ||
      (SE2->MajorVersion == EFI_SHELL_MAJOR_VER && SE2->MinorVersion >= 
EFI_SHELL_MINOR_VER))
    )
  ) {
    Status = LocateHandle (
              ByProtocol,
              &ShellEnvProtocol,
              NULL,
              &HandleNum,
              &HandleBuffer
              );
    if (EFI_ERROR (Status)) {
      Print (L"InitShellApp: 1Shell environment interfaces not found\n");
      gBS->Exit (ImageHandle, Status, 0, NULL);
    }

    Status = EFI_NOT_FOUND;
    for (HandleIndex = 0; HandleIndex < HandleNum; HandleIndex++) {
      gBS->HandleProtocol (
           HandleBuffer[HandleIndex],
           &ShellEnvProtocol,
           (VOID *) &SE2
           );
      if (CompareGuidx (&SE2->SESGuid, &SESGuid) == 0)
	  {
        Status = EFI_SUCCESS;
        break;
      }
    }

    FreePool (HandleBuffer);

    if (EFI_ERROR (Status)) {
      Print (L"InitShellApp: 2Shell environment interfaces not found\n");
      gBS->Exit (ImageHandle, Status, Status, NULL);
    }
  }

  SE = (EFI_SHELL_ENVIRONMENT *) SE2;
  
  //
  // Done with init
  //
  return Status;
}

//
// Entry point function 
//
EFI_STATUS
EFIAPI
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
   INTN	i;
   BOOLEAN ExitMark=FALSE;
   
   gST = SystemTable;
   gBS = SystemTable -> BootServices;
   
   LibInitializeShellApplication (ImageHandle,SystemTable);
   
   while (!ExitMark) 
    {
		//Clear Screen
		gST -> ConOut -> ClearScreen(gST->ConOut);
   	    Print(L"You can break by ESC key\n");
		for (i=0;i<80*(25-1);i++)
		{
			if (SE2 -> GetExecutionBreak() ) {ExitMark=TRUE; break;}
			Print(L".");
		} 
	}
	 
  return EFI_SUCCESS;
}

 

运行结果:

BreakTest

代码:

BreakTest

使用Paypal支付外币多付款的问题

如果你使用的是信用卡,那么一定要注意下面的问题:

1.如果你是在国内的 Paypal 注册的,那么无法支付美元。

2.如果你是在国外注册的 Paypal ,那么一定要联系客服(英文服务),否则无法支付美元。

这里解释一下“无法支付美元”是什么意思。比如:你看中了 100美元的货物,然后购买的话,Paypal会先将这100美元转换为 640人民币(比如当天的汇率为6.2,他们会使用比普通中间值要高的汇率!),然后你的信用卡上面会出现你支付了 640人民币的账目,但是月末出账单的时候上面会显示 你有一笔 104.9美元的支付,(银行会使用比如 6.1 来“帮”你折算成美元,因为你的消费并非美元而是“当地货币”),同时银行还会向你收取 1.5% = 1.5美元左右的手续费。这个手续费银行方面的解释是“一般持卡人都是用人民币-美元的双币种卡,在非美元地区使用,就需要先将当地货币转换为美元,而这时候会被国际卡组织收取至少1.5%的货币转换费。”【参考 1】

上面解释了一圈,归结起来就是一句话:你以为自己支付的是美元,实际上却是人民币,因此Paypal和银行都会过来“帮”你转换货币,代价是你要为此多支付4%左右。

下面就是如何解决这个问题:

对于1的情况,Paypal的客服刚开始告诉我说页面可以进行设置,但是我实在无法找到。后来他们改口说请注册国际的 Paypal。之后,我注册了国际的,但是再次支付又出现了同样的问题。他们客服告诉我说这是因为我的信用卡被自动识别为中国区,为我考虑,自动用人民币给我结算。我还和他们确认过在页面上没有相关选项,必须通知他们客服人工进行修改,当然也没有这条必须人工修改的提示. 【参考 2】

所以,新注册并且绑定信用卡之后,请务必联系客服人员,确定币种问题,问题是他们好像只有英文服务哎~ 国内有中文服务,但是很明显他们无法解决问题,并且态度方面要比国际客服差一些。

参考:

1.http://info.cmbchina.com/Stock/Detail.aspx?channel=Mac&topic=GNHG&id=4722460 境外消费用对信用卡可省1.5%货币转换费

2.客服给我的解释。我是想要他们关于这个问题的详细 policy ,但是那个哥们只是给我解释了一下。

Allow me to explain that when you register a PayPal account and add a local credit card to your PayPal account, the primary currency will follow the local currency. For example:

China registered PayPal account, you add a local credit or debit card; the primary currency for the card is in CNY.
Japan registered PayPal account, you add a local credit or debit card; the primary currency for the card is in JPY.
Singapore registered PayPal account, you add a local credit or debit card; the primary currency for the card is in SGD.

However if you have a local credit or debit card that is in USD or another currency, you need to inform PayPal to change the currency for you as this is considered a special case.

I apologize for the inconvenience caused and I appreciate your understanding regarding this matter.

Should you need further assistance at any point of time, please do remember that you have someone right here who is more than happy to assist and resolve your issue for you personally.

Have a wonderful evening ahead!

Thank you for choosing PayPal. Your satisfaction is important to us. We welcome your feedback and hope you recommend us to your friends and family.

Please make sure you have a valid mobile phone number on your PayPal account so that we can contact you if we need to confirm any unusual account activity and protect you from unauthorized account use. To add or update a mobile phone number is easy: Log in to your PayPal account, click ‘Profile’ near the top of the page, and then click ‘Update’ beside ‘Phone.’

Sincerely,
XXXXX
PayPal, an eBay Company

Copyright© 2014 PayPal Inc. All rights reserved.

Consumer advisory: PayPal Pte Ltd, the Holder of the PayPal™ payment service stored value facility, does not require the approval of the Monetary Authority of Singapore. Consumers (users) are advised to read the terms and conditions carefully.

How to use EADK

If you want to use C library in your program such as: printf, scanf……. You should use EADK library in EDK2.

1.Decompress EADK_A2_Release. There are 3 directories. Put them to your EDK2 source directory. In fact you should make sure your EDK2 can build and run NT32 well.

Capture

2.Run edksetup.bat in your EDK directory.

edksetup

3.You can build EADK_A2_Release by using

build -a IA32 -p AppPkg\AppPkg.dsc

3.You well get the first error message.(AppPkg.dsc error 000E)

error1

4.You should modify AppPkg.dsc as below as we just want to test program under NT32:

[LibraryClasses.IA32]
# TimerLib|PerformancePkg/Library/DxeTscTimerLib/DxeTscTimerLib.inf
## Comment out the above line and un-comment the line below for running under Nt32 emulation.
TimerLib|MdePkg/Library/BaseTimerLibNullTemplate/BaseTimerLibNullTemplate.inf

5.Build again and your will get the second error message (time.h Error C2220)

error2

6.The solution is simple. Use the Notepad to open the time.h and save again.

7.At last all the demos can be build. You can get main.efi hello.efi and Enquire.efi. They can be run in NT32 emulation environment.

They will be placed at “\Build\AppPkg\DEBUG_VS2008\IA32” after compiling. And you can copy them to “\Build\NT32\DEBUG_VS2008\IA32”. After that they can be seen under fsnt0:

result

8.You can use printf in your program now. Ex.

int
EFIAPI
main (
IN int Argc,
IN char **Argv
)
{

puts(“Hello there fellow Programmer.”);
puts(“Welcome to the world of EDK II.”);

printf(“lab-z.com %x\n”,8212);

return 0;
}

result2

<2016> I find it seems to be hard to download an EADK package from internet, and I put one here EadkPkg_A2
reference:
1.http://www.lab-z.com/step-to-uefi-shell-6-shell-%E4%B8%AD%E4%BD%BF%E7%94%A8-float/

2.http://biosren.com/viewthread.php?tid=5651&fromuid=6339]http://biosren.com/viewthread.php?tid=5651&fromuid=6339 .UEFI 实战(1) 配置开发环境

D12 芯片的 0xFD Command在哪?

有人提了这么一个问题:

USB芯片PDIUSBD12读CHIP ID命令在手册中怎么找不到?

刚开始我以为只是没有拿到完整版的手册,后来找了一份比较新的结果里面也没有

PDIUSBD12D-0.9

遇到这样的情况,通常是要去找找 AP Note,找了一下还真找到了

What is the command to read the chip ID of the PDIUSBD12 and what is the chip ID?
The command to read the chip ID of the PDIUSBD12 is FDh and the expected value is 1012h.

PDIUSBD12_FAQ

最后一个没想明白的是,为毛我打开PDF中的网站 http://www.stnwireless.com/ 是下面的内容?莫非这个公司倒闭了,域名也卖掉了?

nxp

万万没想到,串口的故事

周末在家进行实验,打算写个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