2026年2月更新,Step to UEFI 文章索引:
Intel SOL 和 eSOL
SOL 是 Sign-of-Life 的缩写, eSOL 是 extension to SOL 的缩写。
通常来说,MRC Training 会花费很长时间,在之前这段时间屏幕是没有任何显示的。这样会导致很多客户认为机器出现问题反复关机,联系售后等等……
SOL 的出现就是为了解决这个问题:在内存还没有 Ready 的时候就显示一些东西,比如LOGO,提示词等等,这样客户就可以知道当前的状态并非死机。术语来说:SOL提升了MRC Training 期间的用户体验。
接下来出现了 eSOL 更进一步,SOL 和 eSOL 的区别如下:
| SOL | ESOL |
| 在内存检测之前开始进行显示,MRC Traing结束后关闭显示。SOL exit 是必须进行的操作 | 在内存检测之前开始进行显示,显示可以持续到 PEI Post MEM阶段或者到 调用DXE GOP的阶段 。SOL exit 是可选操作 |
| 只能显示静态画面 | 可以显示动态画面,比如,绘制一个进度条 |
| 在 SOL exit 到 PEI GOP 接手期间存无法显示任何信息的时间段 | 从 SOL 到OS 下的 GFXDriver一直都能提供显示服务。显示黑屏持续时间大大缩短了,但是在 PEI POST MEM 或者 DXE GOP 接管显示控制权之后,用户仍然会碰到一次黑屏。产生这个黑屏的原因是 VGA想高分辨率模式切换导致的。 |
ACPI 的_PS0() 和 _PS3()
在 ACPI 的 ASL 里,_PS0() 和 _PS3() 是设备电源状态控制方法:
- _PS0():表示把设备切到 D0,也就是全开 / 工作状态。
- _PS3():表示把设备切到 D3,也就是低功耗 / 关闭状态(通常是 D3hot;如果平台还配合 _PR3 / _OFF,可能进一步到 D3cold)。
更直白一点:
- 设备要“开机能工作”时,OS/ACPI 可能调用 _PS0()
- 设备要“省电停用”时,OS/ACPI 可能调用 _PS3()
此外,类似的还有
- _PR0:声明设备处于 D0 时依赖哪些 power resources
- _PR3:声明设备进入 D3 路径 时依赖哪些 power resources
- _ON() / _OFF():真正控制 power resource 的上电/断电动作
例如,下图中设备需要从 D0 进入 D3,首先是操作系统提出要求,然后通过驱动,调用 ACPI 设备中的 _PS3()。到了这一步,设备就停止了工作。如果你的设备支持D3Cold(彻底断电),那么还会调用_PR3()获得依赖的 Power Resource。最后再调用_OFF() 进行断电。这样设备的供电就被完全切断了。
将设备从 D3唤醒的方法和上面的类似,首先调用_PS0()。如果设备支持 D3 Cold那么需要调用_PR0()获得相关 Power Resource,最后通过_On(),恢复供电或者 Clock 之类的。设备才能够正常的开始工作。

做一个 UEFI 下的 EasyX 图形库(6)
继续给 UEFI 下的 EasyX 增加功能,这次增加了颜色转换的几个函数
- HSVtoRGB(H, S, V)
- HSLtoRGB(H, S, L)
- RGBtoGRAY(Color)
- RGBtoHSV(Color, *H, *S, *V)
- RGBtoHSL(Color, *H, *S, *L)
测试代码绘制彩虹条:
#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>
#include <Library/PrintLib.h>
#include "easyx.h"
/**
程序入口。
**/
EFI_STATUS
EFIAPI
UefiMain (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
INT32 W;
INT32 H;
INT32 PenX;
INT32 PenY;
IMAGE Sprite = { 0, 0, NULL }; // 使用前必须清零
IMAGE Grabbed = { 0, 0, NULL }; // getimage 会读取 Buffer 判断是否需释放
ExMessage Msg;
BOOLEAN Running;
UINTN ColorIndex;
CHAR8 StatusBuf[96];
CONST COLORREF Palette[] = {
LIGHTRED, LIGHTGREEN, LIGHTCYAN, YELLOW, LIGHTMAGENTA, WHITE
};
//
// 初始化绘图设备(跟随 GOP 最大分辨率模式)。
//
Status = initgraph (0, 0);
if (EFI_ERROR (Status)) {
Print (L"initgraph 失败: %r\n", Status);
return Status;
}
W = getwidth ();
H = getheight ();
//
// 批量绘图,减少刷新次数。
//
BeginBatchDraw ();
//
// 背景填充深蓝。
//
setbkcolor (RGB (16, 16, 48));
cleardevice ();
//
// 颜色模型演示:顶部粗大 HSV 彩虹条 + 其灰度条(RGBtoGRAY)。
//
{
INT32 Px;
setlinestyle (PS_SOLID, 1);
for (Px = 20; Px < W - 20; Px++) {
INT32 Hue = (Px - 20) * 360 / (W - 40);
COLORREF Rainbow = HSVtoRGB (Hue % 360, 100, 100);
setlinecolor (Rainbow);
line (Px, 8, Px, 47); // 彩虹条:40 像素高
setlinecolor (RGBtoGRAY (Rainbow));
line (Px, 50, Px, 69); // 灰度条:20 像素高
}
}
//
// 直线:不同颜色/线宽/线型(下移,为彩虹条让位)。
//
setlinecolor (WHITE);
setlinestyle (PS_SOLID, 1);
line (20, 84, W - 20, 84);
setlinecolor (LIGHTGREEN);
setlinestyle (PS_SOLID, 3);
line (20, 96, W - 20, 112);
setlinecolor (YELLOW);
setlinestyle (PS_DASH, 1);
line (20, 122, W - 20, 122);
setlinecolor (LIGHTCYAN);
setlinestyle (PS_DOT, 1);
line (20, 132, W - 20, 132);
//
// 矩形。
//
setlinestyle (PS_SOLID, 1);
setlinecolor (LIGHTRED);
rectangle (40, 140, 200, 260);
setfillcolor (RGB (40, 120, 200));
setlinecolor (WHITE);
fillrectangle (240, 140, 400, 260);
setfillcolor (LIGHTMAGENTA);
solidrectangle (440, 140, 600, 260);
//
// 圆。
//
setlinecolor (WHITE);
circle (110, 360, 60);
setfillcolor (RGB (220, 160, 40));
setlinecolor (WHITE);
fillcircle (320, 360, 60);
setfillcolor (LIGHTGREEN);
solidcircle (520, 360, 60);
//
// 椭圆。
//
setlinecolor (LIGHTCYAN);
ellipse (40, 440, 260, 540);
setfillcolor (RGB (200, 60, 120));
setlinecolor (WHITE);
fillellipse (300, 440, 560, 540);
//
// 文字(P2):不同颜色/大小/背景模式。
//
settextcolor (WHITE);
settextstyle (16, 8);
outtextxy (620, 150, "EasyX for UEFI");
settextcolor (YELLOW);
settextstyle (24, 0); // 高 24,宽按比例
outtextxy (620, 180, "Hello, GOP!");
settextcolor (LIGHTGREEN);
settextstyle (32, 16);
setbkmode (TRANSPARENT);
outtextxy (620, 220, "0123456789");
settextcolor (BLACK);
setbkcolor (LIGHTGRAY);
setbkmode (OPAQUE);
settextstyle (16, 8);
outtextxy (620, 270, " OPAQUE text mode ");
setbkmode (TRANSPARENT);
settextcolor (LIGHTRED);
settextstyle (20, 10);
outtextxy (620, 320, "The quick brown fox");
outtextxy (620, 345, "jumps over lazy dog.");
//
// 图像(P3)。
//
// 1) 用 SetWorkingImage 把绘图重定向到内存图像,绘制一个“精灵”。
//
if (!EFI_ERROR (CreateImage (&Sprite, 80, 80))) {
SetWorkingImage (&Sprite);
setbkcolor (RGB (20, 20, 20));
cleardevice ();
setfillcolor (RGB (240, 200, 40));
solidcircle (40, 40, 34);
setfillcolor (BLACK);
solidcircle (28, 32, 5);
solidcircle (52, 32, 5);
setlinecolor (BLACK);
setlinestyle (PS_SOLID, 2);
line (24, 54, 56, 54);
SetWorkingImage (NULL); // 恢复到屏幕
// 2) 直接覆盖绘制到屏幕多个位置。
putimage (620, 400, &Sprite);
putimage (710, 400, &Sprite);
// 3) 透明贴图:跳过图像四角的深灰背景色。
putimagetrans (800, 400, &Sprite, RGB (20, 20, 20));
}
//
// 4) getimage:抓取屏幕左上角一块区域,复制到别处。
//
if (!EFI_ERROR (getimage (&Grabbed, 40, 140, 160, 120))) {
putimage (620, 500, &Grabbed);
DeleteImage (&Grabbed);
}
DeleteImage (&Sprite);
//
// 高级图形(P5):底部一条演示带(先清出干净背景)。
//
{
INT32 Y0 = H - 150;
setfillcolor (RGB (16, 16, 48));
solidrectangle (0, Y0 - 10, W - 1, Y0 + 100);
// 圆角矩形(边框 + 填充)
setlinestyle (PS_SOLID, 1);
setlinecolor (WHITE);
setfillcolor (RGB (40, 120, 200));
fillroundrect (30, Y0, 150, Y0 + 90, 40, 40);
// 扇形(吃豆人)
setfillcolor (YELLOW);
fillpie (170, Y0, 290, Y0 + 90, 35, 300);
// 圆弧
setlinecolor (LIGHTCYAN);
arc (310, Y0, 430, Y0 + 90, 0, 200);
// 多边形(三角形,边框 + 填充)
setlinecolor (WHITE);
setfillcolor (LIGHTGREEN);
{
POINT Tri[3] = {
{ 450, Y0 + 90 },
{ 510, Y0 },
{ 570, Y0 + 90 }
};
fillpolygon (Tri, 3);
}
// 折线(锯齿)
setlinecolor (LIGHTMAGENTA);
setlinestyle (PS_SOLID, 2);
{
POINT Zig[5] = {
{ 590, Y0 + 90 },
{ 610, Y0 },
{ 630, Y0 + 90 },
{ 650, Y0 },
{ 670, Y0 + 90 }
};
polyline (Zig, 5);
}
// 区域填充:先画空心矩形,再对内部 floodfill
setlinestyle (PS_SOLID, 1);
setlinecolor (WHITE);
rectangle (700, Y0, 800, Y0 + 90);
setfillcolor (RGB (220, 80, 160));
floodfill (750, Y0 + 45, RGB (16, 16, 48));
}
//
// 键盘操作提示。
//
setbkmode (TRANSPARENT);
settextcolor (WHITE);
settextstyle (16, 8);
outtextxy (20, H - 24, "Arrow keys=draw, Space=color, ESC=exit");
//
// 一次性刷新并结束批量绘图。
//
EndBatchDraw ();
//
// 键盘交互循环(P4):方向键移动画笔绘线。
//
PenX = W / 2;
PenY = H / 2;
Running = TRUE;
ColorIndex = 0;
while (Running) {
INT32 NewX;
INT32 NewY;
getmessage (&Msg, EX_KEY);
if (Msg.message != WM_KEYDOWN) {
continue;
}
NewX = PenX;
NewY = PenY;
switch (Msg.vkcode) {
case VK_LEFT: NewX -= 8; break;
case VK_RIGHT: NewX += 8; break;
case VK_UP: NewY -= 8; break;
case VK_DOWN: NewY += 8; break;
case VK_SPACE: ColorIndex++; break;
case VK_ESCAPE: Running = FALSE; break;
default: break;
}
if (!Running) {
break;
}
//
// 夹取到屏幕范围内。
//
if (NewX < 0) {
NewX = 0;
}
if (NewY < 0) {
NewY = 0;
}
if (NewX > W - 1) {
NewX = W - 1;
}
if (NewY > H - 1) {
NewY = H - 1;
}
//
// 从旧位置到新位置画一段线。
//
setlinecolor (Palette[ColorIndex % (sizeof (Palette) / sizeof (Palette[0]))]);
setlinestyle (PS_SOLID, 2);
line (PenX, PenY, NewX, NewY);
PenX = NewX;
PenY = NewY;
//
// 底部显示当前画笔位置与按键(先用背景色清除旧文字)。
//
setfillcolor (RGB (16, 16, 48));
solidrectangle (20, H - 48, 420, H - 28);
AsciiSPrint (
StatusBuf,
sizeof (StatusBuf),
"Pen: (%d, %d) vkcode=0x%02x",
PenX,
PenY,
Msg.vkcode
);
setbkmode (TRANSPARENT);
settextcolor (LIGHTCYAN);
settextstyle (16, 8);
outtextxy (20, H - 46, StatusBuf);
}
closegraph ();
//
// 恢复文本控制台。
//
gST->ConOut->ClearScreen (gST->ConOut);
Print (L"EasyX UEFI Demo 结束。分辨率: %d x %d\n", W, H);
return EFI_SUCCESS;
}
效果如下:

完整的代码下载:
做一个 UEFI 下的 EasyX 图形库(5)
继续改进代码,增加了一些绘制扇形之类的函数。
示例代码:
/** @file
EasyX UEFI 图形库演示程序(阶段 P0–P4)。
先绘制静态场景(线/矩形/圆/椭圆/文字/图像),然后进入
键盘交互:方向键移动“画笔”绘线,空格切换颜色,ESC 退出。
本演示不依赖鼠标。
**/
#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>
#include <Library/PrintLib.h>
#include "easyx.h"
/**
程序入口。
**/
EFI_STATUS
EFIAPI
UefiMain (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
INT32 W;
INT32 H;
INT32 PenX;
INT32 PenY;
IMAGE Sprite = { 0, 0, NULL }; // 使用前必须清零
IMAGE Grabbed = { 0, 0, NULL }; // getimage 会读取 Buffer 判断是否需释放
ExMessage Msg;
BOOLEAN Running;
UINTN ColorIndex;
CHAR8 StatusBuf[96];
CONST COLORREF Palette[] = {
LIGHTRED, LIGHTGREEN, LIGHTCYAN, YELLOW, LIGHTMAGENTA, WHITE
};
//
// 初始化绘图设备(跟随 GOP 最大分辨率模式)。
//
Status = initgraph (0, 0);
if (EFI_ERROR (Status)) {
Print (L"initgraph 失败: %r\n", Status);
return Status;
}
W = getwidth ();
H = getheight ();
//
// 批量绘图,减少刷新次数。
//
BeginBatchDraw ();
//
// 背景填充深蓝。
//
setbkcolor (RGB (16, 16, 48));
cleardevice ();
//
// 直线:不同颜色/线宽/线型。
//
setlinecolor (WHITE);
setlinestyle (PS_SOLID, 1);
line (20, 20, W - 20, 20);
setlinecolor (LIGHTGREEN);
setlinestyle (PS_SOLID, 3);
line (20, 40, W - 20, 60);
setlinecolor (YELLOW);
setlinestyle (PS_DASH, 1);
line (20, 80, W - 20, 80);
setlinecolor (LIGHTCYAN);
setlinestyle (PS_DOT, 1);
line (20, 100, W - 20, 100);
//
// 矩形。
//
setlinestyle (PS_SOLID, 1);
setlinecolor (LIGHTRED);
rectangle (40, 140, 200, 260);
setfillcolor (RGB (40, 120, 200));
setlinecolor (WHITE);
fillrectangle (240, 140, 400, 260);
setfillcolor (LIGHTMAGENTA);
solidrectangle (440, 140, 600, 260);
//
// 圆。
//
setlinecolor (WHITE);
circle (110, 360, 60);
setfillcolor (RGB (220, 160, 40));
setlinecolor (WHITE);
fillcircle (320, 360, 60);
setfillcolor (LIGHTGREEN);
solidcircle (520, 360, 60);
//
// 椭圆。
//
setlinecolor (LIGHTCYAN);
ellipse (40, 440, 260, 540);
setfillcolor (RGB (200, 60, 120));
setlinecolor (WHITE);
fillellipse (300, 440, 560, 540);
//
// 文字(P2):不同颜色/大小/背景模式。
//
settextcolor (WHITE);
settextstyle (16, 8);
outtextxy (620, 150, "EasyX for UEFI");
settextcolor (YELLOW);
settextstyle (24, 0); // 高 24,宽按比例
outtextxy (620, 180, "Hello, GOP!");
settextcolor (LIGHTGREEN);
settextstyle (32, 16);
setbkmode (TRANSPARENT);
outtextxy (620, 220, "0123456789");
settextcolor (BLACK);
setbkcolor (LIGHTGRAY);
setbkmode (OPAQUE);
settextstyle (16, 8);
outtextxy (620, 270, " OPAQUE text mode ");
setbkmode (TRANSPARENT);
settextcolor (LIGHTRED);
settextstyle (20, 10);
outtextxy (620, 320, "The quick brown fox");
outtextxy (620, 345, "jumps over lazy dog.");
//
// 图像(P3)。
//
// 1) 用 SetWorkingImage 把绘图重定向到内存图像,绘制一个“精灵”。
//
if (!EFI_ERROR (CreateImage (&Sprite, 80, 80))) {
SetWorkingImage (&Sprite);
setbkcolor (RGB (20, 20, 20));
cleardevice ();
setfillcolor (RGB (240, 200, 40));
solidcircle (40, 40, 34);
setfillcolor (BLACK);
solidcircle (28, 32, 5);
solidcircle (52, 32, 5);
setlinecolor (BLACK);
setlinestyle (PS_SOLID, 2);
line (24, 54, 56, 54);
SetWorkingImage (NULL); // 恢复到屏幕
// 2) 直接覆盖绘制到屏幕多个位置。
putimage (620, 400, &Sprite);
putimage (710, 400, &Sprite);
// 3) 透明贴图:跳过图像四角的深灰背景色。
putimagetrans (800, 400, &Sprite, RGB (20, 20, 20));
}
//
// 4) getimage:抓取屏幕左上角一块区域,复制到别处。
//
if (!EFI_ERROR (getimage (&Grabbed, 40, 140, 160, 120))) {
putimage (620, 500, &Grabbed);
DeleteImage (&Grabbed);
}
DeleteImage (&Sprite);
//
// 高级图形(P5):底部一条演示带(先清出干净背景)。
//
{
INT32 Y0 = H - 150;
setfillcolor (RGB (16, 16, 48));
solidrectangle (0, Y0 - 10, W - 1, Y0 + 100);
// 圆角矩形(边框 + 填充)
setlinestyle (PS_SOLID, 1);
setlinecolor (WHITE);
setfillcolor (RGB (40, 120, 200));
fillroundrect (30, Y0, 150, Y0 + 90, 40, 40);
// 扇形(吃豆人)
setfillcolor (YELLOW);
fillpie (170, Y0, 290, Y0 + 90, 35, 300);
// 圆弧
setlinecolor (LIGHTCYAN);
arc (310, Y0, 430, Y0 + 90, 0, 200);
// 多边形(三角形,边框 + 填充)
setlinecolor (WHITE);
setfillcolor (LIGHTGREEN);
{
POINT Tri[3] = {
{ 450, Y0 + 90 },
{ 510, Y0 },
{ 570, Y0 + 90 }
};
fillpolygon (Tri, 3);
}
// 折线(锯齿)
setlinecolor (LIGHTMAGENTA);
setlinestyle (PS_SOLID, 2);
{
POINT Zig[5] = {
{ 590, Y0 + 90 },
{ 610, Y0 },
{ 630, Y0 + 90 },
{ 650, Y0 },
{ 670, Y0 + 90 }
};
polyline (Zig, 5);
}
// 区域填充:先画空心矩形,再对内部 floodfill
setlinestyle (PS_SOLID, 1);
setlinecolor (WHITE);
rectangle (700, Y0, 800, Y0 + 90);
setfillcolor (RGB (220, 80, 160));
floodfill (750, Y0 + 45, RGB (16, 16, 48));
}
//
// 键盘操作提示。
//
setbkmode (TRANSPARENT);
settextcolor (WHITE);
settextstyle (16, 8);
outtextxy (20, H - 24, "Arrow keys=draw, Space=color, ESC=exit");
//
// 一次性刷新并结束批量绘图。
//
EndBatchDraw ();
//
// 键盘交互循环(P4):方向键移动画笔绘线。
//
PenX = W / 2;
PenY = H / 2;
Running = TRUE;
ColorIndex = 0;
while (Running) {
INT32 NewX;
INT32 NewY;
getmessage (&Msg, EX_KEY);
if (Msg.message != WM_KEYDOWN) {
continue;
}
NewX = PenX;
NewY = PenY;
switch (Msg.vkcode) {
case VK_LEFT: NewX -= 8; break;
case VK_RIGHT: NewX += 8; break;
case VK_UP: NewY -= 8; break;
case VK_DOWN: NewY += 8; break;
case VK_SPACE: ColorIndex++; break;
case VK_ESCAPE: Running = FALSE; break;
default: break;
}
if (!Running) {
break;
}
//
// 夹取到屏幕范围内。
//
if (NewX < 0) {
NewX = 0;
}
if (NewY < 0) {
NewY = 0;
}
if (NewX > W - 1) {
NewX = W - 1;
}
if (NewY > H - 1) {
NewY = H - 1;
}
//
// 从旧位置到新位置画一段线。
//
setlinecolor (Palette[ColorIndex % (sizeof (Palette) / sizeof (Palette[0]))]);
setlinestyle (PS_SOLID, 2);
line (PenX, PenY, NewX, NewY);
PenX = NewX;
PenY = NewY;
//
// 底部显示当前画笔位置与按键(先用背景色清除旧文字)。
//
setfillcolor (RGB (16, 16, 48));
solidrectangle (20, H - 48, 420, H - 28);
AsciiSPrint (
StatusBuf,
sizeof (StatusBuf),
"Pen: (%d, %d) vkcode=0x%02x",
PenX,
PenY,
Msg.vkcode
);
setbkmode (TRANSPARENT);
settextcolor (LIGHTCYAN);
settextstyle (16, 8);
outtextxy (20, H - 46, StatusBuf);
}
closegraph ();
//
// 恢复文本控制台。
//
gST->ConOut->ClearScreen (gST->ConOut);
Print (L"EasyX UEFI Demo 结束。分辨率: %d x %d\n", W, H);
return EFI_SUCCESS;
}
代码运行结果(新增的在最下面)

完整代码和编译后的 EFI下载:
做一个 UEFI 下的 EasyX 图形库(4)
这个版本加入了Easxy的键盘的函数。经过研究,我放弃了在 EasyX 中的鼠标支持,因此多次测试下来感觉一般的UEFI 使用环境下并不能很好的支持键盘。
编写测试代码实现下面的功能:
| 按键 | 功能 |
| 方向键 ↑↓←→ | 画笔从当前位置向该方向移动 8 像素,并画出一段连线(像涂鸦板) |
| 空格 | 之后画的线换一种颜色(红→绿→青→黄→品红→白循环) |
| ESC | 退出图形界面,回到文本控制台并打印 EasyX UEFI Demo 结束… |
| 任意移动后 | 屏幕底部实时显示 Pen: (x, y) vkcode=0x.. |
完整代码:
#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>
#include <Library/PrintLib.h>
#include "easyx.h"
/**
程序入口。
**/
EFI_STATUS
EFIAPI
UefiMain (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
INT32 W;
INT32 H;
INT32 PenX;
INT32 PenY;
IMAGE Sprite = { 0, 0, NULL }; // 使用前必须清零
IMAGE Grabbed = { 0, 0, NULL }; // getimage 会读取 Buffer 判断是否需释放
ExMessage Msg;
BOOLEAN Running;
UINTN ColorIndex;
CHAR8 StatusBuf[96];
CONST COLORREF Palette[] = {
LIGHTRED, LIGHTGREEN, LIGHTCYAN, YELLOW, LIGHTMAGENTA, WHITE
};
//
// 初始化绘图设备(跟随 GOP 最大分辨率模式)。
//
Status = initgraph (0, 0);
if (EFI_ERROR (Status)) {
Print (L"initgraph 失败: %r\n", Status);
return Status;
}
W = getwidth ();
H = getheight ();
//
// 批量绘图,减少刷新次数。
//
BeginBatchDraw ();
//
// 背景填充深蓝。
//
setbkcolor (RGB (16, 16, 48));
cleardevice ();
//
// 直线:不同颜色/线宽/线型。
//
setlinecolor (WHITE);
setlinestyle (PS_SOLID, 1);
line (20, 20, W - 20, 20);
setlinecolor (LIGHTGREEN);
setlinestyle (PS_SOLID, 3);
line (20, 40, W - 20, 60);
setlinecolor (YELLOW);
setlinestyle (PS_DASH, 1);
line (20, 80, W - 20, 80);
setlinecolor (LIGHTCYAN);
setlinestyle (PS_DOT, 1);
line (20, 100, W - 20, 100);
//
// 矩形。
//
setlinestyle (PS_SOLID, 1);
setlinecolor (LIGHTRED);
rectangle (40, 140, 200, 260);
setfillcolor (RGB (40, 120, 200));
setlinecolor (WHITE);
fillrectangle (240, 140, 400, 260);
setfillcolor (LIGHTMAGENTA);
solidrectangle (440, 140, 600, 260);
//
// 圆。
//
setlinecolor (WHITE);
circle (110, 360, 60);
setfillcolor (RGB (220, 160, 40));
setlinecolor (WHITE);
fillcircle (320, 360, 60);
setfillcolor (LIGHTGREEN);
solidcircle (520, 360, 60);
//
// 椭圆。
//
setlinecolor (LIGHTCYAN);
ellipse (40, 440, 260, 540);
setfillcolor (RGB (200, 60, 120));
setlinecolor (WHITE);
fillellipse (300, 440, 560, 540);
//
// 文字(P2):不同颜色/大小/背景模式。
//
settextcolor (WHITE);
settextstyle (16, 8);
outtextxy (620, 150, "EasyX for UEFI");
settextcolor (YELLOW);
settextstyle (24, 0); // 高 24,宽按比例
outtextxy (620, 180, "Hello, GOP!");
settextcolor (LIGHTGREEN);
settextstyle (32, 16);
setbkmode (TRANSPARENT);
outtextxy (620, 220, "0123456789");
settextcolor (BLACK);
setbkcolor (LIGHTGRAY);
setbkmode (OPAQUE);
settextstyle (16, 8);
outtextxy (620, 270, " OPAQUE text mode ");
setbkmode (TRANSPARENT);
settextcolor (LIGHTRED);
settextstyle (20, 10);
outtextxy (620, 320, "The quick brown fox");
outtextxy (620, 345, "jumps over lazy dog.");
//
// 图像(P3)。
//
// 1) 用 SetWorkingImage 把绘图重定向到内存图像,绘制一个“精灵”。
//
if (!EFI_ERROR (CreateImage (&Sprite, 80, 80))) {
SetWorkingImage (&Sprite);
setbkcolor (RGB (20, 20, 20));
cleardevice ();
setfillcolor (RGB (240, 200, 40));
solidcircle (40, 40, 34);
setfillcolor (BLACK);
solidcircle (28, 32, 5);
solidcircle (52, 32, 5);
setlinecolor (BLACK);
setlinestyle (PS_SOLID, 2);
line (24, 54, 56, 54);
SetWorkingImage (NULL); // 恢复到屏幕
// 2) 直接覆盖绘制到屏幕多个位置。
putimage (620, 400, &Sprite);
putimage (710, 400, &Sprite);
// 3) 透明贴图:跳过图像四角的深灰背景色。
putimagetrans (800, 400, &Sprite, RGB (20, 20, 20));
}
//
// 4) getimage:抓取屏幕左上角一块区域,复制到别处。
//
if (!EFI_ERROR (getimage (&Grabbed, 40, 140, 160, 120))) {
putimage (620, 500, &Grabbed);
DeleteImage (&Grabbed);
}
DeleteImage (&Sprite);
//
// 键盘操作提示。
//
setbkmode (TRANSPARENT);
settextcolor (WHITE);
settextstyle (16, 8);
outtextxy (20, H - 24, "Arrow keys=draw, Space=color, ESC=exit");
//
// 一次性刷新并结束批量绘图。
//
EndBatchDraw ();
//
// 键盘交互循环(P4):方向键移动画笔绘线。
//
PenX = W / 2;
PenY = H / 2;
Running = TRUE;
ColorIndex = 0;
while (Running) {
INT32 NewX;
INT32 NewY;
getmessage (&Msg, EX_KEY);
if (Msg.message != WM_KEYDOWN) {
continue;
}
NewX = PenX;
NewY = PenY;
switch (Msg.vkcode) {
case VK_LEFT: NewX -= 8; break;
case VK_RIGHT: NewX += 8; break;
case VK_UP: NewY -= 8; break;
case VK_DOWN: NewY += 8; break;
case VK_SPACE: ColorIndex++; break;
case VK_ESCAPE: Running = FALSE; break;
default: break;
}
if (!Running) {
break;
}
//
// 夹取到屏幕范围内。
//
if (NewX < 0) {
NewX = 0;
}
if (NewY < 0) {
NewY = 0;
}
if (NewX > W - 1) {
NewX = W - 1;
}
if (NewY > H - 1) {
NewY = H - 1;
}
//
// 从旧位置到新位置画一段线。
//
setlinecolor (Palette[ColorIndex % (sizeof (Palette) / sizeof (Palette[0]))]);
setlinestyle (PS_SOLID, 2);
line (PenX, PenY, NewX, NewY);
PenX = NewX;
PenY = NewY;
//
// 底部显示当前画笔位置与按键(先用背景色清除旧文字)。
//
setfillcolor (RGB (16, 16, 48));
solidrectangle (20, H - 48, 420, H - 28);
AsciiSPrint (
StatusBuf,
sizeof (StatusBuf),
"Pen: (%d, %d) vkcode=0x%02x",
PenX,
PenY,
Msg.vkcode
);
setbkmode (TRANSPARENT);
settextcolor (LIGHTCYAN);
settextstyle (16, 8);
outtextxy (20, H - 46, StatusBuf);
}
closegraph ();
//
// 恢复文本控制台。
//
gST->ConOut->ClearScreen (gST->ConOut);
Print (L"EasyX UEFI Demo 结束。分辨率: %d x %d\n", W, H);
return EFI_SUCCESS;
}
运行结果(中间的线段是运行时我使用方向键绘制的)

有兴趣的朋友可以试试。
CH32V467 官方开发板的一个“坑”
如果你想使用CH32V467官方开发板测试USB Host 功能,那么一定需要特别注意:原始设计并不支持 USB 端口取电(只能对板子供电)。为了确认这一点,你可以使用万用表测试对应的USB端口电压,如果你插入一个U盘,这个端口电压不到1V。
具体设计如下:两个端口分别使用了一个CH213K 的理想二极管。

如果你想征程测试,需要去掉 CH213K, 然后短接 R45 R45。
做一个 UEFI 下的 EasyX 图形库(3)
继续完善 EasyX,这次增加几个了图像处理的函数,可以复制图像到指定位置。有兴趣的可以直接比较这一次的 EasyX.h 和之前的定义。
测试代码:
#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>
#include "easyx.h"
/**
等待用户按下任意键。
**/
STATIC
VOID
WaitAnyKey (
VOID
)
{
EFI_INPUT_KEY Key;
UINTN Index;
gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &Index);
gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
}
/**
程序入口。
**/
EFI_STATUS
EFIAPI
UefiMain (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
INT32 W;
INT32 H;
IMAGE Sprite;
IMAGE Grabbed;
//
// 初始化绘图设备(跟随 GOP 最大分辨率模式)。
//
Status = initgraph (0, 0);
if (EFI_ERROR (Status)) {
Print (L"initgraph 失败: %r\n", Status);
return Status;
}
W = getwidth ();
H = getheight ();
//
// 批量绘图,减少刷新次数。
//
BeginBatchDraw ();
//
// 背景填充深蓝。
//
setbkcolor (RGB (16, 16, 48));
cleardevice ();
//
// 直线:不同颜色/线宽/线型。
//
setlinecolor (WHITE);
setlinestyle (PS_SOLID, 1);
line (20, 20, W - 20, 20);
setlinecolor (LIGHTGREEN);
setlinestyle (PS_SOLID, 3);
line (20, 40, W - 20, 60);
setlinecolor (YELLOW);
setlinestyle (PS_DASH, 1);
line (20, 80, W - 20, 80);
setlinecolor (LIGHTCYAN);
setlinestyle (PS_DOT, 1);
line (20, 100, W - 20, 100);
//
// 矩形。
//
setlinestyle (PS_SOLID, 1);
setlinecolor (LIGHTRED);
rectangle (40, 140, 200, 260);
setfillcolor (RGB (40, 120, 200));
setlinecolor (WHITE);
fillrectangle (240, 140, 400, 260);
setfillcolor (LIGHTMAGENTA);
solidrectangle (440, 140, 600, 260);
//
// 圆。
//
setlinecolor (WHITE);
circle (110, 360, 60);
setfillcolor (RGB (220, 160, 40));
setlinecolor (WHITE);
fillcircle (320, 360, 60);
setfillcolor (LIGHTGREEN);
solidcircle (520, 360, 60);
//
// 椭圆。
//
setlinecolor (LIGHTCYAN);
ellipse (40, 440, 260, 540);
setfillcolor (RGB (200, 60, 120));
setlinecolor (WHITE);
fillellipse (300, 440, 560, 540);
//
// 文字(P2):不同颜色/大小/背景模式。
//
settextcolor (WHITE);
settextstyle (16, 8);
outtextxy (620, 150, "EasyX for UEFI");
settextcolor (YELLOW);
settextstyle (24, 0); // 高 24,宽按比例
outtextxy (620, 180, "Hello, GOP!");
settextcolor (LIGHTGREEN);
settextstyle (32, 16);
setbkmode (TRANSPARENT);
outtextxy (620, 220, "0123456789");
settextcolor (BLACK);
setbkcolor (LIGHTGRAY);
setbkmode (OPAQUE);
settextstyle (16, 8);
outtextxy (620, 270, " OPAQUE text mode ");
setbkmode (TRANSPARENT);
settextcolor (LIGHTRED);
settextstyle (20, 10);
outtextxy (620, 320, "The quick brown fox");
outtextxy (620, 345, "jumps over lazy dog.");
//
// 图像(P3)。
//
// 1) 用 SetWorkingImage 把绘图重定向到内存图像,绘制一个“精灵”。
//
if (!EFI_ERROR (CreateImage (&Sprite, 80, 80))) {
SetWorkingImage (&Sprite);
setbkcolor (RGB (20, 20, 20));
cleardevice ();
setfillcolor (RGB (240, 200, 40));
solidcircle (40, 40, 34);
setfillcolor (BLACK);
solidcircle (28, 32, 5);
solidcircle (52, 32, 5);
setlinecolor (BLACK);
setlinestyle (PS_SOLID, 2);
line (24, 54, 56, 54);
SetWorkingImage (NULL); // 恢复到屏幕
// 2) 直接覆盖绘制到屏幕多个位置。
putimage (620, 400, &Sprite);
putimage (710, 400, &Sprite);
// 3) 透明贴图:跳过图像四角的深灰背景色。
putimagetrans (800, 400, &Sprite, RGB (20, 20, 20));
}
//
// 4) getimage:抓取屏幕左上角一块区域,复制到别处。
//
if (!EFI_ERROR (getimage (&Grabbed, 40, 140, 160, 120))) {
putimage (620, 500, &Grabbed);
DeleteImage (&Grabbed);
}
DeleteImage (&Sprite);
//
// 一次性刷新并结束批量绘图。
//
EndBatchDraw ();
//
// 等待按键后清理退出。
//
WaitAnyKey ();
closegraph ();
//
// 恢复文本控制台。
//
gST->ConOut->ClearScreen (gST->ConOut);
Print (L"EasyX UEFI Demo 结束。分辨率: %d x %d\n", W, H);
return EFI_SUCCESS;
}
运行结果:

因为函数涉及到了内存操作,因此无法再像之前一样用EDK2 自带的模拟器进行测试。这篇以及后面的系列文章改用 QEMU 进行测试。对应的 QEMU 启动命令如下:
qemu-system-x86_64 -bios OVMF.fd -m 1024 -usb -device usb-mouse -drive format=raw,file=fat:rw:./my_efi_app/ -serial stdio -net none
测试时,请在 QEMU目录中创建一个 my_efi_app 目录,并将需要测试的EFI 放入其中。
完整的代码已经编译后的 EFI文件:
WCH CH32V205RCT 开发板
这款开发板拿到手后可能会遇到 WCH LinkE 无法下载的问题。

关键点在于 WCH-LinkE Series 中必须选择 Ch32V205 ,而不要选择 Ch32V20X

做一个 UEFI 下的 EasyX 图形库 (2)
继续前面提到的在 UEFI下重新制作一个 EasyX 的话题,这次加入 Ascii 显示的功能。
新增加了Ascii显示功能,可以调整字体大小。
直接上结果:

上述测试代的代码:
#include <Uefi.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/UefiLib.h>
#include "easyx.h"
/**
等待用户按下任意键。
**/
STATIC
VOID
WaitAnyKey (
VOID
)
{
EFI_INPUT_KEY Key;
UINTN Index;
gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &Index);
gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
}
/**
程序入口。
**/
EFI_STATUS
EFIAPI
UefiMain (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
EFI_STATUS Status;
INT32 W;
INT32 H;
//
// 初始化绘图设备(跟随 GOP 最大分辨率模式)。
//
Status = initgraph (0, 0);
if (EFI_ERROR (Status)) {
Print (L"initgraph 失败: %r\n", Status);
return Status;
}
W = getwidth ();
H = getheight ();
//
// 批量绘图,减少刷新次数。
//
BeginBatchDraw ();
//
// 背景填充深蓝。
//
setbkcolor (RGB (16, 16, 48));
cleardevice ();
//
// 直线:不同颜色/线宽/线型。
//
setlinecolor (WHITE);
setlinestyle (PS_SOLID, 1);
line (20, 20, W - 20, 20);
setlinecolor (LIGHTGREEN);
setlinestyle (PS_SOLID, 3);
line (20, 40, W - 20, 60);
setlinecolor (YELLOW);
setlinestyle (PS_DASH, 1);
line (20, 80, W - 20, 80);
setlinecolor (LIGHTCYAN);
setlinestyle (PS_DOT, 1);
line (20, 100, W - 20, 100);
//
// 矩形。
//
setlinestyle (PS_SOLID, 1);
setlinecolor (LIGHTRED);
rectangle (40, 140, 200, 260);
setfillcolor (RGB (40, 120, 200));
setlinecolor (WHITE);
fillrectangle (240, 140, 400, 260);
setfillcolor (LIGHTMAGENTA);
solidrectangle (440, 140, 600, 260);
//
// 圆。
//
setlinecolor (WHITE);
circle (110, 360, 60);
setfillcolor (RGB (220, 160, 40));
setlinecolor (WHITE);
fillcircle (320, 360, 60);
setfillcolor (LIGHTGREEN);
solidcircle (520, 360, 60);
//
// 椭圆。
//
setlinecolor (LIGHTCYAN);
ellipse (40, 440, 260, 540);
setfillcolor (RGB (200, 60, 120));
setlinecolor (WHITE);
fillellipse (300, 440, 560, 540);
//
// 文字(P2):不同颜色/大小/背景模式。
//
settextcolor (WHITE);
settextstyle (16, 8);
outtextxy (620, 150, "EasyX for UEFI");
settextcolor (YELLOW);
settextstyle (24, 0); // 高 24,宽按比例
outtextxy (620, 180, "Hello, GOP!");
settextcolor (LIGHTGREEN);
settextstyle (32, 16);
setbkmode (TRANSPARENT);
outtextxy (620, 220, "0123456789");
settextcolor (BLACK);
setbkcolor (LIGHTGRAY);
setbkmode (OPAQUE);
settextstyle (16, 8);
outtextxy (620, 270, " OPAQUE text mode ");
setbkmode (TRANSPARENT);
settextcolor (LIGHTRED);
settextstyle (20, 10);
outtextxy (620, 320, "The quick brown fox");
outtextxy (620, 345, "jumps over lazy dog.");
//
// 一次性刷新并结束批量绘图。
//
EndBatchDraw ();
//
// 等待按键后清理退出。
//
WaitAnyKey ();
closegraph ();
//
// 恢复文本控制台。
//
gST->ConOut->ClearScreen (gST->ConOut);
Print (L"EasyX UEFI Demo 结束。分辨率: %d x %d\n", W, H);
return EFI_SUCCESS;
}
完整的代码和编译后的 EFI 文件下载:
CH32V407 I2C 扫描代码
这里提供一个 CH32V407 I2C 设备扫描代码,可以用于调试I2C 设备。使用默认的 PB6-SCL / PB7-SDA 两个引脚。
/*
*@Note
7-bit address mode, master/slave mode, transceiver routine:
I2C1_SCL(PB6)\I2C1_SDA(PB7).
*/
#include "debug.h"
#define I2C_TIMEOUT 0xFFFF
/*********************************************************************
* @fn IIC_Init
*
* @brief Initializes the IIC peripheral.
*
* @return none
*/
void IIC_Init(u32 bound)
{
GPIO_InitTypeDef GPIO_InitStructure = {0};
I2C_InitTypeDef I2C_InitTSturcture = {0};
RCC_PB2PeriphClockCmd(RCC_PB2Periph_GPIOB | RCC_PB2Periph_AFIO, ENABLE);
RCC_PB1PeriphClockCmd(RCC_PB1Periph_I2C1, ENABLE);
/* 不使用重映射,I2C1走PB6/PB7 */
GPIO_PinRemapConfig(GPIO_Remap_I2C1, DISABLE);
GPIO_SetBits(GPIOB, GPIO_Pin_6 | GPIO_Pin_7);
I2C_InitTSturcture.I2C_ClockSpeed = bound;
I2C_InitTSturcture.I2C_Mode = I2C_Mode_I2C;
I2C_InitTSturcture.I2C_DutyCycle = I2C_DutyCycle_16_9;
I2C_InitTSturcture.I2C_OwnAddress1 = 0;
I2C_InitTSturcture.I2C_Ack = I2C_Ack_Enable;
I2C_InitTSturcture.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_Init(I2C1, &I2C_InitTSturcture);
I2C_Cmd(I2C1, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_High;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_High;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
/* 等待某个事件,带超时 */
static int I2C_WaitEvent(I2C_TypeDef *I2Cx, uint32_t event)
{
uint32_t timeout = I2C_TIMEOUT;
while(!I2C_CheckEvent(I2Cx, event))
{
if(--timeout == 0)
return 0;
}
return 1;
}
/* 扫描单个地址是否存在
* addr: 7-bit地址
* 返回: 1=存在, 0=不存在
*/
int I2C_CheckDevice(uint8_t addr)
{
uint32_t timeout;
/* 等待总线空闲 */
timeout = I2C_TIMEOUT;
while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY))
{
if(--timeout == 0)
return 0;
}
/* 产生START */
I2C_GenerateSTART(I2C1, ENABLE);
if(!I2C_WaitEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT))
goto fail;
/* 发送7位地址,写方向 */
I2C_Send7bitAddress(I2C1, addr << 1, I2C_Direction_Transmitter);
timeout = I2C_TIMEOUT;
while(1)
{
/* ADDR: 收到ACK */
if(I2C_GetFlagStatus(I2C1, I2C_FLAG_ADDR))
{
/* 清ADDR标志 */
(void)I2C1->STAR1;
(void)I2C1->STAR2;
I2C_GenerateSTOP(I2C1, ENABLE);
return 1;
}
/* AF: 无应答 */
if(I2C_GetFlagStatus(I2C1, I2C_FLAG_AF))
{
I2C_ClearFlag(I2C1, I2C_FLAG_AF);
goto fail;
}
if(--timeout == 0)
goto fail;
}
fail:
I2C_GenerateSTOP(I2C1, ENABLE);
return 0;
}
/* 扫描I2C设备 */
void I2C_ScanBus(void)
{
uint8_t addr;
uint8_t found = 0;
printf("I2C bus scanning...\r\n");
for(addr = 0x08; addr <= 0x77; addr++)
{
if(I2C_CheckDevice(addr))
{
printf("Found device at 0x%02X\r\n", addr);
found++;
}
Delay_Ms(2);
}
if(found == 0)
printf("No I2C device found.\r\n");
else
printf("Scan done, total %d device(s).\r\n", found);
}
/*********************************************************************
* @fn main
*
* @brief Main program.
*
* @return none
*/
int main(void)
{
SystemCoreClockUpdate();
Delay_Init();
USART_Printf_Init(115200);
printf("SystemClk:%d\r\n", SystemClock);
printf("SystemCoreClk:%d\r\n", SystemCoreClock);
printf("ChipID:%08x\r\n", DBGMCU_GetCHIPID());
printf("IIC Host mode\r\n");
IIC_Init(100000);
while(1)
{
I2C_ScanBus();
printf("Rescan after 2 seconds...\r\n\r\n");
Delay_Ms(2000);
}
}
运行结果
