Files
stc32g128k/Docs/30_architecture/wristband_architecture.html

1528 lines
76 KiB
HTML
Raw Normal View History

<!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">1. 应用业务层 (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>
<!-- 2. 应用管理层 (APP Management) -->
<div class="layer-row appmgr-layer" id="layer-appmgr-row">
<span class="layer-title-tag">2. 应用管理层 (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 Layer) -->
<div class="layer-row event-layer" id="layer-event-row">
<span class="layer-title-tag">3. 事件总线层 (Event 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">4. 中断与驱动层 (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">5. 硬件实体层 (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>1. 应用业务层 (Applications)</strong>:各个独立的业务(如时钟显示、配对管理、主动紧急求救)被封装为具有统一回调接口的 App 对象。各 App 不得直接竞争 CPU 时间片。</li>
<li><strong>2. 应用管理层 (APP Management)</strong>:提供 `StartApp` / `CloseApp` 接口调度活跃 App控制页面跳转及 App 生命周期钩子OnStart、onRun、onClose、onEvent的执行。</li>
<li><strong>3. 事件总线层 (Event Layer)</strong>提供数据解耦。底层中断产生物理事件KeyEvent/RFEvent后封装并压入环形事件队列。主循环从中 Pop 出事件,并调用活跃 App 的 `onEvent` 回调接口消费。</li>
<li><strong>4. 中断与驱动层 (Drivers)</strong>按键移位检测、RF解调算法、Timer1 定时器时基心跳、AMOLED 显示渲染、WS2812B 幻彩 LED 控制及马达振动波形时序。</li>
<li><strong>5. 硬件实体层 (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 = {
ActiveApp: {
title: "ActiveApp (前台活跃应用)",
pin: "无物理引脚 (处于应用业务层)",
level: "1. 应用业务层 (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: "1. 应用业务层 (Applications)",
hwsource: "App 管理器后台轮询",
file: "App/main.c (系统后台守护线程)",
inputs: ["EventDispatcher (接收对码/报警事件)"],
outputs: ["AppManager (当处于布防且匹配防区报警时抢占启动警报App)"],
theory: "后台应用常驻内存,主要任务是监听无线事件。当 EventDispatcher 路由了配对防区的传感器报警包时若系统处于布防状态BgApp 将唤醒并命令 AppManager 强制开启警报页面 (APP_ALARM)。"
},
AppManager: {
title: "AppManager (APP 管理器)",
pin: "无物理引脚",
level: "2. 应用管理层 (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: "2. 应用管理层 (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 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 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: "4. 中断与驱动层 (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: "4. 中断与驱动层 (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: "4. 中断与驱动层 (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: "4. 中断与驱动层 (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: "5. 硬件实体层 (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 / RXD2", net: "TXD", mode: "推挽输出", hw: "外部调试串口发送端,用来下载及打印架构运行日志。", sw: "通过 UART1 串口发送缓冲寄存器 SBUF 直接写入。" },
'2': { mcu: "P1.1 / TXD2", net: "RXD", mode: "高阻输入", hw: "外部调试串口接收端,支持在测试页面输入控制指令。", sw: "UART1 中断接收,解析对应的指令字并投入状态调度。" },
'4': { mcu: "P1.5 / SCL", net: "+3.3V", mode: "电源逻辑供电", hw: "提供 Truly AMOLED 显示芯片的 3.3V 逻辑正向工作电压。", sw: "N/A" },
'5': { mcu: "P1.6 / SDA", net: "+3.3V", mode: "电源逻辑供电并联线", hw: "Truly 0.95 AMOLED 屏 3.3V 逻辑并联通道。", sw: "N/A" },
'6': { mcu: "P1.7 / RST", net: "TP_RST", mode: "推挽输出", hw: "触摸屏复位控制线,本手环为非触摸,引脚悬空扩展。", sw: "GPIO 设置为高电平输出挂起。" },
'8': { mcu: "UCAP", net: "TP_INT", mode: "输入中断", hw: "触摸板中断唤醒电平,非触摸手环,物理悬空。", sw: "N/A" },
'9': { mcu: "P5.4 / SS", net: "SPI_SDO", mode: "推挽输出", hw: " Truly AMOLED 屏 SPI 主动输出 (MISO) 对应 P5.4 物理控制线。", sw: "SPI 寄存器配置复用输出。" },
'10': { mcu: "Vcc / AVcc", net: "SPI_CS", mode: "推挽输出", hw: " Truly AMOLED 屏 SPI 片选使能线,低电平使能对 RM69310 的写屏操作。", sw: "LCD 读写驱动中,写像素前拉低 CS写完后拉高 CS。" },
'11': { mcu: "VREF+", net: "SPI_CLK", mode: "推挽输出", hw: " Truly AMOLED 屏 SPI 时钟同步脉冲控制引脚 (SCL)。", sw: "SPI 写入数据时由硬件模块自动产生串行移位脉冲。" },
'12': { mcu: "GND / AGND", net: "SPI_SDI", mode: "推挽输出", hw: " Truly AMOLED 屏 SPI 输入引脚 (MOSI/SDA),写入像素二进制色彩数据流。", sw: "SPI 发送数据寄存器 SPDAT 写入字节时产生该路时序。" },
'13': { mcu: "P3.0 / RxD", net: "SPI_DCX", mode: "推挽输出", hw: " Truly AMOLED 屏幕的数据命令指示线。拉高表示当前写入 GRAM 的是 16-bit 像素数据;拉低表示写入的是控制寄存器指令。", sw: "由 `lcd.c` 的 `LCD_WriteData` 和 `LCD_WriteCmd` 进行拉高/低控制。" },
'14': { mcu: "P3.1 / TxD", net: "DIS_RST", mode: "推挽输出", hw: " Truly AMOLED 屏硬件复位引脚。开机或安全唤醒时通过高低电平跳变重置屏幕控制寄存器。", sw: "唤醒序列中先拉低 10ms再拉高 10ms 触发硬件复位配置命令。" },
'19': { mcu: "P3.6 / INT2", net: "KEY_DOWN", mode: "高阻输入 + 使能内部上拉", hw: "侧边 ▼ (下移键),按下触发引脚接地低电平;休眠时支持 P3.6 端口中断直接唤醒 CPU。", sw: "KEY 初始化设置 P3PU=0x40Timer1 中断中以 10ms 周期读取 P3.6 电平值进行滤波。" },
'20': { mcu: "P3.7 / INT3", net: "KEY_CONFIRM", mode: "高阻输入 + 使能内部上拉", hw: "侧边 ■ (确认键),短按确认,长按 3 秒进入主菜单;支持休眠中断唤醒。", sw: "KEY 初始化设置 P3PU=0x80中断中定时扫描累加计数若计数满足 300 判定为 CONFIRM_LONG 并投递事件。" },
'21': { mcu: "P2.0 / A8", net: "KEY_UP", mode: "高阻输入 + 使能内部上拉", hw: "侧边 ▲ (上移键),按下触发引脚低电平;支持休眠时中断唤醒系统。", sw: "KEY 初始化设置 P2PU=0x01通过移位计数器累加消抖。" },
'22': { mcu: "P2.1 / A9", net: "BAT_ADC", mode: "模拟输入", hw: "连接电池分压电路。采集 0~1.3V 电池模拟电压提供给系统电量处理模块。", sw: "开启内置 12位 ADC 并配置通道 9 转换。取多次累加平均值消噪,线性转换为 0%~100%。" },
'23': { mcu: "P2.2 / A10", net: "DET", mode: "高阻输入", hw: "底座 USB 充电插入检测。充电时底座供电输出将其拉为高电平;常态为低电平。", sw: "时钟待机 App (ClockApp) 在 onRun 轮询中读取 P2.2 引脚状态更新充电加号显示。" },
'24': { mcu: "P2.3 / A11", net: "KEY_SOS", mode: "高阻输入 + 使能内部上拉", hw: "物理并联的 SOS1 与 SOS2 报警按键共地。按下任意键直接拉低 P2.3 触发紧急求救。", sw: "KEY 初始化设置 P2PU=0x08Timer1 中断检测 P2.3,低电平消抖完毕立刻推送最高优先级的 KEY_SOS_ACTIVE 事件。" },
'25': { mcu: "P2.4 / MISO_2", net: "MOTOR_PWM", mode: "推挽输出", hw: "连接振动马达驱动 MOS 晶体管控制栅。高电平开启震动;低电平关断马达。", sw: "由 `Motor_Start_Pattern` 发起动作Timer1 定时中断在后台扣减时间并切换 P2.4 电平实现异步非阻塞震动。" },
'27': { mcu: "P2.6 / SCLK_2", net: "LED_RGB", mode: "SPI_MOSI 硬件复用输出", hw: "连接上、下级联的双 WS2812B 灯珠的数据输入脚,传送高频 RGB 色彩流。", sw: "写数据时,临时屏蔽全局中断并将 P2.4 马达脚切换为高阻隔离,通过 SPI 高速推字节时序。" },
'28': { mcu: "P2.7 / SS_2", net: "SHUT", mode: "推挽输出", hw: " AMOLED 升压 PMIC (SGM3833) 芯片的使能使能控制脚。拉高开启屏幕正负电网工作;拉低彻底切断供电消除漏电。", sw: "PM 驱动模块控制。睡眠时执行 `SHUT = 0` 关闭屏幕电源;安全唤醒后执行 `SHUT = 1` 并强延时 50ms。" },
'29': { mcu: "P0.0 / ADC8", net: "RF_RX_DATA", mode: "输入中断 (INT0/INT2)", hw: "LR690L 433MHz 无线接收输出,检测到无线载波时输出脉冲沿,休眠时唤醒单片机。", sw: "休眠前配置为中断源;常态下 RF 解调驱动定时捕捉前导同步头宽度,计算基准时基 T 还原 24位 码。" },
'30': { mcu: "P0.1 / ADC9", net: "RF_TX", mode: "推挽输出", hw: "LR690L 发射偏置使能引脚。拉高后射频前端开启天线能量推送。", sw: "主动发射对码或 SOS 求救码时,由 RF 驱动在底层拉高 P0.1 开启发射。" },
'31': { mcu: "P0.2 / ADC10", net: "RF_RX", mode: "推挽输出", hw: "LR690L 接收使能控制引脚。高电平代表芯片处于监听天线高频信号状态。", sw: "常态待机和等待对码搜索下GPIO 默认拉高 P0.2 以监听外界载波。" },
'32': { mcu: "P0.3 / ADC11", net: "RF_TX_DAT", mode: "推挽输出", hw: "LR690L 无线调制数据输入,在此引脚输出特定的高低电平比值即可将数据射出。", sw: "主动发射时,利用 GPIO 高速反转 P0.3 产生同步前导宽及 EV1527 对应的 1/0 码脉冲。" }
};
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>