通过WriteFile对硬盘发送数据

下面这个程序首先用 CreateFile 打开 PhysicalDiskX 然后使用 CreateFile 向里面写入数据。

#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <conio.h>

int main(int argc, char *argv[])
{
   HANDLE hFile;
   BOOL fSuccess;
   char txchar[512];
   DWORD iBytesWritten;

   hFile = CreateFile(L"\\\\.\\PhysicalDrive1",
                    GENERIC_WRITE,
                    FILE_SHARE_WRITE,    // 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 (hFile == INVALID_HANDLE_VALUE) 
   {
       // Handle the error.
        printf ("CreateFile failed with error %d.\n", GetLastError());
		system("PAUSE");
        return (1);
   }

   txchar[0]='l'; txchar[1]='a';txchar[2]='b';txchar[3]='z';

   //有资料说用这个函数对磁盘写的最小值是 512 bytes 我实验了一下如果小于此会出现error
   fSuccess=WriteFile(hFile, &txchar, 512, &iBytesWritten,NULL);
   printf ("done send %d bytes.Status [%d] \n", iBytesWritten,fSuccess,iBytesWritten);
   printf ("Get lasterror %d\n", GetLastError());

   system("PAUSE");
   CloseHandle(hFile);

   return (0);
}

 

我的实验是在虚拟机中进行的。特别提醒:本次实验可能会损害你的数据,请务必小心。如果系统有安装杀毒软件,有可能被判定为病毒行为。实验的目标盘是一个U盘,盘符为 G:

a

之后,先用 Winhex 打开,我们可以看到开始的几个字符是 abcd

b

运行我们的程序(可以看到发送 512 Bytes Status=1 表示 Success GetLastError=0 表示无错误)
c

我们需要重新打开一次硬盘(WinHex有磁盘修改感知功能,但是在这里不好用)
e

我们可以看到最开始的几个字符被修改了,这说明我们的程序是由效果的的。
d

完整的程序下载:

sendbywritefile

参考:

1.http://msdn.microsoft.com/en-us/library/windows/desktop/aa365747(v=vs.85).aspx MSDN WriteFile function

2.http://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx MSDN System Error 对于确定 GetLastError 返回值很有用

3.http://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx MSDN CreateFile function

小问题:UEFI 下,Print 怎样显示十进制的64位的无符号数呢?

有人【参考1】,提了一个问题:UEFI 下,Print 怎样显示十进制的64位的无符号数呢?

首先查了一下资料【参考2】

printfll

于是,试试 %llu 看看, ll 表示 long long, u表示十进制无符号数,代码

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

  printf("%llu\n",0xffffffffL);  

  return EFI_SUCCESS;
}

 

执行结果

quest1

参考:

1.http://biosren.com/viewthread.php?tid=7419&highlight= UEFI 下,Print 怎样显示十进制的64位的无符号数呢?

2.http://www.cplusplus.com/reference/cstdio/printf/

Arduino 的MD5库

这是来自 http://playground.arduino.cc/Main/LibraryList 的Arduino MD5库【参考1】。我实验了一下挺好用的。

#include "MD5.h"
/*
This is en example of how to use my MD5 library. It provides two
easy-to-use methods, one for generating the MD5 hash, and the second
one to generate the hex encoding of the hash, which is frequently used.
*/
void setup()
{
  //initialize serial
  Serial.begin(9600);
  //give it a second
  delay(1000);
  //generate the MD5 hash for our string
  unsigned char* hash=MD5::make_hash("hello world");
  //generate the digest (hex encoding) of our hash
  char *md5str = MD5::make_digest(hash, 16);
  //print it on our serial monitor
  Serial.println(md5str);

  char* test="hello world";
  unsigned char* hash2=MD5::make_hash(test);
  md5str=MD5::make_digest((unsigned char*)hash2, 16);
  Serial.println(md5str);   

}

void loop()
{
}

 

运行结果

md5result

这个结果和我在一个在线MD5的网站【参考2】计算结果是一致的

md5compare

例子下载
MD5_Hash

我只是简单的测试了一下对Char做MD5,头文件中的另外几个函数的用法没搞清楚。从经验上来看,应该是能够不断累积计算一系列MD5值(比如说刚开始有个字符串”ABC”后来又来了一个字符串“DEF”可以继续加入计算中)。试验了一下没搞清楚。

static const void *body(void *ctxBuf, const void *data, size_t size);
static void MD5Init(void *ctxBuf);
static void MD5Final(unsigned char *result, void *ctxBuf);
static void MD5Update(void *ctxBuf, const void *data, size_t size);

参考:
1.https://github.com/tzikis/ArduinoMD5/ 完整代码下载 ArduinoMD5-master
2.http://md5calculator.chromefans.org/ 一个MD5在线计算网站

Step to UEFI (18) —– CLib 获得 ImageHandle

前面介绍了,用CLib我们可以编写出普通C语言一样的代码。入口是 main (int Argc, char **Argv),但是如何获得当前的 ImageHandle 呢?【参考1】给出了一个答案。查看实际的入口ShellCEntryLib (ShellPkg\Library\UefiShellCEntryLib\UefiShellCEntryLib.c)

EFI_STATUS
EFIAPI
ShellCEntryLib (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  INTN                           ReturnFromMain;
  EFI_SHELL_PARAMETERS_PROTOCOL *EfiShellParametersProtocol;
  EFI_SHELL_INTERFACE           *EfiShellInterface;
  EFI_STATUS                    Status;

 

就是说 Shell Call进来的时候实际上和普通的EFI 程序没有差别,都是给了ImageHandle的,只是后面调用的时候去掉了。调用的顺序是 ShellCEntryLib –> ShellAppMain –> main.c(自己写的),所以如果我们能够加入一个参数,那么可以直接把ImageHandle传进去。
那就手动加入这个参数吧,需要修改的文件和内容如下:
1. ShellPkg\Library\UefiShellCEntryLib\UefiShellCEntryLib.c

if (!EFI_ERROR(Status)) {
    //
    // use shell 2.0 interface
    //
    ReturnFromMain = ShellAppMain (
					   ImageHandle,  //

 

2. ShellPkg\Inlcude\Library\ShellCEntryLib.h

INTN
EFIAPI
ShellAppMain (
  IN EFI_HANDLE        ImageHandle, //

 

3. StdLib\LibC\Main\Main.c

INTN
EFIAPI
ShellAppMain (
IN EFI_HANDLE ImageHandle, //<——-Added
IN UINTN Argc,
IN CHAR16 **Argv
)

4.我们自己的程序 main.c ,和之前文章提到的只有main被修改了

/** @file
    A simple, basic, application showing how the Hello application could be
    built using the "Standard C Libraries" from StdLib.

    Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.<BR>
    This program and the accompanying materials
    are licensed and made available under the terms and conditions of the BSD License
    which accompanies this distribution. The full text of the license may be found at
    http://opensource.org/licenses/bsd-license.

    THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
    WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#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;

//SimpleTextInputEx.harderr
//#define	EFI_SHIFT_STATE_VALID		0x80000000
//#define EFI_LEFT_CONTROL_PRESSED	0x00000002

EFI_STATUS
EFIAPI
NotificationFunction(
	IN EFI_KEY_DATA	*KeyData
)
{
	printf("This is a test from www.lab-z.com \n");		
	return(EFI_SUCCESS);
}

/***
  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 EFI_HANDLE        ImageHandle,  //

 

之后还是使用 build –a IA32 –p AppPkg\AppPkg.dsc 来编译,之前如果里面还有编译器他的Application,那么需要从 AppPkg.dsc中先去掉其余的Application。
编译之,通过;运行之和之前的结果一样,好用。

buildimge

本文提到的程序可以在这里下载
Main

上面的程序看起来有些复杂,还会破坏自己的编译环境,后面我会继续研究找到更好的解决方法。就是这样。

参考:
1. 论坛上有人提出了类似的问题
http://biosren.com/viewthread.php?tid=4651&highlight=ShellCentrylib
一直有這個疑問~
目前寫的Shell APP的進入點,參數都是argc,argv
那要怎麼得到他的ImageHandle呢?

UefiShellCEntryLib.c
EFI_STATUS
EFIAPI
ShellCEntryLib (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)

MyShellApp.c
INTN
EFIAPI
ShellAppMain (
IN UINTN Argc,
IN CHAR16 **Argv
)

实验 DS1307 RTC 模块

之前入手过一个 DS1307 模块,但是一直没有实验,今天拿出来玩玩。

实验之前先研究硬件,上面有一个电池,上面写的是 Rechargable ,拆下电池用万用表测试了一下,没有电。不过后来连接USB之后由拔下来稍微放置了一点时间再插上,发现计时仍在继续。应该是充进去一点电了。

1

I2C 接口的,接线很简单【参考1】:

ds1307con

接下来测试程序,当然为了简单起见还是直接用库。但是不知道为什么连续找了几个库编译都无法完成。最终,找到一个好用的库【参考2】

编写简单的测试程序如下

#include <Wire.h>
#include "DS1307A.h"
DS1307A ds = DS1307A(2000);
DS1307A_RAM ram;
void setup()
{     
    Serial.begin(9600);                //init serial

    //ds.setDate(2014,10,1);
    //ds.setTime(20,17,40);
    //ds.setWeek(MONDAY);

}  
void loop()   
{ 
    Time t = ds.getTime();

    Serial.print( t.year);
    Serial.print(".");     
    Serial.print(t.month);     
    Serial.print(".");     
    Serial.print(t.date);     
    Serial.print("  ");     
    Serial.print(t.hour);     
    Serial.print(":");     
    Serial.print(t.minute);     
    Serial.print(":");     
    Serial.print(t.second);     

    Serial.println(); 

    //Serial.print(ds.getDateString("YMD",'-'));
    //Serial.print(" ");
    //Serial.println(ds.getTimeString("HMS",':'));    

    delay(1000);

}

 

运行结果如下

DS1307

代码和测试程序在此 DS1307

参考:

1.http://www.geek-workshop.com/forum.php?mod=viewthread&tid=207&extra=&highlight=ds1307&page=4 arduino学习笔记27 – DS1307 RTC时钟芯片与DS18B20数字温度传感器实验 (特别提醒,这篇文章中给出的库编译无法通过)

2.http://www.geek-workshop.com/thread-2317-1-1.html 自己封装的arduino1.0.1时钟库,使用DS1307芯片
你可以在这里下载文章中提到的库 DS1307A

====================================================================================================================================================================
2015/04/24 特别注意:这个模块对电压比较敏感,特别如果你是 Pro Micro之类的板子,VCC出来的电压可能是 4.6V ,这时候是无法正常工作的,会出现各种稀奇古怪的问题。

VS2008 等待按键

通常我们在 Console 的程序中需要等待按键之类的,通常使用的都是:conio.h中的getch() 或者 stdio.h中的getchar()【参考1】。美中不足的是,这两个函数都会一直在那里等待按键,不会去做其他的事情。如果我们需要实现类似Pascal语言中 Keypress 函数,“检查一下是否有按键,没有的话我继续做其他事情”的功能,就需要其他函数了。

那就是 conio.h中的kbhit()

#include "stdafx.h"
#include<conio.h>
#include<stdio.h>
#include "windows.h"

using namespace System;

int main(array<System::String ^> ^args)
{
  int i=0;
  while (!kbhit())
  {
	  printf("lab-z.com [%d]\n",i++);
	  Sleep(200);
  }
  system("PAUSE");
  return 0;
}

 

运行结果

keypressed
参考:

1.http://baike.baidu.com/view/7942479.htm 阻塞函数

用 1602实现进度条

在 https://www.electronicsblog.net/arduino-lcd-horizontal-progress-bar-using-custom-characters/ 这里发现比较有趣的代码:用1602LCD 实现一个进度条。根据文章指引,我也试验了一下。

弄明白了原理,程序非常简单:

//https://www.electronicsblog.net/
//Arduino LCD horizontal progress bar using custom characters
#include <Wire.h> 
#include "LiquidCrystal_I2C.h"

#define lenght 16.0

double percent=100.0;
unsigned char b;
unsigned int peace;
int value=100;

// custom charaters

LiquidCrystal_I2C lcd(0x27,16,2);

//定义进度块
byte p1[8] = {
                0x10,
                0x10,
                0x10,
                0x10,
                0x10,
                0x10,
                0x10,
                0x10};

byte p2[8] = {
                0x18,
                0x18,
                0x18,
                0x18,
                0x18,
                0x18,
                0x18,
                0x18};

byte p3[8] = {
                0x1C,
                0x1C,
                0x1C,
                0x1C,
                0x1C,
                0x1C,
                0x1C,
                0x1C};

byte p4[8] = {
                0x1E,
                0x1E,
                0x1E,
                0x1E,
                0x1E,
                0x1E,
                0x1E,
                0x1E};

byte p5[8] = {
                0x1F,
                0x1F,
                0x1F,
                0x1F,
                0x1F,
                0x1F,
                0x1F,
                0x1F};

void setup() {
                lcd.init();                      //初始化LCD
                lcd.backlight();		 //打开背光

			//将自定义的字符块发送给LCD
			//P1 是第一个,P2 是第二个,以此类推
                lcd.createChar(0, p1);			 
                lcd.createChar(1, p2);
                lcd.createChar(2, p3);
                lcd.createChar(3, p4);
                lcd.createChar(4, p5);
}

void loop()
{
                //设置光标在左上角
                lcd.setCursor(0, 0);

                percent = value/1024.0*100.0;

		//当超过100%的时候自动校正为 100%
		if (percent>100) {percent=1;value=0;}

                lcd.print("     ");
                lcd.print(percent);
                lcd.print(" % ");

		//移动光标到第二行
                lcd.setCursor(0,1);

                double a=lenght/100*percent;

                // drawing black rectangles on LCD
		// 显示全黑块。
                if (a>=1) {
                    for (int i=1;i<a;i++) {
                                lcd.write(4);
                                b=i;
                        } //for (int i=1;i<a;i++) 
                    a=a-b;
                }
                else {b=0;}

                peace=a*5;

                // drawing charater's colums
		// 显示除去全黑块之后的零头
                switch (peace) {
                  case 0:
                        break;
                  case 1:
                        lcd.write(0);
                        break;
                  case 2:
                        lcd.write(1);
                        break;
                  case 3:
                        lcd.write(2);
                        break;
                  case 4:
                        lcd.write(3);
                        break;
                  } //switch (peace)

               // clearing line
	       // 用空格填充剩下的位置
              for (int i =0;i<(lenght-b);i++) {
                lcd.print(" ");
                }

		//递增
                value=value+10;				
		delay(300);

  }

 

连接

1602pg

大图

1602pg2

代码下载 pb1602

================================================================================
2015年3月13日 特别注意:自定义字符一次不能超过8个,如果需要自定义很多个,可以用动态的方法进行切换,参考 http://www.geek-workshop.com/thread-5190-1-1.html 1602自定义字符的另一种思路,实现超过8种自定义字符的显示

我的网站打开比较慢有一段时间了,刚开始检查过几次,直接Ping出去速度都还可以。加之大多数情况下我都是写好了再上传,打开页面慢点并不碍事。最近几次在网站上直接写文章,稍微留心了一下,发现缓慢的时候浏览器左下角显示在访问Google的什么。忽然灵光一闪:莫非是因为我的网站调用Google一些东西,而Goolge因为“众所不知”的原因无法访问因此会导致巨慢的问题?Baidu了一下发现真是这么回事。

http://www.gox.name/Article/2775.html 谷歌被墙!解决WordPress访问速度慢的问题
http://www.wind88.net/news/news-125.html WordPress最近速度很慢的解决方法

简单的解释:Wordpress调用了Google的一些服务,在打开的时候浏览器需要和服务器进行交互,而Google 肯定是Timeout因此会有问题。

按照文章中的说法,按转一个插件,绕开了Google的服务,再次页面速度马上快了起来。

这是“城门失火殃及池鱼”吗? 不是的,因为你生活在墙中,那是一个看不见摸不着,但是每时每刻你都可能撞上的“墙”。

隐约记得列宁有过一段关于拆墙的论述,查了一下,还真有,应该是出现在历史课本中的。

“当列宁因参加学生运动而被抓捕时,只有17岁。一个警察局长不解地问他:“我不明白,你为什么要起来造反?年轻人,要知道你面前是一堵墙。你不是在用脑袋往墙上撞吗?”列宁藐视地回答:“是的,一堵墙,不过已经腐朽了,一推就倒。我们可以从上面跨越过去。”文中的“一堵墙”被推倒应该是在

A.二月革命中 B.七月事件中

C.十月革命中 D.国内战争中”

唉,最后再次祝愿病魔早日战胜北邮前校长方滨兴先生吧。

Step to UEFI (17) —– Application中注册一个快捷键

前面介绍过如何在 Shell 下实现暂停和中断运行。这里介绍如何实现在Application中注册一个“热键”,当按下这个键的时候去做另外的事情。

根据资料,需要使用EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL 的 RegisterKeyNotify 【参考1,2】

具体的参考代码在ShellProtocol.c

KeyData.KeyState.KeyToggleState = 0;
KeyData.Key.ScanCode = 0;
KeyData.KeyState.KeyShiftState = EFI_SHIFT_STATE_VALID|EFI_LEFT_CONTROL_PRESSED;
KeyData.Key.UnicodeChar = L’c’;

Status = SimpleEx->RegisterKeyNotify(
SimpleEx,
&KeyData,
NotificationFunction,
&ShellInfoObject.CtrlCNotifyHandle1);

查阅Spec【参考3】,有如下资料

reg2

reg1

简单的说,第一个参数是EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL 的实例,第二个参数是按键信息,第三个参数是当按键发生时会被CallBack的函数,第四个参数是注册的Handle(第四个我也不太理解,琢磨一下应该是用来记录这个注册动作的东西,有了这个东西后面可以用来取消这次的注册)。

写一个简单的程序进行测试,在一个循环中,当按下 ctrl+s 的时候,自动在屏幕上输出字符串:

/** @file
    A simple, basic, application showing how the Hello application could be
    built using the "Standard C Libraries" from StdLib.

    Copyright (c) 2010 - 2011, Intel Corporation. All rights reserved.<BR>
    This program and the accompanying materials
    are licensed and made available under the terms and conditions of the BSD License
    which accompanies this distribution. The full text of the license may be found at
    http://opensource.org/licenses/bsd-license.

    THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
    WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#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;

EFI_STATUS
EFIAPI
NotificationFunction(
	IN EFI_KEY_DATA	*KeyData
)
{
	printf("This is a test from www.lab-z.com \n");		
	return(EFI_SUCCESS);
}

/***
  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;

  @param[in]  Argc    Number of argument tokens pointed to by Argv.
  @param[in]  Argv    Array of Argc pointers to command line tokens.

  @retval  0         The application exited normally.
  @retval  Other     An error occurred.
***/
int
EFIAPI
main (
	IN EFI_HANDLE		ImageHandle,
	IN EFI_SYSTEM_TABLE *SystemTable
  )
{
	EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *SimpleEx;
	EFI_KEY_DATA	KeyData;
	EFI_STATUS		Status;
	EFI_HANDLE		CtrlCNotifyHandle = NULL;
	INTN			i;

 	Status = gBS -> OpenProtocol(
		gST -> ConsoleInHandle,
		&gEfiSimpleTextInputExProtocolGuid,
		(VOID**)&SimpleEx,
		ImageHandle,
		NULL,
		EFI_OPEN_PROTOCOL_GET_PROTOCOL);

	if (EFI_ERROR(Status)) {
		printf("OpenProtocol ERROR!!\n");
	}		

	KeyData.KeyState.KeyToggleState = 0;
	KeyData.Key.ScanCode			= 0;
	KeyData.KeyState.KeyShiftState 	= EFI_SHIFT_STATE_VALID | EFI_LEFT_CONTROL_PRESSED;
	KeyData.Key.UnicodeChar			= L's';

	Status = SimpleEx -> RegisterKeyNotify (
			SimpleEx,
			&KeyData,
			NotificationFunction,
			&CtrlCNotifyHandle);

	for (i=0;i<200;i++) 
	 {
		printf("Test\n");		
		gBS -> Stall (5000);
      }

	Status = SimpleEx -> UnregisterKeyNotify(SimpleEx, CtrlCNotifyHandle);

  return EFI_SUCCESS;
}

 

运行结果(模拟坏境中测试的)

regkey

代码下载

registerkey

参考:
1. http://biosren.com/viewthread.php?tid=6666&highlight=RegisterKeyNotify
如何在UEFI shell做一個類似key board hook的方式?
2. http://biosren.com/viewthread.php?tid=6579&highlight=RegisterKeyNotify
UEFI HotKey事件
3. UEFI Specification 2.4 P440

USB 控制七段数码管

这次的目标是要实现用 USB 口来控制七段数码管。

硬件上面选用《圈圈教你玩USB》配套的实验板。上面的单片机为STC89S52。实验板上P0接口用来和D12通讯,P1接口连接到一组开关按钮上,P2接口上有一组LED,LED另外一端通过10K电阻上拉。下面用色块对这些端口的用途做了简单标记:

usbs1

最开始的设想是数码管(用的还是上次出场过的那一款数码管,共阳极,【参考1】)阳极连接到P1(可以理解为SEG选择 Pin接正极),阴极连接到P2(数码段 Pin接负极)。实验发现数码管根本不亮,检查很多次后尝试去掉六个数码段的连接终于能够点亮一段数字。这才恍然大悟,因为共阳极上还有板载的LED所以会使得电压很奇怪,不足以推动全部。同时显示,所以需要更改方案。

做到这里,我忽然明白极客工坊大神弘毅提到的:“驱动数码管限流电阻肯定是必不可少的,限流电阻有两种接法,一种是在d1-d4阳极接,总共接4颗。这种接法好处是需求电阻比较少,但是会产生每一位上显示不同数字亮度会不一样,1最亮,8最暗。另外一种接法就是在其他8个引脚上接,这种接法亮度显示均匀,但是用电阻较多。” 因为电压相同,数字1只需要点亮两根LED,8需要点亮七根。

修改之后,阳极连接到P2,阴极连接到P1上。实验发现即使4个数字同时显示仍然是可以清晰辨认的。连接如下图所示:

usbsc

继续尝试在上位机程序中自动扫描点亮每个LED,前面一篇Arduino就是使用类似的方法点亮的。实做之后发现这样做会导致每个数码管亮灭之间的间隔太长,这样在人类看起来是逐个亮灭的而不是同时亮。猜测这是程序本身发送和USB传输Delay太长导致的。初步估计每个数字显示间隔至少在200ms以上。如果想做一个类似跑马灯的程序用这样的方案没问题,但是如果想持续稳定的显示数字这个方案不可行。

最后上位机程序直接发送显示的数值,由Firmware本身解析,同时利用单片机上的定时器(15ms一次),不断刷新每个显示位。开始时,Firmware中定时器产生中断后,ISR中将四位数值轮流显示一次,但是实验结果肉眼只能看到最后一个点亮的数值。估计这个可能是因为ISR中每个数码位显示过快,而他们点亮的时间远远小于不在ISR中的时间。

经过无数次的调试后,终于完成了。

resize

拍了一个视频,PC来控制七段数码管

http://www.tudou.com/programs/view/-D_w29Pwavc/?resourceId=414535982_06_02_99

对应的Firmware关键点在于中断服务函数

void Timer0Isr(void) interrupt 1
{ 
//LabzDebug_Start
   static i=0; 
//LabzDebug_End

//定时器0重装,定时间隔为5ms,加15是为了修正重装所花费时间
//这个值可以通过软件仿真来确定,在这里设置断点,调整使两次运行
//时间差刚好为5ms即可。
 TH0=(65536-Fclk/1000/12*5+15)/256;	 
 TL0=(65536-Fclk/1000/12*5+15)%256;   // 

//LabZDebug_Start
	P2=1 << i;
	P1=zBuf[i];
	i++;
	if (i==4) {i=0;}
//LabZDebug_End  

}

 

Firmware下载 HID7Seg

PC端上位机主要代码

							int n=0;	
						 	 while (kbhit()==0)
								{
								WriteReportBuffer[1]=Seven_Dig[n / 1000 % 10];
								WriteReportBuffer[2]=Seven_Dig[n / 100 % 10];
								WriteReportBuffer[3]=Seven_Dig[n / 10 % 10];
								WriteReportBuffer[4]=Seven_Dig[n %10];

								printf("%d \n",n);

								//调用WriteFile函数发送数据
								Result=WriteFile(hUsb,
												WriteReportBuffer,
											    9,
											    &lpNumberOfBytesWritten,
										       	NULL);
								//如果函数返回失败,则可能是真的失败,也可能是IO挂起
								if(Result==FALSE)
										{
											//获取最后错误代码
												LastError=GetLastError();
											//看是否是真的IO挂		
												if((LastError==ERROR_IO_PENDING)||(LastError==ERROR_SUCCESS))
													{ return TRUE;	}
												//否则,是函数调用时发生错误,显示错误代码
												else	
													{
														printf("Sending error:%d \n",LastError);
														//如果最后错误为1,说明该设备不支持该函数。
														if(LastError==1)
														{printf("This device doesn't support WriteFile function \n");}
													}
										}

									n=(n++)%10000;

								}//while (kbhit()==0)

 

完整的代码下载 USB7Seg

参考:

1. http://www.lab-z.com/4digitial/ Arduino 四位数码管实验
2. http://www.geek-workshop.com/forum.php?mod=viewthread&tid=82&highlight=%CA%FD%C2%EB%B9%DC arduino学习笔记13 – 4位数码管实验