Ch569 项目从EXAM目录独立出来的方法

在使用 MounRiver 的过程中,你可能会遇到将 Exam中的例子搬移到其他路径之后无法工作的问题,这是由于Exam项目都依赖了\EXAM\SRC 下面的文件。解决这个问题的方法是:

  1. 移动目录到你需要的路径下;
  2. 打开项目编译会报错,会给出你找不到的文件名称
  3. 在原始项目中确认这个文件在项目中的位置,比如:下图的 Peripheral目录。
  4. 在Peripheral目录上点击右键查看属性,然后使用  edit 修改 Location。个人推荐直接将\EXAM\SRC放置到你的项目中,然后修改相对路径指向这个新的位置;

重复2-5步骤,直到所有的错误消失。

附件是一个按照上面修改过的 SimulateCDC 的例子, 可以放在任意的位置。

参考:

1.https://www.wch.cn/downloads/CH569EVT_ZIP.html

Step to UEFI (286)Cpp UEFI 001 类构造函数

C++中使用关键字 class 来定义类, 其基本形式如下:

class 类名
{
public:
//行为或属性 
protected:
//行为或属性
private:
//行为或属性
};

有一种比较特别的函数,被称为“构造函数”,名称和类名称相同。在创建类的对象时,编译器就运行一个构造函数。

设计一个Time类如下,其中有2个构造函数,其中是一个是构造函数的重载。如果在创建过程中有加参数,那么会调用重载之后的构造函数。

class Time {
public:
	Time() {//构造函数
		_hour = 9;
		_min = 17;
		_sec = 20;
		gSystemTable->ConOut->OutputString(gSystemTable->ConOut, u"Time init1\n\r");
	}
	Time(int hour,int min,int sec){//对构造函数的重载
        _hour=hour;
        _min=min;
        _sec=sec;
		gSystemTable->ConOut->OutputString(gSystemTable->ConOut, u"Time init2\n\r");
    }
	void Print() {
		printInt(gSystemTable->ConOut,_hour);
		printInt(gSystemTable->ConOut,_min);
		printInt(gSystemTable->ConOut,_sec);
		gSystemTable->ConOut->OutputString(gSystemTable->ConOut, u"\n\r");
	}
 
private:
	int _hour;
	int _min;
	int _sec;
};

完整代码如下:

#include <UEFI/UEFI.h>
#include <type_traits>

#define EFI_ERROR(status) ((status) != EFI_SUCCESS)

EFI_SYSTEM_TABLE* gSystemTable;

	
void printInt(EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL* conOut, int value) {
	CHAR16 out[32];
	CHAR16* ptr = out;
	static_assert(std::is_unsigned_v<char16_t>);
	if (value == 0)
	{
		conOut->OutputString(conOut, u"0");
		return;
	}

	ptr += 31;
	*--ptr = 0;
	int tmp = value;// >= 0 ? value : -value; 

	while (tmp)
	{
		*--ptr = '0' + tmp % 10;
		tmp /= 10;
	}

	if (value < 0) *--ptr = '-';
	conOut->OutputString(conOut, ptr);
}


class Time {
 public:
	Time() {//构造函数
		_hour = 9;
		_min = 17;
		_sec = 20;
		gSystemTable->ConOut->OutputString(gSystemTable->ConOut, u"Time init1\n\r");
	}
	Time(int hour,int min,int sec){//对构造函数的重载
        _hour=hour;
        _min=min;
        _sec=sec;
		gSystemTable->ConOut->OutputString(gSystemTable->ConOut, u"Time init2\n\r");
    }
	void Print() {
		printInt(gSystemTable->ConOut,_hour);
		printInt(gSystemTable->ConOut,_min);
		printInt(gSystemTable->ConOut,_sec);
		gSystemTable->ConOut->OutputString(gSystemTable->ConOut, u"\n\r");
	}
 
private:
	int _hour;
	int _min;
	int _sec;
};

EFI_STATUS
efi_main(EFI_HANDLE /*image*/, EFI_SYSTEM_TABLE* systemTable)
{
	EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL* conOut = systemTable->ConOut;
	gSystemTable=systemTable;
	
	Time time1;
	Time time2(1,2,3);
	
	return EFI_SUCCESS;
}

上述代码运行结果如下:

此外,还有析构函数,与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。

有兴趣的朋友可以自行研究。

参考:

  1. https://blog.csdn.net/qq_35243382/article/details/124369838
  2. https://blog.csdn.net/qq_39117115/article/details/133484338

EDK2 Stable202311来了

去年11月份 edk2 202311正式发布在:

https://github.com/tianocore/edk2/releases/tag/edk2-stable202311

从 History 来看,解决了不少Bug:

和之前类似,这里放上一个完整版,补全了所有的三方库,大小是136MB 左右。

链接:

https://pan.baidu.com/s/1eFC1XwfNTKj7hs_JRRieuw?pwd=LABZ

提取码: LABZ

个人建议:除非有特别明确的目的,否则没有必要追求最新的版本, 所谓 “小车能跑只管推”。

Step to UEFI (285)Cpp UEFI 000 实验环境的搭建

在之前的文章中【参考1】介绍了一个基于 Visual C++非常简单的 UEFI 开发框架。偶然的机会发现使用这个架构可以方便的实现C++ 的编写。于是,从这里开始,介绍如何使用这个框架学习简单的C++知识。

这次首先介绍实验环境的配置。在 C:\BuildBs\CppStudy\ 下创建 CPP 目录,其中放置测试使用到的文件:

其中 gfx.cpp 是源代码,g1.bat 是用于编译的批处理,这里直接使用批处理来进行编译,其中写入的都是绝对路径,这样更加直观容易理解发生了生么。

编译方法是打开 VS2019 X86 Command窗口(必须使用X64 窗口),在目录下运行 g1.bat 之后就会生成Cpp1.efi 的文件。

将这个文件拷贝到 Emulator 目录下就可以在模拟器中测试了。模拟器是来自EDk202308,有兴趣的朋友可以自行编译生成或者使用其他编译器。

之后进入 Emulator 运行 WinHost.exe ,即可运行 cpp1.efi 进行测试。

上述文件打包为一个文件有兴趣的朋友可以使用这个环境进行实验。

特别需要注意的是:在这一套环境中,当需要使用gSystemTable->ConOut->OutputString() 的时候,需要使用 u”XXX” 来定义字符串,这个是因为在 basic_types.h 中使用了如下定义:

using CHAR16 = char16_t;

这个对于我们编写代码影响不大,字符串使用 u作为前缀即可。

参考:

1. https://www.lab-z.com/sus/ 一个非常简单的UEFI Application开发框架

EasyX 生成计时器的视频

前一段想在一个视频中增加一个计时器的画面,然后忽然发现视频编辑工具并没有直接提供这样的功能。通常建议的手段是:直接插入其他计时器的视频。最简单的做法是在手机上安装一个秒表之类的软件,然后通过内置的录屏功能得到需要的视频。只是这样方法很难获得需要的背景颜色和文字颜色。

正好最近研究了 EasyX 的使用,于是通过编程生成图片,然后使用FFMpeg 把图片粘成视频。

这次测试的代码如下:

#include &lt;graphics.h>
#include &lt;time.h>
#include &lt;conio.h>
#include &lt;stdio.h>

#define WINWIDTH   1920
#define WINHEIGHT  1080

// 一秒25帧,一共1分钟
#define TOTALTIME  60*25

int main()
{
	TCHAR FilenameBuffer[256];
	
	// 初始化图形模式
	initgraph(WINWIDTH, WINHEIGHT);
	settextcolor(RED);
	settextstyle(500, 0, _T("Consolas"));

	for (int i = 0; i &lt; TOTALTIME; i++) {
		BeginBatchDraw();
		cleardevice();
		swprintf(FilenameBuffer, sizeof(FilenameBuffer), L"%02d:%03d", i / 24, (i * 1000 / 24)%1000);
		
		outtextxy(20, 20, FilenameBuffer);
		EndBatchDraw();

		swprintf(FilenameBuffer,sizeof(FilenameBuffer), L"%05d.png", i);
		printf("%ls\n", FilenameBuffer);
		saveimage(FilenameBuffer);
		//Sleep(20);
	}

	// 按任意键退出
	_getch();

	// 关闭图形模式
	closegraph();
	return 0;
}

用于生成视频的命令如下:

ffmpeg -r 25 -i %05d.png -b:v 4M output2.mp4

最终的结果可以在B站看到

【EasyX 生成1分钟计时器视频】

使用 EasyX 生成动画的方法

第一步:编写代码,在代码中需要将每一帧保存为图片格式。比如,下面是一个在圆中绘制另外一个圆的程序:

#include <graphics.h>
#include <time.h>
#include <conio.h>
#include <stdio.h>

#define WINWIDTH  1200
#define WINHEIGHT  600
#define RADIUS	  280

int main()
{
	TCHAR FilenameBuffer[256];
	
	// 初始化图形模式
	initgraph(WINWIDTH, WINHEIGHT);
	//getimage(pImage,0,0, WINWIDTH-1, WINHEIGHT-1);
	setfillcolor(RED);
	fillcircle(WINWIDTH/2, WINHEIGHT/2,RADIUS);
	setfillcolor(GREEN);
	
	for (int i = 1; i < 1001; i++) {
		BeginBatchDraw();
		fillpie(
			(WINWIDTH - RADIUS)/2, 
			(WINHEIGHT - RADIUS)/2, 
			(WINWIDTH + RADIUS)/2, 
			(WINHEIGHT + RADIUS)/2, 
			0, 3.14*2/1000*i);
		EndBatchDraw();

		swprintf(FilenameBuffer,sizeof(FilenameBuffer), L"%05d.jpg", i);
		printf("%ls\n", FilenameBuffer);
		saveimage(FilenameBuffer);
	}

	// 按任意键退出
	_getch();

	// 关闭图形模式
	closegraph();
	return 0;
}

运行这个程序之后,你会在目录下找到 00000.jpg 到 01000.jpg 文件

第二步,使用 FFMPEG 将这些文件“粘”成一个视频。在 https://github.com/BtbN/FFmpeg-Builds/releases 下载编译好的 FFMPEG 工具(对应 ffmpeg-master-latest-win64-gpl 这个名称的文件)。

使用的命令是:

ffmpeg -r 25 -i %05d.jpg -b:v 4M output2.mp4

其中 -r 25表示1秒25帧, -b:v 4M 设定输出视频码率。具体请根据需要酌情修改。

最终就可以生成一个40秒的视频:

最终制作的视频可以在 B站看到:

【EasyX 制作动画视频的例子】

Step to UEFI (284)最小的UEFI Application编译环境

前面介绍过一个最小的 UEFI Application编译器【参考1】,美中不足的是它还要依赖于 EDK2 的环境。这次介绍如何从 EDK2 中抽取所需的最小文件,从而实现一个最简单的编译环境。换句话说,之前介绍的是一个最小的用于生成 UEFI Application的编译器,这次介绍的是编译器和编译所需的环境。

实现的方法很简单:研究上一次的编译命令

x86_64-win32-tcc -I MdePkg/Include hello.c  -Wl,-subsystem=efiapp -nostdlib -o labz1.efi -DMDE_CPU_EBC

可以看到,指定了MdePkg/Include作为Include 路径,因此,只要我们将这个路径下依赖的文件提取出来即可。

最终实验发现提取如下的文件和目录就够了:

运行命令,编译之(需要注意使用 cmd 窗口):

生成文件labz1.efi, 模拟器测试工作正常:

整个环境只有200KB 左右。有兴趣的朋友可以自行研究。

同样的我们可以直接在 Visual C++ X64窗口中使用下面的命令编译:

cl -I MdePkg/Include hello.c
link.exe /ENTRY:_start /SUBSYSTEM:EFI_APPLICATION /LIBPATH:MdePkg/Include /NODEFAULTLIB /nologo /OUT:YourApp.efi hello.obj

同样可以正常运行:

参考:

1. https://www.lab-z.com/stu279tcc/ 介绍一个最小的UEFI Application 编译器

如何下载 Windows 11 指定版本

如果你只是想下载安装最新的 Windows 11 ,可以到 https://msdn.itellyou.cn/ 进行下载。但是如果你想获得一个指定的版本或者说不是正是发布的版本,上面的网站未必能够找到。这次推荐的使用 https://uupdump.net/ 这个网站。

以获得 Windows 11 22621.1250版本为例,介绍使用方法:

1.打开网站搜索 “22621 1250”

2.在搜索的结果中选择你需要的版本,比如:Windows 11 Insider Preview 10.0.22621.1250 (ni_release) amd64

3.点击链接进入下载界面,首先选择语言版本,通常推荐选择英语(美国)版本用于测试。因为很多测试工具是基于英文开发。比如,测试使用的工具可能会在设备管理器中搜索驱动名称,而驱动名称的英文和中文可能是不一样的。因此,如果你使用中文版本,可能会出现设备明明存在,但是工具却无法找到。

4.接着选择 SKU ,通常推荐使用专业版

5.为了便于分发和使用,推荐“下载并转化为 ISO 镜像文件”

6.点击“创建下载包”之后可以得到“22621.1250_amd64_en-us_professional_60821a74_convert.zip”这个文件,解压之后文件如下:

7.这些是下载镜像的脚本,以管理员权限打开cmd窗口后,运行“uup_download_windows.cmd”即可启动下载(特别注意:脚本不支持 Windows 7)。速度主要取决于你的网络情况。比如,我下载 22621 这个版本,最终的 ISO 是 5.9G,花费了2个小时。完成之后的界面如下:

8. 可以看到最终下载得到了一个 ISO 文件:22621.1250.230124-1741.NI_RELEASE_SVC_BETAFLT_PROD1_CLIENTPRO_OEMRET_X64FRE_EN-US.iso 这就是我们需要的 Windows 版本。

之后,就可以使用这个镜像进行系统的安装了。

Step to UEFI (283)QEMU下面的 LVGL 测试

LVGL(Light and Versatile Graphics Library,轻巧而多功能的图形库)是一个免费的开放源代码图形库,它提供创建具有易于使用的图形元素,精美的视觉效果和低内存占用的嵌入式GUI所需的一切。LVGL的项目作者是来自匈牙利首都布达佩斯的 Gábor Kiss-Vámosi 。Kiss 在2016年将其并发布在 GitHub 上。

当时叫 LittlevGL而不是LVGL,后来作者统一修改为 LVGL 甚至连仓库地址都改了。 像一般的开源项目的那样,它是作为一个人的项目开始的。 从那时起,陆续有近 100 名贡献者参与了项目开发,使得 LVGL 逐渐成为最受欢迎的嵌入式图形库之一。【参考1】

https://ay123.net/mystudy/845/ 由Bin Porting 到了UEFI上。他在 https://github.com/mxmks/LVGLUEFIRWNothing/tree/main 提供了完整的代码。这次就以这套代码为例进行测试。

需要注意的是:如果你在 EDK2 的 Emulator上测试,需要调整分辨率大于等于1024×768【参考2】. 如果你在 QEMU 上测试,那么需要使用如下命令打开USB 鼠标支持(默认情况下是支持 PS2 鼠标的,但是我测试没有成功):

qemu-system-x86_64 -bios ovmf.fd  -net none -hda fat:rw:c:\temp\ov -usb -device usb-mouse

之后,在 QEMU 中加载 USB 驱动(MdeModulePkg\Bus\Usb\UsbBusDxe\UsbBusDxe)和USB Mouse 驱动(MdeModulePkg\Bus\Usb\UsbMouseDxe\UsbMouseDxe)。

有兴趣的朋友不妨自行实验。

本问题提到的 USB 驱动和 USB Mouse 驱动可以通过 EDK2 编译获得,也可以在这里下载:

参考:

1. https://www.xpstem.com/article/10465

2.

ch569 上手测试

为了玩转 Ch569 ,需要准备如下硬件和软件:

硬件要求:

1.官方的 Ch569 开发板,官方买到的是两块插接在一起的,大多数情况下我们的实验都只使用到一块。

CH559 开发板

CH559 开发板

2.USB 3.0 A-A 线一根

软件要求(都可以在官网下载到):

1.项目管理和编译工具:MounRiver

2.官方 Demo: CH569EVT

3.烧写软件:WCHISPTool

安装完成CH569 后,以\CH569EVT\EVT\EXAM\USBSS\USBD\CH372Device为例。这个代码能够将Ch569模拟为Ch372设备。

编译之后的得到编译后的文件位于: obj\CH372Device.hex。

Ch569开发板的烧写方式是将HD0 Pin 接地然后再上电。设备管理器中会出现 “USB Module”这个设备:

之后我们打开WCHISP 工具,如果没有出现下图左侧的界面,那么需要点击右侧“Dedicated MCUs”按钮。

之后在上面的 Object File1 中选择上面提到的 CH372Device.hex 文件,最后点击 Download 即可。

烧录完成后,可以通过 EVT\EXAM\USBSS\USBD\CH372-HSPIDataTransDemo.zip 中的程序进行测试。这个程序会对设备进行一个速度读写测试,在我电脑上测试结果如下:

这个程序测试的是对一个端点写入,然后从另外的端口读取出来。