English | 中文
多模态自动滴定控制器。STM32F103 裸机固件驱动进样与滴定双蠕动泵,采集电位与光谱信号;Rust/Tauri 上位机对两路信号做在线终点判定,并支持泵标定、浓度计算与数据记录。
| 项目 | 值 |
|---|---|
| MCU | STM32F103C8T6 (ARM Cortex-M3) |
| Flash | 64 KB @ 0x08000000 |
| RAM | 20 KB @ 0x20000000 |
| 工具链 | GCC ARM (arm-none-eabi-g++) |
| 语言标准 | C++23,裸机,无 HAL / 无 RTOS / 无堆 |
| 构建系统 | SCons |
| 调试器 | ST-Link V2 (SWD) |
| 上位机 | Rust/Tauri 2 + Next.js |
| 授权 | PolyForm Shield 1.0.0 |
| 文档 | 读者 | 内容 |
|---|---|---|
| 上位机使用手册 | 做实验的人 | 安装、连接、标定、滴定、导出、常见问题 |
| 通信协议 | 对接协议的人 | 帧格式、命令表、时序、重试 |
| 固件二次开发指南 | 固件开发者 | 代码布局、初始化、寄存器层、中断、协议扩展 |
| 硬件接线 | 组装样机的人 | 引脚分配、接线说明、供电 |
| 标定与数据格式 | 处理数据的人 | calibre.npz 结构、sidecar、settings.json |
| 算法技术报告 | 关注算法的开发者 | 多模态融合、终点判定、参数与验证 |
AutoTitrator-Free
├── SConstruct # SCons 构建脚本
├── Startup/ # 启动代码、中断向量表、链接脚本
│ ├── Vectors.cpp
│ ├── linker.ld
│ └── CXXStubs.cpp
├── src/ # 固件应用源码
│ ├── main.cpp # 主循环入口
│ └── Interrupts.cpp # 中断处理函数
├── include/ # 头文件
│ ├── register/ # Cortex-M3 寄存器/MMIO 抽象层
│ ├── stm32f103/ # 从 SVD 自动生成的 61 个外设头文件
│ ├── platform/ # 系统时钟、SysTick、NVIC 辅助
│ ├── hal/ # 外设 HAL 驱动
│ ├── device/ # 设备级驱动
│ └── protocol/ # 通信协议栈
├── TController/ # Rust/Tauri 上位机
│ ├── crates/controller-core/ # 协议、检测、重建与工作流
│ ├── app/src-tauri/ # Tauri 命令与后端状态
│ ├── app/ui-next/ # Next.js 仪器工作台
│ └── data/ # calibre.npz 与运行时状态
├── scripts/ # 代码生成脚本
│ └── generate_stm32f103.py # 从 CMSIS-SVD 生成外设头文件
├── requirements-dev.txt # 固件构建与寄存器生成依赖
├── openocd.cfg # OpenOCD 调试配置
├── .gdbinit # GDB 初始化脚本
├── README.md
└── LICENSE
位于 include/register/,为 header-only、纯 C++23、零运行时开销的 MMIO 抽象:
CortexM3::Register<T, Address>:整寄存器读写、位域读写、原子Set/Clear/Modify。CortexM3::Field<T, Position, Width>:位域类型,Mask()在编译期计算。atomic.hpp:通过LDREX/STREX实现 8/16/32 位原子 RMW。
include/stm32f103/ 下 61 个外设头文件由 scripts/generate_stm32f103.py 从 CMSIS-SVD 自动生成:
uv run scripts/generate_stm32f103.py每个寄存器对应一个纯静态单例类,按 SVD <access> 属性生成 Read / Write 与按位域访问方法,命名空间为 STM32F103::{Peripheral}。
| 文件 | 外设 | 说明 |
|---|---|---|
include/hal/GPIO.hpp |
GPIO | 端口模式/上下拉/速度配置、置位/读取 |
include/hal/UART.hpp |
USART1 | RX 用 DMA1_CH5 循环 + IDLE 中断,TX 用 TXE 中断逐字节 |
include/hal/I2C.hpp |
I2C1 | 100 kHz,PB8/PB9,同步阻塞 + 异步中断双模式 |
include/hal/TIM.hpp |
TIM3 / TIM4 | TIM3 作为 ADC 触发时基,TIM4 双通道 PWM 驱动蠕动泵 |
include/hal/ADC.hpp |
ADC1 | 单通道 PA0,TIM3_TRGO 触发,EOC 中断 |
| 文件 | 功能 |
|---|---|
include/device/PumpMotor.hpp |
两个蠕动泵驱动(TIM4 CH1/CH2),支持 MaxCount / FreeRun |
include/device/ADCOversample.hpp |
256 次 ADC 累加过采样,右移 4 位输出 16-bit 结果 |
include/device/AS7341.hpp |
AS7341 光谱传感器驱动,两 phase SMUX 扫描状态机 |
include/device/SerialPort.hpp |
环形缓冲 RX + 中断逐字节 TX |
include/protocol/CommandDispatcher.hpp |
解析下行命令、调用设备 API、打包上行数据 |
include/protocol/FrameCodec.hpp |
CRC-8(Maxim-Dallas, poly = 0x31)编解码 |
include/protocol/CommandParser.hpp |
下行帧状态机解析器 |
| 中断 | 优先级 | 用途 |
|---|---|---|
| USART1 | 0 | IDLE 接收 + TXE 发送 |
| DMA1_Channel5 | 0 | USART1 RX DMA half/full |
| TIM4 | 1 | 泵脉冲计数 |
| ADC1_2 | 2 | ADC 转换完成 |
| I2C1_EV / I2C1_ER | 2 | AS7341 异步 I2C |
| SysTick | 15 | 1 ms 时基 |
所有中断处理函数在 Startup/Vectors.cpp 中以 [[gnu::weak]] 声明为弱符号并默认指向 Default_Handler,用户只需在任意 .cpp 中定义同名 extern "C" 函数即可覆盖。
src/main.cpp 初始化时钟、SysTick、LED、串口、泵、ADC 过采样和 AS7341,随后在主循环中轮询协议服务、光谱服务和 ADC 服务,并在光谱测量完成后自动启动下一轮采集。
需要 arm-none-eabi 工具链在 PATH 中:
scons
# 或指定前缀
scons CROSS=arm-none-eabi-构建产物位于 build/:
AutoTitrator-Firmware.elf— 可执行文件(含调试信息)AutoTitrator-Firmware.hex— Intel HEXAutoTitrator-Firmware.map— 内存映射AutoTitrator-Firmware.lst— 反汇编清单
清理:
scons -c# Terminal 1 — 启动 OpenOCD
cd D:/Projects/AutoTitrator/Firmware
openocd -f openocd.cfg
# Terminal 2 — 连接 GDB
arm-none-eabi-gdb build/AutoTitrator-Firmware.elf -x .gdbinit寄存器头文件生成脚本需要 cmsis-svd,开发依赖使用:
uv pip install -r requirements-dev.txt
uv run scripts/generate_stm32f103.pyRust/Tauri 上位机通过串口与 MCU 通信,提供:
- 实时光谱曲线与电位曲线
- 在线滴定终点检测
- 双泵控制与进度显示
- 泵校准与 pH 电极校准
- 状态持久化、运行历史和可靠性诊断
| 目录 | 功能 |
|---|---|
TController/crates/controller-core/src/protocol/ |
串口线程、协议帧解析与重试 |
TController/crates/controller-core/src/processing/ |
终点检测、光谱重建、泵校准 |
TController/crates/controller-core/src/workflow.rs |
滴定工作流与泵控状态机 |
TController/app/src-tauri/ |
后端状态快照、命令和持久化 |
TController/app/ui-next/ |
Next.js 仪器工作台 |
| 组件 | 技术 |
|---|---|
| UI 框架 | Tauri 2 + Next.js |
| 状态管理 | Rust backend snapshot + Zustand 视图缓存 |
| 数值计算 | Rust ndarray / ndarray-npy |
| 串口通信 | Rust serialport |
cd TController/app/src-tauri
cargo tauri dev # 自动启动 Next.js 开发服务器
cargo tauri build # 自动执行 Next.js 静态导出并打包 Tauri 应用单独验证前端时,可在 TController/app/ui-next 下运行 npm run build 或 npm run lint。浏览器直接访问 Next 开发服务器时使用显式 mock adapter,真实 Tauri 环境始终以 Rust backend snapshot 为状态源。
TController/app/src-tauri/icons/ 下的 PNG 与 ICO 由 scripts/gen_app_icons.mjs 从同目录的 icon.svg 生成,产物已提交进仓库,CI 不会重新生成。换图标时改 icon.svg 后本地重跑:
cd TController/app/ui-next && npm ci # 脚本复用前端的 sharp
cd ../../.. && node scripts/gen_app_icons.mjsbundle.icon 必须至少包含一个正方形 PNG:tauri-bundler 的 Linux 分支会跳过非 PNG 图标,而 AppImage 打包器在找不到正方形 PNG 时会直接 panic。macOS 不需要额外准备 .icns;列表里没有 .icns 时,打包器会把这些 PNG 合成为 ICNS。
.github/workflows/build.yml 在推送到任意分支、提交 PR、以及打 RELEASE-* 标签时运行,固件与上位机各平台并行构建(fail-fast: false,单一构建目标失败不影响其他目标)。
| 构建目标 | Runner | 产物 |
|---|---|---|
| 固件 cortex-m3 | ubuntu-24.04 |
.elf .hex(.map .lst 另存为 CI 制品,不进 Release) |
| 上位机 Windows x86_64 | windows-latest |
.msi -setup.exe -portable.zip |
| 上位机 Windows aarch64 | windows-11-arm |
-setup.exe -portable.zip |
| 上位机 Linux x86_64 | ubuntu-24.04 |
.deb .rpm .tar.gz .AppImage |
| 上位机 Linux aarch64 | ubuntu-24.04-arm |
.deb .rpm .tar.gz .AppImage |
| 上位机 macOS aarch64 | macos-15 |
.dmg .app.zip |
| 上位机 macOS x86_64 | macos-15-intel |
.dmg .app.zip |
六个上位机构建目标都跑在原生架构的 runner 上,不做交叉编译。Tauri 的 AppImage、MSI、DMG 打包器都无法跨架构工作。
发布时推 RELEASE-* 标签:所有构建成功后,release job 汇总全部产物、生成 SHA256SUMS 并创建 GitHub Release。任一平台失败则不发布,避免出现只覆盖部分平台的 Release。
已知约束:
- Windows aarch64 只出 NSIS。WiX v3 的 arm64 支持没在 Windows on ARM 上验证过,Tauri 官方也只保证 NSIS 支持 ARM64。
.tar.gz由 CI 自己打。Tauri 没有 tar.gz 目标,CI 把.deb的文件树解出来重新打包,内容与 deb 一致,安装方式是sudo tar -xzf TController_*.tar.gz -C /(依赖需自行安装)。- Linux 产物的 glibc 下限是 2.39(Ubuntu 24.04 及更新)。选 24.04:22.04 镜像从 2026-09-17 起进入弃用期;若要支持更老的发行版需换回 22.04 或改用容器构建。
- macOS 下限压到 10.13 (High Sierra),由
bundle.macOS.minimumSystemVersion设定,同时写入LSMinimumSystemVersion与MACOSX_DEPLOYMENT_TARGET。这是当前工具链能压到的最低值:Apple 的 SDK 支持表里 Xcode 16.x 支持的 deployment target 是 macOS 10.13–15(Xcode 27 起抬到 12.0),Tauri 打包器的默认下限也是 10.13。所以 macOS 的两个构建目标都固定用macos-15(Xcode 16.x),不用macos-latest。 注意 Tauri 官方在 Prerequisites 页只声明支持 macOS 10.15 (Catalina) 及以上,10.13 / 10.14 属于工具链允许但上游未验证的区间。若实测在 10.13/10.14 起不来,把minimumSystemVersion改成"10.15"即可;Apple Silicon 机型本身最低就是 11.0,不受影响。 - macOS 产物未签名、未公证。仓库没有配置 Apple 开发者证书,Tauri 只在设置了
APPLE_CERTIFICATE时才签名。用户首次打开需右键「打开」,或执行xattr -dr com.apple.quarantine /Applications/TController.app。要启用签名就给 workflow 加上APPLE_CERTIFICATE/APPLE_CERTIFICATE_PASSWORD/APPLE_SIGNING_IDENTITY等 secret。
- 无 HAL / 无标准库:所有外设寄存器通过自定义抽象层直接访问。
- 堆内存:
new/delete默认触发死循环;如需动态分配请在Startup/CXXStubs.cpp中实现。 - 静态构造:
.init_array在main()之前由Reset_Handler调用,支持全局 C++ 对象的构造函数。
本项目采用 PolyForm Shield 1.0.0 授权。
- 允许个人学习、研究、内部使用
- 禁止将本软件或其衍生品作为竞争产品提供
- 分发时必须附带本许可证全文或其 URL