修复了网络参数的保存逻辑
This commit is contained in:
@@ -216,6 +216,8 @@ flowchart TD
|
||||
- **文本**:静态行 `text_static=1`(Flash 字符串不拷贝);动态行与页脚页码用栈缓冲
|
||||
(`lv_draw_label` 在同一刷新周期内**同步**光栅化,指针仅需调用期间有效)
|
||||
- **页脚**:`lv_snprintf` 现算(MSVC 对 `snprintf` 中文格式串误报 C4819,故用 LVGL 自带 `lv_snprintf`)
|
||||
|
||||
> ⚠️ **`lv_draw_label` 折行陷阱**:`lv_draw_label` 的换行宽度取 **`coords` 宽度**(未置 `LV_TEXT_FLAG_EXPAND` 时 `w = lv_area_get_width(coords)`)。在**窄区域**(光标盒 / 小方框)里传入**长于该区域的文本**时,LVGL 会按该宽度**折行并逐行向下绘制**,溢出到下方其它行与空白区(表现为"下方弹出一串字符")。规则:窄区域只传"恰好该区域宽度的文本"(如网口设置光标段只传 3 个字符),长文本请在整行宽条带内绘制。
|
||||
- 容器样式为**常量样式**(`LV_STYLE_CONST_INIT`,零 LVGL 池分配),见 `UI_设计架构.md` §5.6
|
||||
|
||||
## 四、页面描述符(content_page_def_t)
|
||||
@@ -473,3 +475,5 @@ extern const content_page_def_t ui_page_device_info_desc; /* 描述符(Flash
|
||||
| 2026-09-09 | 密码页落地(§4.1):框架描述符新增 title_y/hide_deco/hide_footer/key_intercept + 新 API `ui_content_page_invalidate`;新模块 `ui_page_password`(3 数字框反显光标 + arm API,draw_cb+key_cb);菜单受保护叶子路由(arm→开密码页,确认一致后 `menu_password_ok` **先关页再续做进入动作**);字体 97→102 汉字(198 字形);双端注册;回归扩至 7 页 + 往返水位,五门禁全绿 |
|
||||
| 2026-09-09 | 权限模型(替代"每页专属密码",§4.1):表行尾注记 ` #P`(需密码,无类别);`ui_pwd_def.h` 两个编译期密码(查看 000 / 修改 123)+ 新 `ui_perm_t` 权限枚举——密码页输查看密码授只读权限、输修改密码授修改权限(只有修改权限进入后才能改参数);生成器同步改造并重新生成(pwd 列 = 0/1 布尔,`ui_menu_tab_has_pwd`);框架 `ui_content_page_open(id, perm)` 携授予权限 + 描述符 `edit_capable` + 新 API `ui_content_page_is_editable`(页面钩子据以判只读/可编辑);路由密码检查提到内容页判断之前(修复带内容页需密码叶子绕过密码的路由缺口);无密码叶子直接进、默认只读。§4.1 功能规则/待确认表/落地要点同步。验证:五门禁全绿(菜单 8 态 + 7 页像素一致、池 4784/4864 B 不变、Keil 0 错 0 警、代码规范 PASS) |
|
||||
| 2026-09-10 | 网口设置内容页:新增 `Drivers/BSP/NETCFG/`(Flash 持久化 magic 'NCFG'+CRC-16/MODBUS,工厂默认 192.168.1.100/24、gw .1、MAC 全零=芯片内置)+ 页面插件 `ui_page_net_cfg`(5 行动态值 + 可编辑:↑↓调字节 0..255 环绕 / ←→移光标 / ENTER 确认,保存行 ENTER → `net_cfg_save` 下次上电生效);框架新增描述符钩子 `init_cb`(开页重置运行态)+ API `ui_content_page_selected_row`;`net_init` 增 `mac` 参数、netTask 改为等 `SYS_EVT_STORAGE_READY` → `net_cfg_load` → `net_init(ip,mask,gw,mac)` → 回写实际 MAC 影子;模拟器 main 调 `net_cfg_load` 使参考图显示工厂默认值;字体修复:lv_font_conv 丢弃低码位符号(U+FF0C 全角逗号 < 0x4E2D 范围起点 → 页脚逗号长期空白)改由 `merge_extra_symbols()` 补回 + 原本缺字(存/密/户/本/用/钥)→ 111 字 / 208 字形;回归扩至 11 页(网口设置 4 态:可编辑/只读/编辑光标反显/保存 OK),五门禁全绿 |
|
||||
| 2026-09-10 | 修复网口设置页编辑态渲染 bug:光标段原传整串 `value + start`,而 `lv_draw_label` 按 `coords`(光标盒,仅段宽)折行 → 逐行向盒下方绘制、溢出到其它行与空白区("下方弹出一串字符")。改为只传**段文本**(IP/掩码/网关 3 字符、MAC 2 字符);§3.5 补"`lv_draw_label` 折行陷阱"说明 |
|
||||
| 2026-09-10 | 网口设置页 MAC 值改常识格式 **`A4:94:B6:12:34:56`**(冒号分隔,17 字符 = 102px);布局调整 名 x=6→4、值 x=60→54(MAC ink 止于 155,仍在白带 2..157 内);编辑光标段偏移 MAC 2i→3i |
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,89 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1400" height="802" viewBox="0 0 1400 802" font-family="Microsoft YaHei, Segoe UI, PingFang SC, sans-serif">
|
||||
<rect x="0" y="0" width="1400" height="802" fill="#ffffff"/>
|
||||
<text x="40" y="42" font-size="20" font-weight="700" fill="#0f172a">就绪屏障(Readiness Barrier)· 启动依赖链</text>
|
||||
<text x="40" y="68" font-size="13" fill="#64748b">CMSIS-RTOS2 Event Flags · 生产者置位声明「我好了」,消费者阻塞等待</text>
|
||||
<rect x="40" y="96" width="16" height="16" rx="4" fill="#ffffff" stroke="#2563eb" stroke-width="2"/>
|
||||
<text x="64" y="108" font-size="12" fill="#475569">生产者(set)</text>
|
||||
<rect x="178" y="96" width="16" height="16" rx="4" fill="#ffffff" stroke="#7c3aed" stroke-width="2"/>
|
||||
<text x="202" y="108" font-size="12" fill="#475569">消费者(wait)</text>
|
||||
<rect x="324" y="96" width="16" height="16" rx="4" fill="#ffffff" stroke="#16a34a" stroke-width="2"/>
|
||||
<text x="348" y="108" font-size="12" fill="#475569">事件位(Event Flag)</text>
|
||||
<circle cx="520" cy="104" r="6" fill="#0d9488"/>
|
||||
<text x="536" y="108" font-size="12" fill="#475569">All 汇合(等全部位)</text>
|
||||
<line x1="694" y1="104" x2="710" y2="104" stroke="#94a3b8" stroke-width="2.4" stroke-dasharray="7 5"/>
|
||||
<text x="718" y="108" font-size="12" fill="#475569">预留 / 未接入</text>
|
||||
<rect x="400" y="166" width="420" height="462" rx="14" fill="#f8fafc" stroke="#64748b" stroke-opacity="0.35"/>
|
||||
<text x="416" y="192" font-size="14" font-weight="700" fill="#334155">sys_evtHandle(Event Flags 事件组)</text>
|
||||
<rect x="414" y="200" width="392" height="84" rx="10" fill="#f0fdf4" stroke="#16a34a" stroke-opacity="0.6"/>
|
||||
<rect x="428" y="227" width="30" height="30" rx="7" fill="#16a34a"/>
|
||||
<text x="443" y="247" font-size="14" font-weight="700" fill="#ffffff" text-anchor="middle">1</text>
|
||||
<text x="470" y="236" font-size="14" font-weight="700" fill="#16a34a">bit1 · STORAGE_READY</text>
|
||||
<text x="470" y="258" font-size="12" fill="#64748b">defaultTask 置位 · netTask / ftpTask 等待</text>
|
||||
<rect x="414" y="310" width="392" height="84" rx="10" fill="#f0fdfa" stroke="#0d9488" stroke-opacity="0.6"/>
|
||||
<rect x="428" y="337" width="30" height="30" rx="7" fill="#0d9488"/>
|
||||
<text x="443" y="357" font-size="14" font-weight="700" fill="#ffffff" text-anchor="middle">0</text>
|
||||
<text x="470" y="346" font-size="14" font-weight="700" fill="#0d9488">bit0 · NET_READY</text>
|
||||
<text x="470" y="368" font-size="12" fill="#64748b">netTask 置位 · 4 个任务等待</text>
|
||||
<rect x="414" y="420" width="392" height="84" rx="10" fill="#fffbeb" stroke="#d97706" stroke-opacity="0.6"/>
|
||||
<rect x="428" y="447" width="30" height="30" rx="7" fill="#d97706"/>
|
||||
<text x="443" y="467" font-size="14" font-weight="700" fill="#ffffff" text-anchor="middle">2</text>
|
||||
<text x="470" y="456" font-size="14" font-weight="700" fill="#d97706">bit2 · FTP_READY</text>
|
||||
<text x="470" y="478" font-size="12" fill="#64748b">ftpTask 置位 · 预留消费者</text>
|
||||
<rect x="414" y="530" width="392" height="84" rx="10" fill="#f8fafc" stroke="#94a3b8" stroke-opacity="0.6"/>
|
||||
<rect x="428" y="557" width="30" height="30" rx="7" fill="#94a3b8"/>
|
||||
<text x="443" y="577" font-size="14" font-weight="700" fill="#ffffff" text-anchor="middle">3</text>
|
||||
<text x="470" y="566" font-size="14" font-weight="700" fill="#94a3b8">bit3 · TIMESYNC_READY</text>
|
||||
<text x="470" y="588" font-size="12" fill="#64748b">预留(尚未接入调度)</text>
|
||||
<rect x="40" y="200" width="290" height="84" rx="10" fill="#eff6ff" stroke="#2563eb" stroke-opacity="0.5"/>
|
||||
<text x="56" y="234" font-size="15" font-weight="700" fill="#0f172a">defaultTask</text>
|
||||
<text x="56" y="258" font-size="12" fill="#64748b">FatFS 挂载(f_mount / f_mkfs)</text>
|
||||
<polyline points="330.0,242.0 400.0,242.0" fill="none" stroke="#16a34a" stroke-width="2.2" stroke-linejoin="round"/>
|
||||
<path d="M 400.0 242.0 L 389.0 247.5 L 389.0 236.5 z" fill="#16a34a"/>
|
||||
<text x="365" y="234.0" font-size="11.5" fill="#16a34a" text-anchor="middle">set bit1</text>
|
||||
<rect x="40" y="310" width="290" height="84" rx="10" fill="#eff6ff" stroke="#2563eb" stroke-opacity="0.5"/>
|
||||
<text x="56" y="344" font-size="15" font-weight="700" fill="#0f172a">netTask</text>
|
||||
<text x="56" y="368" font-size="12" fill="#64748b">net_stack_bring_up()</text>
|
||||
<polyline points="330.0,352.0 400.0,352.0" fill="none" stroke="#0d9488" stroke-width="2.2" stroke-linejoin="round"/>
|
||||
<path d="M 400.0 352.0 L 389.0 357.5 L 389.0 346.5 z" fill="#0d9488"/>
|
||||
<text x="365" y="344.0" font-size="11.5" fill="#0d9488" text-anchor="middle">set bit0</text>
|
||||
<rect x="40" y="420" width="290" height="84" rx="10" fill="#eff6ff" stroke="#2563eb" stroke-opacity="0.5"/>
|
||||
<text x="56" y="454" font-size="15" font-weight="700" fill="#0f172a">ftpTask</text>
|
||||
<text x="56" y="478" font-size="12" fill="#64748b">lftpd_start("/", 21)</text>
|
||||
<polyline points="330.0,462.0 400.0,462.0" fill="none" stroke="#d97706" stroke-width="2.2" stroke-linejoin="round"/>
|
||||
<path d="M 400.0 462.0 L 389.0 467.5 L 389.0 456.5 z" fill="#d97706"/>
|
||||
<text x="365" y="454.0" font-size="11.5" fill="#d97706" text-anchor="middle">set bit2</text>
|
||||
<rect x="910" y="200" width="450" height="84" rx="10" fill="#f5f3ff" stroke="#7c3aed" stroke-opacity="0.5"/>
|
||||
<text x="926" y="234" font-size="15" font-weight="700" fill="#0f172a">netTask</text>
|
||||
<text x="926" y="258" font-size="12" fill="#64748b">wait bit1 · ≤10s</text>
|
||||
<rect x="910" y="310" width="450" height="84" rx="10" fill="#f5f3ff" stroke="#7c3aed" stroke-opacity="0.5"/>
|
||||
<text x="926" y="344" font-size="15" font-weight="700" fill="#0f172a">ftpTask</text>
|
||||
<text x="926" y="368" font-size="12" fill="#64748b">wait bit0 + bit1 · All</text>
|
||||
<rect x="910" y="420" width="450" height="84" rx="10" fill="#f5f3ff" stroke="#7c3aed" stroke-opacity="0.5"/>
|
||||
<text x="926" y="454" font-size="15" font-weight="700" fill="#0f172a">uiTask</text>
|
||||
<text x="926" y="478" font-size="12" fill="#64748b">wait bit0 · Forever</text>
|
||||
<rect x="910" y="530" width="450" height="84" rx="10" fill="#f5f3ff" stroke="#7c3aed" stroke-opacity="0.5"/>
|
||||
<text x="926" y="564" font-size="15" font-weight="700" fill="#0f172a">timeSyncTask</text>
|
||||
<text x="926" y="588" font-size="12" fill="#64748b">wait bit0 · Forever</text>
|
||||
<rect x="910" y="640" width="450" height="84" rx="10" fill="#f5f3ff" stroke="#7c3aed" stroke-opacity="0.5"/>
|
||||
<text x="926" y="674" font-size="15" font-weight="700" fill="#0f172a">testTask</text>
|
||||
<text x="926" y="698" font-size="12" fill="#64748b">wait bit0 · NET 模式</text>
|
||||
<polyline points="820.0,242.0 910.0,242.0" fill="none" stroke="#16a34a" stroke-width="2.2" stroke-linejoin="round"/>
|
||||
<path d="M 910.0 242.0 L 899.0 247.5 L 899.0 236.5 z" fill="#16a34a"/>
|
||||
<text x="865" y="234.0" font-size="11.5" fill="#16a34a" text-anchor="middle">wait</text>
|
||||
<polyline points="832.0,242.0 832.0,352.0" fill="none" stroke="#16a34a" stroke-width="2.2" stroke-linejoin="round"/>
|
||||
<circle cx="832" cy="352.0" r="5.5" fill="#0d9488"/>
|
||||
<text x="841" y="344.0" font-size="11.5" font-weight="700" fill="#0d9488">All</text>
|
||||
<polyline points="820.0,352.0 862.0,352.0" fill="none" stroke="#0d9488" stroke-width="2.2" stroke-linejoin="round"/>
|
||||
<path d="M 862.0 352.0 L 851.0 357.5 L 851.0 346.5 z" fill="#0d9488"/>
|
||||
<polyline points="862.0,352.0 862.0,682.0" fill="none" stroke="#0d9488" stroke-width="2.2" stroke-linejoin="round"/>
|
||||
<polyline points="862.0,352.0 910.0,352.0" fill="none" stroke="#0d9488" stroke-width="2.2" stroke-linejoin="round"/>
|
||||
<path d="M 910.0 352.0 L 899.0 357.5 L 899.0 346.5 z" fill="#0d9488"/>
|
||||
<polyline points="862.0,462.0 910.0,462.0" fill="none" stroke="#0d9488" stroke-width="2.2" stroke-linejoin="round"/>
|
||||
<path d="M 910.0 462.0 L 899.0 467.5 L 899.0 456.5 z" fill="#0d9488"/>
|
||||
<polyline points="862.0,572.0 910.0,572.0" fill="none" stroke="#0d9488" stroke-width="2.2" stroke-linejoin="round"/>
|
||||
<path d="M 910.0 572.0 L 899.0 577.5 L 899.0 566.5 z" fill="#0d9488"/>
|
||||
<polyline points="862.0,682.0 910.0,682.0" fill="none" stroke="#0d9488" stroke-width="2.2" stroke-linejoin="round"/>
|
||||
<path d="M 910.0 682.0 L 899.0 687.5 L 899.0 676.5 z" fill="#0d9488"/>
|
||||
<text transform="rotate(-90 850.0 517.0)" x="850.0" y="517.0" font-size="11.5" fill="#0d9488" text-anchor="middle">wait bit0</text>
|
||||
<text x="40" y="784" font-size="12" fill="#94a3b8">生产者与消费者互不知晓,只经事件组解耦;新增子系统 = 新 bit + 各自 wait/set,不改已有任务。详见 docs/项目启动顺序管理.md</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.3 KiB |
+252
@@ -0,0 +1,252 @@
|
||||
# 网口设置页配置说明
|
||||
|
||||
> 版本:v2.0 日期:2026-09-10
|
||||
> 适用工程:STM32F4-Base(STM32F407ZGTx + CH395F 以太网控制器)
|
||||
> 范围:UI「网口设置」页可配置的网口参数(IP 地址 / 子网掩码 / 默认网关 / MAC 地址)——
|
||||
> 参数与默认值、持久化、生效链路、页面操作与权限。
|
||||
|
||||
---
|
||||
|
||||
## 0. 速览
|
||||
|
||||
| 项目 | 内容 |
|
||||
|---|---|
|
||||
| **可配置** | IP 地址、子网掩码、默认网关、MAC 地址 |
|
||||
| **入口** | 屏幕:`通讯参数 → 网口设置`(修改密码 `123` 可编辑;查看密码 `000` 只读) |
|
||||
| **存储** | NAND Flash 文件 `/cfg/netcfg.bin`(掉电不丢) |
|
||||
| **生效** | 按「保存」后**立即生效**(网络重建约 1~3s,既有 TCP 连接会断开,无需重启) |
|
||||
| **工厂默认** | `192.168.1.100` / `255.255.255.0` / `192.168.1.1` / 芯片内置 MAC |
|
||||
|
||||
**典型操作(改 IP)**:
|
||||
|
||||
1. 屏幕进入 `通讯参数 → 网口设置`,输入修改密码 `123`
|
||||
2. 选中 `IP地址` 行 → 回车进编辑 → `↑/↓` 调数字、`←/→` 移光标 → 回车确认
|
||||
3. 选中 `保存` 行 → 回车 → 显示 `OK`
|
||||
4. 约 1~3 秒后设备在新 IP 上恢复服务(FTP / 对时 / 远程显示自动重建监听)
|
||||
|
||||
---
|
||||
|
||||
## 1. 可配置参数与工厂默认值
|
||||
|
||||
定义在 `Drivers/BSP/NETCFG/net_cfg.h` 的 `net_cfg_t`:
|
||||
|
||||
| 参数 | 字段 | 工厂默认 | 说明 |
|
||||
|---|---|---|---|
|
||||
| IP 地址 | `ip[4]` | **192.168.1.100** | 静态 IP(不启用 DHCP) |
|
||||
| 子网掩码 | `mask[4]` | **255.255.255.0** | /24 |
|
||||
| 默认网关 | `gw[4]` | **192.168.1.1** | |
|
||||
| MAC 地址 | `mac[6]` | **全零** | 全零 = 不覆盖芯片内置 MAC(见 §2) |
|
||||
|
||||
工厂默认值是**单一来源**:`net_cfg.c` 顶部 `NET_CFG_DEF_*` 宏,同时用于 `s_cfg` 静态初始化与 `s_cfg_defaults()`,避免两处漂移。
|
||||
|
||||
> **RAM 副本自启动即有效**:`s_cfg` 以工厂默认值**静态初始化**(落 `.data`),不依赖 `net_cfg_load()` 是否被调用——即便存储异常,页面与网络拿到的也是有效配置而非全零。
|
||||
|
||||
---
|
||||
|
||||
## 2. MAC 地址语义
|
||||
|
||||
| 存储值 | `net_cfg_get_mac()` 返回 | 网络初始化时 |
|
||||
|---|---|---|
|
||||
| 非全零 | 存储的 MAC | 写入芯片 EEPROM |
|
||||
| 全零(默认) | 芯片实际 MAC 影子(已回写时);否则全零 | **不写**,保持芯片内置 MAC |
|
||||
|
||||
- 芯片实际 MAC 由 netTask 在网络初始化成功后读回,经 `net_cfg_set_active_mac()` 存入**影子区**——供页面只读显示(页面不直接触 SPI)。
|
||||
- MAC 写芯片 EEPROM,**每上电重放同值幂等**。
|
||||
|
||||
---
|
||||
|
||||
## 3. 持久化
|
||||
|
||||
### 3.1 磁盘格式(`/cfg/netcfg.bin`)
|
||||
|
||||
`net_cfg.c` 的 `net_cfg_blob_t`(两端布局一致,小端):
|
||||
|
||||
| 偏移 | 字段 | 大小 | 说明 |
|
||||
|---|---|---|---|
|
||||
| 0 | `magic` | 4 | `0x4647434E`(ASCII `'NCFG'`) |
|
||||
| 4 | `ver` | 2 | 格式版本 = 1 |
|
||||
| 6 | `crc` | 2 | **CRC-16/MODBUS**,覆盖 ip..mac 共 18B |
|
||||
| 8 | `ip[4]` | 4 | |
|
||||
| 12 | `mask[4]` | 4 | |
|
||||
| 16 | `gw[4]` | 4 | |
|
||||
| 20 | `mac[6]` | 6 | |
|
||||
| — | 对齐填充 | 2 | 结构体 `sizeof = 28B` |
|
||||
|
||||
- **加载校验**:magic / ver / CRC 任一不符 → 回退工厂默认并打印 `DBG_ERROR`(坏配置不带崩网络)。
|
||||
- **保存**:先建 `/cfg` 目录(`f_mkdir`,已存在不影响),再 `FA_CREATE_ALWAYS` 覆盖写。
|
||||
|
||||
### 3.2 存储后端(双端)
|
||||
|
||||
`net_cfg_store.h` 定义接口,两平台各一份实现(互斥编译):
|
||||
|
||||
| 平台 | 文件 | 位置 |
|
||||
|---|---|---|
|
||||
| MCU | `net_cfg_store_mcu.c` | NAND 上 FatFS 文件 `/cfg/netcfg.bin` |
|
||||
| 模拟器 | `net_cfg_store_sim.c` | 工作目录本地文件 `sim_netcfg.bin`(`fopen`) |
|
||||
|
||||
磁盘格式两端一致,文件可互读。
|
||||
|
||||
---
|
||||
|
||||
## 4. 生效链路
|
||||
|
||||
### 4.1 上电启动
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant DT as defaultTask
|
||||
participant NT as netTask
|
||||
participant CFG as net_cfg
|
||||
participant CH as CH395F
|
||||
|
||||
Note over DT,NT: 上电:CubeMX 已创建任务与事件组,调度器启动
|
||||
DT->>DT: FatFS 挂载(f_mount;无卷则 f_mkfs)
|
||||
DT-->>NT: 置 SYS_EVT_STORAGE_READY(就绪屏障)
|
||||
NT->>NT: 等 SYS_EVT_STORAGE_READY(≤10s;超时用工厂默认继续)
|
||||
NT->>CFG: net_cfg_load()
|
||||
Note over CFG: 读 /cfg/netcfg.bin;缺失/损坏 → 工厂默认
|
||||
CFG-->>NT: RAM 副本(有效)
|
||||
NT->>CFG: net_cfg_get() → net_cfg_fmt_ipv4()
|
||||
CFG-->>NT: "192.168.001.100"
|
||||
NT->>CH: 初始化网络(下发 IP / 掩码 / 网关 / MAC)
|
||||
CH-->>NT: 芯片实际 MAC
|
||||
NT->>CFG: net_cfg_set_active_mac()(MAC 影子)
|
||||
NT->>NT: 置 SYS_EVT_NET_READY(广播网络就绪)
|
||||
```
|
||||
|
||||
### 4.2 保存后立即生效
|
||||
|
||||
保存网口参数后,设备立即按新参数重建网络,无需重启。
|
||||
|
||||
**机制**:`请求 → netTask 重建 → 代次号 → 服务自愈`
|
||||
|
||||
**约束与对策**:网络重建会复位 CH395F,所有监听 Socket 随之失效;而三个 TCP 服务(FTP :21 / 对时 :8888 / 远程显示 :7003)的监听由各自任务持有。为此引入"网络代次号"——各服务检测到代次变化后主动重建监听,在新 IP 上恢复服务。
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant UI as 网口设置页
|
||||
participant CFG as net_cfg
|
||||
participant NT as netTask
|
||||
participant NS as net_socket
|
||||
participant SVC as FTP/对时/远程显示
|
||||
|
||||
UI->>CFG: 保存行 ENTER → net_cfg_save()(写盘)
|
||||
CFG->>NT: net_cfg_request_apply() → 回调置 SYS_EVT_NET_RECONFIG
|
||||
NT->>NT: 主循环检测到该位 → 重建网络(新 IP/掩码/网关/MAC)
|
||||
NT->>NS: net_generation_inc()(代次号 +1,此刻网络已可用)
|
||||
NT->>SVC: 重新广播 SYS_EVT_NET_READY
|
||||
SVC->>SVC: 轮询发现代次变化 → 关闭旧监听 → 重建监听(新 IP)
|
||||
```
|
||||
|
||||
**三个服务的自愈点**:
|
||||
|
||||
| 服务 | 端口 | 检测位置 | 自愈方式 |
|
||||
|---|---|---|---|
|
||||
| FTP | 21 | `lftpd_inet_accept()` | 代次变化 → 返回 -1 → `lftpd_start` 外层循环重建监听 |
|
||||
| 对时 | 8888 | `time_sync_task()` 的 accept 循环 | 代次变化 → 关旧监听 → 回外层重建 |
|
||||
| 远程显示 | 7003 | `ui_net_sink_poll()` | 代次变化 → 关旧监听/客户端 → 重建 |
|
||||
|
||||
**关键设计点**:
|
||||
|
||||
1. **SPI 归 netTask 独占**:UI 只置事件位,不直接操作 CH395F。
|
||||
2. **代次号在链路就绪后才递增**:避免服务在网络未就绪时抢先重建,也避免 uiTask 阻塞在消息队列上冻结界面。
|
||||
3. **无回调 = 空操作**:模拟器不注册应用回调,行为退化为"仅写盘"。
|
||||
4. **既有连接必断**:IP 变更导致连接不可达,属网络层语义。
|
||||
|
||||
---
|
||||
|
||||
## 5. 页面操作(网口设置页)
|
||||
|
||||
菜单路径:`通讯参数 → 网口设置`(菜单节点 30,行尾 ` #P` 需密码)。页面模块 `Drivers/BSP/UI/ui_page_net_cfg.c`。
|
||||
|
||||
### 5.1 页面布局
|
||||
|
||||
| 行 | 名称 | 值格式 |
|
||||
|---|---|---|
|
||||
| 0 | IP地址 | `192.168.001.100`(每段 `%03u` 补零,**定长 11 字符**) |
|
||||
| 1 | 子网掩码 | `255.255.255.000` |
|
||||
| 2 | 默认网关 | `192.168.001.001` |
|
||||
| 3 | MAC地址 | `A4:94:B6:12:34:56`(6 字节十六进制,冒号分隔) |
|
||||
| 4 | 保存 | `ENTER` → 保存后显示 `OK` / `ERR` |
|
||||
|
||||
### 5.2 权限
|
||||
|
||||
`ui_pwd_def.h` 编译期密码:
|
||||
|
||||
| 输入的密码 | 授予权限 | 页面行为 |
|
||||
|---|---|---|
|
||||
| 查看密码 `000` | `UI_PERM_VIEW` | **只读**:ENTER 无效,仅可浏览 |
|
||||
| 修改密码 `123` | `UI_PERM_MODIFY` | **可编辑**:ENTER 进编辑模式 |
|
||||
|
||||
### 5.3 编辑模式
|
||||
|
||||
ENTER 进入选中地址行 → `↑/↓` 调活动字节(0..255 **环绕**)、`←/→` 移字节光标(首↔末环绕)、ENTER 确认退出。调整**实时落 RAM 副本**(`net_cfg_set`),**只有保存行 ENTER 才写盘并应用**(`net_cfg_save` + `net_cfg_request_apply`);ESC 退出不写盘、改动丢弃(重启后恢复上次保存值)。
|
||||
|
||||
> 值格式与解析约定:页面写定长 `%03u` 字符串 → 网络初始化按十进制逐段解析、容忍前导零(如 `"001"` → 1)。
|
||||
|
||||
---
|
||||
|
||||
## 6. 文件与 API 索引
|
||||
|
||||
| 文件 | 职责 |
|
||||
|---|---|
|
||||
| `Drivers/BSP/NETCFG/net_cfg.h/.c` | 配置核心:RAM 副本 + 工厂默认 + 加载/保存编排 + MAC 影子 + IPv4 格式化 |
|
||||
| `Drivers/BSP/NETCFG/net_cfg_store.h` | 存储后端接口(读/写) |
|
||||
| `Drivers/BSP/NETCFG/net_cfg_store_mcu.c` | MCU 后端:FatFS `/cfg/netcfg.bin` |
|
||||
| `Drivers/BSP/NETCFG/net_cfg_store_sim.c` | 模拟器后端:本地 `sim_netcfg.bin` |
|
||||
| `Drivers/BSP/UI/ui_page_net_cfg.c` | 网口设置内容页(显示 / 编辑 / 保存) |
|
||||
| `Drivers/BSP/UI/ui_pwd_def.h` | 编译期密码(查看 000 / 修改 123) |
|
||||
| `App/task/net_task.c` | 上电加载配置 + 消费"立即应用"请求重建网络 |
|
||||
| `Drivers/BSP/NET/net_socket.h/.c` | 网络代次号(服务自愈依据) |
|
||||
|
||||
**对外 API**(`net_cfg.h`):
|
||||
|
||||
| API | 说明 |
|
||||
|---|---|
|
||||
| `net_cfg_load()` | 从存储加载到 RAM 副本;文件缺失/损坏回退工厂默认(恒返回 0) |
|
||||
| `net_cfg_save()` | 组装磁盘格式写盘;0 成功 / -1 失败 |
|
||||
| `net_cfg_request_apply()` | 请求立即应用(页面保存成功后调用) |
|
||||
| `net_cfg_set_apply_cb()` | 注册"立即应用"平台回调 |
|
||||
| `net_cfg_get()` | 取 RAM 副本只读指针(恒非空) |
|
||||
| `net_cfg_set()` | 更新 RAM 副本(不写盘) |
|
||||
| `net_cfg_set_active_mac()` | 回写芯片实际 MAC 影子(netTask 调) |
|
||||
| `net_cfg_get_mac()` | 取显示用 MAC(存储非零 → 存储;否则影子;否则全零) |
|
||||
| `net_cfg_fmt_ipv4()` | 4 字节 → `"192.168.001.100"` 定长字符串 |
|
||||
|
||||
---
|
||||
|
||||
## 7. 陷阱
|
||||
|
||||
1. **保存即生效,但连接会断**:保存后重建网络(约 1~3s),既有 TCP 连接(FTP / 对时 / 远程显示)会断开并自动重建监听——IP 变更的必然结果,非缺陷。
|
||||
2. **MAC 全零是哨兵**,不是"MAC 为 0":要恢复芯片内置 MAC,把 MAC 字段改回全零并保存。
|
||||
3. **配置存在 NAND 上**:存储挂载失败时 `net_cfg_load` 回退工厂默认,网络仍能起,但保存会失败(页面显示 `ERR`)。
|
||||
4. **改配置需"修改密码"**:查看密码 `000` 进页面是只读,ENTER 无效(防误改)。
|
||||
5. **新增 TCP 服务需处理重建**:须在其监听循环中检测 `net_get_generation()` 变化并重建监听,否则网络重建后不再可达(见 §4.2)。
|
||||
|
||||
---
|
||||
|
||||
## 8. 相关文档
|
||||
|
||||
| 主题 | 文档 |
|
||||
|---|---|
|
||||
| CH395F 已知陷阱 | `docs/问题记录/CH395F_Trap_Records.md` |
|
||||
| 网络测试指南 | `docs/测试说明/CH395F_Test_Guide.md` |
|
||||
| 启动顺序 / 就绪屏障 | `docs/项目启动顺序管理.md` |
|
||||
| 项目分层架构 | `docs/项目架构总览.md` |
|
||||
| 内容页框架(含网口设置页) | `docs/UI 设计/内容页架构.md` |
|
||||
|
||||
---
|
||||
|
||||
## 9. 变更记录
|
||||
|
||||
| 日期 | 变更 |
|
||||
|---|---|
|
||||
| 2026-09-10 | 初版:网口参数 + 协议栈参数 + 陷阱 |
|
||||
| 2026-09-10 | `net_config.h` 收敛:删除 8 个从未引用的遗留宏;KeepAlive 三宏改为实际生效值并由 `net_socket.c` 引用 |
|
||||
| 2026-09-10 | **保存后立即生效**:新增"应用回调 + 网络代次号"机制 |
|
||||
| 2026-09-10 | §0 速览重写为并列对比表 + 两条生效时机 + 分步操作 |
|
||||
| 2026-09-10 | §1.7 改为客观描述当前行为,补"三个服务的自愈点"表 |
|
||||
| 2026-09-10 | **v2.0 聚焦 UI 页面配置**:删除"协议栈参数(② 编译期常量)"整章;标题 / 范围 / 速览 / 索引 / 陷阱相应收敛为"网口设置页"内容 |
|
||||
| 2026-09-10 | §4.1 上电启动由文本流改为**时序图**(与 §4.2 一致,任务先后关系与配置回退更直观) |
|
||||
| 2026-09-10 | MAC 显示改为常识格式 **`A4:94:B6:12:34:56`**(冒号分隔,17 字符);页面布局左移(名 x=4 / 值 x=54)以容纳 102px 宽度,编辑光标段偏移随之为 3i |
|
||||
@@ -39,6 +39,11 @@
|
||||
|
||||
这与 CubeMX"一次性创建全部任务"的模式完全兼容:任务仍由 `MX_FREERTOS_Init` 统一创建,但各自在入口 `wait`,相当于"创建即挂起、就绪才放行"。
|
||||
|
||||

|
||||
|
||||
> 上图:**左 = 生产者(set)· 中 = 事件组 `sys_evtHandle` · 右 = 消费者(wait)**;任务卡与事件位对齐同一行,`bit1 → ftpTask` 的额外依赖用 **All 汇合点**(`osFlagsWaitAll`)表达。
|
||||
> 源码 `tools/arch_diagram/gen_barrier_svg.py`(改数据重跑即可),PNG 预览本地渲染、不入库。
|
||||
|
||||
### 为什么用 Event Flags 而非全局变量
|
||||
| 维度 | 全局标志 `g_net_ready` | Event Flags |
|
||||
|------|----------------------|-------------|
|
||||
@@ -65,6 +70,7 @@ extern osEventFlagsId_t sys_evtHandle;
|
||||
#define SYS_EVT_STORAGE_READY (1u << 1) /* defaultTask:FTL/FatFS 挂载完成 */
|
||||
#define SYS_EVT_FTP_READY (1u << 2) /* ftpTask:lftpd_start 完成 */
|
||||
#define SYS_EVT_TIMESYNC_READY (1u << 3) /* timeSyncTask:首次时间同步完成 */
|
||||
#define SYS_EVT_NET_RECONFIG (1u << 4) /* UI:请求立即应用新网口配置(netTask 消费后重建网络) */
|
||||
/* 后续新增子系统继续左移分配 bit,32 位上限内即可 */
|
||||
```
|
||||
|
||||
@@ -75,6 +81,7 @@ extern osEventFlagsId_t sys_evtHandle;
|
||||
| `SYS_EVT_STORAGE_READY` | `defaultTask`(挂载成功后) | 依赖文件系统的测试套件(如 STORAGE/GD5F) |
|
||||
| `SYS_EVT_FTP_READY` | `ftpTask`(`lftpd_start` 成功后) | 无(示例,预留给需要 FTP 就绪的后续功能) |
|
||||
| `SYS_EVT_TIMESYNC_READY` | (预留,暂无生产者置位) | 可选:对时精度敏感的业务 |
|
||||
| `SYS_EVT_NET_RECONFIG` | UI 网口设置页保存成功(经 `net_cfg` 应用回调置位) | `netTask` 主循环:消费后 `net_stack_bring_up()` 重建网络(新网口参数立即生效),并递增网络代次号 |
|
||||
|
||||
### 3.3 事件组创建时机
|
||||
`sys_evtHandle` 必须在**任何任务 `wait` 之前**存在。`main.c` 调用顺序为:
|
||||
|
||||
+307
@@ -0,0 +1,307 @@
|
||||
# STM32F4-Base 项目架构总览
|
||||
|
||||
> 版本:v1.0 日期:2026-09-10
|
||||
> 平台:STM32F407ZGTx @168MHz(HSE 8MHz → PLL M=4/N=168/P=2)+ FreeRTOS V10.3.1(CMSIS-RTOS2)
|
||||
> 用途:一页看清项目的分层、任务、数据流与目录组织。细节以各模块专项文档为准(见 §9)。
|
||||
|
||||
---
|
||||
|
||||
## 1. 全景分层架构
|
||||
|
||||

|
||||
|
||||
> 上图由 `tools/arch_diagram/gen_arch_svg.py` 生成(**手绘 SVG**,布局确定、零连线交叉);改架构后编辑脚本顶部 `ROWS` 数据重跑即可,源数据与 §1.1 表一致。
|
||||
> 需要 PNG 预览(PPT/聊天分享):用 Edge 无头渲染 `docs/项目架构总览.svg` 即可(PNG 不入库)。
|
||||
|
||||
> 分层原则:**上层只依赖相邻下层(单向)**;**① 业务层为顶层**(仅 `net_cfg`,被 UI 与基础服务共用),**② UI 层与 ③ 基础服务层同级并列**。跨层的具体调用关系见 §1.2 表。
|
||||
|
||||
### 1.1 各层职责与关键模块
|
||||
|
||||
> ① 业务层(顶层,仅 `net_cfg`);② UI 层与 ③ 基础服务层**同级并列**(均使用 `net_cfg`);④ 中间件层 → ⑤ 驱动层 → ⑥ 内核/HAL → ⑦ 硬件 依次向下。
|
||||
|
||||
| 层 | 目录 | 职责 | 关键模块 |
|
||||
|---|---|---|---|
|
||||
| ① 业务层 | `Drivers/BSP/NETCFG/` | 顶层共享配置(被 UI 与基础服务共用) | `net_cfg` |
|
||||
| ② UI 层 | `Drivers/BSP/UI/` | LVGL 界面(与 PC 模拟器共用源码) | `ui_port` `ui_menu` `ui_menu_view` `ui_menu_tab` `ui_content_page` `ui_page_*` `ui_net_sink` `ui_font_cn_12` `ui_deco` |
|
||||
| ③ 基础服务层 | `App/`、`Drivers/BSP/NET/lftpd/` | 应用侧服务与编排 | `app_main` `sys_clock` `sys_events` `app_cfg` `task/*` `lftpd` `time_sync` |
|
||||
| ④ 中间件层 | `Drivers/BSP/NET/`、`Lib/`、`App/util/` | 通用库 / 协议栈 | `net_socket` `net_select` `FatFs` `nand_ftl(dhara)` `LVGL` `util(crc·ringbuf)` |
|
||||
| ⑤ 驱动层 | `Drivers/BSP/{CH395F,GD5F2GQ5UE,TPAFE5160,SD2506,RS485}/` | 芯片寄存器 / 时序 / 中断 | `ch395f` `gd5f2gq5ue` `tpafe5160` `sd2506` `rs485` |
|
||||
| ⑥ 内核 / HAL | `Middlewares/`、`Drivers/STM32F4xx_HAL_Driver/`、`Drivers/CMSIS/` | 任务调度、外设 HAL、启动 | FreeRTOS V10.3.1 · STM32 HAL · CMSIS |
|
||||
| ⑦ 硬件 | — | STM32F407ZGTx @168MHz | SPI1 / SPI2 / I2C1 / UART5 / USART1 / DMA / GPIO / TIM7 |
|
||||
| ⑥ 硬件 | — | STM32F407ZGTx @168MHz | SPI1 / SPI2 / I2C1 / UART5 / USART1 / DMA / GPIO / TIM7 |
|
||||
|
||||
### 1.2 关键跨层依赖(对应上图未画出的连线)
|
||||
|
||||
| 依赖 | 说明 |
|
||||
|---|---|
|
||||
| `ui_page_net_cfg` → `net_cfg` | 网口设置页显示/编辑 RAM 副本(不直接触 CH395F SPI) |
|
||||
| `netTask` → `net_cfg` → `net_socket` | 上电 `net_cfg_load` 后 `net_init(ip,mask,gw,mac)` |
|
||||
| `lftpd` / `net_cfg` / 测试 → `FatFs` → `nand_ftl` → `gd5f2gq5ue` | 文件与配置落 NAND |
|
||||
| `ui_net_sink` / `time_sync` → `net_socket` | 远程显示 :7003 / 对时 :8888 共用同一 TCP 栈 |
|
||||
| `ui_menu` → `ui_content_page` → `ui_page_*` | 框架 + 页面插件(描述符解耦) |
|
||||
| `time_sync` / `sys_clock` → `sd2506` | 墙钟基准与写回 RTC |
|
||||
| `ui_port` / `ui_page_*` → `LVGL` | UI 全部绘制走 LVGL |
|
||||
|
||||
> `*` `adc_task.c` / `rs485_task.c` 任务体已实现,但**当前未在 `freertos.c` 创建**(预留/待接入)。
|
||||
|
||||
---
|
||||
|
||||
## 2. 运行时任务与就绪屏障
|
||||
|
||||
### 2.1 任务一览(`Src/freertos.c` 创建)
|
||||
|
||||
| 任务 | 栈 (字节) | 优先级 | 职责 | 入口 |
|
||||
|---|---|---|---|---|
|
||||
| `defaultTask` | 2048 | Normal | FatFS 挂载(无卷 `f_mkfs`)→ 置 `STORAGE_READY` → LED 闪烁 | `StartDefaultTask` |
|
||||
| `netTask` | 4096 | Normal | 等存储 → `net_stack_bring_up()` → 置 `NET_READY` → `net_poll` 轮询 | `StartNetTask` |
|
||||
| `ftpTask` | 4096 | Normal | 等网络+存储 → `lftpd_start("/", 21)` | `StartFtpTask` |
|
||||
| `testTask` | 4096 | BelowNormal | 测试套件入口(`test/test_config.h` 选择;产品构建空转) | `start_test_task` |
|
||||
| `uiTask` | 16384 | BelowNormal | 等网络 → `ui_port_init`/`ui_menu_init`/`ui_net_sink` → 10ms 刷新 | `ui_task_func` |
|
||||
| `timeSyncTask` | 2048 | Normal | 等网络 → TCP :8888 收 PC 推送时间 → 写 RTC | `time_sync_task` |
|
||||
|
||||
> `idle`(128 words)/ `timer`(256 words)由 FreeRTOS 内核创建。所有任务栈从 48KB heap_4 堆分配。
|
||||
|
||||
### 2.2 就绪屏障(Event Flags,取代魔法延时)
|
||||
|
||||

|
||||
|
||||
> 三栏式:**左 = 生产者(set)· 中 = 事件组 `sys_evtHandle` · 右 = 消费者(wait)**。
|
||||
> 任务卡与事件位对齐同一行,箭头 = `set` / `wait` 关系;`bit1 → ftpTask` 的额外依赖用 **All 汇合点**表达(`osFlagsWaitAll`)。
|
||||
> 源码 `tools/arch_diagram/gen_barrier_svg.py`(改数据重跑即可);PNG 预览本地渲染、不入库。
|
||||
|
||||
**设计原则**:生产者与消费者互不知晓,只经事件组解耦;新增子系统只需定义新 bit + 各自 wait/set,不改已有任务。
|
||||
(详见 `docs/项目启动顺序管理.md`)
|
||||
|
||||
### 2.3 线程间通信
|
||||
|
||||
| 机制 | 对象 | 用途 |
|
||||
|---|---|---|
|
||||
| Event Flags | `sys_evtHandle` | 启动就绪屏障(§2.2) |
|
||||
| Message Queue | `netMsgQueue`(深度 8,`net_msg_t`) | 所有 CH395F 操作经队列**串行化**到 netTask 执行,保证 SPI 单线程访问 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 启动时序
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant M as main
|
||||
participant A as app_main_init
|
||||
participant K as osKernelStart
|
||||
participant D as defaultTask
|
||||
participant N as netTask
|
||||
participant F as ftpTask
|
||||
participant U as uiTask
|
||||
participant T as timeSyncTask
|
||||
|
||||
M->>M: HAL_Init + CubeMX 外设初始化<br/>(SPI1/SPI2/I2C1/UART5/USART1/DMA/GPIO/TIM7)
|
||||
M->>A: app_main_init()
|
||||
Note over A: 时间子系统优先,使后续日志带时间戳
|
||||
A->>A: sd2506_init() → sys_clock_init()(墙钟基准)
|
||||
A->>A: rs485_init() → gd5f2gq5ue_init()(BBT 扫描)
|
||||
A->>A: tpafe5160_init() → SD2506 诊断 → start_conv_irq()
|
||||
A-->>M: "App init OK"
|
||||
M->>K: osKernelStart()
|
||||
Note over K: MX_FREERTOS_Init 已创建 6 任务 + 事件组 + 队列
|
||||
|
||||
K->>D: 调度
|
||||
D->>D: f_mount(无卷则 f_mkfs FAT32)→ LED 循环
|
||||
D-->>N: set SYS_EVT_STORAGE_READY
|
||||
|
||||
N->>N: wait STORAGE_READY(≤10s,超时回退工厂默认)
|
||||
N->>N: net_cfg_load() → net_init(ip,mask,gw,mac)<br/>→ PHY 强制 100M 全双工 → 等链路 ≤3s → MAC 回写影子
|
||||
N-->>F: set SYS_EVT_NET_READY
|
||||
N->>N: 循环 net_poll() + net_process_messages() + osDelay(1)
|
||||
|
||||
F->>F: wait NET_READY|STORAGE_READY(All)→ lftpd_start("/", 21)
|
||||
U->>U: wait NET_READY → ui_port_init → ui_menu_init → ui_net_sink_start(7003)
|
||||
T->>T: wait NET_READY → TCP listen :8888
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 存储栈
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
FS["FatFs (Lib/FatFs)<br/>f_mount / f_open / f_mkfs"]
|
||||
DIO["diskio.c<br/>BSP 适配层"]
|
||||
FTL["nand_ftl.c<br/>dhara FTL(磨损均衡 + 逻辑块映射)"]
|
||||
NAND["gd5f2gq5ue.c<br/>页读写 / ECC / BBT 坏块表"]
|
||||
CHIP[("GD5F2GQ5UE<br/>NAND 2Gbit")]
|
||||
|
||||
subgraph USERS["使用方"]
|
||||
NC["net_cfg<br/>/cfg/netcfg.bin"]
|
||||
LF["lftpd<br/>FTP 文件读写"]
|
||||
ST["测试套件<br/>STORAGE/GD5F"]
|
||||
end
|
||||
|
||||
NC --> FS
|
||||
LF --> FS
|
||||
ST --> FS
|
||||
FS --> DIO --> FTL --> NAND --> CHIP
|
||||
|
||||
subgraph RTC["独立时间源"]
|
||||
SD["SD2506 RTC (I2C1)"] --> SC["sys_clock<br/>墙钟 epoch"]
|
||||
end
|
||||
```
|
||||
|
||||
- 掉电持久化:`net_cfg` 配置(magic `'NCFG'` + ver + CRC-16/MODBUS),**保存后下次上电生效**。
|
||||
- NAND 坏块表(BBT)持久化在保留块池,不交给 dhara 管理。
|
||||
|
||||
---
|
||||
|
||||
## 5. 网络栈与 TCP 消费者
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
CHIP[("CH395F<br/>MAC+PHY · SPI2 + DMA")] --> DRV["ch395f.c<br/>寄存器/命令 + 中断集中读取"]
|
||||
DRV --> NS["net_socket.c<br/>BSD Socket API(单连接模式)"]
|
||||
DRV --> NSEL["net_select.c<br/>select/poll 多路复用"]
|
||||
NS --> Q["netMsgQueue<br/>(串行化到 netTask)"]
|
||||
Q --> NT["netTask<br/>net_poll() + net_process_messages()"]
|
||||
NS --> C1["lftpd<br/>FTP :21"]
|
||||
NS --> C2["time_sync<br/>对时 :8888"]
|
||||
NS --> C3["ui_net_sink<br/>RemoDispBus :7003"]
|
||||
C3 -.->|"回送 1bpp 帧 + 接收远端按键"| CLIENT["DTU-RemoteLCD 客户端"]
|
||||
|
||||
NC["net_cfg<br/>IP/掩码/网关/MAC"] --> NS
|
||||
style NT fill:#efe,stroke:#484
|
||||
```
|
||||
|
||||
**单连接约束**:CH395F 配置为多 Socket + TCP Server 单连接(`set_fun_para(0x09)`),Socket 0 复用监听与数据通道;FTP / 对时 / 远程显示三个 TCP 服务**串行**占用连接,连接断开后自动回到 LISTEN。
|
||||
|
||||
---
|
||||
|
||||
## 6. UI 层架构(框架 + 页面插件)
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph LOGIC["逻辑层"]
|
||||
UM["ui_menu.c<br/>级联框状态机 + 按键分发"]
|
||||
MT["ui_menu_tab.c<br/>菜单树生成物(ui_menu_tree.txt)"]
|
||||
end
|
||||
subgraph VIEW["显示层"]
|
||||
MV["ui_menu_view.c<br/>框体/行/装饰自绘"]
|
||||
DECO["ui_deco.c<br/>浅 V 装饰"]
|
||||
end
|
||||
subgraph FRAME["内容页框架"]
|
||||
CP["ui_content_page.c<br/>屏生命周期 + 描述符驱动绘制/按键"]
|
||||
end
|
||||
subgraph PLUGIN["页面插件(每页一文件)"]
|
||||
P1["ui_page_device_info"]
|
||||
P2["ui_page_ac"]
|
||||
P3["ui_page_dc"]
|
||||
P4["ui_page_password"]
|
||||
P5["ui_page_net_cfg"]
|
||||
end
|
||||
subgraph PORT["显示端口"]
|
||||
UP["ui_port.c<br/>160x160 1bpp 缓冲"]
|
||||
SINK["ui_net_sink.c<br/>远程推流"]
|
||||
end
|
||||
|
||||
UM --> MT
|
||||
UM --> MV
|
||||
UM --> CP
|
||||
MV --> DECO
|
||||
CP --> P1 & P2 & P3 & P4 & P5
|
||||
MV --> UP
|
||||
SINK --> UP
|
||||
P5 -.->|"net_cfg RAM 副本"| CFG["net_cfg"]
|
||||
P4 -.->|"ui_pwd_def.h 编译期密码"| PWD["查看 000 / 修改 123"]
|
||||
```
|
||||
|
||||
- 内容页为**独立全屏屏**,由框架一次性自绘(**零 LVGL 子对象**),ESC 返回菜单屏。
|
||||
- 菜单受保护叶子(`ui_menu_tree.txt` 行尾 ` #P`):先弹密码页,授权后 `menu_password_ok` **先关页再续进入动作**。
|
||||
- 字体 `ui_font_cn_12.c` 由 `tools/font_gen/` 生成(宋体 12px 1bpp 子集,111 字/208 字形)。
|
||||
|
||||
---
|
||||
|
||||
## 7. 目录组织
|
||||
|
||||
```
|
||||
STM32F4-Base/
|
||||
├── Src/ Inc/ CubeMX 生成:main.c / freertos.c / HAL 外设 / 中断 / 时基
|
||||
│ ├── freertos.c 6 任务 + netMsgQueue + sys_evt 创建(USER CODE 区自定义)
|
||||
│ └── main.c 入口 → app_main_init → osKernelStart
|
||||
├── App/ 应用层(业务编排,非 CubeMX)
|
||||
│ ├── app_main.c 启动编排(时间子系统优先)
|
||||
│ ├── sys_clock.c 墙钟基准 / Unix epoch
|
||||
│ ├── time_sync.c TCP 对时服务
|
||||
│ ├── sys_events.h 就绪屏障 bit 定义
|
||||
│ ├── app_cfg.h 功能编译开关(APP_NET/ADC/RS485_ENABLE)
|
||||
│ ├── task/ net_task / ui_task / adc_task* / rs485_task*
|
||||
│ └── util/ crc(CRC-16/8)、ringbuf
|
||||
├── Drivers/BSP/ 纯手工驱动/服务(不受 CubeMX 保护)
|
||||
│ ├── CH395F/ 以太网 MAC+PHY
|
||||
│ ├── GD5F2GQ5UE/ NAND + dhara FTL
|
||||
│ ├── TPAFE5160/ ADC
|
||||
│ ├── SD2506/ RTC
|
||||
│ ├── RS485/ 半双工串口
|
||||
│ ├── NET/ net_socket / net_select / lftpd
|
||||
│ ├── NETCFG/ 网口配置持久化(MCU + 模拟器双后端)
|
||||
│ ├── UI/ LVGL UI(与模拟器共用)
|
||||
│ └── dbg_log.h/dbg_cfg.h 分级日志
|
||||
├── Lib/ 第三方:LVGL v9.2、FatFs、FreeRTOS、CMSIS
|
||||
├── Middlewares/ ST 官方中间件
|
||||
├── MDK-ARM/ Keil 工程(uvprojx + scatter + build/download 脚本)
|
||||
├── simulator/ PC 模拟器(CMake + SDL2,共用 UI 源码 + 无头回归测试)
|
||||
├── test/ 测试套件(test_config.h 互斥选择,产品构建空转)
|
||||
├── tools/ font_gen(字体生成)/ menu_gen(菜单表生成)
|
||||
├── scripts/ 代码规范检查、RAM 报告等
|
||||
└── docs/ 设计/测试/问题记录文档
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 构建与双端
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
SRC["共享源码<br/>Drivers/BSP/UI/*<br/>Drivers/BSP/NETCFG/net_cfg.c"]
|
||||
SRC --> MCU["MCU 端<br/>Keil MDK-ARM (ARMCC v5)<br/>@build → BUILD_OK"]
|
||||
SRC --> SIM["PC 模拟器<br/>CMake + SDL2 (MSVC)<br/>lvgl_sim.exe --keys"]
|
||||
MCU --> TEST["@test:串口采集 + 烧录 + 日志校验"]
|
||||
SIM --> REG["UI 回归:<br/>check_menu_rows.py<br/>check_content_pages.py"]
|
||||
```
|
||||
|
||||
| 目标 | 工具链 | 验证 |
|
||||
|---|---|---|
|
||||
| MCU 固件 | Keil MDK-ARM v5(`MDK-ARM/build.bat`) | `@build` 0 错 0 警 → `@test` 串口闭环 |
|
||||
| PC 模拟器 | CMake + SDL2 + MSVC(`simulator/build/`) | `check_menu_rows.py` + `check_content_pages.py`(文本断言 + 像素黄金参考) |
|
||||
|
||||
**功能编译开关**:`App/app_cfg.h`(网络/ADC/RS485);`test/test_config.h` 测试套件互斥选择(产品构建全注释)。
|
||||
|
||||
---
|
||||
|
||||
## 9. 相关专项文档
|
||||
|
||||
| 主题 | 文档 |
|
||||
|---|---|
|
||||
| 启动顺序 / 就绪屏障 | `docs/项目启动顺序管理.md` |
|
||||
| 存储架构(NAND/FTL/FatFs) | `docs/STM32F4-Base存储架构说明.md` |
|
||||
| 墙钟时间维护 | `docs/系统墙钟时间维护说明.md` |
|
||||
| RAM 预算与优化 | `docs/内存管理/RAM使用分析与优化方案.md` |
|
||||
| UI 设计架构 / 内容页 / 菜单 | `docs/UI 设计/UI_设计架构.md` · `内容页架构.md` · `菜单页面设计.md` |
|
||||
| UI 自动化测试 | `docs/UI 设计/UI_自动化测试流程.md` |
|
||||
| 驱动已知陷阱 | `docs/问题记录/{CH395F,GD5F2GQ5UE,SD2506}_Trap_Records.md` |
|
||||
| 各模块测试指南 | `docs/测试说明/` |
|
||||
| 代码规范 | `docs/代码规范/` |
|
||||
|
||||
---
|
||||
|
||||
## 10. 变更记录
|
||||
|
||||
| 日期 | 变更 |
|
||||
|---|---|
|
||||
| 2026-09-10 | 初版:六层全景架构 + 任务/就绪屏障 + 启动时序 + 存储栈 + 网络栈 + UI 分层 + 目录组织 |
|
||||
| 2026-09-10 | §1 全景分层架构重绘:由"36 节点 + 跨层虚线"改为**分层色带(每层一框)+ 职责表 + 跨层依赖表**,消除连线交叉、提升可读性 |
|
||||
| 2026-09-10 | §1 再改为**手绘 SVG**(`tools/arch_diagram/gen_arch_svg.py` → `项目架构总览.svg`:分层色带 + 模块芯片 + 单向下箭头,布局确定);PNG 预览本地渲染、不入库 |
|
||||
| 2026-09-10 | `net_cfg` 分层定位修正:按项目规划归入 **① 应用层**(原列在 ③ 服务/中间件);SVG 与 §1.1 表同步(模块物理路径仍在 `Drivers/BSP/NETCFG/`) |
|
||||
| 2026-09-10 | §1 布局修正:**应用层与 UI 层同级并列**(同一行并排,均向下依赖);生成器数据由"每层一行"改为 `ROWS`(行内可放多个并列层),顶层两框各引一条向下箭头 |
|
||||
| 2026-09-10 | §1 层级重定(按项目规划):**① 业务层**(改名,原"应用层")为**顶层**,含 `net_cfg`(被 UI 与基础服务共用)+ 业务编排;**② UI 层与 ③ 基础服务层同级并列**其下;`time_sync` 归基础服务层 |
|
||||
| 2026-09-10 | §1 定稿(严格版):**① 业务层顶层仅 `net_cfg` 一块**;`app_main`/`sys_clock`/`sys_events`/`app_cfg`/`task/*`/`util/*` 并入 **③ 基础服务层**(任务编排 + 网络/存储/图形);② UI 层与 ③ 同级并列 |
|
||||
| 2026-09-10 | §1 新增 **④ 中间件层**(通用库 / 协议栈:`net_socket`·`net_select`·`lftpd`·`FatFs`·`nand_ftl`·`LVGL`·`util`),位于 ③ 基础服务层与驱动层之间;③ 基础服务层收敛为**应用侧服务与编排**(`app_main`·`sys_clock`·`sys_events`·`app_cfg`·`task/*`·`time_sync`);层号顺延至 ⑦ 硬件 |
|
||||
| 2026-09-10 | `lftpd` 由 ④ 中间件层移入 **③ 基础服务层**(FTP 视为应用侧服务);中间件层余 `net_socket`·`net_select`·`FatFs`·`nand_ftl`·`LVGL`·`util` |
|
||||
| 2026-09-10 | §2.2 就绪屏障图由 Mermaid 改为**手绘 SVG**(`tools/arch_diagram/gen_barrier_svg.py` → `就绪屏障.svg`):三栏(生产者 / 事件组 / 消费者)+ 水平对齐 + All 汇合点,消除原图 9 条边的交叉;PNG 预览本地渲染、不入库 |
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1560" height="1000" viewBox="0 0 1560 1000" font-family="Microsoft YaHei, Segoe UI, PingFang SC, sans-serif">
|
||||
<rect x="0" y="0" width="1560" height="1000" fill="#ffffff"/>
|
||||
<text x="26" y="40" font-size="20" font-weight="700" fill="#0f172a">STM32F4-Base 项目分层架构</text>
|
||||
<text x="26" y="62" font-size="13" fill="#64748b">STM32F407ZGTx @168MHz · FreeRTOS V10.3.1 (CMSIS-RTOS2) · 上层只依赖相邻下层</text>
|
||||
<rect x="26" y="78" width="1508" height="92" rx="12" fill="#eff6ff" stroke="#2563eb" stroke-opacity="0.45"/>
|
||||
<text x="44" y="104" font-size="17" font-weight="700" fill="#2563eb">① 业务层</text>
|
||||
<text x="134" y="104" font-size="12" fill="#64748b">顶层共享配置 · 被 UI 与基础服务共用</text>
|
||||
<rect x="44" y="116" width="394" height="38" rx="8" fill="#ffffff" stroke="#2563eb" stroke-opacity="0.55"/>
|
||||
<text x="241" y="139" font-size="13" fill="#1e293b" text-anchor="middle">net_cfg(网口配置:IP/掩码/网关/MAC + Flash 持久化)</text>
|
||||
<rect x="26" y="214" width="741" height="188" rx="12" fill="#f5f3ff" stroke="#7c3aed" stroke-opacity="0.45"/>
|
||||
<text x="44" y="240" font-size="17" font-weight="700" fill="#7c3aed">② UI 层</text>
|
||||
<text x="129" y="240" font-size="12" fill="#64748b">Drivers/BSP/UI · 与 PC 模拟器共用</text>
|
||||
<rect x="44" y="252" width="162" height="38" rx="8" fill="#ffffff" stroke="#7c3aed" stroke-opacity="0.55"/>
|
||||
<text x="125" y="275" font-size="13" fill="#1e293b" text-anchor="middle">ui_port(显示端口)</text>
|
||||
<rect x="216" y="252" width="275" height="38" rx="8" fill="#ffffff" stroke="#7c3aed" stroke-opacity="0.55"/>
|
||||
<text x="354" y="275" font-size="13" fill="#1e293b" text-anchor="middle">ui_menu / ui_menu_view(级联菜单)</text>
|
||||
<rect x="502" y="252" width="179" height="38" rx="8" fill="#ffffff" stroke="#7c3aed" stroke-opacity="0.55"/>
|
||||
<text x="592" y="275" font-size="13" fill="#1e293b" text-anchor="middle">ui_menu_tab(菜单表)</text>
|
||||
<rect x="44" y="300" width="236" height="38" rx="8" fill="#ffffff" stroke="#7c3aed" stroke-opacity="0.55"/>
|
||||
<text x="162" y="323" font-size="13" fill="#1e293b" text-anchor="middle">ui_content_page(内容页框架)</text>
|
||||
<rect x="290" y="300" width="179" height="38" rx="8" fill="#ffffff" stroke="#7c3aed" stroke-opacity="0.55"/>
|
||||
<text x="380" y="323" font-size="13" fill="#1e293b" text-anchor="middle">ui_page_*(5 页插件)</text>
|
||||
<rect x="480" y="300" width="238" height="38" rx="8" fill="#ffffff" stroke="#7c3aed" stroke-opacity="0.55"/>
|
||||
<text x="599" y="323" font-size="13" fill="#1e293b" text-anchor="middle">ui_net_sink(远程显示 :7003)</text>
|
||||
<rect x="44" y="348" width="208" height="38" rx="8" fill="#ffffff" stroke="#7c3aed" stroke-opacity="0.55"/>
|
||||
<text x="148" y="371" font-size="13" fill="#1e293b" text-anchor="middle">ui_font_cn_12(中文字体)</text>
|
||||
<rect x="262" y="348" width="172" height="38" rx="8" fill="#ffffff" stroke="#7c3aed" stroke-opacity="0.55"/>
|
||||
<text x="348" y="371" font-size="13" fill="#1e293b" text-anchor="middle">ui_deco(浅 V 装饰)</text>
|
||||
<rect x="793" y="214" width="741" height="188" rx="12" fill="#f0fdfa" stroke="#0d9488" stroke-opacity="0.45"/>
|
||||
<text x="811" y="240" font-size="17" font-weight="700" fill="#0d9488">③ 基础服务层</text>
|
||||
<text x="935" y="240" font-size="12" fill="#64748b">应用侧服务 / 编排</text>
|
||||
<rect x="811" y="252" width="170" height="38" rx="8" fill="#ffffff" stroke="#0d9488" stroke-opacity="0.55"/>
|
||||
<text x="896" y="275" font-size="13" fill="#1e293b" text-anchor="middle">app_main(启动编排)</text>
|
||||
<rect x="991" y="252" width="177" height="38" rx="8" fill="#ffffff" stroke="#0d9488" stroke-opacity="0.55"/>
|
||||
<text x="1080" y="275" font-size="13" fill="#1e293b" text-anchor="middle">sys_clock(墙钟基准)</text>
|
||||
<rect x="1179" y="252" width="185" height="38" rx="8" fill="#ffffff" stroke="#0d9488" stroke-opacity="0.55"/>
|
||||
<text x="1271" y="275" font-size="13" fill="#1e293b" text-anchor="middle">sys_events(就绪屏障)</text>
|
||||
<rect x="811" y="300" width="162" height="38" rx="8" fill="#ffffff" stroke="#0d9488" stroke-opacity="0.55"/>
|
||||
<text x="892" y="323" font-size="13" fill="#1e293b" text-anchor="middle">app_cfg(编译开关)</text>
|
||||
<rect x="983" y="300" width="276" height="38" rx="8" fill="#ffffff" stroke="#0d9488" stroke-opacity="0.55"/>
|
||||
<text x="1122" y="323" font-size="13" fill="#1e293b" text-anchor="middle">task/(net · ui · adc* · rs485*)</text>
|
||||
<rect x="1270" y="300" width="148" height="38" rx="8" fill="#ffffff" stroke="#0d9488" stroke-opacity="0.55"/>
|
||||
<text x="1344" y="323" font-size="13" fill="#1e293b" text-anchor="middle">lftpd(FTP :21)</text>
|
||||
<rect x="811" y="348" width="227" height="38" rx="8" fill="#ffffff" stroke="#0d9488" stroke-opacity="0.55"/>
|
||||
<text x="924" y="371" font-size="13" fill="#1e293b" text-anchor="middle">time_sync(TCP 对时 :8888)</text>
|
||||
<rect x="26" y="446" width="1508" height="92" rx="12" fill="#f0fdf4" stroke="#16a34a" stroke-opacity="0.45"/>
|
||||
<text x="44" y="472" font-size="17" font-weight="700" fill="#16a34a">④ 中间件层</text>
|
||||
<text x="151" y="472" font-size="12" fill="#64748b">通用库 / 协议栈</text>
|
||||
<rect x="44" y="484" width="284" height="38" rx="8" fill="#ffffff" stroke="#16a34a" stroke-opacity="0.55"/>
|
||||
<text x="186" y="507" font-size="13" fill="#1e293b" text-anchor="middle">net_socket / net_select(BSD API)</text>
|
||||
<rect x="338" y="484" width="147" height="38" rx="8" fill="#ffffff" stroke="#16a34a" stroke-opacity="0.55"/>
|
||||
<text x="412" y="507" font-size="13" fill="#1e293b" text-anchor="middle">FatFs(文件系统)</text>
|
||||
<rect x="495" y="484" width="186" height="38" rx="8" fill="#ffffff" stroke="#16a34a" stroke-opacity="0.55"/>
|
||||
<text x="588" y="507" font-size="13" fill="#1e293b" text-anchor="middle">nand_ftl(dhara FTL)</text>
|
||||
<rect x="692" y="484" width="164" height="38" rx="8" fill="#ffffff" stroke="#16a34a" stroke-opacity="0.55"/>
|
||||
<text x="774" y="507" font-size="13" fill="#1e293b" text-anchor="middle">LVGL v9.2(图形库)</text>
|
||||
<rect x="866" y="484" width="186" height="38" rx="8" fill="#ffffff" stroke="#16a34a" stroke-opacity="0.55"/>
|
||||
<text x="960" y="507" font-size="13" fill="#1e293b" text-anchor="middle">util(crc · ringbuf)</text>
|
||||
<rect x="26" y="582" width="1508" height="92" rx="12" fill="#fffbeb" stroke="#d97706" stroke-opacity="0.45"/>
|
||||
<text x="44" y="608" font-size="17" font-weight="700" fill="#d97706">⑤ 驱动层</text>
|
||||
<text x="134" y="608" font-size="12" fill="#64748b">Drivers/BSP · 芯片时序/中断</text>
|
||||
<rect x="44" y="620" width="178" height="38" rx="8" fill="#ffffff" stroke="#d97706" stroke-opacity="0.55"/>
|
||||
<text x="133" y="643" font-size="13" fill="#1e293b" text-anchor="middle">ch395f(SPI2 + DMA)</text>
|
||||
<rect x="232" y="620" width="163" height="38" rx="8" fill="#ffffff" stroke="#d97706" stroke-opacity="0.55"/>
|
||||
<text x="314" y="643" font-size="13" fill="#1e293b" text-anchor="middle">gd5f2gq5ue(SPI1)</text>
|
||||
<rect x="406" y="620" width="195" height="38" rx="8" fill="#ffffff" stroke="#d97706" stroke-opacity="0.55"/>
|
||||
<text x="503" y="643" font-size="13" fill="#1e293b" text-anchor="middle">tpafe5160(并行 16 位)</text>
|
||||
<rect x="611" y="620" width="133" height="38" rx="8" fill="#ffffff" stroke="#d97706" stroke-opacity="0.55"/>
|
||||
<text x="677" y="643" font-size="13" fill="#1e293b" text-anchor="middle">sd2506(I2C1)</text>
|
||||
<rect x="754" y="620" width="171" height="38" rx="8" fill="#ffffff" stroke="#d97706" stroke-opacity="0.55"/>
|
||||
<text x="840" y="643" font-size="13" fill="#1e293b" text-anchor="middle">rs485(UART5 + DE)</text>
|
||||
<rect x="26" y="718" width="1508" height="92" rx="12" fill="#f1f5f9" stroke="#475569" stroke-opacity="0.45"/>
|
||||
<text x="44" y="744" font-size="17" font-weight="700" fill="#475569">⑥ 内核 / HAL</text>
|
||||
<text x="176" y="744" font-size="12" fill="#64748b">调度 · 外设抽象</text>
|
||||
<rect x="44" y="756" width="329" height="38" rx="8" fill="#ffffff" stroke="#475569" stroke-opacity="0.55"/>
|
||||
<text x="208" y="779" font-size="13" fill="#1e293b" text-anchor="middle">FreeRTOS V10.3.1(CMSIS-RTOS2 · heap_4)</text>
|
||||
<rect x="383" y="756" width="189" height="38" rx="8" fill="#ffffff" stroke="#475569" stroke-opacity="0.55"/>
|
||||
<text x="478" y="779" font-size="13" fill="#1e293b" text-anchor="middle">STM32 HAL(时基 TIM7)</text>
|
||||
<rect x="582" y="756" width="144" height="38" rx="8" fill="#ffffff" stroke="#475569" stroke-opacity="0.55"/>
|
||||
<text x="655" y="779" font-size="13" fill="#1e293b" text-anchor="middle">CMSIS / 启动文件</text>
|
||||
<rect x="26" y="854" width="1508" height="92" rx="12" fill="#fff1f2" stroke="#e11d48" stroke-opacity="0.45"/>
|
||||
<text x="44" y="880" font-size="17" font-weight="700" fill="#e11d48">⑦ 硬件</text>
|
||||
<text x="117" y="880" font-size="12" fill="#64748b">STM32F407ZGTx @168MHz</text>
|
||||
<rect x="44" y="892" width="179" height="38" rx="8" fill="#ffffff" stroke="#e11d48" stroke-opacity="0.55"/>
|
||||
<text x="133" y="915" font-size="13" fill="#1e293b" text-anchor="middle">CH395F 以太网(SPI2)</text>
|
||||
<rect x="233" y="892" width="201" height="38" rx="8" fill="#ffffff" stroke="#e11d48" stroke-opacity="0.55"/>
|
||||
<text x="334" y="915" font-size="13" fill="#1e293b" text-anchor="middle">GD5F2GQ5UE NAND(SPI1)</text>
|
||||
<rect x="445" y="892" width="182" height="38" rx="8" fill="#ffffff" stroke="#e11d48" stroke-opacity="0.55"/>
|
||||
<text x="536" y="915" font-size="13" fill="#1e293b" text-anchor="middle">TPAFE5160 ADC(并行)</text>
|
||||
<rect x="637" y="892" width="163" height="38" rx="8" fill="#ffffff" stroke="#e11d48" stroke-opacity="0.55"/>
|
||||
<text x="718" y="915" font-size="13" fill="#1e293b" text-anchor="middle">SD2506 RTC(I2C1)</text>
|
||||
<rect x="810" y="892" width="140" height="38" rx="8" fill="#ffffff" stroke="#e11d48" stroke-opacity="0.55"/>
|
||||
<text x="881" y="915" font-size="13" fill="#1e293b" text-anchor="middle">RS-485(UART5)</text>
|
||||
<rect x="961" y="892" width="182" height="38" rx="8" fill="#ffffff" stroke="#e11d48" stroke-opacity="0.55"/>
|
||||
<text x="1052" y="915" font-size="13" fill="#1e293b" text-anchor="middle">USART1 调试(115200)</text>
|
||||
<rect x="1153" y="892" width="77" height="38" rx="8" fill="#ffffff" stroke="#e11d48" stroke-opacity="0.55"/>
|
||||
<text x="1192" y="915" font-size="13" fill="#1e293b" text-anchor="middle">6x LED</text>
|
||||
<line x1="780.0" y1="176.0" x2="780.0" y2="199.0" stroke="#94a3b8" stroke-width="2.5"/>
|
||||
<path d="M 780.0 206.0 l -6 -9 h 12 z" fill="#94a3b8"/>
|
||||
<line x1="396.5" y1="408.0" x2="396.5" y2="431.0" stroke="#94a3b8" stroke-width="2.5"/>
|
||||
<path d="M 396.5 438.0 l -6 -9 h 12 z" fill="#94a3b8"/>
|
||||
<line x1="1163.5" y1="408.0" x2="1163.5" y2="431.0" stroke="#94a3b8" stroke-width="2.5"/>
|
||||
<path d="M 1163.5 438.0 l -6 -9 h 12 z" fill="#94a3b8"/>
|
||||
<line x1="780.0" y1="544.0" x2="780.0" y2="567.0" stroke="#94a3b8" stroke-width="2.5"/>
|
||||
<path d="M 780.0 574.0 l -6 -9 h 12 z" fill="#94a3b8"/>
|
||||
<line x1="780.0" y1="680.0" x2="780.0" y2="703.0" stroke="#94a3b8" stroke-width="2.5"/>
|
||||
<path d="M 780.0 710.0 l -6 -9 h 12 z" fill="#94a3b8"/>
|
||||
<line x1="780.0" y1="816.0" x2="780.0" y2="839.0" stroke="#94a3b8" stroke-width="2.5"/>
|
||||
<path d="M 780.0 846.0 l -6 -9 h 12 z" fill="#94a3b8"/>
|
||||
<text x="26" y="982" font-size="12" fill="#94a3b8">上层只依赖相邻下层(单向);UI 层与基础服务层同级并列,均使用顶层 net_cfg。跨层调用细节见 docs/项目架构总览.md §1.2 * 预留未接入调度</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 11 KiB |
Reference in New Issue
Block a user