- 嵌入式
- 固件
- 硬件开发
- 智能硬件
【免费下载链接】IronOS
Open Source Soldering Iron firmware
导读:本文以 UI 目录 README 为骨架,深入剖析 IronOS(开源焊台固件)的用户界面如何被拆分为
logic(模式逻辑)与drawing(屏幕绘制)两个半区,并进一步按屏幕类型(128×32、96×16)细分绘制实现。读者将理解每个 UI 模式的状态机组织方式、guiContext上下文与 scratch 状态的设计意图、渲染循环与转场动画的工作机制,以及各模式对应的逻辑文件与绘制文件如何协作,为二次开发、移植新焊台或调试 UI 行为提供可直接对照源码的实战指引。
一、整体架构:UI 的两个半区
IronOS 的用户界面位于 source/Core/Threads/UI,整个 UI 被刻意拆分为两个半区(half):
logic文件夹:存放实现每个模式逻辑的.cpp文件,负责处理按键事件(button events)与任何业务逻辑。它回答"这个模式该做什么、按键按下后去哪个模式"。drawing文件夹:存放仅负责屏幕绘制的.cpp文件,回答"这个模式在屏幕上画什么"。绘制文件进一步按**屏幕类型(screen types)**细分。
对应地,drawing下存在两个屏幕类型子目录:
- mono_128x32:面向 128×32 单色 OLED(如 Miniware TS100/TS80 系列、Sequre 部分机型);
- mono_96x16:面向 96×16 单色 OLED(如 Pinecil、MHP30 等)。
两个子目录拥有同名同签名的绘制函数(例如draw_homescreen_detailed.cpp、draw_soldering_power_status.cpp、pre_render_assets.cpp等),通过编译期宏(OLED_128x32/OLED_96x16)选择实现。屏幕类型宏在各自 BSP 的 configuration.h 中定义,例如:
- Miniware/configuration.h:
#define OLED_128x32 1、#define OLED_96x16 1; - Pinecil/configuration.h:
#define OLED_96x16 1; - MHP30/configuration.h:
#define OLED_96x16 1; - Sequre/configuration.h:
#define OLED_128x32 1。
所有绘制函数通过统一的头文件 ui_drawing.hpp 暴露接口,逻辑层只依赖该接口,不关心实际屏幕尺寸——这是两层解耦的关键。
二、逻辑层:模式机与按键状态
2.1 所有模式共用一个OperatingMode状态枚举
逻辑层的心脏是 OperatingModes.h 中定义的OperatingMode枚举,它覆盖了 IronOS 的全部 UI 状态:
| 枚举值 | 含义 |
|---|---|
StartupLogo | 显示启动 Logo |
CJCCalibration | 冷端(CJC)校准 |
StartupWarnings | 启动检查与警告 |
InitialisationDone | 首次启动进入主界面前的特殊过渡态,允许跳转到其他启动态 |
HomeScreen | 主界面,作为进入其他模式的"发射台" |
Soldering | 主焊接模式 |
SolderingProfile | 跟随温度曲线焊接(如回流焊 reflow) |
Sleeping | 睡眠态,保持较低睡眠温度 |
Hibernating | 休眠态,加热器完全关闭直至唤醒 |
SettingsMenu | 设置菜单 |
DebugMenuReadout | 调试信息 |
TemperatureAdjust | 目标温度调整 |
UsbPDDebug | USB-PD 调试信息 |
ThermalRunaway | 热失控警告 |
这些模式分别由逻辑文件实现:主界面在 HomeScreen.cpp、焊接在 Soldering.cpp、曲线焊接在 SolderingProfile.cpp、睡眠在 Sleep.cpp、设置菜单在 SettingsMenu.cpp、调试菜单在 DebugMenu.cpp、温度调整在 TemperatureAdjust.cpp、启动警告在 ShowStartupWarnings.cpp,另有 CJC 校准 CJC.cpp 与三个 USB-PD 调试实现(USBPDDebug_FUSB.cpp、USBPDDebug_FS2711.cpp、USBPDDebug_HUSB238.cpp)。
2.2 按键状态机:ButtonState
按键输入由 Buttons.hpp 中的ButtonState枚举描述:
BUTTON_NONE = 0, /* 无按键 / 低于滤波时间 */ BUTTON_F_SHORT = 1, /* 前键(front)短按 */ BUTTON_B_SHORT = 2, /* 后键(back)短按 */ BUTTON_F_LONG = 4, /* 前键长按(持续按住) */ BUTTON_B_LONG = 8, /* 后键长按 */ BUTTON_BOTH = 16, /* 同时按下两键(按下并释放) */ BUTTON_BOTH_LONG = 32, /* 两键同时长按 */注意其中注释的语义:"Pressed" 表示完整的按下+释放脉冲(__/),"holding" 表示按键保持低电平超过滤波时间。所有逻辑文件都围绕这 7 种按键状态编写switch分支。例如主界面 HomeScreen.cpp 中:
BUTTON_F_SHORT→ 进入Soldering(若烙铁头未断开);BUTTON_B_SHORT→ 进入SettingsMenu;BUTTON_F_LONG→ 进入SolderingProfile(开启PROFILE_SUPPORT时);BUTTON_B_LONG→ 进入DebugMenuReadout。
2.3 上下文与 scratch 状态:guiContext
每个模式函数都以(const ButtonState buttons, guiContext *cxt)为签名,返回下一个OperatingMode。guiContext(定义于 OperatingModes.h)承担跨渲染帧的状态保持:
struct guiContext { TickType_t viewEnterTime; // 进入该视图的 tick 时间 OperatingMode previousMode; // 上一个模式 TransitionAnimation transitionMode;// 转场动画方向 struct scratch { // 跨重绘保留、模式切换时清空的草稿状态 uint16_t state1, state2, state5, state6; uint32_t state3, state4, state7; } scratch_state; };设计意图非常明确(与 GUIRendering.md 中"类即时模式渲染"理念一致):函数应尽量把状态收敛到 context 结构里,保持状态使用扁平化。这样外部事件可以改变状态,状态也可经 BLE 等外部控制接口读写。各模式把scratch_state的不同字段当作"局部变量"使用,例如:
- 焊接模式 Soldering.cpp 中:
state1= 按键锁定状态(0 未锁定+已释放,1 未锁定,2 已锁定,3 已锁定+已释放),state2= 升压(boost)模式,state3= 蜂鸣器定时器; - 设置菜单 SettingsMenu.cpp 中:
state1= 根菜单条目、state2= 子菜单条目、state3/state4= 自动重复加速定时、state5= 当前菜单长度缓存、state6= 是否正在渲染帮助文本; - 温度调整 TemperatureAdjust.cpp 中:
state1= 等待释放标志、state2/state3= 自动重复加速。
scratch_state在模式切换时会被整体清零(见下文渲染循环),因此它天然不适合承载需要跨模式保留的数据——跨模式数据应放全局变量或设置项。
三、绘制层:按屏幕类型细分的绘制函数
3.1 统一接口
绘制层通过 ui_drawing.hpp 暴露以下核心接口(均为void或bool返回,只画屏不改状态):
void ui_draw_warning_undervoltage(void); void ui_draw_power_source_icon(void); void ui_draw_tip_temperature(bool symbol, const FontStyle font); bool warnUser(const char *warning, const ButtonState buttons); void ui_draw_cjc_sampling(const uint8_t num_dots); void ui_draw_debug_menu(const uint8_t item_number); void ui_draw_homescreen_detailed(TemperatureType_t tipTemp); void ui_draw_homescreen_simplified(TemperatureType_t tipTemp); void ui_pre_render_assets(void); void ui_draw_soldering_power_status(bool boost_mode_on); void ui_draw_soldering_basic_status(bool boostModeOn); void ui_draw_soldering_detailed_sleep(TemperatureType_t tipTemp); void ui_draw_soldering_basic_sleep(TemperatureType_t tipTemp); void ui_draw_soldering_profile_advanced(...); void ui_draw_temperature_change(void); void ui_draw_usb_pd_debug_state(...); void ui_draw_usb_pd_debug_pdo(...); void printVoltage(void);从函数命名可看出绘制层遵循两条轴线:详细/简化视图(detailed/simplified,由DetailedIDLE、DetailedSoldering设置项控制)与模式(主界面/焊接/睡眠/温度调整/调试等)。
3.2 以主界面为例:同一函数,两种屏幕类型
ui_draw_homescreen_detailed在 mono_128x32/draw_homescreen_detailed.cpp 与 mono_96x16/draw_homescreen_detailed.cpp 各有一份实现,都通过#ifdef OLED_128x32/#ifdef OLED_96x16保护。两者逻辑一致,差异在布局常量:
- 128×32 版:大号(12×24)温度数字垂直居中靠一侧,另一侧两行 SMALL(8×16)状态行(设定温度、输入电压);断头(tip disconnected)时在对应侧绘制
disconnectedTip位图并显示电压,xTaskGetTickCount() % 1000 < 300控制CoolingTempBlink的 300ms 灭/700ms 亮闪烁; - 96×16 版:单行布局,温度与电压分居左右两侧,同样处理断头与闪烁。
两个版本都考虑OLED::getRotation()来左右翻转布局,适应左右手持握。绘制前会先通过ui_pre_render_assets(见 mono_128x32/pre_render_assets.cpp)把位图预翻转存入 RAM,避免每帧实时镜像。
3.3 焊接状态的两种视图
焊接时根据DetailedSoldering设置选择:
- 详细视图draw_soldering_power_status.cpp:大号温度 + 功率(取自
x10WattHistory.average(),超过 99.9W 时去掉小数位保持 5 格宽度)+ 输入电压; - 基础视图draw_soldering_basic_status.cpp:温度、设定值、电源类型图标与电压等更紧凑的信息。
焊接逻辑 Soldering.cpp 在调用绘制前完成全部决策:根据state2(boost)选择目标温度(SolderingTemp或BoostTemp,并按TemperatureInF转换);误差 ±10℃ 内判定收敛,触发蜂鸣器与LED_HOT,否则LED_HEATING;随后依次检查checkExitSoldering()(欠压退出)、shouldBeSleeping()、heaterThermalRunawayCounter > 8(热失控)后,才把剩余按键交给handleSolderingButtons。
四、渲染循环与转场动画
4.1 GUI 线程:类即时模式渲染
UI 在 FreeRTOS 的 GUI 线程中运行,入口是 GUIThread.cpp 的startGUITask(L215-L243)。启动时依次完成翻译准备(prepareTranslations)、OLED 初始化、亮度/反色/旋转设置、ui_pre_render_assets预渲染资源,随后进入for(;;)主循环,以vTaskDelayUntil(&startRender, TICKS_100MS * 4 / 10)维持约 20–25 FPS。
核心渲染函数是guiRenderLoop(L144-L193),每帧流程:
- 调用
guiHandleDraw()完成一次屏幕绘制; - 若返回的模式与当前模式不同,则记录
viewEnterTime、previousMode,清零scratch_state并切换模式; - 若
context.transitionMode非None,则切换到 OLED 次级帧缓冲,再渲染一帧新视图,然后按动画类型(transitionScrollDown/transitionSecondaryFramebuffer)在两缓冲间转场; - 最后
OLED::refresh()输出。
guiHandleDraw(L44-L143)是模式机的分发中心:先读取按键状态,依据温度/灵敏度决定屏幕亮灭与状态 LED(睡眠判定逻辑见 shouldDeviceSleep.cpp),再以switch(currentOperatingMode)把当前模式分发给对应的逻辑函数(drawHomeScreen、gui_solderingMode、gui_SolderingSleepingMode、gui_solderingTempAdjust、showDebugMenu、performCJCC、gui_SettingsMenu、showPDDebug、showWarnings等)。
4.2 转场动画与方向语义
转场方向定义于 OperatingModes.h 的TransitionAnimation枚举(None/Right/Left/Down/Up)。其"方向感"用于强化菜单导航的空间隐喻。GUI 线程只实现了Left、Right、Down三种动画,Up尚未实现但枚举已预留。
方向约定可参考 GUIRendering.md 中的示意图:
- 主界面向下 → 调试菜单;
- 焊接/曲线焊接模式 ← 主界面 ← 设置主菜单 ← 设置子菜单;
- 设置子菜单之间纵向滚动(Down)。
实际代码中的用法示例:主界面按后键短按进入设置菜单时设TransitionAnimation::Right(HomeScreen.cpp),设置菜单返回时设TransitionAnimation::Left;进入子菜单用Right,退出用Left(SettingsMenu.cpp);主界面进入调试菜单用Down(HomeScreen.cpp),调试菜单返回用Up(DebugMenu.cpp)。在详细视图模式(DetailedIDLE && DetailedSoldering)下部分转场会被抑制为None,因为布局已足够相似、无需动画。
转场得以实现的前提是:逻辑函数先渲染当前屏幕,再返回新状态,保证切换前帧缓冲中有完整的旧视图;随后分发层自动再渲染一帧新视图到次级缓冲并完成过渡(见 GUIThread.cpp)。
五、关键模式逐个拆解
5.1 设置菜单:最复杂的 UI 代码
SettingsMenu.cpp 自述为"最复杂的 GUI 代码",采用两级菜单结构:主菜单(分类)→ 子菜单(设置项)。其数据驱动核心是menuitem结构数组(rootSettingsMenu与subSettingsMenus,定义于 settingsGUI.hpp),每个条目含绘制回调、可见性回调、增量处理回调及多语言短描述/长描述索引。
关键机制:
- 帮助文本:
render_menu在按键静止超过 3 秒(HELP_TEXT_TIMEOUT_TICKS = TICKS_SECOND * 3)后,从"设置项视图"自动切换为滚动显示该设置的长描述(drawScrollingText); - 滚动指示器:
getMenuLength遍历菜单计算可见条目数(隐藏条目不计入),indicatorHeight = OLED_HEIGHT / menuLength计算指示条高度,末项或闪烁节拍时隐藏/闪烁(SettingsMenu.cpp); - 自动重复与加速:长按前/后键时,
autoRepeatAcceleration按PRESS_ACCEL_STEP递增、受PRESS_ACCEL_INTERVAL_MAX/MIN钳制,实现"越按越快"的数值滚动; - 按键交换:
ReverseButtonSettings开启时在进入分支前交换前后键语义(L200-L219); - 保存时机:翻过主菜单末尾(
draw == nullptr)或双键退出时调用saveSettings()持久化设置。
5.2 焊接模式:setpoint、蜂鸣与安全退出
焊接主逻辑 Soldering.cpp 的状态机完整注释(L94-L107)概括了交互流程:
- 短按任意键 → 温度调整屏(
TemperatureAdjust); - 长按前键 → 升压模式(临时切换 PID 目标温度到
BoostTemp); - 长按后键 / 双键 → 退出回主界面;
- 双键长按 → 按键锁定/解锁(锁定逻辑由
state1与LockingMode设置项控制)。
温度收敛检测(L124-L140):目标与实际温差落入 ±10℃ 即视为收敛,触发一次 1/3 秒蜂鸣并点亮LED_HOT,否则LED_HEATING。安全出口依次为:欠压退出(checkExitSoldering,内部调用 checkUndervoltage.cpp 的checkForUnderVoltage,在 DC 供电且电压低于lookupVoltageLevel()时把currentTempTargetDegC归零并绘制ui_draw_warning_undervoltage)、关机判定(shouldShutdown,见 shouldDeviceShutdown.cpp,超时或后键长按触发)、睡眠判定与热失控保护(heaterThermalRunawayCounter > 8)。
5.3 温度调整:带约束的增量步进
TemperatureAdjust.cpp 进入时先关闭加热器(currentTempTargetDegC = 0),并等待用户松开按键(waitForRelease)后才响应输入。增量来源:
- 短按:
TempChangeShortStep; - 长按:
TempChangeLongStep配合自动重复加速; ReverseButtonTempChangeEnabled开启时对delta取反。
新值会按增量取整(newTemp = (newTemp / delta) * delta)并钳制在MIN_TEMP_C ~ MAX_TEMP_C(或华氏MIN_TEMP_F ~ MAX_TEMP_F)范围内,然后写回SolderingTemp设置。3 秒无操作或双键退出,退出前saveSettings()。
5.4 曲线焊接(回流焊):分阶段温度斜坡
SolderingProfile.cpp 实现分段温度曲线:阶段 0 为预热(目标ProfilePreheatTemp、速率ProfilePreheatSpeed),随后依次执行ProfilePhase1..5的 {温度, 持续时间},全部完成后进入冷却段(速率ProfileCooldownSpeed),烙铁头低于 55℃ 时蜂鸣并返回主界面。相位完成条件为"达到目标温度且时间达标"(L71),斜坡目标按"每 tick 温度增量 = 相时长/(温差)"线性插值(L128-L141)。绘制时详细视图调用ui_draw_soldering_profile_advanced显示当前相位、剩余时间与目标温度,并叠加功率状态。
5.5 睡眠/休眠与启动警告
- Sleep.cpp 统一处理
Sleeping与Hibernating:前者把目标温度降到min(SleepTemp, SolderingTemp),后者完全关闭加热(currentTempTargetDegC = 0);shouldBeSleeping()恢复时返回previousMode; - ShowStartupWarnings.cpp 用
state1作为警告序号,依次检查:设置被重置(settingsWereReset)、设备防伪校验、加速度计缺失(AccelMissingWarningCounter计数小于 2 时提示)、PD 控制器缺失(FUSB/HUB238/FS2711 三种实现按POW_PD_EXT宏选择探测),全部通过后进入StartupLogo。
六、如何定位与扩展 UI 代码
对开发者而言,UI 相关改动遵循清晰的分工:
- 改逻辑:进入 logic 对应模式文件(如焊接
Soldering.cpp、主界面HomeScreen.cpp),修改按键处理、状态转移、目标温度等决策; - 改画面:进入 drawing/mono_128x32 或 drawing/mono_96x16 修改同名绘制函数,且必须两个屏幕类型同步维护(同名函数、同一签名);
- 新增模式:在 OperatingModes.h 增加枚举值,在 GUIThread.cpp 的
switch分发中加入分支,并在 ui_drawing.hpp 声明新的绘制接口后到两个 drawing 子目录实现; - 跨状态数据:优先放入
guiContext.scratch_state(模式内保留)或全局变量(跨模式),模式切换时scratch_state会被清零; - 转场动画:在返回新模式前设置
cxt->transitionMode为Left/Right/Down(Up枚举已预留但动画未实现,见 GUIRendering.md)。
整体架构可用一句话概括:逻辑层决定"去往何方",绘制层决定"画成何样",GUI 线程以类即时模式每帧重绘并驱动转场,guiContext在两者之间传递状态。理解这一分工后,无论是修 bug、加功能还是移植到新屏幕尺寸的焊台,都能在 source/Core/Threads/UI 目录下快速定位到正确文件。
- 嵌入式
- 固件
- 硬件开发
- 智能硬件
【免费下载链接】IronOS
Open Source Soldering Iron firmware
相关推荐
Open3D多线程渲染架构:RenderToBuffer与离屏渲染技术
Open3D多线程渲染架构:RenderToBuffer与离屏渲染技术 离屏渲染技术概述 离屏渲染(Offscreen Rendering)是Open3D可视化
计算机视觉图形学3D渲染科学计算OpenRocket 架构深度解析:JPMS 双模块设计、仿真引擎与 3D 渲染管线
OpenRocket 架构深度解析:JPMS 双模块设计、仿真引擎与 3D 渲染管线 OpenRocket 是一款基于 Java 与 Swing 的模型火箭气动
桌面应用科学计算3D渲染OpenUI AgentInterface 深度解析:复合式 Agent 聊天界面组件的架构、插槽 API 与渲染管线
OpenUI AgentInterface 深度解析:复合式 Agent 聊天界面组件的架构、插槽 API 与渲染管线 OpenUI(The Open Stan
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考