dsh-mcu-lab
只想要“照着做一遍”的封装版本(含安装、配置与排错表),见同名技能包 dsh-mcu-lab;本仓库是代码本体。
把「Keil C51 编译 → Proteus 仿真 → 确定性判定」这条 51 单片机开发链路接进 DSH 会话, 让 AI 在对话里直接编译 hex、跑电路、拿到 PASS/FAIL 与判定依据。
这是什么
一个 DSH 插件(lib/index.js)+ 一个命令行内核(kernel/mcu_lab.py),两者是同一件事的两层:
- 插件层:向 DSH 注册 5 个工具,接收 AI 的调用参数,spawn 内核、把内核输出的单行 JSON 交回会话。 它自己不编译、不仿真,只是一层薄壳。
- 内核层:真正干活的 Python 脚本。6 个子命令(
check/list/build/sim/verify/run), 每个都输出单行 JSON,不装插件也能直接在命令行用。
它解决的问题很具体:单片机的作业验证过去只能靠人盯着 Proteus 看 LED 亮不亮、或者靠视觉模型读截图 (引脚编号会漂移,判定不稳定)。这里改成两层确定性判据——编译是否产出 hex、仿真进程的 CPU 是否在增长、 有没有失败标记文本。
仓库结构
| 路径 | 是什么 |
|---|---|
lib/index.js |
DSH 插件本体,注册 5 个工具 |
kernel/mcu_lab.py |
命令行内核(Python 3,纯标准库),插件调用的就是它 |
kernel/README.md |
内核自己的说明文档(含原作者本机的实测记录) |
examples/led_blink.c |
示例:AT89C51 上 P3.7 的 LED 闪烁(正向用例) |
examples/broken.c |
示例:故意漏分号的坏代码(反向用例,用来验证编译失败能被抓到) |
docs/PROJECT_LOG.md |
原项目的开发日志(决策、踩坑、历史实测数据) |
cordis.patch.yml / dsh.plugin.json |
DSH 装载用的补丁层与插件清单 |
test.mjs |
零依赖测试(不需要 Keil / Proteus / DSH) |
内核是本仓库的一部分,不是外挂依赖:kernel/mcu_lab.py 随仓库一起发布,插件默认就用它。
五个工具:各干什么、怎么选
| 工具 | 干什么 | 前提 | 什么时候用 |
|---|---|---|---|
mcu_run |
编译 → 把 hex 复制进电路工作副本 → 仿真 → 判定,返回 verdict: PASS/FAIL + 证据 + 工作目录 |
Keil C51 + Proteus ISIS + 外部三脚本 | 默认入口:只想知道「这段代码在这个电路上跑不跑得起来」 |
mcu_build |
只用 Keil C51 把单文件 C51 源码编译成 hex,返回 hex 路径与日志尾部 | Keil C51 + build51.py |
只想排 C51 语法错误,还不想动电路(快,且不碰电路文件) |
mcu_sim |
打开指定电路跑一段时间并截图,然后关闭 | Proteus ISIS + proteus_ctl.py |
只想看现象、要一张截图给人看;不要用它做判定 |
mcu_verify |
只对指定电路做确定性判定,返回 running 与判定依据文本 |
Proteus ISIS + verify.py |
电路和 hex 都已就位、只想复判一次;或不想让任何文件被改动 |
选法一句话:要结论就 mcu_run;只排编译错误用 mcu_build;只要截图用 mcu_sim;只要复判用 mcu_verify;先找电路用 mcu_list。
参数与返回值(来自 lib/index.js):
| 工具 | 参数 | 主要返回字段 |
|---|---|---|
mcu_run |
source_path(必填,C51 源文件绝对路径)、dsn_path(必填,.DSN 绝对路径)、hex_name、wait_seconds(默认 6) |
ok verdict stage hex evidence workdir elapsed_sec |
mcu_build |
source_path(必填) |
ok hex log_tail |
mcu_sim |
dsn_path(必填)、seconds(默认 8)、shot_path |
ok shot workdir |
mcu_verify |
dsn_path(必填)、wait_seconds(默认 6) |
ok running evidence errors status_text |
mcu_list |
无 | ok count items |
在会话里不必背参数,直接说就行,例如:
把
examples/led_blink.c在这个电路上跑一遍,告诉我过没过。
前置条件
下面这些需要你自己装好或准备好(标「随仓库提供」的除外)。版本要求一栏凡没有代码依据的,一律写「未验证」。
| 组件 | 说明 | 版本要求 | 依据 |
|---|---|---|---|
| Keil C51 工具链 | C51.exe / BL51.exe / OH51.exe;mcu_build / mcu_run 的编译环节要用 |
最低 / 最高版本未验证。原作者实测环境里 C51.exe 的文件版本是 9.00(文件描述 C51/ CX51 Compiler),本次整理读文件版本得到 |
读内核代码:它按固定路径调用工具链,代码里没有任何版本检查 |
| Proteus ISIS | ISIS.EXE;mcu_sim / mcu_verify / mcu_run 的仿真环节要用 |
最低 / 最高版本未验证。原作者实测环境里 ISIS.EXE 的文件版本是 7.08 SP2 IB10468(文件描述 ISIS Schematic Capture) |
同上 |
| Python 3 | 跑内核脚本;mcu_sim / mcu_verify / mcu_run 的仿真环节还会用到接口脚本,它们需要第三方包 |
原作者实测 3.13.15,其他版本未验证 |
内核 kernel/mcu_lab.py 只用 Python 标准库:argparse json os shutil subprocess sys time glob(读过它的 import 行确认);但 kernel/iface/proteus_ctl.py 顶部 import win32api / win32con / win32gui / win32ui / PIL,verify.py 也 import win32gui。所以「编译出 hex」只需要标准库,跑仿真/判定还需要 pip install pywin32 Pillow。 不需要 pyserial |
| 外部接口脚本 | build51.py(调 Keil)、proteus_ctl.py(开/关仿真、截图)、verify.py(确定性判定) |
随仓库提供:kernel/iface/ |
内核默认从这里调用;可用 DSH_MCU_LAB_IFACE 指向你自己的目录。真正的判定逻辑写在 verify.py 里 |
| 电路模板索引 | dsn_index.json,mcu_list 的数据源 |
自备(不在本仓库内,可选) | 内核 cmd_list 读该文件;缺失时 mcu_list 返回 ok:false 并提示设 DSH_MCU_LAB_DSN_INDEX,不影响 mcu_build / mcu_sim / mcu_verify / mcu_run |
| 操作系统 | 内核里是 Windows 路径与 Windows 可执行文件的调用方式 | 原作者实测 Windows 11;其他平台未验证 | 内核把 C51.exe / ISIS.EXE 当子进程调,没有跨平台分支 |
| Node.js | 跑插件与测试 | >= 18(package.json 的 engines.node) |
作者环境 Node v24.19.0 |
| DSH | 插件宿主(只用命令行内核时不需要) | dsh.plugin.json 里声明 >=0.0.1 |
该值来自插件清单,未经跨版本验证 |
另外:插件侧(lib/index.js)运行时依赖 DSH 宿主提供的 @deepseek-ai/dsh-tools,已在 package.json 里以
peerDependencies 声明,不把它复制进自身依赖树——所以脱离 DSH 单独 import 这个插件会解析失败,
这是设计边界,不是缺文件;只想用命令行内核(kernel/)的人完全不需要它。
另外两条来自内核文档的经验(原作者陈述,本次整理未复现):
- Proteus 的安装路径必须纯英文,中文路径会崩。
- C51 源码必须纯 ASCII,带中文注释会报 C141 之类的语法错误。
安装
方式一:装进 DSH(拿到 5 个工具)
把本仓库放到 DSH 的 profile 模块目录下,目录名与包名一致:
<DSH_HOME>\profiles\node_modules\dsh-mcu-lab\其中
<DSH_HOME>默认是%USERPROFILE%\.dsh(可用环境变量DSH_HOME覆盖)。在该 profile 的
cordis.patch.yml末尾加上装载行(本仓库的cordis.patch.yml内容一致):- insert: - id: mcu-lab name: 'dsh-mcu-lab'重启 DSH(
dsh web)后,5 个工具才会出现在会话里。
本仓库不含安装脚本:上面的复制与装载行就是全部步骤,装在哪、装不装由你决定。
方式二:只用命令行内核(不装 DSH)
python kernel/mcu_lab.py check
python kernel/mcu_lab.py list
python kernel/mcu_lab.py build examples\led_blink.c
python kernel/mcu_lab.py run examples\led_blink.c <你的电路.DSN> --wait 6
python kernel/mcu_lab.py verify <你的电路.DSN> --wait 6
python kernel/mcu_lab.py sim <你的电路.DSN> --seconds 8 --shot out.png
每个子命令都把结果打成单行 JSON,退出码 0 表示成功、1 表示跑完但失败、2 表示入参缺失 (例如源文件或电路不存在)。
内核归集说明
- 内核随仓库发布,位置固定在
kernel/mcu_lab.py;插件默认调用的就是仓库内这一份, 用import.meta.url从插件文件位置推算出<仓库根>/kernel/mcu_lab.py, 不需要另外安装内核、也不需要配置路径。 - 内核
kernel/mcu_lab.py本身只用 Python 标准库,不用pip install任何东西(不需要 pyserial); 但仿真环节要调用的kernel/iface/proteus_ctl.py需要win32api/win32gui/win32ui/PIL(pywin32 + Pillow),verify.py用到win32gui。只跑check/list/build不需要这两个包。 - 内核自己会写东西:每次
sim/verify/run都在kernel/work/<时间戳>/下建工作副本 (.DSN与 hex 的副本、截图、证据文件)。这个目录已被.gitignore排除。 - 内核文档的原文件(作者本机的说明)收在
kernel/README.md,开发日志收在docs/PROJECT_LOG.md。
配置
插件侧:两个环境变量
| 环境变量 | 作用 | 默认值 |
|---|---|---|
DSH_MCU_LAB_CLI |
内核脚本 mcu_lab.py 的路径 |
<仓库根>/kernel/mcu_lab.py(按插件文件位置推算) |
DSH_MCU_LAB_PYTHON |
Python 解释器的路径 | Windows 用 python,其他平台用 python3(即按 PATH 查找) |
找不到内核或找不到解释器时,工具会返回一条说明「该设哪个环境变量」的错误,而不是空手失败。
内核侧:路径全部可用环境变量覆盖
内核以及它调用的接口脚本,路径都不写死在代码里,按下表覆盖;不设就用默认值。
| 环境变量 | 作用 | 默认值 |
|---|---|---|
DSH_MCU_LAB_IFACE |
接口脚本目录(build51.py / proteus_ctl.py / verify.py) |
仓库自带:kernel/iface/ |
DSH_MCU_LAB_KEIL |
C51.exe 的路径(check 体检用) |
<KEIL_DIR>\C51\BIN\C51.exe |
DSH_MCU_LAB_ISIS |
ISIS.EXE 的路径(体检与仿真控制用) |
<PROTEUS_DIR>\BIN\ISIS.EXE |
DSH_MCU_LAB_DSN_INDEX |
dsn_index.json 的路径(只有 mcu_list 用) |
空——本仓库不附带电路索引 |
DSH_MCU_LAB_KEIL_DIR |
Keil 安装根目录(接口脚本 build51.py 用) |
<KEIL_DIR> |
DSH_MCU_LAB_PROTEUS_TEMP |
Proteus 仿真临时目录 | <PROTEUS_TEMP> |
DSH_MCU_LAB_SDCC_BIN |
SDCC 工具链目录(只在用 sdcc51.py 时) |
<SDCC_BIN> |
<KEIL_DIR>、<PROTEUS_DIR>、<SDCC_BIN> 是这三款软件的常见默认安装位置,不是作者的个人信息;
装在别处就设上面对应的变量。先跑一次体检:
python kernel/mcu_lab.py check
它会逐项报告每个前置条件是否就位。dsn_index.json 是可选件:它只服务 mcu_list,
索引的是各人自备的电路文件,因此缺失不会让体检失败,只会单独列在 optional 字段里。
没有 Keil 也能编译:SDCC 路线
仓库自带的 kernel/iface/sdcc51.py 用 SDCC(免费开源编译器)替代 Keil:
python kernel/iface/sdcc51.py your.c # .c -> .hex(Intel HEX,Proteus 可直接加载)
需要可从 PATH 调用的 SDCC(或设 DSH_MCU_LAB_SDCC_BIN 指向安装目录;本机实测为 SDCC 4.6.0)。
仿真与判定仍需 Proteus,但"编译出 hex"这一段不再依赖付费的 Keil。
⚠️ 这条路线只对「标准 C + SDCC 认可的扩展」成立,Keil 专有关键字要自己改:
sdcc-inc/reg52.h 这个 shim 只负责把 #include <reg52.h> 转发到 SDCC 的 8052.h,
仓库自带的 examples/led_blink.c 已用 #if defined(__SDCC) 兼容 SDCC 的 __sbit 扩展。
sdcc51.py 会优先使用 PATH 中的 sdcc,也可用 DSH_MCU_LAB_SDCC_BIN 指定目录。
mcu_build / mcu_run 的 --compiler auto 会优先 Keil,Keil 不可用时自动回退 SDCC。
python kernel/iface/sdcc51.py examples\led_blink.c
:: 预期:SDCC 结果: errors=0 ... hex=...\led_blink.hex
其他 Keil 专有语法(例如 interrupt、_at_)仍需按 SDCC 语法改写;未逐项验证。
没有 Keil/Proteus 时的能力边界
python kernel/mcu_lab.py check
该命令会分别报告 keil_c51、proteus_isis、sdcc、脚本文件和 capabilities。
缺失工具不会抛栈,而会给出安装/配置指引;ok:false 只表示完整能力矩阵未齐,不会掩盖可用的 SDCC 编译或离线判定能力。
商业软件缺失时,keil_build 或 proteus_sim 会是 false,对应结果标为未验证。
离线验证
没有 Proteus 时可以用注入的离线后端验证真实 mcu_lab.py -> verify.py 判定链路:
python kernel/mcu_lab.py verify path\pass.dsn --backend offline
python kernel/mcu_lab.py verify path\sim-fail.dsn --backend offline
python kernel/mcu_lab.py verify path\verdict-fail.dsn --backend offline
pass.dsn 内容为空即返回 running:true;包含 OFFLINE_SIM_FAIL 返回仿真失败;包含 OFFLINE_VERDICT_FAIL 返回判定不通过。
这是假桩,不代表真实 Proteus 行为,但三种结果都经过正式 verify 子命令和 JSON/退出码处理。
能力边界
如实划清能做到与做不到的:
- 仓库不自带工具链。clone 下来不能独立跑通端到端:Keil C51 与 Proteus ISIS 都是要自己装的商业软件;
模板索引(
dsn_index.json)也不在这里。三个接口脚本已经随仓库提供(kernel/iface/),缺什么,check会列出来。 - 判定逻辑在随仓库发布的
verify.py里。mcu_verify/mcu_run的判定是调用kernel/iface/verify.py并解析它输出的最后一行 JSON 得到的;判据本身(进程 CPU 增长、失败标记文本、窗口存活)写在那个文件里。DSH_MCU_LAB_IFACE指向别处时,用的就是你自己的那份。 mcu_sim/mcu_verify只复制.DSN,不复制 hex(读内核cmd_sim/cmd_verify可见, 它们调用_stage_dsn时没传 hex 路径)。因此电路引用的 hex 若不在工作副本目录里, 这两个工具是否还能跑起来未验证;要端到端验证请用mcu_run(它会把 hex 一并复制)。- 不改你的原始电路:每次都在
kernel/work/<时间戳>/做副本,原始.DSN不被写入 (这是设计约束,代码路径可核)。 mcu_list的输出随索引文件变化,最多 80 条(内核里是items[:80])。 原文档里「79 个模板」是原作者本机那份索引的数量,不是本插件的固有能力。- 判定视角是「仿真是否真在跑」,不是「电路功能是否正确」。LED 是否按你期望的节奏闪, 判定器不知道;它告的是「程序跑起来了、没有失败标记」。
- 没有 CI 端到端:见下一节。任何「CI 已验证编译/仿真」的说法在本仓库都不成立。
测试
能跑的:零依赖测试
node test.mjs # 等价于 npm test
需要 Node ≥ 18 + Python 3;不需要 Keil、Proteus,也不需要 DSH 在运行。覆盖四类:
- 语法:
node --check lib/index.js、Python 编译kernel/mcu_lab.py。 - 内核命令行契约(不依赖 Keil/Proteus 的部分):
check/list输出单行 JSON 且字段形状正确;build/run/sim/verify在源文件或电路不存在时返回结构化报错与退出码 2,而不是抛栈。 - 纯函数:内核的
_dec()解码兜底(UTF-8 → GBK → 替换字符),以及字符串直通。 - SDCC 与离线后端:临时目录编译自带示例,并通过
verify.py --backend offline覆盖通过、仿真失败、判定失败。 - 仓库卫生与文档一致性:
lib/index.js注册的工具名与两个 README 里写的一致;package.json的包名/版本/files对得上;仓库里没有 Keil/Proteus 构建产物, 也没有 Windows 用户目录下的绝对路径这类本机痕迹。
不能跑的:为什么没有端到端 CI
真正要验证的是「编译 → 仿真 → 判定」这条链路,它必须在本机装好 Keil C51 与 Proteus ISIS:
- 两者都是商业软件,GitHub Actions 的 runner 上没有,也没有免安装的等效替代品能产生同样的证据—— 判定的依据是 Proteus 进程的 CPU 增长与窗口存活,换个模拟器就换了判据;
- 链路中间的三个接口脚本随仓库发布在
kernel/iface/;dsn_index.json需自备(可选)。
所以 .github/workflows/ci.yml 只跑上面第 1–4 类零依赖测试,一个字节都不假装跑过编译或仿真。
你自己怎么自验端到端
在本机装好 Keil C51 与 Proteus ISIS,按「配置」一节用环境变量(或直接改内核里那几个默认值)把路径指到你的安装位置,然后:
python kernel/mcu_lab.py check
:: 期望 6/6 全 true;缺哪一项会列出哪一项是 false
python kernel/mcu_lab.py run examples\led_blink.c <你的电路.DSN> --wait 6
:: 期望最后一行 JSON 里 ok=true、verdict=PASS,evidence 里能看到 CPU 增长与"无失败标记"
python kernel/mcu_lab.py run examples\broken.c <同一个电路.DSN> --wait 6
:: 反向用例:期望 ok=false、stage=build,log_tail 里出现 C141 之类的语法错误
原作者本机的实测记录(2026-09-08)
以下数据来自原项目日志(docs/PROJECT_LOG.md),是在装有 Keil C51 与 Proteus 7.8 的机器上
跑出来的历史记录,不是本次开源整理跑的:
| 测试 | 结果 |
|---|---|
mcu_lab.py check |
6/6 通过 |
正向:led_blink.c → ex4 电路 |
verdict: PASS,11.0 秒,证据 process CPU advanced 0.172s; ISIS window alive; no failure markers |
反向 1:broken.c(语法错) |
ok:false, stage:build,日志含 ERROR C141 |
| 反向 2:电路文件不存在 | ok:false, stage:verify,明确报错 |
mcu_lab.py list |
返回 79 个模板 |
本次整理实际跑过的命令与结果,记录在交付说明里(结论:零依赖测试全部通过;端到端未复跑)。
许可
MIT © 2026 CHIP-PHILO-GH
Keil、Proteus 是各自公司的商业软件,本仓库不包含、也不分发它们。
No comments yet. Be the first to write one.