Files
stc32g128k/Docs/30_architecture/wristband_architecture.html

1541 lines
77 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>STC32G 手环核心系统架构设计交互分析器 (架构.odg)</title>
<!-- 引入 Google Fonts -->
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;700&family=Noto+Sans+SC:wght@300;400;700&family=Fira+Code:wght@400;500&display=swap" rel="stylesheet">
<style>
:root {
--bg-dark: #070b13;
--bg-card: rgba(17, 24, 39, 0.7);
--border-glass: rgba(255, 255, 255, 0.08);
--neon-cyan: #00f2fe;
--neon-pink: #ff007f;
--neon-blue: #3b82f6;
--neon-purple: #8b5cf6;
--neon-green: #10b981;
--neon-gold: #f59e0b;
--text-main: #f3f4f6;
--text-muted: #9ca3af;
--font-display: 'Outfit', 'Noto Sans SC', sans-serif;
--font-mono: 'Fira Code', monospace;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
background-color: var(--bg-dark);
color: var(--text-main);
font-family: var(--font-display);
overflow-x: hidden;
background-image:
radial-gradient(at 10% 20%, rgba(59, 130, 246, 0.1) 0px, transparent 50%),
radial-gradient(at 90% 80%, rgba(139, 92, 246, 0.1) 0px, transparent 50%);
height: 100vh;
display: flex;
flex-direction: column;
}
/* 顶部导航 */
header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 2rem;
background: rgba(10, 15, 30, 0.8);
backdrop-filter: blur(12px);
border-bottom: 1px solid var(--border-glass);
z-index: 100;
}
.logo-area {
display: flex;
align-items: center;
gap: 0.75rem;
}
.logo-icon {
width: 2.5rem;
height: 2.5rem;
background: linear-gradient(135deg, var(--neon-cyan), var(--neon-blue));
border-radius: 0.5rem;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 0 15px rgba(0, 242, 254, 0.4);
font-weight: 700;
color: #000;
}
.logo-title h1 {
font-size: 1.25rem;
font-weight: 700;
background: linear-gradient(90deg, #fff, var(--text-muted));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.logo-title p {
font-size: 0.75rem;
color: var(--neon-cyan);
letter-spacing: 1px;
text-transform: uppercase;
}
.tab-menu {
display: flex;
gap: 1rem;
}
.tab-btn {
background: transparent;
border: 1px solid transparent;
color: var(--text-muted);
padding: 0.5rem 1.25rem;
border-radius: 2rem;
cursor: pointer;
font-family: var(--font-display);
font-weight: 600;
transition: all 0.3s ease;
}
.tab-btn.active {
color: var(--neon-cyan);
background: rgba(0, 242, 254, 0.08);
border-color: rgba(0, 242, 254, 0.3);
box-shadow: 0 0 15px rgba(0, 242, 254, 0.1);
}
.tab-btn:hover:not(.active) {
color: #fff;
background: rgba(255, 255, 255, 0.03);
}
/* 主体布局 */
main {
flex: 1;
display: flex;
overflow: hidden;
position: relative;
}
.content-panel {
flex: 1;
display: none;
padding: 1.5rem;
overflow-y: auto;
gap: 1.5rem;
}
.content-panel.active-panel {
display: flex;
}
/* ------------------ 架构拓扑面板样式 ------------------ */
#panel-topology {
flex-direction: row;
}
.diagram-container {
flex: 2;
position: relative;
background: rgba(10, 16, 32, 0.5);
border: 1px solid var(--border-glass);
border-radius: 1rem;
overflow: hidden;
display: flex;
flex-direction: column;
justify-content: space-around;
padding: 2.5rem 1rem 1rem 1rem;
}
.canvas-svg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 1;
}
.layer-row {
display: flex;
justify-content: space-around;
align-items: center;
width: 100%;
z-index: 2;
position: relative;
padding: 0.9rem 0;
border-radius: 0.75rem;
background: rgba(255, 255, 255, 0.015);
border: 1px dashed rgba(255, 255, 255, 0.03);
margin-bottom: 0.5rem;
}
.layer-title-tag {
position: absolute;
left: 1.5rem;
top: -0.75rem;
background: #141f36;
border: 1px solid var(--border-glass);
padding: 0.2rem 0.75rem;
border-radius: 1rem;
font-size: 0.7rem;
font-weight: 700;
letter-spacing: 0.5px;
color: var(--text-muted);
}
.layer-row.apps-layer .layer-title-tag { color: var(--neon-cyan); border-color: rgba(0, 242, 254, 0.2); }
.layer-row.appmgr-layer .layer-title-tag { color: var(--neon-green); border-color: rgba(16, 185, 129, 0.2); }
.layer-row.event-layer .layer-title-tag { color: var(--neon-pink); border-color: rgba(255, 0, 127, 0.2); }
.layer-row.drivers-layer .layer-title-tag { color: var(--neon-purple); border-color: rgba(139, 92, 246, 0.2); }
.layer-row.hw-layer .layer-title-tag { color: var(--neon-gold); border-color: rgba(245, 158, 11, 0.2); }
.module-card {
background: var(--bg-card);
border: 1px solid var(--border-glass);
border-radius: 0.75rem;
padding: 0.6rem 1.1rem;
width: 8.5rem;
text-align: center;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
user-select: none;
backdrop-filter: blur(8px);
}
.module-card:hover {
transform: translateY(-3px);
border-color: rgba(255, 255, 255, 0.2);
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4);
}
.module-card.selected-card {
background: rgba(0, 242, 254, 0.1);
border-color: var(--neon-cyan);
box-shadow: 0 0 20px rgba(0, 242, 254, 0.2);
}
.module-card.apps-node { border-left: 3px solid var(--neon-cyan); }
.module-card.appmgr-node { border-left: 3px solid var(--neon-green); }
.module-card.event-node { border-left: 3px solid var(--neon-pink); }
.module-card.drivers-node { border-left: 3px solid var(--neon-purple); }
.module-card.hw-node { border-left: 3px solid var(--neon-gold); }
.module-title {
font-size: 0.8rem;
font-weight: 700;
margin-bottom: 0.2rem;
}
.module-tag {
font-size: 0.6rem;
color: var(--text-muted);
text-transform: uppercase;
letter-spacing: 0.5px;
}
/* 侧边信息抽屉 */
.info-sidebar {
flex: 1;
max-width: 25rem;
background: var(--bg-card);
border: 1px solid var(--border-glass);
border-radius: 1rem;
padding: 1.5rem;
display: flex;
flex-direction: column;
gap: 1.25rem;
backdrop-filter: blur(12px);
overflow-y: auto;
}
.sidebar-section h3 {
font-size: 0.9rem;
color: var(--neon-cyan);
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 0.5rem;
border-left: 2px solid var(--neon-cyan);
padding-left: 0.5rem;
}
.sidebar-desc {
font-size: 0.85rem;
color: var(--text-muted);
line-height: 1.5;
}
.info-table {
width: 100%;
border-collapse: collapse;
font-size: 0.8rem;
margin-top: 0.5rem;
}
.info-table td {
padding: 0.4rem 0;
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}
.info-table td.label-col {
color: var(--text-muted);
width: 35%;
}
.info-table td.value-col {
color: #fff;
font-weight: 600;
}
.tag-list {
display: flex;
flex-wrap: wrap;
gap: 0.4rem;
margin-top: 0.5rem;
}
.tag-badge {
background: rgba(255, 255, 255, 0.05);
border: 1px solid var(--border-glass);
padding: 0.2rem 0.5rem;
border-radius: 0.25rem;
font-size: 0.7rem;
color: var(--text-muted);
}
.tag-badge.active-rel {
background: rgba(0, 242, 254, 0.1);
border-color: rgba(0, 242, 254, 0.3);
color: var(--neon-cyan);
}
/* ------------------ 仿真模拟器面板样式 ------------------ */
#panel-simulator {
flex-direction: row;
}
.simulator-canvas {
flex: 2;
background: rgba(10, 16, 32, 0.5);
border: 1px solid var(--border-glass);
border-radius: 1rem;
padding: 1.5rem;
display: flex;
flex-direction: column;
gap: 1.5rem;
overflow: hidden;
position: relative;
}
.sim-triggers {
display: flex;
gap: 1rem;
flex-wrap: wrap;
}
.sim-btn {
background: var(--bg-card);
border: 1px solid var(--border-glass);
padding: 0.75rem 1.25rem;
border-radius: 0.5rem;
color: #fff;
font-size: 0.85rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 0.5rem;
}
.sim-btn:hover {
border-color: var(--neon-cyan);
box-shadow: 0 0 15px rgba(0, 242, 254, 0.2);
transform: translateY(-2px);
}
.sim-btn.running {
background: rgba(255, 0, 127, 0.1);
border-color: var(--neon-pink);
box-shadow: 0 0 15px rgba(255, 0, 127, 0.2);
}
.sim-animation-viewport {
flex: 1;
border: 1px solid var(--border-glass);
border-radius: 0.75rem;
background: #0d1220;
position: relative;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
}
.sim-console {
background: rgba(0, 0, 0, 0.4);
border: 1px solid var(--border-glass);
border-radius: 0.5rem;
padding: 0.75rem 1rem;
font-family: var(--font-mono);
font-size: 0.8rem;
color: var(--neon-green);
height: 8rem;
overflow-y: auto;
line-height: 1.6;
}
/* ------------------ 引脚配置面板样式 ------------------ */
#panel-pins {
flex-direction: row;
}
.pinout-container {
flex: 1.5;
background: rgba(10, 16, 32, 0.5);
border: 1px solid var(--border-glass);
border-radius: 1rem;
display: flex;
align-items: center;
justify-content: center;
padding: 2rem;
}
/* 绘制 QFN-32 芯片的 CSS */
.qfn-package {
width: 22rem;
height: 22rem;
background: #111;
border: 4px solid #333;
border-radius: 1.5rem;
position: relative;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.6);
display: flex;
align-items: center;
justify-content: center;
}
.package-core {
width: 14rem;
height: 14rem;
border: 2px dashed #444;
border-radius: 0.5rem;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: #777;
font-size: 0.8rem;
font-weight: 700;
}
.package-core h2 {
font-size: 1rem;
color: #aaa;
margin-bottom: 0.25rem;
}
.pin-lead {
position: absolute;
background: #555;
transition: all 0.3s ease;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-family: var(--font-mono);
font-size: 0.65rem;
color: #ccc;
font-weight: 700;
}
.pin-lead:hover {
background: var(--neon-cyan);
color: #000;
box-shadow: 0 0 15px var(--neon-cyan);
z-index: 10;
}
.pin-lead.active-pin {
background: var(--neon-pink);
color: #000;
box-shadow: 0 0 15px var(--neon-pink);
z-index: 10;
}
/* QFN 4 边的引脚定位 */
/* Top side: Pin 25 to 32 */
.pin-t { width: 1.25rem; height: 1.5rem; top: -1.5rem; }
/* Bottom side: Pin 9 to 16 */
.pin-b { width: 1.25rem; height: 1.5rem; bottom: -1.5rem; }
/* Left side: Pin 1 to 8 */
.pin-l { width: 1.5rem; height: 1.25rem; left: -1.5rem; }
/* Right side: Pin 17 to 24 */
.pin-r { width: 1.5rem; height: 1.25rem; right: -1.5rem; }
/* ------------------ 分析报告面板样式 ------------------ */
#panel-report {
flex-direction: column;
max-width: 50rem;
margin: 0 auto;
}
.report-section {
background: var(--bg-card);
border: 1px solid var(--border-glass);
border-radius: 1rem;
padding: 2rem;
display: flex;
flex-direction: column;
gap: 1.25rem;
margin-bottom: 1.5rem;
}
.report-section h2 {
font-size: 1.25rem;
color: var(--neon-cyan);
border-bottom: 1px solid var(--border-glass);
padding-bottom: 0.5rem;
margin-bottom: 0.5rem;
}
.report-section p {
font-size: 0.9rem;
color: var(--text-muted);
line-height: 1.6;
}
.report-section ul {
list-style-type: none;
padding-left: 1rem;
}
.report-section li {
font-size: 0.9rem;
color: var(--text-muted);
line-height: 1.6;
margin-bottom: 0.5rem;
position: relative;
}
.report-section li::before {
content: "•";
color: var(--neon-cyan);
font-weight: bold;
display: inline-block;
width: 1em;
margin-left: -1em;
}
.code-box {
background: #080c14;
border: 1px solid var(--border-glass);
border-radius: 0.5rem;
padding: 1rem;
font-family: var(--font-mono);
font-size: 0.8rem;
color: #38bdf8;
overflow-x: auto;
line-height: 1.5;
}
/* SVG 连接线动画 */
.svg-path {
stroke: rgba(255, 255, 255, 0.08);
stroke-width: 2;
fill: none;
transition: all 0.3s ease;
}
.svg-path.active {
stroke: var(--neon-cyan);
stroke-width: 3;
stroke-dasharray: 6, 4;
animation: dash 10s linear infinite;
filter: drop-shadow(0 0 5px var(--neon-cyan));
}
.svg-path.active-pulse {
stroke: var(--neon-pink);
stroke-width: 3;
stroke-dasharray: 6, 4;
animation: dash-reverse 8s linear infinite;
filter: drop-shadow(0 0 5px var(--neon-pink));
}
@keyframes dash {
to {
stroke-dashoffset: -100;
}
}
@keyframes dash-reverse {
to {
stroke-dashoffset: 100;
}
}
/* 模拟器动画面板专用绘制 */
.bracelet-frame {
width: 10rem;
height: 18rem;
background: #111;
border: 4px solid #2c3e50;
border-radius: 1.5rem;
padding: 0.5rem;
display: flex;
flex-direction: column;
justify-content: space-between;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.7);
position: relative;
}
.bracelet-screen {
flex: 1;
background: #000;
border-radius: 0.75rem;
overflow: hidden;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
position: relative;
border: 2px solid #222;
}
.bracelet-led {
width: 0.6rem;
height: 0.6rem;
background: #333;
border-radius: 50%;
margin: 0.2rem auto;
box-shadow: inset 0 1px 3px rgba(255,255,255,0.2);
transition: all 0.2s ease;
}
.bracelet-led.led-red {
background: #ff007f;
box-shadow: 0 0 15px #ff007f, 0 0 5px #ff007f;
}
.bracelet-led.led-green {
background: #00df89;
box-shadow: 0 0 15px #00df89, 0 0 5px #00df89;
}
.sim-step-indicator {
position: absolute;
bottom: 1.5rem;
left: 1.5rem;
right: 1.5rem;
background: rgba(0,0,0,0.85);
border: 1px solid var(--neon-cyan);
border-radius: 0.5rem;
padding: 0.75rem 1rem;
font-size: 0.85rem;
color: #fff;
display: none;
z-index: 10;
}
</style>
</head>
<body>
<!-- 顶部状态栏 -->
<header>
<div class="logo-area">
<div class="logo-icon">STC</div>
<div class="logo-title">
<h1>STC32G手环事件驱动APP系统交互分析器</h1>
<p>Architect-Level Event-Driven App-Framework Analyzer (架构.odg)</p>
</div>
</div>
<div class="tab-menu">
<button class="tab-btn active" onclick="switchTab('topology')">5层拓扑结构</button>
<button class="tab-btn" onclick="switchTab('simulator')">事件生命周期仿真</button>
<button class="tab-btn" onclick="switchTab('pins')">物理引脚映射</button>
<button class="tab-btn" onclick="switchTab('report')">核心原理说明</button>
</div>
</header>
<!-- 主分析工作区 -->
<main>
<!-- Tab 1: 5层拓扑结构面板 -->
<div id="panel-topology" class="content-panel active-panel">
<div class="diagram-container">
<svg id="svg-connections" class="canvas-svg"></svg>
<!-- 1. 应用业务层 (Applications) -->
<div class="layer-row apps-layer" id="layer-apps-row">
<span class="layer-title-tag">5. 应用业务层 (Applications Layer) - 实现 OnStart, onRun, onClose, onEvent</span>
<div class="module-card apps-node" id="ActiveApp" onclick="selectModule('ActiveApp')">
<div class="module-title">ActiveApp</div>
<div class="module-tag">当前前台应用</div>
</div>
<div class="module-card apps-node" id="BgApp" onclick="selectModule('BgApp')">
<div class="module-title">BgApp</div>
<div class="module-tag">后台监测应用</div>
</div>
<div class="module-card apps-node" id="APP" onclick="selectModule('APP')">
<div class="module-title">APP (应用模块)</div>
<div class="module-tag">运行与页面逻辑</div>
</div>
</div>
<!-- 2. 应用管理层 (APP Management) -->
<div class="layer-row appmgr-layer" id="layer-appmgr-row">
<span class="layer-title-tag">4. 应用管理层 (APP Management Layer) - 生命周期与页面管理</span>
<div class="module-card appmgr-node" id="AppManager" onclick="selectModule('AppManager')">
<div class="module-title">AppManager</div>
<div class="module-tag">App 启动与关闭</div>
</div>
<div class="module-card appmgr-node" id="Lifecycle" onclick="selectModule('Lifecycle')">
<div class="module-title">Lifecycle</div>
<div class="module-tag">生命周期分配器</div>
</div>
</div>
<!-- 3. 事件总线与分配层 (Event & Dispatcher Layer) -->
<div class="layer-row event-layer" id="layer-event-row">
<span class="layer-title-tag">3. 事件总线与分配层 (Event & Dispatcher Layer) - 系统解耦消息总线</span>
<div class="module-card event-node" id="EventQueue" onclick="selectModule('EventQueue')">
<div class="module-title">EventQueue</div>
<div class="module-tag">事件环形队列</div>
</div>
<div class="module-card event-node" id="EventDispatcher" onclick="selectModule('EventDispatcher')">
<div class="module-title">EventDispatcher</div>
<div class="module-tag">事件路由器</div>
</div>
</div>
<!-- 4. 中断与驱动层 (Drivers) -->
<div class="layer-row drivers-layer" id="layer-drivers-row">
<span class="layer-title-tag">2. 中断与驱动层 (Drivers Layer) - 中断捕获与硬件驱动</span>
<div class="module-card drivers-node" id="KEY" onclick="selectModule('KEY')">
<div class="module-title">KEY (按键)</div>
<div class="module-tag">外部中断与消抖</div>
</div>
<div class="module-card drivers-node" id="RF" onclick="selectModule('RF')">
<div class="module-title">RF (无线解码)</div>
<div class="module-tag">INT2 捕获/发送</div>
</div>
<div class="module-card drivers-node" id="Timer1" onclick="selectModule('Timer1')">
<div class="module-title">Timer1 (时基)</div>
<div class="module-tag">1ms 中断调度</div>
</div>
<div class="module-card drivers-node" id="LCD" onclick="selectModule('LCD')">
<div class="module-title">LCD DRV (屏幕)</div>
<div class="module-tag">RM69310 偏置写</div>
</div>
<div class="module-card drivers-node" id="LED" onclick="selectModule('LED')">
<div class="module-title">LED (幻彩灯)</div>
<div class="module-tag">WS2812B 驱动</div>
</div>
<div class="module-card drivers-node" id="MOTOR" onclick="selectModule('MOTOR')">
<div class="module-title">MOTOR (马达)</div>
<div class="module-tag">异步时序发生</div>
</div>
</div>
<!-- 5. 硬件实体层 (STC32 HW) -->
<div class="layer-row hw-layer" id="layer-hw-row">
<span class="layer-title-tag">1. 硬件实体层 (STC32 HW Layer) - MCU物理寄存器及引脚</span>
<div class="module-card hw-node" id="MCU" onclick="selectModule('MCU')">
<div class="module-title">STC32G MCU</div>
<div class="module-tag">GPIO & Peripherals</div>
</div>
</div>
</div>
<!-- 右侧信息抽屉 -->
<div class="info-sidebar" id="topology-sidebar">
<div class="sidebar-section">
<h3 id="info-title">选择架构模块</h3>
<p class="sidebar-desc" id="info-desc" style="margin-top: 0.5rem;">
点击左侧 5 层架构卡片,核对该模块在手环 APP 框架系统中的定位、输入与输出依赖、操作的代码源文件及底层逻辑机制。
</p>
</div>
<div id="info-details" style="display: none; display: flex; flex-direction: column; gap: 1rem;">
<div class="sidebar-section">
<h3>框架角色定位</h3>
<table class="info-table">
<tr>
<td class="label-col">物理/逻辑口:</td>
<td class="value-col" id="info-pin">N/A</td>
</tr>
<tr>
<td class="label-col">层级分类:</td>
<td class="value-col" id="info-level">应用层</td>
</tr>
<tr>
<td class="label-col">数据输入源:</td>
<td class="value-col" id="info-hwsource">事件分发器</td>
</tr>
</table>
</div>
<div class="sidebar-section">
<h3>代码实现位置</h3>
<p class="sidebar-desc" id="info-file" style="font-family: var(--font-mono); color: var(--neon-cyan);"></p>
</div>
<div class="sidebar-section">
<h3>输入关系流</h3>
<div class="tag-list" id="info-inputs"></div>
</div>
<div class="sidebar-section">
<h3>输出指令/事件</h3>
<div class="tag-list" id="info-outputs"></div>
</div>
<div class="sidebar-section">
<h3>架构师原理阐述</h3>
<p class="sidebar-desc" id="info-theory" style="font-size: 0.8rem; color: #cbd5e1;"></p>
</div>
</div>
</div>
</div>
<!-- Tab 2: 仿真模拟器面板 -->
<div id="panel-simulator" class="content-panel">
<div class="simulator-canvas">
<h2>基于事件流的手环运行周期仿真模拟</h2>
<p class="sidebar-desc">点击触发典型物理事件,观察信号如何流转,并在各个模块内被消费及回调:</p>
<div class="sim-triggers">
<button class="sim-btn" onclick="startSimulation('sos')">
<span style="display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--neon-pink);"></span>
1. 按下 SOS 键 (主动求救事件)
</button>
<button class="sim-btn" onclick="startSimulation('intrusion')">
<span style="display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--neon-gold);"></span>
2. 传感器入侵 (被动报警事件)
</button>
<button class="sim-btn" onclick="startSimulation('sleep')">
<span style="display: inline-block; width: 8px; height: 8px; border-radius: 50%; background: var(--neon-blue);"></span>
3. 10s 无按键 (自动休眠与安全唤醒)
</button>
</div>
<!-- 模拟动画区域 -->
<div class="sim-animation-viewport">
<div class="bracelet-frame">
<div class="bracelet-led" id="sim-led-1"></div>
<div class="bracelet-screen" id="sim-screen">
<span id="screen-text" style="font-size: 0.9rem; text-align: center; color: var(--text-muted);">STANDBY</span>
</div>
<div class="bracelet-led" id="sim-led-2"></div>
</div>
<!-- 动态状态提示框 -->
<div class="sim-step-indicator" id="sim-step-box">
步骤说明
</div>
</div>
<!-- 仿真控制台输出 -->
<div class="sim-console" id="sim-console">
[EVENT BUS] 事件总线就绪。等待输入事件触发...
</div>
</div>
</div>
<!-- Tab 3: 硬件引脚核对面板 -->
<div id="panel-pins" class="content-panel">
<div class="pinout-container">
<div class="qfn-package">
<div class="package-core">
<h2>STC32G12K128</h2>
<p>QFN-32 封装物理引脚图</p>
<p style="font-size: 0.65rem; color: #555; margin-top: 1rem;">点击引脚进行核对</p>
</div>
<!-- 左侧: 1-8 -->
<div class="pin-lead pin-l" style="top: 2rem;" onclick="selectPin('1')">1</div>
<div class="pin-lead pin-l" style="top: 4.2rem;" onclick="selectPin('2')">2</div>
<div class="pin-lead pin-l" style="top: 6.4rem;" onclick="selectPin('3')">3</div>
<div class="pin-lead pin-l" style="top: 8.6rem;" onclick="selectPin('4')">4</div>
<div class="pin-lead pin-l" style="top: 10.8rem;" onclick="selectPin('5')">5</div>
<div class="pin-lead pin-l" style="top: 13rem;" onclick="selectPin('6')">6</div>
<div class="pin-lead pin-l" style="top: 15.2rem;" onclick="selectPin('7')">7</div>
<div class="pin-lead pin-l" style="top: 17.4rem;" onclick="selectPin('8')">8</div>
<!-- 底侧: 9-16 -->
<div class="pin-lead pin-b" style="left: 2rem;" onclick="selectPin('9')">9</div>
<div class="pin-lead pin-b" style="left: 4.2rem;" onclick="selectPin('10')">10</div>
<div class="pin-lead pin-b" style="left: 6.4rem;" onclick="selectPin('11')">11</div>
<div class="pin-lead pin-b" style="left: 8.6rem;" onclick="selectPin('12')">12</div>
<div class="pin-lead pin-b" style="left: 10.8rem;" onclick="selectPin('13')">13</div>
<div class="pin-lead pin-b" style="left: 13rem;" onclick="selectPin('14')">14</div>
<div class="pin-lead pin-b" style="left: 15.2rem;" onclick="selectPin('15')">15</div>
<div class="pin-lead pin-b" style="left: 17.4rem;" onclick="selectPin('16')">16</div>
<!-- 右侧: 17-24 -->
<div class="pin-lead pin-r" style="bottom: 17.4rem;" onclick="selectPin('17')">17</div>
<div class="pin-lead pin-r" style="bottom: 15.2rem;" onclick="selectPin('18')">18</div>
<div class="pin-lead pin-r" style="bottom: 13rem;" onclick="selectPin('19')">19</div>
<div class="pin-lead pin-r" style="bottom: 10.8rem;" onclick="selectPin('20')">20</div>
<div class="pin-lead pin-r" style="bottom: 8.6rem;" onclick="selectPin('21')">21</div>
<div class="pin-lead pin-r" style="bottom: 6.4rem;" onclick="selectPin('22')">22</div>
<div class="pin-lead pin-r" style="bottom: 4.2rem;" onclick="selectPin('23')">23</div>
<div class="pin-lead pin-r" style="bottom: 2rem;" onclick="selectPin('24')">24</div>
<!-- 顶侧: 25-32 -->
<div class="pin-lead pin-t" style="right: 17.4rem;" onclick="selectPin('25')">25</div>
<div class="pin-lead pin-t" style="right: 15.2rem;" onclick="selectPin('26')">26</div>
<div class="pin-lead pin-t" style="right: 13rem;" onclick="selectPin('27')">27</div>
<div class="pin-lead pin-t" style="right: 10.8rem;" onclick="selectPin('28')">28</div>
<div class="pin-lead pin-t" style="right: 8.6rem;" onclick="selectPin('29')">29</div>
<div class="pin-lead pin-t" style="right: 6.4rem;" onclick="selectPin('30')">30</div>
<div class="pin-lead pin-t" style="right: 4.2rem;" onclick="selectPin('31')">31</div>
<div class="pin-lead pin-t" style="right: 2rem;" onclick="selectPin('32')">32</div>
</div>
</div>
<!-- 引脚信息抽屉 -->
<div class="info-sidebar" id="pin-sidebar">
<div class="sidebar-section">
<h3 id="pin-title">引脚核对清单</h3>
<p class="sidebar-desc" id="pin-desc">
点击 QFN-32 封装芯片引脚,获取其在 Kicad 原理图中的 Net 网线标号、物理外设连接以及在 Drivers 中具体的软件操作细节。
</p>
</div>
<div id="pin-details" style="display: none; display: flex; flex-direction: column; gap: 1rem;">
<div class="sidebar-section">
<h3>物理引脚定义</h3>
<table class="info-table">
<tr>
<td class="label-col">引脚号:</td>
<td class="value-col" id="pin-num-val">Pin 1</td>
</tr>
<tr>
<td class="label-col">GPIO端口:</td>
<td class="value-col" id="pin-mcu-val" style="font-size: 0.75rem;">P1.0</td>
</tr>
<tr>
<td class="label-col">网络标号:</td>
<td class="value-col" id="pin-net-val" style="color: var(--neon-cyan);">TXD</td>
</tr>
<tr>
<td class="label-col">控制配置:</td>
<td class="value-col" id="pin-mode-val">推挽输出</td>
</tr>
</table>
</div>
<div class="sidebar-section">
<h3>电气物理连接</h3>
<p class="sidebar-desc" id="pin-hw-desc"></p>
</div>
<div class="sidebar-section">
<h3>驱动层软件操作</h3>
<p class="sidebar-desc" id="pin-sw-desc" style="font-family: var(--font-mono); color: var(--neon-purple);"></p>
</div>
</div>
</div>
</div>
<!-- Tab 4: 架构分析说明面板 -->
<div id="panel-report" class="content-panel">
<div class="report-section">
<h2>1. 基于 架构.odg 的事件驱动 APP 框架层级关系</h2>
<p>
本工程手环固件采用 **事件驱动式的 App 生命周期框架**Event-Driven App-Framework。整个软件系统划分为五个层级
</p>
<ul>
<li><strong>5. 应用业务层 (Applications)</strong>:各个独立的业务(如时钟显示、配对管理、主动紧急求救)被封装为具有统一回调接口的 App 对象。各 App 不得直接竞争 CPU 时间片。</li>
<li><strong>4. 应用管理层 (APP Management)</strong>:提供 `StartApp` / `CloseApp` 接口调度活跃 App控制页面跳转及 App 生命周期钩子OnStart、onRun、onClose、onEvent的执行。</li>
<li><strong>3. 事件总线与分配层 (Event & Dispatcher Layer)</strong>提供数据解耦。底层中断产生物理事件KeyEvent/RFEvent后封装并压入环形事件队列。主循环从中 Pop 出事件,并调用活跃 App 的 `onEvent` 回调接口消费。</li>
<li><strong>2. 中断与驱动层 (Drivers)</strong>按键移位检测、RF解调算法、Timer1 定时器时基心跳、AMOLED 显示渲染、WS2812B 幻彩 LED 控制及马达振动波形时序。</li>
<li><strong>1. 硬件实体层 (STC32 HW)</strong>STC32G12K128 单片机的寄存器和 GPIO 引脚。</li>
</ul>
</div>
<div class="report-section">
<h2>2. 活跃应用与后台应用的生命周期回调关系</h2>
<p>
为了在有限内存的 8051 架构上提供多页面交互,各个 App 具有极简的声明周期钩子:
</p>
<div class="code-box">
typedef struct {
u8 app_id; // 应用程序 ID
void (*OnStart)(void); // 启动:渲染初始页面
void (*onRun)(void); // 循环:主循环轮空调用,执行非阻塞业务
void (*onClose)(void); // 关闭:清理外设(如强制停振、关屏)
void (*onEvent)(KeyEvent evt); // 事件:读取并消费对应事件
} WristbandApp;</div>
<p>
当发生应用切换如对码成功返回时钟或者超时自动休眠APP 管理器通过调用原 App 的 `onClose()` 并调用新 App 的 `OnStart()`,能够以极其干净的方式清空定时器、马达振动、彩灯时序状态,避免不同业务状态之间的时序冲突。
</p>
</div>
<div class="report-section">
<h2>3. 自适应无线解调事件产生机制</h2>
<p>
无线射频解码采用非阻塞中断比值测量法:
</p>
<p>
当 LR690L 在天线收到脉冲时,引脚 P0.0 的电平沿触发 INT2 外部中断。RF 驱动测量低电平前导脉冲宽度,若落在 1500us~60000us 内则被确认为合法的同步头,并反算基准时基 T=low_time/31。
</p>
<p>
随后在接收循环中,基于 T 的宽度自适应滤出 24 位 EV1527 二进制序列1:3 宽为 0 码3:1 宽为 1 码),并将其封装为 `RF_PAIR_EVENT` 或 `RF_ALARM_EVENT` 压入环形缓冲区,不阻塞主时钟。
</p>
</div>
</div>
</main>
<!-- JS 逻辑控制 -->
<script>
// TAB 切换
function switchTab(tabId) {
document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active'));
document.querySelectorAll('.content-panel').forEach(panel => panel.classList.remove('active-panel'));
event.currentTarget.classList.add('active');
document.getElementById('panel-' + tabId).classList.add('active-panel');
if (tabId === 'topology') {
setTimeout(drawConnections, 100);
}
}
// 模块细节定义 (数据驱动 - 符合5层架构)
const modulesData = {
APP: {
title: "APP (页面运行与渲染模块)",
pin: "无物理引脚 (处于应用业务层)",
level: "5. 应用业务层 (Applications)",
hwsource: "App 管理器与主循环生命周期注册",
file: "Docs/50_module-breakdown/mod-app.md",
inputs: ["AppManager (生命周期 OnStart/onRun/onClose/onEvent)", "EventDispatcher (路由按键与射频警报事件)"],
outputs: ["LCD (刷写 Truly GRAM 窗口)", "MOTOR/LED (发出状态特定的异步动作指令)"],
desc: "手环的核心页面运行与交互层。ClockApp 在前台绘制大字时钟及右上角电量指示MenuApp 显示单屏菜单项并引导页面跳转PairApp 接收按键输入在 [01]~[99] 范围内微调序号并保存至 Flash 数据库AlarmApp 刷新红粗边框闪烁灯效异步输出防区特定的马达振动SOSApp 周期发射 0x08 求救包并红灯爆闪。"
},
ActiveApp: {
title: "ActiveApp (前台活跃应用)",
pin: "无物理引脚 (处于应用业务层)",
level: "5. 应用业务层 (Applications)",
hwsource: "App 管理生命周期注册",
file: "App/main.c (WristbandApp 实体实例)",
inputs: ["AppManager (被调用生命周期 OnStart/onRun/onClose/onEvent)"],
outputs: ["LCD (调用显示)", "LED / MOTOR (调用声光报警)"],
theory: "前台活跃应用代表当前手环屏幕呈现的业务(如 ClockApp, MenuApp, PairApp。它在 OnStart 中刷写页面,在 onRun 中非阻塞检查超时,并在 onEvent 中处理按键事件逻辑。"
},
BgApp: {
title: "BgApp (后台监测应用)",
pin: "无物理引脚 (处于应用业务层)",
level: "5. 应用业务层 (Applications)",
hwsource: "App 管理器后台轮询",
file: "App/main.c (系统后台守护线程)",
inputs: ["EventDispatcher (接收对码/报警事件)"],
outputs: ["AppManager (当处于布防且匹配防区报警时抢占启动警报App)"],
theory: "后台应用常驻内存,主要任务是监听无线事件。当 EventDispatcher 路由了配对防区的传感器报警包时若系统处于布防状态BgApp 将唤醒并命令 AppManager 强制开启警报页面 (APP_ALARM)。"
},
AppManager: {
title: "AppManager (APP 管理器)",
pin: "无物理引脚",
level: "4. 应用管理层 (APP Management)",
hwsource: "App 业务层逻辑跳转",
file: "App/main.c (StartApp / CloseApp)",
inputs: ["ActiveApp (页面返回请求)", "BgApp (强制报警抢占)"],
outputs: ["Lifecycle (驱动钩子回调)"],
theory: "管理各个 WristbandApp 实例的入口与出口。执行 StartApp时调度关闭旧 App执行 onClose 停振关灯),重新挂接 active_app 指针并执行新 App 的 OnStart() 初始化。"
},
Lifecycle: {
title: "Lifecycle (生命周期调度机)",
pin: "无物理引脚",
level: "4. 应用管理层 (APP Management)",
hwsource: "AppManager",
file: "App/main.c (回调指针分发)",
inputs: ["AppManager"],
outputs: ["ActiveApp (驱动 App 状态转换)"],
theory: "生命周期分配器是 App 框架运行的动力源。它控制当前活跃 App 的 `onRun` 能够在主循环中不断被轮空调用,以及 `onEvent` 能够读取缓冲区的按键动作。"
},
EventQueue: {
title: "EventQueue (事件环形队列)",
pin: "无物理引脚 (软件 FIFO 缓冲)",
level: "3. 事件总线与分配层 (Event & Dispatcher Layer)",
hwsource: "KEY / RF 中断驱动打包推送",
file: "App/main.c (key_event_buf)",
inputs: ["KEY (压入按键)", "RF (压入射频对码/报警)"],
outputs: ["EventDispatcher (弹出并分发)"],
theory: "深度为 4 级的环形事件队列。底层中断驱动可在任何时刻异步 Push 事件入队;主循环定时 Pop 弹出事件。这是解耦底层中断与上层应用的关键缓冲,保证在高频通信下按键动作不会遗漏。"
},
EventDispatcher: {
title: "EventDispatcher (事件路由器)",
pin: "无物理引脚",
level: "3. 事件总线与分配层 (Event & Dispatcher Layer)",
hwsource: "EventQueue 队列输出",
file: "App/main.c (事件分发逻辑)",
inputs: ["EventQueue (读取待分发事件)"],
outputs: ["Lifecycle (分发至活跃App)", "BgApp (路由无线警报)"],
theory: "读取环形事件队列。识别事件类型按键事件KEY_EVENT分发给 `active_app->onEvent()` 消费;对码/无线入侵事件RF_EVENT分发给后台应用 `BgApp` 进行防区比对。"
},
KEY: {
title: "KEY (按键外部中断与消抖)",
pin: "Pin 19 (P3.6), Pin 20 (P3.7), Pin 21 (P2.0), Pin 24 (P2.3)",
level: "高阻输入 + 使能内部上拉",
hwsource: "物理侧边按键及上部 SOS 求救按键",
file: "App/main.c (Key_Scan_Process)",
inputs: ["MCU (引脚电平中断沿)"],
outputs: ["EventQueue (Key_Push_Event 压入队列)"],
theory: "通过 10ms 中断进行移位滤波扫描。支持 SW3 长按 3 秒产生主菜单调用事件、SW1+SW2 组合对码长按等。识别完毕后,将按键事件压入 EventQueue。"
},
RF: {
title: "RF (LR690L 编解码驱动)",
pin: "Pin 29 (P0.0), Pin 30 (P0.1), Pin 31 (P0.2), Pin 32 (P0.3)",
level: "INT2 中断输入 / 其余IO推挽输出",
hwsource: "LR690L 433MHz 射频前端芯片",
file: "Drivers/rf.c, Drivers/rf.h",
inputs: ["MCU (INT2 电平变化中断唤醒)"],
outputs: ["EventQueue (压入射频报警/对码事件)", "MCU (P0.3 输出电平发射数据)"],
theory: "外部沿中断触发同步头自适应比值解调:解调 24 位 EV1527 码包。主动发射时开启 RF_TX通过 GPIO 串行驱动 RF_TX_DAT 引脚高低电平,发送 5 遍。屏蔽忽略被动 0x08 码求救广播。"
},
Timer1: {
title: "Timer1 (定时器中断时基)",
pin: "单片机内部 16位 定时器 1",
level: "2. 中断与驱动层 (Drivers)",
hwsource: "MCU 24MHz 晶振时钟源",
file: "App/main.c (Timer1_Isr)",
inputs: ["MCU (内部主频脉冲)"],
outputs: ["KEY (10ms时基触发)", "MOTOR (分频异步计数)", "LED (闪烁时序分频)"],
theory: "系统软件时基源。每 1ms 产生一次中断。它在 ISR 中直接调度按键移位扫描10ms、马达异步状态时间递减、幻彩 LED 闪烁状态的扣减,是多外设并发的基础。"
},
LCD: {
title: "LCD DRV (Truly 屏幕绘制)",
pin: "Pin 9-14 (P5.4-P3.1 SPI控制总线)",
level: "2. 中断与驱动层 (Drivers)",
hwsource: "Truly 0.95寸 AMOLED 屏 (RM69310)",
file: "Drivers/lcd.c, Drivers/lcd.h",
inputs: ["ActiveApp (重绘渲染指令)"],
outputs: ["MCU (写 SPI 数据寄存器)"],
theory: "提供无障碍大字号32x64px、位图图标、居中排版清屏。写 GRAM 局部视窗自动强制加上 `+4` 像素硬件物理偏置,以防花屏和漏绘。"
},
LED: {
title: "LED (WS2812B 幻彩级联驱动)",
pin: "Pin 27 (P2.6 / LED_RGB 控制脚)",
level: "2. 中断与驱动层 (Drivers)",
hwsource: "WS2812B 级联双彩色灯",
file: "App/main.c (WS2812_WriteRGB)",
inputs: ["ActiveApp (指示灯亮起请求)", "Timer1 (闪烁维持计数)"],
outputs: ["MCU (SPI_MOSI 引脚输出)"],
theory: "WS2812B 发送前关闭全局中断并临时将马达脚P2.4)置为高阻,彻底隔离噪声,发送 24-bit 占空比数据。提供异步闪烁线程更新控制字。"
},
MOTOR: {
title: "MOTOR (马达异步震动驱动)",
pin: "Pin 25 (P2.4 / MOTOR_PWM 脚)",
level: "2. 中断与驱动层 (Drivers)",
hwsource: "振动马达驱动晶体管",
file: "App/main.c (Motor_Start_Pattern)",
inputs: ["ActiveApp (按键反馈/告警指令)", "Timer1 (1ms分频状态处理)"],
outputs: ["MCU (P2.4 输出高低电平)"],
theory: "异步脉冲发生提供按键微震、各种防区节奏报警震动、SOS持续间歇振动。APP 调用后立即退出,由 Timer1 自动扣减时间改变电平状态,不阻塞系统运行。"
},
MCU: {
title: "STC32G MCU (单片机寄存器物理层)",
pin: "Pin 1-32",
level: "1. 硬件实体层 (STC32 HW)",
hwsource: "物理硬件芯片",
file: "Drivers/stc32g.h (底层寄存器声明)",
inputs: ["外部物理信号沿跳变 / 电池电压"],
outputs: ["物理端口电平控制信号"],
theory: "硬件层。包括 P0/P1/P2/P3/P5 端口寄存器、INT2 中断控制器、IAP Flash 控制器、12位 ADC 转换内核以及 PCON 电源控制寄存器。"
}
};
// 选中某个模块卡片
function selectModule(modId) {
// 清理旧选中状态
document.querySelectorAll('.module-card').forEach(card => card.classList.remove('selected-card'));
// 设为新选中
const card = document.getElementById(modId);
if (card) card.classList.add('selected-card');
// 填充右侧细节
const data = modulesData[modId];
if (data) {
document.getElementById('info-title').innerText = data.title;
document.getElementById('info-pin').innerText = data.pin;
document.getElementById('info-level').innerText = data.level;
document.getElementById('info-hwsource').innerText = data.hwsource;
document.getElementById('info-file').innerText = data.file;
document.getElementById('info-theory').innerText = data.theory;
// 输入列表
const inList = document.getElementById('info-inputs');
inList.innerHTML = '';
data.inputs.forEach(inp => {
const span = document.createElement('span');
span.className = 'tag-badge active-rel';
span.innerText = inp;
inList.appendChild(span);
});
// 输出列表
const outList = document.getElementById('info-outputs');
outList.innerHTML = '';
data.outputs.forEach(out => {
const span = document.createElement('span');
span.className = 'tag-badge';
span.innerText = out;
outList.appendChild(span);
});
document.getElementById('info-desc').style.display = 'none';
document.getElementById('info-details').style.display = 'flex';
}
// 联动高亮 SVG 连线
highlightLines(modId);
}
// SVG 连接配置关系 (5层架构依赖流向)
const connections = [
{ from: 'MCU', to: 'KEY', color: 'gold' },
{ from: 'MCU', to: 'RF', color: 'gold' },
{ from: 'MCU', to: 'Timer1', color: 'gold' },
{ from: 'Timer1', to: 'KEY', color: 'purple' },
{ from: 'Timer1', to: 'LED', color: 'purple' },
{ from: 'Timer1', to: 'MOTOR', color: 'purple' },
{ from: 'KEY', to: 'EventQueue', color: 'pink' },
{ from: 'RF', to: 'EventQueue', color: 'pink' },
{ from: 'EventQueue', to: 'EventDispatcher', color: 'pink' },
{ from: 'EventDispatcher', to: 'Lifecycle', color: 'green' },
{ from: 'EventDispatcher', to: 'BgApp', color: 'cyan' },
{ from: 'BgApp', to: 'AppManager', color: 'green' },
{ from: 'AppManager', to: 'Lifecycle', color: 'green' },
{ from: 'Lifecycle', to: 'ActiveApp', color: 'cyan' },
{ from: 'ActiveApp', to: 'LCD', color: 'purple' },
{ from: 'ActiveApp', to: 'LED', color: 'purple' },
{ from: 'ActiveApp', to: 'MOTOR', color: 'purple' },
{ from: 'LCD', to: 'MCU', color: 'gold' },
{ from: 'LED', to: 'MCU', color: 'gold' },
{ from: 'MOTOR', to: 'MCU', color: 'gold' }
];
// 动态绘制连接线 (SVG)
function drawConnections() {
const svg = document.getElementById('svg-connections');
if (!svg) return;
svg.innerHTML = ''; // 清空
const containerRect = svg.getBoundingClientRect();
connections.forEach((conn, index) => {
const fromEl = document.getElementById(conn.from);
const toEl = document.getElementById(conn.to);
if (fromEl && toEl) {
const r1 = fromEl.getBoundingClientRect();
const r2 = toEl.getBoundingClientRect();
// 计算相对 SVG 的中心坐标
const x1 = r1.left + r1.width / 2 - containerRect.left;
const y1 = r1.top + r1.height / 2 - containerRect.top;
const x2 = r2.left + r2.width / 2 - containerRect.left;
const y2 = r2.top + r2.height / 2 - containerRect.top;
// 绘制贝塞尔三次曲线获得优雅的流线效果
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
const dy = Math.abs(y2 - y1);
const controlY1 = y1 + (y2 > y1 ? dy * 0.45 : -dy * 0.45);
const controlY2 = y2 - (y2 > y1 ? dy * 0.45 : -dy * 0.45);
const d = `M ${x1} ${y1} C ${x1} ${controlY1}, ${x2} ${controlY2}, ${x2} ${y2}`;
path.setAttribute('d', d);
path.setAttribute('class', 'svg-path');
path.setAttribute('id', `conn-${conn.from}-${conn.to}`);
// 根据类型初始透明配色
if (conn.color === 'cyan') {
path.style.stroke = 'rgba(0, 242, 254, 0.15)';
} else if (conn.color === 'green') {
path.style.stroke = 'rgba(16, 185, 129, 0.15)';
} else if (conn.color === 'pink') {
path.style.stroke = 'rgba(255, 0, 127, 0.15)';
} else if (conn.color === 'purple') {
path.style.stroke = 'rgba(139, 92, 246, 0.15)';
} else {
path.style.stroke = 'rgba(245, 158, 11, 0.15)';
}
svg.appendChild(path);
}
});
}
// 高亮相关连线
function highlightLines(selectedModId) {
// 首先全部暗淡
document.querySelectorAll('.svg-path').forEach(p => {
p.classList.remove('active');
p.style.strokeWidth = '2';
if (p.id.includes('ActiveApp') || p.id.includes('BgApp')) {
p.style.stroke = 'rgba(0, 242, 254, 0.15)';
} else if (p.id.includes('AppManager') || p.id.includes('Lifecycle')) {
p.style.stroke = 'rgba(16, 185, 129, 0.15)';
} else if (p.id.includes('EventQueue') || p.id.includes('EventDispatcher')) {
p.style.stroke = 'rgba(255, 0, 127, 0.15)';
} else if (p.id.includes('Timer1') || p.id.includes('KEY') || p.id.includes('RF')) {
p.style.stroke = 'rgba(139, 92, 246, 0.15)';
} else {
p.style.stroke = 'rgba(245, 158, 11, 0.15)';
}
});
// 寻找连接到该模块的连线并高亮
connections.forEach(conn => {
if (conn.from === selectedModId || conn.to === selectedModId) {
const path = document.getElementById(`conn-${conn.from}-${conn.to}`);
if (path) {
path.classList.add('active');
path.style.stroke = 'var(--neon-cyan)';
path.style.strokeWidth = '3';
}
}
});
}
window.addEventListener('resize', drawConnections);
window.addEventListener('load', () => {
setTimeout(drawConnections, 200);
});
// ------------------ 引脚配置交互数据 ------------------
const pinsInfo = {
'1': { mcu: "P1.0 / DIS_RST", net: "DIS_RST", mode: "推挽输出", hw: "Truly AMOLED 屏幕复位控制脚 (RESET)。拉低重置屏幕驱动芯片寄存器。", sw: "LCD_Init 模块复位时置低 10ms再置高 10ms 以启动上电序列。" },
'2': { mcu: "P1.1 / SPI_DCX", net: "SPI_DCX", mode: "推挽输出", hw: "Truly AMOLED 屏幕的 SPI 数据/指令选择脚。拉高写数据,拉低写指令。", sw: "写数据时拉高引脚;写配置控制命令时拉低引脚。" },
'3': { mcu: "P1.4 / SPI_SDI", net: "SPI_SDI", mode: "推挽输出", hw: "Truly AMOLED 屏幕的 SPI 串行数据输入端 (MOSI/SDA)。", sw: "向 SPI 发送缓存寄存器 SPDAT 写入 8 位字节自动串行移位输出。" },
'4': { mcu: "P1.5 / SPI_CLK", net: "SPI_CLK", mode: "推挽输出", hw: "Truly AMOLED 屏幕的 SPI 串行同步时钟引脚 (SCL)。", sw: "SPI 通信时由单片机片上外设硬件模块自动产生串行移位脉冲。" },
'5': { mcu: "P1.6 / SPI_CS", net: "SPI_CS", mode: "推挽输出", hw: "Truly AMOLED 屏幕的 SPI 片选输入脚。低电平选中Truly显示芯片操作。", sw: "在对屏幕进行像素或指令读写前置 0读写完毕后置 1 挂起片选线。" },
'6': { mcu: "P1.7 / SPI_SDO", net: "SPI_SDO", mode: "推挽输出", hw: "Truly AMOLED 屏幕的 SPI 串行数据输出端 (MISO) 用于状态读取。", sw: "N/A" },
'10': { mcu: "Vcc / AVcc", net: "+3.3V", mode: "电源逻辑供电", hw: "屏幕与系统 3.3V 逻辑工作正向电压轨道。", sw: "N/A" },
'11': { mcu: "VREF+", net: "+3.3V", mode: "电源并联线", hw: "芯片基准电压与 3.3V 电源物理并联通道。", sw: "N/A" },
'12': { mcu: "GND / AGND", net: "+3.3V", mode: "电源并联线", hw: "电源并联,稳定模拟和数字地电平轨道通道。", sw: "N/A" },
'13': { mcu: "P3.1 / RxD", net: "RXD", mode: "高阻输入", hw: "外部调试串口 1 (UART1) 接收引脚,用于接收控制指令或测试配置包。", sw: "UART1 串口接收中断。当收到指定调试帧时触发对应的状态切换。" },
'14': { mcu: "P3.0 / TxD", net: "TXD", mode: "推挽输出", hw: "外部调试串口 1 (UART1) 发送引脚用于向PC输出系统日志及调试标志。", sw: "通过 UART 串口发送缓存寄存器 SBUF 直接写入以触发物理串行传输。" },
'19': { mcu: "P3.6", net: "RF_TX_DAT", mode: "推挽输出", hw: "LR690L 射频模块数据发射脚,向天线前端输入特定的高低电平比值信号。", sw: "主动发射时,利用 GPIO 高速反转产生符合 EV1527 时域码宽的串行方波。" },
'20': { mcu: "P3.7", net: "RF_RX", mode: "推挽输出", hw: "LR690L 射频芯片接收监听使能控制脚,高电平开始监听无线载波。", sw: "常态待机和搜码模式下 GPIO 默认拉高引脚以保持持续接收侦听状态。" },
'21': { mcu: "P2.0", net: "RF_TX", mode: "推挽输出", hw: "LR690L 射频芯片发射偏置使能引脚,拉高打开射频前端发射天线电磁波。", sw: "当需要主动求救或对码发射时,先拉高此引脚,发射完毕后置 0 关闭天线。" },
'22': { mcu: "P2.1", net: "RF_RX_DATA", mode: "输入中断 (INT2)", hw: "LR690L 433MHz 无线接收输出引脚,在捕获到无线载波变化时输出沿信号。", sw: "休眠前配置该引脚上升/下降沿作为 INT2 中断唤醒源;平时由沿捕捉解码。" },
'23': { mcu: "P2.2", net: "SHUT", mode: "推挽输出", hw: "AMOLED 升压芯片 SGM3833 供电控制脚。拉高开启屏幕电网;拉低关断。", sw: "电源管理模块操作。睡眠时置 0 切断屏幕正负压;唤醒后置 1 并强延时 50ms。" },
'24': { mcu: "P2.3", net: "LED_RGB", mode: "SPI_MOSI 硬件复用输出", hw: "WS2812B 级联双幻彩灯 DIN 控制引脚,输入符合 800kHz 高频的 RGB 码流。", sw: "写彩灯时临时屏蔽中断,将 P2.5 马达隔离引脚,高速送出 24bit 灯效数据。" },
'26': { mcu: "P2.5", net: "MOTOR_PWM", mode: "推挽输出", hw: "连接振动马达驱动 MOS 晶体管控制栅。高电平触发震动;低电平关断马达。", sw: "马达驱动模块负责在 Timer1 后台定时控制 P2.5 输出指定时间的占空波形。" },
'27': { mcu: "P2.6", net: "KEY_SOS", mode: "高阻输入 + 上拉", hw: "物理并联的 SOS1 与 SOS2 报警按键,按下将引脚接地拉低,休眠支持唤醒。", sw: "KEY 初始化使能 P2PU 的 Bit 6 上拉Timer1 读取引脚电平沿推送 SOS 警报。" },
'28': { mcu: "P2.7", net: "DET", mode: "高阻输入", hw: "USB 底座充电插入检测。USB 连接时输入高电平,平时为低电平。", sw: "时钟待机应用在 OnRun 循环中读取引脚电平状态,更新时钟后是否带 '+' 充电符。" },
'29': { mcu: "P0.0 / ADC8", net: "BAT_ADC", mode: "模拟输入", hw: "连接电池采样分压电阻中点。采集 0~1.3V 电压以判定当前电池容量。", sw: "使能内置 12位 ADC 的通道 8 进行模数转换,将结果换算为电池百分比。" },
'30': { mcu: "P0.1", net: "KEY_UP", mode: "高阻输入 + 上拉", hw: "侧边按键 ▲ (上移键)。短按进行参数累加或高亮切换,休眠支持唤醒。", sw: "KEY 初始化使能 P0PU 的 Bit 1 上拉,消抖后向事件队列投递 UP_CLICK 事件。" },
'31': { mcu: "P0.2", net: "KEY_CONFIRM", mode: "高阻输入 + 上拉", hw: "侧边按键 ■ (确认/菜单键)。短按确认,长按 3 秒调出主菜单交互。", sw: "P0PU 使能上拉10ms 定时判定短击 (CONFIRM_CLICK) 与 3秒长按 (CONFIRM_LONG)。" },
'32': { mcu: "P0.3", net: "KEY_DOWN", mode: "高阻输入 + 上拉", hw: "侧边按键 ▼ (下移键)。短按移动高亮,长按 2 秒退出警报,休眠支持唤醒。", sw: "P0PU 使能上拉,判定短击 (DOWN_CLICK) 与 2秒长按 (DOWN_LONG) 并投递。" }
};
function selectPin(pinNum) {
// 清理旧高亮
document.querySelectorAll('.pin-lead').forEach(lead => lead.classList.remove('active-pin'));
// 设为新高亮
const el = document.querySelector(`.pin-lead[onclick="selectPin('${pinNum}')"]`);
if (el) el.classList.add('active-pin');
const info = pinsInfo[pinNum];
if (info) {
document.getElementById('pin-num-val').innerText = "Pin " + pinNum;
document.getElementById('pin-mcu-val').innerText = info.mcu;
document.getElementById('pin-net-val').innerText = info.net;
document.getElementById('pin-mode-val').innerText = info.mode;
document.getElementById('pin-hw-desc').innerText = info.hw;
document.getElementById('pin-sw-desc').innerText = info.sw;
document.getElementById('pin-desc').style.display = 'none';
document.getElementById('pin-details').style.display = 'flex';
} else {
document.getElementById('pin-num-val').innerText = "Pin " + pinNum;
document.getElementById('pin-mcu-val').innerText = "N/A";
document.getElementById('pin-net-val').innerText = "NC (悬空未连接)";
document.getElementById('pin-mode-val').innerText = "高阻抗模式";
document.getElementById('pin-hw-desc').innerText = "该引脚在原理图中属于 NC 状态,无任何物理走线,配置为高阻输入以防引入寄生耦合和杂散电流漏电。";
document.getElementById('pin-sw-desc').innerText = "N/A";
document.getElementById('pin-desc').style.display = 'none';
document.getElementById('pin-details').style.display = 'flex';
}
}
// ------------------ 业务流仿真器逻辑 ------------------
let simInterval = null;
function appendConsole(text) {
const consoleBox = document.getElementById('sim-console');
consoleBox.innerHTML += `<br>[EVENT BUS] ${text}`;
consoleBox.scrollTop = consoleBox.scrollHeight;
}
function clearConsole() {
document.getElementById('sim-console').innerHTML = '[EVENT BUS] 物理输入事件已捕获,进入事件流周期...';
}
function showStep(text) {
const stepBox = document.getElementById('sim-step-box');
stepBox.style.display = 'block';
stepBox.innerText = text;
}
function stopSimulationVisuals() {
if (simInterval) clearInterval(simInterval);
document.getElementById('sim-led-1').className = 'bracelet-led';
document.getElementById('sim-led-2').className = 'bracelet-led';
document.getElementById('sim-screen').style.background = '#000';
document.getElementById('screen-text').innerText = "STANDBY";
document.getElementById('screen-text').style.color = '#777';
document.getElementById('sim-step-box').style.display = 'none';
document.querySelectorAll('.sim-btn').forEach(b => b.classList.remove('running'));
}
function startSimulation(type) {
stopSimulationVisuals();
clearConsole();
// 标记运行按钮
event.currentTarget.classList.add('running');
let step = 0;
if (type === 'sos') {
appendConsole("物理源Pin 24 (KEY_SOS) 瞬间拉低。");
simInterval = setInterval(() => {
step++;
if (step === 1) {
showStep("1. [Drivers Layer] KEY 定时器扫描:检测到 P2.3 低电平,移位判定消抖成立。");
appendConsole("驱动层:检测到 SOS 按下。封包 KeyEvent = KEY_SOS_ACTIVE。");
} else if (step === 2) {
showStep("2. [Event Layer] 事件总线:将 KEY_SOS_ACTIVE 压入 4 级 EventQueue 环形缓冲区。");
appendConsole("事件层:调用 Key_Push_Event 将事件压入 EventQueue尾指针 tail 偏移。");
} else if (step === 3) {
showStep("3. [APP Management] 应用管理器:主循环读出该事件,判断属于最高优先级强占。");
appendConsole("管理层Pop 读出 SOS 求救。执行 StartApp(APP_SOS)。当前 App 强制调用 onClose()。");
} else if (step === 4) {
showStep("4. [Applications] 活跃应用切换SOS 报警应用切入前台,触发 OnStart() 接口。");
document.getElementById('sim-screen').style.background = '#ff007f';
document.getElementById('screen-text').innerText = "SOS EMIS\n(ACTIVE APP)";
document.getElementById('screen-text').style.color = '#fff';
appendConsole("业务层APP_SOS 前台运行,调用 LCD、WS2812B 驱动及马达异步长震指令。");
} else if (step === 5) {
showStep("5. [Drivers -> HW] 驱动执行与物理输出RF 发射 Factory ID 载波WS2812B 交替闪烁。");
// 闪烁彩灯
let flashCount = 0;
const flashInterval = setInterval(() => {
if (flashCount > 8) {
clearInterval(flashInterval);
return;
}
const led1 = document.getElementById('sim-led-1');
const led2 = document.getElementById('sim-led-2');
if (flashCount % 2 === 0) {
led1.className = 'bracelet-led led-red';
led2.className = 'bracelet-led';
} else {
led1.className = 'bracelet-led';
led2.className = 'bracelet-led led-red';
}
flashCount++;
}, 200);
appendConsole("驱动物理输出:开启 P0.1 发射P0.3 数据翻转WS2812 发送前关中断防马达杂讯干扰。");
} else if (step === 6) {
showStep("6. [Loop Phase] 异步轮询APP_SOS 维持在前台。射频每 2s 周期发射求救包,直至侧边按键退出。");
appendConsole("周期流转:系统处于 APP_SOS 的 onRun 状态轮询,等待事件队列接收按键复位。");
clearInterval(simInterval);
document.querySelectorAll('.sim-btn').forEach(b => b.classList.remove('running'));
}
}, 1500);
} else if (type === 'intrusion') {
appendConsole("物理源:天线捕获 433MHz 载波沿Pin 29 (RF_RX_DATA) 触发中断。");
simInterval = setInterval(() => {
step++;
if (step === 1) {
showStep("1. [Drivers Layer] INT2 外部沿中断触发RF 解调驱动实时自适应比值还原 24 位地址与状态。");
appendConsole("驱动层INT2 中断触发。自适应解出 EV1527 地址: 0xA3F21B, 状态: 0x01 (门磁动作)。");
} else if (step === 2) {
showStep("2. [Event Layer] 事件分发:解调算法验证通过,将 RF_ALARM_EVENT 事件派送给后台监测应用。");
appendConsole("事件层:将包含匹配信息的射频事件路由发送至后台监测应用 BgApp。");
} else if (step === 3) {
showStep("3. [Applications] 后台应用比对BgApp 读出地址,比对 Flash 数据库匹配为配对槽位,处于布防。");
appendConsole("业务层BgApp 匹配 IAP 扇区 Slot0 (PORTE)。检测到布防状态,调用 AppManager 启动报警。");
} else if (step === 4) {
showStep("4. [APP Management] 应用管理器强占AppManager 挂起当前活跃应用,调用 StartApp(APP_ALARM)。");
appendConsole("管理层StartApp 运行。旧 App onClose新 App (APP_ALARM) 调用 OnStart()。");
} else if (step === 5) {
showStep("5. [Applications -> Drivers] 报警应用前台亮屏,渲染门磁入侵字样;调度 LED 专属颜色及马达差异震动。");
document.getElementById('sim-led-1').className = 'bracelet-led led-green';
document.getElementById('sim-led-2').className = 'bracelet-led led-green';
document.getElementById('sim-screen').style.background = '#0f172a';
document.getElementById('screen-text').innerText = "PORTE 01\nINTRUSION!";
document.getElementById('screen-text').style.color = '#ff007f';
appendConsole("业务层APP_ALARM 重绘屏幕LCD 执行 X+4 窗口偏置Timer1 中断在后台自减计时器驱动马达短震。");
} else if (step === 6) {
showStep("6. [Loop Phase] 系统挂起警告:报警状态处于当前活跃状态,主循环周期轮询 onRun声光异步时序运转。");
appendConsole("周期流转:系统处于 APP_ALARM 活跃状态等待处理。");
clearInterval(simInterval);
document.querySelectorAll('.sim-btn').forEach(b => b.classList.remove('running'));
}
}, 1500);
} else if (type === 'sleep') {
appendConsole("物理状态待机正常无按键无线天线无信号inactivity_timer 累加。");
simInterval = setInterval(() => {
step++;
if (step === 1) {
showStep("1. [Applications Layer] 自动休眠判定ActiveApp 在 onRun 轮询中检查发现闲置已满 1000 滴答。");
appendConsole("业务层:时钟应用闲置满 10 秒。主动向系统投递休眠请求事件 AUTO_SLEEP_EVENT。");
} else if (step === 2) {
showStep("2. [APP Management] 关闭活跃 AppAppManager 触发当前时钟应用的 onClose() 接口,清空显示。");
document.getElementById('sim-screen').style.background = '#000';
document.getElementById('screen-text').innerText = "";
document.getElementById('sim-led-1').className = 'bracelet-led';
document.getElementById('sim-led-2').className = 'bracelet-led';
appendConsole("管理层AppManager 执行 active_app->onClose(),关断屏幕 GRAM 写入。");
} else if (step === 3) {
showStep("3. [Drivers -> HW] 切断 PMIC 双电源PM 驱动拉低 P2.7 (SHUT=0),完全断开 SGM3833 电源。");
appendConsole("驱动层PM 写入 P2.7=0SGM3833 关闭,彻底消除 Truly AMOLED 暗漏电流。");
} else if (step === 4) {
showStep("4. [HW Layer] 停机休眠:配置 GPIO 按键沿及无线接收引脚为唤醒源,写入 PCON |= 0x02 进入深度睡眠。");
appendConsole("硬件层MCU 核心挂起,系统晶振停振,全系统耗电降至微安级。");
} else if (step === 5) {
showStep("5. [Wakeup sequence] 外部唤醒与安全上电按键按下触发外部中断唤醒系统PM 优先拉高 SHUT=1。");
appendConsole("硬件层唤醒PM 拉高 P2.7 (SHUT=1) 开启 SGM3833 供电。");
} else if (step === 6) {
showStep("6. [HW -> Drivers -> Apps] 强延时 50ms 避浪涌:强行阻塞延时稳压,重配置 RM69310 亮起时钟应用。");
document.getElementById('sim-screen').style.background = '#0f172a';
document.getElementById('screen-text').innerText = "10:35\nRESTORED";
document.getElementById('screen-text').style.color = '#00f2fe';
appendConsole("唤醒链条:强延时 50ms 避开冲击浪涌 -> 重调 LCD_Init 写入指令 -> StartApp(APP_CLOCK) 恢复大字时钟。");
clearInterval(simInterval);
document.querySelectorAll('.sim-btn').forEach(b => b.classList.remove('running'));
}
}, 1500);
}
}
</script>
</body>
</html>