Files
STM32F4-Base/App/task/adc_task.c
T
wandering1 da66c500ea fix(console): gate adcTask logs on console-writer readiness
B1 on-board closed loop surfaced a byte-level console interleave: before
the console writer (defaultTask) registers, con_append() routes to
dbg_console_write_raw() -> con_tx_bounded(), which is register-level
polling with no mutual exclusion. adcTask logged every 1 ms, so its
first-frame line collided with defaultTask's [NAND_FTL] init line and
the required marker spanned two interleaved lines -> false FAIL.

- console_log: add dbg_console_writer_ready() (returns s_con_writer_up)
- adc_task: gate first-frame and 10 s stats logs on writer readiness;
  defer the first-frame report until the writer is up
- docs: close B1/P1-1 in the registry; flip stale "remaining @test"
  notes; correct the console doc's obsolete "drop-only, no interleave"
  claim (that held for HAL gState, not register-level raw send)

Bench (run_boot_test.py): VERDICT PASS, readback OK. adcTask
frames/seq +10000 per 10 s = 1000 Hz, idle=0, frst_err=0; stack
496/2048 (75%). @build 0/0.
2026-10-10 14:53:36 +08:00

132 lines
6.2 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* 模块名称:ADC Sampling Task
* 模块功能:TPAFE5160 8 通道同步采样——1ms 采样节拍 + 原子快照消费 + 帧统计
* 状态说明:**B1 已落地(2026-10-10)**——本任务从空桩转为真实消费者:
* 每 1ms 触发一次 CONVST,用 tpafe5160_snapshot() 原子取数(AGENTS 铁律),
* 统计帧数/无新帧次数,并纳入看门狗 required_mask(TASK_WD_ADC)。
* ⚠ B1 阶段用**任务周期软触发**(osDelayUntil)产生采样节拍,满足 L1-1
* 判据(BUSY 连续脉冲 + frame_seq 持续递增);**精确 1 kHz / 等周波调频**
* 的硬件定时器触发为后续(需 CubeMX 加 TIM)。
* V0.1 上只有 TPAFE 三路(UA/UN/IA)测量有效;本任务只取数,不做保护运算。
* 接入前必读:取数只用 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 的存储初始化日志字节级交错(台架实测);
* 就绪前挂起上报,就绪后补报最近一帧。
*/
#include "adc_task.h"
#include "cmsis_os.h"
#include "tpafe5160.h"
#include "task_watchdog.h"
#include "console_log.h"
#define DBG_TAG "[ADC_TASK]"
#include "dbg_log.h"
/* 采样节拍周期(ms)。B1:任务周期软触发;硬件定时器方案见文件头说明 */
#define ADC_TASK_BEAT_MS 1U
/* 心跳日志周期(ms):周期性上报帧统计,作为采集链路存活的观测面 */
#define ADC_TASK_REPORT_MS 10000U
/* 最新一帧原始码值(8 通道;语义映射见 docs/硬件平台/ADC通道映射说明.md) */
static int16_t s_frame[TPAFE5160_CH_NUM];
/* 统计:已消费帧数 / 无新帧次数(首拍必为无新帧,之后应稳定为 0 增量) */
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 触发一次 CONVST(采样节拍),随后原子快照取最新一帧并统计。
* 首拍无帧属正常(本次转换尚未完成);之后每拍消费上一拍触发的帧,
* 故稳态下 consumed 每拍 +1、idle 不再增长。idle 持续增长 = EXTI 未在
* 读帧(异常),由周期心跳日志暴露。
*/
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) 采样节拍:触发一次 CONVST(B1:任务周期软触发) */
tpafe5160_start_conv_irq();
/* 2) 原子快照消费最新一帧(仅任务上下文;1=有新帧 0=无新帧 -1=参数错) */
r = tpafe5160_snapshot(s_frame, (uint8_t)TPAFE5160_CH_NUM);
if (r == 1) {
s_frames_consumed++;
} else if (r == 0) {
s_frames_idle++;
} else {
/* r == -1:参数错。调用点 count 为常量,不可达;不计入 idle 以区分异常 */
}
/* 2b) 首帧码值上报:**写者就绪后**才落日志。写者未注册时 con_append 走
* 无锁直发路径,与 defaultTask 的存储初始化日志会字节级交错
* (2026-10-10 台架实测);此时挂起上报,就绪后补报最近一帧。
* 只报 TPAFE 三路(UA=buf[6] UN=buf[5] IA=buf[2]),验证取数契约 */
if ((first_logged == 0u) && (s_frames_consumed != 0u) &&
(dbg_console_writer_ready() != 0u)) {
first_logged = 1u;
DBG_INFO("first frame: UA=%d UN=%d IA=%d",
s_frame[6], s_frame[5], s_frame[2]);
}
/* 3) 看门狗心跳(本任务已纳入 required_mask) */
task_wd_beat(TASK_WD_ADC);
/* 4) 周期心跳日志:帧统计 + 驱动侧 FRSTDATA 错误计数。
* 写者就绪前抑制(同 2b 的交错理由);到点无论是否落日志都推进下个上报点 */
{
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",
(unsigned long)s_frames_consumed,
(unsigned long)s_frames_idle,
(unsigned long)tpafe5160_frame_seq(),
(unsigned)tpafe5160_frstdata_err());
}
report_at = now + ADC_TASK_REPORT_MS;
}
}
/* 5) 等下一个节拍(固定周期;落后则立即补齐,不累积漂移) */
next += ADC_TASK_BEAT_MS;
(void)osDelayUntil(next);
}
}