chore(编号): 存量编号全量转语义短语,门禁识别面与语料同批重打
登记簿、文档与代码注释里的存量字母加数字编号(N-x / S-x / T-x / E-x / A-x / 卡号) 一律改写为语义短语,净删 2,028 处引用;删除基线与待确认、并行分道方案、排期详述、 采集保护收尾 4 篇(内容已并入 docs/任务规划/README 与登记簿)。 门禁机器与三张账同批重打:check_ids.py 新增变更记录行豁免与第二根护栏 undecidable_total_ceiling;check_test_traceability.py 只加 stdout/stderr UTF-8 reconfigure(TC_RE 未动);check_cubemx_survival.py 的 --write 固定 newline=LF; id_registry / id_aliases(+19 条 retired-to-phrase)/ id_known_backlog 重打。 test/ 56 个文件同步 TC 标签语义化,其中 18 个 TU 改了 CHECK / printf 里的标签文字, 是本批唯一改变构建产物的文件集合。 为什么合成一笔、不拆四笔(实测撞闸,非偏好):pre-commit 跑钩子前会 stash 未 staged 部分,拆开后中间提交必然是「新规则 + 旧语料」——实跑可判定 85.15% < 基线 86.00%、 不可判定 1,453 > 上限 1,166,编号门禁直接拦下。编号识别面与它描述的语料只能同码移动。 实跑基线(工作区全量):754 文件 / 8,230 处编号字面量 / 可判定 7,079 = 86.01% (基线 86.0%,容差 0.1pp)/ 不可判定绝对数 1,151(上限 1,166)/ 校验 1-6 错误级 0 / 告警 = 悬空 2(App/algo/freq.c 的 S2,属 §4.4 同号不同义)+ 孤儿 50。 基线从 84.0 抬到 86.0 的理由见 docs/门禁说明/id-check.md §7.6:本批真删了 2,028 处 引用,可判定面随真实内容上升,不是把分母做小。同批新增排除 docs/硬件平台/ 语料,使 识别面少 264 处——芯片手册位号与网表料号不属编号空间(registry 有 excluded_paths_note)。 ⚠ 余量只剩 0.11pp(约 10 处)与绝对数 15 处:把「可判定的引用」改成散文会立刻撞第二 根护栏。另遗留一项:id_aliases 里 retired 的 N / S / T / A 别名没有 scope(U1~U5 有), 今后有人新写一个 A-4,校验 3 不会响,只能靠人,建议下批补 scope。 固件不变证据:115 个 .c/.h 剥注释并归一字符串后与 HEAD 逐字节全等,残留代码差异 0; App / Src / Drivers 的字符串字面量零改动。@build 0 错误 0 警告;40/40 主机套件、2,889 断言全通过;覆盖率 line 89.2% / func 92.6%;pre-commit 48 钩子 --all-files 全绿; check_doc_links --all 通过(176 文档,章节引用无法判定 0);逐字节卫生核过:14 个带 BOM 文件与 HEAD 一致、0 个 CR、0 个 U+FFFD,字符串字面量纯 ASCII。 .cubemx_survival_baseline.json 的 watchdog-module 哈希 56bf27 变 2bb051 属合法: Src/freertos.c 的 USER CODE 区内只有注释文字随编号改写,代码未变。 同批带上体积同码凭证与 RAM 台账行(@build 之后盖戳):RW_IRAM1 79,008 B / 60.3%, RW_CCM 57,376 B / 87.9%,阈值 92%。
This commit is contained in:
+3
-3
@@ -41,7 +41,7 @@ ADC 引脚电压 V)+ `adc_health_rail_watch()` ×9(L5-2,码值域)→ `s
|
||||
## 3. 逐文件要点(只留"改代码时必须知道"的;机制与理由见设计文档)
|
||||
|
||||
> **改本目录前先记五条红线**:① 取数只经 `adc_input_raw()`,不写死下标;② **禁止跨转换器**做
|
||||
> 相位 / 功率 / 矢量运算(N-18);③ 帧值已是 L1.5 工程量,但 **未标定(`adc_cal_flags()` 无
|
||||
> 相位 / 功率 / 矢量运算(双转换器无共同采样触发);③ 帧值已是 L1.5 工程量,但 **未标定(`adc_cal_flags()` 无
|
||||
> VALID)时单位只是 ADC 引脚电压**,不是端子二次值——下游消费精度判据见「数据可信」§4.2 三态
|
||||
> 表;④ 工程量 → 帧的唯一折算点是 `adc_cal_apply()`,禁止旁路再乘系数;⑤ 本层不建任务、
|
||||
> 不等事件、不写日志(`adc_cal` 的 `[ERR]` 哨兵留痕是唯一例外,且每通道每开机一次),
|
||||
@@ -87,7 +87,7 @@ ADC 引脚电压 V)+ `adc_health_rail_watch()` ×9(L5-2,码值域)→ `s
|
||||
`freq_get() == 0 && freq_valid() == 1`。
|
||||
- 精度靠**自适应窗长**(窗长 = 泄漏纹波周期的整数倍);定长平均压不到 0.02 Hz,
|
||||
换窗长 = 换精度,二者不是独立旋钮。
|
||||
- 源切换即废相位基准重建(UA / IA 无共同相位基准,N-18)。
|
||||
- 源切换即废相位基准重建(UA / IA 无共同相位基准,即双转换器无共同采样触发)。
|
||||
- 判据 **J1~J6** 与实测口径 → `docs/算法/采集与算法层设计说明.md` §7.2(与 `test/host/freq/freq_test.c` 文件头同源)。
|
||||
|
||||
### 3.8 `adc_cal.c` / `.h` — L1.5 通道标定折算(「数据可信」卡 2 / 卡 4)
|
||||
@@ -129,7 +129,7 @@ ADC 引脚电压 V)+ `adc_health_rail_watch()` ×9(L5-2,码值域)→ `s
|
||||
### 4.1 挂载与模拟器边界
|
||||
- 九个 `.c` 已登记在 `MDK-ARM/STM32F407-Demo.uvprojx`;`../App/algo` 已在 `<IncludePath>` 内。
|
||||
- 本层**不进** `simulator/CMakeLists.txt`(UI 模拟器不跑采集链路):九个文件全部登记在
|
||||
`scripts/check_registration.py` 的 `SIM_EXEMPT`(附理由),`registration-check` 钩子反向校验(N-23)。
|
||||
`scripts/check_registration.py` 的 `SIM_EXEMPT`(附理由),`registration-check` 钩子反向校验(登记簿条目「rs485_task 白名单缺口」)。
|
||||
|
||||
### 4.2 主机侧单测(L0.5)——生产源文件以 `#include` 原样纳入
|
||||
|
||||
|
||||
+2
-2
@@ -70,7 +70,7 @@ static adc_cal_bcfg_t s_bcfg; /* 生效零点副本 */
|
||||
* (16 位 ±5 V 档),UB/UC/IB/IC/IN 走 STM32 ADC1;逐路对过
|
||||
* docs/硬件平台/ADC通道映射说明.md §3(IN=ADC11/STM32,勿再按分组顺序误写)。
|
||||
* ROM 表按**当前 V0.1 来源排布**显式书写(不查 desc 运行期派生),因为它是
|
||||
* 卡 1 定稿的存档事实;V0.2(N-18)改版时本表与 adc_input 映射表同步改,
|
||||
* 卡 1 定稿的存档事实;V0.2 改版(消除双转换器无共同采样触发)时本表与 adc_input 映射表同步改,
|
||||
* 上层语义与 API 不动。CLIPPED 位则**运行期按来源派生**(见 adc_cal_flags) */
|
||||
static const adc_cal_kcfg_t s_kcfg_rom = {
|
||||
/* idx0 UA / idx1 UB / idx2 UC / idx3 UN */
|
||||
@@ -272,7 +272,7 @@ uint32_t adc_cal_flags(adc_semantic_t sem) {
|
||||
f |= ADC_CAL_F_VALID;
|
||||
}
|
||||
/* CLIPPED 由**来源**派生(R5:STM32 侧单极性无偏置 → 负半周削顶)——
|
||||
* V0.2 改版(N-18,8 路全接 TPAFE)后本位自动恒 0,无需改码,
|
||||
* V0.2 改版(8 路全接 TPAFE,消除双转换器无共同采样触发)后本位自动恒 0,无需改码,
|
||||
* 对应「数据可信」§10「页面与 104 绑定零改动」的要求 */
|
||||
if (s_rom_k_stm32((unsigned)sem) != 0) {
|
||||
f |= ADC_CAL_F_CLIPPED;
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
* 标定(卡 12)完成前不得把输出当二次值消费。TEMP 不参与线性标定
|
||||
* (决策 5),adc_cal_apply() 原样透传码值。
|
||||
* 本层**纯算法 + 纯数据**:不碰寄存器、不等事件;adc_cal_init() 内核前
|
||||
* 可调(注册进引擎必须先于 cfg_file_load,N-19 契约);apply/flags
|
||||
* 可调(注册进引擎必须先于 cfg_file_load,FTP 鉴权确立的契约);apply/flags
|
||||
* 任意上下文可读(cfg RAM 副本 4 字节对齐字读写在 Cortex-M4 上原子,
|
||||
* 与 defaultTask 的 load/poll 复位存在 µs 级混窗——设计接受,见 .c 头)。
|
||||
* 适用平台:STM32F407ZGTx(Cortex-M4F);主机侧 L0.5 单测 test/host/adc_cal/
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
* ④ 错误计数增量:FRSTDATA 跳变 / HAL ADC·DMA 错误 / STM32 停摆计数。
|
||||
* ⚠ 本层**纯逻辑**:不碰寄存器、不取数、不打日志——输入是调用方
|
||||
* 采集的计数快照,输出是故障位;日志与自检上报由消费者
|
||||
* (`App/task/adc_task.c`)按位执行 → 主机侧可单测(L0.5,A-4)。
|
||||
* (`App/task/adc_task.c`)按位执行 → 主机侧可单测(L0.5)。
|
||||
* 适用平台:STM32F407ZGTx + TPAFE5160
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-10-11
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
* 权威口径:docs/硬件平台/ADC通道映射说明.md(2026-10-09 硬件确认的语义分配)。
|
||||
* index 含义:TPAFE 源 = 驱动 buf 下标 0..7;STM32 源 = ADC1 通道号 0..15。
|
||||
* ⚠ 消费侧一律经本表按语义取数,**不得写死下标**(V0.1 有 13/17 路被置换,
|
||||
* 网名编号与通道号不等价);V0.2 改版(N-18)只改本表内容。 */
|
||||
* 网名编号与通道号不等价);V0.2 改版(消除双转换器无共同采样触发)只改本表内容。 */
|
||||
static const adc_channel_desc_t k_adc_map[ADC_SEM_COUNT] = {
|
||||
[ADC_SEM_UA] = { ADC_SRC_TPAFE, 6U }, /* TPAFE buf[6](ch7) */
|
||||
[ADC_SEM_UB] = { ADC_SRC_STM32, (uint8_t)ADC_CHANNEL_14 }, /* PC4 */
|
||||
@@ -240,7 +240,7 @@ int adc_input_init(void) {
|
||||
* STM32 侧无新帧不影响 TPAFE 快照与返回值语义(其状态另由
|
||||
* adc_input_stm32_rc() / adc_input_stm32_seq() 暴露)。
|
||||
* ⚠ STM32 侧数据比 TPAFE 侧**滞后 1 拍**(乒乓双缓冲的固有延迟,
|
||||
* 9 路一致);按 N-18,V0.1 本就禁止跨转换器做矢量运算。
|
||||
* 9 路一致);因双转换器无共同采样触发,V0.1 本就禁止跨转换器做矢量运算。
|
||||
*/
|
||||
int adc_input_poll(void) {
|
||||
int r = tpafe5160_snapshot(s_tpafe_raw, (uint8_t)ADC_INPUT_TPAFE_CH_NUM);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* ① 语义通道映射表(L1-4)——权威口径 docs/硬件平台/ADC通道映射说明.md;
|
||||
* ② STM32 ADC1 9 通道采样(L1-3)——由 **TIM3 TRGO 硬件触发 + DMA 双缓冲
|
||||
* 搬运**,本层只消费、不触发、不等待。
|
||||
* V0.1 上 8 路电气量分属两片转换器(硬件缺陷 N-18):UA/UN/IA 走
|
||||
* V0.1 上 8 路电气量分属两片转换器(硬件缺陷:双转换器无共同采样触发):UA/UN/IA 走
|
||||
* TPAFE5160(16 位 / ±5 V 双极性),UB/UC/IB/IC/IN 走 STM32 ADC1
|
||||
* (12 位 / 0~3.3 V 单极性)。V0.2 改版(8 路电气量全部接 TPAFE)
|
||||
* 只改映射表内容,本层 API 与上层语义不动。
|
||||
@@ -47,7 +47,7 @@ typedef enum {
|
||||
ADC_SEM_COUNT /* 语义通道数(哨兵) */
|
||||
} adc_semantic_t;
|
||||
|
||||
/* 数据来源:V0.1 的 8 路电气量分属两片转换器(N-18) */
|
||||
/* 数据来源:V0.1 的 8 路电气量分属两片转换器(双转换器无共同采样触发) */
|
||||
typedef enum {
|
||||
ADC_SRC_TPAFE = 0, /* TPAFE5160:16 位、±5 V 双极性、8 通道同步 */
|
||||
ADC_SRC_STM32 /* STM32 ADC1:12 位、0~3.3 V 单极性、9 通道顺序扫描 */
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@
|
||||
* - 信号源优先级:UA 基波 → IA 基波(UA 无幅值时用 IA,台架只注电流亦可测频)。
|
||||
*
|
||||
* ⚠ 本层**纯算法**:不碰寄存器、不碰 RTOS、不自己取数(输入 = phasor 使用区)
|
||||
* → 主机侧可免桩单测(L0.5,落实登记簿 A-4)。
|
||||
* → 主机侧可免桩单测(L0.5)。
|
||||
* 适用平台:STM32F407ZGTx(Cortex-M4F,硬件 FPU)
|
||||
* 作者:DTU 项目组
|
||||
* 创建日期:2026-10-12(B8)
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@
|
||||
* 覆盖规划 §3.2:L2-1 系数表 / L2-2 实例 / L2-3 有效值输出 /
|
||||
* L2-6 每 1 ms 滑动窗 / L2-7 双区指针原子交换。
|
||||
* ⚠ 本层**纯算法**:不碰寄存器、不碰任务调度、不自己取数(输入由
|
||||
* `sample_ring` 提供)→ 主机侧可单测(L0.5,落实登记簿 A-4)。
|
||||
* `sample_ring` 提供)→ 主机侧可单测(L0.5)。
|
||||
* 适用平台:STM32F407ZGTx(Cortex-M4F,硬件 FPU)
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-10-10
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
* 同 phasor 实例表模式)。判据为**纯逻辑**(吃测量值 + 定值),
|
||||
* 主机侧可离线单测;出口/SOE(L4)与任务编排为后续。
|
||||
*
|
||||
* V0.1 有效实例(N-18:仅 TPAFE 三路):
|
||||
* V0.1 有效实例(双转换器无共同采样触发 → 仅 TPAFE 三路):
|
||||
* OC1/OC2/OC3 三段式相间过流(A 相 IA,L3-1)
|
||||
* OL 过负荷(A 相 IA,L3-3)
|
||||
* OV/UV 过压/欠压(UA,L3-4)
|
||||
|
||||
+2
-2
@@ -53,7 +53,7 @@ extern "C" {
|
||||
* 入口参数:无
|
||||
* 返 回 值:无
|
||||
* 限定条件:**内核启动前**调用(app_main_init,adc_cal_init 之后);注册必须先
|
||||
* 于 defaultTask 的 cfg_file_load(N-19 契约,同 adc_cal 先例);幂等
|
||||
* 于 defaultTask 的 cfg_file_load(FTP 鉴权确立的契约,同 adc_cal 先例);幂等
|
||||
* 函数说明:注册失败(段名重复/段数超限)打 [ERR] 后继续——工厂默认照常生效,
|
||||
* 仅文件整定失效(adc_cal_init 同款降级)。
|
||||
*/
|
||||
@@ -118,7 +118,7 @@ uint8_t run_par_dirty_peek(void);
|
||||
* 入口参数:无
|
||||
* 返 回 值:0 成功(或无脏可存);-1 写盘失败(脏保持,下轮重试) int
|
||||
* 限定条件:**defaultTask(配置引擎属主)上下文**,由 SYS_EVT_RUN_PAR_SAVE 驱动
|
||||
* 函数说明:save 走引擎唯一写闸(超长拒写/原子改名,E-01 契约);本函数不清
|
||||
* 函数说明:save 走引擎唯一写闸(超长拒写/原子改名);本函数不清
|
||||
* 引擎以外模块的脏语义([adc_cal] 副本值已由写路径就位)。
|
||||
*/
|
||||
int run_par_store_save(void);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* 模块名称:采样环缓冲(L2 相量计算的输入)
|
||||
* 模块功能:按**语义通道**保存最近若干周波的采样值,供 L2 相量计算按窗取数。
|
||||
* V0.1 上 8 路电气量分属两片转换器(硬件缺陷 N-18),本层只做"按时序
|
||||
* V0.1 上 8 路电气量分属两片转换器(双转换器无共同采样触发),本层只做"按时序
|
||||
* 存/取",**不做任何跨通道运算**——同刻性由上游(L1)保证,本层不假设。
|
||||
* 容量取 **2 个周波(40 点)**,与参考系统 `CN_BUFSAM_CYCLE = 2`
|
||||
* (规划 §10.1)一致,为后续加长数据窗(1.5~2 周波,抗衰减直流,
|
||||
|
||||
+2
-2
@@ -6,8 +6,8 @@
|
||||
* 创建日期:2026-07-19
|
||||
* 修改记录:
|
||||
* v1.0 2026-07-19 王建锋 创建初始版本
|
||||
* v1.1 2026-10-04 王建锋 P2-12:如实标注三个 APP_*_ENABLE 为"未接线占位"(全库零消费点)
|
||||
* v1.2 2026-10-22 王建锋 S-4 收口(报告 S-4 / 登记簿 C5):删除 4 个零消费点的宏
|
||||
* v1.1 2026-10-04 王建锋 如实标注三个 APP_*_ENABLE 为"未接线占位"(全库零消费点)
|
||||
* v1.2 2026-10-22 王建锋 收口(登记簿 C5):删除 4 个零消费点的宏
|
||||
* (APP_NET_ENABLE / APP_ADC_ENABLE / APP_RS485_ENABLE /
|
||||
* APP_SYS_TIME_SRC)。留"看起来能裁剪、其实不生效"的宏比不留更糟:
|
||||
* 评审者会误信模块可裁剪,而关掉它们不会产生任何编译期效果。
|
||||
|
||||
+6
-6
@@ -30,7 +30,7 @@
|
||||
#include "prot_set.h" /* L3 定值模型:prot_set_init()(B5,内核前封存) */
|
||||
#include "prot_elm.h" /* L3 元件框架:prot_elm_init()(B5,解析相量索引) */
|
||||
#include "sys_log.h"
|
||||
#include "task_watchdog.h" /* task_wd_seg_report():round 8 复位不丢"进段打点"上报 */
|
||||
#include "task_watchdog.h" /* task_wd_seg_report():第 8 轮复位不丢"进段打点"上报 */
|
||||
#include "test_config.h" /* 套件开关:身份戳的构建变体串(FW_BUILD_VARIANT)依赖本头,
|
||||
* 改 TEST_SUITE_* / ENABLE_DESTRUCTIVE_TESTS 必重编本 TU →
|
||||
* __DATE__/__TIME__ 随之刷新,见 app_main_init() 的戳注释 */
|
||||
@@ -131,7 +131,7 @@ int app_main_init(void) {
|
||||
|
||||
/* 日志引擎初始化(创建待发队列;须先于任何 syslog_post) */
|
||||
(void)syslog_mcu_init();
|
||||
/* 启动锚点(2026-10-02 S10):本函数在内核启动前执行、不创建任何 RTOS 对象,
|
||||
/* 启动锚点(2026-10-02 加):本函数在内核启动前执行、不创建任何 RTOS 对象,
|
||||
* 恒成功且无副作用;本行是"启动进度轨迹"的起点——之后的每一步都有结果日志,
|
||||
* 故某次启动卡死时,最后一行日志即可指向卡住的步骤。 */
|
||||
DBG_INFO("boot: syslog init OK");
|
||||
@@ -166,10 +166,10 @@ int app_main_init(void) {
|
||||
}
|
||||
}
|
||||
|
||||
/* 0b) round 8 取证:上报上一轮 defaultTask 主循环的**最后进段**。
|
||||
/* 0b) 第 8 轮取证:上报上一轮 defaultTask 主循环的**最后进段**。
|
||||
* 轨迹槽在 .noinit(scatter UNINIT_CCM),IWDG/引脚复位不清 SRAM →
|
||||
* 误复位后开机第一时间可见 `PREVRUN seg=N`,直接指向主循环阻塞点
|
||||
* (round 7 观察"判定分支未执行"经 round 8b 更正:真因是 IWDG 超时被静默
|
||||
* (第 7 轮观察"判定分支未执行"经第 8b 轮更正:真因是 IWDG 超时被静默
|
||||
* 配成复位默认值,见 docs/子系统设计/看门狗设计说明.md 2026-10-02 变更记录)。
|
||||
* 裸 UART 直发,且一次复位只报一次(内部清 magic)。 */
|
||||
task_wd_seg_report();
|
||||
@@ -178,7 +178,7 @@ int app_main_init(void) {
|
||||
* 1) 时间子系统初始化(尽早完成,后续所有日志带时间戳)
|
||||
* sd2506_init() 从 RTC 读取初始时间
|
||||
* sys_clock_init() 将 RTC 时间转为 Unix epoch,建立墙钟基准
|
||||
* S10(2026-10-02):sd2506_init 原先所有失败路径静默返回,是启动段
|
||||
* 启动段可观测性(2026-10-02 加):sd2506_init 原先所有失败路径静默返回,是启动段
|
||||
* 唯一无日志的初始化;现内部已逐路径打 DBG_ERROR,此处再补一行
|
||||
* 带错误码的结果回执,使 I2C 不通时能看到"在哪一步、什么错"。
|
||||
*/
|
||||
@@ -308,7 +308,7 @@ int app_main_init(void) {
|
||||
/* 6) 启动 1 kHz 采样节拍:TIM3 更新事件 → ① ISR 内脉冲 CONVST(Src/main.c)
|
||||
* ② 同一事件经 TRGO 硬件触发 STM32 ADC1 一轮 9 通道转换(DMA 自动搬运)。
|
||||
* ⚠ 必须在 tpafe5160_init() 成功之后启动,否则 ISR 会在驱动首帧自检
|
||||
* (E-44 有效性探针)期间乱发 CONVST。消费端 adcTask 退化为纯消费者。
|
||||
* (有效性探针)期间乱发 CONVST。消费端 adcTask 退化为纯消费者。
|
||||
* ⚠ 节拍源必须能触发 ADC1,故用 **TIM3**:F4 上 ADC1 的外部触发只认
|
||||
* TIM1/TIM2/TIM3/TIM4/TIM5/TIM8 的 CC 或 TRGO,**TIM6/TIM7 的 TRGO 只接
|
||||
* DAC**(查 CubeMX DB 与本仓库 stm32f4xx_hal_adc.h 的 EXTSEL 表证实)
|
||||
|
||||
@@ -149,7 +149,7 @@ static void feed_copy_str(char *p_dst, size_t dst_len, const char *a_src) {
|
||||
* 限定条件:uiTask 上下文,ui_data_feed_tick 首帧前调用
|
||||
* 函数说明:只认 harm == 1 的实例;一个语义通道有多个基波实例时取**首个**
|
||||
* (现状 k_inst[] 每通道最多一个基波,取首个即唯一解)。
|
||||
* UB/UC/IB/IC/IN 无实例(V0.1 硬件削顶,N-18)→ 留 -1,该路发 IV
|
||||
* UB/UC/IB/IC/IN 无实例(V0.1 硬件削顶:双转换器无共同采样触发)→ 留 -1,该路发 IV
|
||||
*/
|
||||
static void feed_map_phasor(void) {
|
||||
uint8_t n;
|
||||
|
||||
+15
-15
@@ -8,20 +8,20 @@
|
||||
* 创建日期:2026-09-14
|
||||
* 修改记录:
|
||||
* v1.0 2026-09-14 王建锋 创建初始版本(P3:段键表引擎 + net_cfg 集成)
|
||||
* v1.1 2026-09-18 王建锋 P0-1:临时缓冲改经 cfg_store_alloc(MCU = FreeRTOS 堆),
|
||||
* v1.1 2026-09-18 王建锋 临时缓冲改经 cfg_store_alloc(MCU = FreeRTOS 堆),
|
||||
* 移除对 C 库 malloc 的全部依赖(MicroLIB C 堆仅 512B,
|
||||
* 原 4 处 KB 级 malloc 真机必然失败);增编译期堆峰值预算
|
||||
* 校验 + 失败上报自检记录
|
||||
* v1.2 2026-09-21 王建锋 E-01(历史审计 P0,属主文档已删除):超长文件守卫根治——poll 判定
|
||||
* v1.2 2026-09-21 王建锋 超长文件守卫根治——poll 判定
|
||||
* 前置到 cfg_store_stat 真实 st.size(旧检查位于截断后的
|
||||
* len 上恒假=死码,超长文件被截断当有效解析 → 界外静默
|
||||
* 回工厂默认 → 基线粘住 → 用户配置销毁链);save 输出上限
|
||||
* 由 CFG_OUT_MAX 收紧至 CFG_FILE_MAX_LEN;s_write_doc 写盘
|
||||
* 唯一闸口拒写超长文档;load/poll 超长处置补自检留痕
|
||||
* v1.3 2026-10-04 王建锋 P2-1:受管键行超 CFG_LINE_TEST_MAX 时拒绝保存——
|
||||
* v1.3 2026-10-04 王建锋 受管键行超 CFG_LINE_TEST_MAX 时拒绝保存——
|
||||
* 原实现 Pass1 静默跳过超长行,Pass2 遂在段头补写新 KV 且
|
||||
* 原文超长行原样保留 → 同一键出现两行(旧值残留、静默截断)
|
||||
* v1.4 2026-10-15 王建锋 E-16(历史审计 P2,属主文档已删除):生成文档边界余量收口——
|
||||
* v1.4 2026-10-15 王建锋 生成文档边界余量收口——
|
||||
* s_gen_document 两处 `off += snprintf(...)` 改检查式(截断即
|
||||
* 返回 -1,不再跳过缓冲末尾);注册拒超长键名(≤ 15 字符),
|
||||
* 使每行 112 字节余量成为可编译期验证的前提
|
||||
@@ -29,7 +29,7 @@
|
||||
* len 1/2/4 小端补码)——s_decode_int() 用 strtol + ERANGE +
|
||||
* 宽度上下界校验,s_encode() 带负号输出;负零点偏移从此可
|
||||
* 干净落盘(动因见 docs/任务规划/数据可信.md §4.4 与 §7 决策 3)
|
||||
* 函数说明:引擎状态非重入——调用方须收敛同一任务(defaultTask:poll/save,见 P1-3;
|
||||
* 函数说明:引擎状态非重入——调用方须收敛同一任务(defaultTask:poll/save;
|
||||
* 启动期 load 先于任务竞争);FS 卷并发由 FatFS REENTRANT 卷锁保证
|
||||
* (见《配置文件设计说明》D15 A+ 方案)
|
||||
*/
|
||||
@@ -304,7 +304,7 @@ static int s_encode(const cfg_key_def_t *p_key, const uint8_t *p_src, char *p_ds
|
||||
case CFG_VAL_STR: {
|
||||
size_t slen;
|
||||
|
||||
/* P3-1:len==0 时 `p_key->len - 1u` 回绕为 SIZE_MAX → memcpy 巨长越界。
|
||||
/* len==0 时 `p_key->len - 1u` 回绕为 SIZE_MAX → memcpy 巨长越界。
|
||||
* STR 字段至少 1 字节(含结尾 NUL),len==0 非法,直接拒绝 */
|
||||
if (p_key->len == 0u) {
|
||||
return -1;
|
||||
@@ -619,7 +619,7 @@ static int s_load_file(uint8_t **p_pbuf, size_t *p_len) {
|
||||
* len - 数据长度 size_t
|
||||
* 返回值:0 - 成功;-1 - 写失败或超长拒写 int
|
||||
* 限定条件:p_buf 非空;len ≤ CFG_FILE_MAX_LEN
|
||||
* 函数说明:E-01 纵深防御——超长文档一旦落盘,下次开机 load 守卫即判损坏、
|
||||
* 函数说明:纵深防御——超长文档一旦落盘,下次开机 load 守卫即判损坏、
|
||||
* 整份用户配置连同注释/未知段销毁,故在唯一出口硬性拦截,使
|
||||
* "引擎永不产出超长 config.ini"成为结构性质(调用者新增路径免疫)
|
||||
*/
|
||||
@@ -923,7 +923,7 @@ static int s_surgical(char *p_in, size_t in_len, char *p_out, size_t out_cap, si
|
||||
* i_sec - 段索引 int
|
||||
* 返回值:1 - 是受管键;0 - 不是(无 '=' 或键名不匹配) int
|
||||
* 限定条件:仅比较 '=' 前的键名(大小写无关、剥尾部空白),不解析值
|
||||
* 函数说明:P2-1 用于"超长行是否落在受管键上"的判定(值可能很长,无法拷入定长缓冲);
|
||||
* 函数说明:用于"超长行是否落在受管键上"的判定(值可能很长,无法拷入定长缓冲);
|
||||
* 非修改式,故可安全用于仍供 Pass2 原样保留的行
|
||||
*/
|
||||
static int s_line_key_managed(const char *p_line, int i_sec) {
|
||||
@@ -969,9 +969,9 @@ static int s_line_key_managed(const char *p_line, int i_sec) {
|
||||
/*
|
||||
* 函数功能:保存 Pass 1——定位段头行号与各受管键行号
|
||||
* 入口参数:p_in - 原文本(可修改) char *
|
||||
* 返回值:0 - 成功;-1 - 受管键行超 CFG_LINE_TEST_MAX(拒绝保存,见 P2-1) int
|
||||
* 返回值:0 - 成功;-1 - 受管键行超 CFG_LINE_TEST_MAX(拒绝保存) int
|
||||
* 限定条件:行末被 NUL 化且不复原——调用方须传一次性副本
|
||||
* 函数说明:P2-1(2026-10-04):受管键行超过 CFG_LINE_TEST_MAX 时**不能**静默跳过
|
||||
* 函数说明:受管键行超过 CFG_LINE_TEST_MAX 时**不能**静默跳过
|
||||
* ——Pass2 会因该行未登记而在段头补写新 KV,同时原文超长行原样保留,导致
|
||||
* 同一键出现两行(旧值残留 + 新值,解析取谁取决于行序)。故此处显式拒绝:
|
||||
* 返回 -1,调用方中止保存、原文件不动、自检留痕。非受管键的超长行照旧跳过
|
||||
@@ -1033,7 +1033,7 @@ static int s_surgical_pass1(char *p_in) {
|
||||
}
|
||||
}
|
||||
else if (i_cur >= 0) {
|
||||
/* 超长行(> CFG_LINE_TEST_MAX):若其键名为本段受管键 → 拒绝保存(P2-1);
|
||||
/* 超长行(> CFG_LINE_TEST_MAX):若其键名为本段受管键 → 拒绝保存;
|
||||
* 否则按未知内容放过(Pass2 原样保留) */
|
||||
if (s_line_key_managed(p_line, i_cur) != 0) {
|
||||
return -1;
|
||||
@@ -1165,7 +1165,7 @@ int cfg_file_load(void) {
|
||||
}
|
||||
|
||||
if (st.size > CFG_FILE_MAX_LEN) {
|
||||
/* 文件超长 = 损坏:留工厂默认 + 重建模板(E-01:补自检留痕——销毁
|
||||
/* 文件超长 = 损坏:留工厂默认 + 重建模板(补自检留痕——销毁
|
||||
* 用户文件属重大事件,仅串口 DBG_ERROR 在现场无调试器时不可追溯) */
|
||||
if (s_write_template() != 0) {
|
||||
DBG_ERROR("load: corrupt file, template write FAIL");
|
||||
@@ -1254,7 +1254,7 @@ int cfg_file_save(void) {
|
||||
(void)memcpy(p_in1, p_in, in_len + 1u);
|
||||
|
||||
if (s_surgical_pass1((char *)p_in1) != 0) {
|
||||
/* P2-1:受管键行超 CFG_LINE_TEST_MAX → 中止保存(原文件不动)。此类超长行
|
||||
/* 受管键行超 CFG_LINE_TEST_MAX → 中止保存(原文件不动)。此类超长行
|
||||
* 只可能来自手工编辑(代码生成的受管 KV 行恒 <127B);若强行外科保存会
|
||||
* 产生重复键行,故宁可拒存并留痕,也不静默写坏。 */
|
||||
cfg_store_free(p_in1);
|
||||
@@ -1276,7 +1276,7 @@ int cfg_file_save(void) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* E-01:输出上限由 CFG_OUT_MAX(5120) 收紧至 CFG_FILE_MAX_LEN(4096)——
|
||||
/* 输出上限由 CFG_OUT_MAX(5120) 收紧至 CFG_FILE_MAX_LEN(4096)——
|
||||
* 旧上限允许把 >4096B 文档合法写盘(= 下一颗雷:下次开机 load 守卫
|
||||
* 判损坏、整份用户文件连同注释/未知段一起销毁)。超限走下方既有
|
||||
* 溢出中止路径:原文件不动 + 自检上报,RAM 副本保留(可修复后重试) */
|
||||
@@ -1342,7 +1342,7 @@ void cfg_file_poll(void) {
|
||||
{
|
||||
int i;
|
||||
|
||||
/* E-01(2026-09-21 审计 P0):超长守卫必须基于 cfg_store_stat 的真实
|
||||
/* 2026-09-21 审计 P0:超长守卫必须基于 cfg_store_stat 的真实
|
||||
* st.size 且先于 s_load_file——s_load_file 读长钉死 CFG_FILE_MAX_LEN
|
||||
* (nread ≤ 4096 恒成立),旧检查写在读后的 len 上恒假(死码):
|
||||
* 超长文件被截断当有效解析 → 界外段/键静默回工厂默认 → 基线粘住 →
|
||||
|
||||
+7
-7
@@ -8,12 +8,12 @@
|
||||
* 创建日期:2026-09-14
|
||||
* 修改记录:
|
||||
* v1.0 2026-09-14 王建锋 创建初始版本(P3:段键表引擎 + net_cfg 集成)
|
||||
* v1.1 2026-09-15 王建锋 属主任务由 netTask 改为 defaultTask(配置引擎单一属主,P1-3)
|
||||
* v1.2 2026-09-21 王建锋 E-01(历史审计 P0,属主文档已删除):超长守卫契约同步——poll 沉降
|
||||
* v1.1 2026-09-15 王建锋 属主任务由 netTask 改为 defaultTask(配置引擎单一属主)
|
||||
* v1.2 2026-09-21 王建锋 超长守卫契约同步——poll 沉降
|
||||
* 后先按真实 st.size 判超长(旧实现查截断后读长恒假)、
|
||||
* save 输出上限收紧至 CFG_FILE_MAX_LEN、写盘唯一闸口拒写
|
||||
* 超长;超长处置补自检留痕
|
||||
* v1.3 2026-10-15 王建锋 E-16(历史审计 P2,属主文档已删除):新增公开常量
|
||||
* v1.3 2026-10-15 王建锋 新增公开常量
|
||||
* CFG_FILE_MAX_NAME_LEN(段名/键名上限 15 字符)
|
||||
* v1.4 2026-10-25 DTU 项目组 数据可信卡 3:新增 CFG_VAL_INT 有符号十进制值类型
|
||||
* (len = 1/2/4,小端补码)——标定零点偏移等负值需有符号
|
||||
@@ -32,7 +32,7 @@ extern "C" {
|
||||
#include <stddef.h>
|
||||
|
||||
/* 配置文件长度上限(字节);超过 = 文件损坏,按出厂模板重建
|
||||
* (load 与 poll 同判,E-01:poll 在读取前基于真实 st.size 判定) */
|
||||
* (load 与 poll 同判:poll 在读取前基于真实 st.size 判定) */
|
||||
#define CFG_FILE_MAX_LEN 4096u
|
||||
|
||||
/* 引擎可注册段数上限 */
|
||||
@@ -103,7 +103,7 @@ int cfg_file_register(const cfg_section_reg_t *p_reg);
|
||||
* 2. 文件缺失 → 按当前 RAM 副本生成出厂模板写盘(首启建文件)
|
||||
* 3. 文件存在 → 解析:有效键写入 RAM 副本;无效键留工厂默认并留痕
|
||||
* 4. 文件超长(> CFG_FILE_MAX_LEN)= 损坏 → 工厂默认 + 重建模板
|
||||
* + 自检留痕(poll 检出运行期超长文件执行同一契约,E-01)
|
||||
* + 自检留痕(poll 检出运行期超长文件执行同一契约)
|
||||
*/
|
||||
int cfg_file_load(void);
|
||||
|
||||
@@ -116,7 +116,7 @@ int cfg_file_load(void);
|
||||
* 值原位改写,缺失段/键补写;文件缺失/损坏 → 按 RAM 副本重建
|
||||
* 完整文档(含文件头注释);写盘 = 临时文件 + 原子改名;输出超
|
||||
* CFG_FILE_MAX_LEN → 拒绝保存返回 -1(原文件不动 + 自检上报,
|
||||
* 引擎写盘闸口同拒,E-01)
|
||||
* 引擎写盘闸口同拒)
|
||||
*/
|
||||
int cfg_file_save(void);
|
||||
|
||||
@@ -129,7 +129,7 @@ int cfg_file_save(void);
|
||||
* 函数说明:(size, mtime 变更令牌) 相对基线变化且连续 CFG_FILE_SETTLE_CNT
|
||||
* 次轮询稳定 → 先查 st.size 超 CFG_FILE_MAX_LEN = 损坏 → 工厂默认
|
||||
* + 重建模板 + 自检留痕(不触发段回调:保留在线修复通道,重启
|
||||
* 收敛,E-01);否则重载全部段(各段复位工厂默认后解析);挂回调
|
||||
* 收敛);否则重载全部段(各段复位工厂默认后解析);挂回调
|
||||
* 段有差异 → 调用其回调(如触发网络重建);重载后刷新基线
|
||||
* (回调触发的写盘不引发二次重载)
|
||||
*/
|
||||
|
||||
@@ -9,10 +9,10 @@
|
||||
* 修改记录:
|
||||
* v1.0 2026-09-14 王建锋 创建初始版本
|
||||
* v1.1 2026-09-18 王建锋 增 cfg_store_alloc / cfg_store_free / cfg_store_heap_free:
|
||||
* 共享核心临时缓冲改走 FreeRTOS 堆(P0-1,MicroLIB C 堆仅 512B)
|
||||
* 共享核心临时缓冲改走 FreeRTOS 堆(MicroLIB C 堆仅 512B)
|
||||
* v1.2 2026-09-22 王建锋 原子写修复:rename 前先 unlink 已存在目标——本 FatFs
|
||||
* 移植 f_rename 不覆盖既有目标(FR_EXIST),旧实现一切
|
||||
* "目标存在的覆盖写"必然失败(E-01 台架验证暴露)
|
||||
* "目标存在的覆盖写"必然失败(台架验证暴露)
|
||||
* v1.3 2026-10-02 王建锋 四个后端函数纳入卷锁 try 语义(vol_lock_wait(0)):
|
||||
* 与 ftpTask 的 lftpd 文件操作互斥(并发 FTL 写 →
|
||||
* dhara journal 竞争实证);busy 返回 -1 由调用方
|
||||
@@ -22,7 +22,7 @@
|
||||
* FRESULT 里不存在的 FR_NO_SYSTEM,改成实际会返回的
|
||||
* FR_NOT_READY / FR_NO_FILESYSTEM 并写明"非 FR_OK 一律
|
||||
* 转 -1,不要枚举错误码集合"(无行为改动)
|
||||
* v1.4 2026-10-05 王建锋 原子写临时文件由 p_path 推导(P3-2):与目标同目录的
|
||||
* v1.4 2026-10-05 王建锋 原子写临时文件由 p_path 推导:与目标同目录的
|
||||
* 固定 8.3 名 config.TMP,不再硬编码卷根;目标恒为
|
||||
* "/config.ini" 时推导结果与旧值逐字节一致
|
||||
*/
|
||||
@@ -98,7 +98,7 @@ int cfg_store_read(const char *p_path, uint8_t *p_buf, size_t cap, size_t *p_nre
|
||||
#define CFG_STORE_TMP_PATH_MAX 24u
|
||||
|
||||
/*
|
||||
* 函数功能:由目标路径推导同目录的临时文件路径(P3-2)
|
||||
* 函数功能:由目标路径推导同目录的临时文件路径
|
||||
* 入口参数:p_path - 目标文件路径 const char *
|
||||
* p_out - 输出缓冲 char *
|
||||
* cap - 缓冲容量 size_t
|
||||
@@ -144,7 +144,7 @@ int cfg_store_write_atomic(const char *p_path, const uint8_t *p_buf, size_t len)
|
||||
if (p_buf == NULL) {
|
||||
return -1;
|
||||
}
|
||||
/* P3-2:临时文件由 p_path 推导(同目录 + 固定 8.3 名),不再硬编码卷根 */
|
||||
/* 临时文件由 p_path 推导(同目录 + 固定 8.3 名),不再硬编码卷根 */
|
||||
if (s_build_tmp_path(p_path, tmp_path, sizeof(tmp_path)) != 0) {
|
||||
return -1;
|
||||
}
|
||||
@@ -165,7 +165,7 @@ int cfg_store_write_atomic(const char *p_path, const uint8_t *p_buf, size_t len)
|
||||
}
|
||||
/* 本 FatFs 移植的 f_rename 对已存在目标返回 FR_EXIST、不覆盖(ff.c
|
||||
* "new name already in use" 检查,上游同语义)——"目标存在的覆盖写"必须先
|
||||
* unlink 目标再 rename。2026-09-22 台架实证(E-01 验证暴露):缺本步时
|
||||
* unlink 目标再 rename。2026-09-22 台架实证:缺本步时
|
||||
* 一切非首启写(UI 保存/模板重建)静默失败,模拟器后端(POSIX rename
|
||||
* 覆盖语义)照不出。unlink→rename 非原子窗口掉电 = 配置文件暂失,
|
||||
* 开机 load 按"首启"重建工厂模板——与函数失败语义一致,丢失边界 =
|
||||
@@ -262,7 +262,7 @@ const char *cfg_store_tmp_path(void) {
|
||||
* 返回值:缓冲指针(内容未清零);失败返回 NULL void *
|
||||
* 限定条件:任务上下文调用(heap_4 内部临界区自护);释放必须用 cfg_store_free
|
||||
* 函数说明:MCU 端 MicroLIB C 堆仅 512B(startup Heap_Size=0x200),共享核心的 KB 级
|
||||
* 缓冲若走 C 库 malloc 必然返回 NULL,故统一由本接口取 FreeRTOS 堆(P0-1)
|
||||
* 缓冲若走 C 库 malloc 必然返回 NULL,故统一由本接口取 FreeRTOS 堆
|
||||
*/
|
||||
void *cfg_store_alloc(size_t size) {
|
||||
return pvPortMalloc(size);
|
||||
|
||||
+10
-10
@@ -8,17 +8,17 @@
|
||||
* 修改记录:
|
||||
* 2026-09-24 王建锋 创建(自 Src/usart.c USER CODE 区迁出,实现逻辑不变,
|
||||
* 仅变更文件位置:HAL 库生成文件不得包含应用代码)
|
||||
* 2026-10-02 王建锋 僵尸通道喂狗节奏保护(台架 gen-288 实证):round-3 的
|
||||
* 2026-10-02 王建锋 僵尸通道喂狗节奏保护(台架日志代次 288 实证):第 3 轮的
|
||||
* "单次 drain 预算 250ms" 只限单次调用——ORE 锁存在外设软复位后
|
||||
* 毫秒级重锁(PA10 悬空 + RX 使能),背靠背 drain 每轮再耗 100ms
|
||||
* 块超时 → 主循环周期拉到 450~500ms → 100ms 节流的喂狗抽样间隙
|
||||
* 超过 IWDG 最坏容限 1.7s → 停喂 → 误复位(且心跳位全员健康、
|
||||
* 无 stall 日志,纯节奏性饥饿)。修复:发送故障后立即结束本轮
|
||||
* drain 并进入 300ms 发送冷却(冷却期内 drain 直接让位主循环),
|
||||
* 保证喂狗节奏 ≤ ~300ms。根治(USART1 TX-only 灭 ORE 机制)
|
||||
* 另行推进(.ioc 结构性修改 + CubeMX 重生成,见 docs/任务规划/登记簿.md S-3)。
|
||||
* 2026-10-02 王建锋 round 8b 加固:发送超时判据改用 DWT CYCCNT(时间源无关)。
|
||||
* 台架 round 8a 取证(见 docs/子系统设计/看门狗设计说明.md 2026-10-02 变更记录)定位到:defaultTask
|
||||
* 保证喂狗节奏 ≤ ~300ms。根治(把 USART1 改成只发不收)已于 2026-10-26
|
||||
* 由用户裁定不做,本缓解即长期方案;机制详述 docs/子系统设计/控制台单写者日志设计说明.md §12。
|
||||
* 2026-10-02 王建锋 第 8b 轮加固:发送超时判据改用 DWT CYCCNT(时间源无关)。
|
||||
* 台架第 8a 轮取证(见 docs/子系统设计/看门狗设计说明.md 2026-10-02 变更记录)定位到:defaultTask
|
||||
* 冻结在 console 突发行内、无 STALL、复位为 IWDGRSTF。唯一自洽机理 =
|
||||
* HAL_UART_Transmit 的超时判据 `HAL_GetTick() - Tickstart` 依赖
|
||||
* TIM7 的 uwTick —— TIM7 一旦停摆,超时永不成立 → 写者死等 TXE →
|
||||
@@ -75,13 +75,13 @@
|
||||
#define DBG_CON_TX_TIMEOUT_MS 100U /* 单次发送超时:256B 块 @115200 标称 22ms;僵尸通道限时 + 自愈 */
|
||||
#define DBG_CON_DRAIN_BUDGET_MS 250U /* 单次 drain 连续发送上限:让位喂狗轮询(缺失容忍 800ms) */
|
||||
#define DBG_CON_FAULT_COOL_MS 300U /* 故障后发送冷却:ORE 锁存毫秒级重锁,背靠连发无益
|
||||
且拖长主循环周期 → 喂狗节奏饥饿(gen-288 实证);
|
||||
且拖长主循环周期 → 喂狗节奏饥饿(台架日志代次 288 实证);
|
||||
冷却期内 drain 只让位、不发送,下轮重试 */
|
||||
|
||||
/* ==== round 8b:与时间源无关的硬上界(DWT CYCCNT)====
|
||||
/* ==== 第 8b 轮:与时间源无关的硬上界(DWT CYCCNT)====
|
||||
* HAL_UART_Transmit 的超时判据是 `HAL_GetTick() - Tickstart > Timeout`,而 HAL 时基
|
||||
* = TIM7 的 uwTick。TIM7 停摆 → 超时永不成立 → 写者死等 TXE → 唯一喂狗者停摆 →
|
||||
* IWDG 误复位(round 8a 台架现场)。改用 DWT CYCCNT(内核硬件周期计数,不依赖任何
|
||||
* IWDG 误复位(第 8a 轮台架现场)。改用 DWT CYCCNT(内核硬件周期计数,不依赖任何
|
||||
* 中断/uwTick)做判据;DWT 使能失败时退回 HAL_UART_Transmit(旧行为,功能不退化)。 */
|
||||
#define CON_TX_TIMEOUT_CYC (SystemCoreClock / 10U) /* 发送硬上界 = 100ms 对应周期数 */
|
||||
/*
|
||||
@@ -293,7 +293,7 @@ void dbg_console_drain(void) {
|
||||
t0 = (uint32_t)HAL_GetTick();
|
||||
|
||||
/* 故障冷却:僵尸通道(ORE 重锁)秒内不自愈,冷却期内不发送——
|
||||
* 本轮 drain 直接让位主循环,保住喂狗节奏(gen-288 停喂误复位的
|
||||
* 本轮 drain 直接让位主循环,保住喂狗节奏(日志代次 288 停喂误复位的
|
||||
* 直接诱因 = 背靠连发的块超时链)。是否在冷却看 s_con_cooling 独立标志,
|
||||
* **不再拿 tick 值当哨兵**(2026-10-24 实测:故障恰好落在 tick==0 那一拍时,旧写法
|
||||
* 记出的 cool_until==0 等于没记冷却,下一轮立刻再耗一次 100ms 块超时)。
|
||||
@@ -403,7 +403,7 @@ void dbg_console_drain(void) {
|
||||
* 环满等待(vTaskDelay / DWT 判据)一律在临界区之外,区内绝不睡眠。
|
||||
* 写者自身记录日志时先排空缓冲,避免自等。
|
||||
* 等待上界优先用 DWT 真实时间(CON_APPEND_WAIT_CYC,时间源无关)——
|
||||
* tick 停摆时 vTaskDelay 本身也会停,故不能用它作唯一判据(round 8b)。
|
||||
* tick 停摆时 vTaskDelay 本身也会停,故不能用它作唯一判据(第 8b 轮加固)。
|
||||
*/
|
||||
static int con_append(const char *buf, uint16_t len) {
|
||||
uint32_t waited = 0U;
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
* 模块功能:定长 64B SOE(Sequence-of-Events)记录的构建与解析:
|
||||
* CP56Time2a 时标 + 记录类型 + 元件号 + 单调序号 + 值 + 校验和。
|
||||
* **零 I/O、零 OS 依赖**(仅 stdint/string)→ 主机侧 L0.5 离线单测
|
||||
* (落实登记簿 A-4 约束 1"协议/记录与硬件 I/O 分离")。
|
||||
* (分层约束:协议/记录与硬件 I/O 分离)。
|
||||
* 记录口径(docs/算法/继电保护功能复现规划.md §3.4 L4-4/L4-5,B9):
|
||||
* 落盘文件 = /log/soe.log(8.3 合规;FF_USE_LFN=0 约束),定长 64B 一条
|
||||
* (照 run.log 先例:count = filesize/64 O(1)、容量满删除重建)。
|
||||
|
||||
+1
-1
@@ -152,7 +152,7 @@ void syslog_drain(void);
|
||||
* 返回值:0 - 成功;-1 - 卷占用或写失败(证据可能回滚,**不阻断判定**) int
|
||||
* 限定条件:poller(defaultTask)上下文调用;卷锁一律 try(绝不阻塞,
|
||||
* 本上下文 = 看门狗喂狗点)
|
||||
* 函数说明:round 7 取证通道(设计见 docs/子系统设计/看门狗设计说明.md 2026-10-02 变更记录:
|
||||
* 函数说明:第 7 轮取证通道(设计见 docs/子系统设计/看门狗设计说明.md 2026-10-02 变更记录:
|
||||
* 误复位后 RAM 态随复位消失、stall 行经僵尸串口丢失,故证据必须落盘)。
|
||||
* 内部在释放卷锁后立刻追加一条 s_append(SYSLOG_RUN, ...) 写另一个
|
||||
* 文件——FTL 单页缓存语义为"写到不同 LPN 才把前一脏页刷进 NAND",
|
||||
|
||||
+19
-19
@@ -15,7 +15,7 @@
|
||||
* (本任务 = 看门狗喂狗点,绝不阻塞等锁)
|
||||
* v1.3 2026-10-02 王建锋 新增 sys_log_evidence():把看门狗停喂现场
|
||||
* 证据落 /log/wdev.log(掉电不丢)+ flush 诱导
|
||||
* (round 7 纯取证,见 docs/子系统设计/看门狗设计说明.md 2026-10-02 变更记录)
|
||||
* (第 7 轮纯取证,见 docs/子系统设计/看门狗设计说明.md 2026-10-02 变更记录)
|
||||
* v1.5 2026-10-24 王建锋 主机单测批次照出的缺陷一起修,本文件内 5 条
|
||||
* (A/B/C/D/F;E 在 App/cfg/cfg_file_store_mcu.c):
|
||||
* A 卷忙整批静默丢日志——syslog_drain 批量路径补
|
||||
@@ -34,10 +34,10 @@
|
||||
* 数组满各立 volatile 抑制位,与 B 的 drain 侧那位分
|
||||
* 开):卷被长期占用 + A 项回投让队列常驻满时,串口
|
||||
* 不再按投递频率刷 "queue full, drop"
|
||||
* v1.4 2026-10-04 王建锋 P2-9:syslog_mcu_mounted() 改为幂等 + 卷锁保护;
|
||||
* v1.4 2026-10-04 王建锋 syslog_mcu_mounted() 改为幂等 + 卷锁保护;
|
||||
* defaultTask 每 2s 复查,建 /log 曾失败时自动重试
|
||||
* (原"仅调用一次"→ 失败后 drain 永久 no-op)。
|
||||
* P2-10:s_read_raw() 纳入卷锁 try 语义,与写者
|
||||
* 读者 s_read_raw() 纳入卷锁 try 语义,与写者
|
||||
* s_append 的"容量满 unlink 重建"互斥,消除
|
||||
* "UI 读记录(FA_READ) 时文件被 unlink"竞态窗口。
|
||||
*/
|
||||
@@ -63,7 +63,7 @@ static const uint32_t s_caps[SYSLOG_COUNT] = { SYSLOG_RUN_MAX, SYSLOG_SELFTEST_M
|
||||
|
||||
#define SYSLOG_QUEUE_MAX 8u
|
||||
|
||||
/* N-24(2026-10-15,报告 N-16 写侧):一次排空的合并写入上限。
|
||||
/* 日志读写放大收口(2026-10-15,写侧):一次排空的合并写入上限。
|
||||
* 8 条 × 64 B = 512 B 静态缓冲(不吃任务栈),与队列深度 SYSLOG_QUEUE_MAX 同值
|
||||
* ——即"一轮排空最多一次 open/write/close"。 */
|
||||
#define SYSLOG_BATCH_MAX 8u
|
||||
@@ -84,13 +84,13 @@ typedef struct {
|
||||
} syslog_entry_t;
|
||||
|
||||
static syslog_entry_t s_preboot[SYSLOG_QUEUE_MAX]; /* 启动前待发数组(RTOS 前单生产者/后单消费者,免锁) */
|
||||
static syslog_entry_t s_batch_src[SYSLOG_BATCH_MAX]; /* 本批条目的原始来源(N-24:未落盘的尾部要回投队列,故必须留住 id + desc 副本);
|
||||
static syslog_entry_t s_batch_src[SYSLOG_BATCH_MAX]; /* 本批条目的原始来源(未落盘的尾部要回投队列,故必须留住 id + desc 副本);
|
||||
* 唯一写者 defaultTask,走静态不吃栈 */
|
||||
static volatile uint8_t s_preboot_cnt; /* 数组条目数 */
|
||||
static osMessageQueueId_t s_queue; /* 运行期消息队列(syslog_drain 首次调用时惰性创建) */
|
||||
static const osMessageQueueAttr_t s_queue_attr = { .name = "syslog" };
|
||||
static volatile uint8_t s_fs_ready; /* 卷挂载 + /log 目录就绪后置位 */
|
||||
static volatile uint8_t s_mount_fail_reported; /* P2-9:建目录失败日志去重(成功复位) */
|
||||
static volatile uint8_t s_mount_fail_reported; /* 建目录失败日志去重(成功复位) */
|
||||
/* B 项整改(2026-10-24):syslog_drain 批量写失败"整批回投"这条留痕的节流位——
|
||||
* 只在"从正常转入故障"的那一次打一条,成功一轮后复位。与 s_append_many 内部的
|
||||
* s_batch_fault 各管一层:那位压的是"这个文件写坏了",本位压的是"这批条目回投了"。
|
||||
@@ -108,7 +108,7 @@ static uint8_t s_requeue_reported[SYSLOG_COUNT];
|
||||
static volatile uint8_t s_post_qfull_reported; /* 队列满丢弃:已留痕? */
|
||||
static volatile uint8_t s_post_pboot_full_reported;/* 启动前数组满丢弃:已留痕? */
|
||||
|
||||
/* N-24(2026-10-15,报告 N-16 读侧):8 条滑动读窗口 + 写者代数。
|
||||
/* 日志读写放大收口(2026-10-15,读侧):8 条滑动读窗口 + 写者代数。
|
||||
* 窗口 512 B 走静态(不吃任务栈;defaultTask/uiTask 最坏链见 stack_report.py,
|
||||
* 预算余量见 docs/内存管理/RAM预算台账.md)。s_rd_gen 由写者自增、读者比对,
|
||||
* 对齐 32 位在 ARM 上原子,跨任务无需临界区。 */
|
||||
@@ -195,7 +195,7 @@ static int s_is_selfheal_code(FRESULT fr) {
|
||||
* p_rec - 输出缓冲(SYSLOG_REC_SIZE 字节,不可为 NULL) uint8_t *
|
||||
* 返回值:无
|
||||
* 限定条件:时间戳未就绪时落 "1970-01-01 08:00:00" 占位串
|
||||
* 函数说明:N-24(2026-10-15,报告 N-16)从 s_append 抽出,供"单条写"与
|
||||
* 函数说明:日志读写放大收口(2026-10-15)时从 s_append 抽出,供"单条写"与
|
||||
* "批量写"共用——两条路径必须产出逐字节相同的记录,否则 UI 按定长
|
||||
* 字段偏移解析会错位。
|
||||
*/
|
||||
@@ -327,7 +327,7 @@ static int s_append(syslog_id_t id, const char *desc) {
|
||||
* 返回值:0 - 成功;-1 - 写失败;-2 - 卷被占用(顺延,非故障);
|
||||
* -3 - 剩余容量不足 n 条,本批不能走快路径(交回按条慢路径做轮转) int
|
||||
* 限定条件:仅 defaultTask(唯一写者)调用;卷已挂载
|
||||
* 函数说明:N-24(2026-10-15,报告 N-16 写侧)。旧实现每条记录一轮
|
||||
* 函数说明:日志读写放大收口(2026-10-15,写侧)。旧实现每条记录一轮
|
||||
* f_open + f_write(64 B) + f_close,而 f_close 要回写目录项、f_write
|
||||
* 要对目标扇区做读-改-写 → 每条日志 2~4 次 NAND 页读写。本函数把
|
||||
* 同一次排空里属于同一文件的最多 8 条合并成一次 512 B 追加写。
|
||||
@@ -405,15 +405,15 @@ static int s_append_many(syslog_id_t id, const uint8_t *p_buf, uint32_t n) {
|
||||
* idx - 记录序号(0 起) uint32_t
|
||||
* p_rec- 输出缓冲(SYSLOG_REC_SIZE 字节,不可为 NULL) uint8_t *
|
||||
* 返回值:0 - 成功;-1 - 越界/读失败/卷忙 int
|
||||
* 函数说明:P2-10(2026-10-04):读者纳入卷锁(try),与写者 s_append 的
|
||||
* 函数说明:读者纳入卷锁(try),与写者 s_append 的
|
||||
* "容量满 unlink 重建"互斥——原实现读时无锁,写者 unlink 与读者
|
||||
* open/read 可交错,读到的可能是半删除态文件。try 语义(timeout=0):
|
||||
* 本路径可由 uiTask 调用,uiTask 在 required_mask 内、须每 100ms 喂
|
||||
* 心跳,绝不能阻塞等锁;busy = 本次读失败(UI 下轮刷新重读,非故障)。
|
||||
*
|
||||
* N-24(2026-10-15,报告 N-16 读侧):加**8 条滑动窗口**。旧实现每读
|
||||
* 日志读写放大收口(2026-10-15,读侧):加**8 条滑动窗口**。旧实现每读
|
||||
* 一条记录都要 open + lseek + read + close 一轮,UI 翻 20 条记录 =
|
||||
* 20 次目录项/ FAT 往返(报告 N-16 的"UI 翻记录逐条 open/seek/close")。
|
||||
* 20 次目录项/ FAT 往返(即"UI 翻记录逐条 open/seek/close"这笔开销)。
|
||||
* 记录定长 64 B,故一次 open + 一次 lseek + 一次 f_read 就能取回连续
|
||||
* 8 条(512 B),命中窗口时零 I/O 直接从 RAM 返回。API 与调用方
|
||||
* (ui_page_runlog / ui_page_selftest 的逐行取数)一律不变。
|
||||
@@ -424,7 +424,7 @@ static int s_append_many(syslog_id_t id, const uint8_t *p_buf, uint32_t n) {
|
||||
*/
|
||||
static int s_read_raw(syslog_id_t id, uint32_t idx, uint8_t *p_rec) {
|
||||
static FIL f; /* static:省 528 B 栈;open~close 全在卷锁区间内
|
||||
* (uiTask 与写者任务由 vol_lock 串行化,见上方 P2-10 说明) */
|
||||
* (uiTask 与写者任务由 vol_lock 串行化,见上方读者卷锁说明) */
|
||||
FRESULT fr;
|
||||
UINT br = 0u;
|
||||
uint32_t total;
|
||||
@@ -504,8 +504,8 @@ int syslog_mcu_init(void) {
|
||||
* 入口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:defaultTask f_mount/f_mkfs 成功后调用;**可重复调用(幂等)**——
|
||||
* 产品运行期 defaultTask 每 2s 复查一次(见下 P2-9 说明)
|
||||
* 函数说明:P2-9(2026-10-04):原实现"仅调用一次",若此处建 /log 失败则
|
||||
* 产品运行期 defaultTask 每 2s 复查一次(见下方幂等说明)
|
||||
* 函数说明:原实现"仅调用一次",若此处建 /log 失败则
|
||||
* s_fs_ready 恒 0 → syslog_drain 永久 no-op(运行期日志与自检全部
|
||||
* 滞留/丢弃),且无任何重试。现改为幂等:s_fs_ready 已置位则立即
|
||||
* 返回;否则重试建目录。defaultTask 在 STORAGE_READY 置位后每 2s
|
||||
@@ -570,7 +570,7 @@ void syslog_drain(void) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* N-24(2026-10-15,报告 N-16 写侧):一批一批地排空——同一文件、最多
|
||||
/* 日志读写放大收口(2026-10-15,写侧):一批一批地排空——同一文件、最多
|
||||
* SYSLOG_BATCH_MAX 条合并成一次 open/write/close(旧实现每条一轮
|
||||
* open/write/close,每条日志 2~4 次 NAND 页读写)。
|
||||
* 取数规则:preboot 数组按下标"看而不删",整批落盘成功才真正删除;
|
||||
@@ -831,13 +831,13 @@ int syslog_last_epoch(syslog_id_t id, uint32_t *p_epoch) {
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:把一条取证行追加到 /log/wdev.log(掉电不丢;round 7 纯取证)
|
||||
* 函数功能:把一条取证行追加到 /log/wdev.log(掉电不丢;第 7 轮纯取证)
|
||||
* 入口参数:p_desc - 取证描述行(调用方已格式化;不可为 NULL) const char *
|
||||
* 返回值:0 - 成功;-1 - 卷占用或写失败(本条证据丢失,不阻断判定) int
|
||||
* 限定条件:poller(defaultTask)上下文调用;卷锁一律 try(绝不阻塞)
|
||||
* 函数说明:
|
||||
* 为什么必须落盘:误复位后 RAM 态随复位消失、`[WD] task stall` 行经僵尸串口
|
||||
* 可能丢失(2026-10-02 台架 round 3~6 绕圈的判据缺口),故证据必须落到
|
||||
* 可能丢失(2026-10-02 台架第 3~6 轮绕圈的判据缺口),故证据必须落到
|
||||
* **掉电不丢**的盘上,复位后经 FTP RETR 回读。
|
||||
* 第二条写(旧称"flush 诱导"):写完 wdev.log 后立刻 s_append(SYSLOG_RUN, ...)
|
||||
* 再落一条 run.log。保留理由是**多一条独立证据**(run.log 有 UI 记录页与 FTP
|
||||
@@ -845,7 +845,7 @@ int syslog_last_epoch(syslog_id_t id, uint32_t *p_epoch) {
|
||||
* disk_ioctl(CTRL_SYNC) 已经调 ftl_flush_cache() 把 wdev.log 的脏页刷进 NAND
|
||||
* (nand_ftl.c 的 CTRL_SYNC 分支),所以"写另一个文件(不同 LPN)逼出前一脏页"
|
||||
* 从来不是必要条件。
|
||||
* ⚠ 2026-10-02 round 3~8 那次"复位后写入整段丢失"的根因是 dhara resume 锚错
|
||||
* ⚠ 2026-10-02 台架第 3~8 轮那次"复位后写入整段丢失"的根因是 dhara resume 锚错
|
||||
* checkpoint 使 journal 整段回退(docs/架构设计/STM32F4-Base存储架构说明.md
|
||||
* §5.4.1),不是这层缓存:单页缓存最多丢最后未跨页那笔(≤512 B),丢不出
|
||||
* "整段"。空闲周期 flush 兜底已接线(App/fs/vol_idle_sync.h:STORAGE_READY
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* 模块名称:IEC 60870-5-104 ASDU 编解码(应用层,纯逻辑)
|
||||
* 模块功能:104 规约应用服务数据单元(ASDU)的类型/传送原因常量、组帧与解析。
|
||||
* **零 I/O、零平台依赖**(仅 stdint.h)→ 主机侧 L0.5 可离线单测
|
||||
* (登记簿 A-4 约束 1"协议解析与硬件 I/O 分离")。
|
||||
* (分层约束:协议解析与硬件 I/O 分离)。
|
||||
* 口径来源:docs/算法/继电保护功能复现规划.md §1.5(B0 产品定义)——
|
||||
* 公共地址 2 字节、传送原因 2 字节、信息体地址 IOA **3 字节**(101 为 2 字节);
|
||||
* 遥测上送**短浮点** M_ME_NC_1(系数 = 1);遥信双点上双点、其余单点;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
* → clock_sync 回调)、试验(C_TS)与进程复位(C_RP,清快照)。
|
||||
* **零 I/O**:收 = on_rx 喂 TCP 流片段,发 = send_apdu 回调,
|
||||
* 时基 = 调用方注入 now_ms(tick ms,回绕安全:差值法)
|
||||
* → 全栈(asdu+pts+link)主机侧 L0.5 可离线跑(登记簿 A-4 约束 1)。
|
||||
* → 全栈(asdu+pts+link)主机侧 L0.5 可离线跑(分层约束)。
|
||||
* 参数(DL/T 634.5104 标准默认,§1.5 未另定):k=12 w=8 t1=15s t2=10s t3=20s。
|
||||
* 端口:TCP **2404**(§1.5 B0 定义;本 DTU = 被控站/服务器,主站连入)。
|
||||
* 线程约定:单实例单任务上下文(板端 = 104 服务在 netTask 消息循环内驱动,
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* 2026-10-25 DTU 项目组 卡 9/卡 11(「数据可信」,道D):YC 板端源改读
|
||||
* L2 遥测快照(App/algo/meas.h,meas_get 无锁双区);
|
||||
* 削顶五路由快照品质 CLIPPED 驱动线上 IV(不再绑死 -1,
|
||||
* V0.2/N-18 零改动自动启用);0x401d 绑频率源(卡 11);
|
||||
* V0.2 消除双转换器无共同采样触发后零改动自动启用);0x401d 绑频率源(卡 11);
|
||||
* 死区改运行时副本 + set/get 入口;COV 遥测腿 = 死区
|
||||
* 连续 2 点确认(板端按快照 seq 去重)+ 品质翻转即报。
|
||||
* 单测 = test/host/iec104 TC-IEC104-025~028。
|
||||
@@ -52,8 +52,8 @@ typedef struct {
|
||||
* 频率点不适用"额定百分比"(额定附近 0.5 Hz 与旧值同量级,维持 0.5 并随
|
||||
* 卡 10 运行参数段可调)。自卡 9 起本表**只作工厂默认载体**:生效死区 =
|
||||
* App/algo/run_par.c 的 [run_par] 引擎副本(卡 10 真源收编,本层不再存副本);削顶五路不再 sem=-1 绑死,而是绑快照槽、由品质 CLIPPED
|
||||
* 驱动线上 IV——V0.2(N-18)削顶消失后点位零改动自动启用(docs/任务规划/
|
||||
* 数据可信.md §10 的 N-18 行)。
|
||||
* 驱动线上 IV——V0.2 削顶消失后点位零改动自动启用(docs/任务规划/
|
||||
* 数据可信.md §10 的「双转换器无共同采样触发」行)。
|
||||
* ⚠ 过渡期(eng=0 引脚电压域,卡 12 标定前):上送值本身非二次值,死区
|
||||
* 数字按本域偏保守(迟报不漏报);标定落地后复核(「数据可信」§2.3 表
|
||||
* 第 2 行)。判定 = 死区连续 2 点确认(卡 9),见 iec104_pts.h COV 说明。 */
|
||||
|
||||
@@ -3,13 +3,13 @@
|
||||
* 模块功能:104 点表(遥测 YC / 遥信 YX / 遥控 YK / 定值)的定义、读取与变化检测
|
||||
* (COV:遥信变位 + 遥测死区)。**数据源经 vtable 注入**:板端绑
|
||||
* phasor/prot_elm/prot_set 真实源;主机单测绑假源 → 协议栈全链
|
||||
* (asdu+pts+link)可离线跑(登记簿 A-4 约束 1)。
|
||||
* (asdu+pts+link)可离线跑(分层约束:协议解析与硬件 I/O 分离)。
|
||||
* 点表口径(docs/算法/继电保护功能复现规划.md §1.5,验收 = 省电科院点表):
|
||||
* - YC:IOA 0x4011~0x4018 + 0x401d(频率),短浮点、系数 1;数据源 =
|
||||
* L2 遥测快照(App/algo/meas.h,「数据可信」卡 9/11):电气 8 路绑快照槽,
|
||||
* 有效性由快照品质驱动——V0.1 削顶路(docs/算法/继电保护功能复现规划.md
|
||||
* §1.6)快照置 CLIPPED → 本层映射为线上 IV(V0.2/N-18 后自动启用,绑定零
|
||||
* 改动,见 docs/任务规划/数据可信.md §10 行 N-18);0x401d 绑频率源,
|
||||
* §1.6)快照置 CLIPPED → 本层映射为线上 IV(V0.2 后自动启用,绑定零
|
||||
* 改动,见 docs/任务规划/数据可信.md §10「双转换器无共同采样触发」行);0x401d 绑频率源,
|
||||
* freq_valid()==0 → IV;
|
||||
* - YX:IOA 1~33(§1.5 表(3));双点 = 开关位置类(YX1~4,本期无硬件 → IV),
|
||||
* 保护动作类 YX15~23 映射 prot_elm 状态,闭锁类 YX31~33 映射 prot_set;
|
||||
|
||||
@@ -10,10 +10,10 @@
|
||||
* v1.1 2026-08-28 王建锋 改为 TCP Server,迁入独立任务 timeSyncTask,
|
||||
* 所有 socket 操作经 net_socket 线程安全 API 走消息队列,
|
||||
* netTask 仅负责 net_poll/net_process_messages,多任务并发安全
|
||||
* v1.2 2026-10-02 王建锋 round 8b 消抖:等待片长 200ms→50ms,并把各处长
|
||||
* v1.2 2026-10-02 王建锋 第 8b 轮消抖:等待片长 200ms→50ms,并把各处长
|
||||
* 等待(500/2000/200ms)统一走 time_sync_delay_fed()
|
||||
* 按 ≤50ms 分片、每片喂心跳——消除"心跳滑出两个连续
|
||||
* 100ms 抽样窗"的节拍抖动(round 7 台架 snap=0x17)
|
||||
* 100ms 抽样窗"的节拍抖动(第 7 轮台架 snap=0x17)
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
@@ -33,10 +33,10 @@
|
||||
#define TIME_SYNC_PORT 8888
|
||||
|
||||
/*
|
||||
* E-02 头阻塞修复 + L3 pending 化(timeSync 侧;lftpd 侧见 lftpd_inet.c):
|
||||
* 头阻塞修复 + L3 pending 化(timeSync 侧;lftpd 侧见 lftpd_inet.c):
|
||||
* 旧实现用单次 30s 阻塞 net_recv(默认 NET_RECV_TIMEOUT_MS)等时间包——等待期间
|
||||
* netTask(全部 socket 操作的唯一执行者)被独占,其余任务的网络操作(远端显示 /
|
||||
* FTP)整体停摆(历史审计 E-02,属主文档已删除)。
|
||||
* FTP)整体停摆(历史审计遗留问题)。
|
||||
* 2026-09-22 先落地分片有界读(5ms 片),同日 L3 pending 化后改为"挂起请求":
|
||||
* 每次等待 = 一次挂起登记(netTask 零占用,完成按主循环节拍检查),调用方
|
||||
* 单次超时 = 剩余总预算(总等待 ≤ TIME_SYNC_TOTAL_BUDGET_MS,语义与旧 30s
|
||||
@@ -47,7 +47,7 @@
|
||||
/* 看门狗分片等待:单次 net_recv_to 的等待片长上限。须显著小于看门狗抽样窗
|
||||
* (task_watchdog.c WD_POLL_PERIOD_MS=100ms)——片长 ≤50ms 时每片前喂的心跳
|
||||
* 必然落在同一个 100ms 抽样窗内,消除"心跳恰好滑过两个连续窗口"的边界抖动
|
||||
* (round 7 台架稳态 snap=0x17 即抽样窗 vs 心跳节拍的良性错位,round 8b 消抖)。
|
||||
* (第 7 轮台架稳态 snap=0x17 即抽样窗 vs 心跳节拍的良性错位,第 8b 轮消抖)。
|
||||
* 语义上仅把一次长等待拆成多次短等待,总预算不变。 */
|
||||
#define TIME_SYNC_WAIT_SLICE_MS 50U
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
* 函数说明:看门狗抽样窗为 100ms(task_watchdog.c WD_POLL_PERIOD_MS)。把任意长
|
||||
* 等待切成 ≤TIME_SYNC_WAIT_SLICE_MS 的短片、每片前喂心跳,使心跳必然
|
||||
* 落在单个抽样窗内——语义等价于一次长延时,仅把"心跳节奏"钉在窗内
|
||||
* (round 8b 消抖;不改变总延时预算)。
|
||||
* (第 8b 轮消抖;不改变总延时预算)。
|
||||
*/
|
||||
static void time_sync_delay_fed(uint32_t ms) {
|
||||
while (ms > 0U) {
|
||||
@@ -180,7 +180,7 @@ static void time_sync_handle_conn(int conn_sock) {
|
||||
DBG_INFO("time_sync: wall clock updated (epoch=%lu)", (unsigned long)epoch);
|
||||
}
|
||||
} else {
|
||||
/* E-02 收口(docs/网络/网络自愈架构设计.md §5.2 E13):本层放弃会话 → 主动拆除。
|
||||
/* 头阻塞收口(docs/网络/网络自愈架构设计.md §5.2 E13):本层放弃会话 → 主动拆除。
|
||||
* 挂起但 TCP 仍活的客户端(PC 内核自动 ACK keepalive,芯片僵尸判不死)
|
||||
* 会永久占住单连接监听;EOF 路径 drop 幂等返 0(net 层已 relisten)。
|
||||
* 模拟器多连接后端 conn≠ls,由 sim 实现按数据 fd 关闭。 */
|
||||
|
||||
+2
-2
@@ -28,7 +28,7 @@
|
||||
* 创建日期:2026-07-19
|
||||
* 修改记录:
|
||||
* v1.0 2026-07-19 王建锋 创建初始版本
|
||||
* v1.1 2026-10-05 王建锋 P3-11:注明"预留桩(未启用)",避免误认为已实现
|
||||
* v1.1 2026-10-05 王建锋 注明"预留桩(未启用)",避免误认为已实现
|
||||
* v2.0 2026-10-10 王建锋 B1:实现真实消费者(1ms 节拍 + 原子快照 + 帧统计 +
|
||||
* 看门狗心跳),由 Src/freertos.c 创建为 adcTask
|
||||
* v2.1 2026-10-10 王建锋 日志门控:首帧/周期统计仅在 dbg_console_writer_ready()
|
||||
@@ -75,7 +75,7 @@
|
||||
* 单调性/错误计数增量)——严重事件与故障电平上升沿
|
||||
* 打 DBG_ERROR + 每类每次开机一次 SYSLOG_SELFTEST;
|
||||
* 恢复打 DBG_INFO;心跳日志增 tgap(TPAFE 侧丢帧,
|
||||
* 与 adc1_gap 对称)。判定不在本文件(A-4:算法与
|
||||
* 与 adc1_gap 对称)。判定不在本文件(算法与
|
||||
* I/O 分离),本文件只做"消费计数 → 告警"的编排
|
||||
* v3.0 2026-10-21 王建锋 修复"任务初始化入口从未被调用":adc_task_func 进入
|
||||
* 循环之前先调 adc_task_init()。原实现下判定层状态靠
|
||||
|
||||
+12
-12
@@ -2,7 +2,7 @@
|
||||
* 模块名称:Default Application Task
|
||||
* 模块功能:主控任务(defaultTask)任务体实现:
|
||||
* ① 启动段:存储 bring-up(挂载 + 空卷自动建卷 + 卷健康探针两击 +
|
||||
* 自愈重格式化,E-31 失败后主循环 3×2s 有界重试)+ 网络配置加载
|
||||
* 自愈重格式化,挂载失败后主循环 3×2s 有界重试)+ 网络配置加载
|
||||
* + STORAGE_READY 屏障 + "保存网络配置"回调注册
|
||||
* ② 控制台单写者:dbg_console_writer_attach 注册 + 主循环 dbg_console_drain
|
||||
* 排空(串口唯一发送者,见 App/log/console_log.c 设计说明)
|
||||
@@ -65,7 +65,7 @@
|
||||
* 2026-10-12(B5):堆扩至 56KB(.ioc configTOTAL_HEAP_SIZE=57344,CCM 腾给
|
||||
* protectTask/iec104Task 栈),阈值**不随堆缩放**——它是绝对字节安全线。 */
|
||||
#define HEAP_LOW_WATER_B 3072u
|
||||
/* E-31 自愈策略(2026-09-21 审计 P0 修复):启动期存储挂载失败后,
|
||||
/* 存储自愈策略(2026-09-21 审计 P0 修复):启动期存储挂载失败后,
|
||||
* 主循环按 2s 周期重跑完整 bring-up 的次数上限;耗尽则维持无存储降级
|
||||
* (自检已上报、卷保留、不重格式化),等下次上电重新 resume */
|
||||
#define STORAGE_BRINGUP_RETRIES 3u
|
||||
@@ -106,7 +106,7 @@ static FRESULT storage_bring_up(FATFS *fs);
|
||||
* 只在内核运行期被调度,启动段允许使用 CMSIS 对象
|
||||
* 函数说明:启动段 = 存储 bring-up + 保存回调注册 + 控制台写者注册;
|
||||
* 主循环 = 事件等待(配置保存/控制台待发,50ms 兜底)→ 控制台排空
|
||||
* → 配置保存执行 → 2s 配置轮询(含 E-31 有界重试)→ LED 心跳
|
||||
* → 配置保存执行 → 2s 配置轮询(含存储自愈有界重试)→ LED 心跳
|
||||
* → syslog 落盘 → 栈水位观测
|
||||
*/
|
||||
void default_task_func(void *argument) {
|
||||
@@ -121,7 +121,7 @@ void default_task_func(void *argument) {
|
||||
* 完整 bring-up 序列(挂载 + 空卷自动建卷 + 健康探针 + 自愈重格式化
|
||||
* + 日志挂载 + 配置加载 + 就绪屏障)实现于 storage_bring_up()。
|
||||
*
|
||||
* E-31(2026-09-21 审计,P0 修复):dhara_map_resume 失败不再被当
|
||||
* 存储自愈(2026-09-21 审计,P0 修复):dhara_map_resume 失败不再被当
|
||||
* "新卷"静默重格式化(扫描中任何一次瞬态毛刺都会销毁全部用户数据)——
|
||||
* nand_ftl 仅把 DHARA_E_TOO_BAD(真空白)判为新卷,其余失败使
|
||||
* disk_initialize 失败 → f_mount 失败 → 下方自检上报并保留卷;
|
||||
@@ -131,7 +131,7 @@ void default_task_func(void *argument) {
|
||||
* 小写两击确认、FR_INVALID_NAME 不入重格式化集、写保护类只上报,
|
||||
* 详见该函数注释)。 */
|
||||
#if !defined(TEST_SUITE_STORAGE)
|
||||
/* E-31 自愈:存储 bring-up 剩余重试次数(0 = 已就绪或已耗尽);
|
||||
/* 存储自愈:存储 bring-up 剩余重试次数(0 = 已就绪或已耗尽);
|
||||
* 须在启动块之前声明(启动块失败路径写入、主循环读取) */
|
||||
uint32_t storage_retry = 0u;
|
||||
#endif
|
||||
@@ -146,12 +146,12 @@ void default_task_func(void *argument) {
|
||||
if (fr != FR_OK) {
|
||||
/* 存储不可写:自检故障仅能滞留待发队列(串口已告警) */
|
||||
syslog_post(SYSLOG_SELFTEST, "filesystem fault");
|
||||
/* E-31 自愈:启动主循环中的有界重试(每 2s 一次) */
|
||||
/* 存储自愈:启动主循环中的有界重试(每 2s 一次) */
|
||||
storage_retry = STORAGE_BRINGUP_RETRIES;
|
||||
}
|
||||
|
||||
/* 注册"保存网络配置"请求通知:UI(uiTask)投递 → 置事件 → 本任务立即执行写盘
|
||||
* (配置引擎属主 = 本任务;UI 保存不再直接触碰配置文件引擎,P1-3) */
|
||||
* (配置引擎属主 = 本任务;UI 保存不再直接触碰配置文件引擎) */
|
||||
net_cfg_set_save_req_cb(default_task_net_cfg_save_notify);
|
||||
#else
|
||||
/* STORAGE 套件构建:挂载/配置引擎/日志盘写全部跳过,存储由 testTask 独占 */
|
||||
@@ -177,9 +177,9 @@ void default_task_func(void *argument) {
|
||||
task_wd_start();
|
||||
|
||||
for (;;) {
|
||||
/* round 8 取证:进段打点——每步前记录段号到 .noinit 槽(复位不丢)。
|
||||
/* 第 8 轮取证:进段打点——每步前记录段号到 .noinit 槽(复位不丢)。
|
||||
* 误复位后开机 `PREVRUN seg=N` 即"上一轮主循环死在哪个调用点"
|
||||
* (round 7 初判"poller 自身停摆"经 round 8b 更正:真因是 IWDG 超时被静默
|
||||
* (第 7 轮初判"poller 自身停摆"经第 8b 轮更正:真因是 IWDG 超时被静默
|
||||
* 配成复位默认值,见 docs/子系统设计/看门狗设计说明.md 2026-10-02 变更记录)。 */
|
||||
task_wd_seg(TASK_WD_SEG_POLL);
|
||||
/* 心跳抽样与喂狗判定(内部节流至 100ms;全员到位才喂,否则累计停喂) */
|
||||
@@ -240,7 +240,7 @@ void default_task_func(void *argument) {
|
||||
if ((cfg_poll_cnt++ % 40u) == 0u) {
|
||||
#if !defined(TEST_SUITE_STORAGE)
|
||||
task_wd_seg(TASK_WD_SEG_CFGPOLL);
|
||||
/* E-31 自愈:启动期挂载失败时,每 2s 重跑完整 bring-up——
|
||||
/* 存储自愈:启动期挂载失败时,每 2s 重跑完整 bring-up——
|
||||
* 瞬态毛刺(如 dhara resume 扫描的瞬时 SPI 故障)通常第二次
|
||||
* 即恢复;成功后由 storage_bring_up 补跑启动时跳过的
|
||||
* "存储就绪"序列(日志挂载/配置加载/屏障置位,均幂等);
|
||||
@@ -257,7 +257,7 @@ void default_task_func(void *argument) {
|
||||
syslog_post(SYSLOG_SELFTEST, "filesystem fault");
|
||||
}
|
||||
}
|
||||
/* P2-9:卷已就绪(STORAGE_READY)但日志后端未就绪(建 /log 曾失败,
|
||||
/* 卷已就绪(STORAGE_READY)但日志后端未就绪(建 /log 曾失败,
|
||||
* s_fs_ready 恒 0 → syslog_drain 永久 no-op)→ 2s 周期重试(幂等;
|
||||
* 就绪后 syslog_mcu_mounted 立即返回,仅一次 volatile 读)。 */
|
||||
if (sys_evtHandle != NULL
|
||||
@@ -410,7 +410,7 @@ static void stack_probe_poll(void) {
|
||||
* 下整体不参与编译(卷由 testTask 独占)
|
||||
* 函数说明:
|
||||
* 由 defaultTask 调用:启动期一次;若失败,主循环每 2s 重试至多
|
||||
* STORAGE_BRINGUP_RETRIES 次(E-31 自愈策略,2026-09-21):dhara_map_resume
|
||||
* STORAGE_BRINGUP_RETRIES 次(存储自愈策略,2026-09-21):dhara_map_resume
|
||||
* 失败分类修复后(nand_ftl.c 仅真空白判新卷,其余失败保留卷并返回
|
||||
* disk_initialize 失败),重试让瞬态毛刺(SPI 瞬时故障/扫描窗口抖动)
|
||||
* 在数秒内免重启恢复,成功时补跑启动期跳过的就绪序列(日志挂载/配置
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* 模块功能:2404 监听 + 单连接 socket 收发 + 把字节流喂给 iec104_link 纯逻辑
|
||||
* 层;对时经 sys_clock_set_unix 落墙钟;数据源走 iec104_pts 默认源
|
||||
* (phasor / prot_elm / prot_set)。协议语义全部在 App/protocol/iec104,
|
||||
* 本文件只做 I/O 编排(A-4 约束 1)。
|
||||
* 本文件只做 I/O 编排(分层约束)。
|
||||
* 线程约定:iec104Task 独占(NET_READY 屏障后循环,100ms 节拍 + 看门狗心跳)。
|
||||
* 创建日期:2026-10-12(B4)
|
||||
*/
|
||||
@@ -33,7 +33,7 @@
|
||||
/* 兜底节拍:门控判"无数据"时每 32 轮无条件收一次(ui_net_sink RM 先例,
|
||||
* 防 RECV_OK 事件未收到把数据永久藏住);须为 2 的幂 */
|
||||
#define IEC104_LIVENESS_DIV 32u
|
||||
/* 连续发送失败阈值:同 ui_net_sink E-04(短写=对端收坏帧) */
|
||||
/* 连续发送失败阈值:同 ui_net_sink 先例(短写=对端收坏帧) */
|
||||
#define IEC104_SEND_FAIL_MAX 3u
|
||||
|
||||
/* ---------------- 服务状态(iec104Task 独占) ---------------- */
|
||||
@@ -59,7 +59,7 @@ static const iec104_link_cfg_t s_cfg = { 1u, s_send_apdu, s_clock_sync, s_epoch_
|
||||
* 入口参数:ctx 未用;apdu/len = 完整 APCI 帧
|
||||
* 返回值:无
|
||||
* 限定条件:s_client >= 0(在 link 调用栈内执行,即本任务上下文)
|
||||
* 函数说明:短写/失败累计(E-04 先例);连续 3 次 → 置 s_conn_dead,
|
||||
* 函数说明:短写/失败累计(ui_net_sink 先例);连续 3 次 → 置 s_conn_dead,
|
||||
* 由 s_service_link 在 link 调用返回后统一拆链(严禁在回调里拆)
|
||||
*/
|
||||
static void s_send_apdu(void *ctx, const uint8_t *apdu, uint16_t len) {
|
||||
@@ -191,7 +191,7 @@ static void s_service_link(uint32_t now) {
|
||||
* 返回值:无
|
||||
* 限定条件:NET_READY 已置位;s_cfg/s_link 已 init
|
||||
* 函数说明:门控(net_listener_armed/net_can_read 纯 RAM 读)+ 兜底节拍照
|
||||
* ui_net_sink I-1/I-2;EOF 时 client==listen 严禁 close(net 层
|
||||
* ui_net_sink 先例;EOF 时 client==listen 严禁 close(net 层
|
||||
* 已 auto_relisten,close 会连监听一起毁)
|
||||
*/
|
||||
static void s_serv_poll(void) {
|
||||
|
||||
+9
-9
@@ -11,12 +11,12 @@
|
||||
* v1.2 2026-07-21 王建锋 网络初始化移入本任务(net_init + TCP 监听)
|
||||
* v1.3 2026-07-22 王建锋 单连接模式重构,增加大文件/边界/KeepAlive 测试
|
||||
* v1.4 2026-09-14 王建锋 新增自检埋点:PHY 链路 3s 未建立 → sys_log "comms fault"
|
||||
* v1.5 2026-09-15 王建锋 配置加载移交 defaultTask(配置引擎属主,P1-3);
|
||||
* v1.5 2026-09-15 王建锋 配置加载移交 defaultTask(配置引擎属主);
|
||||
* 本任务只读 RAM 副本
|
||||
* v1.6 2026-09-15 王建锋 P1-6:入口加 CH395F 硬件层测试模式自挂起守卫
|
||||
* v1.6 2026-09-15 王建锋 入口加 CH395F 硬件层测试模式自挂起守卫
|
||||
* (基础线程无条件创建,该模式下不运行);
|
||||
* freertos.c USER CODE 区重复创建已删除
|
||||
* v1.7 2026-10-02 王建锋 P1-8:初始组网失败/重建失败 → 自检记录 + 30s 周期重试
|
||||
* v1.7 2026-10-02 王建锋 初始组网失败/重建失败 → 自检记录 + 30s 周期重试
|
||||
* (原实现:初始失败 = netTask 无限驻留;重建失败 = 静默丢配置)
|
||||
* v1.8 2026-10-10 王建锋 任务耗时测量埋点:net_poll() 单独计时——
|
||||
* 本任务每 ~1ms 分派一次消息队列,若驱动轮询被拉长,
|
||||
@@ -52,7 +52,7 @@
|
||||
* 函数说明:只置事件位;真正的重建在主循环内完成(CH395F SPI 归 netTask 独占,
|
||||
* UI 不得直接操作)
|
||||
*/
|
||||
/* P1-8(2026-10-02):重建失败延迟重试的截止 tick;0 = 无待重试 */
|
||||
/* 重建失败延迟重试的截止 tick;0 = 无待重试 */
|
||||
static uint32_t s_reinit_retry_at;
|
||||
|
||||
static void net_reconfig_request_cb(void) {
|
||||
@@ -186,7 +186,7 @@ void net_task_func(void const *arg) {
|
||||
* 2026-10-02 台架修复:10s 超时等待改 100ms 分片 + 每片喂心跳——原实现
|
||||
* 在等待期无心跳:若存储 bring-up 失败,本等待跑满 10s 超时,而 IWDG
|
||||
* 此时已启动(defaultTask 在 bring-up 后即启动),NET 位长期缺失 →
|
||||
* 必需掩码不可达 → 复位循环,E-31 的 2s 自愈重试根本没有机会。
|
||||
* 必需掩码不可达 → 复位循环,存储自愈的 2s 重试根本没有机会。
|
||||
* 分片喂位保证"存储未就绪"降级态下 NET 位持续新鲜。 */
|
||||
uint32_t rounds_left = 100u; /* 100 × 100ms = 原 10s 超时语义 */
|
||||
for (;;) {
|
||||
@@ -202,7 +202,7 @@ void net_task_func(void const *arg) {
|
||||
}
|
||||
}
|
||||
|
||||
/* P1-8(2026-10-02):初始组网失败不自驻留——自检台账留痕 + 30s 周期重试
|
||||
/* 初始组网失败不自驻留——自检台账留痕 + 30s 周期重试
|
||||
* (1s 分片、每片喂心跳):线缆/芯片恢复即可当场完成组网;原 for(;;)osDelay
|
||||
* 会令看门狗 NET 位持续陈旧,并阻塞全部下游就绪屏障 */
|
||||
for (;;) {
|
||||
@@ -235,7 +235,7 @@ void net_task_func(void const *arg) {
|
||||
|
||||
/* UI 保存网口配置后请求立即应用:在此重建网络(新 IP/掩码/网关/MAC)。
|
||||
* 重建完成后代次号已递增,各 TCP 服务据此重建监听;期间连接会断开(预期)。
|
||||
* P2-11(2026-10-04):用 osEventFlagsWait(timeout=0) 原子"取走并清除"RECONFIG
|
||||
* 用 osEventFlagsWait(timeout=0) 原子"取走并清除"RECONFIG
|
||||
* 位,替换原 Get+Clear 两步;原写法在 Get 与 Clear 之间若有生产者在同一位上
|
||||
* Set,该次请求会被 Clear 一并吞掉(丢事件窗口)。 */
|
||||
if (sys_evtHandle != NULL) {
|
||||
@@ -247,7 +247,7 @@ void net_task_func(void const *arg) {
|
||||
(void)osEventFlagsSet(sys_evtHandle, SYS_EVT_NET_READY);
|
||||
s_reinit_retry_at = 0U;
|
||||
} else {
|
||||
/* P1-8(2026-10-02):重建失败 → 自检留痕 + 30s 延迟重试(重事件位),
|
||||
/* 重建失败 → 自检留痕 + 30s 延迟重试(重事件位),
|
||||
* 新配置不再静默丢失(原实现仅一条 DBG_ERROR 后丢弃) */
|
||||
DBG_ERROR("netTask: re-init FAIL, retry in 30s");
|
||||
(void)syslog_post(SYSLOG_SELFTEST, "net reinit fail");
|
||||
@@ -255,7 +255,7 @@ void net_task_func(void const *arg) {
|
||||
}
|
||||
} else if ((s_reinit_retry_at != 0U)
|
||||
&& ((int32_t)((uint32_t)HAL_GetTick() - s_reinit_retry_at) >= 0)) {
|
||||
/* P1-8:30s 倒计时到 → 重新触发重建(符号差处理 tick 回绕) */
|
||||
/* 30s 倒计时到 → 重新触发重建(符号差处理 tick 回绕) */
|
||||
s_reinit_retry_at = 0U;
|
||||
(void)osEventFlagsSet(sys_evtHandle, SYS_EVT_NET_RECONFIG);
|
||||
}
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
* v1.0 2026-07-19 王建锋 创建初始版本
|
||||
* v1.1 2026-08-24 王建锋 提取 net_stack_bring_up() 供硬件层/NET 层两种测试模式共用
|
||||
* v1.2 2026-09-15 王建锋 硬件层模式描述修正:netTask 恒创建(基础线程),
|
||||
* 该模式下自挂起不运行(P1-6 守卫)
|
||||
* 该模式下自挂起不运行(入口自挂起守卫)
|
||||
* v1.3 2026-10-21 王建锋 删除 net_task_init() 声明(空壳入口、无调用点)
|
||||
*/
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ static uint8_t s_prev_cal_lock; /* 上拍未标定闭锁态(边沿日志用
|
||||
* 返 回 值:1 - 闭锁(判据停走);0 - 放行 uint8_t
|
||||
* 限定条件:仅本任务上下文(adc_cal_flags 纯读,任意上下文可读)
|
||||
* 函数说明:判据 = UA/UN/IA 三路 VALID **全**置位——恰好是 V0.1 唯一有效的
|
||||
* 三个测量通道(N-18);UB/UC/IB/IC/IN 即使将来标定也另受 CLIPPED
|
||||
* 三个测量通道(双转换器无共同采样触发);UB/UC/IB/IC/IN 即使将来标定也另受 CLIPPED
|
||||
* 品质约束(M5 快照层处理),不掺和进本闭锁源。VALID 随 cfg
|
||||
* load/poll 与卡 12 标定自动翻转,本函数无自有状态(每拍现查,
|
||||
* 无陈旧标志位)。台架调试旁路 = 编译期 -DDTU_BENCH(§4.3 决策 1
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
* 创建日期:2026-07-19
|
||||
* 修改记录:
|
||||
* v1.0 2026-07-19 王建锋 创建初始版本
|
||||
* v1.1 2026-10-05 王建锋 P3-11:注明"预留桩(未启用)",避免误认为已实现
|
||||
* v1.1 2026-10-05 王建锋 注明"预留桩(未启用)",避免误认为已实现
|
||||
*/
|
||||
|
||||
#include "rs485_task.h"
|
||||
|
||||
+2
-2
@@ -25,7 +25,7 @@
|
||||
* (2026-10-25 实测 bit4 8 连击、uisn=154ms 仍在段内;
|
||||
* 按位连击判据 = docs/子系统设计/看门狗设计说明.md §5.3)。
|
||||
* 单个往返自身仍无上界——该上界与彻底解耦(事件驱动
|
||||
* accept)归登记簿 E-27/E-28(批 3),本票只做心跳续命。
|
||||
* accept)归登记簿「net 层跨上下文收口」(批 3),本票只做心跳续命。
|
||||
*/
|
||||
#include "ui_task.h"
|
||||
|
||||
@@ -295,7 +295,7 @@ void ui_task_func(void *argument) {
|
||||
* 一次心跳,救不了 → 彻底解耦须走事件驱动 accept,见文件头修改记录 v1.3。
|
||||
* v1.4 补充:sink 内每个往返**返回点**已各自补喂(set_beat_cb 注入),
|
||||
* 「单轮多个慢往返之和」不再构成缺席链;剩单往返 >800ms 才报——那是
|
||||
* netTask 自身停摆,按位判据抓对了人(上界归 E-27/E-28)。 */
|
||||
* netTask 自身停摆,按位判据抓对了人(上界归「net 层跨上下文收口」)。 */
|
||||
task_wd_beat(TASK_WD_UI);
|
||||
/* 推流轮询段单独 enter/exit:它内部经 net_accept → net_wait_result 走到
|
||||
* xTaskNotifyWait(portMAX_DELAY) 的**无限期阻塞**。若 uiTask 永久卡在这里,
|
||||
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-10-11
|
||||
* 修改记录:
|
||||
* v1.0 2026-10-11 王建锋 创建初始版本(N-19 FTP 鉴权;标准向量由
|
||||
* v1.0 2026-10-11 王建锋 创建初始版本(FTP 鉴权;标准向量由
|
||||
* test/host/sha256 主机单测钉死)
|
||||
*/
|
||||
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@
|
||||
* 作者:王建锋
|
||||
* 创建日期:2026-10-11
|
||||
* 修改记录:
|
||||
* v1.0 2026-10-11 王建锋 创建初始版本(N-19 FTP 鉴权:[ftp] pwd_hash 校验)
|
||||
* v1.0 2026-10-11 王建锋 创建初始版本(FTP 鉴权:[ftp] pwd_hash 校验)
|
||||
*/
|
||||
|
||||
#ifndef SHA256_H
|
||||
|
||||
+26
-26
@@ -8,17 +8,17 @@
|
||||
* 创建日期:2026-09-29
|
||||
* 修改记录:
|
||||
* v1.0 2026-09-29 王建锋 创建:心跳位图 + 抽样判定 + IWDG 延后启动
|
||||
* v1.1 2026-10-02 王建锋 round 7 纯取证埋点:近 8 次抽样位图环 + 最大喂狗
|
||||
* v1.1 2026-10-02 王建锋 第 7 轮纯取证埋点:近 8 次抽样位图环 + 最大喂狗
|
||||
* 间隔统计 + 停喂现场落 /log/wdev.log(掉电不丢);
|
||||
* 全部在 TASK_WATCHDOG_UNIT_TEST 守卫内,判定状态机
|
||||
* 与主机侧单测逐字不变(见 docs/子系统设计/看门狗设计说明.md 2026-10-02 变更记录)
|
||||
* v1.2 2026-10-02 王建锋 round 8 取证:复位不丢的"进段打点"(.noinit 槽 +
|
||||
* PREVRUN 开机上报 + STALL 行附 seg)——round 7 结论为
|
||||
* v1.2 2026-10-02 王建锋 第 8 轮取证:复位不丢的"进段打点"(.noinit 槽 +
|
||||
* PREVRUN 开机上报 + STALL 行附 seg)——第 7 轮结论为
|
||||
* "poller 自身停摆",需定位 defaultTask 主循环阻塞点
|
||||
* v1.3 2026-10-02 王建锋 round 8b 取证:第二个 .noinit 槽"时间源存活性"
|
||||
* v1.3 2026-10-02 王建锋 第 8b 轮取证:第二个 .noinit 槽"时间源存活性"
|
||||
* (TIM7 ISR 每 1ms 写 uwTick/FreeRTOS tick/计数)+
|
||||
* TICKRUN 开机上报(含相对进段时刻的增量 duw/dfr)——
|
||||
* round 8a 定位到 console 通道后,需钉死是 TIM7(HAL tick)
|
||||
* 第 8a 轮定位到 console 通道后,需钉死是 TIM7(HAL tick)
|
||||
* 还是 SysTick(FreeRTOS tick) 停摆(见 docs/子系统设计/看门狗设计说明.md 2026-10-02 变更记录)
|
||||
*
|
||||
* v1.5 2026-10-25 DTU 项目组 **停喂判定按位分账**(大窗判读 F2):stall/停喂的唯一
|
||||
@@ -35,7 +35,7 @@
|
||||
* HAL_IWDG_Init() 时,一并丢掉了**唯一**写这两只寄存器的
|
||||
* 路径 → 实机停在复位默认值 PR=0/RLR=4095(≈349~512ms),
|
||||
* 比设计短约 5 倍 → 800ms 容忍分支成死代码、任何 >400ms
|
||||
* 心跳缺口直接 IWDG 误复位(round 3~8 全部"空 STOR 必
|
||||
* 心跳缺口直接 IWDG 误复位(第 3~8 轮全部"空 STOR 必
|
||||
* 复位"的共同根因)。SWD 现场改回设计值后同一用例 100% 通过
|
||||
*
|
||||
* 设计依据:docs/子系统设计/看门狗设计说明.md
|
||||
@@ -64,7 +64,7 @@
|
||||
#include "FreeRTOS.h" /* xTaskGetCurrentTaskHandle / TaskHandle_t(FTL 心跳路由) */
|
||||
#include "task.h"
|
||||
#include "lftpd.h" /* lftpd_task_wd_beat() 弱钩子声明(本文件提供强符号,见下方) */
|
||||
#include "sys_log.h" /* SYSLOG_RUN(flush 诱导落盘)+ sys_log_evidence()(round 7 取证落盘) */
|
||||
#include "sys_log.h" /* SYSLOG_RUN(flush 诱导落盘)+ sys_log_evidence()(第 7 轮取证落盘) */
|
||||
#include "console_log.h" /* dbg_console_tx_fault()(僵尸通道计数) */
|
||||
/* 测试构建可见性(2026-10-25 台架实测缺陷):wd_required_mask() 里那条
|
||||
* ENABLE_HW_LAYER_TESTS 分支要按"硬件层独占测试构建"剔除 netTask,而该宏定义在
|
||||
@@ -134,7 +134,7 @@ static uint32_t s_started; /* 1 = IWDG 已启动 */
|
||||
static uint32_t s_last_tick; /* 上次抽样时刻(100ms 节流基准) */
|
||||
static uint32_t s_stall_evidenced; /* 1 = 本停摆 episode 已落盘证据(门闩;见 task_wd_poll) */
|
||||
|
||||
/* ==== round 7 纯取证状态(仅 MCU 构建;判定状态机逐字不变)====
|
||||
/* ==== 第 7 轮纯取证状态(仅 MCU 构建;判定状态机逐字不变)====
|
||||
* 设计背景见 docs/子系统设计/看门狗设计说明.md 2026-10-02 变更记录:误复位后 RAM 态随复位消失,
|
||||
* 故把"停喂现场"快照落盘(/log/wdev.log)供复位后回读定性。 */
|
||||
#ifndef TASK_WATCHDOG_UNIT_TEST
|
||||
@@ -145,7 +145,7 @@ static uint32_t s_last_feed_tick; /* 上次喂狗时刻(ms) *
|
||||
static uint32_t s_max_feed_gap; /* 观测到的最大喂狗间隔(ms) */
|
||||
static uint32_t s_feed_seen; /* 1 = 已记录首个喂狗时刻 */
|
||||
|
||||
/* ==== round 8 取证:复位不丢的"进段打点"槽(.noinit → SRAM 保留)====
|
||||
/* ==== 第 8 轮取证:复位不丢的"进段打点"槽(.noinit → SRAM 保留)====
|
||||
* IWDG/引脚复位不改变 SRAM 内容,故复位后仍能读出"上一轮 defaultTask 主循环
|
||||
* 最后走到哪一段"。magic 用于区分"本模块写过"与上电时的随机值。
|
||||
* 布局:[0]=magic [1]=段号 [2]=进段时刻(ms) [3]=循环序号
|
||||
@@ -155,7 +155,7 @@ static uint32_t s_feed_seen; /* 1 = 已记录首个喂狗时
|
||||
#define WD_SEG_MAGIC 0x57445347u /* 'WDSG' */
|
||||
__attribute__((section(".noinit"), zero_init)) volatile uint32_t s_wd_seg_trail[4];
|
||||
|
||||
/* ==== round 8b 取证:复位不丢的"时间源存活性"槽(.noinit → SRAM 保留)====
|
||||
/* ==== 第 8b 轮取证:复位不丢的"时间源存活性"槽(.noinit → SRAM 保留)====
|
||||
* 两个槽(均在 UNINIT_CCM,复位不清):
|
||||
* s_wd_tick_cur [0]=magic [1]=uwTick(最新) [2]=本轮 TIM7 中断计数 [3]=保留
|
||||
* —— TIM7 ISR 每 1ms 更新("本轮活动值")
|
||||
@@ -238,7 +238,7 @@ static uint32_t wd_required_mask(void) {
|
||||
|
||||
#ifndef TASK_WATCHDOG_UNIT_TEST
|
||||
/*
|
||||
* 函数功能:记录喂狗时刻并累计最大喂狗间隔(round 7 纯取证)
|
||||
* 函数功能:记录喂狗时刻并累计最大喂狗间隔(第 7 轮纯取证)
|
||||
* 入口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:由 wd_platform_feed() 内调用(任意喂狗路径均经过)
|
||||
@@ -260,7 +260,7 @@ static void wd_feed_track(void) {
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:把停喂现场落盘到 /log/wdev.log(round 7 纯取证)
|
||||
* 函数功能:把停喂现场落盘到 /log/wdev.log(第 7 轮纯取证)
|
||||
* 入口参数:snapshot - 本次抽样到的位图 uint32_t
|
||||
* 返回值:无
|
||||
* 限定条件:task_wd_poll 停喂分支(defaultTask 上下文)
|
||||
@@ -298,14 +298,14 @@ static void wd_stall_evidence(uint32_t snapshot) {
|
||||
}
|
||||
}
|
||||
|
||||
/* ==================== round 8 取证:复位不丢的"进段打点" ====================
|
||||
* 见 task_watchdog.h 的说明(round 7 初判"poller 自身停摆"经 round 8b 更正:
|
||||
/* ==================== 第 8 轮取证:复位不丢的"进段打点" ====================
|
||||
* 见 task_watchdog.h 的说明(第 7 轮初判"poller 自身停摆"经第 8b 轮更正:
|
||||
* 真因是 IWDG 超时被静默配成复位默认值,见 docs/子系统设计/看门狗设计说明.md
|
||||
* 2026-10-02 变更记录)→ 需定位 defaultTask 主循环冻结在哪个调用点。
|
||||
* 槽放 .noinit(scatter UNINIT_CCM):IWDG/引脚复位不改变 SRAM,复位后仍可读。 */
|
||||
|
||||
/*
|
||||
* 函数功能:记录"即将进入的主循环段"到复位不丢的 .noinit 槽(round 8 取证)
|
||||
* 函数功能:记录"即将进入的主循环段"到复位不丢的 .noinit 槽(第 8 轮取证)
|
||||
* 入口参数:seg - 段号(task_wd_seg_t) uint32_t
|
||||
* 返回值:无
|
||||
* 限定条件:defaultTask 主循环调用(任意上下文亦可,纯 SRAM 写)
|
||||
@@ -320,7 +320,7 @@ void task_wd_seg(uint32_t seg) {
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:开机快照上一轮的"时间源存活性"(round 8b 取证)
|
||||
* 函数功能:开机快照上一轮的"时间源存活性"(第 8b 轮取证)
|
||||
* 入口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:**必须在 HAL_Init() 之前调用**(main() 的 USER CODE BEGIN 1)
|
||||
@@ -346,7 +346,7 @@ void task_wd_tick_snapshot(void) {
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:记录"时间源存活性"到复位不丢的 .noinit 槽(round 8b 取证)
|
||||
* 函数功能:记录"时间源存活性"到复位不丢的 .noinit 槽(第 8b 轮取证)
|
||||
* 入口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:TIM7 中断上下文调用(HAL_TIM_PeriodElapsedCallback 内,每 1ms)
|
||||
@@ -363,13 +363,13 @@ void task_wd_tick_probe(void) {
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:开机上报上一轮最后进段 + 时间源存活性(round 8 取证)
|
||||
* 函数功能:开机上报上一轮最后进段 + 时间源存活性(第 8 轮取证)
|
||||
* 入口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:启动早期调用(内核启动前亦可,裸 UART 直发)
|
||||
* 函数说明:magic 命中才打一行 `PREVRUN seg=<段> seq=<序号> t=<ms>`(裸 UART,
|
||||
* 不依赖环形缓冲写者),随后清 magic(一次复位只报一次)。
|
||||
* round 8b:紧接着打 `TICKRUN uw= n= duw=`——uw 为 TIM7 最后一次记下的
|
||||
* 第 8b 轮:紧接着打 `TICKRUN uw= n= duw=`——uw 为 TIM7 最后一次记下的
|
||||
* HAL tick、n 为 TIM7 中断计数、duw = uw - t(时间源是否活到复位)。
|
||||
*/
|
||||
void task_wd_seg_report(void) {
|
||||
@@ -391,7 +391,7 @@ void task_wd_seg_report(void) {
|
||||
}
|
||||
s_wd_seg_trail[0] = 0u; /* 一次复位只报一次 */
|
||||
|
||||
/* round 8b:时间源存活性(取自开机快照 prev,增量相对上一行的 t = 最后进段时刻)。
|
||||
/* 第 8b 轮:时间源存活性(取自开机快照 prev,增量相对上一行的 t = 最后进段时刻)。
|
||||
* duw≈0 → TIM7 与 defaultTask 同刻停摆(HAL 时基停摆);duw≈2000+ → TIM7 活到
|
||||
* 复位(冻结另有阻塞点);n = 上一轮 TIM7 中断总数(≈该轮存活毫秒数)。 */
|
||||
if (s_wd_tick_prev[0] == WD_TICK_MAGIC) {
|
||||
@@ -487,7 +487,7 @@ void task_wd_poll(void) {
|
||||
s_last_mask = snapshot;
|
||||
|
||||
#ifndef TASK_WATCHDOG_UNIT_TEST
|
||||
/* round 7 取证:留存近 8 次抽样位图(环形),停喂时随证据落盘 */
|
||||
/* 第 7 轮取证:留存近 8 次抽样位图(环形),停喂时随证据落盘 */
|
||||
s_snap_hist[s_snap_hist_idx] = snapshot;
|
||||
s_snap_hist_idx = (s_snap_hist_idx + 1u) % WD_SNAP_HIST_DEPTH;
|
||||
#endif
|
||||
@@ -524,7 +524,7 @@ void task_wd_poll(void) {
|
||||
s_miss_total++;
|
||||
if (s_miss_streak == 0u) {
|
||||
/* 复合缺席 episode 起点裸 UART 标记(每 episode 一条):证明 poller 仍在
|
||||
* 抽样、并给出**首次**缺席位图——定位谁先停跑(round 7 出身)。⚠ v1.5 起
|
||||
* 抽样、并给出**首次**缺席位图——定位谁先停跑(第 7 轮出身)。⚠ v1.5 起
|
||||
* 本行为**纯诊断**——复合边沿 ≠ 停喂,stall 判定权在下面的按位连击。 */
|
||||
#ifndef TASK_WATCHDOG_UNIT_TEST
|
||||
char m[96];
|
||||
@@ -549,7 +549,7 @@ void task_wd_poll(void) {
|
||||
(unsigned)wd_required_mask(),
|
||||
(unsigned long)s_miss_total);
|
||||
#ifndef TASK_WATCHDOG_UNIT_TEST
|
||||
/* round 7 取证:把现场落 /log/wdev.log(掉电不丢)。
|
||||
/* 第 7 轮取证:把现场落 /log/wdev.log(掉电不丢)。
|
||||
* 串口 stall 行经僵尸通道可能丢失,故此处独立留盘上证据。
|
||||
* ⚠ 本 episode 只写**一次**(s_stall_evidenced 门闩;注意 worst 连击达
|
||||
* 上限后会**饱和**在容忍值,不能用它判"首次"):证据落盘经
|
||||
@@ -615,7 +615,7 @@ uint32_t task_wd_is_armed(void) {
|
||||
*/
|
||||
__weak void wd_platform_feed(void) {
|
||||
IWDG->KR = WD_IWDG_KEY_FEED; /* 重装计数器(喂狗) */
|
||||
wd_feed_track(); /* round 7 取证:记录喂狗时刻/最大间隔 */
|
||||
wd_feed_track(); /* 第 7 轮取证:记录喂狗时刻/最大间隔 */
|
||||
}
|
||||
|
||||
/* ==== 为什么 IWDG 参数必须在这里写(2026-10-02 台架实测,P0 根因)====
|
||||
@@ -627,10 +627,10 @@ __weak void wd_platform_feed(void) {
|
||||
* 实测读数:`IWDG->PR = 0`、`IWDG->RLR = 4095` → 实际超时 4×4096/LSI
|
||||
* ≈ 349~512ms,比设计的 1701~2498ms **短约 5 倍**。后果有二:
|
||||
* ① `WD_MISS_TOLERANCE`(8×100ms=800ms)**永远等不到**就被 IWDG 复位
|
||||
* → 停喂判定分支成为死代码(这正是 round 7 观察到"STALL 从未执行"
|
||||
* → 停喂判定分支成为死代码(这正是第 7 轮观察到"STALL 从未执行"
|
||||
* 的原因,当时误判为"poller 自身停摆");
|
||||
* ② 任何 >400ms 的心跳缺口都直接 IWDG 误复位。空 STOR(0 字节截断)
|
||||
* → dhara/FTL flush 使 NET/FTP/UI 心跳缺 ~400ms → 复位(round 3~8
|
||||
* → dhara/FTL flush 使 NET/FTP/UI 心跳缺 ~400ms → 复位(第 3~8 轮
|
||||
* 全部"空 STOR 必复位"的共同根因,P0-B 出口阻塞点)。
|
||||
* 实测验证:SWD 现场把 PR/RLR 改成 4/1249 后,同一个空 STOR 由 5/5 复位
|
||||
* 变为 100% 成功(`226 Closing data connection.`)。
|
||||
|
||||
+10
-10
@@ -85,10 +85,10 @@ uint32_t task_wd_miss_count(void); /* 累计未达标轮数 */
|
||||
uint32_t task_wd_is_armed(void); /* 1 = IWDG 已启动 */
|
||||
|
||||
/*
|
||||
* ==================== round 8 取证:复位不丢的"进段打点" ====================
|
||||
* 背景(2026-10-02 台架 round 7~8 取证,完整链条见
|
||||
* ==================== 第 8 轮取证:复位不丢的"进段打点" ====================
|
||||
* 背景(2026-10-02 台架第 7~8 轮取证,完整链条见
|
||||
* docs/子系统设计/看门狗设计说明.md 2026-10-02 变更记录):误复位时判定分支(STALL)从未
|
||||
* 执行,当时判为**喂狗点 task_wd_poll 自身停摆**(round 8b 已更正:真因是 IWDG 超时
|
||||
* 执行,当时判为**喂狗点 task_wd_poll 自身停摆**(第 8b 轮已更正:真因是 IWDG 超时
|
||||
* 被静默配成复位默认值),而非某个被监视任务饿死。RAM 态随复位消失、串口又可能来不及打,
|
||||
* 故需要一条**复位后仍可读**的"defaultTask 主循环最后走到哪一段"轨迹。
|
||||
*
|
||||
@@ -96,7 +96,7 @@ uint32_t task_wd_is_armed(void); /* 1 = IWDG 已启动 */
|
||||
* MDK-ARM/STM32F407-Demo/STM32F407-Demo.sct 的 UNINIT_CCM)——IWDG/引脚复位
|
||||
* 不改变 SRAM 内容,故复位后仍能读出上一轮的最后进段。槽布局:
|
||||
* [0]=magic(校验是否为本模块写过)[1]=段号 [2]=进段时刻(ms) [3]=循环序号
|
||||
* round 8b 追加"时间源存活性"槽(同样在 .noinit):
|
||||
* 第 8b 轮追加"时间源存活性"槽(同样在 .noinit):
|
||||
* cur = [magic, uwTick, 本轮 TIM7 计数, 0](TIM7 ISR 每 1ms 更新)
|
||||
* prev = [magic, uwTick, 上一轮 TIM7 计数, 0](main() 在 HAL_Init 之前快照)
|
||||
* 注意:**仅 MCU 构建存在**(TASK_WATCHDOG_UNIT_TEST 下不编入,主机单测不受影响)。
|
||||
@@ -117,7 +117,7 @@ typedef enum {
|
||||
} task_wd_seg_t;
|
||||
|
||||
/*
|
||||
* 函数功能:记录"即将进入的主循环段"(round 8 取证;**仅 MCU 构建**)
|
||||
* 函数功能:记录"即将进入的主循环段"(第 8 轮取证;**仅 MCU 构建**)
|
||||
* 入口参数:seg - 段号(task_wd_seg_t)
|
||||
* 返回值:无
|
||||
* 限定条件:defaultTask 主循环调用(任意上下文亦可,纯 SRAM 写)
|
||||
@@ -126,20 +126,20 @@ typedef enum {
|
||||
void task_wd_seg(uint32_t seg);
|
||||
|
||||
/*
|
||||
* 函数功能:开机上报上一轮最后进段 + 时间源存活性(round 8 取证;**仅 MCU 构建**)
|
||||
* 函数功能:开机上报上一轮最后进段 + 时间源存活性(第 8 轮取证;**仅 MCU 构建**)
|
||||
* 入口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:启动早期调用(内核启动前亦可,裸 UART 直发)
|
||||
* 函数说明:magic 命中才打一行 `PREVRUN seg=<段> seq=<序号> t=<ms>`(裸 UART,
|
||||
* 不依赖环形缓冲写者),随后清 magic(一次复位只报一次)。
|
||||
* round 8b:紧接着打一行 `TICKRUN uw= n= duw=`——uw 为 TIM7 在**上一轮**
|
||||
* 第 8b 轮:紧接着打一行 `TICKRUN uw= n= duw=`——uw 为 TIM7 在**上一轮**
|
||||
* 最后一次记下的 HAL tick、n 为上一轮 TIM7 中断总数、duw = uw - t
|
||||
* (相对进段时刻的增量),据此判定 TIM7 是否在冻结同刻停摆。
|
||||
*/
|
||||
void task_wd_seg_report(void);
|
||||
|
||||
/*
|
||||
* 函数功能:开机快照上一轮的"时间源存活性"(round 8b 取证;**仅 MCU 构建**)
|
||||
* 函数功能:开机快照上一轮的"时间源存活性"(第 8b 轮取证;**仅 MCU 构建**)
|
||||
* 入口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:**必须在 HAL_Init() 之前调用**(main() 的 USER CODE BEGIN 1)
|
||||
@@ -150,13 +150,13 @@ void task_wd_seg_report(void);
|
||||
void task_wd_tick_snapshot(void);
|
||||
|
||||
/*
|
||||
* 函数功能:记录"时间源存活性"到复位不丢的 .noinit 槽(round 8b 取证;**仅 MCU 构建**)
|
||||
* 函数功能:记录"时间源存活性"到复位不丢的 .noinit 槽(第 8b 轮取证;**仅 MCU 构建**)
|
||||
* 入口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:TIM7 中断上下文调用(`HAL_TIM_PeriodElapsedCallback` 内,每 1ms)
|
||||
* 函数说明:更新 cur 槽 = [magic, uwTick, 本轮 TIM7 中断计数, 0](纯 SRAM 写,无阻塞)。
|
||||
* 与 task_wd_seg 的"进段打点"配合,复位后由 task_wd_seg_report() 上报。
|
||||
* 背景(2026-10-02 台架 round 8b 取证,见 docs/子系统设计/看门狗设计说明.md
|
||||
* 背景(2026-10-02 台架第 8b 轮取证,见 docs/子系统设计/看门狗设计说明.md
|
||||
* 2026-10-02 变更记录):HAL_UART_Transmit 的超时判据
|
||||
* 依赖 uwTick,时间源停摆即死等——必须先把"TIM7 是否停摆"钉死再定向修复。
|
||||
* ⚠ **本函数内严禁调用任何 FreeRTOS/CMSIS-RTOS API**:TIM7 在 HAL_Init
|
||||
|
||||
Reference in New Issue
Block a user