用Arduino配合几个电阻就能实现VGA信号的输出
具体实现方法请参考下面的文章
之前的文章【参考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
上面的调用关系可以用下面的图来简单总结一下
====================================分割线====================================
我们在运行期确定是下面的代码来取得参数的
//
// 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》 找到。
因此,可以看到,取出来的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/
前面一篇介绍了 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处干这个事情的,第一个是在分析 “%”,第二个才是我们想要的)。为了验证,我们将上面这段替换的代码删除,重新编译,运行结果如下:
上面一次运行结果是修改之前,下面是修改之后。可以看到,当我们去掉那段自己添加 \n \r做结尾的代码之后,同样会出现只换行不移动到行首的问题。
结论:Print 之所以 \n 直接就能换行移动到行首,是因为他代码中有特殊处理。
前面的一篇文章遇到了奇怪的问题,字符串输出看起来很不规整。于是研究一下为什么。
首先,试试 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;
}
我们看到有同样的现象。

为了查看汇编级别的程序,我们可以在 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'
运行结果:
最后,提一个问题,如果程序写成这个样子
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 是一款温度湿度传感器。具体可以看【参考1】。参考中使用的是单独的元件,而我使用的是做好的模块,因此不需要额外的电阻。
硬件方面,只有三根线,GND VCC 和OUT。 对照参考中的程序,很容易上手。
下面的代码也是参考中的代码,只是修改了一下波特率为 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
参考:
1.http://www.geek-workshop.com/forum.php?mod=viewthread&tid=997&extra=&highlight=dht11&page=1 DHT11 测试
两年前,再次学习单片机,此次入手的单片机比它的爹妈强多了。不仅把rs232接口用usb硬连接到pc而且直接写其rom!(本人不是硬件专业人士先进东西见的少)。琢磨着做个啥当学习目标呢,做个pc控制的小车吧。首先得解决mcu对数据的处理,恩,定义个包头aa55加个数据长度字0xh加个命令字0xh,再加上数据字。差资料显示状态机方式处理最好。写完后写上位程序。嘿嘿masm32是我的最爱,不过3天调试成功!为了增加可玩性,哈加入tcp方式传输命令流。一切调试ok,接上小车,呵呵大告成功!上传视频共同分享快乐!
pmason_rose 联系方式 pmason_rose@qq.com(332779423)
研究一下: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个函数,下面分别按图索骥
\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
)
{
}
上面很罗嗦的说了很多,总结一下可以用下面的图表示调用过程
当然,说了很多如果没有实验不能保证正确性
验证的办法是在上述提到的过程里面插入下面的语句
SystemTable->ConOut->OutputString(SystemTable->ConOut,L"LABZ Test X.X\n");
如果输出结果能够正常输出全部插入的,那么表示找到的点是正确的。
结果
============================================================
2024年10月9日
还可以在 UefiApplicationEntryPoint.inf文件中加入如下编译指令:
[BuildOptions]
MSFT:*_*_X64_CC_FLAGS = /FAsc /Od
之后,可以在对应的 Application 的 Build 目录下看到生成的 .COD 文件中出现的代码。
文档对应代码
// 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("");
}
想实现一个清屏的功能,刚开始在CLIB中翻了半天没找到,用工具直接搜索了一下 clrscr (应该在 conio.h 中)压根儿没找到。估计是 CLIB没有支持,只好换个方法。想起来Syetem Table中有 EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL 拿着SPEC翻了【参考1】一下,发现可以使用它的 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下面修改(前)背景色
入手了一个热电偶温度传感器,这种东西是专门用来测试温度的,接触式的,具有测量范围大,精度高的特点。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);
}
运行结果
完整的代码库下载
PT100