L2 相量层:采样环缓冲 + 全周傅氏 + 主机单测(台架间歇复位待查)
新增(均已通过主机侧单测与构建,**台架存在未定位的间歇性回归,见下**): - App/algo/sample_ring.c/h:按语义通道的采样环缓冲(2 周波 = 40 点), 整帧压入 + 帧计数,供 L2 按窗取数。 - App/algo/phasor.c/h:全周傅氏相量层,覆盖规划 §3.2 的 L2-1/2/3/6/7—— 系数表按 docs/算法/傅氏算法详解.md §2.3 预乘 √2/N(fAmSQ 直接即有效值); 5 个数据驱动实例(IA/UA/UN 基波 + IA 二/五次谐波);暴力滑动窗; 计算区/使用区双区原子切换。 - test/host/phasor/:主机侧单测(52/52 PASSED,无需桩)——系数表逐项复核、 幅值/有效值/相位、直流与谐波抑制、任意次谐波提取、频率敏感性(±2Hz≤3%)、 环缓冲时序与回绕、实例层与双区交换。 - 集成:adc_task 每拍压帧 + 重算(v2.4);app_main 调 phasor_init(); Keil 工程挂 phasor.c/sample_ring.c(App/algo 已在 IncludePath)。 验证: - 主机单测 52/52 PASSED。 - @build 0 错误 0 警告;内存快照已刷新(RW_IRAM1 89,320→90,936 B)。 - 台架:L2 链路数据正常(ring=10001、skip=0、回读一致、相量日志可见), 但 10 次运行中 3 次出现 UI 任务看门狗停喂(mask=0x67 缺 TASK_WD_UI) → IWDG 复位;改动前基线 6 次运行 0 次。属**未定位的间歇性回归**。 未完成:定位并修复该间歇性复位;L2 文档状态与登记簿条目尚未按结论更新。
This commit is contained in:
+97
-9
@@ -1,7 +1,9 @@
|
||||
/*
|
||||
* 模块名称:ADC Sampling Task
|
||||
* 模块功能:ADC 采集消费——TPAFE5160 快照 + STM32 ADC1 顺序扫描(经 L1 层
|
||||
* App/algo/adc_input.c 按语义取数)+ 帧统计(节拍由 TIM6 硬件定时器提供)
|
||||
* 模块功能:ADC 采集消费 + L2 相量计算——TPAFE5160 快照 + STM32 ADC1 顺序扫描
|
||||
* (经 L1 层 App/algo/adc_input.c 按语义取数)→ 压入采样环
|
||||
* (App/algo/sample_ring.c)→ 全周傅氏相量重算(App/algo/phasor.c)
|
||||
* + 帧统计(节拍由 TIM6 硬件定时器提供)
|
||||
* 状态说明:**B1 已落地(2026-10-10)**——本任务从空桩转为真实消费者:
|
||||
* 用 tpafe5160_snapshot() 原子取数(AGENTS 铁律),统计帧数/无新帧次数,
|
||||
* 并纳入看门狗 required_mask(TASK_WD_ADC)。
|
||||
@@ -35,11 +37,22 @@
|
||||
* v2.3 2026-10-10 王建锋 L1-3/L1-4:取数改走 App/algo/adc_input.c(语义映射 +
|
||||
* STM32 ADC1 9 通道扫描);首帧日志补全 8 路电气量 + 温度,
|
||||
* 心跳日志增 adc1_rc / adc1_err 观测 STM32 侧健康度。
|
||||
* v2.4 2026-10-10 王建锋 L2:接入相量层——每拍把 9 路语义值压入采样环
|
||||
* (sample_ring)并重算全部相量实例(phasor_run);
|
||||
* 攒满一周波后**首拍即落一次相量日志**(开机证据,不等
|
||||
* 10 s 心跳),心跳日志增 ring / skip 计数与各实例相量
|
||||
* (Re/Im/有效值,×0.1 定点打印,因 MicroLIB 的 snprintf
|
||||
* 无 %f)。
|
||||
* ⚠ L1-5 通道标定未实现(缺标准源)→ 暂以**原始码值**
|
||||
* 充作工程量(系数 = 1),故相量读数只证明**链路通**,
|
||||
* 不可作幅值精度判据(同 L1-3 的替代口径)。
|
||||
*/
|
||||
|
||||
#include "adc_task.h"
|
||||
#include "cmsis_os.h"
|
||||
#include "adc_input.h" /* L1 采集层:adc_input_poll()/adc_input_raw()(按语义取数) */
|
||||
#include "sample_ring.h" /* L2 输入:sample_ring_push_frame()(采样环缓冲) */
|
||||
#include "phasor.h" /* L2 相量层:phasor_run()/phasor_get()(全周傅氏) */
|
||||
#include "tpafe5160.h"
|
||||
#include "task_watchdog.h"
|
||||
#include "console_log.h"
|
||||
@@ -56,6 +69,34 @@
|
||||
* 原始码值缓冲由 L1 层 App/algo/adc_input.c 持有(消费侧按语义取数,不写死下标) */
|
||||
static uint32_t s_frames_consumed;
|
||||
static uint32_t s_frames_idle;
|
||||
/* 因通道尚未首次成功而**整拍跳过入环**的次数(不向相量窗注入 0 点) */
|
||||
static uint32_t s_frames_skip;
|
||||
|
||||
/*
|
||||
* 函数功能:打印全部相量实例(×0.1 定点)
|
||||
* 入口参数:tag - 前缀标记("first" 首次产出 / "hb" 周期心跳)
|
||||
* 返 回 值:无
|
||||
* 限定条件:仅任务上下文;调用方须保证 dbg_console_writer_ready() 为真
|
||||
* 函数说明:MicroLIB 的 snprintf **不支持 %f** → 以 ×0.1 定点整数打印
|
||||
* (打印值 = 实际值 × 10,分辨率 0.1)。逐实例数据驱动输出,
|
||||
* 实例表加行即自动覆盖。⚠ L1-5 标定未实现 → 读数单位是**原始码值**。
|
||||
*/
|
||||
static void log_phasors(const char *tag) {
|
||||
uint8_t k;
|
||||
|
||||
for (k = 0u; k < phasor_inst_count(); k++) {
|
||||
const phasor_inst_t *d = phasor_inst(k);
|
||||
phasor_t p;
|
||||
|
||||
if ((d != NULL) && (phasor_get(k, &p) == 0)) {
|
||||
DBG_INFO("phasor(%s) %s: Re=%ld Im=%ld rms=%ld (x0.1)",
|
||||
tag, d->name,
|
||||
(long)(p.fRe * 10.0f),
|
||||
(long)(p.fIm * 10.0f),
|
||||
(long)(p.fAmSQ * 10.0f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:ADC 任务初始化
|
||||
@@ -67,6 +108,7 @@ static uint32_t s_frames_idle;
|
||||
int adc_task_init(void) {
|
||||
s_frames_consumed = 0u;
|
||||
s_frames_idle = 0u;
|
||||
s_frames_skip = 0u;
|
||||
DBG_INFO("adc_task_init");
|
||||
return 0;
|
||||
}
|
||||
@@ -85,6 +127,7 @@ void adc_task_func(void *arg) {
|
||||
uint32_t next = osKernelGetTickCount();
|
||||
uint32_t report_at = next + ADC_TASK_REPORT_MS;
|
||||
uint8_t first_logged = 0u;
|
||||
uint8_t first_phasor_logged = 0u;
|
||||
|
||||
(void)arg;
|
||||
|
||||
@@ -103,7 +146,36 @@ void adc_task_func(void *arg) {
|
||||
/* r == -1:参数错。调用点 count 为常量,不可达;不计入 idle 以区分异常 */
|
||||
}
|
||||
|
||||
/* 2) 首帧码值上报:**写者就绪后**才落日志。写者未注册时 con_append 走
|
||||
/* 2) L2 输入 + 相量重算:把本拍 9 路语义值转浮点帧压入采样环,再重算全部
|
||||
* 相量实例(L2-6 滑动窗:每拍以"当前时刻为终止的前一周波"整窗重算)。
|
||||
* 任一通道读不到(尚未首次成功)→ 本拍**整帧跳过**:不向窗内注入 0 点
|
||||
* (单个 0 点会显著污染 20 点窗),并计入 skip 观测。 */
|
||||
{
|
||||
float frame[ADC_SEM_COUNT];
|
||||
uint8_t s;
|
||||
uint8_t all_ok = 1u;
|
||||
|
||||
for (s = 0u; s < (uint8_t)ADC_SEM_COUNT; s++) {
|
||||
int32_t code = 0;
|
||||
|
||||
if (adc_input_raw((adc_semantic_t)s, &code) != 0) {
|
||||
all_ok = 0u;
|
||||
break;
|
||||
}
|
||||
/* ⚠ L1-5 通道标定尚未实现(缺标准源)→ 暂以**原始码值**充作工程量
|
||||
* (系数 = 1)。标定落地后此处改为 工程量 = (code + b) × k(定点 2^10)。 */
|
||||
frame[s] = (float)code;
|
||||
}
|
||||
|
||||
if (all_ok != 0u) {
|
||||
(void)sample_ring_push_frame(frame, (uint8_t)ADC_SEM_COUNT);
|
||||
phasor_run();
|
||||
} else {
|
||||
s_frames_skip++;
|
||||
}
|
||||
}
|
||||
|
||||
/* 3) 首帧码值上报:**写者就绪后**才落日志。写者未注册时 con_append 走
|
||||
* 无锁直发路径,与 defaultTask 的存储初始化日志会字节级交错
|
||||
* (2026-10-10 台架实测);此时挂起上报,就绪后补报最近一帧。
|
||||
* 8 路电气量 + 温度全部经 adc_input_raw() **按语义**取数(L1-4),
|
||||
@@ -129,29 +201,45 @@ void adc_task_func(void *arg) {
|
||||
(long)ia, (long)ib, (long)ic, (long)in, (long)temp);
|
||||
}
|
||||
|
||||
/* 3) 看门狗心跳(本任务已纳入 required_mask) */
|
||||
/* 4) 首拍相量上报:攒满一周波(20 ms)后立即落一次,作为 L2 链路的开机证据。
|
||||
* 不依赖 10 s 心跳 → 台架 @test 的 settle 窗口内即可见(否则会被静默判据
|
||||
* 截断,见 docs/测试说明/自动化测试流程.md 的 settle 口径)。 */
|
||||
if ((first_phasor_logged == 0u) && (dbg_console_writer_ready() != 0u)) {
|
||||
phasor_t p;
|
||||
|
||||
if (phasor_get(0u, &p) == 0) {
|
||||
first_phasor_logged = 1u;
|
||||
log_phasors("first");
|
||||
}
|
||||
}
|
||||
|
||||
/* 5) 看门狗心跳(本任务已纳入 required_mask) */
|
||||
task_wd_beat(TASK_WD_ADC);
|
||||
|
||||
/* 4) 周期心跳日志:帧统计 + 驱动侧 FRSTDATA 错误计数。
|
||||
* 写者就绪前抑制(同 2) 的交错理由);到点无论是否落日志都推进下个上报点 */
|
||||
/* 6) 周期心跳日志:帧统计 + 驱动侧 FRSTDATA 错误计数 + L2 相量。
|
||||
* 写者就绪前抑制(同 3) 的交错理由);到点无论是否落日志都推进下个上报点 */
|
||||
{
|
||||
uint32_t now = osKernelGetTickCount();
|
||||
|
||||
if ((int32_t)(now - report_at) >= 0) {
|
||||
if (dbg_console_writer_ready() != 0u) {
|
||||
DBG_INFO("frames=%lu idle=%lu seq=%lu frst_err=%u adc1_rc=%d adc1_err=%lu",
|
||||
DBG_INFO("frames=%lu idle=%lu skip=%lu seq=%lu frst_err=%u adc1_rc=%d adc1_err=%lu ring=%lu",
|
||||
(unsigned long)s_frames_consumed,
|
||||
(unsigned long)s_frames_idle,
|
||||
(unsigned long)s_frames_skip,
|
||||
(unsigned long)tpafe5160_frame_seq(),
|
||||
(unsigned)tpafe5160_frstdata_err(),
|
||||
adc_input_stm32_rc(),
|
||||
(unsigned long)adc_input_stm32_errs());
|
||||
(unsigned long)adc_input_stm32_errs(),
|
||||
(unsigned long)sample_ring_frames());
|
||||
|
||||
log_phasors("hb");
|
||||
}
|
||||
report_at = now + ADC_TASK_REPORT_MS;
|
||||
}
|
||||
}
|
||||
|
||||
/* 5) 等下一个节拍(固定周期;落后则立即补齐,不累积漂移) */
|
||||
/* 7) 等下一个节拍(固定周期;落后则立即补齐,不累积漂移) */
|
||||
next += ADC_TASK_BEAT_MS;
|
||||
(void)osDelayUntil(next);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user