Step to UEFI (52) —– EFI_Graphics_Output_Protocol 清屏幕

EFI_Graphics_Output_Protocol 中的 Blt 可以实现在屏幕上绘制图形的功能。

bvf

其中的一个参数 EfiBltVideoFill 可以用来填充整个屏幕的颜色,从而实现清屏的目的。

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

#include  <stdio.h>
#include  <stdlib.h>
#include  <wchar.h>
#include  <time.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>

#include <Protocol/LoadedImage.h>



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

extern EFI_SHELL_ENVIRONMENT2    *mEfiShellEnvironment2;
extern EFI_HANDLE				 gImageHandle;

static EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
static EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput = NULL;

//Copied from  C\MdePkg\Include\Protocol\UgaDraw.h
typedef struct {
  UINT8 Blue;
  UINT8 Green;
  UINT8 Red;
  UINT8 Reserved;
} EFI_UGA_PIXEL;

//
// Drawing to the screen
//
VOID egClearScreen(IN EFI_UGA_PIXEL *FillColor)
{
    
    if (GraphicsOutput != NULL) {
        // EFI_GRAPHICS_OUTPUT_BLT_PIXEL and EFI_UGA_PIXEL have the same
        // layout, and the header from TianoCore actually defines them
        // to be the same type.
       GraphicsOutput->Blt(GraphicsOutput, (EFI_GRAPHICS_OUTPUT_BLT_PIXEL *)FillColor, EfiBltVideoFill,
                           0, 0, 0, 0, GraphicsOutput->Mode->Info->HorizontalResolution, 
						   GraphicsOutput->Mode->Info->VerticalResolution, 0);
    }
}

int
EFIAPI
main (                                         
  IN int Argc,
  IN char **Argv
  )
{
    EFI_STATUS    Status;
    EFI_UGA_PIXEL color;
	UINTN i;
	
    Status = gBS->LocateProtocol(&GraphicsOutputProtocolGuid, NULL, (VOID **) &GraphicsOutput);
    if (EFI_ERROR(Status)) {
        GraphicsOutput = NULL;
		Print(L"Loading Graphics_Output_Protocol error!\n");
		return EFI_SUCCESS;
	}	

	for (i=0;i<255;i++)
	  {
		color.Blue  = i & 0xFF;	
		color.Green = i & 0xFF;
		color.Red   = i & 0xFF;
	
		egClearScreen(&color);
		gBS->Stall(5000);
	  }
  return EFI_SUCCESS;
  
}

 

工作视频:

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

完整代码下载

GFXTest2

参考:

1. 本文参考 https://github.com/chengs 的代码 在此表示感谢!

Arduino 控制USB设备(2)硬件测试篇

这篇文章目标是让你知道你买的USB Host Shield能否正常工作。

我们要运行一段代码来确保板子工作正常。从经验的角度来看,这个非常必要,对于很多卖家来说,板子之间的差别只有进货价格的高低,他们对于质量一无所知。

下面例子中的代码来自 http://www.circuitsathome.com/mcu/arduino-usb-host-part-2-classes

第一个代码是测试SPI通信是否正常

/* MAX3421E USB Host controller SPI test */
/* This sketch tests SPI communication between Arduino and MAX3421E USB host controller */
#include <spi.h>
#include "max3421e.h"
 
void setup();
void loop();
 
byte i;
byte j = 0;
byte gpinpol_copy;
 
MAX3421E Max;
 
void setup()
{
    Serial.begin( 9600 );
    Max.powerOn();
    delay(200);
}
 
void loop()
{
  gpinpol_copy = Max.regRd( rGPINPOL );
  Serial.println("SPI test. Each  '.' indicates 64K transferred. Press any key to stop.");
  while( Serial.available() == 0 ) {
    for( i = 0; i < 255; i++ ) {
      Max.regWr( rGPINPOL, i );
      if( Max.regRd( rGPINPOL ) != i ) {
        Serial.println("SPI transmit/receive mismatch");
      }
    }//for( i = 0; i < 255; i++
      j++;
      if( j == 0 ) {
        Serial.print(".");
      }
  }//while( Serial.available() == 0
  Max.regWr( rGPINPOL, gpinpol_copy );
  Serial.println("\r\nStopped.");
  while( 1 );    //stop here
}

 

运行结果

usbspitest1

下面这个代码测试的是 MAX3421E 寄存器是否正常

/* This sketch dumps MAX3421E registers */
#include <spi.h>
#include "max3421e.h"
 
MAX3421E Max;  //MAX3421E instance
 
/* Regiser names/numbers for MAX3421E register dump */
typedef struct {
  const char* name;
  char number;
}
REGISTER_OUTPUT;
 
REGISTER_OUTPUT max_register[] = {
  { "\r\nRCVFIFO:\t",   rRCVFIFO    },
  { "\r\nSNDFIFO:\t",   rSNDFIFO    },
  { "\r\nSUDFIFO:\t",   rSUDFIFO    },
  { "\r\nRCVBC:\t",     rRCVBC      },
  { "\r\nSNDBC:\t",     rSNDBC      },
  { "\r\nUSBIRQ:\t",    rUSBIRQ     },
  { "\r\nUSBIEN:\t",    rUSBIEN     },
  { "\r\nUSBCTL:\t",    rUSBCTL     },
  { "\r\nCPUCTL:\t",    rCPUCTL     },
  { "\r\nPINCTL:\t",    rPINCTL     },
  { "\r\nREVISION:\t",  rREVISION   },
  { "\r\nIOPINS1:\t",   rIOPINS1    },
  { "\r\nIOPINS2:\t",   rIOPINS2    },
  { "\r\nGPINIRQ:\t",   rGPINIRQ    },
  { "\r\nGPINIEN:\t",   rGPINIEN    },
  { "\r\nGPINPOL:\t",   rGPINPOL    },
  { "\r\nHIRQ:\t",      rHIRQ       },
  { "\r\nHIEN:\t",      rHIEN       },
  { "\r\nMODE:\t",      rMODE       },
  { "\r\nPERADDR:\t",   rPERADDR    },
  { "\r\nHCTL:\t",      rHCTL       },
  { "\r\nHXFR:\t",      rHXFR       },
  { "\r\nHRSL:\t",      rHRSL       }
};
 
 
void setup()
{
  Serial.begin( 9600 );
  Max.powerOn();
}
 
void loop()
{
  unsigned char i;
  unsigned char numregs = sizeof( max_register )/sizeof( REGISTER_OUTPUT);
  for( i = 0; i < numregs; i++ ) {
    Serial.print( max_register[ i ].name);
    print_hex( Max.regRd( max_register[ i ].number ), 8 );
  }
  while(1);
 
}
 
/* prints hex numbers with leading zeroes */
// copyright, Peter H Anderson, Baltimore, MD, Nov, '07
// source: http://www.phanderson.com/arduino/arduino_display.html
void print_hex(int v, int num_places)
{
  int mask=0, n, num_nibbles, digit;
 
  for (n=1; n<=num_places; n++)
  {
    mask = (mask << 1) | 0x0001;
  }
  v = v & mask; // truncate v to specified number of places
 
  num_nibbles = num_places / 4;
  if ((num_places % 4) != 0)
  {
    ++num_nibbles;
  }
 
  do
  {
    digit = ((v >> (num_nibbles-1) * 4)) & 0x0f;
    Serial.print(digit, HEX);
  }
  while(--num_nibbles);
 
}

 

运行结果

usbspitest2

下面这个代码测试的是 USB 当前状态

/* MAX3421E interrupt loop */
#include <spi.h>
#include "max3421e.h"
 
MAX3421E Max;
 
byte rcode;
byte vbus_state;
 
void setup()
{
  Serial.begin( 9600 );
  Serial.println("Start");
  Max.powerOn();
}
 
void loop()
{
  Max.Task();
  print_vbus_state();
}
 
void print_vbus_state( void )
{
  char* vbus_states[] = { "Disconnected", "Illegal", "Full speed", "Low speed" };
  byte tmpbyte;
  static byte last_state = 4;
    tmpbyte = Max.getVbusState();
    if( tmpbyte != last_state ) {
      last_state = tmpbyte;
      Serial.println( vbus_states[ tmpbyte ] );
    }
    return;
}

 

刚开始没有插任何设备,显示为 Disconnected 状态。之后插入一个USB鼠标,识别为Low Speed设备。拔掉之后再插入两个不同的U盘,因为IC本身不支持High Speed,所以都显示为Full Speed设备。

usbspitest3

最后,三个修改后的完整代码可以在这里下载:

usb2

经过上述测试,可以确定你的板子没问题。

If you are using Delphi TComPort VCL ……

If you are using Delphi TComPort VCL for some Arduino programs. Please set FlowControl -> ControlDTR to dtrEnable. Otherwise, you will get nothing from Serial Port.

comport

It costed me one afternoon for this issue. I worked with a Arduino Pro Micro (Leonardo). The Serial Monitor of IDE worked well. Putty worked well and ‘Serial Port Utility’ worked well. Arduino Uno was tried. It works well……. Only my Delphi program couldn’t get anything from the Serial Port. At last, I noticed the example program of TComPort worked well. But if I deleted this VCL and added again, it would fail. At last I found this Properties.

I don’t know why. All the documents said Leonardo didn’t use DTR pin.

If you have any suggestion, please let me know. Thanks a lot.

Step to UEFI (51) —– EFI_Graphics_Output_Protocol获得基本信息

学习了一下如何获得 Shell 下当前的显示信息。通过 GraphicsOutputProtocol 来完成这个功能。这个 Protocol 在【参考1】 中有描述。

Capture

头定义在 \MdePkg\Include\Protocol\GraphicsOutput.h

typedef struct _EFI_GRAPHICS_OUTPUT_PROTOCOL EFI_GRAPHICS_OUTPUT_PROTOCOL;

///
/// Provides a basic abstraction to set video modes and copy pixels to and from 
/// the graphics controller's frame buffer. The linear address of the hardware 
/// frame buffer is also exposed so software can write directly to the video hardware.
///
struct _EFI_GRAPHICS_OUTPUT_PROTOCOL {
  EFI_GRAPHICS_OUTPUT_PROTOCOL_QUERY_MODE  QueryMode;
  EFI_GRAPHICS_OUTPUT_PROTOCOL_SET_MODE    SetMode;
  EFI_GRAPHICS_OUTPUT_PROTOCOL_BLT         Blt;
  ///
  /// Pointer to EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE data.
  ///
  EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE        *Mode;
};

 

从名称上来看,这个 Protocol 能够实现的功能是:查询/设置当前显示模式,将屏幕内容和内存互copy等。

这次实验的是查询功能。

查询的结果输出是 EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE 结构体

typedef struct {
  ///
  /// The number of modes supported by QueryMode() and SetMode().
  ///
  UINT32                                 MaxMode;
  ///
  /// Current Mode of the graphics device. Valid mode numbers are 0 to MaxMode -1.
  ///
  UINT32                                 Mode;
  ///
  /// Pointer to read-only EFI_GRAPHICS_OUTPUT_MODE_INFORMATION data.
  ///
  EFI_GRAPHICS_OUTPUT_MODE_INFORMATION   *Info;
  ///
  /// Size of Info structure in bytes.
  ///
  UINTN                                  SizeOfInfo;
  ///
  /// Base address of graphics linear frame buffer.
  /// Offset zero in FrameBufferBase represents the upper left pixel of the display.
  ///
  EFI_PHYSICAL_ADDRESS                   FrameBufferBase;
  ///
  /// Amount of frame buffer needed to support the active mode as defined by 
  /// PixelsPerScanLine xVerticalResolution x PixelElementSize.
  ///
  UINTN                                  FrameBufferSize;
} EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE;

 

可以看到能够获得当前显示模式,屏幕分辨率和格式信息等。根据上面的信息,编写程序如下

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

#include  <stdio.h>
#include  <stdlib.h>
#include  <wchar.h>
#include  <time.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>

#include <Protocol/LoadedImage.h>



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

extern EFI_SHELL_ENVIRONMENT2    *mEfiShellEnvironment2;
extern EFI_HANDLE				 gImageHandle;

static EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
static EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput = NULL;

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

    Status = gBS->LocateProtocol(&GraphicsOutputProtocolGuid, NULL, (VOID **) &GraphicsOutput);
    if (EFI_ERROR(Status)) {
        GraphicsOutput = NULL;
		Print(L"Loading Graphics_Output_Protocol error!\n");
		return EFI_SUCCESS;
	}	

	Print(L"Max mode     =[%d] \n",GraphicsOutput->Mode->MaxMode);
	Print(L"Current mode =[%d] \n",GraphicsOutput->Mode->Mode);
	Print(L"Version      =[%d] \n",GraphicsOutput->Mode->Info->Version);
	Print(L"Screen Width =[%d] \n",GraphicsOutput->Mode->Info->HorizontalResolution);
	Print(L"Screen height=[%d] \n",GraphicsOutput->Mode->Info->VerticalResolution);
	Print(L"Format       =[%d] \n",GraphicsOutput->Mode->Info->PixelFormat);
	Print(L"Num of pixel =[%d] \n",GraphicsOutput->Mode->Info->PixelsPerScanLine);
		
  return EFI_SUCCESS;
  
}

 

运行结果(还是NT32模拟器中)

gopinfo

完整代码下载:

GFXTest

参考:

1.UEFI Spec 2.4 P488 11.9 Graphics Output Protocol

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

2025年3月17日 额外同一个功能类似的代码

#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>
#include <Protocol/GraphicsOutput.h>
#include <Library/DebugLib.h>

INTN
EFIAPI
ShellAppMain (
  IN UINTN Argc,
  IN CHAR16 **Argv
)
{
  EFI_STATUS Status;
  EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput;
  UINTN HandleCount;
  EFI_HANDLE *HandleBuffer;
  UINTN Index;
  UINT32 ModeIndex;
  EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *Info;
  UINTN SizeOfInfo;

  // Locate the handles that support the Graphics Output Protocol
  Status = gBS->LocateHandleBuffer(ByProtocol, &gEfiGraphicsOutputProtocolGuid, NULL, &HandleCount, &HandleBuffer);
  if (EFI_ERROR(Status)) {
    Print(L"Failed to locate handles for Graphics Output Protocol\n");
    return Status;
  }

  // Iterate over all handles
  for (Index = 0; Index < HandleCount; Index++) {
    // Get the Graphics Output Protocol instance
    Status = gBS->HandleProtocol(HandleBuffer[Index], &gEfiGraphicsOutputProtocolGuid, (VOID**)&GraphicsOutput);
    if (EFI_ERROR(Status)) {
      Print(L"Failed to handle protocol for handle %d\n", Index);
      continue;
    }
	Print(L"Controler [%d]\n", Index);
    // Iterate over each mode
    for (ModeIndex = 0; ModeIndex < GraphicsOutput->Mode->MaxMode; ModeIndex++) {
      // Get mode information
      Status = GraphicsOutput->QueryMode(GraphicsOutput, ModeIndex, &SizeOfInfo, &Info);
      if (EFI_ERROR(Status)) {
        Print(L"Failed to query mode %d\n", ModeIndex);
        continue;
      }

      // Output the resolution and pixel format
      Print(L"Mode %d: Resolution: %ux%u, Pixels Per Scanline: %u\n",
            ModeIndex,
            Info->HorizontalResolution,
            Info->VerticalResolution,
            Info->PixelsPerScanLine);

      // Free the mode information
      gBS->FreePool(Info);
    }
  }

  // Free the handle buffer
  gBS->FreePool(HandleBuffer);

  return EFI_SUCCESS;
}
## @file
#  A simple, basic, EDK II native, "hello" application.
#
#   Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.&lt;BR>
#   SPDX-License-Identifier: BSD-2-Clause-Patent
#
##

[Defines]
  INF_VERSION                    = 0x00010006
  BASE_NAME                      = gm
  FILE_GUID                      = a912f198-2025-0317-b908-b757b806ec83
  MODULE_TYPE                    = UEFI_APPLICATION
  VERSION_STRING                 = 0.1
  ENTRY_POINT                    = ShellCEntryLib

#
#  VALID_ARCHITECTURES           = IA32 X64
#

[Sources]
  GetModeInfo.c

[Packages]
  MdePkg/MdePkg.dec
  ShellPkg/ShellPkg.dec

[LibraryClasses]
  UefiLib
  ShellCEntryLib

Arduino String的问题

比如我想从串口得到一个输入,然后再输出到串口上,然后写下面的程序

String comdata="";
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);

}

void loop() {
// put your main code here, to run repeatedly:
while (Serial.available() &gt; 0)
{
comdata += Serial.read();
delay(2);
}
if (comdata.length()&gt;1) {Serial.println(comdata); comdata="";}

}

 

但是惊奇的发现,结果是下面这样。”1″ 对应为 “49”两个字符;“2”对应为 “50”两个字符………

dd

如果想得到想要的结果,需要做一个强制转换

String comdata="";
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);

}

void loop() {
// put your main code here, to run repeatedly:
while (Serial.available() &gt; 0)
{

comdata +=(char)Serial.read();
delay(2);
}
if (comdata.length()&gt;1) {Serial.println(comdata); comdata="";}

}

 

这样的结果就符合预期

ee

感谢极客工坊Super169这位朋友,他给出的解释:

String class 的特性, 當 operator “+” 之後是 numeric type, 會自動把數值先轉成 字符再加上去.
“1” 的數值就是 49, 由於 Serial.read() 的結果是 int type, 當你用 comdata += Serial.read() 時, comdata 是 String, Serial.read() 是 int, 就會執行 String “+” int 的 operation. 當你輸入第一個字 “1” 時, 就會先把 “1” (int value 為 ASCII 值, 即 49) 轉成 “49”, 然後再連接上去. 之後都是一樣了.

當你加上 (char) 的轉換後, comdata += (char)Serial.read(), 就變成是 String “+” char 的 operation, 這時就不需要任何轉換而直接連上去了.

这个问题的原文可以在 http://www.geek-workshop.com/thread-14981-1-1.html 看到

蓝牙控制小灯泡亮度的实验

这里实现用 Windows x86 平板电脑控制小灯泡亮度。

硬件方面在我们最初实验设备【参考1】的基础上增加一个蓝牙模块(这里建议使用蓝牙的朋友选用 HC06系列的,和HC05的不同,这个系列只有Slave的功能,但是个人感觉HC06更容易搜索连接上,我用HC05的时候每次都需要重新搜索配对设备,但是HC06上不用),用来和Windows平板进行通讯。

image001

代码方面,Arduino使用的程序非常简单,将串口收到的char当作PWM值直接输出。程序使用了2个串口,一个是通常的USB,同PC进行通讯,主要是为了方便Debug;真正工作的是另外一个进行蓝牙通讯的串口。

int  n=255;
void setup()
{
    Serial.begin(9600);
    Serial1.begin(9600);    
    pinMode(6,OUTPUT);      //该端口需要选择有#号标识的数字口
}

void loop()
{
  char  c;
    while (Serial.available() > 0)  
    {
        c=Serial.read();
        analogWrite(6,c);
        Serial.println(c);
    }
    while (Serial1.available() > 0)  
    {
        c=Serial1.read();
        analogWrite(6,c);
        Serial.println(c);
    }    
}

 

上位机使用的是Delphi 2010,使用控件很简单即可完成编程。

unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, iComponent, iVCLComponent, iCustomComponent, iPositionComponent,
  iScaleComponent, iKnob, iSevenSegmentDisplay, iSevenSegmentBinary,
  iSevenSegmentInteger, StdCtrls, CPortCtl, CPort, Buttons;

type
  TForm2 = class(TForm)
    iKnob1: TiKnob;
    iSevenSegmentInteger1: TiSevenSegmentInteger;
    ComPort1: TComPort;
    Button1: TButton;
    Button2: TButton;
    procedure iKnob1PositionChange(Sender: TObject);
    procedure FormActivate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

procedure TForm2.Button1Click(Sender: TObject);
begin
  ComPort1.ShowSetupDialog;
end;

procedure TForm2.Button2Click(Sender: TObject);
begin
  if ComPort1.Connected then
    begin
      ComPort1.Close;
      Button2.Caption:='Connect';
    end
  else
    begin
      ComPort1.Open;
      ComPort1.WriteStr(chr(0));
      Button2.Caption:='Disconnect';
    end
end;

procedure TForm2.FormActivate(Sender: TObject);
begin
  iKnob1.Width:=Form2.Width;
end;

procedure TForm2.iKnob1PositionChange(Sender: TObject);
var
  c:byte;
begin
  c:=trunc(iKnob1.Position);
  iSevenSegmentInteger1.Value:=c;
  if Comport1.Connected then
    begin
      ComPort1.Write(&c,1);
    end;
end;

end.

 

界面
image002

上位机完整代码下载

knobtest

工作的视频:
http://www.tudou.com/programs/view/yE4EHhUFcvU/?resourceId=414535982_06_02_99

最后说点其他的:除了Apple和各式各样的 Anrdoid平板电脑,x86的Windows平板也在崛起。

相比之下,使用Windows平板编程有如下优点:
1. 编程简单。工具方面Delphi VB VC 都是非常成熟的工具,能在普通PC上运行的程序,即可顺利移植到Windows平板上(甚至可以说‘移植’这个词不合适,因为不用任何改动直接放上去即可);
2. 发布简单。从时效性上来说,不需要发布到什么市场,也不需要什么审核,各种方法让对方拿到即可运行;
3. 周边设备多多,比如:各种摇杆方向盘,价格也比Apple专用的低很多;
4. 程序运行非常稳定,除非程序有错误,否则根本不会出现那种莫名其妙的“闪退”;

此外,从我的实践的角度来说,Windows 平板目前还有如下的缺点:
1. Windows本质上是给有鼠标的机器运行的,而不是触摸类的设备。这两者在精度上差别很大,传统的Window的各种控件,默认的调用者也都是鼠标,如果直接用触摸操作起来很困难,也容易误操作。因此,我用普通台式机做平板程序的感受是:你一定要把你的用户当成视力有困难的人,能调大的菜单或者按钮一定要做到最大…….
2. 目前比较缺少Windows x86平板方面的中文资料,在使用板载的各种传感器时,缺少资料

参考:
1. http://www.lab-z.com/mos%E6%8E%A7%E5%88%B6%E5%B0%8F%E7%81%AF%E6%B3%A1%E7%9A%84%E5%AE%9E%E9%AA%8C/ MOS控制小灯泡的实验

Step to UEFI (50) —– 实现一个简单的菜单功能

屏幕显示功能搭配取按键信息的功能可以实现一个简单的菜单,用户可以使用上下键移动后选项,回车确认,ESC退出。

代码本身不复杂

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

#include  <stdio.h>
#include  <stdlib.h>
#include  <wchar.h>
#include  <time.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_BOOT_SERVICES         *gBS;
extern EFI_SYSTEM_TABLE			 *gST;
extern EFI_RUNTIME_SERVICES 	 *gRT;

extern EFI_SHELL_ENVIRONMENT2    *mEfiShellEnvironment2;
extern EFI_HANDLE				 gImageHandle;

//
// EFI Scan codes 
// copied from \EdkCompatibilityPkg\Foundation\Efi\Protocol\SimpleTextIn\SimpleTextIn.h
//
#define SCAN_NULL       0x0000
#define SCAN_UP         0x0001
#define SCAN_DOWN       0x0002
#define SCAN_ESC        0x0017
#define CHAR_CARRIAGE_RETURN  0x000D

#define	POSX 7
#define	POSY 3
#define NUM  5
CHAR16 HELPSTR[40]=L"UP/DOWN, ENTER , ESC";
CHAR16 ITEM[NUM][20]= {
				L"Item1 MOVSXX",
				L"Item2 CPUID",
				L"Item3 RETF",
				L"Item4 PUSHF",
				L"Item5 SUB"
				};

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

  EFI_INPUT_KEY	Key;
  EFI_STATUS		Status;
  int current=0;
  int i;
  
  gST -> ConOut ->ClearScreen(gST->ConOut);
 
  ShellPrintEx(POSX-3,POSY-1,L"%N%S",HELPSTR);
  ShellPrintEx(POSX,POSY,L"%H%S",ITEM[0]);
  for (i=1;i<NUM;i++) 
	{
    ShellPrintEx(POSX,POSY+i,L"%N%S",ITEM[i]);
	}
	
  Key.ScanCode=SCAN_NULL;
  while (SCAN_ESC!=Key.ScanCode)
    {
		Status= gST -> ConIn -> ReadKeyStroke(gST->ConIn,&Key);	
		if (Status == EFI_SUCCESS)	{
			ShellPrintEx(POSX,POSY+current,L"%N%S",ITEM[current]);
			if (SCAN_UP == Key.ScanCode) {current = (current-1+NUM)%NUM;}
			if (SCAN_DOWN == Key.ScanCode) {current = (current+1)%NUM;}
			ShellPrintEx(POSX,POSY+current,L"%H%S",ITEM[current]);			
			ShellPrintEx(POSX,POSY+NUM,L"Current[%d] Scancode[%d] UnicodeChar[%x] \n\r",current,Key.ScanCode,Key.UnicodeChar);		
		}
		if (CHAR_CARRIAGE_RETURN == Key.UnicodeChar) {
			ShellPrintEx(POSX,POSY+NUM+1,L"You have chosen: %N%S",ITEM[current]);
			break;
		}
	};	

  return EFI_SUCCESS;
}

 

运行结果:

smenu

工作视频:

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

完整的代码下载:

Menu

达文西的灯

前几天,偶然知道周星驰《国产凌凌漆》英文名称是“From Beijing with Love”。然后又去重温了一遍。

image001

零零七系列一定要有各种无厘头的道具,比如:要你命3000

image003

还有就是经典的太阳能手电。于是,仿造一个用打火机才能“点亮”的小灯。用到之前的MOS模块,小灯泡,还有就是火焰传感器。

image006

这种传感器的原理是:检测火焰或者波长在760纳米~1100纳米范围内的光源。

代码非常简单,火焰传感器输出接在Arduino的Pin9上。

#define firePin  9
#define lampPin  6

void setup()
{
    Serial.begin(9600);
    pinMode(lampPin,OUTPUT);      
    pinMode(firePin, INPUT);
}

void loop()
{
  if (0==digitalRead(firePin)) {  
    for (int i=1;i<128;i++)
      {
          analogWrite(lampPin,i); 
          delay(100);
      }  
    delay(5000);  
    for (int i=128;i>0;i--)
      {
          analogWrite(lampPin,i); 
          delay(100);
      }      
  }  
}

 

实现的效果是:当没有火焰的时候绝对不亮,如果用一个打火机晃一下,它就会亮起来。

工作视频:

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

艺术源于生活,因为时代的发展,未必高于生活【参考2】。

参考:
1. http://www.lab-z.com/?p=3201 MOS控制小灯泡的实验
2. http://baike.baidu.com/link?url=Ldym-lA1eNVcdMMGXyE-ARW5R2ECOA02w-XbslPob1yitMhbngKTwggrS8HbzXDTPQ8UXz7-TIC7ekCaSKsUwK#1

Step to UEFI (49) —– 内存驻留程序

之前,我们通过修改Shell的代码,能够实现在右上角不断显示当前时间的功能。但是这个方法过于复杂。于是我请教了一下 HZZZ ,看看他是否有什么好办法。

经过他的研究,还是使用定时器的方式不断触发,困难点在于如果退出时没有对应的 Handle,再次触发会导致问题。解决这个问题的方法是:让程序在内存中重新加载自己,跳进去执行之后,最开始的部分就可以丢弃掉了。

当然,如果你想读懂这个程序,要先读懂前面几篇 《Step to UEFI>> 系列的文章。

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

#include  <stdio.h>
#include  <stdlib.h>
#include  <wchar.h>
#include  <time.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>

#include <Protocol/LoadedImage.h>

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

extern EFI_SHELL_ENVIRONMENT2    *mEfiShellEnvironment2;
extern EFI_HANDLE		  gImageHandle;

STATIC CONST UINTN SecondsToNanoSeconds = 500000;

/**
  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_TIME   ET;
	UINTN		x;
	UINTN		y;
  

  //Get cursor postion
  x = gST->ConOut->Mode->CursorColumn;
  y = gST->ConOut->Mode->CursorRow;
  //Move cursor to Up-Left 
  gST -> ConOut -> SetCursorPosition(gST -> ConOut,70,0);  
  //Output current time
  gRT->GetTime(&ET, NULL);  
  Print(L"%2d:%2d:%2d",ET.Hour,ET.Minute,ET.Second);
  //Move cursor back
  gST -> ConOut -> SetCursorPosition(gST -> ConOut,x,y);
  
  return ;
}

typedef struct {
  UINTN                       Signature;
  /// Image handle
  EFI_HANDLE                  Handle;   
  /// Image type
  UINTN                       Type;           
  /// If entrypoint has been called
  BOOLEAN                     Started;        
  /// The image's entry point
  EFI_IMAGE_ENTRY_POINT       EntryPoint;     
  /// loaded image protocol
  EFI_LOADED_IMAGE_PROTOCOL   Info; 
} LOADED_IMAGE_PRIVATE_DATA_TEMP;

#define _CR(Record, TYPE, Field)  ((TYPE *) ((CHAR8 *) (Record) - (CHAR8 *) &(((TYPE *) 0)->Field)))

#define LOADED_IMAGE_PRIVATE_DATA_FROM_THIS(a) \
          _CR(a, LOADED_IMAGE_PRIVATE_DATA_TEMP, Info)



typedef void (*Fun)();

void function()
{
    EFI_STATUS                Status;
    EFI_HANDLE                TimerOne = NULL;
	
    printf("function called\n");
	
    Status  = gBS->CreateEvent (
                    EVT_NOTIFY_SIGNAL | EVT_TIMER,
                    TPL_CALLBACK,
                    Timeout,
                    NULL,
                    &TimerOne
                    );

    if (EFI_ERROR (Status)) {
        Print(L"Create Event Error! \r\n");
		return ;
    }

    Status = gBS->SetTimer (
                   TimerOne,
                   TimerPeriodic,
                   MultU64x32 (SecondsToNanoSeconds, 1)
                   );

    if (EFI_ERROR (Status)) {
        Print(L"Set Timer Error! \r\n");
		return ;
    }
}


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


  EFI_STATUS                         Status = EFI_SUCCESS;
  EFI_LOADED_IMAGE_PROTOCOL          *ImageInfo = NULL;
  EFI_HANDLE                         Handle = 0;
  EFI_GUID                           gEfiLoadedImageProtocolGuid = 
                                     { 0x5B1B31A1, 0x9562, 0x11D2, { 0x8E, 0x3F, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B }};
  LOADED_IMAGE_PRIVATE_DATA_TEMP      *private = NULL;
  Fun                                fun;
  UINTN                              FunOffset;
  UINTN                              FunAddr;

  Status = gBS->HandleProtocol (gImageHandle, &gEfiLoadedImageProtocolGuid, &ImageInfo);
  // function offset in the old image
  FunOffset = (UINTN)function - (UINTN)ImageInfo->ImageBase;

  // load the image in memory again
  Status = gBS->LoadImage(FALSE, gImageHandle, NULL, ImageInfo->ImageBase, (UINTN)ImageInfo->ImageSize, &Handle);  

  // get the newer imageinfo
  Status = gBS->HandleProtocol (Handle, &gEfiLoadedImageProtocolGuid, &ImageInfo);

  private = LOADED_IMAGE_PRIVATE_DATA_FROM_THIS(ImageInfo);
  FunAddr = (UINTN)FunOffset + (UINTN)ImageInfo->ImageBase;
  
  fun = (Fun)((UINTN)FunOffset + (UINTN)ImageInfo->ImageBase);
  // called the newer function in new image,the new image will be always in memory because it will not be free
  fun();
  return EFI_SUCCESS;
}

 

工作的视频,看得出来这样的方法适应性更好

完整代码下载

ShowTime2

Arduino 控制USB设备(1)硬件基础篇

相较而言,如何将Arduino模拟为USB设备是一个比较简单的事情,可以用Arduino Uno加入电阻伪装成USB Keyboard/Mouse 【参考1】; 或者直接使用芯片是ATmega32U4的板子,比如Leonardo ,Pro Micro等。但是如果你想直接让Arduno能够和USB设备进行交互就需要动一番脑筋了。

经过搜索发现一块 USB Host Shield能够完成上面的目标。

特别注意,国内能够购买到的USB Host Shield有下面两种

image001

image002

务必购买下面这种(蓝色板子的)。他们之间芯片都是相同的,用的都是Maxim的MAX3421E芯片,和Arduino用SPI接口通信。差别在于蓝色的那种是有官方网站支持的,用起来会好很多,同时上面多了74AHC125 和 74HCT125【参考2】而红色的是第三方开发出来的。

非常不幸的是,我是在Taobao上佳明电子科技这家购买到红色那块。拿到手开始玩之后惊奇的发现有这样的差别。当然,这也和我在发动之前没有做好功课有很大关系。联系卖家也得不到帮助,没有库,没有电路图,让人郁闷和恼火,冷静下来思考:国内Arduino研发能力很弱,通常都是仿造少有原创,那么买到手的应该也是仿制国外的……功夫不负有心人,找到了他的网站
https://www.sparkfun.com/products/9947
image003

顺藤摸瓜,这才搞清楚它和上面蓝色板子差别不大,对付一下还是能用的。前车之鉴,希望后来人在购买硬件上多留心。

然后 Arduino USB Host Shield 的库支持网站是

http://www.circuitsathome.com/arduino_usb_host_shield_projects

这一系列文章都是以这个网站作为参考,有兴趣的朋友可以直接上去翻看各种资料会有很多收获。下面就可以开始 Arduino 操作USB设备之旅。

提醒:插在Uno上的时候是下面这个样子,Shield上的USB方向和Arduino的USB方向刚好相反,错开了(千万别搞反了)
image004

参考:

1. http://www.lab-z.com/20140101/ 用 Arduino 打造一个自动锁屏装置

2. http://www.circuitsathome.com/mcu/arduino-usb-host-shield-build-log-part-4

上面提到这两个IC 不过具体作用我也没搞明白,看起来对我们使用最常见的Arduino Uno没有影响

image005

======================2021年11月14日 ======================

收集到的2个USB Host Shield 的电路,特别需要注意的地方是:Arduino Uno 引脚是 5V, 而主芯片工作在 3.3V 上,所以必须有用于电平转换的设计。