fix(BSP): SPI1 完成标志收回 GD5F 私有,HAL 回调改契约转调

推荐清单第 4a 步(结构债 S-2 剩余面里唯一带"静默失效"属性的那条)。

**问题不是风格**:HAL 的 SPI 完成/错误回调是**全局唯一符号**,整个工程只能有一份
实现,当前由 CH395F 代管并按 `hspi->Instance` 分发——于是 GD5F 的完成标志
`g_spi1_dma_done` 被 CH395F 跨模块裸 `extern` 直接写,**GD5F 出错时的"释放等待者"
寄居在别人的 .c 里**。谁哪天做"CH395F 清理",把那两个 `else if (SPI1)` 分支当无关
代码删掉:编译零报错,GD5F 的 DMA 等待在出错时只能干等 `GD5F_SPI_TIMEOUT` 兜底。

**改法(时序一字不动,只换所有权)**:
· 标志收为 GD5F 文件内 `static volatile uint8_t s_spi1_dma_done`(6 处引用同步改名)
· GD5F 导出 `gd5f_spi1_notify_done()` / `gd5f_spi1_notify_error()`,头文件里写明
  "SPI1 分支必须转调这两个函数,否则本模块的 DMA 等待在出错时只能靠超时兜底"
· CH395F 删除裸 `extern`、include `gd5f2gq5ue.h`、两个 SPI1 分支改为转调并就地注明
  "勿删:少了这行 GD5F 的 DMA 出错只能靠超时退出"

`HAL_SPI_AbortCpltCallback`(`HAL_SPI_DMAStop()` 会触发)本次**刻意不做**——它会改
变中止语义、需要真机专项,当前超时兜底可用(已在报告 §0.4 记为观察项)。

**体量代价**:`ch395f.o` −8 B、`gd5f2gq5ue.o` +24 B → `Code` 213,188 → **213,204 B
(+16 B)**,RO/RW/ZI 全不变,`ER_IROM1` 231,768 → **231,784 B(22.1%)**。16 B 换
掉一个静默失效类,划算。

**真机 `@test` PASS**:无 `[ERR]`/Fault,复位原因 software;`[STACK] heap
free=15800 min_ever=8464` 与改前**逐项相同**;各任务实测峰值 default 2152 / net
1116 / ftp 764 / ui 4052 / timesync 616 / adc 952 / iec104 540 / protect 624,
**静态最坏链全部未变**(3024/1912/1456/4052/1264/1320/1456/872)→ 9/9 PASS。

⚠ 记一条读数陷阱:测试脚本打印的 "boot to net init: OK = 7,907 ms" 是采集窗口
锚点造成的度量偏差,真机日志里 `boot: syslog init OK → net init: OK` 实测 ≈699 ms,
与改前一致,**不是性能回退**。

**数字联动**:`data/ram_report_20261022.md`、`data/stack_report_20261022.md` 重跑;
`ram_baseline.py --update/--write`;`check_mem_numbers.py --write`(RAM 方案受管区 +
台账);`gen_memory_svg.py` 常量更新后重新生成 FLASH 与任务栈两张配图(逐文本按
字号与锚点复查越界 = 0;RAM 图无需变更);报告 §1.3/§5.1/§5.4、部署文档 §3.3 的
Flash 数字同步;§0.4/§9 的 S-2 口径改为"24 处手写已消 23 处,只剩
`net_socket.c:77`"。

门禁:`@build` 0 错 0 警告;`cubemx-survival --check`、`mem-numbers`、`mem-budget`
(57.4% / 87.9%)、`task-table`、`doc-links --all --sections-strict`(146 文档 0 问题)、
`id-check --strict`(增量拦截通过、可判定 82.83%)全绿;字节卫生 BOM/CR/U+FFFD/尾
空格全清;未提交 UV4 抖动的 `.uvprojx`(本次未被改写)。未推送。
This commit is contained in:
2026-10-22 19:21:08 +08:00
parent 36a3c3449c
commit 7e77af08a0
13 changed files with 131 additions and 71 deletions
+3 -5
View File
@@ -20,6 +20,7 @@
#include <stdio.h>
#include "ch395f.h"
#include "spi.h"
#include "gd5f2gq5ue.h" /* SPI1 分支转调 gd5f_spi1_notify_*(HAL SPI 回调全局唯一,本文件代管分发) */
#include "usart.h"
/*
@@ -173,14 +174,11 @@ static int ch395f_spi_dma_transfer(uint16_t len) {
* 入口参数:hspi - SPI句柄指针 SPI_HandleTypeDef*
* 函数说明:由HAL SPI驱动在DMA TX+RX完成后调用
*/
/* SPI1 DMA完成标志(定义在gd5f2gq5ue.c中) */
extern volatile uint8_t g_spi1_dma_done;
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi) {
if (hspi->Instance == SPI2) {
s_spi2_dma_done = 1U;
} else if (hspi->Instance == SPI1) {
g_spi1_dma_done = 1U;
gd5f_spi1_notify_done(); /* 归 GD5F 所有:本文件只代 HAL 分发,不碰它的标志 */
}
}
@@ -193,7 +191,7 @@ void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi) {
if (hspi->Instance == SPI2) {
s_spi2_dma_done = 1U;
} else if (hspi->Instance == SPI1) {
g_spi1_dma_done = 1U;
gd5f_spi1_notify_error(); /* 勿删:少了这行 GD5F 的 DMA 出错只能靠超时退出 */
}
}
+37 -9
View File
@@ -45,8 +45,14 @@ static uint8_t s_bbt_best[GD5F_BBT_SIZE]; /* 最高版本位图缓存 */
static const uint8_t s_bbt_magic[4] = { 'G', 'B', 'B', 'T' }; /* BBT 存储魔数 "GBBT" */
/* DMA 完成标志(ch395f.c 中的 HAL_SPI_TxRxCpltCallback 会设置此标志) */
volatile uint8_t g_spi1_dma_done;
/* DMA 完成标志(本模块私有,2026-10-22 收编):
* HAL 的 SPI 完成/错误回调是**全局唯一符号**(整个工程只能有一份实现),当前由
* Drivers/BSP/CH395F/ch395f.c 实现并按 hspi->Instance 分发——SPI1 分支必须转调本
* 模块导出的 gd5f_spi1_notify_done() / gd5f_spi1_notify_error(),不得反过来由
* CH395F 直接写本模块的变量(原先是跨模块裸 extern,删掉那两行不会报错,只会让
* 下面的 DMA 等待循环在出错时退化成干等 GD5F_SPI_TIMEOUT)。
*/
static volatile uint8_t s_spi1_dma_done;
/* DMA 阈值(字节数超过此值使用 DMA 传输):取值定义在头文件,理由如下——
* 阈值以下走 CPU 轮询。存在把**栈上小缓冲**直接传进本驱动做收发的大小
@@ -68,11 +74,11 @@ volatile uint8_t g_spi1_dma_done;
* 入口参数:hspi - 触发回调的 SPI 句柄 SPI_HandleTypeDef*
* 返回值:无
* 限定条件:由 HAL DMA 中断调用
* 函数说明:仅当 SPI1 传输完成时置位 g_spi1_dma_done 标志
* 函数说明:仅当 SPI1 传输完成时置位 s_spi1_dma_done 标志
*/
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi) {
if (hspi->Instance == SPI1) {
g_spi1_dma_done = 1U;
s_spi1_dma_done = 1U;
}
}
@@ -84,10 +90,32 @@ void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi) {
*/
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi) {
if (hspi->Instance == SPI1) {
g_spi1_dma_done = 1U;
s_spi1_dma_done = 1U;
}
}
/*
* 函数功能:SPI1 DMA 完成通知(供实际注册 HAL 回调的一方转调)
* 入口参数:无
* 返回值:无
* 限定条件:由 HAL SPI 回调(中断上下文)调用;只写一个标志,不阻塞
* 函数说明:HAL 的 SPI 回调在整个工程里只能有一份实现,本模块不抢占 Error 回调
* 的所有权,只导出这两个通知入口。**转调方不得省略本调用**——少了它,
* 出错路径只能靠 GD5F_SPI_TIMEOUT 超时退出(能自保但会静默变慢)。
*/
void gd5f_spi1_notify_done(void) {
s_spi1_dma_done = 1U;
}
/*
* 函数功能:SPI1 DMA 错误通知(释放等待者;语义与 notify_done 相同)
* 入口参数:无
* 返回值:无
* 限定条件:由 HAL SPI 错误回调(中断上下文)调用
*/
void gd5f_spi1_notify_error(void) {
s_spi1_dma_done = 1U;
}
/* ======================== 私有函数声明 ======================== */
static int gd5f_page_program(uint32_t page_addr, uint16_t column,
@@ -318,12 +346,12 @@ int32_t gd5f_read_from_cache(uint16_t column, uint8_t *p_buf,
if (use_dma != 0U) {
uint32_t tick_start = HAL_GetTick();
g_spi1_dma_done = 0U;
s_spi1_dma_done = 0U;
if (HAL_SPI_Receive_DMA(&hspi1, p_buf, (uint16_t)size) != HAL_OK) {
GD5F_CS_HIGH();
return GD5F_ERROR;
}
while (g_spi1_dma_done == 0U) {
while (s_spi1_dma_done == 0U) {
if ((HAL_GetTick() - tick_start) >= GD5F_SPI_TIMEOUT) {
HAL_SPI_DMAStop(&hspi1);
GD5F_CS_HIGH();
@@ -399,13 +427,13 @@ int32_t gd5f_program_load(uint16_t column, const uint8_t *p_buf, size_t size) {
if (use_dma != 0U) {
uint32_t tick_start = HAL_GetTick();
g_spi1_dma_done = 0U;
s_spi1_dma_done = 0U;
if (HAL_SPI_Transmit_DMA(&hspi1, (uint8_t *)p_buf, (uint16_t)size)
!= HAL_OK) {
GD5F_CS_HIGH();
return GD5F_ERROR;
}
while (g_spi1_dma_done == 0U) {
while (s_spi1_dma_done == 0U) {
if ((HAL_GetTick() - tick_start) >= GD5F_SPI_TIMEOUT) {
HAL_SPI_DMAStop(&hspi1);
GD5F_CS_HIGH();
+13
View File
@@ -324,6 +324,19 @@ void nand_ftl_wd_beat(void);
*/
void nand_ftl_wd_feed(void);
/*
* 函数功能:SPI1 DMA 完成 / 错误通知(由实际注册 HAL SPI 回调的模块转调)
* 入口参数:无
* 返回值:无
* 限定条件:中断上下文(HAL SPI 回调内),两者只写一个完成标志
* 函数说明:HAL 的 SPI 完成/错误回调是全局唯一符号,本模块只实现 TxCplt/RxCplt
* 两个(供自己的收发用),Error 回调的所有权留给回调的实际注册方
* (当前 = Drivers/BSP/CH395F/ch395f.c,按 hspi->Instance 分发)。**SPI1
* 分支必须转调这两个函数**,否则本模块的 DMA 等待在出错时只能靠超时
* 兜底。2026-10-22:取代原先被 CH395F 跨模块裸 extern 直接写的全局变量。
*/
void gd5f_spi1_notify_done(void);
void gd5f_spi1_notify_error(void);
#ifdef __cplusplus
}
#endif
+4 -4
View File
@@ -13,7 +13,7 @@
<text x="186" y="72" text-anchor="end" dominant-baseline="central" class="label" fill="#333">defaultTask</text>
<rect class="bar-seg" x="190" y="64" width="203.1" height="16" fill="#BBDEFB" rx="3"/>
<rect x="190" y="64" width="120.0" height="16" fill="#2196F3" rx="3"/>
<text x="190" y="91" dominant-baseline="central" class="legend-text" fill="#333">配置 5120B | 实测峰值 2164B | 最坏 3024B | 余量 2096B (40%) <tspan fill="#2E7D32" font-weight="bold">PASS</tspan></text>
<text x="190" y="91" dominant-baseline="central" class="legend-text" fill="#333">配置 5120B | 实测峰值 2152B | 最坏 3024B | 余量 2096B (40%) <tspan fill="#2E7D32" font-weight="bold">PASS</tspan></text>
<text x="186" y="112" text-anchor="end" dominant-baseline="central" class="label" fill="#333">netTask</text>
<rect class="bar-seg" x="190" y="104" width="152.3" height="16" fill="#BBDEFB" rx="3"/>
<rect x="190" y="104" width="75.9" height="16" fill="#2196F3" rx="3"/>
@@ -21,7 +21,7 @@
<text x="186" y="152" text-anchor="end" dominant-baseline="central" class="label" fill="#333">ftpTask</text>
<rect class="bar-seg" x="190" y="144" width="162.5" height="16" fill="#BBDEFB" rx="3"/>
<rect x="190" y="144" width="57.8" height="16" fill="#2196F3" rx="3"/>
<text x="190" y="171" dominant-baseline="central" class="legend-text" fill="#333">配置 4096B | 实测峰值 756B | 最坏 1456B | 余量 2640B (64%) <tspan fill="#2E7D32" font-weight="bold">PASS</tspan></text>
<text x="190" y="171" dominant-baseline="central" class="legend-text" fill="#333">配置 4096B | 实测峰值 764B | 最坏 1456B | 余量 2640B (64%) <tspan fill="#2E7D32" font-weight="bold">PASS</tspan></text>
<text x="186" y="192" text-anchor="end" dominant-baseline="central" class="label" fill="#333">uiTask</text>
<rect class="bar-seg" x="190" y="184" width="650.0" height="16" fill="#FFE0B2" rx="3"/>
<rect x="190" y="184" width="160.8" height="16" fill="#FF9800" rx="3"/>
@@ -41,11 +41,11 @@
<text x="186" y="352" text-anchor="end" dominant-baseline="central" class="label" fill="#333">iec104Task</text>
<rect class="bar-seg" x="190" y="344" width="101.6" height="16" fill="#BBDEFB" rx="3"/>
<rect x="190" y="344" width="57.8" height="16" fill="#2196F3" rx="3"/>
<text x="190" y="371" dominant-baseline="central" class="legend-text" fill="#333">配置 2560B | 实测峰值 544B | 最坏 1456B | 余量 1104B (43%) <tspan fill="#2E7D32" font-weight="bold">PASS</tspan></text>
<text x="190" y="371" dominant-baseline="central" class="legend-text" fill="#333">配置 2560B | 实测峰值 540B | 最坏 1456B | 余量 1104B (43%) <tspan fill="#2E7D32" font-weight="bold">PASS</tspan></text>
<text x="186" y="392" text-anchor="end" dominant-baseline="central" class="label" fill="#333">protectTask</text>
<rect class="bar-seg" x="190" y="384" width="81.2" height="16" fill="#BBDEFB" rx="3"/>
<rect x="190" y="384" width="34.6" height="16" fill="#2196F3" rx="3"/>
<text x="190" y="411" dominant-baseline="central" class="legend-text" fill="#333">配置 2048B | 实测峰值 828B | 最坏 872B | 余量 1176B (57%) <tspan fill="#2E7D32" font-weight="bold">PASS</tspan></text>
<text x="190" y="411" dominant-baseline="central" class="legend-text" fill="#333">配置 2048B | 实测峰值 624B | 最坏 872B | 余量 1176B (57%) <tspan fill="#2E7D32" font-weight="bold">PASS</tspan></text>
<text x="186" y="434" text-anchor="end" dominant-baseline="central" class="label" fill="#333">idle</text>
<rect class="bar-seg" x="190" y="428" width="20.3" height="12" fill="#90A4AE" rx="2"/>
<text x="214.3125" y="434" dominant-baseline="central" class="legend-text" fill="#333">512B(未实测)</text>

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

+16 -16
View File
@@ -10,32 +10,32 @@
<text x="410.0" y="24" text-anchor="middle" class="title">Flash (1MB) 使用布局 — 2026-10-22 @build</text>
<rect class="bar-seg" x="60" y="50" width="150.5" height="40" fill="#2196F3" rx="2"/>
<text x="135.2" y="70.0" text-anchor="middle" dominant-baseline="central" class="label" fill="#000">代码段 (Code) 208.2KB</text>
<rect class="bar-seg" x="210.45082092285156" y="50" width="13.1" height="40" fill="#4CAF50" rx="2"/>
<rect class="bar-seg" x="210.4621124267578" y="50" width="13.1" height="40" fill="#4CAF50" rx="2"/>
<text x="227.6" y="70.0" text-anchor="start" dominant-baseline="central" class="legend-text" fill="#333">只读数据 (RO) 18.1KB</text>
<rect class="bar-seg" x="223.56307983398438" y="50" width="575.6" height="40" fill="#E0E0E0" rx="2"/>
<rect class="bar-seg" x="223.57437133789062" y="50" width="575.6" height="40" fill="#E0E0E0" rx="2"/>
<text x="511.4" y="70.0" text-anchor="middle" dominant-baseline="central" class="label" fill="#000">Flash 空闲 796.5KB</text>
<text x="60" y="108" class="label" fill="#333">已用: 227.5KB (22.2%) 空闲: 796.5KB (77.8%) ER_IROM1(Code+RO): 226.3KB (22.1%) 总量: 1MB (= ROM 合计口径)</text>
<text x="60" y="124" class="label" fill="#555">Code + RO 分布(ER_IROM1 226.3KB,按 .map 对象归类)</text>
<text x="60" y="108" class="label" fill="#333">已用: 227.5KB (22.2%) 空闲: 796.5KB (77.8%) ER_IROM1(Code+RO): 226.4KB (22.1%) 总量: 1MB (= ROM 合计口径)</text>
<text x="60" y="124" class="label" fill="#555">Code + RO 分布(ER_IROM1 226.4KB,按 .map 对象归类)</text>
<rect class="bar-seg" x="60" y="130" width="280.0" height="24" fill="#4CAF50" rx="2"/>
<text x="200.0" y="142.0" text-anchor="middle" dominant-baseline="central" class="label" fill="#000">85.7KB</text>
<rect class="bar-seg" x="340.0319284802044" y="130" width="182.0" height="24" fill="#2196F3" rx="2"/>
<rect class="bar-seg" x="340.0125979360094" y="130" width="182.0" height="24" fill="#2196F3" rx="2"/>
<text x="431.0" y="142.0" text-anchor="middle" dominant-baseline="central" class="label" fill="#000">55.7KB</text>
<rect class="bar-seg" x="522.0593869731802" y="130" width="81.9" height="24" fill="#F44336" rx="2"/>
<text x="563.0" y="142.0" text-anchor="middle" dominant-baseline="central" class="label" fill="#000">25.1KB</text>
<rect class="bar-seg" x="603.9623243933589" y="130" width="57.1" height="24" fill="#607D8B" rx="2"/>
<text x="632.5" y="142.0" text-anchor="middle" dominant-baseline="central" class="label" fill="#000">17.5KB</text>
<rect class="bar-seg" x="661.0376756066412" y="130" width="34.8" height="24" fill="#FF9800" rx="2"/>
<rect class="bar-seg" x="695.8620689655173" y="130" width="34.4" height="24" fill="#00BCD4" rx="2"/>
<rect class="bar-seg" x="730.2234993614305" y="130" width="27.0" height="24" fill="#9C27B0" rx="2"/>
<rect class="bar-seg" x="757.2605363984676" y="130" width="17.5" height="24" fill="#795548" rx="2"/>
<rect class="bar-seg" x="774.7765006385698" y="130" width="1.5" height="24" fill="#B0BEC5" rx="2"/>
<rect class="bar-seg" x="776.2707535121331" y="130" width="23.7" height="24" fill="#E0E0E0" rx="2"/>
<rect class="bar-seg" x="522.027491112415" y="130" width="81.9" height="24" fill="#F44336" rx="2"/>
<text x="563.0" y="142.0" text-anchor="middle" dominant-baseline="central" class="label" fill="#000">25.0KB</text>
<rect class="bar-seg" x="603.8992337693714" y="130" width="57.1" height="24" fill="#607D8B" rx="2"/>
<text x="632.4" y="142.0" text-anchor="middle" dominant-baseline="central" class="label" fill="#000">17.5KB</text>
<rect class="bar-seg" x="660.9706450833535" y="130" width="34.9" height="24" fill="#FF9800" rx="2"/>
<rect class="bar-seg" x="695.8692575846478" y="130" width="34.4" height="24" fill="#00BCD4" rx="2"/>
<rect class="bar-seg" x="730.228316018362" y="130" width="27.0" height="24" fill="#9C27B0" rx="2"/>
<rect class="bar-seg" x="757.2634866945087" y="130" width="17.5" height="24" fill="#795548" rx="2"/>
<rect class="bar-seg" x="774.7782418113417" y="130" width="1.5" height="24" fill="#B0BEC5" rx="2"/>
<rect class="bar-seg" x="776.2723915369482" y="130" width="23.7" height="24" fill="#E0E0E0" rx="2"/>
<rect x="60" y="167" width="10" height="10" fill="#4CAF50" rx="2"/>
<text x="74" y="176" class="legend-text" fill="#333">LVGL 库 85.7KB</text>
<rect x="255" y="167" width="10" height="10" fill="#2196F3" rx="2"/>
<text x="269" y="176" class="legend-text" fill="#333">应用代码/UI 55.7KB</text>
<rect x="450" y="167" width="10" height="10" fill="#F44336" rx="2"/>
<text x="464" y="176" class="legend-text" fill="#333">CH395F+FTP 25.1KB</text>
<text x="464" y="176" class="legend-text" fill="#333">CH395F+FTP 25.0KB</text>
<rect x="645" y="167" width="10" height="10" fill="#607D8B" rx="2"/>
<text x="659" y="176" class="legend-text" fill="#333">HAL 驱动 17.5KB</text>
<rect x="60" y="183" width="10" height="10" fill="#FF9800" rx="2"/>

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

@@ -38,10 +38,10 @@
<!-- MEM_SNAPSHOT:BEGIN generated by scripts/check_mem_numbers.py -->
| 区域 | 用量 | 容量 | 占比 |
|---|---|---|---|
| ER_IROM1 | 231,768 B (226.34 KB) | 1,048,576 B (1024 KB) | 22.1% |
| ER_IROM1 | 231,784 B (226.35 KB) | 1,048,576 B (1024 KB) | 22.1% |
| RW_IRAM1 | 75,264 B (73.50 KB) | 131,072 B (128 KB) | 57.4% |
| RW_CCM | 57,376 B (56.03 KB) | 65,280 B (64 KB) | 87.9% |
| **Flash (ROM 合计)** | **232,952 B (227.5 KB)** | 1,048,576 B (1024 KB) | **22.2%** |
| **Flash (ROM 合计)** | **232,968 B (227.5 KB)** | 1,048,576 B (1024 KB) | **22.2%** |
<!-- MEM_SNAPSHOT:END -->
各区的容量与常量来源:
+1 -1
View File
@@ -15,7 +15,7 @@
<!-- MEM_BUDGET:BEGIN generated by scripts/ram_baseline.py -->
| 区域 | 用量 | 容量 | 占比 | 门禁(≤92%) |
|---|---|---|---|---|
| ER_IROM1 (Flash) | 231,768 B | 1,048,576 B | 22.1% | 参考 |
| ER_IROM1 (Flash) | 231,784 B | 1,048,576 B | 22.1% | 参考 |
| RW_CCM | 57,376 B | 65,280 B | 87.9% | PASS |
| UNINIT_CCM | 48 B | 256 B | 18.8% | 参考 |
| RW_IRAM1 | 75,264 B | 131,072 B | 57.4% | PASS |
+12 -12
View File
@@ -1,24 +1,24 @@
# STM32F4-Base RAM/Flash Usage Report
> Generated: 2026-10-22 12:06 Map: `STM32F407-Demo.map`
> Generated: 2026-10-22 19:14 Map: `STM32F407-Demo.map`
## 1. Grand Totals (全局总量)
| Metric | Value |
|---|---|
| Code (inc. data) | 213,188 B (208.2 KB) |
| Code (inc. data) | 213,204 B (208.2 KB) |
| RO Data | 18,580 B (18.1 KB) |
| RW Data (initialized) | 1,184 B (1.16 KB) |
| ZI Data (zero-init) | 131,504 B (128.4 KB) |
| **Total RO (Code + RO)** | **231,768 B (226.3 KB)** |
| **Total RO (Code + RO)** | **231,784 B (226.4 KB)** |
| **Total RW (RW + ZI) = SRAM used** | **132,688 B (129.58 KB)** |
| **Total ROM (Code + RO + RW init)** | **232,952 B (227.5 KB)** |
| **Total ROM (Code + RO + RW init)** | **232,968 B (227.5 KB)** |
## 2. 执行区水位 (Execution Regions)
| Region | Base | Size | Max | Util% |
|---|---|---|---|---|
| ER_IROM1 | 0x08000000 | 231,768 B (226.34 KB) | 1,048,576 B (1024 KB) | 22.1% |
| ER_IROM1 | 0x08000000 | 231,784 B (226.35 KB) | 1,048,576 B (1024 KB) | 22.1% |
| RW_CCM | 0x10000000 | 57,376 B (56.03 KB) | 65,280 B (64 KB) | 87.9% |
| UNINIT_CCM | 0x1000FF00 | 48 B (0.05 KB) | 256 B (0 KB) | 18.8% |
| RW_IRAM1 | 0x20000000 | 75,264 B (73.50 KB) | 131,072 B (128 KB) | 57.4% |
@@ -140,7 +140,7 @@
| 4 | ui_net_sink.o | 1,126 | 67 | 20 | 6,746 | 6,766 |
| 5 | ui_port.o | 418 | 0 | 12 | 6,408 | 6,420 |
| 6 | nand_ftl.o | 2,352 | 175 | 16 | 6,200 | 6,216 |
| 7 | ch395f.o | 3,986 | 603 | 10 | 4,152 | 4,162 |
| 7 | ch395f.o | 3,978 | 603 | 10 | 4,152 | 4,162 |
| 8 | console_log.o | 980 | 55 | 24 | 4,096 | 4,120 |
| 9 | sys_log_mcu.o | 3,074 | 83 | 36 | 3,552 | 3,588 |
| 10 | default_task.o | 2,332 | 280 | 12 | 2,724 | 2,736 |
@@ -150,7 +150,7 @@
| 14 | cfg_file_store_mcu.o | 380 | 24 | 0 | 1,104 | 1,104 |
| 15 | soe_store.o | 816 | 28 | 20 | 1,064 | 1,084 |
| 16 | startup_stm32f407xx.o | 36 | 392 | 0 | 1,024 | 1,024 |
| 17 | gd5f2gq5ue.o | 3,930 | 478 | 20 | 800 | 820 |
| 17 | gd5f2gq5ue.o | 3,954 | 478 | 20 | 800 | 820 |
| 18 | iec104_serv.o | 1,188 | 16 | 16 | 712 | 728 |
| 19 | cfg_file.o | 5,970 | 810 | 20 | 704 | 724 |
| 20 | spi.o | 584 | 0 | 0 | 560 | 560 |
@@ -204,7 +204,7 @@
| 68 | vol_lock.o | 92 | 0 | 4 | 0 | 4 |
| 69 | errno.o | 20 | 0 | 4 | 0 | 4 |
| 70 | ui_page_net_cfg.o | 1,004 | 105 | 2 | 0 | 2 |
| | **All .o files** | **213,188** | **18,580** | **1,184** | **131,504** | **132,688** |
| | **All .o files** | **213,204** | **18,580** | **1,184** | **131,504** | **132,688** |
## 5. Flash (Code + RO Data) Top-30(按大小降序)
@@ -215,11 +215,11 @@
| 3 | lftpd.o | 6,108 | 509 | 6,617 |
| 4 | net_socket.o | 4,916 | 443 | 5,359 |
| 5 | lv_indev.o | 4,928 | 0 | 4,928 |
| 6 | ch395f.o | 3,986 | 603 | 4,589 |
| 6 | ch395f.o | 3,978 | 603 | 4,581 |
| 7 | ui_font_cn_12.o | 0 | 4,545 | 4,545 |
| 8 | lv_obj_pos.o | 4,526 | 16 | 4,542 |
| 9 | lv_refr.o | 4,486 | 0 | 4,486 |
| 10 | gd5f2gq5ue.o | 3,930 | 478 | 4,408 |
| 10 | gd5f2gq5ue.o | 3,954 | 478 | 4,432 |
| 11 | tasks.o | 4,342 | 0 | 4,342 |
| 12 | lv_obj_scroll.o | 4,126 | 0 | 4,126 |
| 13 | lv_obj_style.o | 4,106 | 0 | 4,106 |
@@ -240,6 +240,6 @@
| 28 | printfa.o | 2,312 | 0 | 2,312 |
| 29 | stm32f4xx_hal_i2c.o | 2,294 | 0 | 2,294 |
| 30 | lv_display.o | 2,284 | 0 | 2,284 |
| | **All .o files** | **213,188** | **18,580** | **231,768** |
| | **All .o files** | **213,204** | **18,580** | **231,784** |
> 自洽校验:per-file 213,188/18,580/1,184/131,504 vs Grand Totals 213,188/18,580/1,184/131,504(CodeΔ=+0 / ROΔ=+0 / RWΔ=+0 / ZIΔ=+0;精确一致(含 '('incl. Generated)'-'(incl. Padding)' 台账行))→ **PASS**
> 自洽校验:per-file 213,204/18,580/1,184/131,504 vs Grand Totals 213,204/18,580/1,184/131,504(CodeΔ=+0 / ROΔ=+0 / RWΔ=+0 / ZIΔ=+0;精确一致(含 '('incl. Generated)'-'(incl. Padding)' 台账行))→ **PASS**
@@ -1,7 +1,7 @@
# Stack budget report
- Source: `MDK-ARM\STM32F407-Demo\STM32F407-Demo.htm` (Keil static call graph)
- Linker run: Thu Oct 22 10:36:17 2026
- Linker run: Thu Oct 22 19:12:02 2026
- Global maximum stack usage: **3024 B** (any call tree)
- Runtime watermark: 8 task(s) from 串口采集日志 `.dsh\skills\serial-assistant\scripts\state\serial.log`
@@ -9,15 +9,15 @@ Rule: `margin = cfg - max(static_max_depth, runtime_peak)` must be >= max(1024 B
| Task | Entry | Cfg (B) | Static max (B) | Runtime peak (B) | Worst (B) | Margin (B) | Margin % | Verdict |
|---|---|---|---|---|---|---|---|---|
| defaultTask | `StartDefaultTask` | 5120 | 3024 | 2164 | 3024 | 2096 | 40% | PASS |
| defaultTask | `StartDefaultTask` | 5120 | 3024 | 2152 | 3024 | 2096 | 40% | PASS |
| netTask | `StartNetTask` | 3840 | 1912 | 1116 | 1912 | 1928 | 50% | PASS |
| ftpTask | `StartFtpTask` | 4096 | 1456 | 756 | 1456 | 2640 | 64% | PASS |
| ftpTask | `StartFtpTask` | 4096 | 1456 | 764 | 1456 | 2640 | 64% | PASS |
| uiTask | `StartUiTask` | 16384 | 1336 | 4052 | 4052 | 12332 | 75% | PASS |
| testTask | `start_test_task` | 8192 | 736 | - | 736 | 7456 | 91% | PASS |
| timeSyncTask | `time_sync_task` | 2560 | 1264 | 616 | 1264 | 1296 | 50% | PASS |
| adcTask | `adc_task_func` | 2560 | 1320 | 952 | 1320 | 1240 | 48% | PASS |
| iec104Task | `iec104_serv_task` | 2560 | 1456 | 544 | 1456 | 1104 | 43% | PASS |
| protectTask | `protect_task` | 2048 | 872 | 828 | 872 | 1176 | 57% | PASS |
| iec104Task | `iec104_serv_task` | 2560 | 1456 | 540 | 1456 | 1104 | 43% | PASS |
| protectTask | `protect_task` | 2048 | 872 | 624 | 872 | 1176 | 57% | PASS |
`Static max` = armlink worst-case call-tree depth; `Runtime peak` = measured high-water mark (boot + whatever workload ran before the sample); `Worst` = max of the two, which is what the verdict is computed from.
@@ -504,7 +504,7 @@ $env:DTU_KEIL_UV4 = 'D:\NotExist\UV4.exe' # 仅当前会话,不动用户级
| 资源 | 已用 | 总量 | 利用率 | 余量 | 红线说明 |
|---|---|---|---|---|---|
| Flash(`ER_IROM1` = Code + RO) | 231,768 B(226.3 KB) | 1 MB | 22.1% | ≈804 KB | **充裕**:产品需求无 OTA / 双镜像预算,Flash 不是卡点;死代码裁剪簇已收口 → `docs/问题记录/项目审查与优化建议报告.md` §5.4 |
| Flash(`ER_IROM1` = Code + RO) | 231,784 B(226.4 KB) | 1 MB | 22.1% | ≈804 KB | **充裕**:产品需求无 OTA / 双镜像预算,Flash 不是卡点;死代码裁剪簇已收口 → `docs/问题记录/项目审查与优化建议报告.md` §5.4 |
| 主 SRAM1(静态区;FreeRTOS 堆已迁 CCM) | 75,264 B(73.5 KB) | 128 KB | 57.4% | 55,808 B | 已不紧张——2026-10-11 堆迁 CCM 与 2026-10-15 `LV_MEM_SIZE` 40→16 KB 两步的结果 |
| CCM(`RW_CCM` 段) | 57,376 B(56.0 KB) | 65,280 B | **87.9%** | **7,904 B** | **当前真正的红线**:加任务 / 抬栈 / 加堆会先在这里撞墙;`mem-budget` 钩子在 92% 拦提交 |
@@ -49,7 +49,7 @@
| 内存 RAM | N-14 🟡(N-22) | FTL 单页写回缓存无空闲/掉电强制 flush(整页写与换页已 flush,`f_close` 经 `CTRL_SYNC` 已落盘) | 用户裁定 2026-10-22 暂不做;残余窗口只是"单条记录" |
| Flash | **F-5 🟡 观察项**(N-28) | uvprojx 201 个 LVGL `.c` 里 **127 个空 TU**;增量构建 4~5 s、CI 不跑 `@build` | 有实测全量耗时痛点再做,用 `<IncludeInBuild>` |
| 结构 | **S-1 ❌**(口径已按实测更正) | **不是"命名错位",是真实的 BSP→App 反向依赖**:`Drivers/BSP/NETCFG/{net_cfg,ftp_cfg}.c` include `App/cfg/cfg_file.h`(10 处调用点:`cfg_file_load/save/poll/register`),`Drivers/BSP/UI` 另有 3 个文件 include App 层头(`ui_page_runlog.c`、`ui_page_selftest.c`、`ui_w_records.h`);21 个 BSP 文件用 `dbg_log.h`(横切,待显式豁免) | **反转依赖**(约 7 文件 / 15 处)。⚠ 修正一条我先前写下的理由:这不是"运行时属主纪律被破"——UI 只投递 `net_cfg_save_request()`,写盘仍在 defaultTask,且 NETCFG 已有 `net_cfg_set_save_req_cb` / `net_cfg_request_apply` 的**回调注入**先例;也不是"测不了"——模拟器今天就在主机侧跑 `net_cfg_load → poll → save` 整条链(`../../simulator/README.md` 的验证链)。要修的只是**静态分层方向**(BSP 引用了 App 的头)+ 加一条「`Drivers/BSP/**` 不得 include `App/**`」的门禁;**目录搬迁暂缓**(远程 CI 仍不跑 `@build`,兜底未成立) |
| 结构 | **S-2 大半已消**(24 处手写已消 22 处) | **该修的手写跨模块裸 `extern` 共 24 处,今天已消 22 处,只剩 2 处**:`ch395f.c:177`(CH395F 裸写 GD5F 的 `g_spi1_dma_done`,还在 `HAL_SPI_ErrorCallback` 的 SPI1 分支里**替 GD5F 释放等待者**——谁清理那两行「看着无关」的分支,编译零报错,GD5F 的 DMA 出错就退化成干等 `GD5F_SPI_TIMEOUT`)与 `net_socket.c:77`(`netMsgQueueHandle` 跨文件裸引)。已消的 22 处 = 2 处与 `Inc/spi.h` / `Inc/i2c.h` 重复(GD5F/SD2506)+ 19 处任务句柄与属性(`default_task.c:52-69` 的 18 行 + `adc_task.c:125`)+ 1 处 `main.c` 函数内的 `g_rcc_csr_latched`。**两类不算债要分清**:`stm32f4xx_it.c:241-256` 那 16 条是 CubeMX「External variables」生成块(删了会被重生成写回来);`:183/195/207/220` 那 4 条是手写在 `__asm` Handler 里引用汇编标签 `fault_entry_*`(ARMCC `__asm` 取不到 C 数据符号,**合法例外**)。上一版写的「25 处」把这两类混进来了,已更正 | 剩余两步:① `ch395f.c` 改为经 GD5F 导出的 `gd5f_spi1_notify_done()` / `notify_error()` 转调(标志收回 GD5F 内部)② `netMsgQueueHandle` 收进 net_socket 内部 + 提供投递 API |
| 结构 | **S-2 只剩 1 处**(24 处手写已消 23) | 今天已消 23 处:2 处与 `Inc/spi.h`/`Inc/i2c.h` 重复(GD5F/SD2506)+ 19 处任务句柄与属性(新建 `App/task/task_handles.h`)+ 1 处 `main.c` 函数内 `g_rcc_csr_latched`(改由 `App/app/app_main.h` 声明)+ 1 处 `ch395f.c:177`(**改为契约转调**:GD5F 的完成标志收为文件内 `static`,导出 `gd5f_spi1_notify_done()` / `gd5f_spi1_notify_error()`,CH395F 那个"HAL 全局唯一回调"的 SPI1 分支只转调、不再直接写它的变量;`HAL_SPI_AbortCpltCallback` 仍无人实现 → 另列观察)。**只剩 `net_socket.c:77` 的 `netMsgQueueHandle`**。**两类不算债**:`stm32f4xx_it.c:241-256` 是 CubeMX「External variables」生成块(删了会被写回来);`:183/195/207/220` 是 `__asm` Handler 里的汇编标签引用(ARMCC `__asm` 取不到 C 数据符号,**合法例外**) | 剩下一步:`netMsgQueueHandle` 收进 net_socket 内部 + 提供投递 API |
| 驱动规范 | S-5 ❌ | `bsp_err_t` 至今只出现在本报告里(无统一错误码/超时/重试约定) | 定义后进 `docs/代码规范/` |
| 网络 | N-09 / PHY ❌(N-25) | `NET_MAX_SOCKETS` 仍为 8(与单连接复用语义不符);PHY 强制 100M 无自协商回退;PASV 每次新建监听 | 见 §4.2;随 N-25 一并处置 |
| 存储 | N-15 🟡(N-25) | `nand_ftl.o` 静态 6,216 B——三块 2 KB 级页缓冲并存未合并 | 与 R-3 同批评估 |
@@ -88,7 +88,7 @@ git ls-files -z | xargs -0 stat -c '%s'
| 名词 | 口径 |
|---|---|
| Flash 占用 | `Code + RO-data` = `ER_IROM1` = **231,768 B(22.1%)**;`ROM 合计` 再加 `RW` 初始化表 = 232,952 B(22.2%) |
| Flash 占用 | `Code + RO-data` = `ER_IROM1` = **231,784 B(22.1%)**;`ROM 合计` 再加 `RW` 初始化表 = 232,968 B(22.2%) |
| SRAM 占用 | `RW_IRAM1`(DMA 可达区)与 `RW_CCM`(仅 CPU)分开计,二者不可互换(规则 R2/R3) |
| 最坏栈 | `max(armlink 静态调用图深度, 真机 [STACK] 高水位)`;判定线 `余量 ≥ max(1024 B ISR, 配置×30%)` |
| 编译单元 | uvprojx 的 `<FilePath>` 计数(今日 **326 个 `.c`**,其中 LVGL 201);"自研 `.c`" 另按目录口径 114 个 |
@@ -225,8 +225,8 @@ defaultTask 单属主;原子写经卷锁;旧档迁移已删除。**仍列为
| 指标 | 2026-10-22 | 2026-10-15 | 说明 |
|---|---|---|---|
| `Code` | 213,188 B | 213,520 B | F-6 收口 −332 B |
| `ER_IROM1`(Code+RO) | **231,768 B / 22.1%** | 232,280 B / 22.2% | 余 ≈804 KB |
| `Code` | 213,204 B | 213,520 B | F-6 收口 −332 B;同日 SPI 回调归属纠正 +16 B |
| `ER_IROM1`(Code+RO) | **231,784 B / 22.1%** | 232,280 B / 22.2% | 余 ≈804 KB |
| `RW_IRAM1` | **75,264 B / 57.4%**(余 55,808) | 73,568 B / 56.1% | 增量来自 B5/B8 功能码 |
| `RW_CCM` | **57,376 B / 87.9%**(余 7,904) | 57,376 B / 87.9% | **当前唯一红线** |
| FreeRTOS 堆 | 56 KB(57,344 B)@CCM;实测 `free=15,800` / `min_ever=8,464` | 同 | 2026-10-22 真机 |
@@ -266,7 +266,7 @@ defaultTask 单属主;原子写经卷锁;旧档迁移已删除。**仍列为
| **F-6** 双精度软浮点 | ≈1 KB | **归因错**:全库 0 处 `%f`;剩余 `dadd/dmul/ddiv/depilogue/dfixul` 1,018 B 锁在 MicroLIB `printfa.o` 的 `_printf_core → _fp_digits`,只要用 `snprintf` 就删不掉。但**根因侧确有可修点**:`App/algo/prot_set.h` 两个 Q10 宏写成 `double` | ✅ **已落地 2026-10-22**:改单精度 → `@build` −332 B、5 个 helper(300 B)消失;数值逐位等价(穷举 42 个字面量 + int32 全域 ÷1024 往返 4.49×10⁶ 例零差异);真机 `@test` PASS |
**当前可回收 Flash = 0 B**(F-4/F-6 已定论,图片链同理不打补丁)。LVGL 仍占镜像 **37.8%**
(87,706 / 231,768 B)——这是"用 LVGL 画单色菜单"的结构性代价,F-1/F-2/F-3 已省掉 64,576 B。
(87,706 / 231,784 B)——这是"用 LVGL 画单色菜单"的结构性代价,F-1/F-2/F-3 已省掉 64,576 B。
### 5.5 内存治理长效机制(常开,不受暂缓影响)
@@ -374,7 +374,7 @@ W6 簇按 U4 → U1 → U3 → U2 的顺序执行完毕(U2 停在"够用即止
| **D-1** | 外部资料与工程文档同仓同树 | ❌ 暂不处理 | 本文 §3.3、登记簿 N-29 |
| D-2 ~ D-7 | 文档治理条目(链接/同源/编号/BOM 等) | ✅ 多数已制度化为钩子 | 本文 §3.2、`../文档规范.md` |
| D-9 | 文档面残余观察 | 🟡 观察 | 本节即定义位 |
| **S-1 / S-2** | 分层反向依赖 / 跨模块裸 `extern` | 🟡 半办(2026-10-22:S-2 的 24 处手写已消 22 处、剩 2 处;S-1 未动 = NETCFG+UI 反向 include) | 本文 §0.4、登记簿 C5 |
| **S-1 / S-2** | 分层反向依赖 / 跨模块裸 `extern` | 🟡 半办(2026-10-22:24 处手写裸 extern 已消 23 处,只剩 `net_socket.c:77`;S-1 未动 = NETCFG+UI 反向 include) | 本文 §0.4、登记簿 C5 |
| **S-4** | `app_cfg.h` 未接线占位开关 | ✅ **已闭环 2026-10-22**(4 个零消费点宏删除) | `App/app/app_cfg.h` 修改记录 v1.2;口径同步见 `../环境与部署/项目部署环境要求.md` §3.2/§3.5、`../产品需求/产品需求设计文档.md`、`../任务规划/排期详述.md` C5 |
| **S-3** | `.ioc` 侧 USART1 配置 | ❌ 在策 | 登记簿 S-3 |
| **S-5** | 统一 `bsp_err_t` 错误码约定 | ❌ 未做 | 本文 §0.4、`docs/代码规范/`(待建) |
@@ -409,6 +409,27 @@ W6 簇按 U4 → U1 → U3 → U2 的顺序执行完毕(U2 停在"够用即止
**改判的三条旧结论**:① §6 的 U1/U3/U4 原写「❌ 未建」,实际已落地(`ui_tokens.h` 69 行、`ui_page_registry.c` 描述符注册表、`ui-golden`+`font-charset` 两枚钩子);U2 只有 `ui_w_records` 一个组件;"27 页未落地"实为 7 页。② "新增一页 ≤30 行"目标**未达**(最省 40 行),如实记为未达项。③ §0.4 与登记簿已漂开 → 本版把待办清单降级为**快照 + 指向登记簿**,权威只留一处。
**刻意保留的三层**(删了会出事):§9 **编号族登记处**——本文是 `id_registry.json` 里 `N-RPT`/`D-RPT`/`S-RPT`/`R-RPT`(游标 R-9)/`F-RPT`/`U-RPT`(游标 U5) 六族的属主登记处,且外部存在「报告 N-14」「报告 F-4/F-5/F-6」「报告 D-1」「报告 R-2/R-3」式紧邻引用与 `check_dangling`,删定义位即触发编号门禁;§1.2/§1.3 方法与口径(数字可重跑);文末**变更记录 + 已销项去处表**原样冻结保留(52 行)。被外部点名的锚点(`§0.3`/`§3.1`/`§4.1`/`§4.2`/`§4.3`/`§5.3`×9/`§5.4`/`§5.5`×4/`§6.2`/`§6.3`/`§6.5`)逐个保留,**68 处入站引用零改动**。
**门禁**:`check_doc_links.py --all --sections-strict` → 146 文档 0 问题(章节引用 2,499、可判定 1,483);`check_ids.py --strict` 增量拦截通过、**存量债 650 → 641(−9)**、可判定比例 82.98% → **82.79%**(仍高于基线 82.6%,但缓冲从 0.38pp 缩到 0.19pp——删掉的那批引用 99% 是可判定的,属预期副作用);`mem-numbers` / `ram_baseline --gate` / `task-table` / `cubemx-survival` 全 0。字节卫生:BOM 无 / CR 0 / U+FFFD 0 / 无行尾空格 / LF 结尾。 |
| **2026-10-22** | **S-2 第三步:SPI1 完成标志的回调归属拆清**(推荐清单第 4a 步) | 问题不是风格:HAL 的 SPI 完成/错误回调
是**全局唯一符号**,全工程只能有一份实现,当前由 CH395F 代管并按 `hspi->Instance` 分发——于是 GD5F 的
`g_spi1_dma_done` 被 CH395F 跨模块裸 `extern` 直接写,**GD5F 出错时的"释放等待者"寄居在别人的 .c 里**。
谁哪天做"CH395F 清理",把那两个 `else if (SPI1)` 分支当无关代码删掉:**编译零报错**,GD5F 的 DMA 等待
在出错时只能干等 `GD5F_SPI_TIMEOUT` 兜底。
| | **改法(时序一字不动,只换所有权)**:标志收为 GD5F 文件内 `static s_spi1_dma_done`;GD5F 导出
`gd5f_spi1_notify_done()` / `gd5f_spi1_notify_error()`(头文件里写明"SPI1 分支必须转调这两个函数");
CH395F 删掉裸 `extern`、include `gd5f2gq5ue.h`、两个 SPI1 分支改为转调并就地注明"勿删"。
`HAL_SPI_AbortCpltCallback`(`HAL_SPI_DMAStop()` 会触发)仍无人实现,**本次刻意不做**——它会改变中止
语义,属真机专项,当前超时兜底可用。
| | **体量代价**:`ch395f.o` −8 B、`gd5f2gq5ue.o` +24 B → `Code` 213,188 → **213,204 B(+16 B)**,
RO/RW/ZI 全部不变,`ER_IROM1` 231,768 → **231,784 B(22.1%)**。16 B 换一个静默失效类,划算。
数字联动一并做掉:`data/ram_report_20261022.md`、`data/stack_report_20261022.md` 重跑;`ram_baseline
--update/--write`;`check_mem_numbers --write`;三张配图常量更新后重新生成(逐文本按字号与锚点复查
越界 = **0**);本文 §1.3/§5.1/§5.4 与 `docs/环境与部署/项目部署环境要求.md` §3.3 的 Flash 数字同步。
| | **真机验证 `@test` PASS**:无 `[ERR]`/Fault,复位原因 software;`[STACK] heap free=15800
min_ever=8464`**与改前逐项相同**;各任务实测峰值 default 2152 / net 1116 / ftp 764 / ui 4052 /
timesync 616 / adc 952 / iec104 540 / protect 624,**静态最坏链全部未变**(3024/1912/1456/4052/1264/
1320/1456/872)→ 9/9 PASS。⚠ 记录一条读数陷阱:测试脚本打印的"boot to net init: OK = 7,907 ms"是
采集窗口锚点造成的度量偏差,真机日志里 `boot: syslog init OK → net init: OK` 实测 ≈699 ms,与改
前一致,**不是性能回退**。 |
| **2026-10-22** | **S-2 第二步:19 处任务句柄裸 extern 收拢成引用契约**(推荐清单第 3 步) | 新建 `App/task/task_handles.h`——**只有声明、没有 `.c`**:头文件不需要双端注册,`../App/task` 本来就在 uvprojx 的 `<IncludePath>` 里,所以这一步零构建面。`default_task.c:52-69` 的 18 行 + `adc_task.c:125` 的 1 行局部 extern 全部删除改 include(栈水位表本体仍留在 `default_task.c`);同批把 `Src/main.c` 函数内的 `extern volatile uint32_t g_rcc_csr_latched;` 移进 `App/app/app_main.h`(该文件 `main.c` 本来就 include),
USER CODE 区内只留「读 RCC_CSR → 写 RMVF 清除」两句、顺序约束的原注释保留。**价值**:原先每加一个任务都得记得往 `default_task.c` 再抄两行 extern,抄漏一行 = 读别人的栈(静默);现在只有一个改动点,且 CubeMX 若把句柄改名会在链接期报错而不是静默通过。**镜像逐项体积不变**(`Code=213,188 RO=18,580 RW=1,184 ZI=131,504`,`@build` 0 错 0 警告)证明行为中性。`check_cubemx_survival.py` 对这次有意的 USER CODE 区改动报「漂移」,已按其文档流程 `--write`重建基线(`.cubemx_survival_baseline.json` 仅 1 行变化),`--check` 复跑通过、易失清单 29 项全部到位;顺带把 §0.4 的 S-2 口径改成精确分类:**手写该修 24 处(已消 22)+ CubeMX 生成块 16 条(不算债)+ `__asm` 汇编标签 4 条(合法例外)**——上一版「25 处」混了这三类。 |
| **2026-10-22** | **结构债 S-4 闭环 + S-2 第一步(用户批准「先只跑 1+2」)** | **S-4**:删除 `App/app/app_cfg.h` 里 4 个**零消费点**宏(`APP_NET_ENABLE`/`APP_ADC_ENABLE`/`APP_RS485_ENABLE`/`APP_SYS_TIME_SRC`),文件降格为「决策占位说明」——修改记录 v1.2 写明为什么删与将来怎么接回;并去掉 `app_main.c` 里只为它存在的 include。留「看着能裁剪、其实不生效」的宏比不留更糟:它不产生任何编译期效果,却让评审误信模块可裁剪;此前为了说明它,AGENTS 之外还得在 6 处文档各写一句「未接线占位,别当开关用」。同步改口径 **17 处**:`App/app/README.md`、`App/README.md`、根 `README.md`、`项目架构总览.md`、`项目部署环境要求.md`(§3.2 两行 + 配置表一行)、`产品需求设计文档.md`(B0 结论 + 配置表)、`基线与待确认.md`、`排期详述.md` C5、`登记簿.md` N-23 残余、`RAM使用分析与优化方案.md` 三处。
+1 -1
View File
@@ -4,4 +4,4 @@ date code ro rw zi er_irom1 er_pct iram1 iram1_pct ccm ccm_pct top_rwzi
2026-10-14 212296 18740 1112 154456 231036 22.0 98144 74.9 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=40960;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4120;default_task.o=2736;sys_log_mcu.o=1924
2026-10-15 213540 18740 1136 131520 232280 22.2 75232 57.4 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4120;sys_log_mcu.o=3588;default_task.o=2736
2026-10-20 213356 18580 1184 131480 231936 22.1 75240 57.4 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4120;sys_log_mcu.o=3588;default_task.o=2736
2026-10-22 213188 18580 1184 131504 231768 22.1 75264 57.4 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4120;sys_log_mcu.o=3588;default_task.o=2736
2026-10-22 213204 18580 1184 131504 231784 22.1 75264 57.4 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4120;sys_log_mcu.o=3588;default_task.o=2736
1 date code ro rw zi er_irom1 er_pct iram1 iram1_pct ccm ccm_pct top_rwzi
4 2026-10-14 212296 18740 1112 154456 231036 22.0 98144 74.9 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=40960;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4120;default_task.o=2736;sys_log_mcu.o=1924
5 2026-10-15 213540 18740 1136 131520 232280 22.2 75232 57.4 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4120;sys_log_mcu.o=3588;default_task.o=2736
6 2026-10-20 213356 18580 1184 131480 231936 22.1 75240 57.4 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4120;sys_log_mcu.o=3588;default_task.o=2736
7 2026-10-22 213188 213204 18580 1184 131504 231768 231784 22.1 75264 57.4 57376 87.9 heap_4.o=57376;lv_mem_core_builtin.o=16384;lftpd.o=7884;ui_net_sink.o=6766;ui_port.o=6420;nand_ftl.o=6216;ch395f.o=4162;console_log.o=4120;sys_log_mcu.o=3588;default_task.o=2736
+9 -9
View File
@@ -47,11 +47,11 @@ RAM2_CATEGORIES = [
# ── Flash breakdown (1MB = 1,048,576 B; 2026-10-22 @build) ──────────────────
FLASH_TOTAL = 1048576
FLASH_CODE = 213188
FLASH_CODE = 213204
FLASH_RO = 18580
FLASH_RW = 1184
FLASH_USED = FLASH_CODE + FLASH_RO + FLASH_RW # ROM 合计(含 RW 初始化表)
FLASH_IROM = FLASH_CODE + FLASH_RO # ER_IROM1 区用量 = 231,768 B(22.1%)
FLASH_IROM = FLASH_CODE + FLASH_RO # ER_IROM1 区用量 = 231,784 B(22.1%)
FLASH_FREE = FLASH_TOTAL - FLASH_USED
FLASH_CATEGORIES = [
@@ -61,14 +61,14 @@ FLASH_CATEGORIES = [
("Flash 空闲", FLASH_FREE, "#E0E0E0"),
]
# Flash Code+RO 分布(= ER_IROM1 231,768 B;2026-10-22 按 .map 对象表逐对象归类,
# Flash Code+RO 分布(= ER_IROM1 231,784 B;2026-10-22 按 .map 对象表逐对象归类,
# 口径同 data/ram_report_20261022.md;"其他"含链接器填充)
FLASH_CODE_SUB = [
("LVGL 库", 87706, "#4CAF50"),
("应用代码/UI", 57011, "#2196F3"),
("CH395F+FTP", 25652, "#F44336"),
("CH395F+FTP", 25644, "#F44336"),
("HAL 驱动", 17876, "#607D8B"),
("dhara / FTL", 10907, "#FF9800"),
("dhara / FTL", 10931, "#FF9800"),
("FreeRTOS 内核", 10762, "#00BCD4"),
("FatFS", 8468, "#9C27B0"),
("C 库 (microlib)", 5486, "#795548"),
@@ -81,15 +81,15 @@ FLASH_CODE_SUB = [
# worst = max(静态最坏深度, 实测峰值); margin = cfg - worst
# 9 行含 testTask(仅测试构建创建、无实测 → 取静态最坏链,峰位列填同值)
TASK_STACKS = [
("defaultTask", 5120, "Normal", 3024, 2164, 2096, 40, "PASS", "FatFS 挂载/探针 + 配置引擎 + syslog + LED"),
("defaultTask", 5120, "Normal", 3024, 2152, 2096, 40, "PASS", "FatFS 挂载/探针 + 配置引擎 + syslog + LED"),
("netTask", 3840, "Normal", 1912, 1116, 1928, 50, "PASS", "CH395F 轮询、消息处理"),
("ftpTask", 4096, "Normal", 1456, 756, 2640, 64, "PASS", "lftpd FTP 服务(大缓冲已静态化)"),
("ftpTask", 4096, "Normal", 1456, 764, 2640, 64, "PASS", "lftpd FTP 服务(大缓冲已静态化)"),
("uiTask", 16384, "BelowNormal", 4052, 4052, 12332, 75, "PASS", "LVGL UI 主循环 + 远程显示推流"),
("testTask", 8192, "BelowNormal", 736, 736, 7456, 91, "PASS", "测试套件(产品构建不创建,取静态最坏链)"),
("timeSyncTask", 2560, "Normal", 1264, 616, 1296, 50, "PASS", "TCP 时间同步(8888)"),
("adcTask", 2560, "AboveNormal", 1320, 952, 1240, 48, "PASS", "1 kHz 节拍消费者(L1 采集,纯取数)"),
("iec104Task", 2560, "Normal", 1456, 544, 1104, 43, "PASS", "IEC 60870-5-104 上行(2404)"),
("protectTask", 2048, "Normal", 872, 828, 1176, 57, "PASS", "20 ms 保护判据 + 闭锁 + 定值校验和巡检"),
("iec104Task", 2560, "Normal", 1456, 540, 1104, 43, "PASS", "IEC 60870-5-104 上行(2404)"),
("protectTask", 2048, "Normal", 872, 624, 1176, 57, "PASS", "20 ms 保护判据 + 闭锁 + 定值校验和巡检"),
]
KERNEL_STACKS = [("idle", 512), ("timer", 1024)] # 128/256 words(未实测)