Step to UEFI (25) —– 命令行参数为什么是Unicode

之前的文章【参考1】,提出了一个问题:为什么 CLIB 下面收到的参数 IN char **Argv 实际上是一个 Unicode ?

为了回答这个问题,还要在代码中寻找答案。同样,追踪一下当我们使用 CLIB 的时候,编译过程中程序被添加了什么。分析方法和之前的类似,我们最终得到下面这个结果:

_ModuleEntryPoint:\MdePkg\Library\UefiApplicationEntryPoint\ApplicationEntryPoint.c 入口还是他

框架没变

ProcessLibraryConstructorList (1)
ProcessModuleEntryPointList (2)
ProcessLibraryDestructorList (3)

(1) ProcessLibraryConstructorList:\Build\AppPkg\DEBUG_VS2008\IA32\AppPkg\Applications\ArgTest\ArgTest\DEBUG\AutoGen.c

  Status = UefiRuntimeServicesTableLibConstructor (ImageHandle, SystemTable);  (1.1)
  Status = UefiBootServicesTableLibConstructor (ImageHandle, SystemTable);(1.2)
  Status = UefiLibConstructor (ImageHandle, SystemTable);	(1.3)
  Status = ShellLibConstructor (ImageHandle, SystemTable);	(1.4)
  Status = UefiHiiServicesLibConstructor (ImageHandle, SystemTable); 	(1.5)
  Status = __wchar_construct (ImageHandle, SystemTable);	(1.6)

 

(1.1) UefiRuntimeServicesTableLibConstructor :\MdePkg\Library\UefiRuntimeServicesTableLib\UefiRuntimeServicesTableLib.c

(1.2) UefiBootServicesTableLibConstructor :\MdePkg\Library\UefiBootServicesTableLib\UefiBootServicesTableLib.c

(1.3) UefiLibConstructor :\MdePkg\Library\UefiLib\UefiLib.c

(1.4) ShellLibConstructor :\ShellPkg\Library\UefiShellLib\UefiShellLib.c

ShellLibConstructorWorker //加载一些 Shell Protocol

(1.5) UefiHiiServicesLibConstructor :\MdeModulePkg\Library\UefiHiiServicesLib\UefiHiiServicesLib.c

(1.6) __wchar_construct :\StdLib\LibC\Wchar\ConsDecons.c

(2) ProcessModuleEntryPointList :\Build\AppPkg\DEBUG_VS2008\IA32\AppPkg\Applications\ArgTest\ArgTest\DEBUG\AutoGen.c

	return ShellCEntryLib (ImageHandle, SystemTable); (2.1)

 

(2.1) ShellCEntryLib : \ShellPkg\Library\UefiShellCEntryLib\UefiShellCEntryLib.c

ReturnFromMain = ShellAppMain (
                       EfiShellParametersProtocol->Argc,
                       EfiShellParametersProtocol->Argv
                      );  

 

(2.1.1) ShellAppMain : \StdLib\LibC\Main\Main.c

	ExitVal = (INTN)main( (int)Argc, (wchar_t **)Argv);

 

对照 map 文件可以看到这个main就是我们写的 ArgTest中的Main

0001:000007d0 _ShellGetEnvironmentVariable 00000a50 f UefiShellLib:UefiShellLib.obj
0001:000007eb _ShellIsFile 00000a6b f UefiShellLib:UefiShellLib.obj
0001:00000830 _UefiHiiServicesLibConstructor 00000ab0 f UefiHiiServicesLib:UefiHiiServicesLib.obj
0001:000008a8 _main 00000b28 f ArgTest:ArgTest.obj
0001:00000962 _GetPerformanceCounter 00000be2 f BaseTimerLibNullTemplate:TimerLibNull.obj
0001:00000967 _GetPerformanceCounterProperties 00000be7 f BaseTimerLibNullTemplate:TimerLibNull.obj

(3) ProcessLibraryDestructorList :\Build\AppPkg\DEBUG_VS2008\IA32\AppPkg\Applications\ArgTest\ArgTest\DEBUG\AutoGen.c

	  Status = __wchar_deconstruct (ImageHandle, SystemTable); (3.1)
	  Status = ShellLibDestructor (ImageHandle, SystemTable);  (3.2)

 

(3.1) __wchar_deconstruct :\StdLib\LibC\Wchar\ConsDecons.c
(3.2) ShellLibDestructor :\ShellPkg\Library\UefiShellLib\UefiShellLib.c

上面的调用关系可以用下面的图来简单总结一下

UEFICLIBAPP

====================================分割线====================================

我们在运行期确定是下面的代码来取得参数的

    //
    // try to get shell 1.0 interface instead.
    //
    Status = SystemTable->BootServices->OpenProtocol(ImageHandle,
                               &gEfiShellInterfaceGuid,
                               (VOID **)&EfiShellInterface,
                               ImageHandle,
                               NULL,
                               EFI_OPEN_PROTOCOL_GET_PROTOCOL
                              );
    if (!EFI_ERROR(Status)) {
      //
      // use shell 1.0 interface
      //
      ReturnFromMain = ShellAppMain (
                         EfiShellInterface->Argc,
                         EfiShellInterface->Argv
                        );
    } else {
      ASSERT(FALSE);
    }

 

对于这个EfiShellInterface ,我们可以在 《EFI Shell Developer’s Guide》 找到。

25

因此,可以看到,取出来的Argc就是CHAR16.

参考:

1.http://www.lab-z.com/step-to-uefi-15%EF%BC%89-%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%8F%82%E6%95%B0-again/

Step to UEFI (24) —– Print的换行问题

前面一篇介绍了 ConOut 的换行,然后问题就来了:为什么 Print 的String不需要 \n \r 呢?

这里继续分析:

首先看一下ClsTest.map

 0001:0000006d       _DebugAssert               000002cd f   BaseDebugLibNull:DebugLib.obj
 0001:0000006e       _DebugAssertEnabled        000002ce f   BaseDebugLibNull:DebugLib.obj
 0001:00000071       _InternalPrint             000002d1 f   UefiLib:UefiLibPrint.obj
 0001:000000b1       _Print                     00000311 f   UefiLib:UefiLibPrint.obj
 0001:000000cc       _InternalAllocatePool      0000032c f   UefiMemoryAllocationLib:MemoryAllocationLib.obj
 0001:000000f3       _UnicodeVSPrint            00000353 f   BasePrintLib:PrintLib.obj
 0001:00000112       _BasePrintLibFillBuffer    00000372 f   BasePrintLib:PrintLibInternal.obj

就是说 Print 是来自 UefiLibPrint.Obj,接下来搜索 UefiLibPrint 能找到2个,用实验的方法确定我们需要的是在 \MdePkg\Library\UefiLib\UefiLibPrint.c

INTN
EFIAPI
Print (
  IN CONST CHAR16  *Format,
  ...
  )
{
  VA_LIST Marker;
  UINTN   Return;

  VA_START (Marker, Format);

  Return = InternalPrint (Format, gST->ConOut, Marker);

  VA_END (Marker);

  return Return;
}

继续追 InternalPrint 发现它调用下面的语句

Return = UnicodeVSPrint (Buffer, BufferSize, Format, Marker);

而这个函数在 \MdePkg\Library\BasePrintLib\PrintLib.c 中

UINTN
EFIAPI
UnicodeVSPrint (
  OUT CHAR16        *StartOfBuffer,
  IN  UINTN         BufferSize,
  IN  CONST CHAR16  *FormatString,
  IN  VA_LIST       Marker
  )
{

  ASSERT_UNICODE_BUFFER (StartOfBuffer);
  ASSERT_UNICODE_BUFFER (FormatString);
  return BasePrintLibSPrintMarker ((CHAR8 *)StartOfBuffer, BufferSize >> 1, FORMAT_UNICODE | OUTPUT_UNICODE, (CHAR8 *)FormatString, Marker, NULL);
}

继续追踪 BasePrintLibSPrintMarker 发现他在 \MdePkg\Library\BasePrintLib\PrintLibInternal.c

其中有一个程序段,如下

    case '\r':
      Format += BytesPerFormatCharacter;
      FormatCharacter = ((*Format & 0xff) | (*(Format + 1) << 8)) & FormatMask;
      if (FormatCharacter == '\n') {
        //
        // Translate '\r\n' to '\r\n'
        //
        ArgumentString = "\r\n";
      } else {
        //
        // Translate '\r' to '\r'
        //
        ArgumentString = "\r";
        Format   -= BytesPerFormatCharacter;
      }
      break;

    case '\n':
      //
      // Translate '\n' to '\r\n' and '\n\r' to '\r\n'
      //
      ArgumentString = "\r\n";
      Format += BytesPerFormatCharacter;
      FormatCharacter = ((*Format & 0xff) | (*(Format + 1) << 8)) & FormatMask;
      if (FormatCharacter != '\r') {
        Format   -= BytesPerFormatCharacter;
      }
      break;

就是说,实际上他在检查字符串是否有 \n 和 \r如果有,那么用 \n \r 替换之(文件中有2处干这个事情的,第一个是在分析 “%”,第二个才是我们想要的)。为了验证,我们将上面这段替换的代码删除,重新编译,运行结果如下:

24

上面一次运行结果是修改之前,下面是修改之后。可以看到,当我们去掉那段自己添加 \n \r做结尾的代码之后,同样会出现只换行不移动到行首的问题。

结论:Print 之所以 \n 直接就能换行移动到行首,是因为他代码中有特殊处理。

Step to UEFI (23) —– ConOut ->OutputString 的换行问题

前面的一篇文章遇到了奇怪的问题,字符串输出看起来很不规整。于是研究一下为什么。

首先,试试 Application 是否也会有这样的显示问题,修改程序如下

#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/ShellLib.h>
#include <Library/UefiApplicationEntryPoint.h>

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

//
// Entry point function 
//
EFI_STATUS
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  Print(L"www.lab-z.com\n");
  gST->ConOut->OutputString(gST->ConOut,L"LABZ Test1\n");
  gST->ConOut->OutputString(gST->ConOut,L"LABZ Test2\n");
  gST->ConOut->OutputString(gST->ConOut,L"LABZ Test3\n");  
 
  return EFI_SUCCESS;
}

 

我们看到有同样的现象。

23a
为了查看汇编级别的程序,我们可以在 ClsTest.inf 加入下面的代码

[BuildOptions]
  MSFT:*_*_IA32_CC_FLAGS  = /Oi- /FAcs

 

真正有效的成分是 /FAcs,这让编译器在编译过程中生成C语言和汇编代码对应的中间文件。

再次编译之后我们可以在 \Build\AppPkg\DEBUG_VS2008\IA32\AppPkg\Applications\ClsTest\ClsTest 找到 ClsTest.cod

文件。这就是我们想要的。它的内容如下:

; Listing generated by Microsoft (R) Optimizing Compiler Version 15.00.30729.01 

	TITLE	c:\edk2\AppPkg\Applications\ClsTest\ClsTest.c
	.686P
	.XMM
	include listing.inc
	.model	flat

INCLUDELIB OLDNAMES

PUBLIC	??_C@_1BO@BCGMLOBC@?$AAw?$AAw?$AAw?$AA?4?$AAl?$AAa?$AAb?$AA?9?$AAz?$AA?4?$AAc?$AAo?$AAm?$AA?6?$AA?$AA@ ; `string'
PUBLIC	??_C@_1BI@GJIEKEJP@?$AAL?$AAA?$AAB?$AAZ?$AA?5?$AAT?$AAe?$AAs?$AAt?$AA1?$AA?6?$AA?$AA@ ; `string'
PUBLIC	??_C@_1BI@OPBANGDB@?$AAL?$AAA?$AAB?$AAZ?$AA?5?$AAT?$AAe?$AAs?$AAt?$AA2?$AA?6?$AA?$AA@ ; `string'
PUBLIC	??_C@_1BI@CEEMAFJE@?$AAL?$AAA?$AAB?$AAZ?$AA?5?$AAT?$AAe?$AAs?$AAt?$AA3?$AA?6?$AA?$AA@ ; `string'
;	COMDAT ??_C@_1BI@CEEMAFJE@?$AAL?$AAA?$AAB?$AAZ?$AA?5?$AAT?$AAe?$AAs?$AAt?$AA3?$AA?6?$AA?$AA@
CONST	SEGMENT
??_C@_1BI@CEEMAFJE@?$AAL?$AAA?$AAB?$AAZ?$AA?5?$AAT?$AAe?$AAs?$AAt?$AA3?$AA?6?$AA?$AA@ DB 'L'
	DB	00H, 'A', 00H, 'B', 00H, 'Z', 00H, ' ', 00H, 'T', 00H, 'e', 00H
	DB	's', 00H, 't', 00H, '3', 00H, 0aH, 00H, 00H, 00H ; `string'
CONST	ENDS
;	COMDAT ??_C@_1BI@OPBANGDB@?$AAL?$AAA?$AAB?$AAZ?$AA?5?$AAT?$AAe?$AAs?$AAt?$AA2?$AA?6?$AA?$AA@
CONST	SEGMENT
??_C@_1BI@OPBANGDB@?$AAL?$AAA?$AAB?$AAZ?$AA?5?$AAT?$AAe?$AAs?$AAt?$AA2?$AA?6?$AA?$AA@ DB 'L'
	DB	00H, 'A', 00H, 'B', 00H, 'Z', 00H, ' ', 00H, 'T', 00H, 'e', 00H
	DB	's', 00H, 't', 00H, '2', 00H, 0aH, 00H, 00H, 00H ; `string'
CONST	ENDS
;	COMDAT ??_C@_1BI@GJIEKEJP@?$AAL?$AAA?$AAB?$AAZ?$AA?5?$AAT?$AAe?$AAs?$AAt?$AA1?$AA?6?$AA?$AA@
CONST	SEGMENT
??_C@_1BI@GJIEKEJP@?$AAL?$AAA?$AAB?$AAZ?$AA?5?$AAT?$AAe?$AAs?$AAt?$AA1?$AA?6?$AA?$AA@ DB 'L'
	DB	00H, 'A', 00H, 'B', 00H, 'Z', 00H, ' ', 00H, 'T', 00H, 'e', 00H
	DB	's', 00H, 't', 00H, '1', 00H, 0aH, 00H, 00H, 00H ; `string'
CONST	ENDS
;	COMDAT ??_C@_1BO@BCGMLOBC@?$AAw?$AAw?$AAw?$AA?4?$AAl?$AAa?$AAb?$AA?9?$AAz?$AA?4?$AAc?$AAo?$AAm?$AA?6?$AA?$AA@
CONST	SEGMENT
??_C@_1BO@BCGMLOBC@?$AAw?$AAw?$AAw?$AA?4?$AAl?$AAa?$AAb?$AA?9?$AAz?$AA?4?$AAc?$AAo?$AAm?$AA?6?$AA?$AA@ DB 'w'
	DB	00H, 'w', 00H, 'w', 00H, '.', 00H, 'l', 00H, 'a', 00H, 'b', 00H
	DB	'-', 00H, 'z', 00H, '.', 00H, 'c', 00H, 'o', 00H, 'm', 00H, 0aH
	DB	00H, 00H, 00H				; `string'
PUBLIC	_UefiMain
; Function compile flags: /Ogspy
; File c:\edk2\apppkg\applications\clstest\clstest.c
;	COMDAT _UefiMain
_TEXT	SEGMENT
_UefiMain PROC						; COMDAT

; 22   :   Print(L"www.lab-z.com\n");

  00000	68 00 00 00 00	 push	 OFFSET ??_C@_1BO@BCGMLOBC@?$AAw?$AAw?$AAw?$AA?4?$AAl?$AAa?$AAb?$AA?9?$AAz?$AA?4?$AAc?$AAo?$AAm?$AA?6?$AA?$AA@
  00005	e8 00 00 00 00	 call	 _Print

; 23   :   gST->ConOut->OutputString(gST->ConOut,L"LABZ Test1\n");

  0000a	a1 00 00 00 00	 mov	 eax, DWORD PTR _gST
  0000f	8b 40 2c	 mov	 eax, DWORD PTR [eax+44]
  00012	c7 04 24 00 00
	00 00		 mov	 DWORD PTR [esp], OFFSET ??_C@_1BI@GJIEKEJP@?$AAL?$AAA?$AAB?$AAZ?$AA?5?$AAT?$AAe?$AAs?$AAt?$AA1?$AA?6?$AA?$AA@
  00019	50		 push	 eax
  0001a	ff 50 04	 call	 DWORD PTR [eax+4]

; 24   :   gST->ConOut->OutputString(gST->ConOut,L"LABZ Test2\n");

  0001d	a1 00 00 00 00	 mov	 eax, DWORD PTR _gST
  00022	8b 40 2c	 mov	 eax, DWORD PTR [eax+44]
  00025	68 00 00 00 00	 push	 OFFSET ??_C@_1BI@OPBANGDB@?$AAL?$AAA?$AAB?$AAZ?$AA?5?$AAT?$AAe?$AAs?$AAt?$AA2?$AA?6?$AA?$AA@
  0002a	50		 push	 eax
  0002b	ff 50 04	 call	 DWORD PTR [eax+4]

; 25   :   gST->ConOut->OutputString(gST->ConOut,L"LABZ Test3\n");  

  0002e	a1 00 00 00 00	 mov	 eax, DWORD PTR _gST
  00033	8b 40 2c	 mov	 eax, DWORD PTR [eax+44]
  00036	68 00 00 00 00	 push	 OFFSET ??_C@_1BI@CEEMAFJE@?$AAL?$AAA?$AAB?$AAZ?$AA?5?$AAT?$AAe?$AAs?$AAt?$AA3?$AA?6?$AA?$AA@
  0003b	50		 push	 eax
  0003c	ff 50 04	 call	 DWORD PTR [eax+4]
  0003f	83 c4 18	 add	 esp, 24			; 00000018H

; 26   :   
; 27   :   return EFI_SUCCESS;

  00042	33 c0		 xor	 eax, eax

; 28   : }

  00044	c3		 ret	 0
_UefiMain ENDP
END

 

特别注意到,编译后,我们定义的字符串汇编写成下面这样形式的Unicode字符串

CONST	SEGMENT
??_C@_1BI@CEEMAFJE@?$AAL?$AAA?$AAB?$AAZ?$AA?5?$AAT?$AAe?$AAs?$AAt?$AA3?$AA?6?$AA?$AA@ DB 'L'
	DB	00H, 'A', 00H, 'B', 00H, 'Z', 00H, ' ', 00H, 'T', 00H, 'e', 00H
	DB	's', 00H, 't', 00H, '3', 00H, 0aH, 00H, 00H, 00H ; `string'
CONST	ENDS

 

可以看到上面只有 0ah 这是换行的意思,并没有“换行然后切换到下行行首”的意思。

找到原因,我们可以加上切换到行首,就是下面这个样子

  gST->ConOut->OutputString(gST->ConOut,L"LABZ Test4\n\r");    
  gST->ConOut->OutputString(gST->ConOut,L"LABZ Test5\n\r");   

 

再编译查看生成的 COD 文件

CONST	SEGMENT
??_C@_1BK@FBECEOIH@?$AAL?$AAA?$AAB?$AAZ?$AA?5?$AAT?$AAe?$AAs?$AAt?$AA5?$AA?6?$AA?$AN?$AA?$AA@ DB 'L'
	DB	00H, 'A', 00H, 'B', 00H, 'Z', 00H, ' ', 00H, 'T', 00H, 'e', 00H
	DB	's', 00H, 't', 00H, '5', 00H, 0aH, 00H, 0dH, 00H, 00H, 00H ; `string'
CONST	ENDS
;	COMDAT ??_C@_1BK@JNOIEOBJ@?$AAL?$AAA?$AAB?$AAZ?$AA?5?$AAT?$AAe?$AAs?$AAt?$AA4?$AA?6?$AA?$AN?$AA?$AA@
CONST	SEGMENT
??_C@_1BK@JNOIEOBJ@?$AAL?$AAA?$AAB?$AAZ?$AA?5?$AAT?$AAe?$AAs?$AAt?$AA4?$AA?6?$AA?$AN?$AA?$AA@ DB 'L'
	DB	00H, 'A', 00H, 'B', 00H, 'Z', 00H, ' ', 00H, 'T', 00H, 'e', 00H
	DB	's', 00H, 't', 00H, '4', 00H, 0aH, 00H, 0dH, 00H, 00H, 00H ; `string'
CONST	ENDS
;	COMDAT ??_C@_1BI@CEEMAFJE@?$AAL?$AAA?$AAB?$AAZ?$AA?5?$AAT?$AAe?$AAs?$AAt?$AA3?$AA?6?$AA?$AA@
CONST	SEGMENT
??_C@_1BI@CEEMAFJE@?$AAL?$AAA?$AAB?$AAZ?$AA?5?$AAT?$AAe?$AAs?$AAt?$AA3?$AA?6?$AA?$AA@ DB 'L'
	DB	00H, 'A', 00H, 'B', 00H, 'Z', 00H, ' ', 00H, 'T', 00H, 'e', 00H
	DB	's', 00H, 't', 00H, '3', 00H, 0aH, 00H, 00H, 00H ; `string'
CONST	ENDS
;	COMDAT ??_C@_1BI@OPBANGDB@?$AAL?$AAA?$AAB?$AAZ?$AA?5?$AAT?$AAe?$AAs?$AAt?$AA2?$AA?6?$AA?$AA@
CONST	SEGMENT
??_C@_1BI@OPBANGDB@?$AAL?$AAA?$AAB?$AAZ?$AA?5?$AAT?$AAe?$AAs?$AAt?$AA2?$AA?6?$AA?$AA@ DB 'L'
	DB	00H, 'A', 00H, 'B', 00H, 'Z', 00H, ' ', 00H, 'T', 00H, 'e', 00H
	DB	's', 00H, 't', 00H, '2', 00H, 0aH, 00H, 00H, 00H ; `string'
CONST	ENDS
;	COMDAT ??_C@_1BI@GJIEKEJP@?$AAL?$AAA?$AAB?$AAZ?$AA?5?$AAT?$AAe?$AAs?$AAt?$AA1?$AA?6?$AA?$AA@
CONST	SEGMENT
??_C@_1BI@GJIEKEJP@?$AAL?$AAA?$AAB?$AAZ?$AA?5?$AAT?$AAe?$AAs?$AAt?$AA1?$AA?6?$AA?$AA@ DB 'L'
	DB	00H, 'A', 00H, 'B', 00H, 'Z', 00H, ' ', 00H, 'T', 00H, 'e', 00H
	DB	's', 00H, 't', 00H, '1', 00H, 0aH, 00H, 00H, 00H ; `string'
CONST	ENDS
;	COMDAT ??_C@_1BO@BCGMLOBC@?$AAw?$AAw?$AAw?$AA?4?$AAl?$AAa?$AAb?$AA?9?$AAz?$AA?4?$AAc?$AAo?$AAm?$AA?6?$AA?$AA@
CONST	SEGMENT
??_C@_1BO@BCGMLOBC@?$AAw?$AAw?$AAw?$AA?4?$AAl?$AAa?$AAb?$AA?9?$AAz?$AA?4?$AAc?$AAo?$AAm?$AA?6?$AA?$AA@ DB 'w'
	DB	00H, 'w', 00H, 'w', 00H, '.', 00H, 'l', 00H, 'a', 00H, 'b', 00H
	DB	'-', 00H, 'z', 00H, '.', 00H, 'c', 00H, 'o', 00H, 'm', 00H, 0aH
	DB	00H, 00H, 00H				; `string'

 

运行结果:

23b

最后,提一个问题,如果程序写成这个样子

EFI_STATUS
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  Print(L"www.lab-z.com\n");
  gST->ConOut->OutputString(gST->ConOut,L"LABZ Test1\r");
  gST->ConOut->OutputString(gST->ConOut,L"LABZ Test2\r");
  gST->ConOut->OutputString(gST->ConOut,L"LABZ Test3\n");  
  
  return EFI_SUCCESS;
}

 

输出结果应该是什么样的呢?

DHT11 测试

DHT11 是一款温度湿度传感器。具体可以看【参考1】。参考中使用的是单独的元件,而我使用的是做好的模块,因此不需要额外的电阻。

dht11

硬件方面,只有三根线,GND VCC 和OUT。 对照参考中的程序,很容易上手。

dht11r

下面的代码也是参考中的代码,只是修改了一下波特率为 9600,这样方便我们测试之后,直接打开串口监视。

#include "dht11.h"

dht11 DHT11;
#define DHT11PIN 3 //DHT11 PIN 3 连接UNO 3
 
void setup()
{
  Serial.begin(9600);
  Serial.println("DHT11 TEST PROGRAM ");
  Serial.print("LIBRARY VERSION: ");
  Serial.println(DHT11LIB_VERSION);
  Serial.println();
}
 
void loop()
{
  Serial.println("\n");
 
  int chk = DHT11.read(DHT11PIN);
 
  Serial.print("Read sensor: ");
  switch (chk)
  {
    case DHTLIB_OK: 
                Serial.println("OK"); 
                break;
    case DHTLIB_ERROR_CHECKSUM: 
                Serial.println("Checksum error"); 
                break;
    case DHTLIB_ERROR_TIMEOUT: 
                Serial.println("Time out error"); 
                break;
    default: 
                Serial.println("Unknown error"); 
                break;
  }
  Serial.print("Humidity (%): ");
  Serial.println((float)DHT11.humidity, 2);
  Serial.print("Temperature (oC): ");
  Serial.println((float)DHT11.temperature-2, 2);
  delay(2000);
}

 

简单测试一下,对着传感器吹一口气,数值会有变化

dht11test

完整代码下载

DHT11Test
参考:

1.http://www.geek-workshop.com/forum.php?mod=viewthread&tid=997&extra=&highlight=dht11&page=1 DHT11 测试

pmason_rose 改造的遥控小车

两年前,再次学习单片机,此次入手的单片机比它的爹妈强多了。不仅把rs232接口用usb硬连接到pc而且直接写其rom!(本人不是硬件专业人士先进东西见的少)。琢磨着做个啥当学习目标呢,做个pc控制的小车吧。首先得解决mcu对数据的处理,恩,定义个包头aa55加个数据长度字0xh加个命令字0xh,再加上数据字。差资料显示状态机方式处理最好。写完后写上位程序。嘿嘿masm32是我的最爱,不过3天调试成功!为了增加可玩性,哈加入tcp方式传输命令流。一切调试ok,接上小车,呵呵大告成功!上传视频共同分享快乐!

pmason_rose 联系方式 pmason_rose@qq.com(332779423)

Step to UEFI (22) —– Application的入口分析

研究一下:UEFI APP 在编译的时候会加入什么头。

使用上一次的示例程序 ClrTest。稍微修改一下,去掉清屏的调用以便我们能看清结果:

#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/ShellLib.h>
#include <Library/UefiApplicationEntryPoint.h>

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

//
// Entry point function 
//
EFI_STATUS
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
   Print(L"www.lab-z.com\n");
   return EFI_SUCCESS;
}

编译命令是 build -p AppPkg\AppPkg.dsc

首先查看生成的 Makefile

在 \Build\AppPkg\DEBUG_VS2008\IA32\AppPkg\Applications\ClsTest\ClsTest\Makefile

下面的语句指定了入口函数

DLINK_FLAGS = /NOLOGO /NODEFAULTLIB /IGNORE:4001 /OPT:REF /OPT:ICF=10 /MAP
/ALIGN:32 /SECTION:.xdata,D /SECTION:.pdata,D /MACHINE:X86 /LTCG /DLL
/ENTRY:$(IMAGE_ENTRY_POINT) /SUBSYSTEM:EFI_BOOT_SERVICE_DRIVER /SAFESEH:NO
/BASE:0 /DRIVER /DEBUG

同样的 Makefile中,给出入口函数

IMAGE_ENTRY_POINT = _ModuleEntryPoint

顺便看一眼 \Build\AppPkg\DEBUG_VS2008\IA32\AppPkg\Applications\ClsTest\ClsTest\OUTPUT\ClsTest.map

__ModuleEntryPoint 00000260 f UefiApplicationEntryPoint:ApplicationEntryPoint.obj

就是说 ModuleEntryPoint 是从 ApplicationEntryPoint.obj 中链接进来的

这个函数的头文件在 \MdePkg\Include\Library\UefiApplicationEntryPoint.h

再进一步查找 \MdePkg\Library\UefiApplicationEntryPoint\UefiApplicationEntryPoint.inf 其中给出了对应的函数体的位置

[Sources]
ApplicationEntryPoint.c

打开看看 \MdePkg\Library\UefiApplicationEntryPoint\ApplicationEntryPoint.c

EFI_STATUS
EFIAPI
_ModuleEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
...............
//
// Call constructor for all libraries.
//
ProcessLibraryConstructorList (ImageHandle, SystemTable);
//
// Call the module's entry point
//
Status = ProcessModuleEntryPointList (ImageHandle, SystemTable);
//
// Process destructor for all libraries.
//
ProcessLibraryDestructorList (ImageHandle, SystemTable);

其中调用了4个函数,下面分别按图索骥

  1. ProcessLibraryConstructorList 函数,他在

\Build\AppPkg\DEBUG_VS2008\IA32\AppPkg\Applications\ClsTest\ClsTest\DEBUG\AutoGen.c

追进去,看一下

VOID
EFIAPI
ProcessLibraryConstructorList (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
Status = UefiBootServicesTableLibConstructor (ImageHandle, SystemTable);
ASSERT_EFI_ERROR (Status);
Status = UefiRuntimeServicesTableLibConstructor (ImageHandle,SystemTable);
ASSERT_EFI_ERROR (Status);
Status = UefiLibConstructor (ImageHandle, SystemTable);
ASSERT_EFI_ERROR (Status);
}

 

1.1 追一下UefiBootServicesTableLibConstructor  发现它在\MdePkg\Library\UefiBootServicesTableLib\UefiBootServicesTableLib.c

EFI_STATUS
EFIAPI
UefiBootServicesTableLibConstructor (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
//
// Cache the Image Handle
//
gImageHandle = ImageHandle;  //看到这里也能明白之前文章中Extern 的gImageHandle哪里来了
ASSERT (gImageHandle != NULL);
//
// Cache pointer to the EFI System Table
//
gST = SystemTable;
ASSERT (gST != NULL);

//
// Cache pointer to the EFI Boot Services Table
//
gBS = SystemTable->BootServices;
ASSERT (gBS != NULL);

return EFI_SUCCESS;
}

 

1.2   再看看UefiRuntimeServicesTableLibConstructor 在 \MdePkg\Library\UefiRuntimeServicesTableLib\UefiRuntimeServicesTableLib.c

EFI_STATUS
EFIAPI
UefiRuntimeServicesTableLibConstructor (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  //
  // Cache pointer to the EFI Runtime Services Table
  //
  gRT = SystemTable->RuntimeServices;
  ASSERT (gRT != NULL);
  return EFI_SUCCESS;
}

 

1.3   UefiLibConstructor  在\MdePkg\Library\UefiLib\UefiLib.c

EFI_STATUS
EFIAPI
UefiLibConstructor (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
  return EFI_SUCCESS;
}

 

2. ProcessModuleEntryPointList在

\Build\AppPkg\DEBUG_VS2008\IA32\AppPkg\Applications\ClsTest\ClsTest\DEBUG\AutoGen.c

EFI_STATUS
EFIAPI
ProcessModuleEntryPointList (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
return UefiMain (ImageHandle, SystemTable); //至此,马上进入到了我们写的函数中
}

 

3.继续追ProcessLibraryDestructorList 这里应该是收尾的一些工作了

\Build\AppPkg\DEBUG_VS2008\IA32\AppPkg\Applications\ClsTest\ClsTest\DEBUG\AutoGen.c

VOID
EFIAPI
ProcessLibraryDestructorList (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{

}

 

上面很罗嗦的说了很多,总结一下可以用下面的图表示调用过程

AppEntry

 

当然,说了很多如果没有实验不能保证正确性

验证的办法是在上述提到的过程里面插入下面的语句

SystemTable->ConOut->OutputString(SystemTable->ConOut,L"LABZ Test X.X\n");

如果输出结果能够正常输出全部插入的,那么表示找到的点是正确的。

结果

appentry2

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

2024年10月9日

还可以在 UefiApplicationEntryPoint.inf文件中加入如下编译指令:


[BuildOptions]
  MSFT:*_*_X64_CC_FLAGS  = /FAsc /Od
  

之后,可以在对应的 Application 的 Build 目录下看到生成的 .COD 文件中出现的代码。

Arduino 读取显示器 EDID

arduinoedid

Arduinogetedid

Arduino2

文档对应代码

// SDA (20)  HDB15-12
// SCL (21)  HDB15-15
// +5V       HDB15-9
// GND       HDB15-5

#include <Wire.h>

const int i2c_port = 0x50;
byte buffer[BUFFER_LENGTH]; // 128 byte EEPROM data buffer

void setup() {
  Serial.begin(9600);
  Wire.begin();
  while (!Serial) 
    {;}
}

void loop() {
  Serial.println("(1) Read EDID and print.");
  Serial.println("(2) getInput()");
  Serial.println("(3) Item 3");
  Serial.println("");

  while (!Serial.available()) {;}

  switch (Serial.parseInt())
  {
      case 1: ddcRead(); break;
      case 2: getInput(); break;
      case 3: Serial.println("Item 3."); break;
      default: printError("Menu item does not exist.");
  }
  
  Serial.println("*************");
}

void printError(String message) {
  Serial.println("Error: " + message);
}

void ddcRead() {
  int blocks = 128 / BUFFER_LENGTH;
  
  Serial.println("Reading DDC...");
  
  Wire.beginTransmission(i2c_port);
  Wire.write(0);
  Wire.endTransmission(); 
 
  for (int block = 0; block < blocks; block += BUFFER_LENGTH) {
    Wire.requestFrom(i2c_port, BUFFER_LENGTH);
    for (int i = 0; i < BUFFER_LENGTH; i++) {
       //Serial.println(block + i, HEX);
       byte x = Wire.read();
       buffer[block + i] = x;
       //Serial.print(x, HEX); Serial.print(" ");
    }
  }
  Serial.println("Finished reading DDC.");
  printData();
}

void printData() {
  int rows = 128 / 16;
  for (int row = 0; row < rows; row++) {
    Serial.print(" ("); 
    if (row == 0) 
      Serial.print(0, HEX);
    Serial.print(row * 16, HEX); 
    Serial.print(") ");
    
    for (int half_col = 0; half_col < 2; half_col++) {
      for (int col = 0; col < 8; col++) {
        int index = (row * 16) + (half_col * 8) + col;
        byte b = buffer[index];
        if (b < 16) {
          Serial.print(0, HEX);
        }
        Serial.print(b, HEX);
        Serial.print(" ");
//        Serial.print("["); Serial.print(index, HEX); Serial.print("]");
      }
      
      if (half_col == 0) {
        Serial.print("- ");
      }
      else {
        Serial.println();
      }
    } 
  }
}

void getInput() {
  int input_buffer_len = 16;
  char input[input_buffer_len];
  Serial.println("Enter a string. 32 chars max. Input not echoed.");
  while (!Serial.available()) { ; }
  int input_len = Serial.readBytes(&input[0], input_buffer_len);
  Serial.print("Input is: ");
  for (int i = 0; i < input_len; i++) {
     Serial.write(input[i]); 
  }
  Serial.println(".");
  Serial.println("");
}

参考文档
I2C and Monitor DDC – Chrisbot

Step to UEFI (21) —– 清屏

想实现一个清屏的功能,刚开始在CLIB中翻了半天没找到,用工具直接搜索了一下 clrscr (应该在 conio.h 中)压根儿没找到。估计是 CLIB没有支持,只好换个方法。想起来Syetem Table中有 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL 拿着SPEC翻了【参考1】一下,发现可以使用它的 ClearScreen 函数。

ClearScreen

写一个简单的程序验证之:

//
// ClearScreen
//
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/ShellLib.h>
#include <Library/UefiApplicationEntryPoint.h>

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

//
// Entry point function 
//
EFI_STATUS
UefiMain (
  IN EFI_HANDLE        ImageHandle,
  IN EFI_SYSTEM_TABLE  *SystemTable
  )
{
   INTN	i;

  for (i=0;i<1000;i++)
    {
	Print(L".");
    }
  Print(L".\n");

  gST -> ConOut -> SetAttribute(gST->ConOut,0x1);
  gST -> ConOut -> ClearScreen(gST->ConOut);
 
  //SystemTable ->ConOut ->ClearScreen(SystemTable ->ConOut);
  //Print(L"%lX\n",SystemTable ->ConOut ->ClearScreen);
  //Print(L"%lX\n",gST -> ConOut -> ClearScreen);
  //Print(L"%lX\n",gST->ConOut);
  return EFI_SUCCESS;
}

 

工作正常,能够清屏。

程序下载 ClsTest

参考:

1.UEFI Spec 2.4 P459

2.http://biosren.com/viewthread.php?tid=3050&highlight=clearscreen 关于SHELL下面修改(前)背景色

Max6675 热电偶

入手了一个热电偶温度传感器,这种东西是专门用来测试温度的,接触式的,具有测量范围大,精度高的特点。Taobao上搜索 “Arduino 热电偶”,卖家没有几个,我是从“圣源电子制作”的店铺卖的。

他家直接提供的代码包有问题,其中对应的Arduino程序无法打开。不知道是否有其他朋友也遇到过同样的问题。好在网上搜索到了对应的库 http://www.geek-workshop.com/forum.php?mod=viewthread&tid=4554&highlight=max6675 。测试了一下工作正常。最后代码如下

#include "Max6675.h"

Max6675 ts(8, 9, 10);
// Max6675 module: SO on pin #8, SS on pin #9, CSK on pin #10 of Arduino UNO
// Other pins are capable to run this library, as long as digitalRead works on SO,
// and digitalWrite works on SS and CSK

void setup()
{
	ts.setOffset(0);
	// set offset for temperature measurement.
	// 1 stannds for 0.25 Celsius

	Serial.begin(9600);
}


void loop()
{
	Serial.print(ts.getCelsius(), 2);
	Serial.print(" C / ");
	Serial.print(ts.getFahrenheit(), 2);
	Serial.print(" F / ");
	Serial.print(ts.getKelvin(), 2);
	Serial.print(" K\n");
	delay(300);
}

运行结果

完整的代码库下载

Max6675

PT100