给 EXE 加入 Resource

最近忽然想起来一个问题:如何给一个做好的 EXE 加入其他的内容?比如,我编写一个 EXE 需要更改内容而又不想重新 Build 代码。

经过研究,可以通过给EXE 添加 Resource 的方法来实现这一目标。在 https://github.com/tc-hib/go-winres 这里有一个从命令行给 EXE 添加Resource 的项目。配合这个项目可以实现前述目标。

首先,编写一个测试代码,使用 VC 编写在 VS2019 下编译通过:

#include <windows.h>
#include <iostream>

// 回调函数用于枚举资源
BOOL CALLBACK EnumResNameProc(HMODULE hModule, LPCWSTR lpszType, LPWSTR lpszName, LONG_PTR lParam) {
    // 每找到一个资源,就增加计数
    (*(int*)lParam)++;
    return TRUE; // 继续枚举
}

int main()
{
    HMODULE hModule = GetModuleHandle(NULL); // 获取当前模块句柄
    int resourceCount = 0; // 用于计数的变量

    // 枚举所有RT_RCDATA类型的资源
    EnumResourceNames(hModule, RT_RCDATA, EnumResNameProc, (LONG_PTR)&resourceCount);

    std::cout << "Number of RT_RCDATA resources: " << resourceCount << std::endl;

    return 0;
}

代码非常简单,单纯的输出当前EXE RT_RCDATA类型的 Resource 数量。

接下来将go-winres.exe放在同一个目录下,然后运行下面的命令

go-winres.exe init

对应的会生成 winres 目录,其中有下面三个文件

前面2个是可以作为EXE 的图标的,winres.json是配置文件。例如,我们对这个目录放置一个 png 文件,然后修改如下,增加 RT_RCDATA的部分:

  "RT_GROUP_ICON": {
    "APP": {
      "0000": [
        "icon.png",
        "icon16.png"
      ]
    }
  },
  "RT_RCDATA": {
    "OTHER": {
      "0000": "2.png"
    }
  },  
  "RT_MANIFEST": {
    "#1": {
      "0409": {
        "identity": {
          "name": "",
          "version": ""

运行如下命令:

go-winres.exe patch ResourceTest.exe

工具会自动给 ResourceTest.exe 添加内容,之后再次运行:

如果使用 CFF 工具还可以看到多了一个 Resource。这样,你可以在代码中先判断Resource数量,然后再进行动作。

官方提供的版本(和 Github上的相同,0.3.3版本)

VC 编写的WAVE测试文件生成器

最近为了调试,写了一个 WAVE 的生成器,能够生成指定采样率,指定格式的 WAVE文件,这样可以在输出端直接查看输出是否是指定的数据。特别注意的是,WAVE 中,使用 int16 (有符号十六进制)来表示当前的信号,更具体来说,负数使用补码来表示。代码如下:

// WaveGenerator.cpp : This file contains the 'main' function. Program execution begins and ends there.
//

#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
#pragma pack(push, 1)
struct Chunk1 {          // 'R', 'I', 'F', 'F'
	char chunk_id[4];
	uint32_t chunk_size;
	char format[4];      // 'W', 'A', 'V', 'E'
};

struct Chunk2 {
	char chunk_id[4];
	uint32_t chunk_size;
	int16_t wFormatTag;
	int16_t nChannels;
	int32_t nSamplesPerSec;
	int32_t nAvgBytesPerSec;
	int16_t nBlockAlign;  // => num_channels * bits_per_sample / 8
	int16_t wBitsPerSample;
};

struct Chunk3 {
	char chunk_id[4];
	uint32_t chunk_size;
};
#pragma pack(pop) // 恢复到之前的对齐设置

#define AUDIODURATION  5  // 音频时长,以秒为单位
#define SAMPLEBITS  16  // 采样位
#define SAMPLERATE  96000  // 采样率
#define CHANNELNUM  2  // Channel 数量
int main()
{
	Chunk1 chunk1;
	chunk1.chunk_id[0] = 'R'; chunk1.chunk_id[1] = 'I'; chunk1.chunk_id[2] = 'F'; chunk1.chunk_id[3] = 'F';
	// 数据长度= Chunk123 总长 + RAW 数据长度(双声道,SAMPLEBITS Bits)
	chunk1.chunk_size = sizeof(Chunk1)-8 + sizeof(Chunk2) + sizeof(Chunk3)+ AUDIODURATION* CHANNELNUM *(SAMPLEBITS/8)* SAMPLERATE;
	chunk1.format[0] = 'W'; chunk1.format[1] = 'A'; chunk1.format[2] = 'V'; chunk1.format[3] = 'E';

	Chunk2 chunk2;

	chunk2.chunk_id[0] = 'f'; chunk2.chunk_id[1] = 'm'; chunk2.chunk_id[2] = 't'; chunk2.chunk_id[3] = ' ';
	chunk2.chunk_size = sizeof(Chunk2)-8;
	chunk2.wFormatTag = 0x0001; //PCM 格式
	chunk2.nChannels = CHANNELNUM;
	chunk2.nSamplesPerSec = SAMPLERATE;
	chunk2.nAvgBytesPerSec = CHANNELNUM * SAMPLERATE * (SAMPLEBITS / 8);
	chunk2.nBlockAlign = CHANNELNUM * (SAMPLEBITS / 8);
	chunk2.wBitsPerSample = SAMPLEBITS;

	Chunk3 chunk3;
	chunk3.chunk_id[0] = 'd'; chunk3.chunk_id[1] = 'a'; chunk3.chunk_id[2] = 't'; chunk3.chunk_id[3] = 'a';
	chunk3.chunk_size = AUDIODURATION * CHANNELNUM * SAMPLERATE * (SAMPLEBITS / 8);

	// 打开文件用于写入,以二进制模式
	FILE* file = fopen("C:\\WaveGenerator\\Debug\\example.wav", "wb");
	fwrite(&chunk1, sizeof(chunk1), 1, file);
	fwrite(&chunk2, sizeof(chunk2), 1, file);
	fwrite(&chunk3, sizeof(chunk3), 1, file);
	short int s=0,e=0;
	for (int i = 0; i < AUDIODURATION* SAMPLERATE; i++) {
		s = sin(i * 2 * 3.1415 / (SAMPLERATE/1000)) * (65535 / 2);
		// 左声道
		s++;
		fwrite(&s, sizeof(s), 1, file);
		// 右声道
		e = 0; 
		fwrite(&e, sizeof(e), 1, file);
	}

	fclose(file);
	//getchar();
}

小米2代体脂秤维修

最近给一个朋友维修了一下小米2代体脂秤。他遇到的问题是:插入电池后电池发热。拿到手后,看到了现象:装入电池后,电池组和线都会发烫。 

使用万用表进行测量,直接测量电池盒两端电阻为 170欧姆,装入电池后测量发现输出电流达到3A左右,这就是发热原因。 

根据部分资料【参考1】,上图中红色框芯片为CST34M96,用于蓝牙通讯;绿色框中芯片为CS1256,这是用于获得体脂数据并且处理的单片机;紫色方框中是稳压(降压)芯片,型号未知丝印D3E1,照片是拆除这个芯片后拍摄。 

不上电测量正常,上电后有问题猜测是电源芯片出了问题。于是拆除了稳压芯片。这部分电路如下: 

VDD 是电池盒输入;经过芯片降压输出为 AVCC,最终提供给后端使用。万用表测试表明AGND 和 GND 是连通的。 

拆除芯片后,从 AVCC 送入 3.3V,数码管会有动作,证明后面的芯片都是能够正常工作的,所以选择一个稳压芯片替换。 

最终,替换稳压/降压芯片后,外加更换C4电容(测量发现C4短路了,可能被击穿了?)后,可以正常工作。

总结:

  1. 检查 C4 是否短路,如果短路那么请更换,具体容值不太确定,这里可以去掉不上件
  2. 找一个稳压芯片,比如: ME XC6206 焊接在 U1 位置,如果找不到这个型号任何3.3V降压稳压同尺寸封装芯片都可以。

参考: 

1. https://www.mydigit.cn/forum.php?mod=viewthread&tid=286533&page=1 

Timed GPIO

Timed GPIO 或者称作 Timed I/O 是基于系统时钟产生或者接收信号的功能。从 EHL/TGL(11代)平台开始,Intel 引入了这个功能。

这个功能的背景是:一些时候我们需要产生精确的 GPIO 时序,但是因为系统CPU分配的原因,这一点难以实现。比如,我们在代码中每隔0.001s需要反转一次GPIO,如果使用代码来驱动的话,无法保证CPU每隔0.001s执行一次,这样会导致实际生成的信号会有“抖动”(Jitter)。因此,引入了 Time-Aware GPIO(TGPIO)。直接从硬件上保证能够在指定的时间产生信号。除了能够输出信号(GPO),还能够作为 GPI,这样可以精确的测量两个信号的间隔。

有兴趣的朋友可以更深入的进行研究。

参考:

1.https://edc.intel.com/content/www/us/en/design/ipla/software-development-platforms/servers/platforms/intel-pentium-silver-and-intel-celeron-processors-datasheet-volume-1-of-2/005/timed-gpio-time-sync/

2.https://edc.intel.com/content/www/us/en/design/products/platforms/processor-and-core-i3-n-series-datasheet-volume-1-of-2/001/timed-gpio/

3.https://edc.intel.com/content/www/us/en/design/ipla/software-development-platforms/servers/platforms/intel-pentium-silver-and-intel-celeron-processors-datasheet-volume-1-of-2/005/timed-gpio-time-sync/

WIM安装解决方案

很多年前,Ghost 是通用的安装方案,但是随着时代的进步,Ghost 方案逐渐被淘汰。取而代之的是WIM 的方案,最新的 OS 和 Driver 都使用 WIM 的方式进行分发。需要注意的是:市面上很多能够实现 WIM 安装的 Windows PE 盘会在安装过程中对安装后的Windows 插入应用程序或者驱动程序。这样的结果可能导致影响你的测试结果。因此,这次制作了Windows PE 盘以及一个WIM 安装工具。这一套具有如下优点:

  1. 安装过程中不会添加任何额外软件;
  2. 方便使用,启动WindowPE 之后只需要键盘即可选择 WIM 文件进行安装;
  3. 体积较小,包括 WindowsPE 镜像,整体不超过350MB。

接下来介绍如何部署和使用这一套软件。

首先,使用 Ventoy 制作一个 USB 启动盘。需要注意的是:这个U盘上的内容会被完全清除掉。

安装完成 Ventoy 之后,需要手工将Winpe062907.iso和AutoWim目录拷贝到U盘Ventoy 的 exFAT分区上。同时,将需要安装的 WIM 同样放置于根目录下。

之后,使用这个U盘进行启动。

启动Ventoy后从菜单上选择从 Winpe062907.iso 启动。

启动之后,这个 WinPE 会自动执行 AutoWim 目录中的InWim.bat批处理。自动列出U盘根目录下的所有 WIM 请用户选择,选择之后会对硬盘进行自动分区,然后部署 WIM 内容。最后会执行 bcdboot 修复ESP 分区。

相信这个工具会成为你测试的得力助手,有兴趣的朋友不妨试试看。

ISO 镜像下载

AutoWim 目录下载

工作的视频可以在下面看到:

=====================================================

2025年4月

1.更新了新的 ISO 镜像,支持从256G U盘启动;

ISO 镜像下载

2.更新了软件,在部署镜像的过程中可以显示当前进度百分比。

新的版本工作演示可以在下面看到:

Intel Microcode PDB 格式转INC工具

通常情况下,Intel Microcode 都是以 INC 文件 Release 的,并且官方提供了INC转PDB的工具。但是一些情况下,我们拿到的是 PDB 格式,这时候可以使用这里提供的反向转换工具,将PDB 重新转为 INC格式。

有兴趣的朋友可以先找一个INC文件,然后转为 PDB文件,再使用这里的工具将PDB转为 INC ,然后比较前后文件内容以便验证工具正确性。

Arduino 开发的 CH32V307 IIS 音频输出测试

这次介绍的是如何在 CH32V307上通过 Arduino 编程,从 IIS 接口输出一个正弦波然后通过 NS4168 播放出来。关于 NS4168的介绍,可以在【参考1】看到。

线路方面:

B12(I2S2_WS)连接到 LRCLK

B13(I2S2_CK) 连接到 BCKL

B15(I2S2_SD)连接到  SDAT

 同时特别提醒 NS4168 的CTRL 需要连接 3.3V

之后,给 Ch32v307烧录如下代码:

//#include "ch32v30x_spi.h"
/* Global Variable */
#define  Len    48*16*2
u16 I2S2_Tx[Len] = {
16383,16383,20660,20660,24864,24864,28922,28922,32767,32767,-29206,-29206,-25983,-25983,-23157,-23157,-20776,-20776,-18880,-18880,-17502,-17502,-16666,-16666,-16385,-16385,-16665,-16665,-17501,-17501,-18879,-18879,-20774,-20774,-23155,-23155,-25981,-25981,-29203,-29203,-32767,-32767,28925,28925,24867,24867,20663,20663,16386,16386,12109,12109,7906,7906,3847,3847,3,3,-3560,-3560,-6783,-6783,-9610,-9610,-11991,-11991,
-13887,-13887,-15266,-15266,-16102,-16102,-16383,-16383,-16104,-16104,-15268,-15268,-13891,-13891,-11996,-11996,-9615,-9615,-6790,-6790,-3568,-3568,-4,-4,3838,3838,7897,7897,12100,12100,16377,16377,20654,20654,24858,24858,28917,28917,32761,32761,-29210,-29210,-25987,-25987,-23161,-23161,-20779,-20779,-18882,-18882,-17504,-17504,-16667,-16667,-16385,-16385,-16665,-16665,-17500,-17500,-18876,-18876,-20771,-20771,
-23152,-23152,-25977,-25977,-29198,-29198,-32762,-32762,28931,28931,24873,24873,20669,20669,16392,16392,12115,12115,7911,7911,3852,3852,8,8,-3556,-3556,-6779,-6779,-9606,-9606,-11988,-11988,-13885,-13885,-15264,-15264,-16102,-16102,-16383,-16383,-16104,-16104,-15270,-15270,-13893,-13893,-11999,-11999,-9619,-9619,-6794,-6794,-3572,-3572,-10,-10,3833,3833,7891,7891,12094,12094,16371,16371,
20648,20648,24852,24852,28911,28911,32756,32756,-29215,-29215,-25992,-25992,-23164,-23164,-20782,-20782,-18885,-18885,-17505,-17505,-16667,-16667,-16385,-16385,-16664,-16664,-17498,-17498,-18874,-18874,-20768,-20768,-23148,-23148,-25972,-25972,-29194,-29194,-32756,-32756,28937,28937,24879,24879,20675,20675,16398,16398,12121,12121,7917,7917,3858,3858,13,13,-3551,-3551,-6775,-6775,-9602,-9602,-11985,-11985,
-13883,-13883,-15262,-15262,-16101,-16101,-16383,-16383,-16105,-16105,-15271,-15271,-13896,-13896,-12002,-12002,-9623,-9623,-6798,-6798,-3577,-3577,-15,-15,3827,3827,7885,7885,12088,12088,16365,16365,20642,20642,24846,24846,28906,28906,32751,32751,-29220,-29220,-25996,-25996,-23168,-23168,-20785,-20785,-18887,-18887,-17507,-17507,-16668,-16668,-16385,-16385,-16663,-16663,-17497,-17497,-18872,-18872,-20765,-20765,
-23144,-23144,-25968,-25968,-29189,-29189,-32751,-32751,28942,28942,24884,24884,20681,20681,16405,16405,12127,12127,7923,7923,3864,3864,18,18,-3546,-3546,-6770,-6770,-9598,-9598,-11982,-11982,-13880,-13880,-15261,-15261,-16100,-16100,-16383,-16383,-16106,-16106,-15273,-15273,-13898,-13898,-12005,-12005,-9626,-9626,-6803,-6803,-3582,-3582,-20,-20,3822,3822,7879,7879,12082,12082,16359,16359,
20636,20636,24840,24840,28900,28900,32746,32746,-29225,-29225,-26000,-26000,-23172,-23172,-20788,-20788,-18889,-18889,-17508,-17508,-16669,-16669,-16385,-16385,-16662,-16662,-17495,-17495,-18870,-18870,-20762,-20762,-23140,-23140,-25964,-25964,-29184,-29184,-32746,-32746,28948,28948,24890,24890,20687,20687,16411,16411,12133,12133,7929,7929,3869,3869,24,24,-3541,-3541,-6766,-6766,-9595,-9595,-11979,-11979,
-13878,-13878,-15259,-15259,-16099,-16099,-16383,-16383,-16107,-16107,-15274,-15274,-13900,-13900,-12008,-12008,-9630,-9630,-6807,-6807,-3587,-3587,-25,-25,3816,3816,7873,7873,12076,12076,16353,16353,20630,20630,24835,24835,28894,28894,32740,32740,-29230,-29230,-26005,-26005,-23176,-23176,-20791,-20791,-18892,-18892,-17510,-17510,-16670,-16670,-16385,-16385,-16661,-16661,-17493,-17493,-18867,-18867,-20759,-20759,
-23137,-23137,-25960,-25960,-29179,-29179,-32741,-32741,28953,28953,24896,24896,20693,20693,16417,16417,12139,12139,7935,7935,3875,3875,29,29,-3536,-3536,-6762,-6762,-9591,-9591,-11976,-11976,-13876,-13876,-15258,-15258,-16098,-16098,-16383,-16383,-16107,-16107,-15276,-15276,-13902,-13902,-12011,-12011,-9634,-9634,-6811,-6811,-3592,-3592,-31,-31,3810,3810,7867,7867,12070,12070,16347,16347,
20624,20624,24829,24829,28889,28889,32735,32735,-29235,-29235,-26009,-26009,-23179,-23179,-20794,-20794,-18894,-18894,-17512,-17512,-16671,-16671,-16385,-16385,-16661,-16661,-17492,-17492,-18865,-18865,-20756,-20756,-23133,-23133,-25955,-25955,-29174,-29174,-32735,-32735,28959,28959,24902,24902,20699,20699,16423,16423,12145,12145,7941,7941,3880,3880,34,34,-3532,-3532,-6757,-6757,-9587,-9587,-11973,-11973,
-13873,-13873,-15256,-15256,-16098,-16098,-16383,-16383,-16108,-16108,-15277,-15277,-13905,-13905,-12014,-12014,-9637,-9637,-6815,-6815,-3597,-3597,-36,-36,3805,3805,7862,7862,12064,12064,16341,16341,20618,20618,24823,24823,28883,28883,32730,32730,-29239,-29239,-26013,-26013,-23183,-23183,-20797,-20797,-18896,-18896,-17513,-17513,-16671,-16671,-16385,-16385,-16660,-16660,-17490,-17490,-18863,-18863,-20753,-20753,
-23129,-23129,-25951,-25951,-29170,-29170,-32730,-32730,28965,28965,24908,24908,20705,20705,16429,16429,12152,12152,7947,7947,3886,3886,39,39,-3527,-3527,-6753,-6753,-9584,-9584,-11970,-11970,-13871,-13871,-15255,-15255,-16097,-16097,-16383,-16383,-16109,-16109,-15279,-15279,-13907,-13907,-12017,-12017,-9641,-9641,-6820,-6820,-3601,-3601,-41,-41,3799,3799,7856,7856,12058,12058,16335,16335,
20612,20612,24817,24817,28878,28878,32724,32724,-29244,-29244,-26018,-26018,-23187,-23187,-20800,-20800,-18899,-18899,-17515,-17515,-16672,-16672,-16385,-16385,-16659,-16659,-17489,-17489,-18860,-18860,-20750,-20750,-23126,-23126,-25947,-25947,-29165,-29165,-32725,-32725,28970,28970,24914,24914,20711,20711,16435,16435,12158,12158,7953,7953,3892,3892,45,45,-3522,-3522,-6749,-6749,-9580,-9580,-11967,-11967,
-13869,-13869,-15253,-15253,-16096,-16096,-16383,-16383,-16110,-16110,-15280,-15280,-13909,-13909,-12020,-12020,-9645,-9645,-6824,-6824,-3606,-3606,-46,-46,3794,3794,7850,7850,12052,12052,16329,16329,20606,20606,24811,24811,28872,28872,32719,32719,-29249,-29249,-26022,-26022,-23190,-23190,-20803,-20803,-18901,-18901,-17516,-17516,-16673,-16673,-16385,-16385,-16658,-16658,-17487,-17487,-18858,-18858,-20747,-20747,
-23122,-23122,-25942,-25942,-29160,-29160,-32720,-32720,28976,28976,24920,24920,20717,20717,16441,16441,12164,12164,7958,7958,3897,3897,50,50,-3517,-3517,-6745,-6745,-9576,-9576,-11964,-11964,-13866,-13866,-15251,-15251,-16095,-16095,-16383,-16383,-16111,-16111,-15282,-15282,-13912,-13912,-12023,-12023,-9648,-9648,-6828,-6828,-3611,-3611,-52,-52,3788,3788,7844,7844,12046,12046,16323,16323,
20600,20600,24805,24805,28866,28866,32714,32714,-29254,-29254,-26026,-26026,-23194,-23194,-20806,-20806,-18903,-18903,-17518,-17518,-16674,-16674,-16385,-16385,-16657,-16657,-17486,-17486,-18856,-18856,-20744,-20744,-23118,-23118,-25938,-25938,-29155,-29155,-32714,-32714,28981,28981,24925,24925,20723,20723,16447,16447,12170,12170,7964,7964,3903,3903,55,55,-3512,-3512,-6740,-6740,-9573,-9573,-11961,-11961,
-13864,-13864,-15250,-15250,-16094,-16094,-16383,-16383,-16111,-16111,-15284,-15284,-13914,-13914,-12026,-12026,-9652,-9652,-6833,-6833,-3616,-3616,-57,-57,3782,3782,7838,7838,12040,12040,16316,16316,20594,20594,24799,24799,28861,28861,32709,32709,-29259,-29259,-26030,-26030,-23198,-23198,-20809,-20809,-18906,-18906,-17519,-17519,-16675,-16675,-16385,-16385,-16657,-16657,-17484,-17484,-18853,-18853,-20741,-20741,
-23115,-23115,-25934,-25934,-29150,-29150,-32709,-32709,28987,28987,24931,24931,20729,20729,16453,16453,12176,12176,7970,7970,3909,3909,60,60,-3507,-3507,-6736,-6736,-9569,-9569,-11958,-11958,-13862,-13862,-15248,-15248,-16094,-16094,-16383,-16383,-16112,-16112,-15285,-15285,-13916,-13916,-12029,-12029,-9656,-9656,-6837,-6837,-3621,-3621,-62,-62,3777,3777,7832,7832,12034,12034,16310,16310,
20588,20588,24793,24793,28855,28855,32703,32703,-29264,-29264,-26035,-26035,-23202,-23202,-20812,-20812,-18908,-18908,-17521,-17521,-16675,-16675,-16385,-16385,-16656,-16656,-17483,-17483,-18851,-18851,-20738,-20738,-23111,-23111,-25930,-25930,-29145,-29145,-32704,-32704,28993,28993,24937,24937,20735,20735,16459,16459,12182,12182,7976,7976,3914,3914,66,66,-3503,-3503,-6732,-6732,-9565,-9565,-11955,-11955,
-13859,-13859,-15247,-15247,-16093,-16093,-16383,-16383,-16113,-16113,-15287,-15287,-13919,-13919,-12032,-12032,-9659,-9659,-6841,-6841,-3625,-3625,-67,-67,3771,3771,7826,7826,12028,12028,16304,16304,20582,20582,24788,24788,28850,28850,32698,32698,-29268,-29268,-26039,-26039,-23205,-23205,-20815,-20815,-18910,-18910,-17523,-17523,-16676,-16676,-16385,-16385,-16655,-16655,-17481,-17481,-18849,-18849,-20735,-20735,
-23107,-23107,-25925,-25925,-29141,-29141,-32698,-32698,28998,28998,24943,24943,20741,20741,16465,16465,12188,12188,7982,7982,3920,3920,71,71,-3498,-3498,-6727,-6727,-9561,-9561,-11952,-11952,-13857,-13857,-15245,-15245,-16092,-16092,-16383,-16383,-16114,-16114,-15288,-15288,-13921,-13921,-12035,-12035,-9663,-9663,-6845,-6845,-3630,-3630,-73,-73,3766,3766,7821,7821,12022,12022,16298,16298,
20576,20576,24782,24782,28844,28844,32693,32693,-29273,-29273,-26043,-26043,-23209,-23209,-20818,-20818,-18913,-18913,-17524,-17524,-16677,-16677,-16385,-16385,-16654,-16654,-17479,-17479,-18846,-18846,-20732,-20732,-23104,-23104,-25921,-25921,-29136,-29136,-32693,-32693,29004,29004,24949,24949,20747,20747,16471,16471,12194,12194,7988,7988,3925,3925,76,76,-3493,-3493,-6723,-6723,-9558,-9558,-11949,-11949,
-13855,-13855,-15243,-15243,-16091,-16091,-16383,-16383,-16115,-16115,-15290,-15290,-13923,-13923,-12038,-12038,-9667,-9667,-6850,-6850,-3635,-3635,-78,-78,3760,3760,7815,7815,12016,12016,16292,16292,20570,20570,24776,24776,28838,28838,32688,32688,-29278,-29278,-26048,-26048,-23213,-23213,-20821,-20821,-18915,-18915,-17526,-17526,-16678,-16678,-16385,-16385,-16654,-16654,-17478,-17478,-18844,-18844,-20729,-20729,
-23100,-23100,-25917,-25917,-29131,-29131,-32688,-32688,29009,29009,24955,24955,20753,20753,16477,16477,12200,12200,7994,7994,3931,3931,82,82,-3488,-3488,-6719,-6719,-9554,-9554,-11946,-11946,-13852,-13852,-15242,-15242,-16090,-16090,-16383,-16383,-16115,-16115,-15291,-15291,-13926,-13926,-12041,-12041,-9670,-9670,-6854,-6854,-3640,-3640,-83,-83,3754,3754,7809,7809,12010,12010,
};

//#define  Len    10

//u16 I2S2_Tx[Len] = { 0x1111, 0x2222, 0x3333, 0x4444, 0x5555, 0x6666, 0x7777, 0x8888, 0x9999, 0xAAAA };

/*********************************************************************
 * @fn      I2S2_Init
 *
 * @brief   Init I2S2
 *
 * @return  none
 */
void I2S2_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure={0};
    I2S_InitTypeDef  I2S_InitStructure={0};

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC, ENABLE);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_15;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOC, &GPIO_InitStructure);

    I2S_InitStructure.I2S_Mode = I2S_Mode_MasterTx;
    I2S_InitStructure.I2S_Standard = I2S_Standard_Phillips;
    I2S_InitStructure.I2S_DataFormat = I2S_DataFormat_16b;
    I2S_InitStructure.I2S_MCLKOutput = I2S_MCLKOutput_Enable;//I2S_MCLKOutput_Disable;
    I2S_InitStructure.I2S_AudioFreq = I2S_AudioFreq_48k;
    I2S_InitStructure.I2S_CPOL = I2S_CPOL_High;
    I2S_Init(SPI2, &I2S_InitStructure);

    SPI_I2S_DMACmd( SPI2, SPI_I2S_DMAReq_Tx, ENABLE );
    I2S_Cmd(SPI2, ENABLE);
}

/*********************************************************************
 * @fn      DMA_Tx_Init
 *
 * @brief   Initializes the DMAy Channelx configuration.
 *
 * @param   DMA_CHx - x can be 1 to 7.
 *          ppadr - Peripheral base address.
 *          memadr - Memory base address.
 *          bufsize - DMA channel buffer size.
 *
 * @return  none
 */
void DMA_Tx_Init( DMA_Channel_TypeDef* DMA_CHx, u32 ppadr, u32 memadr, u16 bufsize)
{
    DMA_InitTypeDef DMA_InitStructure={0};

    RCC_AHBPeriphClockCmd( RCC_AHBPeriph_DMA1, ENABLE );

    DMA_DeInit(DMA_CHx);

    DMA_InitStructure.DMA_PeripheralBaseAddr = ppadr;
    DMA_InitStructure.DMA_MemoryBaseAddr = memadr;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
    DMA_InitStructure.DMA_BufferSize = bufsize;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init( DMA_CHx, &DMA_InitStructure );
}

void setup() {
    SPI_I2S_DeInit(SPI2);

    I2S2_Init();


}

void loop() {
    DMA_Tx_Init( DMA1_Channel5, (u32)&SPI2->DATAR, (u32)I2S2_Tx, Len);  
    DMA_Cmd( DMA1_Channel5, ENABLE );
    while( (!DMA_GetFlagStatus(DMA1_FLAG_TC5))){};
    DMA_Cmd( DMA2_Channel1, DISABLE );
}

即可工作。
I2S2_Tx 里面定义的是一个正弦波数据,通过如下C# 代码生成:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


namespace SinAudio
{
    class Program
    {
        public const int LOOPS = 16;

        static void Main(string[] args)
        {
            double sin,cos;
            for (int i = 0; i < LOOPS * 48; i++) {
                sin = (0.5 + Math.Sin(i * 2 * 3.1415 / 48))* 65535/2;
                //cos = (0.5 + Math.Cos(i * 2 * 3.1415 / 48)) * 65535 / 2;
                Console.Write("{0},{1},", (Int16)sin, (Int16)sin);// (Int16)() * );
                if (i % 32 == 0) {
                    Console.WriteLine("");
                }
            }
            Console.ReadLine();
        }
    }
}

数据可以看成是 48K 16Bits 采样结果,数据是int16 (负数使用补码形式表示)

工作的测试视频如下:

https://www.bilibili.com/video/BV1Yi421m7n8/?share_source=copy_web&vd_source=5ca375392c3dd819bfc37d4672cb6d54

WordPress 标题中文检测工具

因为设置上的原因,Wordpress 最好不要使用中文作为标题。我的网站设置上支持中文标题,但是因为更换服务器的缘故,中文标题的文章又出现了无法访问的问题。于是,编写一个工具扫描网站上的所有文章,通过检查标题的 url 是否带有“%”来判断是否有中文。

扫描全部页面的方式是通过扫描全部日期归档链接实现的,类似”https://www.lab-z.com/2022/03/”,发送 http 请求之后会对返回的结果进行分析,取出其中的 http://www.lab-z.com/ 为开头的 URL,然后进行检测,如果其中带有 “%”,那么就是带有汉字了。然后就可以根据指示在 WordPress 中手工进行修改。

代码比较简单,有兴趣的可以试试。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Http;
using System.Text.RegularExpressions;

namespace WordPressUrlChecker
{
    class Program
    {
        private static readonly HttpClient client = new HttpClient();
        // 定义并初始化一个字符串列表
        private static List<string> UrlToCheck = new List<string> {
                "https://www.lab-z.com/2024/04/",
                // 全部日期
         "https://www.lab-z.com/2005/10/"
        };

        static async Task Main(string[] args)
        {
            string url = "https://www.lab-z.com/";
            //Console.WriteLine(responseBody);
            //string pattern = @"(http|https)://([\w-]+\.)+[\w-]+(/[\w- ./?%&=]*)?";
            //https://www.lab-z.com/2006/05/
            string pattern = @"https://www\.lab-z\.com/[^\s]*";

            foreach (string uString in UrlToCheck) {
                Console.WriteLine("Checking in "+uString.ToString());
                string responseBody = await GetRequest(uString);
                MatchCollection matches = Regex.Matches(responseBody, pattern);
                foreach (Match match in matches)
                {
                    string getUrl = match.Value;
                    // 如果字符串中带有 " 那么就截取它之前的所有字符串
                    if (getUrl.IndexOf('\"')!=-1) {
                        getUrl= getUrl.Substring(0, getUrl.IndexOf('\"'));
                        //Console.WriteLine(getUrl);
                    }
                    
                    if (ContainsChinese(getUrl))
                    {
                        Console.WriteLine(getUrl);
                    }
                    
                }
            }
            /*
           
            */

            Console.ReadLine();
        }

        // 发送 HTTP 请求并且得到服务器返回
        static async Task<string> GetRequest(string url)
        {
            HttpResponseMessage response = await client.GetAsync(url);
            response.EnsureSuccessStatusCode();
            string responseBody = await response.Content.ReadAsStringAsync();
            return responseBody;
        }

        // 检查字符串是否含有汉字
        static bool ContainsChinese(string input)
        {
            for (int i = 0; i < input.Length; i++)
            {
                if (input[i] == '%')
                    return true;
            }
            return false;
        }


    }
}

CH32V305 模拟 Ch372的例子

前面介绍过如何使用 Arduino 环境进行 Ch32V305 的开发,这次带来的是一个 CH32V305 Arduino 实现模拟 Ch372的例子,参考的是 Ch32v307EVT 中的HS Device的代码。根据Exam中的CH32V30x_List.txt描述,这个CH372例子是模拟自定义USB设备(CH372设备),端点1,3下传,2,4上传,端点1下传的数据从端点3上传,不取反,端点2下传的数据从端点4上传,取反。但是,应该是描述存在错误,实际代码不是这样。

首先,改造代码,然后烧写到板子上。使用 USBView 查看,端点1 有一个 OUT 和 IN; 端点3是OUT,端点4是 IN, 端点5是OUT, 端点6是IN.

          ===>Endpoint Descriptor&lt;===
bLength:                           0x07
bDescriptorType:                   0x05
bEndpointAddress:                  0x01  -> Direction: OUT - EndpointID: 1
bmAttributes:                      0x02  -> Bulk Transfer Type
wMaxPacketSize:                  0x0200 = 0x200 max bytes
bInterval:                         0x00

          ===>Endpoint Descriptor&lt;===
bLength:                           0x07
bDescriptorType:                   0x05
bEndpointAddress:                  0x81  -> Direction: IN - EndpointID: 1
bmAttributes:                      0x02  -> Bulk Transfer Type
wMaxPacketSize:                  0x0200 = 0x200 max bytes
bInterval:                         0x00

          ===>Endpoint Descriptor&lt;===
bLength:                           0x07
bDescriptorType:                   0x05
bEndpointAddress:                  0x03  -> Direction: OUT - EndpointID: 3
bmAttributes:                      0x02  -> Bulk Transfer Type
wMaxPacketSize:                  0x0200 = 0x200 max bytes
bInterval:                         0x00

          ===>Endpoint Descriptor&lt;===
bLength:                           0x07
bDescriptorType:                   0x05
bEndpointAddress:                  0x84  -> Direction: IN - EndpointID: 4
bmAttributes:                      0x02  -> Bulk Transfer Type
wMaxPacketSize:                  0x0200 = 0x200 max bytes
bInterval:                         0x00

          ===>Endpoint Descriptor&lt;===
bLength:                           0x07
bDescriptorType:                   0x05
bEndpointAddress:                  0x05  -> Direction: OUT - EndpointID: 5
bmAttributes:                      0x02  -> Bulk Transfer Type
wMaxPacketSize:                  0x0200 = 0x200 max bytes
bInterval:                         0x00

          ===>Endpoint Descriptor&lt;===
bLength:                           0x07
bDescriptorType:                   0x05
bEndpointAddress:                  0x86  -> Direction: IN - EndpointID: 6
bmAttributes:                      0x02  -> Bulk Transfer Type
wMaxPacketSize:                  0x0200 = 0x200 max bytes
bInterval:                         0x00

对应处理的代码在 ch32v30x_usbhs_device.c 文件中。下面对代码进行研读。

代码中Ch32端点1 OUT 收到的数据直接放到端点1 IN中。

                    /* end-point 1 data out interrupt */
                    case USBHS_UIS_TOKEN_OUT | DEF_UEP1:
                        if ( intst &amp; USBHS_UIS_TOG_OK )
                        {
                            /* Write In Buffer */
                            USBHSD->UEP1_RX_CTRL ^= USBHS_UEP_R_TOG_DATA1;
                            RingBuffer_Comm.PackLen[RingBuffer_Comm.LoadPtr] = USBHSD->RX_LEN;
                            RingBuffer_Comm.LoadPtr ++;
                            if(RingBuffer_Comm.LoadPtr == DEF_Ring_Buffer_Max_Blks)
                            {
                                RingBuffer_Comm.LoadPtr = 0;
                            }
                            USBHSD->UEP1_RX_DMA = (uint32_t)(&amp;Data_Buffer[(RingBuffer_Comm.LoadPtr) * DEF_USBD_HS_PACK_SIZE]);
                            RingBuffer_Comm.RemainPack ++;
                            if(RingBuffer_Comm.RemainPack >= DEF_Ring_Buffer_Max_Blks-DEF_RING_BUFFER_REMINE)
                            {
                                USBHSD->UEP1_RX_CTRL = ((USBHSD->UEP1_RX_CTRL) &amp; ~USBHS_UEP_R_RES_MASK) | USBHS_UEP_R_RES_NAK;
                                RingBuffer_Comm.StopFlag = 1;
                            }
                        }
                        break;

端点3收到的数据取反后放到端点4上。

                  /* end-point 3 data out interrupt */
                    case USBHS_UIS_TOKEN_OUT | DEF_UEP3:
                        if ( intst &amp; USBHS_UIS_TOG_OK )
                        {
                            len = (uint16_t)(USBHSD->RX_LEN);
                            USBHSD->UEP3_RX_CTRL ^= USBHS_UEP_R_TOG_DATA1;
                            USBHSD->UEP3_RX_CTRL = ((USBHSD->UEP3_RX_CTRL) &amp; ~USBHS_UEP_R_RES_MASK) | USBHS_UEP_R_RES_NAK;
                            for(i=0; i&lt;len; i++)
                            {
                                USBHS_EP4_Tx_Buf[i] = ~USBHS_EP3_Rx_Buf[i];
                            }
                            USBHSD->UEP4_TX_LEN = len;
                            USBHSD->UEP4_TX_CTRL = (USBHSD->UEP4_TX_CTRL &amp; ~USBHS_UEP_T_RES_MASK) | USBHS_UEP_T_RES_ACK;
                        }
                        break;

类似的端点5收到的数据取反,通过端点6上传

                    /* end-point 5 data out interrupt */
                    case USBHS_UIS_TOKEN_OUT | DEF_UEP5:
                        if ( intst &amp; USBHS_UIS_TOG_OK )
                        {
                            len = (uint16_t)(USBHSD->RX_LEN);
                            USBHSD->UEP5_RX_CTRL ^= USBHS_UEP_R_TOG_DATA1;
                            USBHSD->UEP5_RX_CTRL = ((USBHSD->UEP5_RX_CTRL) &amp; ~USBHS_UEP_R_RES_MASK) | USBHS_UEP_R_RES_NAK;
                            for(i=0; i&lt;len; i++)
                            {
                                USBHS_EP6_Tx_Buf[i] = ~USBHS_EP5_Rx_Buf[i];
                            }
                            USBHSD->UEP6_TX_LEN = len;
                            USBHSD->UEP6_TX_CTRL = (USBHSD->UEP6_TX_CTRL &amp; ~USBHS_UEP_T_RES_MASK) | USBHS_UEP_T_RES_ACK;
                        }
                        break;

从代码上可以看出,Exam中的描述是存在一些问题的。

之后,再编写一个VC 代码,进行速度测试:

代码来自 Ch569 的EVT  Package, 有部分修改:

// 2003.09.08, 2003.12.28
//****************************************
//**  Copyright  (C)  W.ch  1999-2005   **
//**  Web:  http://www.winchiphead.com  **
//****************************************
//**  DLL for USB interface chip CH375  **
//**  C, VC5.0                          **
//****************************************
//
// USB总线接口芯片CH375的数据块测试程序 V1.0
// 南京沁恒电子有限公司  作者: W.ch 2003.12
// CH375-BLK  V1.0
// 运行环境: Windows 98/ME, Windows 2000/XP
// support USB chip: CH372/CH375
//

#include	<windows.h>
#include	<stdlib.h>
#include	<stdio.h>
#include	<conio.h>
#include	<winioctl.h>

#include	"CH375DLL.H"			
#pragma comment(lib,"CH375DLL")
#define		TEST_DATA_LEN		4096
#define		TEST_NUM     		1000
unsigned char	mReadBuf[TEST_DATA_LEN];
unsigned char	mWriteBuf[TEST_DATA_LEN];
//程序入口
void main (int argc,char **argv )
{
	unsigned long mLength, mTestCount, mErrCnt,mArg,mFirstTick,mLastTick;
	long long mTotal=0;
	double          speed;
	USHORT          mCount = 0;
	printf( "\nCH372/CH375 Bulk Data Test Program V1.1 ,   Copyright (C) W.ch 2004.12\n" );
	printf( "test data correctness \n" );
	mArg = TEST_DATA_LEN;

// 需要使用DLL则需要先加载,没有此句则会自动加载
	printf( "*** CH375OpenDevice: 0# \n" );
	if ( CH375OpenDevice( 0 ) == INVALID_HANDLE_VALUE ) return;  /* 使用之前必须打开设备 */

	memset(mWriteBuf, 0xFF, sizeof(mWriteBuf));
	
	mErrCnt=0;

	printf( "*** CH375ReadData: 1000 times 4M Byte ***\n" );

	mTotal = 0.0;
	for ( mTestCount=0; mTestCount < TEST_NUM; ++mTestCount )  // 循环测试
	{
		if(mTestCount == 0)
		{
			mFirstTick=GetTickCount();
		}
		mLength = mArg;
		if (CH375WriteEndP(0, 1, mWriteBuf, &mLength))  // 写入成功
		{
			mTotal += mLength;
			if (mLength == 0)
			{
				Sleep(0);  //放弃当前线程的时间片,防止CPU出现100%情况
			}
		}
		else
		{  // 写操作失败
			printf("S1-T%0ld-C%ld CH375WriteEndP return error, length=%d\n", mTestCount, mTestCount, mTotal);
		}

		mLength = mArg;
		if (CH375ReadEndP(0, 1, mReadBuf, &mLength))  // 接收成功
		{
			mTotal += mLength;
			if(mLength == 0 )
			{
				Sleep(0);  //放弃当前线程的时间片,防止CPU出现100%情况
			} 
		}
		else 
		{
			
			printf( "S1-T%0ld-C%ld CH375ReadData return error, length=%d\n", mTestCount, mTestCount, mTotal );
		}


		
	}
	
	mLastTick =GetTickCount();
	mLastTick = mLastTick - mFirstTick;
	speed=1000;
	speed=speed*mTotal/mLastTick;
	printf( "*** average speed = %7.1f MBytes/Sec, total=%lld bytes\n", speed/1000/1000, mTotal);
	
	CH375CloseDevice( 0 );

	printf( "\nExit.\n" );
	_getch();
	
}

完整的源代码和可执行 EXE, 建议有需要的朋友重新编译。

完整的 Arduino 代码: