似乎国内使用 Windows IIS 架设 Wordpress 的用户非常少,以至于我遇到问题通常只能在英文网站中搜索到需要的信息。最近遇到了Wordpress 的网站健康提示“模组 GD未被安装或已被禁用”的问题。经过搜索答案非常简单:PHP 的配置文件 php.ini 中默认禁止了 GD2, 但是实际上内置的是GD。因此将对应的那一行取消注释,并且将 GD2 修改为 GD 即可。
Arduino Leonardo PWM 测试
用户可以从串口输入一个在1-255的数字,然后在D9上输出对应的占空比,PWM 频率是 62.5KHz。
需要注意:如果需要输出全低或者全高需要修改代码。
// Leonardo 测试,在 D9 上输出从串口给定的PWM 值
void setup() {
Serial.begin(115200);
/* Set speakers as outputs */
DDRB |= ((1 << 6) | (1 << 5));
/* PWM speaker timer initialization */
TCCR1A = ((1 << WGM10) | (1 << COM1A1) | (1 << COM1A0)
| (1 << COM1B1) | (1 << COM1B0)); // Set on match, clear on TOP
TCCR1B = ((1 << WGM12) | (1 << CS10)); // Fast 8-Bit PWM, F_CPU speed
}
void loop() {
if (Serial.available() > 0) {
//读取一个整数
int Value = Serial.parseInt();
Serial.print("Get:");
Serial.println(Value);
if (Value > 255)||(Value==0) {
Serial.println("Please input a 0<number<256");
} else {
OCR1A = Value;
}
}
}
【通知】近期将更换新的服务器
近期将切换服务器,因此可能出现服务器不稳定的情况,预计持续一周左右。
在此期间不会更新网站内容。
感谢支持,预祝春节快乐!
2024年1月19日,新服务器上线,相比之前增加了带宽和硬盘容量。
Step to UEFI (287)Cpp UEFI 002 Cout
我们看到的最简单的 C++ 代码是如下形式:
int main()
{
std::cout << "Hello World!\n";
}
问题来了:如何在 UEFI 下面实现这种形式的代码?根据【参考1】,cout << n; 中,<< 是个运算符,n 是个变量,运算符应该接的是变量,所以 cout是个变量,但是在C++中这种高级变量叫做对象。cout 是一个对象。
因此,我们可以通过定义 cout 这个对象,然后定义 << 这个运算符即可。完整代码如下:
#include <UEFI/UEFI.h>
#include <type_traits>
#define EFI_ERROR(status) ((status) != EFI_SUCCESS)
EFI_SYSTEM_TABLE* gSystemTable;
void printInt(int value) {
CHAR16 out[32];
CHAR16* ptr = out;
static_assert(std::is_unsigned_v<char16_t>);
if (value == 0)
{
gSystemTable->ConOut->OutputString(gSystemTable->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 = '-';
gSystemTable->ConOut->OutputString(gSystemTable->ConOut, ptr);
}
class ostream {
public:
void operator<<(int x);
};
void ostream::operator<<(int x) {
printInt(x);
return ;
}
ostream cout;
EFI_STATUS
efi_main(EFI_HANDLE /*image*/, EFI_SYSTEM_TABLE* systemTable)
{
gSystemTable=systemTable;
cout << 122;
gSystemTable->ConOut->OutputString(gSystemTable->ConOut, u"\r\n");
return EFI_SUCCESS;
}
运行结果如下:

已经非常像了。接下来还有一个 std 的问题。这个可以通过 Namespace来实现。“编写程序过程中,名称(name)可以是符号常量、变量、函数、结构、枚举、类和对象等等。工程越大,名称互相冲突性的可能性越大。另外使用多个厂商的类库时,也可能导致名称冲突。为了避免,在大规模程序的设计中,以及在程序员使用各种各样的 C++ 库时,这些标识符的命名发生冲突,标准 C++ 引入关键字 namespace(命名空间/名字空间/名称空间),可以更好地控制标识符的作用域。
例如,我们在 C 语言中,通过 static 可以限制名字只在当前编译单元内可见,在 C++ 中我们通过 namespace 来控制对名字的访问。”【参考2】
修改代码如下形式:
namespace std {
class ostream {
public:
void operator<<(int x);
};
void ostream::operator<<(int x) {
printInt(x);
return ;
}
ostream cout;
}
namespace 是C++中的关键字,用来定义一个命名空间,语法格式为:
namespace name{
//variables, functions, classes
}
name是命名空间的名字,它里面可以包含变量、函数、类、typedef、#define 等,最后由{ }包围【参考3】。
我们就可以直接使用 std::cout << 122; 这种形式了。接下来,还有如何实现 std::cout << 122 << 13; 有兴趣的朋友可以继续研究。
参考:
- https://blog.csdn.net/u011386173/article/details/121085201
- https://baijiahao.baidu.com/s?id=1662580430712018597&wfr=spider&for=pc
- https://c.biancheng.net/view/2192.html
Windows 内存占用工具
最近因为测试需要一款能够占用内存的软件,于是求助天杀,请他帮忙编写了一个能够占用指定内存大小的代码。
在使用之前因为微软的限制需要对 Windows进行一些设定:
1.运行 gpedit.msc ,打开“本地组策略编辑器”

2.找到位于 “计算机配置”-> “Windows设置”->“安全设置”->“本地策略”->“用户权限分配”中的“锁定内存页”

3.接下来的目标是将“Administrators”加入其中。点击“添加用户组或组”。

4.点击“对象类型”按钮,勾选其中的“组”

5. 之后在“输入对象名称来选择”中输入“Administrators”(注意末尾有“s”),然后点击“检查名称”按钮

6.重启系统后以管理员权限打开 cmd 串口。这时候你可能遇到无法正常显示汉字的问题,例如:

7. 使用 chcp 936 切换到中文,再次运行即可,程序运行之后要求你输入的需要占用的内存,比如,这里输入 1024 ,可以在任务管理器中看到内存使用率升高了。按任意键之后释放占用的内存。

8.还可以运行多个程序方便进行内存调整

源代码和可执行程序:
串口速度测试工具
写了一个简单的串口测试工具,测试的是写入的速度。简单的说,就是打开串口,然后向里面写入数值,计算写入耗费的时间。通常来说,我们使用 USB 转串口设备,决定速度的因素有两个:1. USB 处理数据的时间 2.设备转串口的速度。其中最主要的因素是后者。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO.Ports; // For SerialPort
using System.Threading;
using System.IO;
using System.Diagnostics;
namespace _433CMD
{
class Program
{
const int COUNTER = 3;
static void Main(string[] args)
{
Stopwatch stopwatch = new Stopwatch();
if (args.Count() != 2)
{
Console.WriteLine("Please input COM PORT Number and ON or OFF");
Console.WriteLine("Usage:");
Console.WriteLine("SST COM3 115200");
Environment.Exit(1);
}
if (args[0].IndexOf("COM") == -1)
{
Console.WriteLine("Parameters error");
Environment.Exit(2);
}
int BaudRate;
if (int.TryParse(args[1], out BaudRate))
{
Console.WriteLine(BaudRate);
}
else
{
Console.WriteLine("BaudRate wrong");
}
SerialPort serialPort1 = new SerialPort();
serialPort1.PortName = args[0];
serialPort1.BaudRate = BaudRate;
serialPort1.DataBits = 8;
serialPort1.Parity = 0;
serialPort1.StopBits = (StopBits)1;
serialPort1.Encoding = System.Text.Encoding.GetEncoding(28591);
serialPort1.DtrEnable = true;
serialPort1.RtsEnable = true;
// Buffer 长1秒
Byte[] Buffer = new Byte[BaudRate/10];
stopwatch.Start();
try
{
// 打开串口
serialPort1.Open();
for (int i=0;i< COUNTER;i++)
{
serialPort1.Write(Buffer, 0, Buffer.Length);
}
serialPort1.Close();
}
catch (IOException e)
{
Console.WriteLine("Open " + args[1] + " failed ");
Console.WriteLine(e.Message);
Environment.Exit(4);
}
stopwatch.Stop();
TimeSpan ts = stopwatch.Elapsed;
Console.WriteLine("Send " + (Buffer.Length *COUNTER /1024).ToString() + "KB in " +(ts.TotalMilliseconds/1000).ToString("F3")+"s");
Console.WriteLine("Serial speed: "+(Buffer.Length * COUNTER/1024 / (ts.TotalMilliseconds/1000)).ToString("F3") +"KBytes/s");
Console.ReadLine();
}
}
}
使用 CH343 进行测试:

‘
编译后的 EXE 下载:
Ch569 项目从EXAM目录独立出来的方法
在使用 MounRiver 的过程中,你可能会遇到将 Exam中的例子搬移到其他路径之后无法工作的问题,这是由于Exam项目都依赖了\EXAM\SRC 下面的文件。解决这个问题的方法是:
- 移动目录到你需要的路径下;
- 打开项目编译会报错,会给出你找不到的文件名称
- 在原始项目中确认这个文件在项目中的位置,比如:下图的 Peripheral目录。
- 在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;
}
上述代码运行结果如下:

此外,还有析构函数,与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。
有兴趣的朋友可以自行研究。
参考:
- https://blog.csdn.net/qq_35243382/article/details/124369838
- 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:
- UefiPayloadPkg:Enhance the build processing for Universalpayload
- SplitFspBin.py cannot support FSP binary with child FV included
- Cache Disable should not be set by default in CR0 after ResetVector in x64 build
- SMM perf record is copied multiple times to FPDT table if multiple ReadyToBoot events are signaled
- In some cases, LocateHandleBuffer() may allocate a callee freed buffer when an error occurs
- OvmfPkg/IoMmuDxe: don’t rely on TPLs to manage concurrency
- Recent OVMF build.sh change breaks useful functionality
- UEFI cryptography agile solution – separate crypto algorithm (phase I)
- Use MpService2Ppi to wakeup CPU in Smm CpuS3
- Move RngLibTimer from MdePkg to MdeModulePkg
- EFI_RNG_PROTOCOL Describe the DRBG algorithm used in the Arm RNDR instruction
- Faulty Rng algo selection for Arm
- RngDxe assert
- Add New Intel Processor family for SMBIOS Type 4 from SMBIOS 3.7.0
- NetworkPkg: HTTP protocol throughput too small
- MailBoxVersion should be 0 according to the ACPI spec 6.5
- Pyrite support – Secure erase is only available if encryption is supported
- Remove assembly/tool logic that creates AP waking vector in 4G-20h
- MdeModulePkg/Bus/Ata/AtaBusDxe: Coverity scan flags SIGN_EXTENSION issue
- MdeModulePkg/Bus/Pci/NvmExpressPei: Coverity scan flags DEADCODE issue
- MdeModulePkg/Bus/Pci/UhciDxe: fix Coverity issues
- DynamicTablesPkg: Add support for generating ACPI ThermalZones
- DynamicTablesPkg: Add support for PCI IO using Qword resources
- MdeModulePkg/XhciDxe: Use Performance Timer for XHCI Timeouts
- MdeModulePkg/Bus/Pci/XhciDxe: Need to abort the command for command timeout
- BaseTools: Add support for LOONGARCH64 R_LARCH_RELAX relocation
- UefiPayloadPkg: Add FIT support
- SMBIOS BCD revision is not match SMBIOS version
- Xhci: Skip size round up for TRB when getting PCI device/host memory address
- MdePkg: various fixes to ARM/AArch64 SetJump/LongJump
- TlsLib should not have a list of Ciphers which may or may not agree with what is available
- MtrrLib modules and Unit test Enhancement
- Use the base SortLib for Redfish modules only
- evaluate the feasibility of using mbedtls as crypto library
- bogus RealTimeClockLib class interface: LibRtcVirtualNotifyEvent
- OvmfPkg/VirtioFsDxe: tolerate opening an absolute pathname relative to a regular file
- UefiDevicePathLib DevPathToTextAcpiEx overflows the device path node when searching for optional strings
- DynamicTablesPkg/TableHelperLib updates
- Update Edk2-pytools to latest versions
- duplicate installation of EFI_REAL_TIME_CLOCK_ARCH_PROTOCOL in RealTimeClockLib instances
- UefiCpuPkg/BaseXApicX2ApicLib: fix CPUID_V2_EXTENDED_TOPOLOGY detection
- OvmfPkg/AcpiPlatformDxe: Coverity scan flags FORWARD_NULL and UNUSED_VALUE issues
- RedfishPkg/RedfishLib: Return HTTP headers to caller
- ArmVirtPkg: support two PL011 UARTs
- Update MADT for ACPI 6.5, and add TRBE & ETE support
- Add Variable Policy Audit App and Shell Command
- Use CodeQL CLI
- TDVF: TdVmCall needs handle the retry results for MapGPA
- Update GetMaintainer to handle case where a package has only reviewers and no maintainers
- Add 0x0B/Platform Runtime Mechanism (PRM) in Address Space ID of Generic Address Structure (GAS)
- Fix build error when remove “-Wno-sometimes-uninitialized” option
和之前类似,这里放上一个完整版,补全了所有的三方库,大小是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开发框架