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.
This commit is contained in:
@@ -26,6 +26,11 @@
|
||||
* 都换成 DWT 周期计数硬上界(DWT 使能失败时退回旧 HAL 行为)。
|
||||
* 另加 USART1 未初始化(huart1.Instance == NULL)短路:防止在
|
||||
* HAL_Init 早期/断言-故障上报路径上解引用 NULL 读写地址 0。
|
||||
* 2026-10-10 王建锋 新增 dbg_console_writer_ready() 查询:写者未注册时
|
||||
* con_append 走无锁直发(前提是"此窗口只有 defaultTask 记日志"),
|
||||
* adcTask(B1)等周期任务此时落日志会与 defaultTask 的存储初始化
|
||||
* 日志字节级交错(台架实测 [ADC_TASK] 首帧行与 [NAND_FTL] 行交错)。
|
||||
* 提供就绪查询供周期任务门控自身日志,不改直发路径本身。
|
||||
*/
|
||||
|
||||
#include "console_log.h"
|
||||
@@ -223,6 +228,19 @@ void dbg_console_writer_attach(void) {
|
||||
s_con_writer_up = 1U;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:查询控制台写者是否已注册(1=已注册,此后日志走环形缓冲行级原子路径)
|
||||
* 返回值:s_con_writer_up(volatile 读,任意任务上下文可调)
|
||||
* 限定条件:无
|
||||
* 函数说明:供**周期性任务**在写者就绪前抑制自身日志。写者未注册时 con_append 走
|
||||
* dbg_console_write_raw 无锁直发(设计前提是"此窗口只有 defaultTask 记日志"),
|
||||
* 其余任务此时落日志会与 defaultTask 的存储初始化日志逐字节交错
|
||||
* (2026-10-10 台架:adcTask 首帧日志与 [NAND_FTL] 初始化行交错)。
|
||||
*/
|
||||
uint8_t dbg_console_writer_ready(void) {
|
||||
return s_con_writer_up;
|
||||
}
|
||||
|
||||
/*
|
||||
* 函数功能:USART1 僵尸通道自愈(清错误锁存 + 整外设重初始化)
|
||||
* 函数说明:RX 悬空噪声(PA10 未接线)锁存的 ORE/NE/FE/PE 使 HAL 轮询 TX 恒快速失败
|
||||
|
||||
@@ -34,6 +34,10 @@ extern "C" {
|
||||
* DBG_PRINT 宏内用库 snprintf 完成)
|
||||
* dbg_console_write_raw - 异常/早期路径:有界单次直发(不加锁)
|
||||
* dbg_console_writer_attach- 写者任务(defaultTask)注册自己为唯一发送者
|
||||
* dbg_console_writer_ready - 写者是否已注册(1=已注册)。供**周期性任务**在写者就绪前
|
||||
* 抑制日志用:写者未注册时 con_append 走无锁直发路径
|
||||
* (仅假设 defaultTask 在日志),其余任务此时落日志会与
|
||||
* defaultTask 的存储初始化日志字节级交错(2026-10-10 台架实测)
|
||||
* dbg_console_drain - 写者任务排空缓冲并发送(单次有界;TX 失败自动软复位自愈)
|
||||
* dbg_console_dropped - 因缓冲满丢弃的行数(诊断)
|
||||
* dbg_console_tx_fault - 因僵尸通道软复位自愈的次数(诊断)
|
||||
@@ -47,6 +51,7 @@ extern "C" {
|
||||
int dbg_console_write_line(const char *line, unsigned len);
|
||||
void dbg_console_write_raw(const char *buf, unsigned len);
|
||||
void dbg_console_writer_attach(void);
|
||||
uint8_t dbg_console_writer_ready(void);
|
||||
void dbg_console_drain(void);
|
||||
uint32_t dbg_console_dropped(void);
|
||||
uint32_t dbg_console_tx_fault(void);
|
||||
|
||||
+25
-12
@@ -20,12 +20,17 @@
|
||||
* 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"
|
||||
@@ -82,31 +87,39 @@ void adc_task_func(void *arg) {
|
||||
r = tpafe5160_snapshot(s_frame, (uint8_t)TPAFE5160_CH_NUM);
|
||||
if (r == 1) {
|
||||
s_frames_consumed++;
|
||||
if (first_logged == 0u) {
|
||||
first_logged = 1u;
|
||||
/* 只报 TPAFE 三路(UA=buf[6] UN=buf[5] IA=buf[2]),验证取数契约 */
|
||||
DBG_INFO("first frame: UA=%d UN=%d IA=%d",
|
||||
s_frame[6], s_frame[5], s_frame[2]);
|
||||
}
|
||||
} 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 错误计数 */
|
||||
/* 4) 周期心跳日志:帧统计 + 驱动侧 FRSTDATA 错误计数。
|
||||
* 写者就绪前抑制(同 2b 的交错理由);到点无论是否落日志都推进下个上报点 */
|
||||
{
|
||||
uint32_t now = osKernelGetTickCount();
|
||||
|
||||
if ((int32_t)(now - report_at) >= 0) {
|
||||
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());
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user