feat(adc): 新增变比维 r 三维折算,录波段格式升 v2
采样板注入 Ua=5V/Ia=0.5A 读数偏小,根因是固件只输出引脚电压、 缺「端子→引脚」变比。折算改三维: 工程量 = (code + b) × k[µV/码] × 1e-6 × r k 保持 ROM 引脚 LSB,b 为零点,r 为端子变比(int32 Q16)。 变比不能并进 k:UA 端子域 k 达 1.5e5 µV/码、Q16 溢出 int32; r 单独存 Q16 不溢出。两点标定改标 r,k 仍是板级事实。 - 新开单段 [adc_cal_sec](8 键 r_ua..r_in),cfg 段数达 8/8 上限 - 一次变比复用 prot_set 四项,不新开段;r=0 表示本路不折算 - ROM 默认带台架实测变比(电压族 1000、电流族 29.4118), 烧进固件即显二次值;VALID 仍须两点标定,两者互不派生 - eng 判据改「每路要么已配变比、要么削顶不显示」,不再看 VALID - 104 地址 0x8050~0x8057 写 r,run_par_cal_r_set 写值与置脏同函数 - 录波 v2:通道表加 int32 r_q16,头 128→160、整段 5250→5282; code_to_milli 用余数分解换等价单次圆整,20 万组大整数对拍 0 不符 - 文档同步:采集与算法层/通道映射/故障录波/网络配置/配置文件/ 产品需求/内存台账,历史带日期实测数字按冻结口径不改 - 单测 13 套全绿,/keil-build 0 错误 0 警告(256024/31760/ 2080/152368),RAM 实测回填内存内容分配策略 §6.1
This commit is contained in:
+39
-22
@@ -519,38 +519,55 @@ void adc_task_func(void *arg) {
|
||||
wev = wave_ring_tick(now, s_frames_consumed);
|
||||
|
||||
if (wev == WAVE_RING_EVT_SEALED) {
|
||||
uint8_t c;
|
||||
|
||||
/* 封存瞬间事实(交棒结构 s_wave_cal 为文件级 static,不上栈):
|
||||
* eng / VALID 位图 / CLIPPED 位图 / 墙钟 / 逐路 conv+bits+avail+k+b。
|
||||
* eng / VALID 位图 / CLIPPED 位图 / 墙钟 / 逐路 conv+bits+avail+k+b+r。
|
||||
* eng 口径 = adc_cal.h 注③ 的 8 路量纲标签(判据源
|
||||
* App/app/ui_data_feed.c feed_eng(),与 prot_blocked 的三路口径**不是**
|
||||
* 一件事):8 路电气量全 VALID 才是二次值;V0.1 无 CT 变比,一次值不可达。
|
||||
* 一件事):**每路要么已配变比、要么削顶(页面本来就显 "-",不参与
|
||||
* 整帧量纲断言)——八路全满足即二次值,否则引脚域**。不再看 VALID:
|
||||
* VALID 回答"准不准"、r 回答"什么单位",V0.1 五路削顶永远拿不到 VALID,
|
||||
* 用 VALID 当量纲判据会让徽标永不解除。
|
||||
* wall 未对时写 0(sys_clock_get 失败 = UINT32_MAX,不许拿上电毫秒冒充)。
|
||||
* k 取 adc_cal_k_q16() int32 原值;b 的 float 回乘在本域无损
|
||||
* (|b| 限幅 8192 << 2^24,float32 精确表示整数;禁用浮点只针对 k)。 */
|
||||
* k / r 取 adc_cal_k_q16() / adc_cal_ratio_q16() 的 int32 原值(float 在
|
||||
* 5.28e7 / 6.5e7 量级掉尾数);b 的 float 回乘在本域无损
|
||||
* (|b| 限幅 8192 << 2^24,float32 精确表示整数;禁用浮点只针对 k / r)。 */
|
||||
s_wave_cal.flags_seq = 0u;
|
||||
s_wave_cal.clip_mask = 0u;
|
||||
for (c = 0u; c < WAVE_RING_CH; c++) {
|
||||
const uint32_t f = adc_cal_flags((adc_semantic_t)c);
|
||||
const adc_channel_desc_t *d = adc_input_desc((adc_semantic_t)c);
|
||||
/* 整帧量纲:每路要么已配变比、要么削顶不显示 → 全满足即二次值。
|
||||
* 削顶通道不参与断言的理由:它们页面本来就显 "-",把它们的量纲也算进
|
||||
* 整帧会把「已全部配好变比」的段误标成引脚域(V0.1 五路削顶恒成立)。
|
||||
* 与逐路取数**同一遍循环**算完:adc_cal 每拍只走一遍(逐路 flags /
|
||||
* ratio_configured / k_q16 / b_codes / ratio_q16 五个纯读子查询),
|
||||
* 不为了一个派生量再走第二趟。 */
|
||||
{
|
||||
uint8_t eng_ok = 1u;
|
||||
uint8_t c;
|
||||
|
||||
if ((f & ADC_CAL_F_VALID) != 0u) {
|
||||
s_wave_cal.flags_seq |= (uint8_t)(1u << c);
|
||||
for (c = 0u; c < WAVE_RING_CH; c++) {
|
||||
const uint32_t f = adc_cal_flags((adc_semantic_t)c);
|
||||
const adc_channel_desc_t *d = adc_input_desc((adc_semantic_t)c);
|
||||
|
||||
if ((f & ADC_CAL_F_VALID) != 0u) {
|
||||
s_wave_cal.flags_seq |= (uint8_t)(1u << c);
|
||||
}
|
||||
if ((f & ADC_CAL_F_CLIPPED) != 0u) {
|
||||
s_wave_cal.clip_mask |= (uint16_t)(1u << c);
|
||||
} else if (adc_cal_ratio_configured((adc_semantic_t)c) == 0u) {
|
||||
eng_ok = 0u;
|
||||
}
|
||||
s_wave_cal.ch[c].conv = (d != NULL) ? (uint8_t)d->source
|
||||
: (uint8_t)ADC_SRC_STM32;
|
||||
s_wave_cal.ch[c].bits = (s_wave_cal.ch[c].conv == (uint8_t)ADC_SRC_TPAFE)
|
||||
? 16u : 12u;
|
||||
s_wave_cal.ch[c].avail = (uint8_t)f;
|
||||
s_wave_cal.ch[c].k_q16 = adc_cal_k_q16((adc_semantic_t)c);
|
||||
s_wave_cal.ch[c].b_codes = (int32_t)adc_cal_b_codes((adc_semantic_t)c);
|
||||
s_wave_cal.ch[c].r_q16 = adc_cal_ratio_q16((adc_semantic_t)c);
|
||||
}
|
||||
if ((f & ADC_CAL_F_CLIPPED) != 0u) {
|
||||
s_wave_cal.clip_mask |= (uint16_t)(1u << c);
|
||||
}
|
||||
s_wave_cal.ch[c].conv = (d != NULL) ? (uint8_t)d->source
|
||||
: (uint8_t)ADC_SRC_STM32;
|
||||
s_wave_cal.ch[c].bits = (s_wave_cal.ch[c].conv == (uint8_t)ADC_SRC_TPAFE)
|
||||
? 16u : 12u;
|
||||
s_wave_cal.ch[c].avail = (uint8_t)f;
|
||||
s_wave_cal.ch[c].k_q16 = adc_cal_k_q16((adc_semantic_t)c);
|
||||
s_wave_cal.ch[c].b_codes = (int32_t)adc_cal_b_codes((adc_semantic_t)c);
|
||||
s_wave_cal.eng = (eng_ok != 0u)
|
||||
? (uint8_t)WAVE_ENG_SEC : (uint8_t)WAVE_ENG_PIN;
|
||||
}
|
||||
s_wave_cal.eng = (s_wave_cal.flags_seq == 0xFFu)
|
||||
? (uint8_t)WAVE_ENG_SEC : (uint8_t)WAVE_ENG_PIN;
|
||||
{
|
||||
uint32_t wall = sys_clock_get();
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
* (自检已上报、卷保留、不重格式化),等下次上电重新 resume */
|
||||
#define STORAGE_BRINGUP_RETRIES 3u
|
||||
|
||||
/* 录波段落盘持锁耗时告警线(lead 裁③i):一次心跳整段 5250 B(约 21 次 256 B
|
||||
/* 录波段落盘持锁耗时告警线(lead 裁③i):一次心跳整段 5282 B(约 21 次 256 B
|
||||
* f_write),设计 §4.2 预估落盘窗「几十 ms」;越过本线说明本段在占用 defaultTask
|
||||
* 喂狗链前的最慢一段,按事件补一条 WARN(不是周期行),并把历史峰值留在本层
|
||||
* 静态里供收工报告核喂狗余量(vol_lock.h 铁律「等锁即毁喂狗链」的前例在案) */
|
||||
@@ -267,7 +267,8 @@ void default_task_func(void *argument) {
|
||||
syslog_post(SYSLOG_OPER, "settings saved");
|
||||
}
|
||||
/* 运行参数段落盘(「数据可信」这一项):同款脏语义;save = 引擎整档外科写
|
||||
* ([run_par] 9 键 + 被 adc_cal_set 联动改写的 [adc_cal] 两段一趟落) */
|
||||
* ([run_par] 9 键 + 被 adc_cal_set() / adc_cal_set_ratio() 联动改写的
|
||||
* [adc_cal] / [adc_cal_ofs] / [adc_cal_sec] 三段一趟落) */
|
||||
(void)run_par_store_save();
|
||||
/* [comm] 段落盘(道G task-9 通讯设置页):同款脏位、每轮无条件,
|
||||
* 无事件位(task-8 D5 口径——ca 改后重启生效,落盘只保断电不丢) */
|
||||
@@ -624,7 +625,7 @@ static FRESULT storage_bring_up(FATFS *fs) {
|
||||
* 入口参数:无 void
|
||||
* 返回值:无 void
|
||||
* 限定条件:仅 defaultTask 主循环调用(vol_idle_sync_poll 之后);内部绝不阻塞——
|
||||
* try 锁 + 有界段长(5250 B / 256 B ≈ 21 次 f_write,设计 §4.2 预估几十 ms),
|
||||
* try 锁 + 有界段长(5282 B / 256 B ≈ 21 次 f_write,设计 §4.2 预估几十 ms),
|
||||
* 绝不把喂狗点做成等待态(vol_lock.h 使用约定:defaultTask 一律 try)
|
||||
* 函数说明:所有权按 docs/子系统设计/故障录波设计说明.md §5.1——环的生产者是
|
||||
* adcTask,本道是唯一落盘消费者;跨任务握手只认 wave_ring_sealed()。
|
||||
@@ -632,9 +633,10 @@ static FRESULT storage_bring_up(FATFS *fs) {
|
||||
* ① 打开一律 FA_WRITE|FA_CREATE_ALWAYS(**覆盖,不是追加**):段名是 100 个
|
||||
* 槽位的轮转复用(wave_store.h 的「号 0..99 轮转覆盖,写满回绕盖最老的」),
|
||||
* 槽位被复用时同名文件已存在——用 FA_OPEN_APPEND 会把新段接在旧段尾巴上,
|
||||
* 文件长成 5250×N,而回放页的单段读侧要求 size == 5250,于是逐件判
|
||||
* 文件长成 段长×N,而回放页的单段读侧要求 size == 段长,于是逐件判
|
||||
* 「长度废」(reject=2),全板没有一段放得出来。2026-11-10 板级实测:
|
||||
* 100 个文件 / 1868 段全部合法、零损坏,纯粹是容器尺寸与写侧追加语义相抵。
|
||||
* 100 个文件 / 1868 段全部合法、零损坏,纯粹是容器尺寸与写侧追加语义相抵
|
||||
* (当时段长 5250 B = v1 布局;v2 加变比维后为 5282 B,判据形状不变)。
|
||||
* 本移植 FA_CREATE_ALWAYS 会 remove_chain 并清零目录项的 FileSize,
|
||||
* 是真正的截断(Lib/FatFs/ff.c 的 f_open 覆盖分支);
|
||||
* 注意 FA_CREATE_ALWAYS 单独用**不含 FA_WRITE**(本移植旧式标志位),
|
||||
@@ -802,7 +804,7 @@ static void wave_store_drain(void) {
|
||||
if (rc == WAVE_ST_DONE) {
|
||||
break;
|
||||
}
|
||||
if (++iter > 32u) { /* 5250/256 = 21 片;游标卡死时的有界保险 */
|
||||
if (++iter > 32u) { /* 5282/256 ≈ 21 片;游标卡死时的有界保险 */
|
||||
why = "pack stall";
|
||||
break;
|
||||
}
|
||||
|
||||
+20
-15
@@ -84,7 +84,7 @@
|
||||
* wave_store.h 给段格式解析与定点 mV 换算(唯一口径,页面不复述算式);
|
||||
* vol_lock.h + ff.h:/wave 只读遍历按 soe_store.c 读者同款纪律——
|
||||
* try 锁非阻塞(卷忙本轮按「无段」处理,uiTask 绘制节拍绝不等待)、
|
||||
* 锁跨完整文件操作序列、小窗口分次读、整段 5250B 绝不驻留 RAM */
|
||||
* 锁跨完整文件操作序列、小窗口分次读、整段 5282B 绝不驻留 RAM */
|
||||
#include "ff.h"
|
||||
#include "vol_lock.h"
|
||||
#include "wave_ring.h"
|
||||
@@ -404,7 +404,7 @@ static const ui_soe_reader_t s_soe_reader = { ui_task_soe_count, ui_task_soe_get
|
||||
* - 段完整性判定只信 wave_store 自己的判据(尺寸等式 / magic+ver / 双 CRC /
|
||||
* sem==index),本胶水不读 avail/flags 做二次裁断(§6.3 与 2026-10-29
|
||||
* 「VALID+CLIPPED 非矛盾」裁定同源:可用性不是完整性);
|
||||
* - 整段 5250B 不驻留:256B 静态窗口分次读,头区一次、数据区流式过 CRC。
|
||||
* - 整段 5282B 不驻留:256B 静态窗口分次读,头区一次、数据区流式过 CRC。
|
||||
* uiTask 阻塞上界(IWDG 800ms 窗、uiTask 在 required_mask 内;写法对齐上方
|
||||
* 维护通道那段上界,同文件先例):
|
||||
* - 第零项是「本块不等待」:vol_lock_wait(0) 拿不到锁即按「本轮无段」
|
||||
@@ -412,7 +412,7 @@ static const ui_soe_reader_t s_soe_reader = { ui_task_soe_count, ui_task_soe_get
|
||||
* - 一次渲染实际发生的 I/O(进回放页首绘、或按键后重绘那帧)三件叠加:
|
||||
* ① 表重扫 = 1 次 f_opendir + 至多 WAVE_ST_SEG_MAX(100)次 f_readdir,
|
||||
* 收尾是 100 元插入排序(纯内存);
|
||||
* ② 段头装载 = 仅未缓存段才发生:1 次 f_open + 头区 1 次 128B 读 +
|
||||
* ② 段头装载 = 仅未缓存段才发生:1 次 f_open + 头区 1 次 160B 读 +
|
||||
* head_parse 内的头 CRC + 数据区 20 次 256B 流式读 + 尾 2B 一次
|
||||
* = 最坏 22 次小读;5120B 逐字节 CRC 与 sem 表校验是纯计算,
|
||||
* 168MHz 下微秒到毫秒量级;缓存命中零卷操作;
|
||||
@@ -589,7 +589,7 @@ static uint8_t ui_task_wave_find(uint8_t a_idx, uint8_t *a_pos) {
|
||||
* 成功则把段头留在 s_wave_meta 供 seg_win 免重读
|
||||
* 入口参数:a_idx - 段号
|
||||
* 返回值:UI_WAVE_OK / UI_WAVE_NONE(卷忙/文件没了)/ UI_WAVE_BAD(判废) int
|
||||
* 限定条件:uiTask;锁跨 open→close 完整序列;头 128B 一次、数据区 256B
|
||||
* 限定条件:uiTask;锁跨 open→close 完整序列;头 160B 一次、数据区 256B
|
||||
* 分窗流式(共约 1 + 20 次 f_read),整段不驻留
|
||||
* 耗时账(lead 裁定 2026-10-29 第 1 条·第三条并存):未缓存段的完整性
|
||||
* 判定最坏约 21 次小读 + 5120B 逐位 CRC,全部在卷锁内、uiTask 上;
|
||||
@@ -597,7 +597,7 @@ static uint8_t ui_task_wave_find(uint8_t a_idx, uint8_t *a_pos) {
|
||||
* 缓存只留一段:左右切段 A→B→A 会把 B 的校验再来一遍——接受该
|
||||
* 成本的代价换「CRC 不符必须拒收」永远现算,不做跨键存的位图
|
||||
* 函数说明:缓存命中(同段且已验)直接返 OK——左右切段回到上一段、上下切
|
||||
* 通道都靠它把重复的 5250B 全扫压成一次;文件读中途消失(被别的
|
||||
* 通道都靠它把重复的 5282B 全扫压成一次;文件读中途消失(被别的
|
||||
* 写者轮转掉)按 NONE 处理,页面下轮自然重扫;五类判废(reject=
|
||||
* 原因码见上方 UI_WAVE_REJ_* 组)在本函数收尾统一收敛成一条
|
||||
* DBG_ERROR——只有串口在手的远程轮也要有判废可查面(#93 F2,
|
||||
@@ -641,7 +641,7 @@ static int ui_task_wave_load_head(uint8_t a_idx) {
|
||||
size = (uint32_t)f_size(&f);
|
||||
total = 0u;
|
||||
rc = UI_WAVE_BAD;
|
||||
/* 第一步:头区(首窗 128B = 当前形态整头);head_parse 自带判废 */
|
||||
/* 第一步:头区(首窗 160B = 当前形态整头);head_parse 自带判废 */
|
||||
n = (size < (uint32_t)WAVE_ST_HEAD_SZ_CUR) ? size
|
||||
: (uint32_t)WAVE_ST_HEAD_SZ_CUR;
|
||||
fr = f_read(&f, s_wave_buf, n, &br);
|
||||
@@ -656,7 +656,7 @@ static int ui_task_wave_load_head(uint8_t a_idx) {
|
||||
uint16_t crc;
|
||||
uint8_t tail[2];
|
||||
|
||||
/* 第三步:数据区流式 CRC(128B 窗;CRC 覆盖不含尾两字节自身) */
|
||||
/* 第三步:数据区流式 CRC(256B 窗;CRC 覆盖不含尾两字节自身) */
|
||||
crc = WAVE_ST_CRC_INIT;
|
||||
off = wave_store_head_bytes(s_wave_meta.ch_count);
|
||||
rc = UI_WAVE_BAD;
|
||||
@@ -876,15 +876,17 @@ static int ui_task_wave_seg_head(uint8_t a_idx, uint8_t *a_eng,
|
||||
/*
|
||||
* 函数功能:读固定显示窗(触发点前后各一周波)的码值与峰值
|
||||
* 入口参数:a_idx - 段号;a_ch - 通道号;a_codes - UI_WAVE_WIN_PTS 个码值
|
||||
* (已并入段头 b_codes);a_peak_milli - 窗内绝对值最大点的 mV
|
||||
* (已并入段头 b_codes);a_peak_milli - 窗内绝对值最大点的**录波域毫单位**
|
||||
* (eng=1 二次 mV/mA,eng=0 引脚 mV;变比由 wave_store_code_to_milli
|
||||
* 按段头 r_q16 现折,本层不重算)
|
||||
* 返回值:UI_WAVE_OK / NONE(段头未验过/卷忙)/ BAD(数据区读不齐) int
|
||||
* 限定条件:uiTask;先段头后窗口(未过完整性判定的段不供数据,页面显示
|
||||
* 「该段不可读」而不是半截波形——判废优先于展示)
|
||||
* 函数说明:窗口起点用段头 pre_pts 现算(§8.2「数据不是编译常量」:
|
||||
* 起点 = pre_pts - 窗长一半,与光标居中是同一个 20 的一半,
|
||||
* 改窗宽只改 ui_data.h 的 UI_WAVE_WIN_PTS 一处);峰值的 mV
|
||||
* 换算逐点走 wave_store_code_to_milli —— 全仓唯一口径,
|
||||
* 这里不重抄公式(§6.3;PC 道同一条款)
|
||||
* 改窗宽只改 ui_data.h 的 UI_WAVE_WIN_PTS 一处);峰值的米制单位
|
||||
* 换算逐点走 wave_store_code_to_milli —— 全仓唯一口径(v2 起自带变比,
|
||||
* peak 自动跟着录波域走),这里不重抄公式(§6.3;PC 道同一条款)
|
||||
*/
|
||||
static int ui_task_wave_seg_win(uint8_t a_idx, uint8_t a_ch,
|
||||
int16_t *a_codes, int32_t *a_peak_milli) {
|
||||
@@ -959,10 +961,13 @@ static int ui_task_wave_seg_win(uint8_t a_idx, uint8_t a_ch,
|
||||
a_codes[i] = (int16_t)v;
|
||||
/* 显示与度量同一把尺(「谁被显示谁被度量」,§6.3):
|
||||
* a_codes 画的是 raw+b,而 wave_store_code_to_milli 的公式是
|
||||
* (a_code + b_codes) × k_q16 / 65536000 取整(App/algo/
|
||||
* wave_store.c:567 实测口径)——传 raw 进去,b 已由唯一口径
|
||||
* 自己加过,PK 数与屏上波形天然同尺,这里不再加第二遍
|
||||
* (加两遍才是错);clamp 只护 int16 画布,b 的 V0.1 恒 0 */
|
||||
* (a_code + b_codes) × k_q16 × r_q16 的定点折算(口径唯一属主
|
||||
* App/algo/wave_store.c 的 wave_store_code_to_milli)——传 raw 进去,
|
||||
* b 与 r 都已由唯一口径自己算过,PK 数与屏上波形天然同尺,
|
||||
* **这里不再加第二遍 b,也不乘 r**(重复折算才是错);
|
||||
* 输出是「录波域毫单位」:eng=1 时是二次 mV/mA,eng=0 时是引脚 mV,
|
||||
* 本段不重算公式、也不按当前固件系数猜单位。
|
||||
* clamp 只护 int16 画布,b 的 V0.1 恒 0 */
|
||||
if (wave_store_code_to_milli(&s_wave_meta.ch[a_ch], raw[i],
|
||||
&milli) == 0) {
|
||||
if ((milli < 0 ? -milli : milli) >
|
||||
|
||||
Reference in New Issue
Block a user