蓝牙模块进入 AT 模式

一般的蓝牙模块,进入 AT 模式进行设置的方式很简单,就是直接用电线连接之后在PC上发送AT Command 即可。如果有问题,需要检查下面三个方面:
1. 供电,最好用自带供电的串口模块,用它直接给蓝牙模块供电;
2. 串口连接,RX/TX需要交叉,波特率需要匹配,一般默认都是9600
3. 发送命令需要特别的后缀,有些是回车,有些是换行,大多数是回车加换行。你可以直接使用 Arduino 的串口监视器
image002
如果用其他工具,那么需要十六进制发送,手工加上需要的后缀
4. 通常的模块都支持 AT 命令,建议用这个命令直接测试,应该能收到回复 OK

如果上述检查多次,仍然不响应 AT 命令,那么很可能是你用的模块需要特殊的方式才能进入AT 模式(也有称作“命令模式”)。比如,我在使用的蓝牙鼠标模块。我按照上述方法检查过无数次,最终还是再次研读Spec。发现有一组红色标记的字。
image004
转念一想,这个说的可能是这个模块一种特殊的状态,应该有什么方式能进入这个状态中,再回到前面仔细阅读,发现PIO3是很特别的引脚。
image006
我拿到的是已经焊接在底板上的蓝牙模块
image008
最后,需要用导线短路一下Pin12和Pin26,然后模块才能正常响应AT 命令。

所以,如果遇到了问题,最好认真阅读卖家提供的 Datasheet。其实,最快捷的还是直接问卖家,当然,大多数情况下你无法从卖家得到答案。

Step to UEFI (101)Application 驻留内存

前面一篇文章提到“提供服务的代码需要常驻内存,一般的 Application 是不会常驻内存的,而驱动直接可以常驻内存”。普通的Application 不能常驻内存,但是可以做个特殊的Application 来完成这个功能,之前的文章【参考1】我们尝试过写一个能够一直在 Shell 右上角显示当前时间的程序。结合前面的Protocol安装的驱动代码,我们做一个安装Protocol的Application。

#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 <Library/PrintLib.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 <Library/DebugLib.h>
#include <Protocol/LoadedImage.h>

#include "Print9.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;

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;

EFI_GUID gEfiPrint9ProtocolGuid=
	{ 0xf05976ef, 0x83f1, 0x4f3d, 
	{ 0x86, 0x19, 0xf7, 0x59, 0x5d, 0x41, 0xe5, 0x61 } };

#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)

CONST EFI_PRINT9_PROTOCOL mPrint9Protocol = {
  UnicodeBSPrint,
  UnicodeSPrint,
  UnicodeBSPrintAsciiFormat,
  UnicodeSPrintAsciiFormat,
  UnicodeValueToString,
  AsciiBSPrint,
  AsciiSPrint,
  AsciiBSPrintUnicodeFormat,
  AsciiSPrintUnicodeFormat,
  AsciiValueToString
};

EFI_LOADED_IMAGE_PROTOCOL          *ImageInfo = NULL;
  
typedef void (*Fun)();

void function()
{
  EFI_STATUS  Status;

  Status = gBS->InstallMultipleProtocolInterfaces (
                  &gImageHandle,
                  &gEfiPrint9ProtocolGuid, 
				  &mPrint9Protocol,
                  NULL
                  );
  ASSERT_EFI_ERROR (Status);
}


int
EFIAPI
main (                                         
  IN int Argc,
  IN char **Argv
  )
{
  EFI_STATUS                         Status = EFI_SUCCESS;
  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;
}

 

运行结果,和使用Driver方式的没有差别。
stu101

完整的代码下载:
appprotocol

参考:
1. http://www.lab-z.com/49str/ Step to UEFI (49) —– 内存驻留程序

WiDo 实现HTTP Server 的例子

最近在研究如何搭建一个 WIFI HTTP Server ,本打算使用ESP8266,但是购买了板子才发现资料比较少,联系卖家也没有结果。后来只得入手了一块 CC3000 的 WIFI 板子。这个芯片是 Arduino IDE 原生自带的,所以资料方面不是问题。入手的是 DFRobot 出品的 WiDo,主控芯片是 32U4,这意味着有足够的串口可供Debug之类使用, 更具体的说:主控芯片用 SPI 和 WIFI芯片CC3000打交道,然后多出来一个串口,USB上还有一个串口。

image001

根据【参考1】编写一个简单的程序,控制板子上 Pin 13 上面的LED亮灭。【参考1】的程序会向浏览器端发送一个 HTTP 的页面,上面有2个按钮,能够通过按下按钮控制亮灭。经过我的测试发现浏览器打卡会很慢,很多时候会卡死,并且打开之后页面是错乱的。我猜测原因可能是HTTP代码并不标准,有兼容性问题(我用的Chrome),另外用浏览器访问有可能会负载过高,比如,我偶然发现浏览器打开网站之后还有 GET favorite.ico 的动作。因此,我对程序做了一下简单的修改。修改之后HTTP页面只是简单显示一行字符。使用 http://ip/open 打开 LED,http://ip/close关闭LED。

1.直接访问 http://192.168.0.103 可以看到下面的信息

image002

具体的控制可以用 http://192.168.1.103/open 点亮,http://192.168.1.103/close 熄灭。
image003

2.此外,还可以用 curl 工具在控制台进行操作,和方法1 相比,这样的更简单可靠,避免浏览器“暗箱操作”

image004

#include <Adafruit_CC3000.h>
#include <SPI.h>
#include "utility/debug.h"
#include "utility/socket.h"

// These are the interrupt and control pins
#define ADAFRUIT_CC3000_IRQ   7  // MUST be an interrupt pin!
// These can be any two pins
#define ADAFRUIT_CC3000_VBAT  5
#define ADAFRUIT_CC3000_CS    10
// Use hardware SPI for the remaining pins
// On an UNO, SCK = 13, MISO = 12, and MOSI = 11
Adafruit_CC3000 cc3000 = Adafruit_CC3000(ADAFRUIT_CC3000_CS, ADAFRUIT_CC3000_IRQ, ADAFRUIT_CC3000_VBAT,
SPI_CLOCK_DIVIDER); // you can change this clock speed


#define WLAN_SSID       "ChinaNet-73"           //这里填写你的 WIFI  名称
#define WLAN_PASS       "adminp1988"            //这里填写你的 WIFI 密码  
// Security can be WLAN_SEC_UNSEC, WLAN_SEC_WEP, WLAN_SEC_WPA or WLAN_SEC_WPA2
#define WLAN_SECURITY   WLAN_SEC_WPA2

#define LISTEN_PORT           80   // What TCP port to listen on for connections.

Adafruit_CC3000_Server webServer(LISTEN_PORT);
boolean led_state;
//
void setup(void) {
        //简单起见,我们只用板载的 13pin 上的 LED 演示
        pinMode (13, OUTPUT);
		//默认是灭的
        digitalWrite (13, LOW);
        
		//使用串口输出Debug信息
        Serial.begin(115200);
        Serial.println(F("Hello, CC3000!\n")); 
        //while (!Serial);
        //Serial.println ("Input any key to start:");
        //while (!Serial.available ());
		//输出当前可用内存
        Serial.print("Free RAM: "); 
        Serial.println(getFreeRam(), DEC);

        /* Initialise the module */
        Serial.println(F("\nInitializing..."));
        if (!cc3000.begin()) {
                Serial.println(F("Couldn't begin()! Check your wiring?"));
                while(1);
        }

        Serial.print(F("\nAttempting to connect to ")); 
        Serial.println(WLAN_SSID);
        if (!cc3000.connectToAP(WLAN_SSID, WLAN_PASS, WLAN_SECURITY)) {
                Serial.println(F("Failed!"));
                while(1);
        }

        Serial.println(F("Connected!"));

		//使用 DHCP 分配的IP 
        Serial.println(F("Request DHCP"));
        while (!cc3000.checkDHCP()) {
                delay(100); // ToDo: Insert a DHCP timeout!
        }  
		
        //显示当前的IP信息
        /* Display the IP address DNS, Gateway, etc. */
        while (! displayConnectionDetails()) {
                delay(1000);
        }

        // Start listening for connections
        webServer.begin();
        Serial.println(F("Listening for connections..."));
}

//
void loop(void) {
        // Try to get a client which is connected.
        Adafruit_CC3000_ClientRef client = webServer.available();
        if (client) {
		        //处理输入的 GET 信息,对于 GET 方法来说,Url中既有传递的信息
                processInput (client);
				//对发送 HTTP 请求的浏览器发送HTTP代码
                sendWebPage (client);
        }
        client.close();
}

//分析收到的 GET 方法的参数
void processInput (Adafruit_CC3000_ClientRef client) {
        char databuffer[45];
      //安全起见,保证截断
       databuffer[44]=’\0’;
        while (client.available ()) {
                client.read (databuffer, 40);
	
	  //下面这个代码是查找PC端发送的数据中的换行,以此作为字符串的结尾
                char* sub = strchr (databuffer, '\r');
                if (sub > 0)
                        *sub = '\0';
                Serial.println (databuffer);
				
                //下面是解析 GET 方法提供的参数
	    //如果是 open 命令,那么点亮 LED
                if (strstr (databuffer, "open") != 0) {
                        Serial.println (F("clicked open"));
                        digitalWrite (13, HIGH); 
                        led_state = true;
                } 
	     //如果是 close 命令,那么熄灭 LED 
                else if (strstr (databuffer, "close") != 0) {
                        Serial.println (F("clicked close"));
                        digitalWrite (13, LOW);
                        led_state = false;
                }
                break;
        }
}

void sendWebPage (Adafruit_CC3000_ClientRef client) {
        //为了节省空间,这里只发送简单的提示字符
        webServer.write ("Waiting for command");
        delay (20);
        client.close();
}

//输出当前WIFI 设备通过 DHCP 取得的基本信息
bool displayConnectionDetails(void) {
        uint32_t ipAddress, netmask, gateway, dhcpserv, dnsserv;

        if(!cc3000.getIPAddress(&ipAddress, &netmask, &gateway, &dhcpserv, &dnsserv)) {
                Serial.println(F("Unable to retrieve the IP Address!\r\n"));
                return false;
        } 
        else {
                Serial.print(F("\nIP Addr: ")); 
                cc3000.printIPdotsRev(ipAddress);
                Serial.print(F("\nNetmask: ")); 
                cc3000.printIPdotsRev(netmask);
                Serial.print(F("\nGateway: ")); 
                cc3000.printIPdotsRev(gateway);
                Serial.print(F("\nDHCPsrv: ")); 
                cc3000.printIPdotsRev(dhcpserv);
                Serial.print(F("\nDNSserv: ")); 
                cc3000.printIPdotsRev(dnsserv);
                Serial.println();
                return true;
        }
}

 

最后,对于初学者来说个人强烈不推荐使用“深圳四博智联科技有限公司开发的一款基于乐鑫ESP8266的各种板子“,原因是:资料很少,基本上没有Support,初学者使用的话很可能走很多弯路。
对于WiDo 来说,因为使用的是 CC3000,这也是 Arduino 官方用的 Wifi 使用的芯片,因此资料很全。不得不承认一点,国内Arduino方面的硬件价格便宜,但是软件还是很弱,特别是基础性的研究。如果你的项目比较急,或者对于稳定性要求高,首选官方支持的设备。

参考:
1. 通过网页按钮控制台灯,wido做服务器http://www.dfrobot.com.cn/community/forum.php?mod=viewthread&tid=10001&highlight=wido

Step to UEFI (100)InstallProtocolInterface

前面介绍了很多“消费”Protocol的代码,这次试试自己生产一个 Protocol 试试。根据我的理解,产生的 protocol 可以挂接在任何的 Handle 上(DH 命令看到的都可以),提供服务的代码需要常驻内存,一般的 Application 是不会常驻内存的,驱动直接可以常驻内存。于是,我们实验编写一个驱动程序,使用 Load 来加载驱动产生自定义的 Protocol ,挂接在自身Image 的Handle上。
需要使用的主要服务是 InstallProtocolInterface,我们先看看它的定义和输入参数:

typedef
EFI_STATUS
(EFIAPI *EFI_INSTALL_PROTOCOL_INTERFACE)(
  IN OUT EFI_HANDLE               *Handle,          //安装到哪个Handle上
  IN     EFI_GUID                 *Protocol,               //安装的Protocol 的名字
  IN     EFI_INTERFACE_TYPE       InterfaceType,  ,  //目前只有一种类型:EFI_NATIVE_INTERFACE
  IN     VOID                     *Interface                  //Protocol的实例
  );

 

实际上,代码中更加常见的是InstallMultipleProtocolInterfaces, 这是因为 “ Installs a protocol interface on a device handle. If the handle does not exist, it is created and added to the list of handles in the system. InstallMultipleProtocolInterfaces() performs more error checking than InstallProtocolInterface(), so it is recommended that InstallMultipleProtocolInterfaces() be used in place of InstallProtocolInterface()” 因此,我们代码中会使用 InstallMultipleProtocolInterfaces 而不是前面提到的 InstallProtocolInterface。

/**
  Installs one or more protocol interfaces into the boot services environment.

  @param[in, out]  Handle       The pointer to a handle to install the new protocol interfaces on,
                                or a pointer to NULL if a new handle is to be allocated.
  @param  ...                   A variable argument list containing pairs of protocol GUIDs and protocol
                                interfaces.

  @retval EFI_SUCCESS           All the protocol interface was installed.
  @retval EFI_OUT_OF_RESOURCES  There was not enough memory in pool to install all the protocols.
  @retval EFI_ALREADY_STARTED   A Device Path Protocol instance was passed in that is already present in
                                the handle database.
  @retval EFI_INVALID_PARAMETER Handle is NULL.
  @retval EFI_INVALID_PARAMETER Protocol is already installed on the handle specified by Handle.

**/
typedef
EFI_STATUS
(EFIAPI *EFI_INSTALL_MULTIPLE_PROTOCOL_INTERFACES)(
  IN OUT EFI_HANDLE           *Handle,
  ...
  );

 

InstallMultipleProtocolInterfaces 服务支持一次性安装多个Protocol,所以接收的变量是可变数量的,看起来让人有眼晕的感觉。最简单的方法是照葫芦画瓢,我们在UDK2014的代码中找一下我们的“葫芦”。在MdeModulePkg中,有一个 PrintDxe,它会向系统中注册名称为Print2Protocol 的 Protocol。

先分析一下他的代码结构。主要文件有3个:

1. \MdeModulePkg\Universal\PrintDxe\PrintDxe.inf 可以看做是工程文件,特别注意,其中有用到gEfiPrint2ProtocolGuid 在 MdeModulePkg.dec 中有定义:

  ## Print protocol defines basic print functions to print the format unicode and ascii string.
  # Include/Protocol/Print2.h
  gEfiPrint2ProtocolGuid          = { 0xf05976ef, 0x83f1, 0x4f3d, { 0x86, 0x19, 0xf7, 0x59, 0x5d, 0x41, 0xe5, 0x38 } }

 

2 \MdeModulePkg\Universal\PrintDxe\Print.c 其中有这个 Protocol的实例,还有入口函数PrintEntryPoint。其中使用了InstallMultipleProtocolInterfaces 将mPrint2Protocol 安装到了mPrintThunkHandle(==0) 上面。

3. \MdeModulePkg\Include\Protocol\Print2.h 定义了提供的 Protocol 的结构。

struct _EFI_PRINT2_PROTOCOL {
  UNICODE_BS_PRINT                     UnicodeBSPrint;
  UNICODE_S_PRINT                      UnicodeSPrint;
  UNICODE_BS_PRINT_ASCII_FORMAT        UnicodeBSPrintAsciiFormat;
  UNICODE_S_PRINT_ASCII_FORMAT         UnicodeSPrintAsciiFormat;
  UNICODE_VALUE_TO_STRING              UnicodeValueToString;
  ASCII_BS_PRINT                       AsciiBSPrint;
  ASCII_S_PRINT                        AsciiSPrint;
  ASCII_BS_PRINT_UNICODE_FORMAT        AsciiBSPrintUnicodeFormat;
  ASCII_S_PRINT_UNICODE_FORMAT         AsciiSPrintUnicodeFormat;
  ASCII_VALUE_TO_STRING                AsciiValueToString;
};

 

更多的介绍可以在【参考1】中看到。
之后就可以尝试进行编译,需要在 MdeModulePkg.dec 中声明一次 gEfiPrint9ProtocolGuid ,注意我修改了GUID保证不会和原来的发生冲突。再在MdeModulePkg.dsc 中加入 PrintDxe.inf 加入的位置和我们之前编译过的GOP旋转驱动还有截图的驱动位置一样。

编译之后的结果可以直接在 NT32 模拟器下试验:
st1001
加载之后可以发现用 DH 命令列出当前的 Handle会多了一个image(如果我们再Load一次,那么还会多出来一个Image,我们的代码不完善,没有自动查找确认当前是否已经执行过一次的功能)
stu1002

我们编写一个简单的Application来调用这个 Protocol 测试:

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

#include "Print9.h"

EFI_GUID gEfiPrint9ProtocolGuid =
		{ 0xf05976ef, 0x83f1, 0x4f3d, 
			{ 0x86, 0x19, 0xf7, 0x59, 
				0x5d, 0x41, 0xe5, 0x61 } };

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

int
EFIAPI
main (
  IN int Argc,
  IN CHAR16 **Argv
  )
{
	EFI_PRINT9_PROTOCOL		*Print9Protocol;
	EFI_STATUS				Status;	
	CHAR16					*Buffer=L"12345678";
	// Search for the Print9 Protocol
    //
    Status = gBS->LocateProtocol(
      &gEfiPrint9ProtocolGuid,
      NULL,
      (VOID **)&Print9Protocol
     );
    if (EFI_ERROR(Status)) {
      Print9Protocol = NULL;
	  Print(L"Can't find Print9Protocol.\n");
	  return EFI_SUCCESS;
     }
	Print(L"Find Print9Protocol.\n"); 
	Print9Protocol->UnicodeSPrint(Buffer,8,L"%d",200);
	Print(L"%s\n",Buffer); 
	
	return EFI_SUCCESS;
}

 

运行结果:
stu1003
首先运行的时候找不到对应的 Protocol,需要Load一次,再次运行就可以正常运行了。
本文提到的完整代码下载:

pdt

printdriver

参考:
1. http://feishare.com/attachments/065_EFI%20Howto,%20%20%20%E6%B3%A8%E5%86%8C%EF%BC%8C%E5%8F%91%E5%B8%83%EF%BC%8C%E4%BD%BF%E7%94%A8%E8%87%AA%E5%AE%9A%E4%B9%89%E7%9A%84protocol.pdf EFI Howto ,注册,发布,使用自定义的protocol

Step to UEFI (99)使用 Protocol的总结

前面很多程序一直在使用各种 Protocol 提供的服务,《UEFI 原理与编程》第五章是关于 UEFI基础服务的,正好借着本章,总结一下如何查找和调用 Protocol。

要使用Protocol,首先要知道使用哪个 Protocol ,它的名字就是 GUID。比如:gEfLoadedImageProtocolGuid ,他是 EFI_GUID类型。

启动服务(Boot Services)提供了下面一些服务用来查找指定的 Protocol。

OpenProtocol 打开指定句柄上面的 Protocol
HandleProtocol 上面的OpenProtocol对于参数要求比较复杂,HandleProtocol可以看做是前者的简化版本。
LocateProtocol 用于找出给定的 Protocol 在系统中的第一个实例。当你确定系统中只有一个 Protocol 的时候可以使用这个函数。比如,查找系统中的 EfiShellProtocol
LocateHandleBuffer 和上面的类似,差别在于这个函数能够给出系统中所有的支持给定 Protocol 设备的 Handle。比如,找到系统中所有支持 BlockIO 的设备

下面分别介绍每个函数:
1. OpenProtocol 原型定义可以在这支文件中看到 \MdePkg\Include\Uefi\UefiSpec.h

/**
  Queries a handle to determine if it supports a specified protocol. If the protocol is supported by the
  handle, it opens the protocol on behalf of the calling agent.

  @param[in]   Handle           The handle for the protocol interface that is being opened.
  @param[in]   Protocol         The published unique identifier of the protocol.
  @param[out]  Interface        Supplies the address where a pointer to the corresponding Protocol
                                Interface is returned.
  @param[in]   AgentHandle      The handle of the agent that is opening the protocol interface
                                Specified by Protocol and Interface.
  @param[in]   ControllerHandle If the agent that is opening a protocol is a driver that follows the
                                UEFI Driver Model, then this parameter is the controller handle
                                that requires the protocol interface. If the agent does not follow
                                the UEFI Driver Model, then this parameter is optional and may
                                be NULL.
  @param[in]   Attributes       The open mode of the protocol interface specified by Handle
                                and Protocol.

  @retval EFI_SUCCESS           An item was added to the open list for the protocol interface, and the
                                protocol interface was returned in Interface.
  @retval EFI_UNSUPPORTED       Handle does not support Protocol.
  @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
  @retval EFI_ACCESS_DENIED     Required attributes can't be supported in current environment.
  @retval EFI_ALREADY_STARTED   Item on the open list already has requierd attributes whose agent
                                handle is the same as AgentHandle.

**/
typedef
EFI_STATUS
(EFIAPI *EFI_OPEN_PROTOCOL)(
  IN  EFI_HANDLE                Handle, //指定在哪个 Handle 上查找 Protocol 
  IN  EFI_GUID                  *Protocol,//查找哪个 Protocol
  OUT VOID                      **Interface, OPTIONAL //返回打开的 Protocol 对象
  IN  EFI_HANDLE                AgentHandle, //打开此 Protocol 的Image(书上说的,我并不理解)
  IN  EFI_HANDLE                ControllerHandle, //使用此 Protocol的控制器
  IN  UINT32                    Attributes     //打开 Protocol 的方式
  );

 

2. HandleProtocol 和前面的那个函数相比,调用参数上简单多了。 原型定义可以在这支文件中看到 \MdePkg\Include\Uefi\UefiSpec.h

/**
  Queries a handle to determine if it supports a specified protocol.

  @param[in]   Handle           The handle being queried.
  @param[in]   Protocol         The published unique identifier of the protocol.
  @param[out]  Interface        Supplies the address where a pointer to the corresponding Protocol
                                Interface is returned.

  @retval EFI_SUCCESS           The interface information for the specified protocol was returned.
  @retval EFI_UNSUPPORTED       The device does not support the specified protocol.
  @retval EFI_INVALID_PARAMETER Handle is NULL.
  @retval EFI_INVALID_PARAMETER Protocol is NULL.
  @retval EFI_INVALID_PARAMETER Interface is NULL.

**/
typedef
EFI_STATUS
(EFIAPI *EFI_HANDLE_PROTOCOL)(
  IN  EFI_HANDLE               Handle,   //打开 Handle上面的 
  IN  EFI_GUID                 *Protocol, //GUID给定的Protocol
  OUT VOID                     **Interface //打开之后放在这里
  );

 

3. LocateProtocol 前面两个函数都是用来打开特定 Handle 上的Protocol,如果我们不知道需要的Protocol 在哪个Handle上,就可以用这个函数。调用参数上简单多了. 原型定义可以在这支文件中看到\MdePkg\Include\Uefi\UefiSpec.h

/**
  Returns the first protocol instance that matches the given protocol.

  @param[in]  Protocol          Provides the protocol to search for.
  @param[in]  Registration      Optional registration key returned from
                                RegisterProtocolNotify().
  @param[out]  Interface        On return, a pointer to the first interface that matches Protocol and
                                Registration.

  @retval EFI_SUCCESS           A protocol instance matching Protocol was found and returned in
                                Interface.
  @retval EFI_NOT_FOUND         No protocol instances were found that match Protocol and
                                Registration.
  @retval EFI_INVALID_PARAMETER Interface is NULL.

**/
typedef
EFI_STATUS
(EFIAPI *EFI_LOCATE_PROTOCOL)(
  IN  EFI_GUID  *Protocol,                //要打开的 Protocol
  IN  VOID      *Registration, OPTIONAL  //从 RegisgerProtocolNotify() 获得的Key (不懂)
  OUT VOID      **Interface     //返回找到的对个匹配的 Protocol 实例
  );

 

4. LocateHandleBuffer 这个函数用来找出支持某个 Protocol的所有设备, 原型定义可以在这支文件中看到 \MdePkg\Include\Uefi\UefiSpec.h

/**
  Returns an array of handles that support a specified protocol.

  @param[in]       SearchType   Specifies which handle(s) are to be returned.
  @param[in]       Protocol     Specifies the protocol to search by.
  @param[in]       SearchKey    Specifies the search key.
  @param[in, out]  BufferSize   On input, the size in bytes of Buffer. On output, the size in bytes of
                                the array returned in Buffer (if the buffer was large enough) or the
                                size, in bytes, of the buffer needed to obtain the array (if the buffer was
                                not large enough).
  @param[out]      Buffer       The buffer in which the array is returned.

  @retval EFI_SUCCESS           The array of handles was returned.
  @retval EFI_NOT_FOUND         No handles match the search.
  @retval EFI_BUFFER_TOO_SMALL  The BufferSize is too small for the result.
  @retval EFI_INVALID_PARAMETER SearchType is not a member of EFI_LOCATE_SEARCH_TYPE.
  @retval EFI_INVALID_PARAMETER SearchType is ByRegisterNotify and SearchKey is NULL.
  @retval EFI_INVALID_PARAMETER SearchType is ByProtocol and Protocol is NULL.
  @retval EFI_INVALID_PARAMETER One or more matches are found and BufferSize is NULL.
  @retval EFI_INVALID_PARAMETER BufferSize is large enough for the result and Buffer is NULL.

**/
typedef
EFI_STATUS
(EFIAPI *EFI_LOCATE_HANDLE)(
  IN     EFI_LOCATE_SEARCH_TYPE   SearchType,   //查找方法
  IN     EFI_GUID                 *Protocol,    OPTIONAL //指定的 Protocol
  IN     VOID                     *SearchKey,   OPTIONAL   //PROTOCOL_NOTIFY 类型
  IN OUT UINTN                    *BufferSize,                  //找到的Handle数量
  OUT    EFI_HANDLE               *Buffer                       //返回的 Handle Buffer 
  );

 

其中EFI_LOCATE_SEARCH_TYPE 可以定义为下面几种类型

///
/// Enumeration of EFI Locate Search Types
///
typedef enum {
  ///
  /// Retrieve all the handles in the handle database.
  ///
  AllHandles,               //返回当前系统中的全部 Handle
  ///
  /// Retrieve the next handle fron a RegisterProtocolNotify() event.
  ///
  ByRegisterNotify, //搜索从 RegisterProtocolNodify 中找出匹配SearchKey的 Handle
  ///
  /// Retrieve the set of handles from the handle database that support a 
  /// specified protocol.
  ///
  ByProtocol            //返回系统Handle数据可中支持指定Protocol的HANDLE
} EFI_LOCATE_SEARCH_TYPE;


 

Adarfruit的触摸库

之前我介绍过一个MPR121 触摸传感器模块的库【参考1】,这次再介绍另外一个实现同样功能的库,差别在于这个新的库可以从Arduino IDE中直接下载到:
image001
国内买到的板子都是根据 Adafruit仿造的,下图就是 Adafruit的原图
image002
引出的 12个触摸脚,还有如下控制pin:
1.IRQ 可以做到按下时同时 Arduino
2.SDA/SCL I2C通讯用的
3.ADDR: 作用是选择I2C的地址,模块本身有下拉电阻,这里可以不接。默认地址是0x5B
image003
image004

4.GND 地
5. 3.3V 电源
6. VIN 模块本身有一个电压转换芯片,可以接入 5V
image005
运行库自带的例子,接线上只要接3.3V GND SDA SCL 四根线即可,这个库本身是用轮询的方法来处理引脚的触摸信息的。
代码如下:

/*********************************************************
This is a library for the MPR121 12-channel Capacitive touch sensor

Designed specifically to work with the MPR121 Breakout in the Adafruit shop 
  ----> https://www.adafruit.com/products/

These sensors use I2C communicate, at least 2 pins are required 
to interface

Adafruit invests time and resources providing this open source code, 
please support Adafruit and open-source hardware by purchasing 
products from Adafruit!

Written by Limor Fried/Ladyada for Adafruit Industries.  
BSD license, all text above must be included in any redistribution
**********************************************************/

#include <Wire.h>
#include "Adafruit_MPR121.h"

// You can have up to 4 on one i2c bus but one is enough for testing!
Adafruit_MPR121 cap = Adafruit_MPR121();

// Keeps track of the last pins touched
// so we know when buttons are 'released'
uint16_t lasttouched = 0;
uint16_t currtouched = 0;

void setup() {
  while (!Serial);        // needed to keep leonardo/micro from starting too fast!

  Serial.begin(9600);
  Serial.println("Adafruit MPR121 Capacitive Touch sensor test"); 
  
  // Default address is 0x5A, if tied to 3.3V its 0x5B
  // If tied to SDA its 0x5C and if SCL then 0x5D
  if (!cap.begin(0x5A)) {
    Serial.println("MPR121 not found, check wiring?");
    while (1);
  }
  Serial.println("MPR121 found!");
}

void loop() {
  // Get the currently touched pads
  currtouched = cap.touched();
  
  for (uint8_t i=0; i<12; i++) {
    // it if *is* touched and *wasnt* touched before, alert!
    if ((currtouched & _BV(i)) && !(lasttouched & _BV(i)) ) {
      Serial.print(i); Serial.println(" touched");
    }
    // if it *was* touched and now *isnt*, alert!
    if (!(currtouched & _BV(i)) && (lasttouched & _BV(i)) ) {
      Serial.print(i); Serial.println(" released");
    }
  }

  // reset our state
  lasttouched = currtouched;

  // comment out this line for detailed data from the sensor!
  return;
  
  // debugging info, what
  Serial.print("\t\t\t\t\t\t\t\t\t\t\t\t\t 0x"); Serial.println(cap.touched(), HEX);
  Serial.print("Filt: ");
  for (uint8_t i=0; i<12; i++) {
    Serial.print(cap.filteredData(i)); Serial.print("\t");
  }
  Serial.println();
  Serial.print("Base: ");
  for (uint8_t i=0; i<12; i++) {
    Serial.print(cap.baselineData(i)); Serial.print("\t");
  }
  Serial.println();
  
  // put a delay so it isn't overwhelming
  delay(100);
}

 

运行结果:
image006
需要注意的是模块的地址问题,根据DataSheet ADDR 如果接到GND,地址应该是0x5B ,但是实际上是 0x5A。
参考:
1. http://www.lab-z.com/mpr121/ MPR121 触摸传感器模块
2. https://www.adafruit.com/products/1982

Step to UEFI (98)Shell加载EFI的分析

继续阅读《UEFI 原理与编程》,其中的第三章介绍了一个 EFI Application如何在 Shell下加载运行的。ShellProtocol.c中的InternalShellExecuteDevicePath 是具体实现这个功能的函数。
函数头:

/**
  internal worker function to load and run an image via device path.

  @param ParentImageHandle      A handle of the image that is executing the specified
                                command line.
  @param DevicePath             device path of the file to execute
  @param CommandLine            Points to the NULL-terminated UCS-2 encoded string
                                containing the command line. If NULL then the command-
                                line will be empty.
  @param Environment            Points to a NULL-terminated array of environment
                                variables with the format 'x=y', where x is the
                                environment variable name and y is the value. If this
                                is NULL, then the current shell environment is used.
                            
  @param[out] StartImageStatus  Returned status from gBS->StartImage.

  @retval EFI_SUCCESS       The command executed successfully. The  status code
                            returned by the command is pointed to by StatusCode.
  @retval EFI_INVALID_PARAMETER The parameters are invalid.
  @retval EFI_OUT_OF_RESOURCES Out of resources.
  @retval EFI_UNSUPPORTED   Nested shell invocations are not allowed.
**/
EFI_STATUS
EFIAPI
InternalShellExecuteDevicePath(
  IN CONST EFI_HANDLE               *ParentImageHandle,
  IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath,
  IN CONST CHAR16                   *CommandLine OPTIONAL,
  IN CONST CHAR16                   **Environment OPTIONAL,
  OUT EFI_STATUS                    *StartImageStatus OPTIONAL
  )

 

第一步,先将 EFI Application加载到内存中,生成Image对象。取得这个对象的句柄为 NewHandle。

  //
  // Load the image with:
  // FALSE - not from boot manager and NULL, 0 being not already in memory
  //
  Status = gBS->LoadImage(
    FALSE,
    *ParentImageHandle,
    (EFI_DEVICE_PATH_PROTOCOL*)DevicePath,
    NULL,
    0,
    &NewHandle);

  if (EFI_ERROR(Status)) {
    if (NewHandle != NULL) {
      gBS->UnloadImage(NewHandle);
    }
    return (Status);
  }

第二步,取得命令行参数,将命令行的参数交给 NewHandle

 Status = gBS->OpenProtocol(
    NewHandle,
    &gEfiLoadedImageProtocolGuid,
    (VOID**)&LoadedImage,
    gImageHandle,
    NULL,
    EFI_OPEN_PROTOCOL_GET_PROTOCOL);

  if (!EFI_ERROR(Status)) {
    ASSERT(LoadedImage->LoadOptionsSize == 0);
    if (NewCmdLine != NULL) {
      LoadedImage->LoadOptionsSize  = (UINT32)StrSize(NewCmdLine);
      LoadedImage->LoadOptions      = (VOID*)NewCmdLine;
    }

 

看到这里我有一个问题:前面加载之后,为什么第二步就能够在被加载的Image上找到“EFI_Loaded_Image_Protocol”? 带着这样的问题追踪了代码。首先要找到 gBS->LoadImage 的真正实现代码,在\MdeModulePkg\Core\Dxe\DxeMain\DxeMain.c有定义,

//
// DXE Core Module Variables
//
EFI_BOOT_SERVICES mBootServices = {
  {
.................
.................
  (EFI_IMAGE_LOAD)                              CoreLoadImage,                            // LoadImage
.................
.................
}

 

再追CoreLoadImage代码在 \MdeModulePkg\Core\Dxe\Image\Image.c

/**
  Loads an EFI image into memory and returns a handle to the image.

  @param  BootPolicy              If TRUE, indicates that the request originates
                                  from the boot manager, and that the boot
                                  manager is attempting to load FilePath as a
                                  boot selection.
  @param  ParentImageHandle       The caller's image handle.
  @param  FilePath                The specific file path from which the image is
                                  loaded.
  @param  SourceBuffer            If not NULL, a pointer to the memory location
                                  containing a copy of the image to be loaded.
  @param  SourceSize              The size in bytes of SourceBuffer.
  @param  ImageHandle             Pointer to the returned image handle that is
                                  created when the image is successfully loaded.

  @retval EFI_SUCCESS             The image was loaded into memory.
  @retval EFI_NOT_FOUND           The FilePath was not found.
  @retval EFI_INVALID_PARAMETER   One of the parameters has an invalid value.
  @retval EFI_UNSUPPORTED         The image type is not supported, or the device
                                  path cannot be parsed to locate the proper
                                  protocol for loading the file.
  @retval EFI_OUT_OF_RESOURCES    Image was not loaded due to insufficient
                                  resources.
  @retval EFI_LOAD_ERROR          Image was not loaded because the image format was corrupt or not
                                  understood.
  @retval EFI_DEVICE_ERROR        Image was not loaded because the device returned a read error.
  @retval EFI_ACCESS_DENIED       Image was not loaded because the platform policy prohibits the 
                                  image from being loaded. NULL is returned in *ImageHandle.
  @retval EFI_SECURITY_VIOLATION  Image was loaded and an ImageHandle was created with a 
                                  valid EFI_LOADED_IMAGE_PROTOCOL. However, the current 
                                  platform policy specifies that the image should not be started.

**/
EFI_STATUS
EFIAPI
CoreLoadImage (
  IN BOOLEAN                    BootPolicy,
  IN EFI_HANDLE                 ParentImageHandle,
  IN EFI_DEVICE_PATH_PROTOCOL   *FilePath,
  IN VOID                       *SourceBuffer   OPTIONAL,
  IN UINTN                      SourceSize,
  OUT EFI_HANDLE                *ImageHandle
  )
{
  EFI_STATUS    Status;
  UINT64        Tick;
  EFI_HANDLE    Handle;

  Tick = 0;
  PERF_CODE (
    Tick = GetPerformanceCounter ();
  );

  Status = CoreLoadImageCommon (
             BootPolicy,
             ParentImageHandle,
             FilePath,
             SourceBuffer,
             SourceSize,
             (EFI_PHYSICAL_ADDRESS) (UINTN) NULL,
             NULL,
             ImageHandle,
             NULL,
             EFI_LOAD_PE_IMAGE_ATTRIBUTE_RUNTIME_REGISTRATION | EFI_LOAD_PE_IMAGE_ATTRIBUTE_DEBUG_IMAGE_INFO_TABLE_REGISTRATION
             );

  Handle = NULL; 
  if (!EFI_ERROR (Status)) {
    //
    // ImageHandle will be valid only Status is success. 
    //
    Handle = *ImageHandle;
  }

  PERF_START (Handle, "LoadImage:", NULL, Tick);
  PERF_END (Handle, "LoadImage:", NULL, 0);

  return Status;
}
在CoreLoadImageCommon 中可以找到加载安装EFI_Loaded_Image_Protocol 的代码。
  //
  //Reinstall loaded image protocol to fire any notifications
  //
  Status = CoreReinstallProtocolInterface (
             Image->Handle,
             &gEfiLoadedImageProtocolGuid,
             &Image->Info,
             &Image->Info
             );
  if (EFI_ERROR (Status)) {
    goto Done;
  }

 

就是说,在加载过程中,还要对Image 安装EFI_Loaded_Image_Protocol。因此,Load 的动作并不是简单的读取到内存中。

继续回到InternalShellExecuteDevicePath 代码中。

    //第三步,将修改好的ShellParamsProtocol 再安装到 Image上。
    //
    // Initialize and install a shell parameters protocol on the image.
    //
    ShellParamsProtocol.StdIn   = ShellInfoObject.NewShellParametersProtocol->StdIn;
    ShellParamsProtocol.StdOut  = ShellInfoObject.NewShellParametersProtocol->StdOut;
    ShellParamsProtocol.StdErr  = ShellInfoObject.NewShellParametersProtocol->StdErr;
    Status = UpdateArgcArgv(&ShellParamsProtocol, NewCmdLine, NULL, NULL);
    ASSERT_EFI_ERROR(Status);
    //
    // Replace Argv[0] with the full path of the binary we're executing:
    // If the command line was "foo", the binary might be called "foo.efi".
    // "The first entry in [Argv] is always the full file path of the
    //  executable" - UEFI Shell Spec section 2.3
    //
    ImagePath = EfiShellGetFilePathFromDevicePath (DevicePath);
    // The image we're executing isn't necessarily in a filesystem - it might
    // be memory mapped. In this case EfiShellGetFilePathFromDevicePath will
    // return NULL, and we'll leave Argv[0] as UpdateArgcArgv set it.
    if (ImagePath != NULL) {
      if (ShellParamsProtocol.Argv == NULL) {
        // Command line was empty or null.
        // (UpdateArgcArgv sets Argv to NULL when CommandLine is "" or NULL)
        ShellParamsProtocol.Argv = AllocatePool (sizeof (CHAR16 *));
        if (ShellParamsProtocol.Argv == NULL) {
          Status = EFI_OUT_OF_RESOURCES;
          goto UnloadImage;
        }
        ShellParamsProtocol.Argc = 1;
      } else {
        // Free the string UpdateArgcArgv put in Argv[0];
        FreePool (ShellParamsProtocol.Argv[0]);
      }
      ShellParamsProtocol.Argv[0] = ImagePath;
    }

    Status = gBS->InstallProtocolInterface(&NewHandle, &gEfiShellParametersProtocolGuid, EFI_NATIVE_INTERFACE, &ShellParamsProtocol);
    ASSERT_EFI_ERROR(Status);
//第四步,用 gBS->StartImage执行Image。
    //
    // now start the image and if the caller wanted the return code pass it to them...
    //
    if (!EFI_ERROR(Status)) {
      StartStatus      = gBS->StartImage(
                          NewHandle,
                          0,
                          NULL
                          );
      if (StartImageStatus != NULL) {
        *StartImageStatus = StartStatus;
      }

 

Step to UEFI (97)Shell下获得和设置环境变量

Shell 下可以通过 Set 命令获取和设置当前的环境变量。比如:在模拟环境下运行 Set 可以看到默认的 path 路径:
image001

这里介绍一下如何使用代码来实现这个功能。

首先介绍取得环境变量的函数,在 \ShellPkg\Include\Library\ShellLib.h 中可以看到他的原型。输入函数是要读取的环境变量名称。比如: path

/**
  Return the value of an environment variable.

  This function gets the value of the environment variable set by the
  ShellSetEnvironmentVariable function.

  @param[in] EnvKey             The key name of the environment variable.

  @retval NULL                  The named environment variable does not exist.
  @return != NULL               The pointer to the value of the environment variable.
**/
CONST CHAR16*
EFIAPI
ShellGetEnvironmentVariable (
  IN CONST CHAR16                *EnvKey
  );

 

其次,介绍一下设置环境变量的函数,同样在在 \ShellPkg\Include\Library\ShellLib.h 中可以看到他的原型。输入的参数是:环境变量名称,要设置的值,还有一个是该变量是否为易失的。如果设置为非易失,那么下次重新其中之后这个值还会存在Shell中。

/**
  Set the value of an environment variable.

  This function changes the current value of the specified environment variable. If the
  environment variable exists and the Value is an empty string, then the environment
  variable is deleted. If the environment variable exists and the Value is not an empty
  string, then the value of the environment variable is changed. If the environment
  variable does not exist and the Value is an empty string, there is no action. If the
  environment variable does not exist and the Value is a non-empty string, then the
  environment variable is created and assigned the specified value.

  This is not supported pre-UEFI Shell 2.0.

  @param[in] EnvKey             The key name of the environment variable.
  @param[in] EnvVal             The Value of the environment variable
  @param[in] Volatile           Indicates whether the variable is non-volatile (FALSE) or volatile (TRUE).

  @retval EFI_SUCCESS           The operation completed sucessfully
  @retval EFI_UNSUPPORTED       This operation is not allowed in pre-UEFI 2.0 Shell environments.
**/
EFI_STATUS
EFIAPI
ShellSetEnvironmentVariable (
  IN CONST CHAR16               *EnvKey,
  IN CONST CHAR16               *EnvVal,
  IN BOOLEAN                    Volatile
  );

 

编写一个测试的 Application

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

#include <Library/MemoryAllocationLib.h>
#include <Library/BaseMemoryLib.h>
#include <Protocol/UnicodeCollation.h>

#include  "ShellLib.h"

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

int
EFIAPI
main (
  IN int Argc,
  IN CHAR16 **Argv
  )
{
  EFI_STATUS          Status;
  CONST CHAR16		  *Result;
  
  Result=ShellGetEnvironmentVariable(L"path");
  if (Result!=NULL) {
		Print(L"Get path=[%s]\n",Result);
	}
  
  Status=ShellSetEnvironmentVariable(L"labz1",L"100",TRUE);
  if (EFI_ERROR(Status)) {
		Print(L"Set Env. variable error\n",Result);
	}  

  Status=ShellSetEnvironmentVariable(L"labz2",L"200",FALSE);
  if (EFI_ERROR(Status)) {
		Print(L"Set Env. variable error\n",Result);
	}  
	
  Result=ShellGetEnvironmentVariable(L"labz1");
  if (Result!=NULL) {
		Print(L"Get labz1=[%s]\n",Result);
	}	
  
  return EFI_SUCCESS;
}

 

需要特别注意的地方是:在 NT32 模拟环境下,ShellSetEnvironmentVariable 函数无法使用:

image003
上面的代码首先显示一下当前的 path 变量,之后设置2个变量分别是 lab1=100和lab2=200.
下面的都是在实体机上进行测试的(测试平台:Kabylake HDK Board)

image005

运行之后我们再检查环境变量可以看到 labz1 和 labz2 已经设置进去了。
image006
重启之后,再次检查可以看到变量labz2仍然存在
image007
比如,某天你需要在BIOS中加入一个重启动测试的代码,可以考虑将重启次数放在环境变量中。

完整的代码下载 envtest

Putty 的辅助工具 V3.0

spc3V1.0
因为工作的关系需要取得串口信息来Debug。之前在XP下使用的一款软件(我也不清楚具体应该叫做什么名字,因为我下载时它的名字是“好用的串口工具.exe”),如同他的名字一样,确实很好用。但是令人郁闷的是自从切换到了Windows7 64位系统下,这个工具不再工作正常。于是又开始寻找起来新的工具。

后来用了一段时间的 Putty 发现功能强大。美中不足是每次打开操作复杂,首先双击之后会弹出权限的窗口,其次需要手动输入com号,而因为我使用USB串口,经常因为插拔而导致端口的变化,于是每次还要去设备管理器重查看目前的端口,打开设备管理器还会弹出权限窗口……..真的很麻烦。

因为Putty是开源软件,就想试图修改去掉限制。再后来发现Putty一起发布的有一个功能比较单一的PuttyTel,并且可以通过命令行方式进行端口的指定和调用,最后就编写这个软件。使用方法非常简单:使用时将这个文件和PuttyTel放在同一个目录下,然后运行之后双击当前端口列表中出现的需要串口即可立即调用起来。

源程序在Source目录下。特别注意这是Delphi XE4 下面Build通过的,并没有使用XE4的新特性,如果你使用其他版本Delphi需要修改对应的头文件。

V2.0 升级了一下,可以自己设置波特率,也可以兼容更多的能接受命令行参数的串口软件。

V2.1 上一个版本会确认一下当前的命令行参数,对于普通用户来说是多余的。删掉这个重新编译。另外,调整代码将 JediAPILib.inc 放在源程序的目录下。还有就是多显示一位版本号,之前只能显示大版本,比如:2 修改之后可以显示 2.1 这样的版本号。

V2.2 根据 krman@biosren的建议,增加如果配置文件中保存了一个端口,并且这个端口现在仍然在列表中,倒计时三秒后启动该端口。

V2.2 根据 krman@biosren的建议,增加如果配置文件中保存了一个端口,并且这个端口现在仍然在列表中,倒计时三秒后启动该端口。

V3.0 根据 heyjude@biosren的建议,增加Log的功能。勾选 ‘Log To File’之后,根据当前时间生成一个Log文件。另外,我更换掉之前的 PuttyTel 工具,因为这个工具不支持命令行指定 log 文件名。替换为 Putty.exe 。

参考:

1.Putty的官方网站(因为安全问题请不要使用国内的Putty软件。)
http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html

2.关于Putty命令行调用方式来自
http://tartarus.org/~simon/putty-snapshots/htmldoc/Chapter3.html
例如:-serial com4 -sercfg 115200,8,n,1,N

-sessionlog 文件名, 指定使用的 Log文件名

下载serialportchooseriii

Step to UEFI (96)Build 参数介绍

最近在认真研读《UEFI 原理与编程》,强烈推荐这本书,希望深入学习UEFI 的朋友都应该购买一本反复阅读。

《UEFI 原理与编程》第二章提到了build 命令的常用参数,我认真考察了一下。在 CMD窗口下运行 build –h 可以获得支持的命令参数列表:

–version 显示build的版本然后退出
-h, –help 显示帮助信息然后退出
-a TARGETARCH, –arch=TARGETARCH 指定编译目标,可选的有IA32, X64, IPF, ARM, AARCH64 或者 EBC。这个参数会覆盖 target.txt中指定的TARGET_ARCH。我们经常使用的 –a IA32 编译出来的 Application,更换为 –a X64即可编译出X64平台下可以用的 Application。
  -p PLATFORMFILE, –platform=PLATFORMFILE

 

 

指令要编译的 package。这个参数会覆盖 target.txt中指定的 ACTIVE_PLATFORM。
  -m MODULEFILE, –module=MODULEFILE 编译指定的模块。就是编译指定 INF文件。
  -b BUILDTARGET, –buildtarget=BUILDTARGET 指定编译为 RELASE版本还是 DEBUG版本。这个参数会覆盖 target.txt中指定的TARGET。
  -t TOOLCHAIN, –tagname=TOOLCHAIN

 

指定使用的工具集。这个参数会覆盖 target.txt中的TOOL_CHAIN_TAG 定义.

 

 

-x SKUID, –sku-id=SKUID

 

 

使用的给出的SKU ID来进行编译,覆盖DSC文件中的SKUID_IDENTIFIER (没用过。检查UDK2015 Source Code这个参数定义为 DEFAULT)
  -n THREADNUMBER 指定编译中使用的线程数量。理论上说线程越多,编译速度越快。这个参数会覆盖target.txt中的MAX_CONCURRENT_THREAD_NUMBER
  -f FDFFILE, –fdf=FDFFILE

 

指定 FDF的名称。这个参数会覆盖DSC文件中指定的FDF文件名。
  -r ROMIMAGE, –rom-image=ROMIMAGE 指定生成的 FD文件名称。这个名称必须是FDF文件中[FD] 节中存在的文件名。
  -i FVIMAGE, –fv-image=FVIMAGE

file.

指定生成的FV文件名称。这个名称必须是FDF文件中[FV] 节中存在的名称。
  -C CAPNAME, –capsule-image=CAPNAME

 

 

指定生成的Capsule文件名称。这个名称必须是FDF文件中[Capsule] 节中存在的名称。Capsule 是一种专门用来升级的UEFI定义的格式。比如:有对应的BIOS Capsule 文件,在启动过程中你把对应的文件放在U盘上,BIOS会自动识别并且加载然后升级自身,这样的好处是刷新环境稳定,避免操作系统的影响,避免被破解的风险。坏处是实际操中挺麻烦,需要准备FAT32格式的U盘。
  -u, –skip-autogen 跳过 Autogen 的过程进行编译
  -e, –re-parse 只做分析和生成编译需要的准备工作,不做实际编译,相当于只做AutoGen。
  -c, –case-insensitive 不检查 INF文件中的大小写
  -w, –warning-as-error 将 Warning视作 Error
-j LOGFILE, –log=LOGFILE

 

将编译过程中输出到屏幕上的信息同样输入到文件中。如果信息太多,无法看到期望的出错信息不妨试试这个参数直接查看Log文件。
  -s, –silent 沉默模式执行NMAKE。
  -q, –quiet           Disable all messages except FATAL ERRORS.

 

 

关闭除了致命错误之外的全部信息显示。感觉和上面的 –s没有差别。
-v, –verbose

 

打开 verbose模式(完整信息),包括 library实例,dependency表达式,warning信息等等。
  -d DEBUG, –debug=DEBUG

 

 

看起来是控制编译期的Debug信息输出。
  -D MACROS, –define=MACROS

 

定义宏
-y REPORTFILE, –report-file=REPORTFILE

Create/overwrite the report to the specified filename.

 

创建/覆盖指定的报告。这个功能非常有用,可以生成一个 report文件,其中包括了项目定义的 PCD变量等等的信息。这对于查看一些编译期的参数非常有用。
  -Y REPORTTYPE, –report-type=REPORTTYPE

 

 

创建/覆盖指定内容的报告。内容范围在[PCD, LIBRARY, FLASH, DEPEX, BUILD_FLAGS, FIXED_ADDRESS, EXECUTION_ORDER]。
  -F FLAG, –flag=FLAG  Specify the specific option to parse EDK UNI file.

Must be one of: [-c, -s]. -c is for EDK framework UNI

file, and -s is for EDK UEFI UNI file. This option can

also be specified by setting *_*_*_BUILD_FLAGS in

[BuildOptions] section of platform DSC. If they are

both specified, this value will override the setting

in [BuildOptions] section of platform DSC.

 

 

指定处理uni文件的方法,可选项是[-c,-s]。-c是用来处理EDK架构的UNI;-s用来处理EDK架构的 UNI。我不清楚具体有什么差别,如果你了解不妨告诉我
-N, –no-cache 关闭缓存机制。在EDK编译过程中是要生成一个小型的数据库文件的。我之前就遇到过杀毒软件会误杀这个数据库导致无法正常编译的情况。
–conf=CONFDIRECTORY 指定 Conf 的目录。
  –check-usage

 

检查 INF中提到的Source文件是否存在。只是检查而已,没有其他动作。
  –ignore-sources      Focus to a binary build and ignore all source files 不使用源程序,强制使用编译生成的二进制文件继续编译。

 

上述是通过简单实验和个人理解进行翻译的,如果有不妥当欢迎直接评论中指出或者给我写邮件。万分感谢!

 

 

2019年2月12日 补充。上面提到的 -Y 参数查看 PCD 设定的例子(UDK2018):

build -x X64 -Y PCD -y zz.txt

运行结束后在根目录下可以看到 zz.txt。需要注意的是:

1.建议先编译一次,确定能跑过再继续;

2.zz.txt 是编译结束的时候才生成的。