XiNGRZ's BLOG

一些唠叨

WS2812 踩坑记录

起因是…

两年前捣鼓的一个启发自稚晖君瀚文键盘的项目。当时照着瀚文的规格在淘宝用 WS2812 为关键词随便找了一家买了一小卷 RGB 灯珠(键圈通常称之为反贴 3528)。

当时虽然已经知道 WS2812 是来自 WORLDSEMI 公司的一种通过数字信号控制的 RGB LED、且国内大家实际在用的都是各种大小厂做的协议兼容但根据需要衍生出了各种封装的变体。但想着估计都是同一种东西应该大差不差吧。就没怎么留意。

是的,当时我设计的电路是照着 WORLDSEMI 家的原版 WS2812B 的资料,用的是 5V 的 VDD。板子到了后擦电开机一顿输出,不亮。然后发现改成 3.3V 供电就好了。然后想着可能是这些给键盘用的反贴封装就是 3.3V 的?于是这事就这么着了,后面也没继续深究。

然后!

前阵子又要捣鼓一个键盘项目。这么多年摸爬打滚觉得做东西也该稍微正规点了,最起码用的料不能是连厂家都不知道的。项目必须要足够 DFM,必须要足够 reproducible。

于是,就开始考究,选定了欧思科光电的 SK6812MINI-E。

这里插一嘴,

既然是反贴安装,并且打算量产(或者说照着量产的标准)的话,那么一定要留意 datasheet 里的包装信息。只有下图这种屁股朝外的编带才是对的。不留意的话可能找了发光面朝外的版本,工厂看了估计会骂娘。为啥?你想想,贴片的时候 PCB 焊盘朝上刷锡膏,同时机器会将编带透明面朝上剥开用吸盘把元件吸起来摆放到 PCB 上。如果你的编带是发光面朝上,那就是正贴,是不会有人帮你挨个翻面摆成反贴的…

2024-06-14-22-45-12

好了,回到正题。

看到 datasheet 的时候多留意了一眼电压范围。嗯?3.7V ~ 5.5V?不是应该支持 3.3V 的吗?我记得 5V 明明点不亮啊?

2024-06-14-22-50-10

抱着必须把这个疑问解决的想法,我终于去找了当时淘宝上的卖家要了当时那一小卷料的 datasheet。嘿,人家或许真的支持 3.3V(没写 VDD 最小值)。但也不对,它明明支持 5V 甚至可以到 7.5V,为什么当时我用 5V 点不亮呢?

2024-06-14-22-52-48

这个疑问其实看到这里我已经大概知道怎么回事了。只需要把 datasheet 往下翻一点。虽然这两家 3528 的可用电压范围不一样,但是有两个参数是一致的:高电平电压最小值 (VIH) 和低电平电压最小值 (VIL)。

2024-06-14-22-55-28

如图所示,VIH 最小是 0.7*VDD,代入一下即可得:5V 供电下 DIN 的输入至少要达到 3.5V 才能被认为是高电平(逻辑1)。然而我的 MCU 是 3.3V 逻辑电平,所以灯珠没有收到有效的控制信号输入,难怪就不亮了。

同理把公式反一下,即可得出在 3.3V 逻辑电平下,我的 VDD 最高不能超过 4.714V。

到这里,原理是解释清楚了,

但是!问题得回溯啊朋友们。

我清晰地记得当时我折腾瀚文键盘,没两天就被静电劈坏了出厂的 LED,自己修的时候用的就是当时买的那小卷 3528 灯珠。证明至少在瀚文的设计上它是可互换的。那瀚文是怎么设计的呢?于是我随手翻了一下稚晖君做的原理图:

2024-06-14-23-06-07 2024-06-14-23-06-30

瀚文的 RGB 供电是直接接在 VCC_BAT 上的,而 VCC_BAT 是充电管理芯片 TP4056 的输出。查阅芯片 datasheet 得知电池充满电后 BAT 脚恒定 4.2V。

2024-06-14-23-09-48

4.2V 既大于灯珠所需要的 3.7V,也小于之前得出的 4.714V。而瀚文是单模的(电池只是为了应对 FOC 旋钮输出力矩时的电流需求),电池总是处于满电状态。所以的它总是满足工作条件。

总之,一直以来的疑问都能解释得通了。

总结

  • 如果你使用电池供电(且 MCU 逻辑电平是 3.3V):直连电池输出,无需经过任何 LDO 和 DC-DC 的降压/升压
  • 如果你使用 5V VDD:你需要一些 MOS 把输入的控制信号转换为 5V 电平
  • 如果你使用 3.3V 的逻辑电平:你需要一颗 DC-DC 把 VDD 调节到满足工作条件的电压范围内(比如 3.7V)

恰好最近手上两个项目都分别适用第一条和第二条的情况。

解决了一些之前「虽然不懂但总之这样能用」和「我也不知道为啥那样不能用反正改成这样就能用了」的问题。爽!

知其所以然的快乐

Go 语言

Go 语言,2023年以前:

1
fmt.Println("Hello, 世界")

2023年7月之后:

1
为什么要演奏春日影! ​​​

还好我写 Rust

Jupyter,好好好

我以前:Jupyter 是什么鬼啊装一堆包还要起个服务器用,直接写 Markdown 不好吗

我现在:你有那么一种高速运转的文档,能嵌 Python,代码能直接运行,结果会保存在文档里,还能用来画图!

(而且 VSCode 装上 ms-toolsai.jupyter 插件编辑 .ipynb 比用什么 Jupyter Lab 方便太多了)

笑死,被 Tauri 坑了

笑死,快两年没更新博客了。
差点又鸽了呢。赶紧搬运点原创微博过来凑个数。

笑死,被 Tauri 坑了。

起因是提测后被 QA 发现 Ubuntu 上拖放功能异常。拖入的文件会被内嵌的 WebView 直接打开而不是被业务捕捉到。

然后发现 Tauri 抓到的事件,鼠标拖住文件在窗口上松开时直接 CANCELLED 了。

往深了发现,在 wry 里会收到 WebKitGTK 发出来的两次 drag-leave 信号,并且其中的 time 参数是 0(正常应该是一个大于 0 的时间间隔),因此 wry 会给到上层一个 CANCELLED 而不是 DROP 的回调。并且,还错误地给了两次。

进而发现,这个行为在 Ubuntu 20.04 上是好的,22.04 开始就炸了。无论是 libwebkit2gtk-4.0 还是 libwebkit2gtk-4.1

看到这里稍微有点头绪了。

嘿你猜咋的?就是 Wayland 的锅。切回 Xorg 无论 22.04 还是 24.04 都正常了。

那为什么唯独 Tauri 被坑了呢?

因为按照正常的思路,drag-enterdrag-leave 是一对成对出现的事件,然后当用户松开鼠标才触发一个 drag-drop 的事件表示成功。

但是 Tauri 的 API 设计偏不。它先是一个 DRAG (对应 drag-enter 表示开始),然后要么 CANCELLED 要么 DROP 来结束。这种非对称的设计意味着我从 DRAG 调起的资源 (比如视觉反馈),就必须在 CANCELLEDDROP 都各自释放一次。

因此,它内部需要在 drag-leave 时的状态做一些判断来决定向上抛出 CANCELLED 还是 DROP。也因此,当上游的事件时序出现了变化,它就出问题了。

没办法,先保发版。临时修复了一下,Cargo 里加了个 patch 推上去了。不过这个修复方案相当于 DROP 之前会先冒一个 CANCELLED,其实是不符合 Tauri 的行为定义的。但是,能用就行。

终于有空把我收到的 HW-75 组起来了

前段时间收到了稚晖君和 Xikii 搞的那套瀚文 HW-75,一直没空整。这几天居家办公,终于组装起来了。

过程…略。说来惭愧,这是我第一次接触客制化,所以我的经历没什么参考价值。

整体效果

  • 键帽:PBTfans 深海来客
  • 轴体:TTC 月白
  • 卫星轴:Owlab v3 黑武士

没啥讲究,咱也不懂…在 zhudi 的店里按销量随便挑的。

好,那我折腾了个啥呢…

其实接触客制化键盘之前我就偶然发现过一个键盘固件项目 —— ZMK。它是一个基于 Zephyr 的客制化键盘固件。正因为是基于 Zephyr,所以它天生能兼容各种不同的平台,除了键圈耳熟能详的 STM32F103 之外,还支持 ESP32 系列、nRF 系列等。主要是 Zephyr 我熟啊 (doge)

好,那就动手。

烧 Bootloader

SWD

瀚文的 PCB 上预留了一个 4P PH1.0 插座的调试口,接上 DAPLink 就能烧固件。但是每次烧固件都要拆开,或者调试的时候都得一直保持拆开状态,是不优雅的。这时候我们就需要一个可以直接从 USB 更新固件的 bootloader。

之前稚晖君预告过一个 DFU bootloader,后来鸽了。所以我自己移植了一个。详情可以看我之前的分享

如上图所示,从左到右分别是 GND、SWDIO、SWCLK、5V,某宝随便买个 DAPLink 调试器(甚至合宙的白菜价 Air32 开发板)对应插上即可。注意如果你键盘插着 USB 了,那么 5V 就不用接了。

DAPLink

烧完。按住 Fn 键(空格往右第二个)插上 USB 即可进入 DFU 模式。

烧 ZMK

我已经移植好了:https://github.com/xingrz/zmk-config_xikii_hw-75

按住 Fn 进 DFU 模式,然后还是按照上面那个分享烧就行。

键位映射

我做的是 Mac 的键位映射。

ZMK 默认带了几种灯效。可以通过 Fn 组合键切换:

  • Fn + Tab:开关
  • Fn + Q/A:切换效果
  • Fn + W/S:调整亮度
  • Fn + E/D:调整色相
  • Fn + R/F:调整饱和度
  • Fn + T/G:调整动画速度

结尾

以上就是我的一点分享。各位有什么想法可以留言。后续我会在 ZMK 上继续完善一些功能。

玩了一把 wasm

最近发现了 wasm-micro-runtime 这么个小玩意,一个超小的 wasm 运行时。随手记录下。

0. 先看全貌

在 wasm-micro-runtime (简称 wamr) 里要把一个程序从源文件一直到跑起来,会经历以下步骤:

1
test.c --[wasi-sdk]-> test.wasm --[wamrc]-> test.aot --[iwasm]-> 跑起来

如你所见,现在我需要 wasi-sdk wasmrc iwasm 这仨玩意儿。

1.环境准备

1.1. 取得 wasi-sdk

现成的。在 https://github.com/WebAssembly/wasi-sdk/releases 找到对应你系统的版本下载解压即可。这里假设你把它解压到了 /opt/wasi-sdk。实际上放哪无所谓。

1.2. 编译 iwasm:wamr 运行时

wamr 官方已经提供了几个不同平台的运行时。我用 macOS,直接有现成的:

1
2
cd product-mini/platforms/darwin
cmake -G Ninja -B build && cmake --build build

你可能发现我的编译命令和官方的不一样…没错,cmake 本身就有 -B--build,大可不必手动 mkdir build && cd make,多麻烦。而且 Ninja 香啊~

跑完,build/iwasm 就是当前平台的运行时。

1.3. 编译 wamrc:AOT 编译器

先编译一遍 LLVM 的一些库,需要的时间比较久。不得不说,原版脚本用的是 GNU Makefiles 是真的慢…所以我又夹带了点私货

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
diff --git a/build-scripts/build_llvm.py b/build-scripts/build_llvm.py
index bc0daf1f..9cd23d51 100755
--- a/build-scripts/build_llvm.py
+++ b/build-scripts/build_llvm.py
@@ -114,11 +114,7 @@ def build_llvm(llvm_dir, platform, backends, projects):
)

CONFIG_CMD = f"cmake {compile_options} ../llvm"
- if "windows" == platform:
- if "mingw" in sysconfig.get_platform().lower():
- CONFIG_CMD += " -G'Unix Makefiles'"
- else:
- CONFIG_CMD += " -A x64"
+ CONFIG_CMD += " -G Ninja"
print(f"{CONFIG_CMD}")
subprocess.check_call(shlex.split(CONFIG_CMD), cwd=build_dir)

跑它:

1
python build-scripts/build_llvm.py --platform darwin --arch ARM X86

然后编译 wamrc。这里我同样用 Ninja,比官方的命令快多了。

1
2
cd wamr-compiler
cmake -G Ninja -B build && cmake --build build

跑完,同样得到了 build/wamrc

2. 编译应用

先来个简单的程序,我假设它叫 test.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
char *buf;

printf("Hello world!\n");

buf = malloc(1024);
if (!buf) {
printf("malloc buf failed\n");
return -1;
}

printf("buf ptr: %p\n", buf);

sprintf(buf, "%s", "1234\n");
printf("buf: %s", buf);

free(buf);
return 0;
}

编译它

1
$ /opt/wasi-sdk/bin/clang -O3 -o test.wasm test.c

现在,你得到了一个 test.wasm 文件了。理论上,你可以直接用 iwasm 去执行它。但我想玩玩 AOT。所以继续。

1
2
3
4
5
6
7
8
9
$ ./wamrc -o test.aot test.wasm
Create AoT compiler with:
target: x86_64
target cpu: westmere
cpu features:
opt level: 3
size level: 1
output format: AoT file
Compile success, file test.aot was generated.

现在你得到了一个 test.aot,执行它:

1
2
3
4
$ ./iwasm test.aot
Hello world!
buf ptr: 0x114e0
buf: 1234

完成!

3. 顺带一提

编译 wamrc AOT 编译器之前,使用 build_llvm.py 编译 LLVM 运行库时所传入的参数,--platform 表示当前 (也就是运行 wamrc 的构建机) 平台,--arch 表示需要 wamrc 支持生成的目标架构。具体可选值可执行 build-scripts/build_llvm.py --help 查看。

如果你需要生成 Xtensa 目标,直接传入 --arch Xtensa 会遇到两个问题,需要额外处理一下。

一是由于 LLVM 的 Xtensa 支持目前还是试验阶段,需要一个宏打开:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
diff --git a/build-scripts/build_llvm.py b/build-scripts/build_llvm.py
index bc0daf1f..db8dd1f7 100755
--- a/build-scripts/build_llvm.py
+++ b/build-scripts/build_llvm.py
@@ -66,7 +66,9 @@ def build_llvm(llvm_dir, platform, backends, projects):
"windows": [
"-DCMAKE_INSTALL_PREFIX=LLVM-install",
],
- "default": [],
+ "default": [
+ '-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD:STRING="Xtensa"',
+ ],
}

LLVM_TARGETS_TO_BUILD = [

二是由于官方 llvm/llvm-project 不支持 Xtensa 目标,需要换成乐鑫的 espressif/llvm-project

1
2
3
4
5
cd core/deps/llvm
git remote set-url origin https://github.com/espressif/llvm-project.git
git fetch origin xtensa_release_13.0.0:xtensa_release_13.0.0 --depth 1
git checkout xtensa_release_13.0.0
cd ../../..

删掉 build 重新编译一下。

1
2
rm -rf core/deps/llvm/build
python build-scripts/build_llvm.py --platform darwin --arch Xtensa

4. 参考资料

做了个基于 Web 的 Gerber 渲染器

关注我比较久的人可能知道,我之前会利用 tracespace 和一些脚本配合将我做的 PCB 渲染成效果图。但是每次操作的步骤有点繁琐。于是在鸽了不知道多久后…我终于自己做了一个工具来完成这件事情了。

image

特性:

  1. 可以将 Gerber 渲染成 SVG 或 PNG (高达 100 dots per mm 的分辨率)
  2. 内置了几种常见配色方案 (咖喱创用户都懂)
  3. 支持渲染浮雕材质,便于你扔进 3D 软件渲染出逼真的铜层凹凸效果 (用法见 BV1pK4y1g7fY)

地址:gerber.xingrz.me

image
image
image
image

关于新博客

上一篇文章说了,我不喜欢用 Jekyll 写博客。具体来说,我不喜欢在代码仓里码字。我觉得这么做很奇怪,感觉我需要正襟危坐打开 VSCode,准备各种图片素材,git commitgit push

那这次重开的博客,怎么解决这个问题呢?简而言之,Issues 和 Actions。

GitHub Issues 本身就可以充当一个内容管理系统。你想想:它支持 Markdown,它的 open 和 closed 状态正好可以对应草稿和发布,它的 labels 就是 tags…它甚至可以粘贴带图!

image

How?

这一整个流程,可以简化如下:

  1. 我有一个私有仓,xingrz/blog,我在里面码字

  2. 我有一个公开仓,xingrz/xingrz.github.io,用来发布 GitHub Pages

  3. 在私有仓里,我做了一个 sync workflow。只要 Issues 有任何变化,就触发公开仓的另一个 deploy workflow

    image
  4. 公开仓的 deploy workflow 作用很简单,就是拉取私有仓的 Issues,生成 posts,然后生成最终 HTML,部署

    image

公开仓的 deploy workflow,大家自己也可以看到:deploy.yml。它的另一个关键是 sync.mjs 这个用来拉取 Issues 并生成 posts 的脚本。

私有仓的 sync workflow 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
name: Sync posts

on:
issues:
workflow_dispatch:

permissions:
contents: read

concurrency:
group: sync
cancel-in-progress: true

jobs:
sync:
runs-on: ubuntu-latest
steps:
- name: Sync
run: gh workflow run deploy.yml
env:
GH_REPO: xingrz/xingrz.github.io
GH_TOKEN: ${{ secrets.DEPLOY_TOKEN }}

如果你想把这套流程用到你自己的博客,你需要开一个 Personal Access Token 分别加到两个仓库的 Secrets 里(名称为 DEPLOY_TOKEN)。并且对应更改这两个 workflow 里的 GH_REPO 变量。

挖坑

基于 Actions,可以有更多玩法,比如:

  • 自动发一条 Telegram channel 广播
  • 调用 Puppeteer 生成长截图

以后再说吧。

结语

好了,这篇分享就到这里。主要就是想试试真正用这个流程发一篇文章而已。

博客又重开了

为什么呢

刚毕业工作的时候试着写了一段时间博客。后来因为 Jekyll 每次新建文章都过于严肃,慢慢地就鸽了(刚刚去翻了翻仓库,甚至有一篇草稿躺了 8 年没写完)。

这几年有时候会忍不住码一条非常长的微博来分享一些技术。但是逐渐发现这种分享还是应该好好地用 Markdown 写下来。想来想去,决定还是重新搭一个博客好一点。

就看这次会在什么时候鸽了。

Proudly powered by Hexo and Theme by Hacker
© 2024 XiNGRZ