有兴趣的朋友可以看一下
https://pan.baidu.com/s/1wl1_POWDaINXdo-b9BOo8g
熟悉汇编语言的朋友都知道,DOS下面有很多INT服务,通过这样的入口能够实现硬件相关的服务。比如:INT 15 中可以支持关闭显示。与之类似,UEFI 里面是通过Protocol来实现这样的功能的。找到需要的 Protocol 即可 Call 到其提供的函数。这样的设计也给我们一个机会,可以用自己编写的函数来替换真实的函数。为此,设计了一个实验替换掉 Simple File System 中提供的 Read函数,这样每次 Application 读取到的都会是我们提供的数据。
代码比较长,从原理的角度解释具体流程:
首先,编写测试用的 Application,用 LocateHandleBuffer 枚举出来所有的 Simple File System Protocol,然后在找到的 Handle 上用 OpenVolume 打开 Root Directory。打开之后再用Open 函数打开文件,最后用 Read 来读取文件内容并且显示出来。实验中读取的是一个 44 Bytes 长的 Test.txt 文件。
实验环境是 NT32模拟器,我们只在 fs0: 上放置了 test.txt,因此,能找到2个SimpleFileSystemProtcol,第一个能够读取并且显示内容,第二个会出现Open File error 的错误。

接下来,编写我们的“驱动”,为了简单起见,这里并不是一个正经的驱动。对于我们来说,只是需要程序常驻内存,结束之后不希望被释放掉,否则会出现其他程序调用而无法找到函数的情况。因此,我们在 MdeModulePkg 中写程序,编译之后使用 Load 来加载。代码流程如下:
1. 在入口 MyEntryPoint 中查找一个 SimpleFileSystemProtocol,先保存OpenVolume 的实际入口,再用自己编写的 MySimpleFileSystemOpenVolume替换掉这个入口;
2. Application 在调用 OpenVolume 函数的时候,实际上是会进入MySimpleFileSystemOpenVolume 中。在这个函数中,使用之前保存的实际OpenVolume完成工作,然后将 OpenVolume返回的EFI_FILE_PROTOCOL替换为 MySimpleFileSystemOpen;
3. Application在调用 Open 函数的时候,实际上是会进入MySimpleFileSystemOpen。我们在这里检查参数判断要打开的是否为我们指定的 test.txt 文件,如果是,那么用 MySimpleFileSystemOpen来替换实际的 Open 函数;
4. 这样,当 Application 要Read test.txt 的时候,我们就可以送一个假的值出去。这样就实现了替换的功能。
完整的代码:
用于测试的 Application
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/ShellCEntryLib.h>
#include <Library/BaseMemoryLib.h>
#include <Protocol/SimpleFileSystem.h>
#include <Library/MemoryAllocationLib.h>
extern EFI_RUNTIME_SERVICES *gRT;
extern EFI_BOOT_SERVICES *gBS;
int
EFIAPI
main (
IN int Argc,
IN CHAR16 **Argv
)
{
EFI_STATUS Status;
EFI_HANDLE *HandleBuffer = NULL;
UINTN NumHandles;
UINTN Index;
EFI_FILE_IO_INTERFACE *ioDevice;
EFI_FILE_HANDLE handleRoots;
EFI_FILE_PROTOCOL *TestFile;
CHAR8 Buffer[4*1024];
UINTN BufferSize;
//Find all Simnple File System Protocol Handle
Status = gBS->LocateHandleBuffer(
ByProtocol,
&gEfiSimpleFileSystemProtocolGuid,
NULL,
&NumHandles,
&HandleBuffer);
Print(L"Walk handles %ld\n", NumHandles);
for(Index =0; Index<NumHandles; Index++){
//Get one Simple File Protocol from a Handle
Status = gBS->HandleProtocol (
HandleBuffer[Index],
&gEfiSimpleFileSystemProtocolGuid,
(VOID **) &ioDevice
);
//Opens the root directory on the volume
Status = ioDevice->OpenVolume( ioDevice, &handleRoots );
if (EFI_ERROR(Status)) {
Print(L"OpenVolume error \n");
}
//Open a file
Status = handleRoots->Open(handleRoots,&TestFile,L"test.txt",EFI_FILE_MODE_READ, 0);
if (!EFI_ERROR(Status)) {
//The size of "Test.txt" is 44bytes, I use hard code here
BufferSize=44;
TestFile->Read(TestFile,&BufferSize,&Buffer);
Print(L"%a",Buffer);
TestFile->Close(TestFile);
}
else
{
Print(L"Open file error [%r]\n",Status);
}
}
FreePool (HandleBuffer);
return EFI_SUCCESS;
}
简单的“驱动” 代码
#include <PiDxe.h>
#include <Library/UefiLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/DebugLib.h>
#include <Library/UefiDriverEntryPoint.h>
#include <Library/MemoryAllocationLib.h>
#include <Protocol/SimpleFileSystem.h>
#include <Library/MemoryAllocationLib.h>
extern EFI_SYSTEM_TABLE *gST;
EFI_GUID gEfiSimpleFileSystemProtocolGuid ={ 0x964E5B22, 0x6459, 0x11D2,
{ 0x8E, 0x39, 0x00, 0xA0, 0xC9, 0x69, 0x72, 0x3B }};
//Backup of Read function
EFI_FILE_READ OldSimpleFileSystemRead;
//Backup of Open function
EFI_FILE_OPEN OldSimpleFileSystemOpen;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_OPEN_VOLUME OldSimpleFileSystemOpenVolume;
//This one will replace the Read function of Read in Simple File System
EFI_STATUS
EFIAPI
MySimpleFileSystemRead (
IN EFI_FILE_PROTOCOL *This,
IN OUT UINTN *BufferSize,
OUT VOID *Buffer
)
{
CHAR8 TestBuffer[]="Replaced buffer\n\r";
AsciiStrCpyS(Buffer,AsciiStrnLenS(TestBuffer,255)+1,TestBuffer);
return EFI_SUCCESS;
}
//This one will replace the Open function of Open in Simple File System
EFI_STATUS
EFIAPI
MySimpleFileSystemOpen (
IN EFI_FILE_PROTOCOL *This,
OUT EFI_FILE_PROTOCOL **NewHandle,
IN CHAR16 *FileName,
IN UINT64 OpenMode,
IN UINT64 Attributes
)
{
EFI_STATUS Status;
//Call into Open function in the Simple File system
Status=(*OldSimpleFileSystemOpen)(This,NewHandle,FileName,OpenMode,Attributes);
if (!EFI_ERROR(Status)) {
//Check the filename to make sure it's the one we will replace
if (StrnCmp(FileName,L"test.txt",StrLen(FileName))==0) {
if ((**NewHandle).Read!=MySimpleFileSystemRead) {
//Backup the Read Function
OldSimpleFileSystemRead=(**NewHandle).Read;
//Replace the Read Function
(**NewHandle).Read=MySimpleFileSystemRead;
}
}
}
return Status;
}
EFI_STATUS
EFIAPI
MySimpleFileSystemOpenVolume (
IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This,
OUT EFI_FILE_PROTOCOL **Root
)
{
EFI_STATUS Status;
Status=(*OldSimpleFileSystemOpenVolume)(This,Root);
if (!EFI_ERROR(Status)) {
if ((**Root).Open!=MySimpleFileSystemOpen) {
OldSimpleFileSystemOpen=(**Root).Open;
(**Root).Open=MySimpleFileSystemOpen;
}
}
return Status;
}
EFI_STATUS
EFIAPI
MyEntryPoint (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
//EFI_FILE_PROTOCOL *Root;
EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *SimpleFile;
//Look for one Simple File System Protocol
Status = gBS->LocateProtocol (
&gEfiSimpleFileSystemProtocolGuid,
NULL,
&SimpleFile);
if (EFI_ERROR(Status)) {
gST->ConOut->OutputString(gST->ConOut,L"Can't find Simple File PROTOCOL\n");
return Status;
}
OldSimpleFileSystemOpenVolume=SimpleFile->OpenVolume;
SimpleFile->OpenVolume=MySimpleFileSystemOpenVolume;
return Status;
}
完整的代码下载:
在我们使用 Eagle 绘制电路板的的过程中,有时候需要取消当前已经布好的线路,比如使用自动布线,但是发现选择的线宽太窄,需要重新自动布线。
这时候可以使用 ripup * 命令即可取消之前的布线。
参考:
1.http://web.mit.edu/xavid/arch/i386_rhel4/help/81.htm
最近需要使用 DEVCON 又不想下载庞大的 WDK ,网上搜索了一下,找到如下的方法:
Windows 10 version 1803 Redstone 4
(April 2018 Update)
Windows Build: 10.0.17134
Driver Kit Build: 10.0.17134
下载之后,用7Z 之类的工具进行解压,在解压之后的文件中查找 filbad6e2cce5ebc45a401e19c613d0a28f 文件,改名为 devcon.exe 即可。
参考:
1.http://www.cnblogs.com/litifeng/p/9211792.html 如何安全的下载Devcon.exe文件
2.https://superuser.com/questions/1002950/quick-method-to-install-devcon-exe
Whiskey Lake 是Intel 2018年推出的更新8代酷睿CPU,我最近拿到了一块六联智能(SIXUNITED)推出的 Whiskey Lake HDK 平台,这也是和我之前使用的 KabyLake-R HDK 属于同一系列的开发板。

相比之下,WHL HDK 的板子比之前 KBL-R 要小很多,有如下接口:
1. MIPI Camera
2. M.2 Sata/PCIE NVME
3. USB 接口,支持DCI 调试
4. M.2 PCIE 接口,支持WIFI、CNVI
5. PCIE x4 Slot
6. WWAN 接口 + SIM卡接口
7. Intel Sensor Hub 接口
8. HDMI 接口
开发板仍然采用 IO Board + Core Board 的设计方式,二者通过中间的 DIMM 插槽进行连接(去掉散热器的 Core Board 可以看到 SoC 还是挺大的):


此外,该板加入了检测功耗的功能,能够在本机或者远端机器上读取当前的各路电流功耗信息,从而能够实现监视每个设备的功耗的功能。下图是配套软件在另外的机器上读取功耗的情况,因为我不是硬件工程师,所以没做更详细的测试。

工作的照片,板载数码管直接输出80Port值,这样能够很方便的让用户识别当前状态。右下角的USB是提供给其他机器读取当前功耗信息的。

整体来说吗,感觉 WHL 平台功耗挺低的,在测试过程中,风扇转动的次数不多。
官网为http://www.hdkboards.com/ 有兴趣的朋友可以上去查看更多信息。
常见电容器参数识别方法
电容单位是F
1法拉(F)= 1000毫法(mF)=1000000微法(μF)
1微法(μF)= 1000纳法(nF)= 1000000皮法(pF) 【参考1】
常见的电容标注:
| 1PF | 20PF(200) | 100PF(101) | 510PF(511) | 10NF(103) | 220NF(224) |
| 4.7PF | 22P(220) | 150PF(151) | 680PF(681) | 22NF(223) | 330NF(334) |
| 6.8PF | 30PF(300) | 180PF(181) | 1NF(102) | 33NF(333) | 470NF(474) |
| 10PF(100) | 33PF(330) | 220PF(221) | 2.2NF(222) | 47NF(473) | 1UF(105) |
| 15PF(150) | 47PF(470) | 330PF(331) | 3.3NF(332) | 68NF(683) | 2.2UF(225) |
| 18PF(180) | 68PF(680) | 470PF(471PF) | 4.7NF(472) | 100NF(104) | 4.7UF(475) |
| 3.3UF(335) | 10UF(106) |
参考:
现在最常见的 GIF 就是各种动图了,因为它足够小,无需支付版权费用和不用安装额外的插件,使得它成为分享动图的首选。
这次介绍来自https://github.com/lecram/gifdec 的 GIF 解码库,可以用来解码静态和动态GIF格式。
需要注意的是,这个库有下面2个局限(GIF 最多支持256色,这个限制是来自他的调色板只有256色。通常情况下,一幅有很多帧的动图都会共享同一个调色板。特殊情况下,每一帧都有一个调色板。这个库不支持后面那种情况):
* no support for GIF files that don’t have a global color table
* no direct support for the plain text extension (rarely used)
实例程序如下,我在 IA32 和 X64 NT32环境下测试过,都是可以正常工作的:
#include <stdlib.h>
#include <stdio.h>
#include <Protocol/GraphicsOutput.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/BaseMemoryLib.h>
#include "gifdec.h"
extern EFI_BOOT_SERVICES *gBS;
extern EFI_SYSTEM_TABLE *gST;
extern EFI_RUNTIME_SERVICES *gRT;
#define EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID \
{ \
0x9042a9de, 0x23dc, 0x4a38, {0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a } \
}
static EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID;
int
main (
IN int Argc,
IN char *Argv[]
)
{
EFI_STATUS Status;
EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput;
int i,j;
UINT8* RGB32;
UINT8* frame;
if (Argc<2) {
printf("Please input file name\n");
return 0;
}
gd_GIF* gif = gd_open_gif(Argv[1]);
if (gif == NULL) {
printf("Open file Error\n");
return -1;
}
printf("width %d height %d\n",gif->width,gif->height);
printf("number of colors: %d\n", gif->palette->size);
printf("number of frames: %d\n", gif->loop_count);
Status = gBS->LocateProtocol(&GraphicsOutputProtocolGuid, NULL, (VOID **) &GraphicsOutput);
if (EFI_ERROR(Status)) {
GraphicsOutput = NULL;
printf("Loading Graphics_Output_Protocol error!\n");
return EFI_SUCCESS;}
frame=(UINT8*)AllocatePool(gif->width*gif->height*3);
RGB32 = (UINT8*)AllocatePool(gif->width*gif->height*4);
while (1)
{
int retx = gd_get_frame(gif);
if (retx == -1)
break;
gd_render_frame(gif, frame);
for (j=0;j<gif->height;j++) {
for (i=0;i<gif->width;i++) {
RGB32[(j*gif->width+i)*4] = frame[(j*gif->width+i)*3+2]; //Blue
RGB32[(j*gif->width+i)*4+1]= frame[(j*gif->width+i)*3+1]; //Green
RGB32[(j*gif->width+i)*4+2]= frame[(j*gif->width+i)*3]; //Red
RGB32[(j*gif->width+i)*4+3]=0;
}
GraphicsOutput->Blt(
GraphicsOutput,
(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) RGB32,
EfiBltBufferToVideo,
0, 0,
0, 0,
gif->width, gif->height, 0);
}
if (retx == 0)
break;
//gd_rewind(gif);
}
free(RGB32);
free(frame);
gd_close_gif(gif);
return 0;
}
工作截图:

完整代码下载:
giftest
简单说一下工作流程,首先gd_open_gif() 函数打开 GIF,这步可以获得尺寸信息,但是从我实验来看,无法取得帧数信息(loop_count),但是readme 中说是可以的;接下来使用gd_get_frame() 函数检查当前GIF播放的位置,返回1表示还有下一帧,0表示达到尾部,-1表示出现错误;然后,gd_render_frame()函数取出来解码后的图像信息,经过格式变换即可通过 BLT显示出来;最后,检查是否播放完毕,完毕之后退出。
代码写的比较简单,效率不高,在处理较大 GIF 的时候,还是能够感受到刷新的。有兴趣的朋友可以琢磨一下如何提速,我猜测瓶颈应该是在处理解码后的数据RGB顺序的那里。
=======================================================================================
2018年11月23日 感谢 Cai 3023772977@qq.com 在评论中之处问题,代码中 GraphicsOutput() 函数,写在循环内了,这是导致播放缓慢的原因,将它移动到循环外面就可以正常工作了。
for (j=0;j
for (i=0;i
RGB32[(j*gif->width+i)*4] = frame[(j*gif->width+i)*3+2]; //Blue
RGB32[(j*gif->width+i)*4+1]= frame[(j*gif->width+i)*3+1]; //Green
RGB32[(j*gif->width+i)*4+2]= frame[(j*gif->width+i)*3]; //Red
RGB32[(j*gif->width+i)*4+3]=0;
}
GraphicsOutput->Blt(
GraphicsOutput,
(EFI_GRAPHICS_OUTPUT_BLT_PIXEL *) RGB32,
EfiBltBufferToVideo,
0, 0,
0, 0,
gif->width, gif->height, 0);
}
做了一个简单的实验,测试 Teensy 3.2 板子IO 的速度,具体代码如下,就是使用digitalWrite进行 GPIO反转,然后示波器查看结果。
void setup() {
// put your setup code here, to run once:
pinMode(14,OUTPUT);
digitalWrite(14,HIGH);
}
void loop() {
// put your main code here, to run repeatedly:
digitalWrite(14,LOW);
digitalWrite(14,HIGH);
digitalWrite(14,LOW);
digitalWrite(14,HIGH);
digitalWrite(14,LOW);
digitalWrite(14,HIGH);
digitalWrite(14,LOW);
digitalWrite(14,HIGH);
digitalWrite(14,LOW);
digitalWrite(14,HIGH);
}
速度首先和主频有关系,可以在下面的位置找到,我这边测试72Mhz和96Mhz的情况。

另外,还和编译选项有关系。

1.72Mhz+Faster 测试结果是 1.21Mhz (光标测量,下同)

2.72Mhz + Faster with LTO 测试结果是 2.34Mhz (光标测量,下同)

3.96Mhz+Faster 测试结果是 1.61Mhz

4. 96Mhz+Faster with LTO 测试结果是 3.07Mhz

大多数情况下,2层循环的冒泡排序对于一般用户已经足够,但是如果对于速度有要求,那就需要考虑效率更高的排序算法了。最近偶然看到 MdeModulePkg\Include\Library\SortLib.h 提供的排序功能,研究之后编写如下的例子。
这个库的核心是PerformQuickSort() 函数,调用者给出需要排序的 buffer,然后给出其中每一个元素的大小和数量,然后这个函数还会调用作为参数指定的CompareFunction函数。通过这个函数,不但可以比较数字或者字符串,还可以比较自动定义的规则,比方说设定书记>厂长>科长之类的……
示例代码如下:
/**
Function to perform a Quick Sort on a buffer of comparable elements.
Each element must be equally sized.
If BufferToSort is NULL, then ASSERT.
If CompareFunction is NULL, then ASSERT.
If Count is < 2 , then perform no action.
If Size is < 1 , then perform no action.
@param[in, out] BufferToSort On call, a Buffer of (possibly sorted) elements;
on return, a buffer of sorted elements.
@param[in] Count The number of elements in the buffer to sort.
@param[in] ElementSize The size of an element in bytes.
@param[in] CompareFunction The function to call to perform the comparison
of any two elements.
**/
VOID
EFIAPI
PerformQuickSort (
IN OUT VOID *BufferToSort,
IN CONST UINTN Count,
IN CONST UINTN ElementSize,
IN SORT_COMPARE CompareFunction
);
代码如下:
#include <stdlib.h>
#include <stdio.h>
#include <Uefi.h>
#include <Library/UefiLib.h>
#include <Library/ShellCEntryLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/BaseMemoryLib.h>
#include <Library/SortLib.h>
#define TOTAL 100
#define NAMELENGTH 4
/** Expands to an integer constant expression that is the maximum value
returned by the rand function.
**/
#define RAND_MAX 0x7fffffff
static UINT32 next = 1;
typedef struct {
char Name[NAMELENGTH+1];
int Score;
} TScoreRecord;
/** Compute a pseudo-random number.
*
* Compute x = (7^5 * x) mod (2^31 - 1)
* without overflowing 31 bits:
* (2^31 - 1) = 127773 * (7^5) + 2836
* From "Random number generators: good ones are hard to find",
* Park and Miller, Communications of the ACM, vol. 31, no. 10,
* October 1988, p. 1195.
**/
int
rand()
{
INT32 hi, lo, x;
/* Can't be initialized with 0, so use another value. */
if (next == 0)
next = 123459876;
hi = next / 127773;
lo = next % 127773;
x = 16807 * lo - 2836 * hi;
if (x < 0)
x += 0x7fffffff;
return ((next = x) % ((UINT32)RAND_MAX + 1));
}
/**
Test comparator.
@param[in] b1 The first INTN
@param[in] b2 The other INTN
@retval 0 They are the same.
@retval -1 b1 is less than b2
@retval 1 b1 is greater then b2
**/
INTN
EFIAPI
Compare (CONST TScoreRecord *b1, CONST TScoreRecord *b2)
{
if ((b1->Score)<(b2->Score)) {
return -1;
}
if ((b1->Score)>(b2->Score)) {
return 1;
}
return (0);
}
int main()
{
TScoreRecord *SR;
int Index,NameIndex;
SR = AllocatePool (sizeof(TScoreRecord)*TOTAL);
for (Index=0;Index<TOTAL;Index++) {
//Generate a name
for (NameIndex=0;NameIndex<NAMELENGTH;NameIndex++) {
SR[Index].Name[NameIndex]=(rand()%26)+'A';
}
SR[Index].Name[NAMELENGTH]=0;
//Generate a score
SR[Index].Score=rand()%150;
//Output
printf("[%d] %s %d\n",Index,SR[Index].Name,SR[Index].Score);
}
//Sort
PerformQuickSort (SR,TOTAL,sizeof(TScoreRecord),Compare);
//Show the sort result
for (Index=0;Index<TOTAL;Index++) {
printf("[%d] %s %d\n",Index,SR[Index].Name,SR[Index].Score);
}
FreePool(SR);
return 0;
}
运行结果:

完整代码和EFI 下载:
此外,还有一个有意思的问题:如果Compare (CONST TScoreRecord *b1, CONST TScoreRecord *b2) 这里定义为Compare (TScoreRecord *b1, TScoreRecord *b2),那么在编译时,会在PerformQuickSort (SR,TOTAL,sizeof(TScoreRecord),Compare); 报告C4090错误。因为编译器报告错误位置和实际位置着实不同,为了找到原因着实让我花费了不少时间。
1.列出当前系统中串口编号的方法:
Array ports = System.IO.Ports.SerialPort.GetPortNames();
for (int x = 0; x < ports.Length; x++)
Console.Write(ports.GetValue(x).ToString());
2.列出当前系统中串口设备完整名称的方法:
static void Main(string[] args)
{
ManagementObjectSearcher searcher =
new ManagementObjectSearcher("root\\CIMV2",
"SELECT * FROM Win32_PnPEntity");
foreach (ManagementObject queryObj in searcher.Get())
{
if (queryObj["Caption"]!=null)
if (queryObj["Caption"].ToString().Contains("(COM"))
{
Console.WriteLine(queryObj["Caption"]);
}
}
需要注意的是,要在文件头部加上 using System.Management; 并且在菜单 Project -> Add Reference -> Assembies -> Framework 选中 System.Management
