merge: task/cal-scale-doc 合入 v2-on-ui
任务 cal-scale(1 个提交)经 task-worktree 回收: - feat(采集标定): 卡1 TPAFE 量程口径定死 + 卡3 cfg_file 补 CFG_VAL_INT
This commit is contained in:
+73
-1
@@ -25,6 +25,10 @@
|
||||
* s_gen_document 两处 `off += snprintf(...)` 改检查式(截断即
|
||||
* 返回 -1,不再跳过缓冲末尾);注册拒超长键名(≤ 15 字符),
|
||||
* 使每行 112 字节余量成为可编译期验证的前提
|
||||
* v1.5 2026-10-25 DTU 项目组 数据可信卡 3:新增值类型 CFG_VAL_INT(有符号十进制,
|
||||
* len 1/2/4 小端补码)——s_decode_int() 用 strtol + ERANGE +
|
||||
* 宽度上下界校验,s_encode() 带负号输出;负零点偏移从此可
|
||||
* 干净落盘(动因见 docs/任务规划/数据可信.md §4.4 与 §7 决策 3)
|
||||
* 函数说明:引擎状态非重入——调用方须收敛同一任务(defaultTask:poll/save,见 P1-3;
|
||||
* 启动期 load 先于任务竞争);FS 卷并发由 FatFS REENTRANT 卷锁保证
|
||||
* (见《配置文件设计说明》D15 A+ 方案)
|
||||
@@ -33,12 +37,14 @@
|
||||
/* 头文件包含区 */
|
||||
#include <string.h>
|
||||
|
||||
#include <stdlib.h> /* strtoul(键值解码);堆分配一律走 cfg_store_alloc,禁 malloc */
|
||||
#include <stdlib.h> /* strtoul / strtol(键值解码);堆分配一律走 cfg_store_alloc,禁 malloc */
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#include <errno.h> /* ERANGE:strtol 饱和判别(INT 键解码需要,数据可信卡 3) */
|
||||
|
||||
#include "cfg_file.h"
|
||||
|
||||
#include "cfg_file_store.h"
|
||||
@@ -263,6 +269,28 @@ static int s_encode(const cfg_key_def_t *p_key, const uint8_t *p_src, char *p_ds
|
||||
n = snprintf(p_dst, cap, "%lu", v);
|
||||
break;
|
||||
}
|
||||
case CFG_VAL_INT: {
|
||||
long v = 0L;
|
||||
unsigned long raw = 0uL;
|
||||
size_t i;
|
||||
|
||||
if (p_key->len != 1u && p_key->len != 2u && p_key->len != 4u) {
|
||||
return -1;
|
||||
}
|
||||
for (i = 0u; i < p_key->len; i++) {
|
||||
raw |= ((unsigned long)p_src[i]) << (8u * i);
|
||||
}
|
||||
/* 小端补码还原为有符号值(两端 long = 32 位:ARMCC 32 位 ARM / MSVC LLP64) */
|
||||
if (p_key->len == 1u) {
|
||||
v = (long)(int8_t)(raw & 0xFFuL);
|
||||
} else if (p_key->len == 2u) {
|
||||
v = (long)(int16_t)(raw & 0xFFFFuL);
|
||||
} else {
|
||||
v = (long)(int32_t)raw;
|
||||
}
|
||||
n = snprintf(p_dst, cap, "%ld", v);
|
||||
break;
|
||||
}
|
||||
case CFG_VAL_IP4:
|
||||
n = snprintf(p_dst, cap, "%u.%u.%u.%u",
|
||||
(unsigned)p_src[0], (unsigned)p_src[1],
|
||||
@@ -329,6 +357,44 @@ static int s_decode_uint(uint8_t *p_dst, size_t len, const char *p_val) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:解析有符号十进制值(小端补码写入,数据可信卡 3)
|
||||
* 入口参数:p_dst - 目标字段 uint8_t *
|
||||
* len - 字段字节数(1/2/4) size_t
|
||||
* p_val - 值文本(可带 +/- 号) const char *
|
||||
* 返回值:0 - 成功;-1 - 非法(空/非数字/带后缀/超本宽度范围/strtol 饱和) int
|
||||
* 限定条件:无
|
||||
* 函数说明:两端 long = 32 位,strtol 对超范围文本饱和到 LONG_MAX/MIN 并置
|
||||
* ERANGE(如 "2147483648" 饱和成 INT32_MAX 本身,光比 v>max_v 抓不住),
|
||||
* 故 ERANGE 与宽度上下界两道都要查;1/2 字节键按 int8/int16 范围收窄
|
||||
*/
|
||||
static int s_decode_int(uint8_t *p_dst, size_t len, const char *p_val) {
|
||||
const long min_v = (len == 1u) ? -128L : ((len == 2u) ? -32768L : (-2147483647L - 1L));
|
||||
const long max_v = (len == 1u) ? 127L : ((len == 2u) ? 32767L : 2147483647L);
|
||||
char *p_end = NULL;
|
||||
long v;
|
||||
size_t i;
|
||||
|
||||
if (len != 1u && len != 2u && len != 4u) {
|
||||
return -1;
|
||||
}
|
||||
if (p_val[0] == '\0') {
|
||||
return -1;
|
||||
}
|
||||
errno = 0;
|
||||
v = strtol(p_val, &p_end, 10);
|
||||
if (p_end == p_val || *p_end != '\0' || errno == ERANGE) {
|
||||
return -1;
|
||||
}
|
||||
if (v < min_v || v > max_v) {
|
||||
return -1;
|
||||
}
|
||||
for (i = 0u; i < len; i++) {
|
||||
p_dst[i] = (uint8_t)(((unsigned long)v) >> (8u * i));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:解析点分 IPv4("a.b.c.d",每段 0~255)
|
||||
* 入口参数:p_dst - 4 字节目标 uint8_t *
|
||||
@@ -440,6 +506,8 @@ static int s_decode(const cfg_key_def_t *p_key, uint8_t *p_dst, const char *p_va
|
||||
switch (p_key->type) {
|
||||
case CFG_VAL_UINT:
|
||||
return s_decode_uint(p_dst, p_key->len, p_val);
|
||||
case CFG_VAL_INT:
|
||||
return s_decode_int(p_dst, p_key->len, p_val);
|
||||
case CFG_VAL_IP4:
|
||||
return s_decode_ip4(p_dst, p_val);
|
||||
case CFG_VAL_MAC:
|
||||
@@ -1025,6 +1093,10 @@ int cfg_file_register(const cfg_section_reg_t *p_reg) {
|
||||
(p_key->len != 1u && p_key->len != 2u && p_key->len != 4u)) {
|
||||
return -1;
|
||||
}
|
||||
if (p_key->type == CFG_VAL_INT &&
|
||||
(p_key->len != 1u && p_key->len != 2u && p_key->len != 4u)) {
|
||||
return -1;
|
||||
}
|
||||
if (p_key->type == CFG_VAL_IP4 && p_key->len != 4u) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
+7
-1
@@ -15,6 +15,9 @@
|
||||
* 超长;超长处置补自检留痕
|
||||
* v1.3 2026-10-15 王建锋 E-16(历史审计 P2,属主文档已删除):新增公开常量
|
||||
* CFG_FILE_MAX_NAME_LEN(段名/键名上限 15 字符)
|
||||
* v1.4 2026-10-25 DTU 项目组 数据可信卡 3:新增 CFG_VAL_INT 有符号十进制值类型
|
||||
* (len = 1/2/4,小端补码)——标定零点偏移等负值需有符号
|
||||
* 落盘,动因与取舍见 docs/任务规划/数据可信.md §4.4
|
||||
*/
|
||||
|
||||
#ifndef CFG_FILE_H
|
||||
@@ -51,12 +54,15 @@ extern "C" {
|
||||
* IP4 :点分 IPv4 "a.b.c.d";字段 uint8_t[4]
|
||||
* MAC :十六进制 MAC "xx:xx:xx:xx:xx:xx"(分隔符兼容 - 与 .);字段 uint8_t[6]
|
||||
* STR :ASCII 字符串;字段 char[len](len = 缓冲长度,自动补 NUL)
|
||||
* INT :有符号十进制(可带 + / - 号);len = 目标字段字节数(1/2/4,小端补码,
|
||||
* 范围按宽度收窄:int8/int16/int32;负值合法,如标定零点偏移)
|
||||
*/
|
||||
typedef enum {
|
||||
CFG_VAL_UINT = 0,
|
||||
CFG_VAL_IP4,
|
||||
CFG_VAL_MAC,
|
||||
CFG_VAL_STR
|
||||
CFG_VAL_STR,
|
||||
CFG_VAL_INT
|
||||
} cfg_val_type_t;
|
||||
|
||||
/* 键定义(offset/len 均相对段 RAM 副本起始) */
|
||||
|
||||
@@ -117,6 +117,9 @@ test/host/cfg_file/cfg_file_test.c
|
||||
| **TC-CONFIG-003** | IP4:`0.0.0.0` / `255.255.255.255` / 常规;段数不足/超、>255、非数字、尾点、空 | 点分四段 |
|
||||
| **TC-CONFIG-004** | MAC:`:` / `-` / `.` 三种分隔符、大小写;过短/过长/非十六进制/坏分隔符 | 分隔符 = **字节边界**替代 |
|
||||
| **TC-CONFIG-005** | STR:正常 + NUL;恰好 `len-1` 放下;`len` 字符拒绝;空串 | 长度边界 |
|
||||
| **TC-CONFIG-006** | INT 合法:0 / `-1` / INT32_MIN / INT32_MAX / int8·int16 正负边界 / 前导 `+` / `-0` / 前导零 | 有符号十进制解码(数据可信卡 3,引擎 v1.5) |
|
||||
| **TC-CONFIG-007** | INT 非法:空 / 非数字 / 尾随垃圾 / 裸符号 / 小数点 / `0x` 文本 / 各宽度越界(含 strtol 饱和带 `2147483648`)/ 坏 len | ERANGE + 宽度上下界双校验(两端 long=32 位) |
|
||||
| **TC-CONFIG-008** | INT 编码带负号(`%ld`);`[sys]` 段 load→save→load 全链往返一致、出厂模板带负号默认;越界键拒收留默认并留痕;注册拒绝 INT len=3 | 有符号落盘闭环(负零点偏移可表达) |
|
||||
|
||||
### 2.2 INI 解析
|
||||
|
||||
@@ -280,3 +283,4 @@ if (s_encode(p_key, ..., v, sizeof(v)) >= 0) {
|
||||
| 日期 | 变更 |
|
||||
|---|---|
|
||||
| 2026-10-02 | 创建;20 用例 / 139 断言点全绿;抓出并修复 `s_encode` 返回值误判 P0 缺陷(外科手术保存死码);挂 pre-commit 钩子 `cfg-file-unit-test` |
|
||||
| 2026-10-25 | 随配置引擎新增 `CFG_VAL_INT`(数据可信卡 3)补登记 TC-CONFIG-006~008;套件 200/200 PASS |
|
||||
|
||||
+36
-3
@@ -1,6 +1,6 @@
|
||||
# V0.1 ADC 通道映射表(权威口径)
|
||||
|
||||
> **一句话结论**:V0.1 共 **17 路**模拟采样——**8 路进 TPAFE5160**(16 位、8 通道同步、±10 V 双极性)、**9 路进 STM32 内部 ADC**(12 位、顺序采样、0~3.3 V 单极性)。网名 `AINn`、板级网名 `ADCn`、转换器通道号**三套编号互不等价**(V0.1 有 13/17 路被置换),任何代码 / UI 显示 / 自检注入**必须以本表为准,不得假设 `AINn = ADCn`**。
|
||||
> **一句话结论**:V0.1 共 **17 路**模拟采样——**8 路进 TPAFE5160**(16 位、8 通道同步、**±5 V 满量程 = 10 V 跨度**、LSB ≈ 152.59 µV,板上 RANGE 接 GND,口径见 §7.1)、**9 路进 STM32 内部 ADC**(12 位、顺序采样、0~3.3 V 单极性)。网名 `AINn`、板级网名 `ADCn`、转换器通道号**三套编号互不等价**(V0.1 有 13/17 路被置换),任何代码 / UI 显示 / 自检注入**必须以本表为准,不得假设 `AINn = ADCn`**。
|
||||
>
|
||||
> 语义分配由硬件于 **2026-10-09** 确认;网名与电阻对应关系取自 [`V0.1.tel`](V0.1.tel)(第 227~262 行),引脚号取自数据手册 LQFP144 引脚表([`../芯片手册/stm32f405rg.md`](../芯片手册/stm32f405rg.md))。
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
**缺陷实质(为什么必须改)**:
|
||||
|
||||
1. **跨转换器通道没有同一时刻性**:TPAFE5160 是 8 通道**同步**采样,STM32 ADC 是**顺序**扫描(9 通道一轮 ≈ 67 µs),且两片转换器**没有共同采样触发**(CONVST 只打 TPAFE,ADC1 为软件触发)。于是 UA↔UB、IA↔IB 这类同族量之间**相位关系不确定**,主板侧任何功率 / 功率因数 / 相角 / 对称分量类运算都不成立。
|
||||
2. **精度与量程不一致**:TPAFE 16 位 ±5 V **双极性**(LSB ≈ 152.6 µV)vs STM32 12 位 0~3.3 V **单极性**(前端无中点偏置,交流量负半周削顶;LSB ≈ 806 µV)。同为相电压,UA 与 UB/UC 满量程码值差 16 倍、可用象限也不同——同一套算法无法统一处理 8 路电气量。
|
||||
2. **精度与量程不一致**:TPAFE 16 位 ±5 V **双极性**(LSB ≈ 152.6 µV,满量程 = 10 V 跨度,口径见 §7.1)vs STM32 12 位 0~3.3 V **单极性**(前端无中点偏置,交流量负半周削顶;LSB ≈ 806 µV)。同为相电压,UA 与 UB/UC 满量程码值差 16 倍、可用象限也不同——同一套算法无法统一处理 8 路电气量。
|
||||
3. **语义与硬件归属交叉**:UA 在 TPAFE、UB 在 STM32,固件映射表 / UI 通道序 / 自检注入都必须逐通道查表绕开这个交叉(本文件 §4、§5 两张表的存在本身就是代价)。
|
||||
|
||||
**下一版硬件(V0.2)改版要求**:**8 路电气量 UA/UB/UC/UN/IA/IB/IC/IN 全部接到 TPAFE5160 的 8 个通道**,备用路与温度(V_ZL2)留给 STM32 ADC。TPAFE 现被 5 条备用路占着(`AIN5`/`AIN13`/`AIN7`/`AIN15`/`AIN12` → ADC1/ADC2/ADC4/ADC5/ADC8),与要迁过来的 5 路(UB/UC/IB/IC/IN)**正好 5 换 5**:只需把这 5 路串联电阻在转换器一侧的目的地对调,**元件数量与料号不变,属走线级改动**。改版后本表 §3 的"转换器 / 代码通道"两列须按 V0.2 网表重出——这也是消费侧**必须按语义取数、不得写死下标**的原因。
|
||||
@@ -135,9 +135,41 @@ STM32(U17)为 LQFP144 脚号。
|
||||
| `adc_task` | **已实现(B1,2026-10-10;L1 层 v2.3;P1-5 事件驱动消费,2026-10-10)**:`App/task/adc_task.c` 每拍调 `adc_input_poll()`(`App/algo/adc_input.c`)= TPAFE `tpafe5160_snapshot()` 原子快照(8 通道)+ 消费一帧 STM32 ADC1 DMA 乒乓缓冲,**经 `adc_input_raw()` 按语义取数**(不再直接索引驱动缓冲);**消费唤醒由 TIM3 ISR 置位 `ADC_WAKE_FLAG`**(不再 1 ms 轮询);任务在 `Src/freertos.c` 的 `USER CODE` 区创建为 `adcTask`(512 words / **AboveNormal**) | 数据路径已通(台架 `@test`:`frames` 严格 1000 Hz 递增、**`idle=0`**、`adc1_rc=0` / `adc1_err=0`) |
|
||||
| TPAFE 采样触发 | **周期 CONVST 已实现(2026-10-10)**:`Src/main.c` 的 `HAL_TIM_PeriodElapsedCallback` 在 **TIM3 更新事件(1 kHz)** 内调 `tpafe5160_start_conv_irq()`;`app_main_init()` 用 `HAL_TIM_Base_Start_IT(&htim3)` 启动节拍(在 `tpafe5160_init()` 之后)。同一 TIM3 更新事件还经 **TRGO** 硬件触发 ADC1 一轮 9 通道(DMA 搬运),并调 `adc_tick_irq_notify()` **唤醒消费者**(P1-5 方案 a,2026-10-10;内含"消费者就绪"门)→ 两片转换器由同一硬件事件同源对齐 | 连续波形(1 kHz);详见 `docs/子系统设计/ADC采样节拍设计说明.md` §4(DMA 乒乓搬运)与 §6(事件驱动唤醒) |
|
||||
| 前端偏置 | 每路 1k 串联 + 1k 到 `*_GND` + 滤波 C,**无中点偏置** | STM32 侧只能测 **0~3.3 V 单极性**,交流量负半周被削顶;主板侧只做监视可接受,若要算 RMS/功率需硬件补偏置或明确算法口径 |
|
||||
| TPAFE 量程 | 默认 ±5 V(RANGE 接 GND),16 位二进制补码,LSB ≈ 152.59 µV | 用 `tpafe5160_to_voltage()` 换算 |
|
||||
| TPAFE 量程(**2026-10-25 卡 1 定死**) | 板上 RANGE 接 GND → **±5 V 双极性量程**(芯片手册输入量程表:RANGE = 0 → −5 ~ +5 V),16 位二进制补码,**±5 V 满量程 = 10 V 跨度**,LSB = 10 V / 65536 ≈ **152.59 µV/码(@ 转换器引脚)**。“±10 V” 是芯片的另一可选档(RANGE = HIGH,跨度 20 V、LSB ≈ 305.18 µV),**本板未用**——历史版本曾把本板误标为“±10 V 双极性”,与 §7 本行矛盾、且会让 TPAFE 侧系数差一倍,更正记录见 §9 | 用 `tpafe5160_to_voltage()` 换算;端到端口径见 §7.1 |
|
||||
| 取数契约(**2026-10-10 已修**,E-44 / E-45 / E-58 闭环) | 旧的 `get_buf` / `data_ready` / `clear_ready` **已删除**(删除前确认全仓库零调用者):`get_buf` 只给指针、任务再拷 → 被 BUSY EXTI 抢占就是**两帧混合**;`s_ready` 电平标志被 `start_conv_irq` 触发前清 0 → **重触发即丢未消费帧**;`read_all` 从不看 FRSTDATA(宏有定义、代码零引用)→ 芯片死亡时返回 OK + 常量垃圾。现口径:**`tpafe5160_snapshot(dst, count)`**(临界区原子拷贝,返回 1/0/-1,**仅任务上下文**)+ **`tpafe5160_frame_seq()`**(帧序号只增不清)+ **`tpafe5160_frstdata_err()`**(无跳变帧计数);`tpafe5160_init()` 增**首帧自检**——一帧读下来 FRSTDATA 无跳变(芯片不驱动并行总线;板上 R24 4.7k 上拉会把它钉死为高)→ 返回 `-3` + `[ERR]` | 消费者**只能**用 `snapshot()` 取数(`AGENTS.md` 已立为铁律);真机实测开机 `[ADC] Init OK` 现只在自检帧通过后才打印 → 判据在本板成立、非空转 |
|
||||
|
||||
### 7.1 端到端量纲链路口径(网名 AINn → 端子 → 变比 → 码值 → 满量程,2026-10-25 卡 1 定死)
|
||||
|
||||
> 出处:`docs/任务规划/数据可信.md` 卡 1。本节把 **语义端子(二次侧)→ 子板/端子板 → 网名 `AINn` → 主板电阻网络 → 板级网名 `ADCn` → 转换器引脚 → 码值 → 满量程** 的链路**逐段**定口径;
|
||||
> 待实测段只给**口径与判据**,不填任何实测数值(本轮按用户指示跳过台架,标注“待台架实测”)。
|
||||
|
||||
**芯片级口径(定死,与前端网络无关)**:
|
||||
|
||||
| 项 | 口径 | 依据 |
|
||||
|---|---|---|
|
||||
| TPAFE5160 量程档 | 板上 RANGE(U3 pin8)接 GND → **±5 V 双极性**(−5 ~ +5 V);RANGE = HIGH 的 ±10 V 档本板未用 | [`V0.0_网表详细分析报告.md`](V0.0_网表详细分析报告.md) §4.5“模式配置全对:RANGE(pin8)=GND → ±5V” + [`../芯片手册/TPAFE5160.md`](../芯片手册/TPAFE5160.md) 输入量程表(RANGE = 0 → −5 ~ +5;RANGE = 1 → −10 ~ +10) |
|
||||
| TPAFE 码值格式 | 16 位二进制补码(−32768 ~ +32767,全量程 65536 码 = 10 V 跨度) | 芯片手册 DC Accuracy(16 bit / NO missing code);`Drivers/BSP/TPAFE5160/tpafe5160.c` 的 `tpafe5160_to_voltage()` 注释 |
|
||||
| TPAFE LSB(@ 引脚) | 10 V / 65536 ≈ **152.59 µV/码**(≈6553 码/V) | 由上两行推得;与 §7“TPAFE 量程”行、驱动注释一致 |
|
||||
| STM32 ADC1 LSB(@ 引脚) | 12 位单极性 0 ~ 3.3 V:3.3 V / 4096 ≈ **805.66 µV/码**(≈1241 码/V) | §7 生成配置行;`docs/任务规划/数据可信.md` §2.1 同口径 |
|
||||
|
||||
“±10 V”只允许出现在“芯片可选档”语境。本文件 2026-10-09 建档时曾把文首摘要写成“TPAFE … ±10 V 双极性”,与 §7 的 ±5 V 档矛盾、**TPAFE 侧系数因此会差一倍**——2026-10-23 `docs/任务规划/数据可信.md` §2.1 挂账该冲突并要求卡 1 定死;现**定死为“±5 V 满量程 = 10 V 跨度、LSB 152.59 µV”**,文首摘要与 §7 已按此更正。全仓其余“±10 V”字样仅剩芯片手册转写与 `tpafe5160.h` 中 RANGE 枚举可选档注释(均为芯片能力描述,非本板配置口径),不构成歧义。
|
||||
|
||||
**链路分段口径**:
|
||||
|
||||
| 段 | 关系 | 状态 |
|
||||
|---|---|---|
|
||||
| ① 语义 ↔ 网名 ↔ 板级网名 ↔ 转换器代码通道 | §2/§3/§4/§5 的 17 路映射表 | **定死**(本文件为唯一权威) |
|
||||
| ② 转换器引脚电压 ↔ 码值 | TPAFE `U_pin = code × 152.59 µV`(code 带符号);STM32 `U_pin = code × 805.66 µV` | **定死**(纯转换器事实,上表) |
|
||||
| ③ `AINn`(串联电阻子板侧)↔ `ADCn`/转换器引脚 | 每路 1k 串联 + 1k 到 `*_GND` + 滤波 C(§7“前端偏置”行)。两种读法并存:**A** 按 2:1 电阻分压折算到 `AINn` 侧 → 系数 305.18(TPAFE)/ 1611.3(STM32)µV/码;**B** [`V0.0_网表详细分析报告.md`](V0.0_网表详细分析报告.md) §4.7 定性“源阻抗匹配(手册推荐做法,非分压)”→ 两侧系数相同 | **待台架实测**——`docs/任务规划/数据可信.md` 卡 0 注入斜率判别:引脚侧注入验 ②(期望 ≈6553 码/V),`AINn`/端子侧注入的斜率即 ③(或 ③+④)的合成变比。本卡不裁决 A/B |
|
||||
| ④ 语义端子(二次端子)↔ `AINn` | 子板/端子板变比:仓库内**无任何文档**(`docs/硬件平台/` 只有 `.tel` 网表与分析报告,无子板原理图;§8 步骤 4) | **缺口**——卡 0b 实测确认;拿不到变比则“端子二次值”不成立、`eng` 保持 0 |
|
||||
|
||||
**可评审结论**:
|
||||
|
||||
1. **ROM 默认系数按 ② 引脚口径冻结**:TPAFE 通道 `k = 152.59 µV/码`、STM32 通道 `k = 805.66 µV/码`;标定折算的输出单位 = **ADC 引脚电压 V**(`eng = 0`)。该口径**不依赖** ③④ 两个未决项,自本卡定死即成立——这就是 M4 承诺边界止于“引脚电压”的依据(`docs/任务规划/数据可信.md` §2.6)。
|
||||
2. `docs/任务规划/数据可信.md` §2.1 表中“板级系数(输入侧)305.18 / 1611.3 µV/码”是**按 ③ 读法 A(2:1 分压)折算到 `AINn` 侧**的条件值,而同一网络已被 V0.0 §4.7 定性为非分压(读法 B)。卡 0 判别 A/B 之前,305.18 / 1611.3 **不得**进任何代码或换算;引脚侧一律用 152.59 / 805.66。
|
||||
3. **满量程必须写明参考点**:TPAFE 通道满量程 = ±5 V **@ 引脚**(跨度 10 V);③④ 定死前,端子侧满量程只允许写“引脚侧数值 × ③ 比 × ④ 比”,两个比值当前均未知,**严禁**把“端子电压 = 码 × 305.18 µV”写进代码/文档当作已成立结论。
|
||||
4. **待台架实测占位(本轮按用户指示跳过)**:卡 0(三点注入:线性 R² ≥ 0.999、斜率、零点、逐通道映射确认)、卡 0b(端子→引脚变比)、卡 12(两点标定)完成后,回写本节 ③④ 两行与 §8。实测斜率与 ② 理论值偏差 >2 % 时**先查前端**(分压/变比/焊接),不得用系数凑错斜率(`docs/任务规划/数据可信.md` §8 风险表)。
|
||||
|
||||
---
|
||||
|
||||
## 8. 逐通道验证方法(上板自检口径)
|
||||
@@ -158,3 +190,4 @@ STM32(U17)为 LQFP144 脚号。
|
||||
| 2026-10-10 | **口径更正**:8 路电气量分属 TPAFE / STM32 两片转换器**定性为 V0.1 硬件缺陷**,撤销 2026-10-09 建档时"有意分配、不要修正"的说法;补记缺陷实质(跨转换器无同一时刻性、16 位双极性 vs 12 位单极性、语义与归属交叉)与 **V0.2 改版要求**(8 路电气量全部接 TPAFE,与 5 路备用路对调串联电阻目的地,BOM 不变);§7 新增"通道归属"行,明确 V0.1 上禁止跨转换器做相位/功率类运算。同步更正 `V0.1_网表详细分析报告.md` §1.4(新增 V10)/ §6-3,登记簿新增 **N-18** |
|
||||
| 2026-10-10 | **STM32 ADC1 消费者落地(L1-3/L1-4)**:新增 `App/algo/adc_input.c/h`——语义映射表 `k_adc_map[]` 按本文件 §3/§5 逐条落地(`UA/UN/IA`→TPAFE `buf[6]/[5]/[2]`,`UB/UC/IB/IC/IN/TEMP`→`CH14/3/6/1/2/5`),并提供 `adc_input_raw()` **按语义取数**;`stm32_scan()` 实现 9 通道软件触发顺序扫描;`adc_input_init()` 增**映射自检**(STM32 映射通道必在扫描序列内)。§7 的"STM32 ADC 外设 / `adc_task` / TPAFE 采样触发"三行同步更新(后两者原为陈旧描述:TIM6 周期触发、L1 层取数)。§8 步骤 2 标注**引脚级注入验证尚未执行**。台架 `@test` PASS(`adc1_rc=0` / `adc1_err=0`、`ADC input layer init: OK`;首帧 `UA=11429 UN=11454 IA=11452`、`UB=1018 UC=1044 IB=943 IC=1069 IN=1052 T=850`——互异非饱和,且两次开机读数不同 → 实时转换而非常量),证据 `测试说明/boot_log_data/20261010_prod_adc_l1_boot_log.txt` |
|
||||
| 2026-10-23 | **验证现状核查与排期**:确认 §8 步骤 1/2 与 `V0.0_网表详细分析报告.md` §5.1 第 6 条的"首次功能级 ADC 验证"**均无执行记录**——现有台架判据只证"通路活"(9 通道转换成功、零超时、互异非饱和、跨次开机不同),不证读数正确,连极性都未验。新增 §8 步骤 4:**"端子电压 → ADC 引脚电压"的变比在仓库内无任何文档**(无子板原理图),且 `1k 串联 + 1k 到地` 已被 §4.7 定性为"源阻抗匹配、非分压",故撤销"按 2:1 换算二次值"的用法。验证方法同步降为**不需要示波器 / 信号源**:`BUSY` 脉冲改用 `tpafe5160_frame_seq()` 帧计数,幅值改用万用表 + 三点线性拟合。排期见 `docs/任务规划/数据可信.md` 卡 0 / 0b |
|
||||
| 2026-10-25 | **量程口径定死(本波卡 1)**:文首摘要行“TPAFE5160(16 位、8 通道同步、±10 V 双极性)”更正为“±5 V 满量程 = 10 V 跨度、LSB ≈ 152.59 µV(板上 RANGE 接 GND)”;§1“缺陷实质 2”补“满量程 = 10 V 跨度”;§7“TPAFE 量程”行重写并括注“±10 V 是芯片可选档(RANGE=HIGH)、本板未用”;新增 §7.1 固化“端子 → 子板变比 → 网名 AINn → 电阻网络 → 引脚 → 码值 → 满量程”链路分段口径——② 引脚侧 LSB 系数(TPAFE 152.59 / STM32 805.66 µV/码)**定死**,③ 1k+1k 网络“2:1 分压 vs 源阻抗匹配”两读法待卡 0 斜率判别,④ 端子变比为仓库缺口待卡 0b;“板级系数 305.18 / 1611.3 µV/码”定性为读法 A 条件值、判别前禁入代码;新增内容全部收进 §7.1 子节、本章编号刻意保持 §9 不动(`docs/算法/继电保护功能复现规划.md` §4 有指向本文件 §9 的既有跨文档引用,挪号会打断 doc-links 章节校验)。实测数值本轮按用户指示未采(台架未跑),相关段均标“待台架实测” |
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* **内存虚拟文件系统**桩实现(cfg_store_* 系列强符号)。
|
||||
*
|
||||
* 为什么要主机侧单测(而不是只用板端套件):
|
||||
* 1. 配置引擎的核心是"文本 <-> 结构"的纯变换(INI 段/键解析、四种值类型
|
||||
* 1. 配置引擎的核心是"文本 <-> 结构"的纯变换(INI 段/键解析、五种值类型
|
||||
* 编解码、外科手术式保存、超长守卫),与硬件无关;板端套件无法穷举
|
||||
* 畸形输入,更无法在毫秒内构造"超长文件 / 写失败 / 堆分配失败"。
|
||||
* 2. 主机侧可**精确注入**故障:文件缺失、读取失败、写失败、分配失败——
|
||||
@@ -21,6 +21,12 @@
|
||||
* TC-CONFIG-003 IP4 正常与非法(段数不足、>255、前导非数字、多余后缀)
|
||||
* TC-CONFIG-004 MAC 三种分隔符 + 大小写 + 非法
|
||||
* TC-CONFIG-005 STR 正常 / 超长拒绝 / 边界(恰好放下)
|
||||
* TC-CONFIG-006 INT 合法解码:0 / -1 / INT32_MIN / INT32_MAX / 各宽度边界 /
|
||||
* 前导 + / 前导 0(数据可信卡 3:负零点偏移可落盘)
|
||||
* TC-CONFIG-007 INT 非法拒绝:空 / 非数字 / 带后缀 / 越界(含 strtol 饱和带,
|
||||
* 即超 INT32 但钳位后恰落界内的文本)/ 坏 len
|
||||
* TC-CONFIG-008 INT 编码带负号输出 + 注册校验(INT len=3 拒绝)+
|
||||
* [sys] 段 load/save/load 全链往返一致(出厂模板带负号默认)
|
||||
* —— INI 解析(s_split_line / s_match_section / s_key_value / s_parse)——
|
||||
* TC-CONFIG-010 注释(; 与 #)与空行忽略
|
||||
* TC-CONFIG-011 CRLF / LF 混排
|
||||
@@ -229,16 +235,23 @@ static const cfg_key_def_t s_net_keys[] = {
|
||||
{ "name", CFG_VAL_STR, offsetof(sec_net_t, name), 12u },
|
||||
};
|
||||
|
||||
/* 段 B:[sys],无回调,用于验证多段与超长字段拒绝 */
|
||||
/* 段 B:[sys],无回调,用于验证多段、超长字段拒绝与 INT 有符号往返(卡 3)。
|
||||
* 字段按自然对齐排布:u32@0 / i8@4 / i16@6 / i32@8,offset 用 offsetof 取真值 */
|
||||
typedef struct {
|
||||
uint32_t baud; /* offset 0 len 4 UINT */
|
||||
uint32_t baud; /* offset 0 len 4 UINT */
|
||||
int8_t ofs8; /* offset 4 len 1 INT */
|
||||
int16_t ofs16; /* offset 6 len 2 INT(负默认:模板须带负号写出) */
|
||||
int32_t ofs32; /* offset 8 len 4 INT(默认 INT32_MIN:极值往返) */
|
||||
} sec_sys_t;
|
||||
|
||||
static sec_sys_t s_sys_data;
|
||||
static const sec_sys_t s_sys_def = { 115200u };
|
||||
static const sec_sys_t s_sys_def = { 115200u, -5, -300, (-2147483647 - 1) };
|
||||
|
||||
static const cfg_key_def_t s_sys_keys[] = {
|
||||
{ "baud", CFG_VAL_UINT, offsetof(sec_sys_t, baud), 4u },
|
||||
{ "baud", CFG_VAL_UINT, offsetof(sec_sys_t, baud), 4u },
|
||||
{ "ofs8", CFG_VAL_INT, offsetof(sec_sys_t, ofs8), 1u },
|
||||
{ "ofs16", CFG_VAL_INT, offsetof(sec_sys_t, ofs16), 2u },
|
||||
{ "ofs32", CFG_VAL_INT, offsetof(sec_sys_t, ofs32), 4u },
|
||||
};
|
||||
|
||||
/* ===================== 辅助 ===================== */
|
||||
@@ -417,6 +430,103 @@ static void tc_str(void) {
|
||||
CHECK(s_decode_str(d, 8u, "") == 0 && d[0] == '\0', "empty string ok");
|
||||
}
|
||||
|
||||
/* TC-CONFIG-006/007:INT 解码(数据可信卡 3——有符号零点偏移) */
|
||||
static void tc_int_decode(void) {
|
||||
uint8_t d[4];
|
||||
|
||||
printf("[TC-CONFIG-006] INT decode valid\n");
|
||||
CHECK(s_decode_int(d, 4u, "0") == 0 && d[0] == 0u && d[3] == 0u, "int32 zero");
|
||||
CHECK(s_decode_int(d, 4u, "-1") == 0 && d[0] == 0xFFu && d[1] == 0xFFu &&
|
||||
d[2] == 0xFFu && d[3] == 0xFFu, "-1 = four FF bytes (two's complement)");
|
||||
CHECK(s_decode_int(d, 4u, "-2147483648") == 0 && d[0] == 0x00u &&
|
||||
d[1] == 0x00u && d[2] == 0x00u && d[3] == 0x80u, "INT32_MIN accepted");
|
||||
CHECK(s_decode_int(d, 4u, "2147483647") == 0 && d[0] == 0xFFu &&
|
||||
d[1] == 0xFFu && d[2] == 0xFFu && d[3] == 0x7Fu, "INT32_MAX accepted");
|
||||
CHECK(s_decode_int(d, 1u, "-128") == 0 && d[0] == 0x80u, "int8 -128");
|
||||
CHECK(s_decode_int(d, 1u, "127") == 0 && d[0] == 0x7Fu, "int8 +127");
|
||||
CHECK(s_decode_int(d, 2u, "-32768") == 0 && d[0] == 0x00u && d[1] == 0x80u,
|
||||
"int16 -32768");
|
||||
CHECK(s_decode_int(d, 2u, "32767") == 0 && d[0] == 0xFFu && d[1] == 0x7Fu,
|
||||
"int16 +32767");
|
||||
CHECK(s_decode_int(d, 4u, "+42") == 0 && d[0] == 42u, "leading + accepted");
|
||||
CHECK(s_decode_int(d, 4u, "-0") == 0 && d[0] == 0u && d[3] == 0u, "-0 decodes 0");
|
||||
CHECK(s_decode_int(d, 4u, "00012") == 0 && d[0] == 12u && d[3] == 0u,
|
||||
"leading zeros accepted");
|
||||
|
||||
printf("[TC-CONFIG-007] INT decode invalid rejected\n");
|
||||
CHECK(s_decode_int(d, 4u, "") == -1, "empty rejected");
|
||||
CHECK(s_decode_int(d, 4u, "abc") == -1, "non-numeric rejected");
|
||||
CHECK(s_decode_int(d, 4u, "12x") == -1, "trailing garbage rejected");
|
||||
CHECK(s_decode_int(d, 4u, "-") == -1, "bare sign rejected");
|
||||
CHECK(s_decode_int(d, 4u, "1.5") == -1, "decimal point rejected");
|
||||
CHECK(s_decode_int(d, 4u, "0x10") == -1, "hex text rejected");
|
||||
CHECK(s_decode_int(d, 1u, "-129") == -1, "int8 -129 rejected");
|
||||
CHECK(s_decode_int(d, 1u, "128") == -1, "int8 128 rejected");
|
||||
CHECK(s_decode_int(d, 2u, "-32769") == -1, "int16 -32769 rejected");
|
||||
CHECK(s_decode_int(d, 2u, "32768") == -1, "int16 32768 rejected");
|
||||
CHECK(s_decode_int(d, 4u, "2147483648") == -1, "INT32_MAX + 1 rejected (ERANGE)");
|
||||
CHECK(s_decode_int(d, 4u, "-2147483649") == -1, "INT32_MIN - 1 rejected (ERANGE)");
|
||||
CHECK(s_decode_int(d, 4u, "99999999999") == -1, "huge magnitude rejected (ERANGE)");
|
||||
CHECK(s_decode_int(d, 4u, "-99999999999") == -1, "huge negative rejected (ERANGE)");
|
||||
CHECK(s_decode_int(d, 3u, "1") == -1, "invalid len 3 rejected");
|
||||
}
|
||||
|
||||
/* TC-CONFIG-008:INT 编码带负号 + [sys] 段 load/save/load 全链往返 */
|
||||
static void tc_int_encode(void) {
|
||||
char buf[24];
|
||||
uint8_t field[4];
|
||||
static const cfg_key_def_t k32 = { "ofs32", CFG_VAL_INT, 0u, 4u };
|
||||
static const cfg_key_def_t k8 = { "ofs8", CFG_VAL_INT, 0u, 1u };
|
||||
|
||||
printf("[TC-CONFIG-008] INT encode (sign in text)\n");
|
||||
field[0] = 0xFFu; field[1] = 0xFFu; field[2] = 0xFFu; field[3] = 0xFFu;
|
||||
CHECK(s_encode(&k32, field, buf, sizeof(buf)) > 0 && strcmp(buf, "-1") == 0,
|
||||
"FFFFFFFF encodes -1");
|
||||
field[0] = 0x00u; field[1] = 0x00u; field[2] = 0x00u; field[3] = 0x80u;
|
||||
CHECK(s_encode(&k32, field, buf, sizeof(buf)) > 0 &&
|
||||
strcmp(buf, "-2147483648") == 0, "INT32_MIN bytes encode with sign");
|
||||
field[0] = 0xFFu; field[1] = 0xFFu; field[2] = 0xFFu; field[3] = 0x7Fu;
|
||||
CHECK(s_encode(&k32, field, buf, sizeof(buf)) > 0 &&
|
||||
strcmp(buf, "2147483647") == 0, "INT32_MAX bytes encode");
|
||||
field[0] = 0x80u;
|
||||
CHECK(s_encode(&k8, field, buf, sizeof(buf)) > 0 && strcmp(buf, "-128") == 0,
|
||||
"int8 0x80 encodes -128");
|
||||
field[0] = 0x7Fu;
|
||||
CHECK(s_encode(&k8, field, buf, sizeof(buf)) > 0 && strcmp(buf, "127") == 0,
|
||||
"int8 0x7F encodes 127");
|
||||
|
||||
printf("[TC-CONFIG-008] INT section roundtrip (load -> save -> load)\n");
|
||||
vfs_reset();
|
||||
register_std();
|
||||
CHECK(cfg_file_load() == 0, "load creates template");
|
||||
CHECK(strstr(vfs_get(), "ofs32=-2147483648") != NULL,
|
||||
"template INT32_MIN default written with sign");
|
||||
CHECK(strstr(vfs_get(), "ofs8=-5") != NULL, "template int8 default -5");
|
||||
|
||||
s_sys_data.ofs32 = -123456;
|
||||
s_sys_data.ofs16 = -2;
|
||||
s_sys_data.ofs8 = -128;
|
||||
CHECK(cfg_file_save() == 0, "save with negatives");
|
||||
CHECK(strstr(vfs_get(), "ofs32=-123456") != NULL, "negative saved with sign");
|
||||
CHECK(strstr(vfs_get(), "ofs8=-128") != NULL, "int8 -128 saved");
|
||||
s_sys_data.ofs32 = 1;
|
||||
s_sys_data.ofs16 = 1;
|
||||
s_sys_data.ofs8 = 1;
|
||||
CHECK(cfg_file_load() == 0, "reload ok");
|
||||
CHECK(s_sys_data.ofs32 == -123456 && s_sys_data.ofs16 == -2 &&
|
||||
s_sys_data.ofs8 == -128, "INT values roundtrip identical");
|
||||
|
||||
/* 越界文本 = 该键拒绝、留工厂默认并留痕;同段其余键不受牵连 */
|
||||
vfs_reset();
|
||||
register_std();
|
||||
vfs_put("[sys]\r\nbaud=9600\r\nofs32=2147483648\r\nofs8=-129\r\nofs16=-300\r\n");
|
||||
CHECK(cfg_file_load() == 0, "load with out-of-range INT keys");
|
||||
CHECK(s_sys_data.ofs32 == s_sys_def.ofs32, "out-of-range ofs32 keeps default");
|
||||
CHECK(s_sys_data.ofs8 == s_sys_def.ofs8, "out-of-range ofs8 keeps default");
|
||||
CHECK(s_sys_data.ofs16 == -300, "valid ofs16 in same section parsed");
|
||||
CHECK(s_sys_data.baud == 9600u, "UINT key beside INT parsed");
|
||||
}
|
||||
|
||||
/* TC-CONFIG-010~015:INI 解析 */
|
||||
static void tc_parse(void) {
|
||||
printf("[TC-CONFIG-010..015] INI parsing via load\n");
|
||||
@@ -522,6 +632,7 @@ static void tc_register(void) {
|
||||
{
|
||||
cfg_section_reg_t r;
|
||||
static const cfg_key_def_t bad_uint[] = { { "k", CFG_VAL_UINT, 0u, 3u } };
|
||||
static const cfg_key_def_t bad_int[] = { { "k", CFG_VAL_INT, 0u, 3u } };
|
||||
static const cfg_key_def_t bad_mac[] = { { "k", CFG_VAL_MAC, 0u, 4u } };
|
||||
static const cfg_key_def_t bad_str[] = { { "k", CFG_VAL_STR, 0u, 80u } };
|
||||
static const cfg_key_def_t zero_len[] = { { "k", CFG_VAL_UINT, 0u, 0u } };
|
||||
@@ -536,6 +647,8 @@ static void tc_register(void) {
|
||||
|
||||
r.p_keys = bad_uint;
|
||||
CHECK(cfg_file_register(&r) == -1, "UINT len=3 rejected");
|
||||
r.p_keys = bad_int;
|
||||
CHECK(cfg_file_register(&r) == -1, "INT len=3 rejected");
|
||||
r.p_keys = bad_mac;
|
||||
CHECK(cfg_file_register(&r) == -1, "MAC len=4 rejected");
|
||||
r.p_keys = bad_str;
|
||||
@@ -859,6 +972,8 @@ int main(void) {
|
||||
printf("=== Config File Tests ===\n");
|
||||
|
||||
tc_uint();
|
||||
tc_int_decode();
|
||||
tc_int_encode();
|
||||
tc_ip4();
|
||||
tc_mac();
|
||||
tc_str();
|
||||
|
||||
Reference in New Issue
Block a user