Files
stc32g128k/Docs/30_architecture/wristband_architecture.html

1523 lines
74 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 手环核心系统架构设计交互分析器</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: 2rem 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: 1rem 0;
border-radius: 0.75rem;
background: rgba(255, 255, 255, 0.01);
border: 1px dashed rgba(255, 255, 255, 0.03);
}
.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.app-layer .layer-title-tag { color: var(--neon-cyan); border-color: rgba(0, 242, 254, 0.2); }
.layer-row.drv-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.75rem 1.25rem;
width: 9rem;
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.app-node { border-left: 3px solid var(--neon-cyan); }
.module-card.drv-node { border-left: 3px solid var(--neon-purple); }
.module-card.hw-node { border-left: 3px solid var(--neon-gold); }
.module-title {
font-size: 0.85rem;
font-weight: 700;
margin-bottom: 0.2rem;
}
.module-tag {
font-size: 0.65rem;
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.8);
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手环系统核心架构交互分析器</h1>
<p>Architect-Level Interactive Architecture Analyzer</p>
</div>
</div>
<div class="tab-menu">
<button class="tab-btn active" onclick="switchTab('topology')">层级拓扑关系</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: 架构拓扑面板 -->
<div id="panel-topology" class="content-panel active-panel">
<div class="diagram-container">
<svg id="svg-connections" class="canvas-svg"></svg>
<!-- APP 逻辑层 -->
<div class="layer-row app-layer" id="layer-app-row">
<span class="layer-title-tag">应用逻辑层 (APP Layer) - 事件循环与状态流转</span>
<div class="module-card app-node" id="MainScheduler" onclick="selectModule('MainScheduler')">
<div class="module-title">MainScheduler</div>
<div class="module-tag">主循环调度</div>
</div>
<div class="module-card app-node" id="SysStateMachine" onclick="selectModule('SysStateMachine')">
<div class="module-title">SysStateMachine</div>
<div class="module-tag">核心业务状态机</div>
</div>
<div class="module-card app-node" id="SoftRTC" onclick="selectModule('SoftRTC')">
<div class="module-title">SoftRTC</div>
<div class="module-tag">软件计时器</div>
</div>
</div>
<!-- DRV 外设驱动层 -->
<div class="layer-row drv-layer" id="layer-drv-row">
<span class="layer-title-tag">外设驱动层 (DRV Layer) - 底层驱动与硬件隔离</span>
<div class="module-card drv-node" id="KEY" onclick="selectModule('KEY')">
<div class="module-title">KEY</div>
<div class="module-tag">消抖与事件队列</div>
</div>
<div class="module-card drv-node" id="RF" onclick="selectModule('RF')">
<div class="module-title">RF</div>
<div class="module-tag">LR690L 编解码</div>
</div>
<div class="module-card drv-node" id="PM" onclick="selectModule('PM')">
<div class="module-title">PM</div>
<div class="module-tag">极低功耗管理</div>
</div>
<div class="module-card drv-node" id="AMOLED" onclick="selectModule('AMOLED')">
<div class="module-title">AMOLED</div>
<div class="module-tag">Truly 屏幕渲染</div>
</div>
<div class="module-card drv-node" id="LED" onclick="selectModule('LED')">
<div class="module-title">LED</div>
<div class="module-tag">WS2812B 幻彩</div>
</div>
<div class="module-card drv-node" id="MOTOR" onclick="selectModule('MOTOR')">
<div class="module-title">MOTOR</div>
<div class="module-tag">马达异步震动</div>
</div>
</div>
<!-- HW 硬件抽象层 -->
<div class="layer-row hw-layer" id="layer-hw-row">
<span class="layer-title-tag">硬件抽象与时基层 (HW Layer) - 底层外设控制器</span>
<div class="module-card hw-node" id="Timer1" onclick="selectModule('Timer1')">
<div class="module-title">Timer1</div>
<div class="module-tag">1ms 中断时基</div>
</div>
<div class="module-card hw-node" id="GPIO" onclick="selectModule('GPIO')">
<div class="module-title">GPIO</div>
<div class="module-tag">数字输入输出</div>
</div>
<div class="module-card hw-node" id="ADC" onclick="selectModule('ADC')">
<div class="module-title">ADC</div>
<div class="module-tag">12位电压采集</div>
</div>
<div class="module-card hw-node" id="Flash" onclick="selectModule('Flash')">
<div class="module-title">Flash</div>
<div class="module-tag">IAP 扇区存取</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;">
点击左侧架构卡片,查看模块的输入/输出关系、底层引脚映射、驱动代码位置及架构原理解释。
</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">P0.0</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>
上部紧急按键触发 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>
已配对防区传感器入侵报警
</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>
无按键 10 秒超时自动休眠
</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">
[SYSTEM] 模拟器就绪,等待触发...
</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>
<!-- 引脚布局 (32引脚 + EP) -->
<!-- 左侧: 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">
点击芯片封装上的物理引脚号,可查询对应的 STC32G 引脚名、PCB 原理图网络标号、所连接的硬件模块及关联的代码控制位置。
</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">芯片引脚名称:</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. 核心架构设计思想:单向依赖与分层抽象</h2>
<p>
手环固件基于经典的 **三层单向依赖架构**APP层 -> DRV层 -> HW层进行设计
</p>
<ul>
<li><strong>硬件隔离</strong>:不允许应用层代码绕过驱动去改变硬件寄存器。所有的引脚操作都封装在 DRV 层内。这为手环的软硬件升级(如屏幕升级、传感器类型增加)提供了极佳的移植性。</li>
<li><strong>单向无环依赖</strong>:数据的向上汇报只能通过全局变量/事件队列异步进行,应用层向下发起硬件动作通过高级函数 API 进行,避免了组件之间的紧耦合与死锁。</li>
</ul>
</div>
<div class="report-section">
<h2>2. 高可靠性按键环形事件缓冲队列 (Ring Buffer)</h2>
<p>
为了避免用户在快速点击或同时按下多个按键时发生丢键KEY 驱动中建立了一个深度为 4 级的环形事件队列。
</p>
<div class="code-box">
#define KEY_BUF_SIZE 4
volatile KeyEvent key_event_buf[KEY_BUF_SIZE];
volatile u8 key_buf_head = 0;
volatile u8 key_buf_tail = 0;
bit Key_Push_Event(KeyEvent evt) {
u8 next = (key_buf_head + 1) % KEY_BUF_SIZE;
if (next != key_buf_tail) { // 队列未满
key_event_buf[key_buf_head] = evt;
key_buf_head = next;
return 1;
}
return 0; // 溢出丢弃
}</div>
<p>
Timer 1 中断(每 10ms 扫描消抖一次)把有效的短按、长按事件直接 `Push` 进入队列;主循环在空闲时通过 `Pop` 处理并重置队列,从而彻底消除了阻塞死等消抖(如 `while(key==0)`)的低效设计。
</p>
</div>
<div class="report-section">
<h2>3. 报警指示非阻塞异步时序设计</h2>
<p>
由于马达震动(例如 300ms和 LED 闪烁是长达数百毫秒的时序动作,如果采用软件阻塞延时(如 `delay_ms(300)`),主循环将完全卡死,无法处理 LR690L 433MHz 高频射频接收。
</p>
<p>
手环将马达和 LED 动作拆分为多个维持电平段,在 Timer1 的时钟中断中不断自减维持时间,当计数归零时自动切换引脚状态:
</p>
<div class="code-box">
// MOTOR 异步闪烁时序示例 (在 10ms 中断服务中调用)
void Motor_Pulse_Process(void) {
if (motor_timer > 0) {
motor_timer--;
if (motor_timer == 0) {
// 切换马达引脚电平,进入下一阶段
MOTOR_PIN = !MOTOR_PIN;
// 根据特定的震动波形重新装填 motor_timer
}
}
}</div>
</div>
<div class="report-section">
<h2>4. 安全唤醒重初始化时序</h2>
<p>
手环在低功耗休眠时,为了消除 Truly AMOLED 屏幕的暗漏电流,使用 `P2.7`SGM3833 PMIC 使能脚)切断了 OLED 屏幕的逻辑及负压电源供电。
</p>
<p>
在被外部中断(按键按下或 LR690L 接收沿)瞬间唤醒后,由于电源滤波电容充电需要时间,电网在最初的几十毫秒是不稳定的。因此,唤醒后**严禁立即进行写屏操作**,必须遵循严格的唤醒初始化序列:
</p>
<ul>
<li>1. 唤醒 CPU立即拉高 `SHUT = 1` 使能 SGM3833 启动。</li>
<li>2. **软件强行阻塞延时延时 50ms**,保证电压完全稳定并避开浪涌冲击电流。</li>
<li>3. 重新对 RM69310 屏幕控制器写入全套初始化指令配置(建立 SPI 像素写通道)。</li>
<li>4. 触发屏幕重绘,亮起时钟页。</li>
</ul>
</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);
}
}
// 模块细节定义 (数据驱动)
const modulesData = {
MainScheduler: {
title: "MainScheduler (主循环调度器)",
pin: "无物理引脚 (软件纯逻辑)",
level: "N/A",
hwsource: "CPU 核心控制器",
file: "App/main.c",
inputs: ["KEY (环形事件队列)", "RF (433MHz数据包)"],
outputs: ["SysStateMachine (事件分发)", "GPIO (定时喂看门狗)"],
theory: "主程序采用解耦的时间片轮询结构。主循环是非阻塞的,主要任务是周期性读取按键队列、射频解码状态,当有事件到达时调用状态机。其余时间喂狗防卡死。"
},
SysStateMachine: {
title: "SysStateMachine (业务状态机)",
pin: "无物理引脚 (软件状态变量)",
level: "N/A",
hwsource: "系统内存状态变量",
file: "App/main.c",
inputs: ["MainScheduler (分配出的KeyEvent/RFEvent)"],
outputs: ["AMOLED (触发重绘)", "LED (彩灯警示)", "MOTOR (马达震动)", "PM (睡眠判定)", "Flash (对码保存)", "RF (发射求救/对码)"],
theory: "驱动核心 10 个 SystemState 状态迁移,是系统业务大脑。根据当前状态和输入事件,决定外设输出动作,并管理 IAP Flash 存储读写请求。"
},
SoftRTC: {
title: "SoftRTC (软件时钟计时器)",
pin: "无物理引脚",
level: "N/A",
hwsource: "Timer1 时钟分频器",
file: "App/main.c",
inputs: ["Timer1 (秒分频中断)"],
outputs: ["AMOLED (待机大字时钟渲染)"],
theory: "基于 Timer 1 中断分频得到的高可靠时钟累加器,维护时、分、秒、年、月、日。常态待机下提供大字时钟及日期的文字渲染源。"
},
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: ["GPIO (引脚电平电平扫描)"],
outputs: ["MainScheduler (通过环形队列写入)"],
theory: "由 Timer1 (10ms) 定时触发消抖扫描。通过移位寄存器滤波3次结果一致才有效。支持双 SOS 键的并联电气逻辑。支持按键的长短按检测,并将 KeyEvent 压入环形队列防丢。"
},
RF: {
title: "RF (LR690L 433MHz 编解码)",
pin: "Pin 29 (P0.0), Pin 30 (P0.1), Pin 31 (P0.2), Pin 32 (P0.3)",
level: "P0.0中断输入 / 其余IO输出",
hwsource: "LR690L 射频前端芯片",
file: "Drivers/rf.c, Drivers/rf.h",
inputs: ["GPIO (INT2 沿中断捕获同步头)"],
outputs: ["MainScheduler (被动接收报警)", "GPIO (主动发射数据线)"],
theory: "自适应时间比值解调机制:首先利用计数过滤 >1500us 的同步低电平前导头,计算时钟基准 T=low_time/31。按 1:3 对应 0 码3:1 对应 1 码自适应解出 24 位 EV1527 载波。主动发射时,重复发射 5 遍防止射频碰撞。"
},
PM: {
title: "PM (极低功耗电源管理)",
pin: "Pin 28 (P2.7 / SHUT 控制脚)",
level: "推挽输出",
hwsource: "SGM3833 AMOLED PMIC 供电使能",
file: "App/main.c (Low_Power_Sleep)",
inputs: ["SysStateMachine (休眠判定)"],
outputs: ["GPIO (控制 SHUT=0 关闭电源 / PCON|=0x02 进入深度睡眠)"],
theory: "在连续 10 秒无操作后PM 先向屏幕发送关闭指令,随后将 P2.7 (SHUT) 拉低,彻底切断 Truly OLED 显示供电,避免负压漏电。最后开启 GPIO 口变化中断,将 MCU 挂起Power-Down 极低功耗模式)。"
},
AMOLED: {
title: "AMOLED (屏幕局部绘制驱动)",
pin: "Pin 9-14 (P5.4-P3.1 SPI控制总线)",
level: "推挽输出",
hwsource: "Truly 0.95寸 120x240 AMOLED 屏 (RM69310)",
file: "Drivers/lcd.c, Drivers/lcd.h",
inputs: ["SysStateMachine (重绘渲染请求)"],
outputs: ["GPIO (SPI 控制线像素刷写)"],
theory: "提供高效 SPI 屏刷指令。核心在于 X 坐标 +4 像素物理偏置修正(物理 GRAM 起始偏置。并实现了无障碍大字号32x64px时钟、大图标渲染以及水平居中排版公式。"
},
LED: {
title: "LED (WS2812B RGB 灯珠时序)",
pin: "Pin 27 (P2.6 / LED_RGB 控制脚)",
level: "SPI MOSI 硬件复用输出",
hwsource: "WS2812B 级联双彩灯珠",
file: "App/main.c (LED_Flash_Process)",
inputs: ["Timer1 (闪烁定时)", "SysStateMachine (设置颜色请求)"],
outputs: ["GPIO (SPI_MOSI 硬件引脚数据流)"],
theory: "利用硬件 SPI 控制 WS2812B 单线纳秒级数据时序。为了防止马达动作引入高频地线杂讯影响灯时序,每次写灯前将临时执行关全局中断 (EA=0) 并切换马达脚为高阻输入以隔离干扰。"
},
MOTOR: {
title: "MOTOR (马达非阻塞震动控制)",
pin: "Pin 25 (P2.4 / MOTOR_PWM 脚)",
level: "推挽输出",
hwsource: "振动马达驱动电路",
file: "App/main.c (Motor_Pulse_Process)",
inputs: ["Timer1 (时序递减分频)", "SysStateMachine (加载震动类型)"],
outputs: ["GPIO (P2.4 高低电平输出)"],
theory: "完全非阻塞异步设计。报警的长振或多脉冲震动,全部分解为在 1ms 中断里递减的毫秒计数值。APP 层在加载震动模式后立即退出,绝不卡死 CPU实现高效并发运行。"
},
Timer1: {
title: "Timer1 (1ms 定时中断时基)",
pin: "芯片内部硬件定时器 1",
level: "N/A",
hwsource: "24MHz 主时钟分频",
file: "App/main.c (Timer1_Isr)",
inputs: ["GPIO (系统主时钟时源)"],
outputs: ["KEY (10ms触发)", "MOTOR (脉冲递减)", "LED (闪烁更新)", "SoftRTC (秒累加)"],
theory: "系统最底层的心跳时基。通过配置 STC 内部寄存器,每 1ms 产生一次定时器 3 中断。是马达异步波形、彩灯闪烁、按键移位消抖和软件 RTC 时钟生存的绝对基础。"
},
GPIO: {
title: "GPIO (数字输入输出端口)",
pin: "QFN-32 引脚 1-32",
level: "推挽输出 / 高阻输入等可配置模式",
hwsource: "单片机物理引脚控制寄存器",
file: "Drivers/stc32g.h (硬件寄存器映射)",
inputs: ["物理输入电平沿变化 (按键/射频沿)"],
outputs: ["外设物理驱动信号"],
theory: "硬件最底层通道。STC32G 每个引脚都支持四种模式(准双向、推挽输出、高阻输入、开漏)。配置 GPIO 能够控制电路通断、输出通信数据流,并充当停机模式下的中断唤醒源。"
},
ADC: {
title: "ADC (12位电量电压采样)",
pin: "Pin 22 (P2.1 / ADC9通道)",
level: "模拟输入",
hwsource: "手环电池分压监测线",
file: "App/main.c (Read_Battery_Percent)",
inputs: ["GPIO (外部电压模拟输入)"],
outputs: ["SysStateMachine (返回0-100%电量数据)"],
theory: "利用单片机内部 12 位高速逐次逼近型 ADC。对电池采样引脚进行多次读取并取均值滤波。匹配 1.03V 到 1.31V 物理输入线性映射为电池 0%~100% 格式百分比输出。"
},
Flash: {
title: "Flash (IAP 内部数据保存)",
pin: "芯片内置 Flash 空间 (第 254 扇区)",
level: "N/A",
hwsource: "0xFE0000 扇区物理闪存",
file: "App/main.c (IAP_Write / Save_Database)",
inputs: ["SysStateMachine (保存对码防区请求)"],
outputs: ["SysStateMachine (上电载入 16 组传感器配置)"],
theory: "利用 STC 内部 IAP (In-Application Programming) 功能在程序运行中擦写 Flash。将已配对的 24 位传感器地址、传感器类型、防区类别及法语前缀序号持久化,掉电不丢失。"
}
};
// 选中某个模块卡片
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 连接配置关系
const connections = [
{ from: 'Timer1', to: 'KEY', label: '10ms', color: 'cyan' },
{ from: 'Timer1', to: 'MOTOR', label: 'PWM', color: 'cyan' },
{ from: 'Timer1', to: 'LED', label: '闪烁', color: 'cyan' },
{ from: 'Timer1', to: 'SoftRTC', label: '秒递增', color: 'cyan' },
{ from: 'GPIO', to: 'KEY', label: '引脚', color: 'purple' },
{ from: 'GPIO', to: 'RF', label: '沿', color: 'purple' },
{ from: 'KEY', to: 'MainScheduler', label: '读', color: 'cyan' },
{ from: 'RF', to: 'MainScheduler', label: '读', color: 'cyan' },
{ from: 'MainScheduler', to: 'SysStateMachine', label: '流', color: 'cyan' },
{ from: 'SysStateMachine', to: 'AMOLED', label: '控', color: 'purple' },
{ from: 'SysStateMachine', to: 'LED', label: '控', color: 'purple' },
{ from: 'SysStateMachine', to: 'MOTOR', label: '控', color: 'purple' },
{ from: 'SysStateMachine', to: 'PM', label: '控', color: 'purple' },
{ from: 'SysStateMachine', to: 'Flash', label: '读写', color: 'purple' },
{ from: 'SysStateMachine', to: 'ADC', label: '读', color: 'purple' },
{ from: 'AMOLED', to: 'GPIO', label: 'SPI', color: 'gold' },
{ from: 'LED', to: 'GPIO', label: 'P2.6', color: 'gold' },
{ from: 'MOTOR', to: 'GPIO', label: 'P2.4', color: 'gold' },
{ from: 'PM', to: 'GPIO', label: '断电', color: 'gold' }
];
// 动态绘制连接线 (SVG)
function drawConnections() {
const svg = document.getElementById('svg-connections');
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.4 : -dy * 0.4);
const controlY2 = y2 - (y2 > y1 ? dy * 0.4 : -dy * 0.4);
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 === '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('Timer1') || p.id.includes('KEY') || p.id.includes('RF')) {
p.style.stroke = 'rgba(0, 242, 254, 0.15)';
} else {
p.style.stroke = 'rgba(139, 92, 246, 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 / ADC0 / RXD2", net: "TXD", mode: "推挽输出", hw: "连到串口下载口及外部调试串口,用于固件下载及仿真打印输出。", sw: "通过 UART1 初始化函数及 printf 函数操作 SBUF 寄存器。" },
'2': { mcu: "P1.1 / ADC1 / TXD2", net: "RXD", mode: "高阻输入", hw: "外部调试串口输入接收,可在免焊测试阶段注入仿真串口指令。", sw: "UART1 串口接收中断 ISR 实时读入 SBUF 并解析状态指令。" },
'4': { mcu: "P1.5 / SCL / S2SCLK / ADC5", net: "+3.3V", mode: "电源 / 逻辑供电", hw: "Truly AMOLED 显示屏的 3.3V 逻辑电源引脚,以及外部上拉供电路径。", sw: "无操作,硬件电源层直接接入。" },
'5': { mcu: "P1.6 / XTALO / PWM4P", net: "+3.3V", mode: "电源 / 逻辑供电", hw: "Truly AMOLED 显示屏的 3.3V 逻辑电源引脚并联引线。", sw: "无操作,硬件电源层直接接入。" },
'6': { mcu: "P1.7 / XTALI / PWM4N", net: "TP_RST", mode: "推挽输出", hw: "触摸屏 TP 重置引脚(在当前无触摸功能的手环上作为悬空扩展引脚)。", sw: "GPIO 初始化配置为高电平输出悬空。" },
'8': { mcu: "UCAP", net: "TP_INT", mode: "输入中断", hw: "触摸屏 TP 中断脚(在当前版本中悬空未接入电平)。", sw: "N/A" },
'9': { mcu: "P5.4 / RST / SS", net: "SPI_SDO", mode: "推挽输出", hw: "Truly AMOLED 屏幕的 MISO 引脚,用于读回屏幕控制器寄存器数据(在单向刷屏中常处于高阻悬空)。", sw: "初始化为复用 SPI 功能,通过 SPI 数据寄存器 SPDAT 进行串行写操作。" },
'10': { mcu: "Vcc / AVcc", net: "SPI_CS", mode: "推挽输出", hw: "Truly AMOLED 屏幕的片选信号引脚,低电平使能对屏幕控制器的 SPI 写操作。", sw: "写屏幕像素或控制命令时,通过 GPIO 驱动该脚拉低;写完后拉高。" },
'11': { mcu: "VREF+", net: "SPI_CLK", mode: "推挽输出", hw: "Truly AMOLED 屏幕的 SPI 时钟控制引脚 (SCL)。", sw: "SPI 控制器内部自动产生移位时钟脉冲信号。" },
'12': { mcu: "GND / AGND / VREF-", net: "SPI_SDI", mode: "推挽输出", hw: "Truly AMOLED 屏幕的 SPI 串行像素数据输入引脚 (SDA/MOSI)。", sw: "SPI 发送寄存器 SPDAT 自动将字节转换为串行比特流输出至此引脚。" },
'13': { mcu: "P3.0 / RxD / INT4", net: "SPI_DCX", mode: "推挽输出", hw: "Truly AMOLED 屏幕的 SPI 数据/命令选择引脚。高电平时发送的是像素色彩数据,低电平是控制命令。", sw: "在 lcd.c 的写数据/写命令底层函数中直接通过 GPIO 拉高或拉低。" },
'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 变化中断瞬间唤醒系统。", sw: "在 KEY 初始化中设置 P3PU=0x40Timer1 中断中定时读取 P3.6 端口电平进行移位滤波。" },
'20': { mcu: "P3.7 / INT3", net: "KEY_CONFIRM", mode: "高阻输入 + 使能内部上拉", hw: "侧边按键 ■(确认键/长按菜单键),按下为低电平;在休眠下支持中断唤醒。", sw: "KEY 初始化设置 P3PU=0x80中断中定时扫描 P3.7 电平以检测 3s 长按动作。" },
'21': { mcu: "P2.0 / A8 / PWM5", net: "KEY_UP", mode: "高阻输入 + 使能内部上拉", hw: "侧边按键 ▲(上移键),按下为低电平;在休眠下支持中断唤醒系统。", sw: "KEY 初始化设置 P2PU=0x01中断中扫描 P2.0 累加计数消抖。" },
'22': { mcu: "P2.1 / A9 / PWM6", net: "BAT_ADC", mode: "模拟输入", hw: "连接电池分压电阻网络,输入 0 到 1.31V 之间的模拟电压作为电量检测源。", sw: "开启 ADC 模块,配置通道 9 转换。计算多次采集的平均值并线性转换为电量百分比。" },
'23': { mcu: "P2.2 / A10 / SS_2", net: "DET", mode: "高阻输入", hw: "USB 充电检测端口。当手环插入充电底座时,该脚被拉为高电平;未充电时为低电平。", sw: "主循环待机页渲染中,实时读取 P2.2 的引脚电平,更新百分比后面的充电加号显示。" },
'24': { mcu: "P2.3 / A11 / MOSI_2", net: "KEY_SOS", mode: "高阻输入 + 使能内部上拉", hw: "上部并联物理按键 SOS1 与 SOS2 的硬件接入脚。两键物理并联,按下任何一个均将该脚拉低触发求救。", sw: "KEY 初始化设置 P2PU=0x08Timer1 中断扫描 P2.3 低电平,一旦满足消抖立刻触发主动 SOS。" },
'25': { mcu: "P2.4 / MISO_2", net: "MOTOR_PWM", mode: "推挽输出", hw: "连接振动马达驱动 MOS 晶体管栅极。高电平导通,马达震动;低电平关断,马达静止。", sw: "Motor 控制模块中通过 GPIO 写入 P2.4 电平。结合 Timer1 中断时间片实现异步长短脉冲震动波形。" },
'27': { mcu: "P2.6 / SCLK_2", net: "LED_RGB", mode: "SPI MOSI 硬件复用输出", hw: "连接手环上灯及下灯级联 WS2812B 灯珠的数据输入 (DIN) 脚,发送纳秒时序数据流。", sw: "复用单片机 SPI 的 MOSI 引脚。通过 SPDAT 高速发字节,将二进制 0/1 扩展为纳秒占空比波形。" },
'28': { mcu: "P2.7 / SS_2", net: "SHUT", mode: "推挽输出", hw: "OLED 升压负压 PMIC SGM3833 芯片的使能控制引脚。高电平开启供电;低电平彻底关断电路,消灭暗漏电流。", sw: "在休眠函数中直接拉低 P2.7=0在安全唤醒中拉高 P2.7=1 并强阻塞等待 50ms。" },
'29': { mcu: "P0.0 / ADC8 / AD0", net: "RF_RX_DATA", mode: "输入中断 (INT0/INT2)", hw: "LR690L 433MHz 无线接收芯片的数据信号输出线,接收到 RF 载波信号时输出脉冲沿,唤醒挂起的 CPU。", sw: "休眠前配置该脚的中断沿使能。唤醒后,主循环软件轮询算法计算高低电平比值并还原 24 位 EV1527 信号。" },
'30': { mcu: "P0.1 / ADC9 / AD1", net: "RF_TX", mode: "推挽输出", hw: "LR690L 射频前端芯片的发射电路电源/逻辑使能控制管脚。拉高后射频芯片开始向天线推送能量信号。", sw: "在调用 EV1527_Transmit 主动发送对码或求救载波时,由 GPIO 接口拉高该引脚开启发射。" },
'31': { mcu: "P0.2 / ADC10 / AD2", net: "RF_RX", mode: "推挽输出", hw: "LR690L 射频前端芯片的接收电路电源使能控制管脚。拉高芯片处于被动监听电磁波状态。", sw: "系统在进入 STATE_CLOCK 和等待对码状态下GPIO 默认拉高该引脚保持持续射频监听状态。" },
'32': { mcu: "P0.3 / ADC11 / AD3", net: "RF_TX_DAT", mode: "推挽输出", hw: "LR690L 射频前端芯片的发射调制数据输入引脚。按特定时间宽度输出脉冲以发射对应二进制码包。", sw: "主动发射时,利用 GPIO 高速翻转 P0.3 引脚的电平发送同步码头、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>[SYSTEM] ${text}`;
consoleBox.scrollTop = consoleBox.scrollHeight;
}
function clearConsole() {
document.getElementById('sim-console').innerHTML = '[SYSTEM] 仿真开始运行...';
}
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-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("触发:按下上部 SOS 紧急求救按键。");
simInterval = setInterval(() => {
step++;
if (step === 1) {
showStep("1. 按下 SOS 键 -> 硬件检测 P2.3 引脚电平被拉低为 0V。");
appendConsole("硬件检测到 Pin 24 (P2.3) 低电平。");
} else if (step === 2) {
showStep("2. Timer1 10ms 中断检测到低电平,消抖完毕,压入环形队列 'KEY_SOS_ACTIVE'。");
appendConsole("Timer1 ISR 移位消抖完成。入队 KEY_SOS_ACTIVE。");
} else if (step === 3) {
showStep("3. 主循环 Pop 出按键事件,分发至业务状态机。");
appendConsole("主循环调度,状态机跳转 STATE_SOS_EMITTED并设置重绘标志 Redraw=1。");
} else if (step === 4) {
showStep("4. 状态机指示 AMOLED 重绘大图标;同时调度 LED 和 MOTOR 开始报警。");
document.getElementById('sim-screen').style.background = '#ff007f';
document.getElementById('screen-text').innerText = "SOS EMIS\nEMERGENCY";
document.getElementById('screen-text').style.color = '#fff';
appendConsole("状态机调用WS2812_WriteRGB、Motor_Start_Pattern。");
} else if (step === 5) {
showStep("5. 射频层通过 P0.3 串行输出 EV1527 数据,发射 0x08 SOS 求救码,红灯高速交替闪烁,马达长震。");
// 闪烁彩灯
let flashCount = 0;
const flashInterval = setInterval(() => {
if (flashCount > 10) {
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("RF 驱动使能 P0.1(RF_TX),并通过 P0.3 (RF_TX_DAT) 发送 Factory ID + 0x08。");
} else if (step === 6) {
showStep("6. 紧急呼救包Factory ID发射完毕。进入报警状态挂起。按任意侧边键可清除报警。");
appendConsole("发射完毕。系统处于 STATE_SOS_EMITTED 循环挂起监听。");
clearInterval(simInterval);
document.querySelectorAll('.sim-btn').forEach(b => b.classList.remove('running'));
}
}, 1500);
} else if (type === 'intrusion') {
appendConsole("触发LR690L 被动接收到门磁传感器 (PORTE 01) 发送的 433MHz 入侵信号。");
simInterval = setInterval(() => {
step++;
if (step === 1) {
showStep("1. LR690L 射频前端引脚 P0.0 检测到沿变化,产生 INT2 沿中断唤醒系统。");
appendConsole("INT2 中断触发唤醒。CPU 开始监听接收脚脉冲。");
} else if (step === 2) {
showStep("2. 射频驱动开始自适应解密前导头低电平宽,反算出时基 T 并还原 24-bit 码字。");
appendConsole("解出 EV1527 地址: 0xA3F21B, 数据码: 0x01 (门磁动作)。");
} else if (step === 3) {
showStep("3. 主循环将地址码与 Flash 数据库进行匹配,确认为已配对的 'PORTE 01' 防区。");
appendConsole("检索 IAP Flash 第 254 扇区成功。匹配 Slot 0: PORTE 01防区类别为 0 (24小时布防防区)。");
} else if (step === 4) {
showStep("4. 状态机切入 STATE_ALARMING 警报状态LED 绿色指示闪烁,马达开启短震 (300ms)。");
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("状态机调用LED闪烁控制字启动 MOTOR P2.4 短振。");
} else if (step === 5) {
showStep("5. 屏幕红边框亮起并显示报警防区名称,声光震动同步提示。按侧边键可解除。");
appendConsole("屏幕渲染完毕。系统进入 STATE_ALARMING 被动警报等待处理。");
clearInterval(simInterval);
document.querySelectorAll('.sim-btn').forEach(b => b.classList.remove('running'));
}
}, 1500);
} else if (type === 'sleep') {
appendConsole("触发:待机常态,无任何按键或无线射频信号到达。");
simInterval = setInterval(() => {
step++;
if (step === 1) {
showStep("1. inactivity_timer 计时器开自增,计满 1000 (10秒无任何输入事件)。");
appendConsole("inactivity_timer 累加至 1000。触发低功耗判定。");
} else if (step === 2) {
showStep("2. 主循环调度进入休眠,状态机切入 STATE_SLEEP 状态,关闭 AMOLED 屏幕写入指令。");
appendConsole("状态机进入 STATE_SLEEP。AMOLED 发送 Register 0x28 (Display OFF) 关屏指令。");
} else if (step === 3) {
showStep("3. 电源 PM 驱动执行断电:将 P2.7 (SHUT) 拉为低电平 (0V),完全切断 SGM3833 供电消除漏电。");
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("PM拉低 Pin 28 (SHUT=0)。Truly 屏幕 PMIC 完全断电。");
} else if (step === 4) {
showStep("4. 配置 GPIO 的 Pin 19-24 为电平沿唤醒中断,配置 Pin 29 (RF) 为沿唤醒。");
appendConsole("使能 P3.6, P3.7, P2.0, P2.3 端口电平变化中断及 INT2。");
} else if (step === 5) {
showStep("5. 写入 PCON |= 0x02 使 STC32G 进入 Power-Down 极低功耗挂起模式,全板电流微安级。");
appendConsole("CPU 执行 PCON |= 0x02。主时钟停止全系统进入停机深度休眠。");
clearInterval(simInterval);
document.querySelectorAll('.sim-btn').forEach(b => b.classList.remove('running'));
}
}, 1500);
}
}
</script>
</body>
</html>