之前使用 WCH 家的 Ch9350 制作了一些板卡,但是这个芯片焊接对于我来说是有一些难度的。因此,这里专门设计了一个 ch9350的测试小板能够方便的进行原型验证。
电路图如下,相当于这个芯片的最小系统,外围软件只有2个电容:

PCB设计如下:

3D 预览如下:

焊接实物如下:

可以看到这个板子的优点如下:
- 外部线路简单,只预留了必要的引脚;
- 引出了2个USB端口,可以同时支持2个USB HID 设备
- 体型小巧便于在原型上使用
电路图和 PCB 下载:
之前使用 WCH 家的 Ch9350 制作了一些板卡,但是这个芯片焊接对于我来说是有一些难度的。因此,这里专门设计了一个 ch9350的测试小板能够方便的进行原型验证。
电路图如下,相当于这个芯片的最小系统,外围软件只有2个电容:

PCB设计如下:

3D 预览如下:

焊接实物如下:

可以看到这个板子的优点如下:
电路图和 PCB 下载:
ESP32-C3 不支持 Windows 7, 意思是插上之后设备管理器中会出现Yellow Bang,无法识别串口。这样会给我们的开发和调试造成一定困难。经过研究可以通过修改Arduino Drivers 目录下面的 arduino-org.inf 来解决。
具体操作如下:
1.在[Strings]增加如下两行
LABZ.bootloader.name="Arduino ESP32-C3 bootloader"
LABZ.sketch.name="Arduiono ESP32-C3"
2.在 [DeviceList] 增加如下两行:
%LABZ.bootloader.name%=DriverInstall, USB\VID_2A03&PID_003A
%LABZ.sketch.name%=DriverInstall, USB\VID_2A03&PID_803A&MI_00
3.在[DeviceList.NTamd64]增加如下代码:
%LABZ.bootloader.name%=DriverInstall, USB\VID_303A&PID_1001
%LABZ.sketch.name%=DriverInstall, USB\VID_303A&PID_1001&MI_00
然后再像其他驱动一样安装即可:

修改后的 INF可以在这里下载:
SoundWire 是 MIPI 协会推出的关于音频的规范,有时候会被缩写为 SNDW。这个规范定义的总线有如下好处:
基本拓扑结构是【参考1】:其中的 ADC 可以理解为麦克风,DAC 可以理解为喇叭,下图就是一个 SoundWire Master 连接了2个麦克风和三个喇叭:

复杂一点:Multi Lanes拓扑如下, Clock 提供给 Codec/BT/DSP,然后多个Lane(Data[0]/Data[1]/Data[2]), 这样能够提升一个周期数据的传输数量。

因此,对于 PC 来说,SoundWire 是重新定义了 SoC 或者说南桥对 Codec 的数据传输协议,取代了之前的 HDA 总线。对于软件或者 BIOS 来说应该是透明的。
参考:
如果你在编写代码时有使用到类似 PciRead8() 这样的函数,那么可以通过在 INF 中加入 PciLib:
[LibraryClasses]
UefiLib
ShellCEntryLib
IoLib
PciLib
然后在代码中使用 include 调用头文件即可:
#include <Library/PciLib.h>
具体的 PciLib.h 定义如下:
#define PCI_LIB_ADDRESS(Bus, Device, Function, Register) \
(((Register) & 0xfff) | (((Function) & 0x07) << 12) | (((Device) & 0x1f) << 15) | (((Bus) & 0xff) << 20))
RETURN_STATUS
EFIAPI
PciRegisterForRuntimeAccess (
IN UINTN Address
);
UINT8
EFIAPI
PciRead8 (
IN UINTN Address
);
UINT8
EFIAPI
PciWrite8 (
IN UINTN Address,
IN UINT8 Value
);
UINT8
EFIAPI
PciOr8 (
IN UINTN Address,
IN UINT8 OrData
);
UINT8
EFIAPI
PciAnd8 (
IN UINTN Address,
IN UINT8 AndData
);
UINT8
EFIAPI
PciAndThenOr8 (
IN UINTN Address,
IN UINT8 AndData,
IN UINT8 OrData
);
UINT8
EFIAPI
PciBitFieldRead8 (
IN UINTN Address,
IN UINTN StartBit,
IN UINTN EndBit
);
UINT8
EFIAPI
PciBitFieldWrite8 (
IN UINTN Address,
IN UINTN StartBit,
IN UINTN EndBit,
IN UINT8 Value
);
UINT8
EFIAPI
PciBitFieldOr8 (
IN UINTN Address,
IN UINTN StartBit,
IN UINTN EndBit,
IN UINT8 OrData
);
UINT8
EFIAPI
PciBitFieldAnd8 (
IN UINTN Address,
IN UINTN StartBit,
IN UINTN EndBit,
IN UINT8 AndData
);
UINT8
EFIAPI
PciBitFieldAndThenOr8 (
IN UINTN Address,
IN UINTN StartBit,
IN UINTN EndBit,
IN UINT8 AndData,
IN UINT8 OrData
);
UINT16
EFIAPI
PciRead16 (
IN UINTN Address
);
UINT16
EFIAPI
PciWrite16 (
IN UINTN Address,
IN UINT16 Value
);
UINT16
EFIAPI
PciOr16 (
IN UINTN Address,
IN UINT16 OrData
);
UINT16
EFIAPI
PciAnd16 (
IN UINTN Address,
IN UINT16 AndData
);
UINT16
EFIAPI
PciAndThenOr16 (
IN UINTN Address,
IN UINT16 AndData,
IN UINT16 OrData
);
UINT16
EFIAPI
PciBitFieldRead16 (
IN UINTN Address,
IN UINTN StartBit,
IN UINTN EndBit
);
UINT16
EFIAPI
PciBitFieldWrite16 (
IN UINTN Address,
IN UINTN StartBit,
IN UINTN EndBit,
IN UINT16 Value
);
UINT16
EFIAPI
PciBitFieldOr16 (
IN UINTN Address,
IN UINTN StartBit,
IN UINTN EndBit,
IN UINT16 OrData
);
UINT16
EFIAPI
PciBitFieldAnd16 (
IN UINTN Address,
IN UINTN StartBit,
IN UINTN EndBit,
IN UINT16 AndData
);
UINT16
EFIAPI
PciBitFieldAndThenOr16 (
IN UINTN Address,
IN UINTN StartBit,
IN UINTN EndBit,
IN UINT16 AndData,
IN UINT16 OrData
);
UINT32
EFIAPI
PciRead32 (
IN UINTN Address
);
UINT32
EFIAPI
PciWrite32 (
IN UINTN Address,
IN UINT32 Value
);
UINT32
EFIAPI
PciOr32 (
IN UINTN Address,
IN UINT32 OrData
);
UINT32
EFIAPI
PciAnd32 (
IN UINTN Address,
IN UINT32 AndData
);
UINT32
EFIAPI
PciAndThenOr32 (
IN UINTN Address,
IN UINT32 AndData,
IN UINT32 OrData
);
UINT32
EFIAPI
PciBitFieldRead32 (
IN UINTN Address,
IN UINTN StartBit,
IN UINTN EndBit
);
UINT32
EFIAPI
PciBitFieldWrite32 (
IN UINTN Address,
IN UINTN StartBit,
IN UINTN EndBit,
IN UINT32 Value
);
UINT32
EFIAPI
PciBitFieldOr32 (
IN UINTN Address,
IN UINTN StartBit,
IN UINTN EndBit,
IN UINT32 OrData
);
UINT32
EFIAPI
PciBitFieldAnd32 (
IN UINTN Address,
IN UINTN StartBit,
IN UINTN EndBit,
IN UINT32 AndData
);
UINT32
EFIAPI
PciBitFieldAndThenOr32 (
IN UINTN Address,
IN UINTN StartBit,
IN UINTN EndBit,
IN UINT32 AndData,
IN UINT32 OrData
);
UINTN
EFIAPI
PciReadBuffer (
IN UINTN StartAddress,
IN UINTN Size,
OUT VOID *Buffer
);
UINTN
EFIAPI
PciWriteBuffer (
IN UINTN StartAddress,
IN UINTN Size,
IN VOID *Buffer
);
之前有介绍过,ESP32 没有 DAC 引脚,但是可以通过 PWM 来进行模拟,这次入手了 MCP4922 这款双通道 DAC 芯片。它使用 SPI 接口,提供12Bit 的输出精度。芯片引脚定义如下:

| 引脚编号 | 名称 | 介绍 |
| 1 | Vdd | 供电 2.7-5.5V |
| 2 | NC | 空 |
| 3 | CS# | SPI CS 引脚 |
| 4 | SCK | SPI Clock |
| 5 | SDI | SPI MOSI |
| 6 | NC | 空 |
| 7 | NC | 空 |
| 8 | LDAC# | 锁存,只有为低时,芯片才会从 Vouta和Voutb 输出电压 |
| 9 | SHDN# | 关闭,为低芯片停止工作 |
| 10 | Voutb | 输出B电压引脚 |
| 11 | Vrefb | 输出B 的参考电压,可以接入Vss到Vdd 以内的电压.这次测试接入了Vdd(3.3V) |
| 12 | Vss | 地 |
| 13 | Vrefa | 输出A 的参考电压,,可以接入Vss到Vdd 以内的电压.这次测试接入了Vdd(3.3V) |
| 14 | Vouta | 输出A的电压引脚 |
这次测试基于 https://github.com/michd/Arduino-MCP492X 提供的库文件,代码如下:
#include <MCP492X.h> // Include the library
#define PIN_SPI_CHIP_SELECT_DAC 25 // Or any pin you'd like to use
MCP492X myDac(PIN_SPI_CHIP_SELECT_DAC);
void setup() {
// put your setup code here, to run once:
myDac.begin();
}
void loop() {
for (int i=0;i<4096;i++) {
myDac.analogWrite(0, i);
myDac.analogWrite(1, 4095-i);
delay(1);
}
代码运行后使用示波器进行测试,结果如下:

修改代码,loop如下:
void loop() {
myDac.analogWrite(0, 0);
myDac.analogWrite(0, 4095);
}
可以看到波形如下:

参考:
本文使用的库:
MCP492X Datasheet
使用 Win+R 打开运行窗口,输入 shell:startup

会打开启动项文件夹,将想开机运行的程序快捷方式放入即可:

参考:
1.https://blog.csdn.net/qq_37372909/article/details/135615454 添加新的windows11系统开机登录后启动项
最近拿到一块 Ch32V305 的 EVT 板子,芯片的具体型号是CH32V305FBP6 ,它使用TSSOP20 封装,对于DIY 非常友好。这里介绍如何烧录和运行一个 USB KBMS 的代码。

代码来自官方的 EVT Package: CH32V307EVT\EVT\EXAM\USB\USBHS\DEVICE\CompositeKM
特别注意:和其他芯片不同,这款芯片必须使用 WCK LinkE 进行烧录。

MounRiver Studio 自带的烧录软件

需要特别注意的是在《CH32V30x 评估板说明及应用参考》上有提到还可以使用WCH-LinkUtility.exe 进行下载,但是实验中我发现这个会和 MounRiver Studio 自带的烧录软件冲突。打开WCH-LinkUtility后会提示你需要升级 WCH LinkE ,否则不能进行烧录,升级之后再使用MounRiver Studio ,它又会提示需要升级WCH LinkE ,否则不能进行烧录。
最后放上一个独立版本的键盘鼠标代码。
起因:每次维护网站都需要进行全站备份,目前使用压缩后下载全部文件的方法。但是这样每次都要下载很大的文件,费时费力。因此需要一种方法能够让我只下载有差异的部分。为此编写了一个目录比较并且提取差异的工具,例如:我基于 edk2202308 修改出来了 edk2202308modified ,然后用工具进行比较,比较后的差别会自动提取到 100916 目录下。

需要注意有如下几点:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
namespace ComparePatch
{
class Program
{
static void Printusage()
{
Console.WriteLine("Directory compare and patch utility");
Console.WriteLine("Usage:");
Console.WriteLine("cap Latest LastBackup PatchDir");
Console.WriteLine("");
}
static void CopyFileTo(String Filename, String TargetDir)
{
}
static void Main(string[] args)
{
if (args.Length != 2)
{
Printusage();
Environment.Exit(0);
}
// 指定要枚举的目录
string Latest, LastBackup, PatchDir;
Latest = args[0];
LastBackup = args[1];
if (Latest[Latest.Length - 1] != '\\')
{
Latest = Latest + '\\';
}
if (LastBackup[LastBackup.Length - 1] != '\\')
{
LastBackup = LastBackup + '\\';
}
PatchDir = Directory.GetCurrentDirectory() + "\\" + DateTime.Now.ToString("HHmmss") + "\\";
Console.WriteLine("Compare " + LastBackup + " with " + Latest);
Console.WriteLine("And put files into " + PatchDir);
try
{
// 使用Directory类的GetFiles方法获取所有文件,包括所有子目录
string[] filesInLastBackup = Directory.GetFiles(LastBackup, "*", SearchOption.AllDirectories);
Console.WriteLine("Files in " + LastBackup + " and all subdirectories:");
// 遍历所有文件并打印
foreach (string file in filesInLastBackup)
{
// 情况1
if (File.Exists(file.Replace(LastBackup, Latest)))
{
// 新目录下存在该文件
// 创建一个FileInfo对象
FileInfo NewFileInfo = new FileInfo(file);
FileInfo OldFileInfo = new FileInfo(file.Replace(LastBackup, Latest));
String PatchFileName = file.Replace(LastBackup, PatchDir);
if (NewFileInfo.Length != OldFileInfo.Length)
{
// 文件大小不同,判定为不同
Console.WriteLine("=========================");
Console.Write("New file would be copy to");
Console.WriteLine(PatchFileName);
if (!Directory.Exists(Path.GetDirectoryName(PatchFileName))) {
Directory.CreateDirectory(Path.GetDirectoryName(PatchFileName));
}
File.Copy(file, PatchFileName);
}
}
}
}
catch (Exception e)
{
Console.WriteLine("The process failed: {0}", e.ToString());
}
// Console.ReadKey();
}
}
}
可执行文件:
如果你有更专业的需求,可以考虑直接使用 GIT 这种来进行维护。