d59c2198eb
新增 App/algo/adc_input.c/h(L1 采集与预处理层,落实 L1-3/L1-4):
- L1-4 语义通道映射表 k_adc_map[]:UA/UN/IA → TPAFE buf[6]/[5]/[2];
UB/UC/IB/IC/IN/TEMP → STM32 ADC1 CH14/3/6/1/2/5。消费侧一律经
adc_input_raw() 按语义取数,不写死下标(V0.2 改版只改表内容)。
- L1-3 STM32 ADC1 9 通道软件触发顺序扫描:HAL_ADC_Start + 逐通道
PollForConversion/GetValue(EOC_SINGLE_CONV + 无 DMA,可逐通道轮询)。
- adc_input_poll() 统一"TPAFE 原子快照 + STM32 扫描";
adc_input_init() 做映射自检(STM32 映射通道必落在扫描序列内,否则
返回 -1),挡住"映射到不扫描的通道 → 恒读陈旧值"的静默错。
集成:
- adc_task.c 取数改走 L1 层(首帧日志补全 8 路 + 温度;心跳增
adc1_rc/adc1_err 观测 STM32 侧健康度)。
- app_main.c 在 tpafe5160_init() 之后调 adc_input_init(),失败记
SYSLOG_SELFTEST("ADC map fault")。
- Keil 工程挂 adc_input.c 并加 ../App/algo 包含路径。
- 文档同步:继电保护规划 §1.2/§3.1(L1-3/L1-4 改 ✅)、ADC通道映射说明
§7/§8/§9、产品需求 §5.9/§9、架构总览 §7、AGENTS.md、App/README.md、
App/task/README.md、新增 App/algo/README.md、boot_log_data 清单。
验证:
- @build 0 错 0 警;镜像无源文件新于 hex。
- 台架 @test PASS:ADC input layer init OK、adc1_rc=0/adc1_err=0
(30 s ≈27 万次转换零超时)、seq 仍 1000.0 Hz、adc 栈峰值
528→584 B(余 71%)、回读一致、零 [ERR]/HardFault。
证据 docs/测试说明/boot_log_data/20261010_prod_adc_l1_boot_log.txt
- 内存快照已刷新(Flash 269,404→270,712 B)。
159 lines
8.2 KiB
C
159 lines
8.2 KiB
C
/*
|
||
* 模块名称:ADC Sampling Task
|
||
* 模块功能:ADC 采集消费——TPAFE5160 快照 + STM32 ADC1 顺序扫描(经 L1 层
|
||
* App/algo/adc_input.c 按语义取数)+ 帧统计(节拍由 TIM6 硬件定时器提供)
|
||
* 状态说明:**B1 已落地(2026-10-10)**——本任务从空桩转为真实消费者:
|
||
* 用 tpafe5160_snapshot() 原子取数(AGENTS 铁律),统计帧数/无新帧次数,
|
||
* 并纳入看门狗 required_mask(TASK_WD_ADC)。
|
||
* **采样节拍(v2.2,2026-10-10)**:由 **TIM6 更新中断(1 kHz)** 硬件触发——
|
||
* ISR 内调 tpafe5160_start_conv_irq()(Src/main.c 的
|
||
* HAL_TIM_PeriodElapsedCallback);本任务**不再自行触发**,退化为**纯消费者**,
|
||
* 1ms 轮询(osDelayUntil)仅作消费节奏。详见
|
||
* docs/子系统设计/ADC采样节拍设计说明.md。
|
||
* **L1 采集层(v2.3,2026-10-10)**:取数改走 App/algo/adc_input.c——
|
||
* 本任务每拍调 adc_input_poll()(TPAFE 快照 + STM32 ADC1 9 通道扫描),
|
||
* 经 adc_input_raw() **按语义**读通道(L1-3/L1-4),不再直接触碰驱动缓冲。
|
||
* V0.1 上只有 TPAFE 三路(UA/UN/IA)测量有效;本任务只取数,不做保护运算。
|
||
* 接入前必读:取数只用 adc_input_raw() / tpafe5160_snapshot()(临界区原子快照,
|
||
* 仅任务上下文;旧的 get_buf/data_ready/clear_ready 已删除);接消费者前的
|
||
* 强制门禁见根 AGENTS.md「接 ADC / RS-485 消费者前的强制门禁」+ 建任务前后
|
||
* 各跑 scripts/stack_report.py 做栈预算。
|
||
* 适用平台:STM32F407ZGTx + TPAFE5160
|
||
* 作者:王建锋
|
||
* 创建日期:2026-07-19
|
||
* 修改记录:
|
||
* v1.0 2026-07-19 王建锋 创建初始版本
|
||
* v1.1 2026-10-05 王建锋 P3-11:注明"预留桩(未启用)",避免误认为已实现
|
||
* v2.0 2026-10-10 王建锋 B1:实现真实消费者(1ms 节拍 + 原子快照 + 帧统计 +
|
||
* 看门狗心跳),由 Src/freertos.c 创建为 adcTask
|
||
* v2.1 2026-10-10 王建锋 日志门控:首帧/周期统计仅在 dbg_console_writer_ready()
|
||
* 为真后落日志。写者未注册时控制台走无锁直发,与
|
||
* defaultTask 的存储初始化日志字节级交错(台架实测);
|
||
* 就绪前挂起上报,就绪后补报最近一帧。
|
||
* v2.2 2026-10-10 王建锋 采样节拍改由 TIM6 更新中断(1 kHz)硬件触发;本任务
|
||
* 删除自行触发,退化为纯消费者(见文件头"采样节拍")。
|
||
* v2.3 2026-10-10 王建锋 L1-3/L1-4:取数改走 App/algo/adc_input.c(语义映射 +
|
||
* STM32 ADC1 9 通道扫描);首帧日志补全 8 路电气量 + 温度,
|
||
* 心跳日志增 adc1_rc / adc1_err 观测 STM32 侧健康度。
|
||
*/
|
||
|
||
#include "adc_task.h"
|
||
#include "cmsis_os.h"
|
||
#include "adc_input.h" /* L1 采集层:adc_input_poll()/adc_input_raw()(按语义取数) */
|
||
#include "tpafe5160.h"
|
||
#include "task_watchdog.h"
|
||
#include "console_log.h"
|
||
|
||
#define DBG_TAG "[ADC_TASK]"
|
||
#include "dbg_log.h"
|
||
|
||
/* 消费轮询周期(ms)。采样节拍由 TIM6(1 kHz)提供,本周期仅作消费节奏 */
|
||
#define ADC_TASK_BEAT_MS 1U
|
||
/* 心跳日志周期(ms):周期性上报帧统计,作为采集链路存活的观测面 */
|
||
#define ADC_TASK_REPORT_MS 10000U
|
||
|
||
/* 统计:已消费帧数 / 无新帧次数(首拍必为无新帧,之后应稳定为 0 增量)。
|
||
* 原始码值缓冲由 L1 层 App/algo/adc_input.c 持有(消费侧按语义取数,不写死下标) */
|
||
static uint32_t s_frames_consumed;
|
||
static uint32_t s_frames_idle;
|
||
|
||
/*
|
||
* 函数功能:ADC 任务初始化
|
||
* 入口参数:无
|
||
* 返 回 值:0 - 成功,-1 - 失败
|
||
* 限定条件:tpafe5160_init() 已在 app_main_init()(内核启动前)完成
|
||
* 函数说明:驱动初始化不在本任务(见限定条件);此处仅清帧统计。
|
||
*/
|
||
int adc_task_init(void) {
|
||
s_frames_consumed = 0u;
|
||
s_frames_idle = 0u;
|
||
DBG_INFO("adc_task_init");
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
* 函数功能:ADC 任务主函数
|
||
* 入口参数:arg - FreeRTOS 传入参数(未使用)
|
||
* 返 回 值:无
|
||
* 限定条件:adc_task_init() 已调用;内核已启动
|
||
* 函数说明:每 1ms 采集一拍(adc_input_poll():TPAFE 原子快照 + STM32 ADC1 9 通道
|
||
* 顺序扫描)并统计。采样节拍由 TIM6 硬件定时器提供(ISR 内触发 CONVST),
|
||
* 本任务不再触发。稳态下 consumed 每拍 +1、idle 不再增长;idle 持续增长 =
|
||
* EXTI 未在读帧或 TIM6 未启动(异常),由周期心跳日志暴露。
|
||
*/
|
||
void adc_task_func(void *arg) {
|
||
uint32_t next = osKernelGetTickCount();
|
||
uint32_t report_at = next + ADC_TASK_REPORT_MS;
|
||
uint8_t first_logged = 0u;
|
||
|
||
(void)arg;
|
||
|
||
for (;;) {
|
||
int r;
|
||
|
||
/* 1) 采集一拍:L1 层 adc_input_poll() = TPAFE 原子快照 + STM32 ADC1 顺序扫描
|
||
* (仅任务上下文;TPAFE 侧 1=有新帧 0=无新帧 -1=参数错)。
|
||
* 采样节拍由 TIM6 更新中断提供(ISR 内触发 CONVST),本任务不触发。 */
|
||
r = adc_input_poll();
|
||
if (r == 1) {
|
||
s_frames_consumed++;
|
||
} else if (r == 0) {
|
||
s_frames_idle++;
|
||
} else {
|
||
/* r == -1:参数错。调用点 count 为常量,不可达;不计入 idle 以区分异常 */
|
||
}
|
||
|
||
/* 2) 首帧码值上报:**写者就绪后**才落日志。写者未注册时 con_append 走
|
||
* 无锁直发路径,与 defaultTask 的存储初始化日志会字节级交错
|
||
* (2026-10-10 台架实测);此时挂起上报,就绪后补报最近一帧。
|
||
* 8 路电气量 + 温度全部经 adc_input_raw() **按语义**取数(L1-4),
|
||
* 用于验证"语义 → 物理通道"映射与两片转换器取数契约 */
|
||
if ((first_logged == 0u) && (s_frames_consumed != 0u) &&
|
||
(dbg_console_writer_ready() != 0u)) {
|
||
int32_t ua = 0, ub = 0, uc = 0, un = 0;
|
||
int32_t ia = 0, ib = 0, ic = 0, in = 0, temp = 0;
|
||
|
||
(void)adc_input_raw(ADC_SEM_UA, &ua);
|
||
(void)adc_input_raw(ADC_SEM_UB, &ub);
|
||
(void)adc_input_raw(ADC_SEM_UC, &uc);
|
||
(void)adc_input_raw(ADC_SEM_UN, &un);
|
||
(void)adc_input_raw(ADC_SEM_IA, &ia);
|
||
(void)adc_input_raw(ADC_SEM_IB, &ib);
|
||
(void)adc_input_raw(ADC_SEM_IC, &ic);
|
||
(void)adc_input_raw(ADC_SEM_IN, &in);
|
||
(void)adc_input_raw(ADC_SEM_TEMP, &temp);
|
||
|
||
first_logged = 1u;
|
||
DBG_INFO("first: UA=%ld UB=%ld UC=%ld UN=%ld IA=%ld IB=%ld IC=%ld IN=%ld T=%ld",
|
||
(long)ua, (long)ub, (long)uc, (long)un,
|
||
(long)ia, (long)ib, (long)ic, (long)in, (long)temp);
|
||
}
|
||
|
||
/* 3) 看门狗心跳(本任务已纳入 required_mask) */
|
||
task_wd_beat(TASK_WD_ADC);
|
||
|
||
/* 4) 周期心跳日志:帧统计 + 驱动侧 FRSTDATA 错误计数。
|
||
* 写者就绪前抑制(同 2) 的交错理由);到点无论是否落日志都推进下个上报点 */
|
||
{
|
||
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",
|
||
(unsigned long)s_frames_consumed,
|
||
(unsigned long)s_frames_idle,
|
||
(unsigned long)tpafe5160_frame_seq(),
|
||
(unsigned)tpafe5160_frstdata_err(),
|
||
adc_input_stm32_rc(),
|
||
(unsigned long)adc_input_stm32_errs());
|
||
}
|
||
report_at = now + ADC_TASK_REPORT_MS;
|
||
}
|
||
}
|
||
|
||
/* 5) 等下一个节拍(固定周期;落后则立即补齐,不累积漂移) */
|
||
next += ADC_TASK_BEAT_MS;
|
||
(void)osDelayUntil(next);
|
||
}
|
||
}
|