SD卡的实验

实验了一下之前买的一个 Micro SD Card的模块【参考1】

sd1

SD卡是工作在 3.3v电压下面的,这个板子最主要的作用是实现5v转3.3v。我使用的是 Arduino Micro Pro.线路接法如下:

 

SD Card Arduino Pro Micro
GND GND
VCC VCC(5V)
MISO Pin14
MOSI Pin16
SCK Pin15
CS Pin10

 

测试代码在 文件 -> 示例 –> SD –>CardInfo

/*
  SD card test

 This example shows how use the utility libraries on which the'
 SD library is based in order to get info about your SD card.
 Very useful for testing a card when you're not sure whether its working or not.

 The circuit:
  * SD card attached to SPI bus as follows:
 ** MOSI - pin 11 on Arduino Uno/Duemilanove/Diecimila
 ** MISO - pin 12 on Arduino Uno/Duemilanove/Diecimila
 ** CLK - pin 13 on Arduino Uno/Duemilanove/Diecimila
 ** CS - depends on your SD card shield or module.
 		Pin 4 used here for consistency with other Arduino examples


 created  28 Mar 2011
 by Limor Fried
 modified 9 Apr 2012
 by Tom Igoe
 */
// include the SD library:
#include <SPI.h>
#include <SD.h>

// set up variables using the SD utility library functions:
Sd2Card card;
SdVolume volume;
SdFile root;

// change this to match your SD shield or module;
// Arduino Ethernet shield: pin 4
// Adafruit SD shields and modules: pin 10
// Sparkfun SD shield: pin 8
const int chipSelect = 10;

void setup()
{
  // Open serial communications and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }


  Serial.print("\nInitializing SD card...");
  // On the Ethernet Shield, CS is pin 4. It's set as an output by default.
  // Note that even if it's not used as the CS pin, the hardware SS pin
  // (10 on most Arduino boards, 53 on the Mega) must be left as an output
  // or the SD library functions will not work.
  pinMode(10, OUTPUT);     // change this to 53 on a mega


  // we'll use the initialization code from the utility libraries
  // since we're just testing if the card is working!
  if (!card.init(SPI_HALF_SPEED, chipSelect)) {
    Serial.println("initialization failed. Things to check:");
    Serial.println("* is a card is inserted?");
    Serial.println("* Is your wiring correct?");
    Serial.println("* did you change the chipSelect pin to match your shield or module?");
    return;
  } else {
    Serial.println("Wiring is correct and a card is present.");
  }

  // print the type of card
  Serial.print("\nCard type: ");
  switch (card.type()) {
    case SD_CARD_TYPE_SD1:
      Serial.println("SD1");
      break;
    case SD_CARD_TYPE_SD2:
      Serial.println("SD2");
      break;
    case SD_CARD_TYPE_SDHC:
      Serial.println("SDHC");
      break;
    default:
      Serial.println("Unknown");
  }

  // Now we will try to open the 'volume'/'partition' - it should be FAT16 or FAT32
  if (!volume.init(card)) {
    Serial.println("Could not find FAT16/FAT32 partition.\nMake sure you've formatted the card");
    return;
  }


  // print the type and size of the first FAT-type volume
  uint32_t volumesize;
  Serial.print("\nVolume type is FAT");
  Serial.println(volume.fatType(), DEC);
  Serial.println();

  volumesize = volume.blocksPerCluster();    // clusters are collections of blocks
  volumesize *= volume.clusterCount();       // we'll have a lot of clusters
  volumesize *= 512;                            // SD card blocks are always 512 bytes
  Serial.print("Volume size (bytes): ");
  Serial.println(volumesize);
  Serial.print("Volume size (Kbytes): ");
  volumesize /= 1024;
  Serial.println(volumesize);
  Serial.print("Volume size (Mbytes): ");
  volumesize /= 1024;
  Serial.println(volumesize);


  Serial.println("\nFiles found on the card (name, date and size in bytes): ");
  root.openRoot(volume);

  // list all files in the card with date and size
  root.ls(LS_R | LS_DATE | LS_SIZE);
}


void loop(void) {

}

 

运行结果

sd2

参考:
1. 淘宝店铺 赛宝电子
ArduinoSB配件 Micro SD卡模块 SPI接口 迷你TF卡读写(H5A2)

Step to UEFI (76) —–Dump ACPI DSDT

前面介绍了如何在 Shell下 Dump 系统中各种 ConfigurationTable的方法,这里介绍一下如何取得 ACPI 的 DSDT Table。
简单说一下原理:

1. 在ConfigurationTable中查找 GUID 为 ACPI_TABLE_GUID 和 EFI_ACPI_TABLE_GUID 的两个 Table. 检查获得 Table 的 Revision,只有 >=2 的才是我们需要的
2. 通过Table找到 XsdtAddress 这样我们能找到 XSDT Table
3. 在 XSDT 给出的Table中找到指向 FADT Table 的
4. 在 FADT 中直接给出了 DSDT 的地址
5. 最后将整个DSDT Table dump出来保存为文件即可

用图表简单描述上述过程

image001

代码看起来很复杂,主要是各种指针比较多。

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

#include <Protocol/SimpleFileSystem.h>

#define ACPI_TABLE_GUID \
  { \
    0xeb9d2d30, 0x2d88, 0x11d3, {0x9a, 0x16, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d } \
  }

#define EFI_ACPI_TABLE_GUID \
  { \
    0x8868e871, 0xe4f1, 0x11d3, {0xbc, 0x22, 0x0, 0x80, 0xc7, 0x3c, 0x88, 0x81 } \
  }

#define zDebug 1

///
/// RSD_PTR Revision (as defined in ACPI 5.0 spec.)
///
#define EFI_ACPI_5_0_ROOT_SYSTEM_DESCRIPTION_POINTER_REVISION 0x02  
			///< ACPISpec (Revision 5.0) says current value is 2
  
extern EFI_SYSTEM_TABLE			 *gST;
extern EFI_BOOT_SERVICES         *gBS;

#pragma pack(1)
// \MdePkg\Include\IndustryStandard\Acpi50.h
//
// ACPI 5.0 table structures
//

///
/// Root System Description Pointer Structure
///
typedef struct {
  UINT64  Signature;
  UINT8   Checksum;
  UINT8   OemId[6];
  UINT8   Revision;
  UINT32  RsdtAddress;
  UINT32  Length;
  UINT64  XsdtAddress;
  UINT8   ExtendedChecksum;
  UINT8   Reserved[3];
} EFI_ACPI_5_0_ROOT_SYSTEM_DESCRIPTION_POINTER;

// \MdePkg\Include\IndustryStandard\Acpi10.h
///
/// The common ACPI description table header. 
/// This structure prefaces most ACPI tables.
///
typedef struct {
  UINT32  Signature;
  UINT32  Length;
  UINT8   Revision;
  UINT8   Checksum;
  UINT8   OemId[6];
  UINT64  OemTableId;
  UINT32  OemRevision;
  UINT32  CreatorId;
  UINT32  CreatorRevision;
} EFI_ACPI_DESCRIPTION_HEADER;

// \MdePkg\Include\IndustryStandard\Acpi50.h
///
/// ACPI 5.0 Generic Address Space definition
///
typedef struct {
  UINT8   AddressSpaceId;
  UINT8   RegisterBitWidth;
  UINT8   RegisterBitOffset;
  UINT8   AccessSize;
  UINT64  Address;
} EFI_ACPI_5_0_GENERIC_ADDRESS_STRUCTURE;

// \MdePkg\Include\Protocol\AcpiSystemDescriptionTable.h  
///
/// Fixed ACPI Description Table Structure (FADT)
///
typedef struct {
  EFI_ACPI_DESCRIPTION_HEADER             Header;
  UINT32                                  FirmwareCtrl;
  UINT32                                  Dsdt;
  UINT8                                   Reserved0;
  UINT8                                   PreferredPmProfile;
  UINT16                                  SciInt;
  UINT32                                  SmiCmd;
  UINT8                                   AcpiEnable;
  UINT8                                   AcpiDisable;
  UINT8                                   S4BiosReq;
  UINT8                                   PstateCnt;
  UINT32                                  Pm1aEvtBlk;
  UINT32                                  Pm1bEvtBlk;
  UINT32                                  Pm1aCntBlk;
  UINT32                                  Pm1bCntBlk;
  UINT32                                  Pm2CntBlk;
  UINT32                                  PmTmrBlk;
  UINT32                                  Gpe0Blk;
  UINT32                                  Gpe1Blk;
  UINT8                                   Pm1EvtLen;
  UINT8                                   Pm1CntLen;
  UINT8                                   Pm2CntLen;
  UINT8                                   PmTmrLen;
  UINT8                                   Gpe0BlkLen;
  UINT8                                   Gpe1BlkLen;
  UINT8                                   Gpe1Base;
  UINT8                                   CstCnt;
  UINT16                                  PLvl2Lat;
  UINT16                                  PLvl3Lat;
  UINT16                                  FlushSize;
  UINT16                                  FlushStride;
  UINT8                                   DutyOffset;
  UINT8                                   DutyWidth;
  UINT8                                   DayAlrm;
  UINT8                                   MonAlrm;
  UINT8                                   Century;
  UINT16                                  IaPcBootArch;
  UINT8                                   Reserved1;
  UINT32                                  Flags;
  EFI_ACPI_5_0_GENERIC_ADDRESS_STRUCTURE  ResetReg;
  UINT8                                   ResetValue;
  UINT8                                   Reserved2[3];
  UINT64                                  XFirmwareCtrl;
  UINT64                                  XDsdt;
  EFI_ACPI_5_0_GENERIC_ADDRESS_STRUCTURE  XPm1aEvtBlk;
  EFI_ACPI_5_0_GENERIC_ADDRESS_STRUCTURE  XPm1bEvtBlk;
  EFI_ACPI_5_0_GENERIC_ADDRESS_STRUCTURE  XPm1aCntBlk;
  EFI_ACPI_5_0_GENERIC_ADDRESS_STRUCTURE  XPm1bCntBlk;
  EFI_ACPI_5_0_GENERIC_ADDRESS_STRUCTURE  XPm2CntBlk;
  EFI_ACPI_5_0_GENERIC_ADDRESS_STRUCTURE  XPmTmrBlk;
  EFI_ACPI_5_0_GENERIC_ADDRESS_STRUCTURE  XGpe0Blk;
  EFI_ACPI_5_0_GENERIC_ADDRESS_STRUCTURE  XGpe1Blk;
  EFI_ACPI_5_0_GENERIC_ADDRESS_STRUCTURE  SleepControlReg;
  EFI_ACPI_5_0_GENERIC_ADDRESS_STRUCTURE  SleepStatusReg;
} EFI_ACPI_5_0_FIXED_ACPI_DESCRIPTION_TABLE;  
#pragma pack()

EFI_STATUS
PrintGuid (
  IN EFI_GUID *Guid
  )
  
/*++

Routine Description:

  This function prints a GUID to STDOUT.

Arguments:

  Guid    Pointer to a GUID to print.

Returns:

  EFI_SUCCESS             The GUID was printed.
  EFI_INVALID_PARAMETER   The input was NULL.

--*/
{
  if (Guid == NULL) {
	Print(L"Parameter error!\n");
    return EFI_INVALID_PARAMETER;
  }

  Print (
    L"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x ",
    (unsigned) Guid->Data1,
    Guid->Data2,
    Guid->Data3,
    Guid->Data4[0],
    Guid->Data4[1],
    Guid->Data4[2],
    Guid->Data4[3],
    Guid->Data4[4],
    Guid->Data4[5],
    Guid->Data4[6],
    Guid->Data4[7]
    );
  return EFI_SUCCESS;
}

UINTN
CompareGUID (
  IN EFI_GUID *Guid1,
  IN EFI_GUID *Guid2
  )
  
/*++

Routine Description:

  This function compares 2 GUIDs

Arguments:

  Guid1    Pointer to 1st GUID.
  Guid2    Pointer to 12st GUID.

Returns:

  0             The GUID was same.
  1  		    The input was different.
  2				PARAMETER error.

--*/
{

  if ((Guid1 == NULL)||(Guid2 == NULL)) {
	Print(L"Parameter error!\n");
    return 2;
  }

  if ((Guid1->Data1 != Guid2->Data1) ||
	  (Guid1->Data2 != Guid2->Data2) ||
	  (Guid1->Data3 != Guid2->Data3) ||
      (Guid1->Data4[0] != Guid2->Data4[0]) ||
      (Guid1->Data4[1] != Guid2->Data4[1]) ||
	  (Guid1->Data4[2] != Guid2->Data4[2]) ||
      (Guid1->Data4[3] != Guid2->Data4[3]) ||
      (Guid1->Data4[4] != Guid2->Data4[4]) ||
	  (Guid1->Data4[5] != Guid2->Data4[5]) ||
      (Guid1->Data4[6] != Guid2->Data4[6]) ||
      (Guid1->Data4[7] != Guid2->Data4[7]))	
	{
		return 1;
	}
	return 0;
}

EFI_STATUS
SaveDSDTToFile (
	CHAR8	*P,
	UINTN	length
  )
  
/*++

Routine Description:

  Write a memory to a file

Arguments:

  P          Pointer to the contant
  length	 Sizeof the contant

Returns:

  EFI_SUCCESS             The GUID was printed.
  EFI_INVALID_PARAMETER   The input was NULL.

--*/
{
    EFI_STATUS          			Status;
	EFI_FILE_PROTOCOL 				*Root,*FileHandle=0;
	EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFileSystem;	
	
	Status = gBS->LocateProtocol(
					&gEfiSimpleFileSystemProtocolGuid, 
					NULL,
					(VOID **)&SimpleFileSystem);
						
	if (EFI_ERROR(Status)) {
			Print(L"Cannot find EFI_SIMPLE_FILE_SYSTEM_PROTOCOL \r\n");
			return Status;	
		}

	Status = SimpleFileSystem->OpenVolume(SimpleFileSystem,&Root);
	if (EFI_ERROR(Status)) {
			Print(L"OpenVolume error \r\n");
			return Status;	
		}
   
	Status = Root -> Open(Root,
				&FileHandle,
				(CHAR16 *) L"dsdt.aml",
				EFI_FILE_MODE_CREATE | EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE,
				0);
			
	if (EFI_ERROR(Status) || (FileHandle==0)) {
			Print(L"Open error \r\n");
			return Status;	
		}	

	Status = FileHandle -> Write(FileHandle, &length, P);
	
	Status  = FileHandle -> Close (FileHandle);
	
	return EFI_SUCCESS;
}

int
EFIAPI
main (
  IN int Argc,
  IN CHAR16 **Argv
  )
{
	UINTN 		i,j,EntryCount;
	EFI_STATUS	Status;
	CHAR16		Sign[20];
	UINT64		*EntryPtr;
	EFI_GUID	AcpiTableGuid  = ACPI_TABLE_GUID;
	EFI_GUID	Acpi2TableGuid = EFI_ACPI_TABLE_GUID;
	EFI_CONFIGURATION_TABLE		*C=NULL;	
	EFI_ACPI_DESCRIPTION_HEADER				 		*XSDT,*Entry,*DSDT;
	EFI_ACPI_5_0_FIXED_ACPI_DESCRIPTION_TABLE		*FADT;
	EFI_ACPI_5_0_ROOT_SYSTEM_DESCRIPTION_POINTER	*Root;

  C=gST->ConfigurationTable;
  
  for (i=0;i<gST->NumberOfTableEntries-1;i++)
	{	  
		//Step1. Find the table for ACPI
		if ((CompareGUID(&C->VendorGuid,&AcpiTableGuid) == 0) ||
			(CompareGUID(&C->VendorGuid,&Acpi2TableGuid) == 0))
			{	
				Print(L"Found table:");
				Status=PrintGuid(&C->VendorGuid); 
				Print(L"@[0x%X]\n",C);
				
				Root=C->VendorTable;
				if (zDebug) {Print(L"ROOT SYSTEM DESCRIPTION @[0x%X]\n",Root);}
				
				if (zDebug) {
						ZeroMem(Sign,sizeof(Sign));
						for (j=0;j<8;j++) { Sign[j]=(Root->Signature >> (j*8) & 0xFF); }
						Print(L"Signature [%s]\n",Sign);
						Print(L"Revision [%d]\n",Root->Revision);
						ZeroMem(Sign,sizeof(Sign));
						for (j=0;j<6;j++) { Sign[j]= (Root->OemId[j] & 0xFF); }
						Print(L"OEMID [%s]\n",Sign);
					}
				
				Print(L"RSDT address= [0x%X], Length=[0x%X]\n",
										Root->RsdtAddress,Root->Length);
				Print(L"XSDT address= [0x%LX]\n",Root->XsdtAddress);
				
				// Step2. Check the Revision, we olny accept Revision >= 2
				if (Root->Revision>=EFI_ACPI_5_0_ROOT_SYSTEM_DESCRIPTION_POINTER_REVISION)
					{
						// Step3. Get XSDT address
						XSDT=(EFI_ACPI_DESCRIPTION_HEADER *)(UINTN) Root->XsdtAddress;
						EntryCount = (XSDT->Length - sizeof(EFI_ACPI_DESCRIPTION_HEADER)) 
											/ sizeof(UINT64);
						
						if (zDebug) {
							ZeroMem(Sign,sizeof(Sign));
							Sign[0]= (XSDT->Signature & 0xFF);
							Sign[1]= (XSDT->Signature >> 8 & 0xFF);
							Sign[2]= (XSDT->Signature >> 16 & 0xFF);
							Sign[3]= (XSDT->Signature >> 24 & 0xFF);
							Print(L"Sign [%s]\n",Sign);						
							Print(L"length [%d]\n",XSDT->Length);						
							Print(L"Counter [%d]\n",EntryCount);	
							}
						
						// Step4. Check the signature of every entry
						EntryPtr=(UINT64 *)(XSDT+1);
						for (j=0;j<EntryCount; j++,EntryPtr++)
						  {
							
							Entry=(EFI_ACPI_DESCRIPTION_HEADER *)((UINTN)(*EntryPtr));
							
							if (zDebug) {
								ZeroMem(Sign,sizeof(Sign));
								Sign[0]= (Entry->Signature & 0xFF);
								Sign[1]= (Entry->Signature >> 8 & 0xFF);
								Sign[2]= (Entry->Signature >> 16 & 0xFF);
								Sign[3]= (Entry->Signature >> 24 & 0xFF);
								Print(L"%d: [%s] @[%X]\n",j,Sign,Entry);
							 }
							
							// Step5. Find the FADT table
							if (Entry->Signature==0x50434146) { //'FACP'
								FADT = (EFI_ACPI_5_0_FIXED_ACPI_DESCRIPTION_TABLE *)(UINTN) Entry;
								
								// Step6. Get DSDT address
								DSDT = (EFI_ACPI_DESCRIPTION_HEADER *) (FADT->Dsdt);
								if (zDebug) {
									Print(L"DSDT table @[%X]\n",DSDT);
								}
								
								// Step7. Save DSDT as a file
								SaveDSDTToFile((CHAR8 *)DSDT,DSDT->Length);
							}
						  }
					}
			}
		C++;
	}
	
  return EFI_SUCCESS;
}

 

运行结果(注意:上述代码是在NT32模拟环境下编译通过的,但是因为模拟坏境中没有对应的 ACPI Table,所以必须在实体机上运行才有结果):

image002

最后得到的 DSDT.AML 我们可以直接使用 ASL 工具反编译。

完整代码下载

GetACPI

本文代码参阅了【参考1】,这里表示感谢。

参考:
1. http://blog.fpmurphy.com/2015/01/list-acpi-tables-from-uefi-shell.html List ACPI Tables From UEFI Shell
2. http://www.cnblogs.com/junzhkevin/archive/2013/02/25/2932801.html ACPI Tables (不确定是否为文章出处。这里有对ACPI Table进行简单的介绍,值得阅读)

Step to UEFI (75) —–取得 ConfigurationTable

EFI_SYSTEM_TABLE 中定义了 EFI_CONFIGURATION_TABLE *ConfigurationTable;

这个结构体定义如下:

typedef struct{
EFI_GUID  VendorGuid;
VOID  *VendorTable;
} EFI_CONFIGURATION_TABLE;

 

整体看起来就是这样:

image001

根据上面的原理可以编写程序枚举系统中的 ConfigurationTable,代码如下:

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


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

EFI_STATUS
PrintGuid (
  IN EFI_GUID *Guid
  )
  
/*++

Routine Description:

  This function prints a GUID to STDOUT.

Arguments:

  Guid    Pointer to a GUID to print.

Returns:

  EFI_SUCCESS             The GUID was printed.
  EFI_INVALID_PARAMETER   The input was NULL.

--*/
{
  if (Guid == NULL) {
	Print(L"Parameter error!\n");
    return EFI_INVALID_PARAMETER;
  }

  Print (
    L"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
    (unsigned) Guid->Data1,
    Guid->Data2,
    Guid->Data3,
    Guid->Data4[0],
    Guid->Data4[1],
    Guid->Data4[2],
    Guid->Data4[3],
    Guid->Data4[4],
    Guid->Data4[5],
    Guid->Data4[6],
    Guid->Data4[7]
    );
  return EFI_SUCCESS;
}

int
EFIAPI
main (
  IN int Argc,
  IN CHAR16 **Argv
  )
{
  UINTN 	i;
  EFI_STATUS	Status;
  EFI_CONFIGURATION_TABLE	*C=NULL;
  
  Print(L"[%d] Tables were found!\n",gST->NumberOfTableEntries);
  C=gST->ConfigurationTable;
  
  for (i=0;i<gST->NumberOfTableEntries-1;i++)
	{
		Status=PrintGuid(&C->VendorGuid);
		C++;
	}
  return EFI_SUCCESS;
}

 

然后我们尝试在模拟环境中运行,结果如下:

image002

我们再看看找到的这些GUID是什么意思:

#define TIANO_CUSTOM_DECOMPRESS_GUID  \
  { 0xA31280AD, 0x481E, 0x41B6, { 0x95, 0xE8, 0x12, 0x7F, 0x4C, 0x98, 0x47, 0x79 } }

#define EFI_CRC32_GUIDED_SECTION_EXTRACTION_GUID \
  { 0xFC1BCDB0, 0x7D31, 0x49aa, {0x93, 0x6A, 0xA4, 0x60, 0x0D, 0x9D, 0xD0, 0x83 } }

#define DXE_SERVICES_TABLE_GUID \
  { \
    0x5ad34ba, 0x6f02, 0x4214, {0x95, 0x2e, 0x4d, 0xa0, 0x39, 0x8e, 0x2b, 0xb9 } \
}
#define HOB_LIST_GUID \
  { \
    0x7739f24c, 0x93d7, 0x11d4, {0x9a, 0x3a, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d } \
  }

#define EFI_MEMORY_TYPE_INFORMATION_GUID \
  { 0x4c19049f,0x4137,0x4dd3, { 0x9c,0x10,0x8b,0x97,0xa8,0x3f,0xfd,0xfa } }

#define EFI_DEBUG_IMAGE_INFO_TABLE_GUID \
  { \
    0x49152e77, 0x1ada, 0x4764, {0xb7, 0xa2, 0x7a, 0xfe, 0xfe, 0xd9, 0x5e, 0x8b } \
  }
#define SMBIOS_TABLE_GUID \
  { \
    0xeb9d2d31, 0x2d88, 0x11d3, {0x9a, 0x16, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d } \
  }

 

能看懂的只有SMBIOS…….我们再用实体机测试,结果如下:

image003

比上面的多了5个GUID,分别是:

#define LZMA_CUSTOM_DECOMPRESS_GUID  \
  { 0xEE4E5898, 0x3914, 0x4259, { 0x9D, 0x6E, 0xDC, 0x7B, 0xD7, 0x94, 0x03, 0xCF } }

#define EFI_TSC_FREQUENCY_GUID \
  { \
    0xdba6a7e3, 0xbb57, 0x4be7, { 0x8a, 0xf8, 0xd5, 0x78, 0xdb, 0x7e, 0x56, 0x87 } \
  }

#define ACPI_TABLE_GUID \
  { \
    0xeb9d2d30, 0x2d88, 0x11d3, {0x9a, 0x16, 0x0, 0x90, 0x27, 0x3f, 0xc1, 0x4d } \
  }

#define EFI_ACPI_TABLE_GUID \
  { \
    0x8868e871, 0xe4f1, 0x11d3, {0xbc, 0x22, 0x0, 0x80, 0xc7, 0x3c, 0x88, 0x81 } \
  }

gEfiOfflineCrashDumpTblGuid             = { 0x3804CF02, 0x8538, 0x11E2, { 0x88, 0x47, 0x8D, 0xF1, 0x60, 0x88, 0x70, 0x9B } }

 

本文完整代码下载:

GetATBL

下一次会介绍 Shell 下如何获取ACPI Table。

用示波器“看”arduino (4) —-Timer0

Arduino 默认使用了 Timer0 【参考1】,在 \arduino-1.6.3\hardware\arduino\avr\cores\arduino\wiring.c 有

#if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
ISR(TIM0_OVF_vect)
#else
ISR(TIMER0_OVF_vect)
#endif
{
	// copy these to local variables so they can be stored in registers
	// (volatile variables must be read from memory on every access)
	unsigned long m = timer0_millis;
	unsigned char f = timer0_fract;

	m += MILLIS_INC;
	f += FRACT_INC;
	if (f >= FRACT_MAX) {
		f -= FRACT_MAX;
		m += 1;
	}

	timer0_fract = f;
	timer0_millis = m;
	timer0_overflow_count++;
}

 

简单的说,设置Timer0 1ms触发一次,然后其中的计数器会自动加1。同样的文件中还能看到millis( ) micros() 和delay() 函数都用到这个计数值。所以有如下结论:
1. 如果修改了Timer0的计时频率,这些函数都会受到影响
2. 如果你关闭了Timer0,那么这些函数都会失效
3. 如果你是编写中断服务,进入你的服务程序之后,关闭了中断,那么不可以使用这些函数

我们尝试修改这个文件,插入翻转pin的代码,可以看到,示波器上一直显示电平有变化

#if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
ISR(TIM0_OVF_vect)
#else
ISR(TIMER0_OVF_vect)
#endif
{
	// copy these to local variables so they can be stored in registers
	// (volatile variables must be read from memory on every access)
	unsigned long m = timer0_millis;
	unsigned char f = timer0_fract;

	m += MILLIS_INC;
	f += FRACT_INC;
	if (f >= FRACT_MAX) {
		f -= FRACT_MAX;
		m += 1;
	}

	timer0_fract = f;
	timer0_millis = m;
	timer0_overflow_count++;
	//LABZ_Debug_Start
	digitalWrite(9,LOW);
	digitalWrite(9,HIGH);
	digitalWrite(9,LOW);	
	//LABZ_Debug_End
}

 

验证的代码非常简单:

void setup() {
  // put your setup code here, to run once:
  pinMode(9,OUTPUT);
}
void loop() {
 }

 

运行结果

image001

放大一些看,间隔在1ms左右,就是Timer0触发一次的时间

image003

同样的程序我们在主程序中使用noInterrupts关掉中断【参考2】,显示出来始终为低电平,因为Timer0被关掉了,ISR中的代码永远不会跑到。

void setup() {
  // put your setup code here, to run once:
  pinMode(9,OUTPUT);
  noInterrupts();
}

void loop() {
  // put your main code here, to run repeatedly:

}

 

运行结果,始终为低电平

image005

某些时候我们只需要关闭Timer0的中断,可以用屏蔽溢出中断的方法,设置TOIE0为0即可。

#define OutPin 8
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

void setup() {
  // put your setup code here, to run once:
  pinMode(OutPin,OUTPUT);
  digitalWrite(OutPin,LOW);
  cbi (TIMSK0, TOIE0);
}

void loop() {
  // put your main code here, to run repeatedly:
  
}

 

运行结果和上图没差别,一直未低

此外还可以通过关闭Timer0的source的方法【参考3】来关闭Timer0.具体只要设置 CS00==CS01==CS02==0 即可。

#define OutPin 9

void setup() {
  // put your setup code here, to run once:
  pinMode(OutPin,OUTPUT);
  digitalWrite(OutPin,LOW);

}

void loop() {
  delay(1000);
  bitWrite(TCCR0B, CS00, 0);
  bitWrite(TCCR0B, CS01, 0);
  bitWrite(TCCR0B, CS02, 0);  
  delayMicroseconds(10000);
  digitalWrite(OutPin,HIGH);   
}

 

进入 loop函数之后 delay 1s 持续有中断产生,之后关闭了中断就出现一直为低的情况,接下来是10ms的delay,最后再拉高(这样只是为了让波形清晰可辩)。

image007

放大一点可以看到,中间有间隔是10539ms.

image001

特别注意:上述代码中,如果我将delayMicroseconds换成 Delay,那么看到的波形将会是一直为低。读者可以思考一下原因。

参考:
1. http://letsmakerobots.com/node/28278 Arduino 101: Timers and Interrupts

2.\arduino-1.6.3\hardware\arduino\avr\cores\arduino\Arduino.h
#define interrupts() sei()
#define noInterrupts() cli()

3. https://arduinodiy.wordpress.com/2012/02/28/timer-interrupts/
Clock Select bit description
CS12 CS11 CS10 Description
0 0 0 No clock source (Timer/Counter stopped)
0 0 1 clki/o/1 (No prescaling)
0 1 0 clki/o/8 (From Prescaler)
0 1 1 clki/o/64 (From Prescaler)
1 0 0 clki/o/256 (From Prescaler)
1 0 1 clki/o/1024 (From Prescaler)
1 1 0 External clock source on T1 pin. Clock on falling edge
1 1 1 External clock source on T1 pin. Clock on rising edge

Processing 摄像头代码改成灰度显示

有朋友在百度贴吧提了下面的问题【参考1】

“求助 帮我把下面的摄像头代码改成黑白的摄像头”

// Original Source created by Ben Stephenson, University of Calgary
// Code modified by Sandy Graham

////////////////////////////////////////////////////////////////////////////////
// DO NOT CHANGE ANYTHING IN THIS FILE EXCEPT IN THE MARKED REGION NEAR THE 
// BOTTOM OF THE FILE.
////////////////////////////////////////////////////////////////////////////////
import processing.video.*;

Capture cam; // the object representing the camera
PImage camimg; // image from the camera
PImage transformed; // the transformed image

float cam_r, cam_g, cam_b; // camera pixel red, green and blue
float trans_r, trans_g, trans_b; // transformed pixel red, green and blue

void setup()
{
String[] cameras = Capture.list(); // get a list of all the available cameras
// and their resolutions / frame rates

if (cameras.length == 0) // check that we have an avaialble camera 
{ 
println("There are no cameras available for capture.");
exit();
} 
else // display a list of the cameras 
{
println("Available cameras:");
for (int i = 0; i < cameras.length; i++) 
{
println(cameras[i]);
}

// This attempts to find a camera with a resolution of 640x480 at 30 frames per
// second. However, there is no guarantee that any particular camera provides
// this resolution / framerate. As an alternative, you can initialize the
// camera as:
//
// cam = new Capture(this, cameras[0])
//
// Use an index other than 0 if you don't like the resolution or framerate of 
// camera 0. While the following code won't do anything about a low framerate,
// it will scale whatever image it gets back from the camera to 640x480 with a
// frame rate of 30 frames per second.
//
cam = new Capture(this, 640, 480, 30);
cam.start();
}

size(640,480); // set the window size
camimg = new PImage(640,480); // create images to hold the original
transformed = new PImage(640,480); // and transformed data

// read the first frame from the camera
if (cam.available() == true) {
cam.read(); // read a new frame of data from the camera
camimg.copy(cam, 0, 0, 640, 480, 0, 0, 640, 480); 
}
}

void draw()
{

if (cam.available() == true) {
cam.read(); // read a new frame of data from the camera
camimg.copy(cam, 0, 0, 640, 480, 0, 0, 640, 480); 
}

////////////////////////////////////////////
// ONLY MAKE CHANGES BELOW THIS POINT
////////////////////////////////////////////

transformed.copy(camimg, 0, 0, 640, 480, 0, 0, 640, 480);

////////////////////////////////////////////
// ONLY MAKE CHANGES ABOVE THIS POINT
////////////////////////////////////////////

image(transformed,0,0); // draw the transformed image on the screen
}

修改方法很简单,加入下面红色代码即可:

////////////////////////////////////////////
// ONLY MAKE CHANGES BELOW THIS POINT
////////////////////////////////////////////

transformed.copy(camimg, 0, 0, 640, 480, 0, 0, 640, 480);

transformed.filter(GRAY);

////////////////////////////////////////////
// ONLY MAKE CHANGES ABOVE THIS POINT
////////////////////////////////////////////

参考:

1.http://tieba.baidu.com/p/4156597158

Step to UEFI (74) —– 通过 OpenVolume访问FSx上的文件

题目有点绕口,简单的说目标就是:我打算用 EFI_SIMPLE_FILE_SYSTEM_PROTOCOL 中的 OpenVolume 打开 FsX: 上面的文件怎么办?
实现的思路是:

1. 查找系统中所有支持 FS Protocol的Device
2. 对于每一个有 FS Protocol 的 Device 用 DevicePathFromHandle 取得 DevicePath
3. 再用 GetFsName 功能取得 FS0 ,FS1 这样的名称,然后判断是否为我们希望的名称
4. 如果是的话,再取得这个设备上的 SimpleFileSystem protocol
5. 最后用 OpenVolue 打开文件。

具体代码:

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

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

#include <Protocol/EfiShell.h>
#include <Library/ShellLib.h>

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

extern EFI_BOOT_SERVICES         *gBS;

extern EFI_SHELL_ENVIRONMENT2    *mEfiShellEnvironment2;
extern EFI_HANDLE				 gImageHandle;

EFI_STATUS
EFIAPI
PerformSingleMappingDisplay(
  IN CONST EFI_HANDLE Handle
  )
{
	EFI_DEVICE_PATH_PROTOCOL  *DevPath;
	CHAR16                    *CurrentName;
	CHAR16					*FSNAME=L"fsnt1";
	EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFileSystem;
	EFI_FILE_INFO     		*FileInfo = NULL;
	EFI_STATUS                Status;
	EFI_FILE_PROTOCOL   		*FileProtocol;
	EFI_FILE_HANDLE     		FileHandle;
	UINTN 					FileDataLength;
	CHAR16 					*FileData;

	CurrentName = NULL;
    DevPath = DevicePathFromHandle(Handle);
  
  //4. Covnver DevicePath to FSx (E.x FS2, FSNT1.....)
  mEfiShellEnvironment2->GetFsName(DevPath,FALSE,&CurrentName);

  //5. If the "FSx" string is what we want 
  if (StrCmp(CurrentName,FSNAME)==0) {
		Print (L"%s \r\n", CurrentName);  		
		
		//6. Open the SimpleFileSystem Protocol on it
		Status = gBS->OpenProtocol(
								Handle,
                                &gEfiSimpleFileSystemProtocolGuid,
                                (VOID**)&SimpleFileSystem,
								gImageHandle,
								NULL,
								EFI_OPEN_PROTOCOL_GET_PROTOCOL
                                );
		if (EFI_ERROR(Status)) {
			Print (L"LocateProtocol SimpleFileSystem Error \r\n");  	
			return (EFI_NOT_FOUND);
		}
	   
	    //7. Use OpenVolue to get FileProtocol
		Status = SimpleFileSystem->OpenVolume(SimpleFileSystem, &FileProtocol);
		if (EFI_ERROR(Status)) {
			Print (L"SimpleFileSystem OpenVolume Error \r\n");
			return Status;
		}
    
		//8. At last we can operate file by FileProtocol
		Status = FileProtocol->Open(FileProtocol, 
                              &FileHandle, 
                              L"Hello.txt",
                              EFI_FILE_MODE_READ,
                              0);
		if (EFI_ERROR(Status)) {
			Print (L"FileProtocol Open Error [%r]\r\n",Status);
			return Status;
		}
    
		FileInfo = ShellGetFileInfo( (SHELL_FILE_HANDLE)FileHandle);	
		Print(L"Filesize [%ld] bytes\n",FileInfo-> FileSize);
		FileDataLength=(UINTN) FileInfo->FileSize;
	
		FileData  = AllocatePool((UINTN) FileInfo->FileSize);
		Status = FileHandle->Read(FileHandle, &FileDataLength, FileData );
		if (EFI_ERROR(Status)) {
			Print(L"Loading file error! \n");
		}
		
		FileData[FileDataLength/2 -1]=0x0;
		Print(L"File contants: [%s]",FileData);
		
		FreePool(FileData);
		FileProtocol->Close(FileHandle);
  }
	
  if ((CurrentName) != NULL) { FreePool((CurrentName)); CurrentName = NULL; }

  return EFI_SUCCESS;
}

int
EFIAPI
main (                                         
  IN int Argc,
  IN char **Argv
  )
{
  EFI_STATUS                Status;
  EFI_HANDLE                *HandleBuffer=NULL;
  UINTN                     BufferSize=0;
  UINTN                     LoopVar;
  BOOLEAN                   Found;

  //1. We have to use some function in SE2 
  //
  // UEFI 2.0 shell interfaces (used preferentially)
  //
  Status = gBS->OpenProtocol(
    gImageHandle,
    &gEfiShellProtocolGuid,
    (VOID **)&gEfiShellProtocol,
    gImageHandle,
    NULL,
    EFI_OPEN_PROTOCOL_GET_PROTOCOL
   );
   
  if (EFI_ERROR(Status)) {
    //
    // Search for the shell protocol
    //
    Status = gBS->LocateProtocol(
      &gEfiShellProtocolGuid,
      NULL,
      (VOID **)&gEfiShellProtocol
     );
    if (EFI_ERROR(Status)) {
      gEfiShellProtocol = NULL;
     }
  }

  
  //
  //2. Look up all SimpleFileSystems in the platform
  //
  Status = gBS->LocateHandle(
    ByProtocol,
    &gEfiSimpleFileSystemProtocolGuid,
    NULL,
    &BufferSize,
    HandleBuffer);
	
  if (Status == EFI_BUFFER_TOO_SMALL) {
		HandleBuffer = AllocateZeroPool(BufferSize);
		if (HandleBuffer == NULL) {
			return (SHELL_OUT_OF_RESOURCES);
		}
		Status = gBS->LocateHandle(
			ByProtocol,
			&gEfiSimpleFileSystemProtocolGuid,
			NULL,
			&BufferSize,
			HandleBuffer);
   }

  //
  // Get the map name(s) for each one.
  //
  for ( LoopVar = 0, Found = FALSE
      ; LoopVar < (BufferSize / sizeof(EFI_HANDLE)) && HandleBuffer != NULL
      ; LoopVar ++
     ) {
	//3.Emulate every Handle which has SimpileFileSystem 
    Status = PerformSingleMappingDisplay(HandleBuffer[LoopVar]);
    if (!EFI_ERROR(Status)) {
      Found = TRUE;
    }
  }
  
  FreePool(HandleBuffer);
	
  return EFI_SUCCESS;
}

 

我是在虚拟机下实验的,运行之后打开并且读取 fsnt2:\hello.txt 的内容(内容是 www.lab-z.com123)。

openfs

特别注意的是:直接读取之后按照 CHAR16 的字符串来处理,但是读取内容没有 0x00 0x00的结尾。直接用Print 输出的时候字符串后面会有意料之外的字符。所以用下面这个语句 FileData[FileDataLength/2 -1]=0x0; 直接添加一个结尾。这也是为什么字符3被截掉的原因。

完整代码下载
OpenFSX

参考:

1. http://www.lab-z.com/esptest/ Step to UEFI (54) —– EFI_SIMPLE_FILE_SYSTEM_PROTOCOL 写文件
2. http://www.lab-z.com/shellfsx/ Step to UEFI (36) —– 枚举Shell下的全部盘符
3. http://www.lab-z.com/stu63/ Step to UEFI (63) —– 常用的字符串函数(下)
4. http://www.lab-z.com/nstring/ Step to UEFI (62) —– 常用的字符串函数(上)

Step to UEFI (73) —– 获得鼠标信息

前面《获得 USB 设备的PID和VID》中提到 USB 鼠标上面有一个 SimplePointer 的 Protocol。 本文介绍一下这个 Protocol 的使用。

在【参考1】上提到EFI_SIMPLE_POINTER_PROTOCOL 的作用是获得鼠标或者轨迹球的输入数据。
“This would include devices such as mice and trackballs.
The EFI_SIMPLE_POINTER_PROTOCOLallows information about a pointer device to be
retrieved. This would include the status of buttons and the motion of the pointer device since the last
time it was accessed. This protocol is attached the device handle of a pointer device, and can be used
for input from the user inthe preboot environment.”

程序原理:首先用 LocateProtocol 取得EFI_SIMPLE_POINTER_PROTOCOL (这里假设系统中只有一个),然后做一次 Reset ,通过这个动作也确定设备是否可以使用。
image001

之后,不断使用 GetState 轮询,如果发现前后两次获得的信息不同那么就输出解析结果,打印当前鼠标的状态。
image002

特别注意,取得的信息如下,是一个 INT32 类型的 RelativeMovement 数值,这个数值必须通过 EFI_SIMPLE_POINTER_MODE 中给出来的 UINT64类型的Resolution 除一次才能得到真正的移动信息。
image003

最终的程序如下:

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

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

// Include/Protocol/SimplePointer.h
EFI_GUID gEfiSimplePointerProtocolGuid  = { 0x31878C87, 0x0B75, 0x11D5, 
			{ 0x9A, 0x4F, 0x00, 0x90, 0x27, 0x3F, 0xC1, 0x4D }};
  
int
EFIAPI
main (
  IN int Argc,
  IN CHAR16 **Argv
  )
{
	EFI_STATUS	Status;
	EFI_SIMPLE_POINTER_PROTOCOL	*Mouse;
	EFI_SIMPLE_POINTER_STATE	Last,Current;
	EFI_INPUT_KEY				Key;
	INT32		X,Y,Z;
	
	//Get MP_Service Protocol
	Status = gBS->LocateProtocol (&gEfiSimplePointerProtocolGuid, NULL, (VOID**)&Mouse);
	if (EFI_ERROR (Status)) {
		Print(L"Unable to initialize EFI_SIMPLE_POINTER_PROTOCOL protocol interface!");
		return EFI_UNSUPPORTED;
	}
	
	Status = Mouse->Reset(Mouse,TRUE);
	if (EFI_ERROR (Status)) {
		Print(L"The device is not functioning correctly and could not be reset!");
		return EFI_UNSUPPORTED;
	}
 
	gST-> ConOut -> ClearScreen(gST->ConOut);
    Print(L"Resolution: [%lX] [%lX] [%lX] \n",
			Mouse->Mode->ResolutionX,
			Mouse->Mode->ResolutionY,
			Mouse->Mode->ResolutionZ);

 
	while (1) {
		Status = Mouse->GetState(Mouse,&Current);
		if (memcmp(&Current,&Last,sizeof(EFI_SIMPLE_POINTER_STATE))!=0) {
		
			X=(INT32) ( Current.RelativeMovementX / Mouse->Mode->ResolutionX);
			Y=(INT32) (Current.RelativeMovementY / Mouse->Mode->ResolutionY);
			Z=(INT32) ( Current.RelativeMovementZ / Mouse->Mode->ResolutionZ);
			
			Print(L"X=[%d] Y=[%d] Z=[%d] ",
					X,
					Y,
					Z);
			Current.LeftButton?
					Print(L"[Left Click]"):
					Print(L"[No Left   ]");
			Current.RightButton?
					Print(L"[Right Click]\n"):
					Print(L"[No Right   ]\n");
			memcpy(&Last,&Current,sizeof(EFI_SIMPLE_POINTER_STATE))	;	
		}
		Status = gST -> ConIn -> ReadKeyStroke (gST->ConIn,&Key);
		if (Status== EFI_SUCCESS) {
			break;
		}
	}
	return EFI_SUCCESS;
}

 

运行结果:

image004
上面可以看到有一些前后没有变化的数据也被打印出来,这个可能是因为取得的原始数据有不同,但是做过除法之后数据是相同的导致的。

关于上面程序需要注意的地方:
1.EDK自带的模拟环境虽然能找到这个 Protocol 但是实际上不会有数据出来的。我试验了很久才发现,同样的程序实体机中跑的很好,但是模拟器不会有结果,所以不能在模拟环境中实验;
2.在处理INT32 INT64输出时要特别注意输出方式,否则会有奇怪的结果。

完整的代码下载:
micetest1

参考:
1. UEFI Spec 2.4 P461

<<与孩子一起学编程>>中的“滑雪的人”游戏

最近在学习 Python,看了一本书<<与孩子一起学编程>>英文名是“Computer Programming for Kids and Other Beginners”,感觉挺有意思的。

Capture

书中推荐用 pygame 来编写游戏,我按照书上的程序打了一个滑雪者的小游戏。输入程序花了半小时,调试这个程序差不多花了2个小时。

最后调试通过的程序如下:

import pygame,sys,random

skier_images = ["skier_down.png","skier_right1.png",
                "skier_right2.png","skier_left2.png",
                "skier_left1.png"]

class SkierClass(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.image.load("skier_down.png")
        self.rect = self.image.get_rect()
        self.rect.center = [320,100]
        self.angle =0

    def turn(self,direction):
        self.angle = self.angle + direction
        if self.angle < -2: self.angle = -2
        if self.angle >  2: self.angle = 2
        center = self.rect.center
        self.image = pygame.image.load(skier_images[self.angle])
        self.rect = self.image.get_rect()
        self.rect.center =center
        speed = [self.angle,6 - abs(self.angle) * 2]
        return speed

    def move(self,speed):
        self.rect.centerx = self.rect.centerx + speed[0]
        if self.rect.centerx < 20: self.rect.centerx = 20
        if self.rect.centerx > 620: self.rect.centerx =620

class ObstacleClass(pygame.sprite.Sprite):
    def __init__(self,image_file,location,type):
        pygame.sprite.Sprite.__init__(self)
        self.image_file = image_file
        self.image = pygame.image.load(image_file)
        self.location = location
        self.rect = self.image.get_rect()
        self.rect.center = location
        self.type =type
        self.passed =False

    def scroll(self, t_ptr):
        self.rect.centery = self.location[1] - t_ptr

def create_map(start, end):
        obstacles = pygame.sprite.Group()
        gates = pygame.sprite.Group()
        locations = []
        for i in range(10):
            row = random.randint(start,end)
            col = random.randint(0,9)
            location = [col * 64+20,row * 64 +20]
            if not (location in locations):
                locations.append(location)
            type = random.choice(["tree","flag"])
            if type == "tree": img = "skier_tree.png"
            elif type == "flag": img ="skier_flag.png"
            obstact = ObstacleClass(img,location,type)
            obstacles.add(obstact)
        return obstacles

def animate():
        screen.fill([255,255,255])
        pygame.display.update(obstacles.draw(screen))
        screen.blit(skier.image, skier.rect)
        screen.blit(score_text,[10,10])
        pygame.display.flip()

def updateObstacleGroup(map0,map1):
        obstacles = pygame.sprite.Group()
        for ob in map0: obstacles.add(ob)
        for ob in map1: obstacles.add(ob)
        return obstacles

pygame.init()
screen = pygame.display.set_mode([640,640])
clock = pygame.time.Clock()
skier = SkierClass()
speed = [0,6]
map_position =0
points=0;
map0 = create_map(20,29)
map1 = create_map(10,19)
activeMap =0;
obstacles = updateObstacleGroup(map0,map1)
font = pygame.font.Font(None,50)
        
while True:
    clock.tick(30)
    for event in pygame.event.get():
        if event.type == pygame.QUIT: sys.exit()
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                    speed = skier.turn(-1)
            elif event.key == pygame.K_RIGHT:
                    speed = skier.turn(1)
    skier.move(speed)
    map_position+=speed[1]

    if map_position >= 640 and activeMap == 0:
            activeMap = 1
            map0 = create_map(20,29)
            obstacles = updateObstacleGroup(map0,map1)
    if map_position>= 1280 and activeMap == 1:
            activeMap = 0
            for ob in map0:
                ob.location[1] = ob.location[1] -1280
            map_position = map_position - 1280
            map1 = create_map(10,19)
            obstacles = updateObstacleGroup(map0,map1)
            
    for obstacle in obstacles:
            obstacle.scroll(map_position)
    hit = pygame.sprite.spritecollide(skier,obstacles,False)
    if hit:
        if hit[0].type == "tree" and not hit[0].passed:
            points = points -100
            skier.image = pygame.image.load("skier_crash.png")
            animate()
            pygame.time.delay(1000)
            skier.image = pygame.image.load("skier_down.png")
            skier.angle = 0
            speed =[0,6]
            hit[0].passed = True
        elif hit[0].type == "flag" and not hit[0].passed:
            points +=10
            obstacles.remove(hit[0])
    score_text = font.render("Score: "+str(points),1,(0,0,0))
    animate()

 

运行结果:

Untitled

如果你也恰好阅读这本书,遇到同样的问题,建议你参考上面的程序,此外需要特别注意的是 Python 使用缩进之类的来表示上下文之间的隶属关系。如果你的程序遇到莫名其妙的错误,请检查是不是缩进搞错了。比如:

class x:
def func1
def func2

这样,func2 是 x 的一个方法了,如果你在其他地方试图直接调用 func2 就会出现未定 func2 的问题。我因为这样的事情被卡了很久。

最后放上完整的代码(包括官方网站下载到的官方板的源程序),如果你调试不过,最后还可以用beyond compare 这样的对比工具找到原因。

skier

Step to UEFI —– TIPs

每次编译 Application 之后生成的 efi 文件都在

\Build\AppPkg\DEBUG_MYTOOLS\IA32\AppPkg\Applications\APPNAME\APPNAME 这样的目录中,每次需要手工 copy 到虚拟出来的目录下,这样比较麻烦。

经过研究发现可以在 \AppPkg\AppPkg.dsc 中做如下修改

#OUTPUT_DIRECTORY = Build/AppPkg
OUTPUT_DIRECTORY = Build/NT32IA32

这样,每次最后生成 efi 文件之后就会有一个自动 copy 的动作。

tips

Step to UEFI (72) —– MP_Service_Protocol 获得CPU信息

UEFI本身不支持多线程,据说主要原因是:UEFI设计本身是为了启动硬件,做一些比较简单的事情,如果支持多线程会使得设计复杂度直线上升,出现问题也不容易调试。

不过UEFI本身是有对应的Protocol 的,称作 EFI_MP_SERVICES_PROTOCOL 。 具体的这个 Protocol 可以在 PI spec 中找到(UEFI Spec中没有)。下面来自【参考1】

image001

同样可以作为参考的还有EFI_Toolkit_2.0.0.1 中的process.c 程序。

程序很简单,首先 LocateProtocol 到EFI_MP_SERVICES_PROTOCOL。然后,用 Protocol 提供的EFI_MP_SERVICES_GET_NUMBER_OF_PROCESSORS 和EFI_MP_SERVICES_GET_PROCESSOR_INFO 来获得关于处理器的一些信息。需要注意的是EFI_MP_SERVICES_GET_PROCESSOR_INFO,输入的参数ProcessorNumber 意思是:当前要获得的 Processor 号,输入0 表示你要去的编号为 0 的Processor 的信息,输入3 表示你要去的编号为 3 的Processor 的信息,取值范围从0到NumberOfProcessors -1。

image002

最终的代码:

#include  <Uefi.h>
#include  <Library/UefiLib.h>
#include  <Library/ShellCEntryLib.h>
#include  <Pi/PiDxeCis.h>
#include  <Protocol/MpService.h>

EFI_GUID  gEfiMpServiceProtocolGuid = { 0x3fdda605, 0xa76e, 0x4f46, 
						{ 0xad, 0x29, 0x12, 0xf4, 0x53, 0x1b, 0x3d, 0x08 }};
  
extern EFI_BOOT_SERVICES         *gBS;

int
EFIAPI
main (
  IN int Argc,
  IN CHAR16 **Argv
  )
{
	EFI_STATUS	Status;
	EFI_MP_SERVICES_PROTOCOL	*MP=NULL;
	UINTN   i;	
	UINTN	NumProcessors;
	UINTN	NumberOfEnabledProcessors;

	EFI_PROCESSOR_INFORMATION	ProcessorInfo;
	
	//Get MP_Service Protocol
	Status = gBS->LocateProtocol (&gEfiMpServiceProtocolGuid, NULL, (VOID**)&MP);
	if (EFI_ERROR (Status)) {
		Print(L"Unable to initialize MP protocol interface!");
		return EFI_UNSUPPORTED;
	}
	
	// Determine number of processors
	Status = MP->GetNumberOfProcessors( MP, &NumProcessors , &NumberOfEnabledProcessors );
	
	if (EFI_ERROR (Status))
	{
		Print( L"MP->GetNumEnabledProcessors:Unable to determine number of processors\n") ;
		return EFI_UNSUPPORTED;
	}	
	Print(L"Number of Processors %d\n",NumProcessors);
	Print(L"Number of Enabled Processors %d\n",NumberOfEnabledProcessors);
	
	//Get more information by GetProcessorInfo
	for (i=0;i<NumProcessors;i++)
	{
		Status = MP->GetProcessorInfo(MP, i , &ProcessorInfo);
		
		Print( L"Prcoessor #%d ACPI Processor ID = %lX, Flags = %x, Package = %x, Core = %x, Thread = %x \n", 
					i,
					ProcessorInfo.ProcessorId, 
					ProcessorInfo.StatusFlag,
					ProcessorInfo.Location.Package,
					ProcessorInfo.Location.Core,
					ProcessorInfo.Location.Thread);
	}
	
	return EFI_SUCCESS;
}

运行结果:

image003

完整的代码下载:

MPTest1

关于这部分程序,在《UEFI 原理与编程》第13章 深入了解多任务 有更详细的论述,如果你有这方面的需求,推荐直接阅读。

另外,如果你想获得更多CPU方面的信息,比如 Cache大小, 还可以通过 CPUID,但是非常不建议通过 SMBIOS 来获得,因为他并不可靠……

最后特别提醒:

开始编写程序时,我遇到了下面的错误,错误代码对于解决问题毫无帮助,最后经过分析发现是因为没有添加PiDxe.h头文件(感谢Marco指出)导致的。

c:\edk\MdePkg\Include\Protocol/MpService.h(361) : error C2146: syntax error : missing ‘)’ before identifier ‘Procedure’
c:\edk\MdePkg\Include\Protocol/MpService.h(361) : error C2081: ‘EFI_AP_PROCEDURE’ : name in formal parameter list illegal
c:\edk\MdePkg\Include\Protocol/MpService.h(361) : error C2061: syntax error : identifier ‘Procedure’
c:\edk\MdePkg\Include\Protocol/MpService.h(361) : error C2059: syntax error : ‘;’
c:\edk\MdePkg\Include\Protocol/MpService.h(361) : error C2059: syntax error : ‘,’
c:\edk\MdePkg\Include\Protocol/MpService.h(367) : error C2059: syntax error : ‘)’
c:\edk\MdePkg\Include\Protocol/MpService.h(459) : error C2146: syntax error : missing ‘)’ before identifier ‘Procedure’
c:\edk\MdePkg\Include\Protocol/MpService.h(459) : error C2081: ‘EFI_AP_PROCEDURE’ : name in formal parameter list illegal
c:\edk\MdePkg\Include\Protocol/MpService.h(459) : error C2061: syntax error : identifier ‘Procedure’
c:\edk\MdePkg\Include\Protocol/MpService.h(459) : error C2059: syntax error : ‘;’
c:\edk\MdePkg\Include\Protocol/MpService.h(459) : error C2059: syntax error : ‘,’
c:\edk\MdePkg\Include\Protocol/MpService.h(465) : error C2059: syntax error : ‘)’
c:\edk\MdePkg\Include\Protocol/MpService.h(623) : error C2061: syntax error : identifier ‘EFI_MP_SERVICES_STARTUP_ALL_APS’
c:\edk\MdePkg\Include\Protocol/MpService.h(624) : error C2061: syntax error : identifier ‘StartupThisAP’
c:\edk\MdePkg\Include\Protocol/MpService.h(624) : error C2059: syntax error : ‘;’
c:\edk\MdePkg\Include\Protocol/MpService.h(628) : error C2059: syntax error : ‘}’
NMAKE : fatal error U1077: ‘”C:\Program Files\Microsoft Visual Studio 9.0\Vc\bin\cl.exe”‘ : return code ‘0x2’
Stop.

参考:

1. Vol2_DXE CIS_1_3.pdf P174