TaskLoop.c/h 现役注释 5 处 + tests 4 处 (180/983/997/1004/84 + 6/103/104/127) 代码本体 V4.36.38 已是一长两短 6 相, 注释滞后同步 devlog V2.77 / spec V2.59 (历史版本行保留当时变更记录) 语法0 error, 4测试全绿
1798 lines
84 KiB
C
1798 lines
84 KiB
C
|
||
|
||
#include "TaskLoop.h"
|
||
#include "at32f421_board.h"
|
||
#include "cmcng.h"
|
||
#include "FreeRTOS.h"
|
||
#include "task.h"
|
||
#include <stdlib.h>
|
||
|
||
/*===========================================================================
|
||
* 灵敏度表 — 对齐 M1H 参考实现
|
||
* 进入阈值 = Origin × SensTable[SENS] / 65536
|
||
* 离开阈值(滞回)= Origin × SensTable_1[SENS] / 65536
|
||
* SENS: 0=低灵敏, 3=高灵敏
|
||
*===========================================================================*/
|
||
#if USE_PLANB_TABLE
|
||
/* 方案B (V4.07): 现场实测调校 — SENS=3 进入 21→25、释放 14→12 接近 PD132T 最高灵敏手感
|
||
* 其他三档按等比 (进入 ×25/21=1.190, 释放 ×12/14=0.857) 推算:
|
||
* {300,60,32,21}→{357,71,38,25} {60,32,21,14}→{51,27,18,12}
|
||
* V4.07: SENS=0 进入 357→337 现场回调 (等比值偏低档触发过深, 折中)
|
||
* 物理: 进入加深(触发更严) + 释放调浅(释放更晚), 滞回 66%→47%
|
||
* ⚠ 与 PD132T 表值不再一致(行为等效) */
|
||
const uint16_t SensTable[4] = {337, 71, 38, 25}; // 方案B: 进入调深 (V4.07: SENS=0 回调 357→337)
|
||
const uint16_t SensTable_1[4] = { 51, 27, 18, 12}; // 方案B: 释放调浅 (V4.06 现场调校)
|
||
#else
|
||
const uint16_t SensTable[4] = {300, 60, 32, 21}; // V2.11: 全档与 PD132T SensTable 完全一致 (0.458%/0.092%/0.049%/0.032% df/f)
|
||
const uint16_t SensTable_1[4] = { 60, 32, 21, 14}; // V2.11: 离开分档与 PD132T 完全一致 (SENUP 标贴OFF=SET_ASB=1=出厂默认)
|
||
#endif
|
||
|
||
/*===========================================================================
|
||
* 全局状态变量 — 捕获 & 测量
|
||
*===========================================================================*/
|
||
uint32_t g_sys_freq = 0;
|
||
uint8_t g_input_div = 1;
|
||
|
||
uint16_t loop1_Xn;
|
||
uint16_t loop1_CapThis;
|
||
uint16_t loop1_CapLast;
|
||
uint16_t loop1_LPCNT;
|
||
uint16_t loop1_CapCnt;
|
||
uint32_t loop1_CapSum;
|
||
uint32_t loop1_Value;
|
||
uint32_t loop1_CAPVD;
|
||
uint32_t loop1_CAPVD_slow = 0; // 释放判定慢滤波值 (方案A)
|
||
uint32_t g_rel_smooth = 0; // V4.36.19: 低Q 释放平滑值 (τ≈50ms, 复刻打印降采样平均效应)
|
||
uint8_t g_q_lo = 0; // V4.36.19: 低Q 档标志 (空场 f>100kHz, update_q_confirm_need 同步)
|
||
uint16_t g_origin_freeze_tick = 0; // V4.36.26/32: 释放后 Origin 全冻结计数 (1s)
|
||
uint16_t g_origin_lowlearn_tick = 0; // V4.36.32: 冻结后降权低速补偿计数 (2s, 残余低值降权学入)
|
||
uint16_t g_rel_band_cnt = 0; // V4.36.30: 低Q 环带超时兜底计数 (离开触发深度持续 2.5s → 强制释放)
|
||
uint32_t g_vacuum_ema = 0; // V4.36.31: 真空 EMA 基线 (B3: 释放/触发参考与锚错 Origin 解耦)
|
||
uint16_t g_ready_hold_tick = 0; // V4.36.36: 判稳后就绪巩固期计数 (5ms tick, poll_green_led 递减; 按 Q 档置 5s/7s/10s)
|
||
uint32_t loop1_Origin;
|
||
uint32_t loop1_ORG_SUM;
|
||
uint16_t loop1_ORG_CNT;
|
||
uint16_t loop1_dlt_ORG;
|
||
uint8_t Flt_Reg;
|
||
volatile uint16_t g_wdg_counter = 0; // 看门狗活性计数 (TMR15 ISR 5ms 递增)
|
||
|
||
/* M4 优化: 快速 IIR + 进入确认 */
|
||
uint8_t loop1_entry_cnt;
|
||
uint16_t loop1_freeze_cnt;
|
||
uint32_t loop1_freeze_ref;
|
||
|
||
/*===========================================================================
|
||
* 断开保持/重连参数 (ISR 5ms tick 使用, 须定义于 TMR15 ISR 之前)
|
||
*===========================================================================*/
|
||
#define RELAY_HOLD_MS 1000 /* V4.36.3/7: 有车断开继电器保持超时(可调宏, ms)
|
||
* 断开前感应有车且继电器输出中 → 不断开而保持,
|
||
* 超时仍断开才释放(防瞬时断开误释放/防砸);
|
||
* 保持期内重连且有车 → 输出无缝续持 */
|
||
|
||
/* V4.36.5/6: 重连起振抑制窗 — 按线圈 Q 分档 (Q∝1/f, 用空场频率代理)
|
||
* 低Q(高频小L): 起振慢/幅度弱 → 前几窗测量偏差大 → 假"有车"跳输出
|
||
* 高Q(低频大L): 起振快样本干净 → 仅需短兜底窗 (V4.36.6: 100ms)
|
||
* 分界(实测): 46~50µH→109~113k 必跳 → >100k 归低Q; 80~100k 中Q; <=80k 高Q */
|
||
#define RECONNECT_Q_HI_F_KHZ 80 /* f <= 80kHz (L≳92µH@43nF) → 高Q */
|
||
#define RECONNECT_Q_MID_F_KHZ 100 /* 80< f <=100kHz → 中Q */
|
||
#define RECONNECT_BLANK_Q_HI_MS 200 /* V4.36.7: 高Q 短兜底窗(起振/插头瞬态) */
|
||
#define RECONNECT_BLANK_Q_MID_MS 300 /* V4.36.7: 中Q 短抑制 */
|
||
#define RECONNECT_BLANK_Q_LO_MS 1500 /* V4.36.11: 低Q(f>100k, L<~59µH): 长抑制 */
|
||
|
||
/*===========================================================================
|
||
* 全局状态变量 — 标志位
|
||
*===========================================================================*/
|
||
uint8_t loop1_INI_LOOP;
|
||
uint8_t loop1_CAP_OK;
|
||
uint8_t loop1_VD_FLAG;
|
||
uint8_t loop1_VD_HOLD;
|
||
uint8_t loop1_RF_FLAG;
|
||
uint8_t loop1_LOOP_OK;
|
||
uint8_t loop1_LOOP_OK0;
|
||
uint8_t loop1_FLAG_IN;
|
||
uint8_t loop1_FLAG_OUT;
|
||
uint8_t loop1_FLAG_PLUSE;
|
||
uint8_t loop1_SensLevel;
|
||
uint8_t loop1_cnt_release;
|
||
uint8_t g_loop_stable;
|
||
|
||
/* V4.23: 稳定期(上电自检)计数器 — 基准值稳定后才结束自检 */
|
||
uint16_t g_stable_cnt = 0; // 稳定期累计样本 (每次 CAP_OK +1)
|
||
uint16_t g_settle_cnt = 0; // 基准稳定连续窗口计数 (窗口=100样本≈1s)
|
||
|
||
/* V4.24: 环境重校准(受控基线更新)状态 — 现场永久环境变化(如金属板A拿走)自动恢复
|
||
* 触发链: dev>+4dlt 高位连续 60s → 学习基线 B (连续空闲窗均值+判稳) → 空闲原子替换 Origin
|
||
* 黄灯常亮指示学习过程; 与双向保护并存: 60s 内瞬态污染仍只冻结不污染 Origin */
|
||
uint8_t g_env_resync = 0; // 1=学习中 (黄灯常亮指示)
|
||
uint16_t g_env_wait_cnt = 0; // dev>+4dlt 高位连续累计 (≥ENV_RESYNC_WAIT 触发学习)
|
||
uint32_t g_env_sum = 0; // 学习窗口 CAPVD 累加
|
||
uint16_t g_env_cnt = 0; // 学习窗口样本数
|
||
uint32_t g_env_prev = 0; // 上一窗口均值 (B 候选)
|
||
uint8_t g_env_settle = 0; // 连续稳定窗口计数 (窗内均值漂移 ≤0.1% 则+1)
|
||
uint16_t g_env_tick = 0; // 学习预算 (超时放弃防死锁)
|
||
|
||
/* V4.25+: 环境异常预判锁存 — 一旦触发黄灯常亮, 回摆/目标靠近不灭;
|
||
* 仅当环境回正常(空闲, dev 未进入线以下)连续 ENV_RECOVER_WAIT 才解除 */
|
||
uint8_t g_env_warn = 0; // 1=环境异常预判锁存 (黄灯常亮)
|
||
uint16_t g_env_recover_cnt = 0; // 恢复确认计数 (空闲持续 5s → 解除预判)
|
||
|
||
#if USE_FLATNESS_EXIT
|
||
uint8_t g_exit_state;
|
||
uint16_t g_max_slope;
|
||
uint16_t g_max_slope_rate;
|
||
uint16_t g_delta2;
|
||
uint16_t g_delta3;
|
||
int32_t g_prev_capvd;
|
||
int32_t g_prev_first_deriv;
|
||
uint8_t g_slope_flat_cnt;
|
||
uint8_t g_flat_ok_cnt;
|
||
#endif
|
||
|
||
/*===========================================================================
|
||
* 全局状态变量 — 计数器
|
||
*===========================================================================*/
|
||
uint16_t Hold_CNT = 0;
|
||
uint8_t loop1_INCNT = 0;
|
||
uint8_t loop1_OUTCNT = 0;
|
||
uint8_t TM1cnt = 0;
|
||
|
||
/*===========================================================================
|
||
* 拨码开关状态
|
||
*===========================================================================*/
|
||
uint8_t SET_PLUS = 0;
|
||
uint8_t SET_DLY = 0; // 离开延时: 0=无, 1=500ms
|
||
uint8_t SET_SAFE = 0;
|
||
|
||
/*===========================================================================
|
||
* 拨码开关去抖变量
|
||
*===========================================================================*/
|
||
uint8_t sw0, swok, sw1, swcnt, SENS = 0, SENS_LAST = 0;
|
||
|
||
/*===========================================================================
|
||
* 安全复位相关
|
||
*===========================================================================*/
|
||
uint8_t loop1_LC_HOLD = 0;
|
||
uint8_t loop1_LC_Reset = 0;
|
||
uint32_t LC_Hold_CNT = 0;
|
||
uint16_t g_safe_max_cnt = LC_HOLD_TIME;
|
||
|
||
/*===========================================================================
|
||
* 故障指示相关 — 黄灯
|
||
*===========================================================================*/
|
||
uint8_t g_loop_power_up_state = 0;
|
||
uint8_t g_disconnect_count = 0;
|
||
uint8_t g_disconnect_active = 0;
|
||
uint8_t g_reconn_candidate = 0; // V4.36.2: 重连确认窗候选(载波上升沿进入)
|
||
uint16_t g_reconn_cnt = 0; // V4.36.2: 载波连续恢复计数 (确认窗 ~100ms)
|
||
uint16_t g_relay_hold_cnt = 0; // V4.36.3: 有车断开继电器保持计时(ms, ISR 5ms 累加)
|
||
uint8_t g_relay_hold_done = 0; // V4.36.3: 本次有车断开已兜底释放(防重复OFF)
|
||
uint16_t g_reconn_blank_ms = 0; // V4.36.5: 重连起振抑制倒计时(ms, 按Q分档, vd1_task 每样本-10)
|
||
uint8_t g_entry_need = 3; // V4.36.8: 进入确认样本数 (按Q档, 高3/中4/低5)
|
||
uint8_t g_prev_ok = 0; // V4.36.14: loop_task 主循环拍级上一拍 LOOP_OK (自身上升沿检测)
|
||
uint8_t g_reconn_stab = 0; // V4.36.15: 重连判稳门控(无车重连: 值稳定才放行)
|
||
uint16_t g_stab_ms = 0; // V4.36.15: 判稳已耗时(兜底 5s)
|
||
uint8_t g_stab_step_tick = 0; // V4.36.15: 200ms 步进计数
|
||
uint8_t g_stab_ok_cnt = 0; // V4.36.15: 连续无漂移步数
|
||
uint32_t g_stab_base = 0; // V4.36.15: 步进比较基准 CAPVD
|
||
uint8_t g_release_need = 3; // V4.36.8: 释放确认样本数 (按Q档, 高3/中4/低5)
|
||
uint8_t g_fault_phase = 0;
|
||
uint16_t g_fault_tick = 0;
|
||
uint8_t g_coil_fault = 0; // V4.26/25/37/38: 电感量异常 0=正常 1=出合理窗→一长一短(f<18k L偏大 / 125k<f≤156k L偏小) 2=156<f≤171k L20~24µH→一长两短(可用级, 恒判车) 3=f>171k L<20µH短路级→断开级快闪(宏=1 等同断开)
|
||
uint16_t g_coil_bad_cnt = 0; // V4.26: 电感异常防抖计数 (V4.36.21: 500tick=5s 置位/清除, 宏 COIL_FAULT_HOLD_TICKS)
|
||
|
||
/*===========================================================================
|
||
* 调试计数器
|
||
*===========================================================================*/
|
||
uint32_t g_xn_counter = 0;
|
||
|
||
/*===========================================================================
|
||
* 拨码开关轮询(5 次去抖,对齐 M1H)
|
||
*===========================================================================*/
|
||
void poll_sw_state(void)
|
||
{
|
||
sw0 = gpio_input_data_bit_read(SW5_BUTTON_PORT, SW5_BUTTON_PIN) << 4;
|
||
sw0 |= gpio_input_data_bit_read(SW4_BUTTON_PORT, SW4_BUTTON_PIN) << 3;
|
||
sw0 |= gpio_input_data_bit_read(SW3_BUTTON_PORT, SW3_BUTTON_PIN) << 2;
|
||
sw0 |= gpio_input_data_bit_read(SW2_BUTTON_PORT, SW2_BUTTON_PIN) << 1;
|
||
sw0 |= gpio_input_data_bit_read(SW1_BUTTON_PORT, SW1_BUTTON_PIN) << 0;
|
||
|
||
if (sw0 == sw1) {
|
||
if (swok != sw0) {
|
||
swcnt++;
|
||
if (swcnt > 5) { // 对齐 M1H: 5 次确认
|
||
swok = sw0;
|
||
|
||
SET_PLUS = swok & 0x04;
|
||
SET_DLY = (swok & 0x08) >> 3;
|
||
SET_SAFE = (swok & 0x10) >> 4;
|
||
}
|
||
}
|
||
} else {
|
||
sw1 = sw0;
|
||
swcnt = 0;
|
||
}
|
||
|
||
SENS = swok & 0x03;
|
||
if (SENS != SENS_LAST) {
|
||
PRINT("SENS changed: %02X -> %02X, resetting...\n", SENS_LAST, SENS);
|
||
nvic_system_reset();
|
||
}
|
||
}
|
||
|
||
/*===========================================================================
|
||
* 一阶 IIR 低通滤波器(对齐 M1H 公式)
|
||
* CAPVD_new = α × new_value + (1-α) × CAPVD_old
|
||
* α = Flt_Reg / 256 = 79/256 ≈ 0.3086
|
||
*===========================================================================*/
|
||
uint32_t get_flt_value(uint32_t new_value, uint32_t last_Value)
|
||
{
|
||
uint32_t value_Flt;
|
||
uint32_t delta = (new_value > last_Value)
|
||
? (new_value - last_Value)
|
||
: (last_Value - new_value);
|
||
uint32_t scaled_delta = (delta * (uint32_t)Flt_Reg) >> 8;
|
||
|
||
if (new_value > last_Value) {
|
||
value_Flt = last_Value + scaled_delta;
|
||
} else {
|
||
value_Flt = last_Value - scaled_delta;
|
||
}
|
||
|
||
return value_Flt;
|
||
}
|
||
|
||
/* 释放慢滤波(方案 A): 独立系数 RELEASE_ALFA, 不复用 Flt_Reg 全局
|
||
* 复刻 PD132T 滤波路径(Value -> IIR), tau~142ms @10ms */
|
||
uint32_t get_flt_value_slow(uint32_t new_value, uint32_t last_value)
|
||
{
|
||
uint32_t value_Flt;
|
||
uint32_t delta = (new_value > last_value)
|
||
? (new_value - last_value)
|
||
: (last_value - new_value);
|
||
uint32_t scaled_delta = (delta * (uint32_t)RELEASE_ALFA) >> 8;
|
||
|
||
if (new_value > last_value) {
|
||
value_Flt = last_value + scaled_delta;
|
||
} else {
|
||
value_Flt = last_value - scaled_delta;
|
||
}
|
||
|
||
return value_Flt;
|
||
}
|
||
|
||
/* V4.36.19: 低Q 释放平滑 (α=REL_ALFA/256≈0.18, τ≈50ms @10ms)
|
||
* 复刻 DEBUG 打印阻塞降采样的平均效应 — 低Q(高频)释放边界抖动大, 快值直接判
|
||
* 会被抖动打断计数(回滞仅 23 counts ≈ σ), 平滑后判定稳定 */
|
||
uint32_t get_flt_value_rel(uint32_t new_value, uint32_t last_value)
|
||
{
|
||
uint32_t value_Flt;
|
||
uint32_t delta = (new_value > last_value)
|
||
? (new_value - last_value)
|
||
: (last_value - new_value);
|
||
uint32_t scaled_delta = (delta * (uint32_t)REL_ALFA) >> 8;
|
||
|
||
if (new_value > last_value) {
|
||
value_Flt = last_value + scaled_delta;
|
||
} else {
|
||
value_Flt = last_value - scaled_delta;
|
||
}
|
||
|
||
return value_Flt;
|
||
}
|
||
|
||
/* V4.36.19: 释放命中判定 — 低Q(21~50µH)高灵敏释放带回仅 ~23 counts ≈ 抖动 σ,
|
||
* 快值直接判在边界试探时计数被抖断 → 释放点外移/卡死;
|
||
* 低Q 用 τ50ms 平滑值(复刻打印阻塞降采样平均效应) + 带回 ×1.5(留 σ 余量).
|
||
* 高Q/中Q 走原快值逻辑, 零影响 */
|
||
/* V4.36.31: B3 判定参考 — min(Origin, 真空 EMA)
|
||
* Origin 可能被 env 学习锚到高位(板搁置态/过冲, J1: 130636 vs 真空 130506) → 释放线偏高
|
||
* → 撤板真空也够不着; 取 min 使参考锚向实时真空(低者=保守方向), 双侧自动正确 */
|
||
static uint32_t vac_ref(void)
|
||
{
|
||
if (g_vacuum_ema == 0) return loop1_Origin;
|
||
/* V4.36.33: 仅明显锚错(Origin 高于 EMA > 1×dlt)才重锚 EMA — 边缘金属悬停拉低 EMA 的小偏差忽略 */
|
||
if ((int32_t)loop1_Origin - (int32_t)g_vacuum_ema >
|
||
(int32_t)(loop1_dlt_ORG * VAC_REF_DELTA_DLT))
|
||
return g_vacuum_ema;
|
||
return loop1_Origin;
|
||
}
|
||
|
||
static uint8_t rel_release_hit(void)
|
||
{
|
||
uint32_t _cmp = loop1_CAPVD;
|
||
uint32_t _line = loop1_Origin - loop1_dlt_ORG;
|
||
|
||
if (g_q_lo) {
|
||
_cmp = g_rel_smooth;
|
||
_line = vac_ref() - ((loop1_dlt_ORG * REL_BAND_LO_MULT) / 100); /* V4.36.31: 锚真空参考 */
|
||
}
|
||
return (_line < _cmp) ? 1 : 0;
|
||
}
|
||
|
||
/*===========================================================================
|
||
* 滑动平均基线更新
|
||
* 累加 new_value,当计数达到 window 时计算平均值并重置
|
||
* 返回 1 表示本轮更新了平均值
|
||
*===========================================================================*/
|
||
uint8_t update_moving_average(uint32_t* p_sum, uint16_t* p_cnt,
|
||
uint32_t* p_origin, uint32_t new_value,
|
||
uint16_t window)
|
||
{
|
||
if (!p_sum || !p_cnt || !p_origin || window == 0) {
|
||
return 0;
|
||
}
|
||
|
||
*p_sum += new_value;
|
||
(*p_cnt)++;
|
||
|
||
if (*p_cnt >= window) {
|
||
*p_origin = *p_sum / window;
|
||
*p_cnt = 0;
|
||
*p_sum = 0;
|
||
return 1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/*===========================================================================
|
||
* 红灯呼吸 (LEDA, PB1) — 每 5ms 由 TMR15 ISR 调用
|
||
*===========================================================================*/
|
||
void LEDA_ON_OFF(void)
|
||
{
|
||
LED_RED_GPIO->odt ^= LED_RED_PIN;
|
||
}
|
||
|
||
/*===========================================================================
|
||
* 绿灯指示 — LEDA (PA9)
|
||
*
|
||
* 由 TMR15 ISR 每 5ms 驱动一次,是绿灯的唯一控制入口。
|
||
*
|
||
* 模式:
|
||
* 自检/稳定中 (loop1_INI_LOOP || 线圈未连 || !g_loop_stable):
|
||
* → 慢闪 (200ms 亮 / 200ms 灭) — 就绪过程指示, 此阶段继电器未驱动
|
||
* 稳定后 (V4.36.4/10):
|
||
* → 绿灯 = 默认继电器 RLY2 (PA5, 恒随车辆存在) 实际输出状态:
|
||
* 吸合(PA5 高) → 绿灯亮; 释放(PA5 低) → 绿灯灭
|
||
* - 有车 → 亮; 无车 → 灭
|
||
* - 有车断开保持(RELAY_HOLD_MS)期间 → 保持亮 (与继电器一致)
|
||
* - 断开超时释放/无车 → 灭
|
||
* (V4.36.10 勘误: 原镜像 RLY1(PA6 脉冲/存在继电器) 指反 →
|
||
* 用户: 绿灯指示【默认继电器】, RLY1 为拨码3 可选脉冲继电器)
|
||
*===========================================================================*/
|
||
void poll_green_led(void)
|
||
{
|
||
#define GREEN_SLOW_HALF 40 // 200ms (40 × 5ms)
|
||
|
||
static uint16_t _slow_tick = 0;
|
||
|
||
/*--- 自检阶段:慢闪(就绪过程指示;此阶段继电器未驱动) ---*/
|
||
if (loop1_INI_LOOP || !g_loop_power_up_state || !g_loop_stable || g_ready_hold_tick > 0) {
|
||
/* V4.36.36: 就绪巩固期 (判稳后按 Q 档 5s/7s/10s) 绿灯继续慢闪, 递减放此 (ISR 5ms) */
|
||
if (g_ready_hold_tick > 0) g_ready_hold_tick--;
|
||
_slow_tick++;
|
||
if (_slow_tick >= GREEN_SLOW_HALF) {
|
||
_slow_tick = 0;
|
||
LED_GREEN_GPIO->odt ^= LED_GREEN_PIN; // toggle
|
||
}
|
||
return;
|
||
}
|
||
|
||
/*--- 稳定后: 绿灯 = 默认继电器 RLY2 (PA5) 实际输出状态 (V4.36.4/10) ---
|
||
* 读 RLY2_GPIO->odt (与 RLY2_ON/OFF 宏同源, 即真实驱动状态):
|
||
* 吸合(高) → 绿灯亮; 释放(低) → 绿灯灭
|
||
* RLY1(PA6) 为拨码3 可配置的脉冲/存在继电器 — 不指示
|
||
* 断开时无强制灭分支 → 与继电器保持逻辑一致 (防误判"继电器释放") */
|
||
_slow_tick = 0;
|
||
if (RLY2_GPIO->odt & RLY2_PIN)
|
||
LEDA_ON; // 默认继电器吸合 → 绿灯亮 (PA9 低电平点亮)
|
||
else
|
||
LEDA_OFF; // 默认继电器释放 → 绿灯灭
|
||
}
|
||
|
||
/*===========================================================================
|
||
* 黄灯故障指示 — LEDC (PA10)
|
||
*
|
||
* 由 TMR15 ISR 每 5ms 驱动一次。
|
||
*
|
||
* 模式判定 (V4.26 优先级: 1>4>5>3>2):
|
||
* 1 当前线圈断开 或 上电后从未接线圈:
|
||
* → 快闪 (200ms 亮 / 200ms 灭)
|
||
* 4 环境异常预判/重校准学习 (g_env_warn/g_env_resync):
|
||
* → 常亮 (V4.24/25, 故障补偿 10s 内完成)
|
||
* 5 线圈电感量不合理 (g_coil_fault, 频率 20~120kHz 窗之外):
|
||
* → 一长一短 (长亮900ms/灭300ms/短亮100ms/灭700ms 循环)
|
||
* 3 线圈已恢复连接, 且上电后有断开记录 (g_disconnect_count >= 1):
|
||
* → N 短闪 (80ms亮/200ms灭 × N, 1.2s间隔, N=断开次数≤3)
|
||
* 2 线圈正常连接, 无断开记录:
|
||
* → 灭
|
||
*===========================================================================*/
|
||
void poll_yellow_led(void)
|
||
{
|
||
#define FLT_ON_SHORT 16 // 80ms (16 × 5ms)
|
||
#define FLT_GAP_SHORT 40 // 200ms
|
||
#define FLT_GAP_LONG 240 // 1.2s (240 × 5ms)
|
||
#define FLT_FAST_HALF 40 // 200ms 快闪半周期
|
||
|
||
g_fault_tick++;
|
||
|
||
/*--- 模式1: 快闪 — 载波缺失/重连判稳接管/L过低断开级 (V4.36.16/17/20):
|
||
* !loop1_LOOP_OK: 上电无线圈 + 运行中断开 (5ms 实时, 不等 loop_task 150ms 慢拍
|
||
* 置 disconnect_active; 上电 phase power_up=0 时不置位, 由本条件兜底)
|
||
* g_reconn_stab: 运行中无车重连判稳门控
|
||
* g_coil_fault==3: 电感量 < COIL_L_SHORT_UH(20µH, f>171kHz, V4.36.37 短路级) → 按线圈断开状态指示
|
||
* 上电有线圈/判稳中: LOOP_OK=1 → 黄灯不闪, 自检指示归绿灯慢闪 ---*/
|
||
if (!loop1_LOOP_OK || g_reconn_stab || g_coil_fault == 3) {
|
||
if (g_fault_tick >= FLT_FAST_HALF) {
|
||
g_fault_tick = 0;
|
||
LED_YELLOW_GPIO->odt ^= LED_YELLOW_PIN; // toggle
|
||
}
|
||
return;
|
||
}
|
||
|
||
/*--- V4.25 模式4: 环境异常预判/重校准学习 → 黄灯常亮
|
||
* g_env_warn == 1: 环境重大变化预判锁存 (高位连续 ~5s 触发; 回摆/目标靠近保持,
|
||
* 环境回正常空闲连续 ~5s 才解除)
|
||
* g_env_resync == 1: 已进入重校准学习 (高位累计 60s 后自动开始, 学完原子替换 Origin)
|
||
* 优先级: 断开快闪 > 环境预判/学习常亮 > N短闪 > 灭 ---*/
|
||
if (g_env_resync || g_env_warn) {
|
||
LED_YELLOW_ON;
|
||
g_fault_tick = 0;
|
||
g_fault_phase = 0;
|
||
return;
|
||
}
|
||
|
||
/*--- V4.36.38 模式6: 电感量低于下限可用级(156~171k, L 20~24µH) → 一长两短 (用户定)
|
||
* 长亮900ms(180) → 灭300ms(60) → 短亮100ms(20) → 灭300ms(60) → 短亮100ms(20) → 灭700ms(140) 循环
|
||
* 与一长一短(fault1, 长-短) 同家族两短区分; 与断开/短路级快闪(200ms) 明显区分; 判定恒照常 ---*/
|
||
if (g_coil_fault == 2) {
|
||
static const uint16_t dur2[6] = { 180, 60, 20, 60, 20, 140 }; // 一长两短 6 相
|
||
if (g_fault_phase > 5) g_fault_phase = 0;
|
||
if (g_fault_phase & 1)
|
||
LED_YELLOW_OFF;
|
||
else
|
||
LED_YELLOW_ON;
|
||
if (g_fault_tick >= dur2[g_fault_phase]) {
|
||
g_fault_tick = 0;
|
||
g_fault_phase++;
|
||
}
|
||
return;
|
||
}
|
||
|
||
/*--- V4.26/25 模式5: 线圈电感量出合理窗(18~125kHz) → 一长一短
|
||
* 长亮900ms(180tick) → 灭300ms(60) → 短亮100ms(20) → 灭700ms(140), 循环
|
||
* g_coil_fault: 1=f<18k L偏大 或 125k<f≤156k L偏小(均一长一短);
|
||
* 2=156~171k 一长两短(V4.36.38); 3=L<20µH 短路级已在模式1 断开级快闪
|
||
* 优先级: 断开(1) > 环境(4) > 一长两短(6) > 一长一短(5) > N短闪(3) > 灭 ---*/
|
||
if (g_coil_fault == 1) {
|
||
static const uint16_t dur[4] = { 180, 60, 20, 140 }; // 900/300/100/700ms (V4.36.13: 灭 100→300ms)
|
||
if (g_fault_phase > 3) g_fault_phase = 0;
|
||
if (g_fault_phase & 1)
|
||
LED_YELLOW_OFF;
|
||
else
|
||
LED_YELLOW_ON;
|
||
if (g_fault_tick >= dur[g_fault_phase]) {
|
||
g_fault_tick = 0;
|
||
g_fault_phase++;
|
||
}
|
||
return;
|
||
}
|
||
|
||
uint8_t N = g_disconnect_count;
|
||
|
||
if (N == 0) {
|
||
/*--- 模式2: 无断开记录 → 灭 ---*/
|
||
LED_YELLOW_OFF;
|
||
g_fault_tick = 0;
|
||
g_fault_phase = 0;
|
||
return;
|
||
}
|
||
|
||
/*--- 模式3: N 短闪 (连接中, 但有断开历史) ---*/
|
||
if (N > 3) N = 3;
|
||
|
||
uint8_t phases_total = N * 2; // N 个 ON_short + N 个 GAP_short
|
||
uint16_t duration;
|
||
|
||
if (g_fault_phase < phases_total) {
|
||
duration = (g_fault_phase & 1) ? FLT_GAP_SHORT : FLT_ON_SHORT;
|
||
if (g_fault_phase & 1)
|
||
LED_YELLOW_OFF;
|
||
else
|
||
LED_YELLOW_ON;
|
||
} else {
|
||
/* LONG 间隔 */
|
||
duration = FLT_GAP_LONG;
|
||
LED_YELLOW_OFF;
|
||
}
|
||
|
||
if (g_fault_tick >= duration) {
|
||
g_fault_tick = 0;
|
||
g_fault_phase++;
|
||
if (g_fault_phase > phases_total)
|
||
g_fault_phase = 0;
|
||
}
|
||
}
|
||
|
||
/*===========================================================================
|
||
* 看门狗 (IWDT) - V2.10 新增
|
||
* 双保险: TMR15 ISR 5ms 递增 g_wdg_counter, 主循环 poll_wdg() 每 500ms 喂狗
|
||
* ISR 死 -> counter 不涨 -> 永不喂狗; 主循环死 -> poll_wdg 不执行 -> 超时复位
|
||
*===========================================================================*/
|
||
void wdt_init(void)
|
||
{
|
||
wdt_register_write_enable(TRUE);
|
||
wdt_divider_set((wdt_division_type)WDT_DIV); // WDT_DIV=2 -> WDT_CLK_DIV_16
|
||
wdt_reload_value_set(WDT_RLD);
|
||
wdt_enable();
|
||
wdt_counter_reload(); // 使能后立即喂一次, 给 FreeRTOS 启动留足时间
|
||
g_wdg_counter = 0;
|
||
}
|
||
|
||
void poll_wdg(void)
|
||
{
|
||
if (g_wdg_counter >= WDT_COUNTER_LIMIT) { // 100 x 5ms = 500ms
|
||
g_wdg_counter = 0;
|
||
wdt_counter_reload();
|
||
}
|
||
}
|
||
|
||
/*===========================================================================
|
||
* 初始化所有状态变量(对齐 M1H INIT_VD)
|
||
*===========================================================================*/
|
||
void INIT_VD(void)
|
||
{
|
||
loop1_INCNT = 0;
|
||
loop1_OUTCNT = 0;
|
||
loop1_CapCnt = 0;
|
||
loop1_CapLast = 0;
|
||
loop1_LPCNT = 0;
|
||
loop1_INI_LOOP = 1;
|
||
|
||
loop1_VD_FLAG = 0;
|
||
loop1_RF_FLAG = 0;
|
||
loop1_LOOP_OK = 1;
|
||
loop1_LOOP_OK0 = 0;
|
||
g_reconn_stab = 0; /* V4.36.15: 判稳门控清零 */
|
||
g_stab_ms = 0;
|
||
g_stab_step_tick = 0;
|
||
g_stab_ok_cnt = 0;
|
||
g_stab_base = 0;
|
||
g_rel_smooth = 0; /* V4.36.19: 低Q 释放平滑清零 */
|
||
g_q_lo = 0; /* V4.36.19: 低Q 档标志清零 */
|
||
g_origin_freeze_tick = 0; /* V4.36.26: 释放冻结计数清零 */
|
||
g_rel_band_cnt = 0; /* V4.36.30: 环带兜底计数清零 */
|
||
g_vacuum_ema = 0; /* V4.36.31: 真空 EMA 清零 (判稳后重建) */
|
||
g_ready_hold_tick = 0; /* V4.36.36: 就绪巩固期清零 (重建判稳时会再置) */
|
||
g_origin_lowlearn_tick = 0; /* V4.36.32: 降权期计数清零 */
|
||
loop1_CAP_OK = 0;
|
||
loop1_CAPVD = 0;
|
||
loop1_CAPVD_slow = 0; // 方案A: 释放慢滤波复位
|
||
|
||
loop1_SensLevel = 2; // 默认中灵敏度
|
||
Flt_Reg = ALFA_CAP1; // 79
|
||
|
||
loop1_ORG_CNT = 0;
|
||
loop1_ORG_SUM = 0;
|
||
|
||
loop1_FLAG_IN = 0;
|
||
loop1_FLAG_OUT = 0;
|
||
loop1_FLAG_PLUSE = 0;
|
||
loop1_cnt_release = 0;
|
||
g_loop_stable = 0;
|
||
g_stable_cnt = 0; // V4.23: 自检计数从零开始
|
||
g_settle_cnt = 0;
|
||
|
||
/* V4.24: 环境重校准复位 */
|
||
g_env_resync = 0;
|
||
g_env_wait_cnt = 0;
|
||
g_env_sum = 0;
|
||
g_env_cnt = 0;
|
||
g_env_prev = 0;
|
||
g_env_settle = 0;
|
||
g_env_tick = 0;
|
||
g_env_warn = 0; // V4.25+: 环境预判锁存复位
|
||
g_env_recover_cnt = 0;
|
||
|
||
/* M4 优化: 快速 IIR + 进入确认 */
|
||
loop1_entry_cnt = 0;
|
||
loop1_freeze_cnt = 0;
|
||
loop1_freeze_ref = 0;
|
||
#if USE_FLATNESS_EXIT
|
||
g_exit_state = 0;
|
||
g_max_slope = 0;
|
||
g_max_slope_rate = 0;
|
||
g_delta2 = 0;
|
||
g_delta3 = 0;
|
||
g_prev_capvd = 0;
|
||
g_prev_first_deriv = 0;
|
||
g_slope_flat_cnt = 0;
|
||
g_flat_ok_cnt = 0;
|
||
#endif
|
||
|
||
loop1_LC_HOLD = 0;
|
||
loop1_LC_Reset = 0;
|
||
LC_Hold_CNT = 0;
|
||
Hold_CNT = 0;
|
||
|
||
g_loop_power_up_state = 0;
|
||
g_disconnect_count = 0;
|
||
g_disconnect_active = 0;
|
||
g_fault_phase = 0;
|
||
g_fault_tick = 0;
|
||
|
||
/* 读取拨码初始状态 */
|
||
sw0 = gpio_input_data_bit_read(SW5_BUTTON_PORT, SW5_BUTTON_PIN) << 4;
|
||
sw0 |= gpio_input_data_bit_read(SW4_BUTTON_PORT, SW4_BUTTON_PIN) << 3;
|
||
sw0 |= gpio_input_data_bit_read(SW3_BUTTON_PORT, SW3_BUTTON_PIN) << 2;
|
||
sw0 |= gpio_input_data_bit_read(SW2_BUTTON_PORT, SW2_BUTTON_PIN) << 1;
|
||
sw0 |= gpio_input_data_bit_read(SW1_BUTTON_PORT, SW1_BUTTON_PIN) << 0;
|
||
|
||
swok = sw0;
|
||
sw1 = sw0;
|
||
SENS = swok & 0x03;
|
||
SENS_LAST = SENS;
|
||
swcnt = 0;
|
||
|
||
switch (SENS) {
|
||
case 0x00: loop1_SensLevel = 0; break;
|
||
case 0x02: loop1_SensLevel = 1; break;
|
||
case 0x01: loop1_SensLevel = 2; break;
|
||
case 0x03: loop1_SensLevel = 3; break;
|
||
default: loop1_SensLevel = 0; break;
|
||
}
|
||
|
||
SET_PLUS = swok & 0x04;
|
||
SET_DLY = (swok & 0x08) >> 3;
|
||
SET_SAFE = (swok & 0x10) >> 4;
|
||
}
|
||
|
||
/*===========================================================================
|
||
* TMR3 输入捕获中断 — 线圈频率测量(对齐 M1H CAP0 ISR)
|
||
*
|
||
* PA7 → TIM3_CH2, 上升沿触发。
|
||
* 每次捕获中断读取 CCR2,计算相邻边沿差 Xn。
|
||
* 累加 LPCNT 次后产生一个测量窗口的 Value。
|
||
*===========================================================================*/
|
||
void TMR3_GLOBAL_IRQHandler(void)
|
||
{
|
||
if (tmr_interrupt_flag_get(TMR3, TMR_C2_FLAG) != RESET) {
|
||
tmr_flag_clear(TMR3, TMR_C2_FLAG);
|
||
loop1_CapThis = tmr_channel_value_get(TMR3, TMR_SELECT_CHANNEL_2);
|
||
|
||
if (!loop1_RF_FLAG) {
|
||
/* 首次捕获,记录基准 */
|
||
loop1_CapLast = loop1_CapThis;
|
||
loop1_RF_FLAG = 1;
|
||
} else {
|
||
/* 计算相邻边沿周期差(16bit 溢出回绕处理) */
|
||
if (loop1_CapThis > loop1_CapLast) {
|
||
loop1_Xn = loop1_CapThis - loop1_CapLast;
|
||
} else {
|
||
loop1_Xn = (0x10000 - loop1_CapLast) + loop1_CapThis;
|
||
}
|
||
g_xn_counter++;
|
||
|
||
loop1_CapLast = loop1_CapThis;
|
||
loop1_CapCnt++;
|
||
|
||
if (loop1_INI_LOOP) {
|
||
/*--- 初始化阶段:计算自适应测量窗口大小 ---*/
|
||
if (loop1_LPCNT == 0) {
|
||
if (loop1_CapCnt > 10) {
|
||
/*
|
||
* 自适应测量窗口: MEASUREMENT_BASE / Xn
|
||
* 使归一化 Value ≈ 131072,无需后续 >>6
|
||
*/
|
||
loop1_LPCNT = MEASUREMENT_BASE / loop1_Xn;
|
||
PRINT("First_calc_loop1_LPCNT:%d, loop1_Xn:%d\n",
|
||
loop1_LPCNT, loop1_Xn);
|
||
if (loop1_LPCNT == 0) {
|
||
loop1_LPCNT = 100;
|
||
}
|
||
loop1_CAPVD = 0;
|
||
loop1_CapSum = 0;
|
||
loop1_CapCnt = 0;
|
||
}
|
||
} else {
|
||
/* 第一个窗口:直接作为基线 Origin */
|
||
loop1_CapSum += loop1_Xn;
|
||
if (loop1_CapCnt >= loop1_LPCNT) {
|
||
loop1_CAPVD = loop1_CapSum;
|
||
loop1_Origin = loop1_CAPVD; // 不再 >>6
|
||
PRINT("First_capSum:%d, Origin:%d\n",
|
||
loop1_CapSum, loop1_Origin);
|
||
loop1_INI_LOOP = 0;
|
||
loop1_CapSum = 0;
|
||
loop1_Value = 0;
|
||
loop1_CapCnt = 0;
|
||
}
|
||
}
|
||
} else {
|
||
/*--- 正常运行:累加到测量窗口 ---*/
|
||
loop1_CapSum += loop1_Xn;
|
||
if (loop1_CapCnt >= loop1_LPCNT) {
|
||
loop1_Value = loop1_CapSum;
|
||
loop1_CAP_OK = 1;
|
||
loop1_CapSum = 0;
|
||
loop1_CapCnt = 0;
|
||
loop1_INI_LOOP = 0;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/*===========================================================================
|
||
* TMR15 定时器中断 — 5ms tick,主状态机(对齐 M1H Timer1 ISR)
|
||
*
|
||
* 职责:
|
||
* 1. 50ms tick 计数器(TM1cnt ÷ 10)
|
||
* 2. 进入/离开/脉冲时序状态机
|
||
* 3. 有限存在超时 / 安全复位超时
|
||
* 4. 线圈载波检测 (RF_FLAG) 及继电器输出刷新
|
||
* 5. 呼吸灯、故障灯驱动
|
||
*===========================================================================*/
|
||
void TMR15_GLOBAL_IRQHandler(void)
|
||
{
|
||
static uint16_t _counter1_init = 0;
|
||
|
||
if (tmr_interrupt_flag_get(TMR15, TMR_OVF_FLAG) != RESET) {
|
||
|
||
/*--- 看门狗活性计数 (5ms) ---*/
|
||
g_wdg_counter++;
|
||
|
||
/*--- 初始化阶段红灯快闪 (200ms 周期) ---*/
|
||
if (!g_loop_power_up_state) {
|
||
_counter1_init++;
|
||
if (_counter1_init >= 40) { // 40 × 5ms = 200ms
|
||
_counter1_init = 0;
|
||
LEDA_ON_OFF();
|
||
}
|
||
}
|
||
|
||
/*--- 50ms tick 分频 ---*/
|
||
TM1cnt++;
|
||
if (TM1cnt >= 10) { // 10 × 5ms = 50ms
|
||
TM1cnt = 0;
|
||
|
||
/*================================================================
|
||
* 时序状态机(对齐 M1H Timer1 ISR)
|
||
*
|
||
* IN_DELAY OUT_DELAY
|
||
* [空闲] ──有车──→ [进入延时] ──→ [有车确认] ──离开──→
|
||
* ↑ │
|
||
* └──脉冲结束──── [脉冲输出] ←──── [离开延时] ←──────┘
|
||
* PULSE_DELAY
|
||
*================================================================*/
|
||
|
||
/* FLAG_IN: 进入延时 500ms */
|
||
if (loop1_FLAG_IN) {
|
||
loop1_INCNT++;
|
||
if (loop1_INCNT > IN_DELAY) {
|
||
loop1_FLAG_IN = 0;
|
||
loop1_INCNT = 0;
|
||
}
|
||
}
|
||
|
||
/* FLAG_OUT: 离开延时(SW_4=ON 时 500ms,OFF 时 0) */
|
||
if (loop1_FLAG_OUT) {
|
||
if (!SET_DLY) {
|
||
loop1_OUTCNT++;
|
||
if (loop1_OUTCNT > OUT_DELAY) {
|
||
loop1_FLAG_OUT = 0;
|
||
loop1_FLAG_PLUSE = 1;
|
||
loop1_OUTCNT = 0;
|
||
}
|
||
} else {
|
||
/* 无离开延时:立即触发脉冲 */
|
||
loop1_FLAG_OUT = 0;
|
||
loop1_FLAG_PLUSE = 1;
|
||
loop1_OUTCNT = 0;
|
||
}
|
||
}
|
||
|
||
/* FLAG_PLUSE: 脉冲宽度 500ms */
|
||
if (loop1_FLAG_PLUSE) {
|
||
loop1_OUTCNT++;
|
||
if (loop1_OUTCNT > PULSE_DELAY) {
|
||
loop1_FLAG_PLUSE = 0;
|
||
loop1_OUTCNT = 0;
|
||
}
|
||
}
|
||
|
||
/* 有限存在超时 */
|
||
if (loop1_VD_HOLD) {
|
||
Hold_CNT++;
|
||
if (Hold_CNT > HOLD_TIME) {
|
||
loop1_VD_HOLD = 0;
|
||
Hold_CNT = 0;
|
||
if (loop1_VD_FLAG) {
|
||
/* 超时:强制释放 */
|
||
RLY1_OFF;
|
||
RLY2_OFF;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 安全复位超时 */
|
||
if (loop1_LC_HOLD) {
|
||
LC_Hold_CNT++;
|
||
if (LC_Hold_CNT > g_safe_max_cnt) {
|
||
loop1_LC_Reset = 1;
|
||
loop1_INI_LOOP = 1;
|
||
loop1_LOOP_OK0 = 0;
|
||
g_loop_stable = 0;
|
||
#if USE_FLATNESS_EXIT
|
||
g_exit_state = 0;
|
||
g_max_slope = 0;
|
||
g_max_slope_rate = 0;
|
||
#endif
|
||
LC_Hold_CNT = 0;
|
||
loop1_ORG_CNT = 0;
|
||
loop1_ORG_SUM = 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
/*================================================================
|
||
* 线圈载波检测 & 继电器输出刷新(每 5ms)
|
||
* RF_FLAG 由 TMR3 ISR 置位,此处消费后清除。
|
||
*================================================================*/
|
||
if (loop1_RF_FLAG) {
|
||
loop1_LOOP_OK = 1;
|
||
loop1_RF_FLAG = 0;
|
||
|
||
/* V4.36.24: L<下限视为断开完全态(宏=1) — 载波强制为断:
|
||
* 主循环走断开路径(vd1_task 停/黄灯快闪), RLY 刷新短路保持释放, 绿灯镜像灭 */
|
||
if (COIL_UNDER_MIN_AS_OPEN && g_coil_fault == 3)
|
||
loop1_LOOP_OK = 0;
|
||
|
||
if (loop1_LC_Reset == 0 && !(COIL_UNDER_MIN_AS_OPEN && g_coil_fault == 3)) {
|
||
/* 继电器 2(辅助/方向) */
|
||
if (loop1_VD_FLAG)
|
||
RLY2_ON;
|
||
else
|
||
RLY2_OFF;
|
||
|
||
/* 继电器 1(主输出) */
|
||
if (SET_PLUS) {
|
||
/* 存在输出模式:有车或离开延时中 */
|
||
if (loop1_VD_FLAG || loop1_FLAG_OUT)
|
||
RLY1_ON;
|
||
else
|
||
RLY1_OFF;
|
||
} else {
|
||
/* 脉冲输出模式:脉冲期间吸合 */
|
||
if (loop1_FLAG_PLUSE)
|
||
RLY1_ON;
|
||
else
|
||
RLY1_OFF;
|
||
}
|
||
}
|
||
} else {
|
||
loop1_LOOP_OK0 = loop1_LOOP_OK;
|
||
loop1_LOOP_OK = 0;
|
||
}
|
||
|
||
/*--- V4.36.3: 有车断开 → 继电器保持 (5ms tick, 500ms 兜底释放) ---
|
||
* 断开前感应有车(VD_FLAG)且已上电 → RLY 不断开, 保持输出:
|
||
* - 500ms 内重连(经 100ms 确认窗) + vd1_task 判有车 → 输出无缝续持
|
||
* - 500ms 内重连 + 判无车 → vd1_task 正常离开释放
|
||
* - 超时仍断开 → 强制释放(防真故障无限保持)
|
||
* 无车断开(VD_FLAG=0)不受影响, 仍立即释放 */
|
||
if (!loop1_LOOP_OK && loop1_VD_FLAG && g_loop_power_up_state && !g_relay_hold_done
|
||
&& !(COIL_UNDER_MIN_AS_OPEN && g_coil_fault == 3)) { /* V4.36.24: 强制期不保持(保持释放) */
|
||
RLY1_ON; /* 保持: 防止瞬时断开继电器抖落 */
|
||
RLY2_ON;
|
||
g_relay_hold_cnt += 5;
|
||
if (g_relay_hold_cnt >= RELAY_HOLD_MS) {
|
||
RLY1_OFF; /* 兜底: 超时仍断开 → 释放 */
|
||
RLY2_OFF;
|
||
g_relay_hold_done = 1;
|
||
}
|
||
} else {
|
||
g_relay_hold_cnt = 0; /* 载波正常或非有车态 → 不保持 */
|
||
}
|
||
|
||
/*--- 硬件驱动:呼吸灯 + 指示灯 ---*/
|
||
poll_red_pwm();
|
||
poll_green_led();
|
||
poll_yellow_led();
|
||
|
||
tmr_flag_clear(TMR15, TMR_OVF_FLAG);
|
||
}
|
||
}
|
||
|
||
/*===========================================================================
|
||
* 调试打印辅助 — V4.20 debug 打印重组 (2026-09-01)
|
||
*
|
||
* calc_freq_khz: 由窗口量反算线圈振荡频率
|
||
* 捕获链路: 线圈振荡信号 → PA7(TMR3_CH2) 输入捕获, 捕获分频 ÷2 (DIV_2),
|
||
* TMR3 计数时钟 = sclk = 120MHz (HEXT×10, AHB/APB1 ÷1, at32f421_clock.c)
|
||
* Xn = 捕获间隔计数 = 2 × 120MHz / f_real → f_kHz = 240000 / Xn
|
||
* CAPVD ≈ LPCNT × Xn → 240000 × LPCNT / CAPVD
|
||
* → f_kHz = 240000 × LPCNT / CAPVD
|
||
* ⚠ V4.27 修正: 原 60000 按 60MHz 假设 + 未计 DIV_2, 少算 4 倍
|
||
* (示波器 30.23kHz 打印 7kHz 实测偏差; 旧日志 16.3kHz 实为 ~65.2kHz)
|
||
* L 粗估: f=120kHz 边界 @66nF → L≈27µH (仅供参考, 窗口判定以拍板频率窗为准)
|
||
*===========================================================================*/
|
||
uint32_t g_slope_limit_cnt = 0; // 斜率限幅截断计数 (EMI/干扰线索, 周期打印带出)
|
||
|
||
static uint32_t calc_freq_khz(uint32_t capvd, uint16_t lp_cnt)
|
||
{
|
||
if (capvd == 0) return 0;
|
||
return (240000UL * lp_cnt) / capvd;
|
||
}
|
||
|
||
/*===========================================================================
|
||
* V4.24: 环境重校准 — 学习被打断/放弃时复位 (黄灯恢复) / 完成时原子替换 Origin
|
||
*
|
||
* 替换安全设计:
|
||
* - 仅空闲(无车, dev 高位)且学习 B 连续 2 窗稳定后才调用
|
||
* - B ≈ 当前空闲 CAPVD → 替换瞬间 dev≈0, 无跳变不会触发继电器
|
||
* - 同步清 freeze/ORG/entry 残留, slow 滤波 snap (同 V4.04 进入时)
|
||
*===========================================================================*/
|
||
static void env_resync_abort(void)
|
||
{
|
||
g_env_resync = 0;
|
||
g_env_sum = 0;
|
||
g_env_cnt = 0;
|
||
g_env_prev = 0;
|
||
g_env_settle = 0;
|
||
g_env_tick = 0;
|
||
}
|
||
|
||
static void env_resync_apply(uint32_t new_origin)
|
||
{
|
||
PRINT("EVT|env_resync Origin:%d->%d CAPVD:%d\n",
|
||
loop1_Origin, new_origin, loop1_CAPVD);
|
||
loop1_Origin = new_origin;
|
||
loop1_freeze_cnt = 0;
|
||
loop1_freeze_ref = 0;
|
||
loop1_ORG_CNT = 0;
|
||
loop1_ORG_SUM = 0;
|
||
loop1_entry_cnt = 0;
|
||
#if USE_SLOW_RELEASE
|
||
loop1_CAPVD_slow = loop1_CAPVD; // 防替换后瞬态假释放 (同 V4.04 snap)
|
||
#endif
|
||
env_resync_abort();
|
||
}
|
||
/*===========================================================================
|
||
* V4.26~28: 线圈电感量粗估
|
||
* L_uH = 2.533e7 / (f_kHz² × C_nF) [f=1/(2π√(LC)), f_kHz 为修正后真实频率]
|
||
* C 用 COIL_CAL_C_NF (标定等效谐振电容, V4.34):
|
||
* 三极管库存板低频档(名义66nF, R19在/R27空): 实测 C_eff≈43nF (41.3~45.5, 2台×5颗) → 宏43
|
||
* (0Ω 板 R19拆/R27焊0Ω 实验值 68.4 仅作机理验证, 产品沿用三极管板 → 标43)
|
||
* (V4.28 的 43 是 5.1K 旧板带病特性 — 三极管开关未饱和, 第二颗33nF未全量并入,
|
||
* 换 0Ω 后并联全导通, C_eff 回到≈名义66; 43 作废)
|
||
* 高频档(名义33nF): 旧板实测 C_eff≈34.4 — 0Ω 板待复测, 拨33nF档请把宏改34
|
||
* 标定法: LCR 测 L → C=2.533e7/(f²×L), 多颗平均
|
||
* 例(0Ω板): f=52kHz→L≈138µH(实136); f=88kHz→L≈48µH(实48); f=127kHz→L≈23µH(实23)
|
||
*===========================================================================*/
|
||
static uint32_t coil_l_uH(uint32_t f_khz)
|
||
{
|
||
if (f_khz == 0) return 0;
|
||
return 25330000UL / (f_khz * f_khz * COIL_CAL_C_NF);
|
||
}
|
||
|
||
/*===========================================================================
|
||
* V4.26: 线圈电感量合理范围监视
|
||
* 拍板规格: 默认低频档口径, 空场频率合理窗 20~120kHz, 出界即判定电感量不合理
|
||
* (黄灯"一长一短"告警, 见 poll_yellow_led 模式5)
|
||
* 调用点保证: 仅稳定 + 无车空闲 + 未断开时执行 (车压/自检/断开不判)
|
||
* g_coil_fault: 0=正常 1=出窗一长一短 2=156~171k一长两短(可用级) 3=f>171k短路级(断开级快闪)
|
||
* 防抖: 越界/恢复均连续 COIL_FAULT_HOLD_TICKS(500tick=5s, V4.36.21) 才置位/清除 — 下限判定重要, 加长防误检
|
||
*===========================================================================*/
|
||
static void coil_monitor(void)
|
||
{
|
||
uint32_t f_khz = calc_freq_khz(loop1_Origin, loop1_LPCNT);
|
||
uint8_t want = 0;
|
||
|
||
if (loop1_Origin == 0 || f_khz == 0)
|
||
return; /* 无有效基准 → 维持原状 */
|
||
|
||
/* V4.36.25/37 四分语义 (43nF, 方案A 分级):
|
||
* f<18k → want=1 L偏大 → 一长一短
|
||
* 125k<f≤156k → want=1 L偏小(出窗) → 一长一短
|
||
* 156k<f≤171k → want=2 L 20~24µH 低于下限可用级 → 一长两短(V4.36.38, 与断开区分), 判定恒照常
|
||
* f>171k → want=3 L<20µH 短路级 → 断开级快闪(200ms), COIL_UNDER_MIN_AS_OPEN 决定功能 */
|
||
if (f_khz < COIL_F_OK_MIN_KHZ)
|
||
want = 1; /* L 偏大 */
|
||
else if (f_khz > COIL_L_SHORT_F_KHZ)
|
||
want = 3; /* L<20µH 短路级 → 断开级快闪 */
|
||
else if (f_khz > COIL_L_MIN_F_KHZ)
|
||
want = 2; /* 156~171k L20~24µH → 一长两短(可用级) */
|
||
else if (f_khz > COIL_F_OK_MAX_KHZ)
|
||
want = 1; /* L 偏小(出合理窗, 不低于下限) → 一长一短 */
|
||
|
||
if (want == g_coil_fault) {
|
||
g_coil_bad_cnt = 0; /* 状态一致 → 计时复位 */
|
||
} else {
|
||
g_coil_bad_cnt++;
|
||
if (g_coil_bad_cnt >= COIL_FAULT_HOLD_TICKS) {
|
||
if (g_coil_fault == 0)
|
||
PRINT("EVT|coil_fault f:%d kHz L~%lu uH Xn:%d %s\n",
|
||
f_khz, coil_l_uH(f_khz), loop1_Xn,
|
||
(want == 3) ? "below_l_short" :
|
||
(want == 2) ? "below_l_min_20_24u" :
|
||
(f_khz < COIL_F_OK_MIN_KHZ) ? "too_large" : "too_small");
|
||
else
|
||
PRINT("EVT|coil_ok f:%d kHz\n", f_khz);
|
||
g_coil_fault = want;
|
||
g_coil_bad_cnt = 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* V4.36.24: 强制断开期(L<下限视为断开, 宏=1)的恢复监测 — 无条件每 10ms 主循环调用
|
||
* coil_fault==2 时 ISR 压 LOOP_OK=0 → vd1_task 停摆, 原 coil_monitor(稳定无车空闲点)
|
||
* 不再执行 → 换好线圈后频率恢复无法清除故障 → 死锁.
|
||
* 此点只做恢复方向(f 回窗连续 5s 清 fault), 载波无效(LPCNT/Origin=0)不算恢复 */
|
||
static void coil_monitor_recover(void)
|
||
{
|
||
uint32_t f_khz;
|
||
|
||
if (!(COIL_UNDER_MIN_AS_OPEN && g_coil_fault == 3))
|
||
return; /* 非强制断开期 → 原 coil_monitor 负责 */
|
||
|
||
/* V4.36.40 热切换修复: 强制断开期 vd1_task 停摆 → Origin/LPCNT 冻结在旧短路线圈值,
|
||
* calc_freq_khz(Origin, LPCNT) 永远算不回窗 → 换正常线圈也无法恢复(死锁, 只能断电).
|
||
* 改由 TMR3 捕获 ISR 实时更新的 Xn 直接计算 f (240000/Xn, 每振荡周期新鲜) —
|
||
* 换线圈后下一周期即反映新频率. */
|
||
f_khz = 240000UL / loop1_Xn;
|
||
if (loop1_Xn == 0 || f_khz < COIL_F_OK_MIN_KHZ || f_khz > COIL_L_SHORT_F_KHZ) {
|
||
g_coil_bad_cnt = 0; /* 仍短路级/载波无效 → 继续等 */
|
||
return;
|
||
}
|
||
g_coil_bad_cnt++;
|
||
if (g_coil_bad_cnt >= COIL_FAULT_HOLD_TICKS) {
|
||
PRINT("EVT|coil_ok f:%d kHz (recover from L<20uH, hot-swap OK)\n", f_khz);
|
||
/* V4.36.37: 按当前 f 实时置档(非清 0) — 避免回 156~171k 可用级/125~156k 出窗区
|
||
* 后黄灯空档 5s (原清 0 等 coil_monitor 重判有小瑕疵) */
|
||
g_coil_fault = (f_khz < COIL_F_OK_MIN_KHZ) ? 1 :
|
||
(f_khz > COIL_L_SHORT_F_KHZ) ? 3 :
|
||
(f_khz > COIL_L_MIN_F_KHZ) ? 2 :
|
||
(f_khz > COIL_F_OK_MAX_KHZ) ? 1 : 0;
|
||
g_coil_bad_cnt = 0;
|
||
/* V4.36.40: 置档后 ISR 847 停止压 LOOP_OK → 主循环自然回 if(LOOP_OK) 路径;
|
||
* 强制期断开路径已置 disconnect_active=1 + g_prev_ok=0 → 顶部沿检测自动发起
|
||
* g_reconn_candidate=1 → reconnect confirmed(100ms) → 无车判稳门控 →
|
||
* CAPVD 重收敛 + Origin 判稳重建(换线圈后基准必须重学) → coil_monitor 按新线圈重判.
|
||
* 此后新线圈正常(fault=0)完全恢复; 仍短路则 coil_monitor 重判 fault3 稳定态不循环. */
|
||
}
|
||
}
|
||
|
||
/*===========================================================================
|
||
* vd1_task — 核心检测算法(对齐 M1H VD1_TASK)
|
||
*
|
||
* 每次 loop1_CAP_OK 时调用一次。
|
||
* 流程:
|
||
* 1. IIR 滤波:CAPVD = α·Value + (1-α)·CAPVD
|
||
* 2. 无车时:基线跟踪 + 进入检测
|
||
* 3. 有车时:离开检测(带滞回)
|
||
*===========================================================================*/
|
||
static void update_q_confirm_need(void); // V4.36.8 前置声明 (定义在 loop_task 上方)
|
||
|
||
void vd1_task(void)
|
||
{
|
||
#define STABLE_SAMPLES 128 // 稳定期最少样本数 (128×10ms≈1.3s)
|
||
#define STABLE_ORIGIN_PPT 1 // 基准稳定判据: 窗口均值漂移 ≤ Origin×1/1000 (0.1%)
|
||
#define STABLE_SETTLE_WINDOWS 2 // 连续 2 个窗口(每窗100样本≈1s)漂移达标 → 判稳
|
||
#define STABLE_MAX_SAMPLES 500 // 硬兜底: 5s 未判稳强制结束自检(防绿灯无限闪)
|
||
#define ENV_RESYNC_WAIT 700 // V4.31: dev>+4dlt 高位连续 7s → 判定永久环境变化 (拍板: 预判2s + 补偿7s + 恢复2s)
|
||
#define ENV_LEARN_WINDOW 200 // V4.24: 环境学习窗口 2s (200×10ms)
|
||
#define ENV_LEARN_MAX_TICKS 3000 // V4.24: 学习预算 30s, 超时放弃 (防黄灯常亮死锁)
|
||
|
||
|
||
if (loop1_Origin == 0) return;
|
||
|
||
/*================================================================
|
||
* 1. IIR 滤波 + 斜率限幅 (M4 优化)
|
||
*
|
||
* ALFA_CAP1=79 @10ms → τ≈32ms
|
||
* 斜率限幅: 单次变化 >5% → 截断 (拒绝 EMI/闪电尖峰)
|
||
*================================================================*/
|
||
if (loop1_CAPVD == 0) {
|
||
loop1_CAPVD = loop1_Value;
|
||
g_rel_smooth = 0; /* V4.36.19: 直锁时释放平滑同步归零 */
|
||
|
||
/* V4.36.5: 首样本直锁(重连重置后) → 按线圈 Q(空场频率)分档设定抑制窗
|
||
* Origin 为断开前空场基线(同线圈同环境) → f 即 Q 档;
|
||
* 上电(Origin=0)时 f=0 → 归高Q档 0ms, 不影响正常判稳流程 */
|
||
g_reconn_blank_ms = 0;
|
||
{
|
||
uint32_t _f = calc_freq_khz(loop1_Origin, loop1_LPCNT);
|
||
if (_f > RECONNECT_Q_MID_F_KHZ)
|
||
g_reconn_blank_ms = RECONNECT_BLANK_Q_LO_MS; /* 低Q(高频) 长抑制 */
|
||
else if (_f > RECONNECT_Q_HI_F_KHZ)
|
||
g_reconn_blank_ms = RECONNECT_BLANK_Q_MID_MS; /* 中Q 短抑制 */
|
||
else
|
||
g_reconn_blank_ms = RECONNECT_BLANK_Q_HI_MS; /* 高Q(低频) 短兜底窗 */
|
||
if (g_reconn_blank_ms)
|
||
PRINT("EVT|reconn_blank %dms f:%d kHz\n",
|
||
g_reconn_blank_ms, _f);
|
||
}
|
||
} else {
|
||
int32_t raw_delta = (int32_t)loop1_Value - (int32_t)loop1_CAPVD;
|
||
int32_t max_step = (int32_t)(loop1_CAPVD * MAX_SLOPE_RATE / 100);
|
||
if (max_step < 100) max_step = 100;
|
||
if (raw_delta > max_step) { raw_delta = max_step; g_slope_limit_cnt++; }
|
||
if (raw_delta < -max_step) { raw_delta = -max_step; g_slope_limit_cnt++; }
|
||
uint32_t clamped_value = (uint32_t)((int32_t)loop1_CAPVD + raw_delta);
|
||
loop1_CAPVD = get_flt_value(clamped_value, loop1_CAPVD);
|
||
}
|
||
|
||
#if USE_SLOW_RELEASE
|
||
/* 方案A: 释放慢滤波独立跟踪(吃原始 Value, 复刻 PD132T 滤波路径) */
|
||
if (loop1_CAPVD_slow == 0) {
|
||
loop1_CAPVD_slow = loop1_Value;
|
||
} else {
|
||
loop1_CAPVD_slow = get_flt_value_slow(loop1_Value, loop1_CAPVD_slow);
|
||
}
|
||
#endif
|
||
|
||
/* V4.36.19: 低Q 释放平滑 (τ≈50ms; 无条件跟随, 只用于低Q 释放判定) */
|
||
if (loop1_CAPVD != 0) {
|
||
if (g_rel_smooth == 0)
|
||
g_rel_smooth = loop1_CAPVD;
|
||
else
|
||
g_rel_smooth = get_flt_value_rel(loop1_CAPVD, g_rel_smooth);
|
||
}
|
||
|
||
/* V4.36.5: 重连起振抑制倒计时 (每样本 ~10ms) */
|
||
if (g_reconn_blank_ms) {
|
||
if (g_reconn_blank_ms > 10) g_reconn_blank_ms -= 10;
|
||
else g_reconn_blank_ms = 0;
|
||
}
|
||
|
||
/* V4.36.8: 进入/释放确认样本数按 Q 档更新 (每样本; Origin=0 时归高Q 3) */
|
||
update_q_confirm_need();
|
||
|
||
/*--- V4.36.15: 重连判稳门控 (无车重连: 值稳定且回带才放行) ---
|
||
* blank 固定窗只挡"最脏起振期"; 低Q 起振蠕变(21µH 实测 ~2.2s)由本门控自适应接管:
|
||
* 200ms 步进检查 CAPVD 漂移 ≤ dlt/2 → 连续 3 步(600ms)无漂移,
|
||
* 且 |CAPVD-Origin| ≤ dlt(回带内=空场确认) → 判稳完成 → Origin 对齐当前空场
|
||
* (消除"新空场≠旧 Origin"基线差, 根治窗末假车; 浅车/蠕变尾 dev 超带时不完成)
|
||
* 强车例外: CAPVD < Origin-8×dlt(真车深压) → 不等判稳立即放行
|
||
* 兜底 5s: 超时强制放行(避免永久无输出; dev 深则进入判定正常接管) */
|
||
if (g_reconn_stab) {
|
||
uint32_t _dlt = loop1_dlt_ORG;
|
||
if (_dlt < 8) _dlt = 8;
|
||
|
||
if (g_stab_base == 0)
|
||
g_stab_base = loop1_CAPVD;
|
||
|
||
if (loop1_CAPVD < (loop1_Origin - 8 * _dlt)) {
|
||
/* 强车例外: 真车深压不等判稳 */
|
||
g_reconn_stab = 0;
|
||
PRINT("EVT|stab bypass strong_car dev:%d\n",
|
||
(int32_t)loop1_CAPVD - (int32_t)loop1_Origin);
|
||
} else {
|
||
g_stab_ms += 10;
|
||
g_stab_step_tick++;
|
||
if (g_stab_step_tick >= STABILIZE_STEP_TICKS) {
|
||
g_stab_step_tick = 0;
|
||
int32_t _drift = (int32_t)loop1_CAPVD - (int32_t)g_stab_base;
|
||
if (_drift < 0) _drift = -_drift;
|
||
if (_drift <= (int32_t)(_dlt / 2))
|
||
g_stab_ok_cnt++;
|
||
else
|
||
g_stab_ok_cnt = 0;
|
||
g_stab_base = loop1_CAPVD;
|
||
|
||
if (g_stab_ok_cnt >= STABILIZE_STEPS_OK) {
|
||
int32_t _d = (int32_t)loop1_CAPVD - (int32_t)loop1_Origin;
|
||
if (_d < 0) _d = -_d;
|
||
if (_d <= (int32_t)_dlt) {
|
||
/* 回带内=空场确认 → Origin 对齐当前空场(消基线差) */
|
||
loop1_Origin = loop1_CAPVD;
|
||
loop1_CAPVD_slow = loop1_CAPVD;
|
||
loop1_dlt_ORG = ((uint32_t)loop1_Origin *
|
||
SensTable[loop1_SensLevel]) >> 16;
|
||
PRINT("EVT|stab done Origin->%d dev:%d\n",
|
||
loop1_Origin, _d);
|
||
} else {
|
||
/* dev 仍超带(浅车/蠕变尾): 继续抑制等回带 */
|
||
PRINT("EVT|stab wait dev:%d drift:%d\n", _d, _drift);
|
||
g_stab_ok_cnt = 0;
|
||
}
|
||
}
|
||
}
|
||
/* 兜底 5s: 强制放行 (dev 深则进入判定正常接管) */
|
||
if (g_stab_ms >= STABILIZE_MAX_MS) {
|
||
g_reconn_stab = 0;
|
||
PRINT("EVT|stab timeout %dms\n", g_stab_ms);
|
||
}
|
||
}
|
||
}
|
||
|
||
/*--- 2. 稳定期:绕过 IIR 和斜率限幅,小窗口快速收敛;
|
||
* 基准值稳定(连续窗口 Origin 漂移达标)后才结束自检 ---*/
|
||
if (!g_loop_stable) {
|
||
uint32_t _prev_origin = loop1_Origin;
|
||
uint8_t _win_done;
|
||
|
||
loop1_CAPVD = loop1_Value;
|
||
#if USE_SLOW_RELEASE
|
||
loop1_CAPVD_slow = loop1_Value; // 方案A: 稳定期同步直锁
|
||
#endif
|
||
|
||
_win_done = update_moving_average(&loop1_ORG_SUM, &loop1_ORG_CNT,
|
||
&loop1_Origin, loop1_CAPVD, 100);
|
||
g_stable_cnt++;
|
||
|
||
/* V4.23: 每窗(100样本≈1s)完成时比较 Origin 均值漂移;
|
||
* 漂移 ≤ Origin×STABLE_ORIGIN_PPT/1000 → settle 计数+1, 否则清零
|
||
* (首个窗口常与 TMR3 首窗 Origin 偏差大, 自然多等一窗再判稳) */
|
||
if (_win_done) {
|
||
uint32_t _drift = (loop1_Origin > _prev_origin)
|
||
? (loop1_Origin - _prev_origin)
|
||
: (_prev_origin - loop1_Origin);
|
||
uint32_t _band = (uint32_t)(loop1_Origin * STABLE_ORIGIN_PPT / 1000);
|
||
if (_drift <= _band) {
|
||
if (g_settle_cnt < 0xFFFF) g_settle_cnt++;
|
||
} else {
|
||
g_settle_cnt = 0;
|
||
}
|
||
}
|
||
|
||
/* 判稳条件: 最少样本 + 连续 2 窗基准稳定; 硬兜底 STABLE_MAX_SAMPLES */
|
||
if ((g_stable_cnt >= STABLE_SAMPLES &&
|
||
g_settle_cnt >= STABLE_SETTLE_WINDOWS) ||
|
||
g_stable_cnt >= STABLE_MAX_SAMPLES) {
|
||
g_loop_stable = 1;
|
||
g_stable_cnt = 0; // 计数退出时清零: 二次稳定期(安全复位)从零开始
|
||
g_settle_cnt = 0;
|
||
g_vacuum_ema = loop1_Origin; /* V4.36.31: 判稳完成建立真空基线 */
|
||
/* V4.36.36: 就绪巩固期按 Q 分档 (高Q 5s/中Q 7s/低Q 10s) — 绿灯继续慢闪期间系统收敛 */
|
||
{
|
||
uint32_t _f0 = calc_freq_khz(loop1_Origin, loop1_LPCNT);
|
||
if (_f0 > RECONNECT_Q_MID_F_KHZ) g_ready_hold_tick = READY_HOLD_LO_TICKS;
|
||
else if (_f0 > RECONNECT_Q_HI_F_KHZ) g_ready_hold_tick = READY_HOLD_MID_TICKS;
|
||
else g_ready_hold_tick = READY_HOLD_HI_TICKS;
|
||
}
|
||
PRINT("Loop stable, Origin:%d, ready:%us\n", loop1_Origin,
|
||
(unsigned)(g_ready_hold_tick / 200));
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (!loop1_VD_FLAG) {
|
||
/*================================================================
|
||
* 无车状态
|
||
*================================================================*/
|
||
|
||
/* V4.36.26: 释放后冻结计时 — 挡释放瞬间残余学入 Origin (板缓离仍在感应边缘) */
|
||
if (g_origin_freeze_tick > 0)
|
||
g_origin_freeze_tick--;
|
||
else if (g_origin_lowlearn_tick > 0) /* V4.36.32: 冻结结束 → 降权期开始递减 */
|
||
g_origin_lowlearn_tick--;
|
||
|
||
/*--- 基线跟踪(仿 TLD-110:有车时冻结)
|
||
* V4.20 修复: 双向对称保护 — 负偏差(负variation)也冻结基线
|
||
* 原单边保护只挡 dev>+4×dlt, 负variation铁块(磁导率主导, CAPVD>Origin)
|
||
* 会把 Origin 污染抬高 → 铁块离开时假进入 → 释放线抬高 → 锁死不释放
|
||
* 修复: ① 偏差窗口对称 [-4×dlt, +4×dlt]
|
||
* ② 负偏差(dev>0, CAPVD高于Origin) 只冻结, 永不超时更新 Origin
|
||
*---*/
|
||
loop1_dlt_ORG = ((uint32_t)loop1_Origin * SensTable[loop1_SensLevel]) >> 16;
|
||
{
|
||
int32_t dev = (int32_t)loop1_CAPVD - (int32_t)loop1_Origin;
|
||
int32_t freeze_band = (int32_t)(loop1_dlt_ORG * 4);
|
||
if (dev < freeze_band && dev > -freeze_band) {
|
||
/* 对称窗口内 → 正常基线跟踪 */
|
||
loop1_freeze_cnt = 0;
|
||
loop1_freeze_ref = 0; // V4.20: 清残留参考, 打印 FRZ 干净
|
||
g_env_wait_cnt = 0; // V4.24: 高位回摆/目标靠近 → 等待重来
|
||
env_resync_abort(); // V4.24: 学习中被打断 → 重算
|
||
/* V4.25: 恢复确认 — 环境回正常(dev 未进进入线以下)连续 5s → 解除预判锁存
|
||
* (预判一旦触发, 回摆/目标靠近都保持黄灯常亮; 只有稳定空闲才灭) */
|
||
if (dev > -(int32_t)loop1_dlt_ORG) {
|
||
if (g_env_warn) {
|
||
g_env_recover_cnt++;
|
||
if (g_env_recover_cnt >= ENV_RECOVER_WAIT) {
|
||
g_env_warn = 0;
|
||
g_env_recover_cnt = 0;
|
||
}
|
||
}
|
||
} else {
|
||
g_env_recover_cnt = 0; // 目标进入确认区 → 恢复计时重来
|
||
}
|
||
/* V4.36.26: 释放后冻结期内 Origin 均值窗不更新 (切断 cycle 间正反馈) */
|
||
if (g_origin_freeze_tick == 0) {
|
||
uint16_t _win = WINDOW_ORIGIN;
|
||
if (g_origin_lowlearn_tick > 0) /* V4.36.32: 降权期放大窗(500→2500=25s) */
|
||
_win = (uint16_t)((uint32_t)WINDOW_ORIGIN * ORIGIN_LOWLEARN_MULT);
|
||
update_moving_average(&loop1_ORG_SUM, &loop1_ORG_CNT,
|
||
&loop1_Origin, loop1_CAPVD, _win);
|
||
/* V4.36.31: 真空 EMA 同步更新 (同条件同守卫; α=128 τ≈1.28s) */
|
||
if (g_vacuum_ema == 0) {
|
||
g_vacuum_ema = loop1_CAPVD;
|
||
} else {
|
||
/* V4.36.34: EMA 钳位 — 每步最多 ±3×dlt (防 CAPVD 毛刺拖带);
|
||
* 与 Origin 偏差 >25% 视为内存污染/失控, 自愈回 Origin (20260908 35uH 日志 VEM 垃圾实锤) */
|
||
int32_t _ema_d = (int32_t)loop1_CAPVD - (int32_t)g_vacuum_ema;
|
||
int32_t _ema_lim = (int32_t)(loop1_dlt_ORG * 3);
|
||
if (_ema_d > _ema_lim) _ema_d = _ema_lim;
|
||
else if (_ema_d < -_ema_lim) _ema_d = -_ema_lim;
|
||
if (_ema_d != 0)
|
||
/* V4.36.35: cast 顺序修复 — (uint32_t)负值/128 = +3355万/样本(爆炸源!)
|
||
* 必须 有符号先除: -10/128=0, -200/128=-1(wrap 减) — 20260908 实锤根因 */
|
||
g_vacuum_ema += (uint32_t)(_ema_d / VACUUM_EMA_ALFA);
|
||
if (((int32_t)loop1_Origin - (int32_t)g_vacuum_ema) > (int32_t)(loop1_Origin >> 2) ||
|
||
((int32_t)g_vacuum_ema - (int32_t)loop1_Origin) > (int32_t)(loop1_Origin >> 2))
|
||
g_vacuum_ema = loop1_Origin;
|
||
}
|
||
}
|
||
} else if (dev < 0) {
|
||
g_env_wait_cnt = 0; // V4.24: 目标靠近(车方向) → 等待重来
|
||
env_resync_abort(); // V4.24: 学习中被打断 → 重算
|
||
g_env_recover_cnt = 0; // V4.25: 目标深压 → 恢复确认计时清零
|
||
/* 正variation (CAPVD低于Origin, 疑似车辆) → 冻结, 保留原超时兜底更新 */
|
||
if (loop1_freeze_cnt == 0) {
|
||
loop1_freeze_ref = loop1_CAPVD;
|
||
} else {
|
||
int32_t drift = (int32_t)loop1_CAPVD - (int32_t)loop1_freeze_ref;
|
||
if (drift < 0) drift = -drift;
|
||
if (drift > (int32_t)(loop1_freeze_ref * FREEZE_STABILITY_RATE / 100)) {
|
||
loop1_freeze_cnt = 0;
|
||
loop1_freeze_ref = loop1_CAPVD;
|
||
}
|
||
}
|
||
loop1_freeze_cnt++;
|
||
if (loop1_freeze_cnt >= FREEZE_TIMEOUT) {
|
||
loop1_Origin = loop1_CAPVD;
|
||
loop1_freeze_cnt = 0;
|
||
loop1_freeze_ref = 0;
|
||
loop1_ORG_CNT = 0;
|
||
loop1_ORG_SUM = 0;
|
||
PRINT("Baseline timeout update, new Origin:%d\\n", loop1_Origin);
|
||
} else {
|
||
loop1_ORG_CNT = 0;
|
||
loop1_ORG_SUM = 0;
|
||
}
|
||
} else {
|
||
/* 负variation (CAPVD高于Origin, 特殊铁块磁导率主导/异常高)
|
||
* V4.24 两段式:
|
||
* ① 前 7s(ENV_RESYNC_WAIT): 只冻结, 不更新 Origin
|
||
* (36321a1 双向保护原语义: 防铁块污染 Origin → 离开假进入锁死)
|
||
* ② 高位连续 ≥60s 未回摆 → 判定"永久环境变化"(如金属板A拿走、基准被学低)
|
||
* → 进入受控重校准: 连续空闲窗口均值 B + settle 判稳 → 空闲原子替换 Origin
|
||
* 权衡: 铁块静止 60s+ 也会被学入 → 靠等待时长 + 黄灯常亮提示(操作接口) */
|
||
if (loop1_freeze_cnt == 0) {
|
||
loop1_freeze_ref = loop1_CAPVD;
|
||
PRINT("EVT|neg_var_freeze CAPVD:%d Origin:%d dev:%d dlt:%d f:%dkHz\n",
|
||
loop1_CAPVD, loop1_Origin, (int)dev, loop1_dlt_ORG,
|
||
calc_freq_khz(loop1_CAPVD, loop1_LPCNT));
|
||
}
|
||
loop1_freeze_cnt++;
|
||
if (loop1_freeze_cnt > FREEZE_TIMEOUT) loop1_freeze_cnt = FREEZE_TIMEOUT; // 打印饱和, 不更新
|
||
loop1_ORG_CNT = 0;
|
||
loop1_ORG_SUM = 0;
|
||
|
||
if (!g_env_resync) {
|
||
/*--- ① 等待确认: dev>+4dlt 高位连续累计 (回摆清零见窗口/dev<0分支)
|
||
* V4.25: 累计满 5s(ENV_WARN_WAIT) → 预判锁存 g_env_warn=1 (黄灯常亮),
|
||
* 锁存后回摆/目标靠近不解除; 高位重现则恢复计时清零 ---*/
|
||
g_env_wait_cnt++;
|
||
if (g_env_wait_cnt >= ENV_WARN_WAIT)
|
||
g_env_warn = 1; // 预判锁存 (只增不清)
|
||
g_env_recover_cnt = 0; // 高位异常中 → 恢复确认失效
|
||
if (g_env_wait_cnt >= ENV_RESYNC_WAIT) {
|
||
g_env_resync = 1;
|
||
g_env_wait_cnt = 0;
|
||
g_env_sum = 0;
|
||
g_env_cnt = 0;
|
||
g_env_prev = 0;
|
||
g_env_settle = 0;
|
||
g_env_tick = 0;
|
||
PRINT("EVT|env_learn_start Origin:%d CAPVD:%d dev:%d\n",
|
||
loop1_Origin, loop1_CAPVD, (int)dev);
|
||
}
|
||
} else {
|
||
/*--- ② 学习基线 B: 连续空闲窗口均值 + settle 判稳 (同 V4.23 上电自检语义) ---*/
|
||
g_env_tick++;
|
||
g_env_sum += loop1_CAPVD;
|
||
g_env_cnt++;
|
||
if (g_env_cnt >= ENV_LEARN_WINDOW) {
|
||
uint32_t _m = g_env_sum / g_env_cnt;
|
||
uint32_t _drift, _band;
|
||
g_env_sum = 0;
|
||
g_env_cnt = 0;
|
||
if (g_env_prev != 0) {
|
||
_drift = (_m > g_env_prev) ? (_m - g_env_prev) : (g_env_prev - _m);
|
||
_band = _m * STABLE_ORIGIN_PPT / 1000;
|
||
if (_drift <= _band) {
|
||
g_env_settle++;
|
||
} else {
|
||
g_env_settle = 0; // 窗口不稳 → 重算
|
||
}
|
||
if (g_env_settle >= STABLE_SETTLE_WINDOWS) {
|
||
/* 连续 2 窗稳定 → B 可信; 空闲由上下文保证, 原子替换 */
|
||
env_resync_apply(_m);
|
||
return; // 本 tick 不再走进入判定
|
||
}
|
||
}
|
||
g_env_prev = _m;
|
||
}
|
||
if (g_env_tick >= ENV_LEARN_MAX_TICKS) {
|
||
/* 学习预算超时 → 放弃, 保留旧 Origin (宁可灵敏度低不冒险污染) */
|
||
PRINT("EVT|env_learn_abort timeout Origin:%d CAPVD:%d\n",
|
||
loop1_Origin, loop1_CAPVD);
|
||
env_resync_abort();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/*--- V4.26: 线圈电感量监视 (调用点=稳定无车空闲, 见 coil_monitor) ---*/
|
||
coil_monitor();
|
||
|
||
/*--- 进入确认 — 连续 ENTRY_CONFIRM 次低于阈值才判有车 ---*/
|
||
if (loop1_CAPVD < (vac_ref() - loop1_dlt_ORG) && g_reconn_blank_ms == 0 && !g_reconn_stab) { /* V4.36.31: 锚真空参考 */
|
||
if (loop1_entry_cnt == 0) {
|
||
/* 过渡事件: 第一次命中进入线 (定位"为什么开始进入") */
|
||
PRINT("EVT|enter_try Value:%d CAPVD:%d Origin:%d dlt:%d f:%dkHz LPCNT:%d Xn:%d\n",
|
||
loop1_Value, loop1_CAPVD, loop1_Origin, loop1_dlt_ORG,
|
||
calc_freq_khz(loop1_CAPVD, loop1_LPCNT), loop1_LPCNT, loop1_Xn);
|
||
}
|
||
loop1_entry_cnt++;
|
||
if (loop1_entry_cnt >= g_entry_need) { // V4.36.8: 按Q档 (高3/中4/低5)
|
||
PRINT("Car_In, Value:%d, CAPVD:%d, Origin:%d, dlt:%d\\n",
|
||
loop1_Value, loop1_CAPVD, loop1_Origin, loop1_dlt_ORG);
|
||
|
||
loop1_VD_FLAG = 1;
|
||
loop1_FLAG_IN = 1;
|
||
loop1_entry_cnt = 0;
|
||
loop1_freeze_cnt = 0;
|
||
loop1_freeze_ref = 0;
|
||
#if USE_SLOW_RELEASE
|
||
/* V4.04: 进入瞬态假释放修正 — snap 慢滤波到当前快滤波值
|
||
* 进入线(档3: 0.032%)恒深于离开线(0.021%), snap 后释放条件结构性为假,
|
||
* 消除"定在HR0/2保持即释放"窗口(2026-09-01 仿真+现场复核) */
|
||
loop1_CAPVD_slow = loop1_CAPVD;
|
||
#endif
|
||
g_rel_smooth = loop1_CAPVD; /* V4.36.19: 低Q 释放平滑 snap 到进入点 */
|
||
g_rel_band_cnt = 0; /* V4.36.30: 环带兜底计数进入时清零 */
|
||
|
||
if (!SET_SAFE) {
|
||
loop1_LC_HOLD = 1;
|
||
} else {
|
||
loop1_LC_HOLD = 0;
|
||
}
|
||
if (loop1_LC_Reset)
|
||
loop1_LC_Reset = 0;
|
||
|
||
loop1_ORG_CNT = 0;
|
||
loop1_ORG_SUM = 0;
|
||
|
||
/* 重置平坦性状态(专利 CN200910309382) */
|
||
#if USE_FLATNESS_EXIT
|
||
g_exit_state = 0;
|
||
g_max_slope = 0;
|
||
g_max_slope_rate = 0;
|
||
g_delta2 = 0;
|
||
g_delta3 = 0;
|
||
g_slope_flat_cnt = 0;
|
||
g_flat_ok_cnt = 0;
|
||
#endif
|
||
}
|
||
} else {
|
||
if (loop1_entry_cnt > 0) loop1_entry_cnt = 0; // 离开阈值区域则重置确认计数
|
||
}
|
||
} else {
|
||
#if USE_FLATNESS_EXIT
|
||
/*================================================================
|
||
* 有车状态 — 平坦性判定离开(专利 CN200910309382)
|
||
*================================================================*/
|
||
#define K1 8
|
||
#define K2 8
|
||
#define SLOPE_FLAT_THRESH 100
|
||
#define MIN_DELTA2 5
|
||
#define MIN_DELTA3 2
|
||
#define FLAT_CONFIRM_CNT 3
|
||
|
||
int32_t first_deriv, abs_fd, abs_sd, second_deriv;
|
||
|
||
first_deriv = (int32_t)loop1_CAPVD - g_prev_capvd;
|
||
second_deriv = first_deriv - g_prev_first_deriv;
|
||
abs_fd = (first_deriv >= 0) ? first_deriv : -first_deriv;
|
||
abs_sd = (second_deriv >= 0) ? second_deriv : -second_deriv;
|
||
|
||
if (g_exit_state == 0) {
|
||
if (abs_fd > g_max_slope) g_max_slope = abs_fd;
|
||
if (abs_sd > g_max_slope_rate) g_max_slope_rate = abs_sd;
|
||
if (abs_fd < SLOPE_FLAT_THRESH) {
|
||
g_slope_flat_cnt++;
|
||
if (g_slope_flat_cnt >= 3) {
|
||
g_delta2 = g_max_slope / K1;
|
||
g_delta3 = g_max_slope_rate / K2;
|
||
if (g_delta2 < MIN_DELTA2) g_delta2 = MIN_DELTA2;
|
||
if (g_delta3 < MIN_DELTA3) g_delta3 = MIN_DELTA3;
|
||
g_exit_state = 1;
|
||
g_flat_ok_cnt = 0;
|
||
}
|
||
} else {
|
||
g_slope_flat_cnt = 0;
|
||
}
|
||
} else {
|
||
int32_t dev = (int32_t)loop1_CAPVD - (int32_t)loop1_Origin;
|
||
int32_t cond1 = (dev >= 0) ? dev : -dev;
|
||
loop1_dlt_ORG = ((uint32_t)loop1_Origin * SensTable_1[loop1_SensLevel]) >> 16;
|
||
if (cond1 < (int32_t)loop1_dlt_ORG && abs_fd < (int32_t)g_delta2
|
||
&& abs_sd < (int32_t)g_delta3) {
|
||
g_flat_ok_cnt++;
|
||
if (g_flat_ok_cnt >= FLAT_CONFIRM_CNT) {
|
||
PRINT("Car_OFF_FLAT, CAPVD:%d Origin:%d d1:%d d2:%d d3:%d f':%d f'':%d\n",
|
||
loop1_CAPVD, loop1_Origin, loop1_dlt_ORG,
|
||
g_delta2, g_delta3, first_deriv, second_deriv);
|
||
loop1_VD_FLAG = 0;
|
||
loop1_FLAG_OUT = 1;
|
||
loop1_VD_HOLD = 0;
|
||
loop1_LC_HOLD = 0;
|
||
loop1_ORG_CNT = 0;
|
||
loop1_ORG_SUM = 0;
|
||
Hold_CNT = 0;
|
||
g_flat_ok_cnt = 0;
|
||
g_exit_state = 0;
|
||
}
|
||
} else {
|
||
g_flat_ok_cnt = 0;
|
||
}
|
||
}
|
||
g_prev_capvd = loop1_CAPVD;
|
||
g_prev_first_deriv = first_deriv;
|
||
#else
|
||
/*================================================================
|
||
* 有车状态 — 简单 cnt_release 防抖离开
|
||
*================================================================*/
|
||
loop1_dlt_ORG = ((uint32_t)loop1_Origin * SensTable_1[loop1_SensLevel]) >> 16; // 方案B(V4.06): 调浅释放表已并入 SensTable_1
|
||
|
||
#if USE_SLOW_RELEASE
|
||
if ((loop1_Origin - loop1_dlt_ORG) < loop1_CAPVD_slow) { // 方案A: 用慢滤波值对齐 PD132T 释放手感
|
||
#else
|
||
if (rel_release_hit()) { // V4.36.19: 低Q 平滑值+带回加宽; 高/中Q 退回原快值逻辑
|
||
#endif
|
||
if (loop1_cnt_release == 0) {
|
||
/* 过渡事件: 第一次命中离开线 (定位"为什么开始释放") */
|
||
PRINT("EVT|leave_try Value:%d CAPVD:%d slow:%d Origin:%d dlt:%d f:%dkHz LPCNT:%d Xn:%d\n",
|
||
loop1_Value, loop1_CAPVD,
|
||
#if USE_SLOW_RELEASE
|
||
loop1_CAPVD_slow,
|
||
#else
|
||
loop1_CAPVD,
|
||
#endif
|
||
loop1_Origin, loop1_dlt_ORG,
|
||
calc_freq_khz(loop1_CAPVD, loop1_LPCNT), loop1_LPCNT, loop1_Xn);
|
||
}
|
||
loop1_cnt_release++;
|
||
if (loop1_cnt_release >= g_release_need) { // V4.36.8: 按Q档 (高3/中4/低5)
|
||
PRINT("Car_OFF, Value:%d CAPVD:%d Origin:%d dlt:%d\n",
|
||
loop1_Value, loop1_CAPVD, loop1_Origin, loop1_dlt_ORG);
|
||
loop1_VD_FLAG = 0;
|
||
loop1_FLAG_OUT = 1;
|
||
loop1_VD_HOLD = 0;
|
||
loop1_LC_HOLD = 0;
|
||
loop1_ORG_CNT = 0;
|
||
loop1_ORG_SUM = 0;
|
||
Hold_CNT = 0;
|
||
loop1_cnt_release = 0;
|
||
g_origin_freeze_tick = REL_ORIGIN_FREEZE_TICKS; /* V4.36.26: 释放后 Origin 均值窗冻结 */
|
||
g_origin_lowlearn_tick = ORIGIN_LOWLEARN_TICKS; /* V4.36.32: 冻结后降权期预置 */
|
||
}
|
||
} else {
|
||
if (loop1_cnt_release > 0) loop1_cnt_release = 0;
|
||
}
|
||
|
||
/* V4.36.30: 环带超时兜底(仅低Q) — 板已离开触发深度(CAPVD 平滑 > 进入线)
|
||
* 持续 2.5s → 强制释放. 空间回滞带(触发线~释放线)内停住不再永久卡死;
|
||
* 真实车深压(dev 远深于触发线)不触发; 撤板/缓离离开触发线后 2.5s 内必释放 */
|
||
if (g_q_lo) {
|
||
uint32_t _entry_ln = vac_ref() -
|
||
(((uint32_t)loop1_Origin * SensTable[loop1_SensLevel]) >> 16); /* V4.36.31: 锚真空参考 */
|
||
if (g_rel_smooth > _entry_ln) {
|
||
g_rel_band_cnt++;
|
||
if (g_rel_band_cnt >= REL_BAND_TIMEOUT_TICKS) {
|
||
g_rel_band_cnt = 0;
|
||
PRINT("Car_OFF(band_timeout), Value:%d CAPVD:%d Origin:%d dlt:%d\n",
|
||
loop1_Value, loop1_CAPVD, loop1_Origin, loop1_dlt_ORG);
|
||
loop1_VD_FLAG = 0;
|
||
loop1_FLAG_OUT = 1;
|
||
loop1_VD_HOLD = 0;
|
||
loop1_LC_HOLD = 0;
|
||
loop1_ORG_CNT = 0;
|
||
loop1_ORG_SUM = 0;
|
||
Hold_CNT = 0;
|
||
loop1_cnt_release = 0;
|
||
g_origin_freeze_tick = REL_ORIGIN_FREEZE_TICKS;
|
||
g_origin_lowlearn_tick = ORIGIN_LOWLEARN_TICKS; /* V4.36.32: 冻结后降权期预置 */
|
||
}
|
||
} else {
|
||
g_rel_band_cnt = 0;
|
||
}
|
||
}
|
||
#endif
|
||
}
|
||
}
|
||
|
||
/*===========================================================================
|
||
* loop_task_function — FreeRTOS 主任务(对齐 M1H main 循环)
|
||
*===========================================================================*/
|
||
/* V4.36.8: 按 Q 档(空场频率代理)更新进入/释放确认样本数 — 每样本调用一次
|
||
* 低Q(高频小L): 测量抖动大 → 多样本防假进入/输出抖落
|
||
* 高Q(低频大L): 信号稳 → 维持快响应 (3 样本) */
|
||
static void update_q_confirm_need(void)
|
||
{
|
||
uint32_t _f = calc_freq_khz(loop1_Origin, loop1_LPCNT);
|
||
if (_f > RECONNECT_Q_MID_F_KHZ) { /* 低Q f>100k */
|
||
g_entry_need = ENTRY_CONFIRM_LO;
|
||
g_release_need = REL_CONFIRM_LO;
|
||
g_q_lo = 1; /* V4.36.19: 低Q 释放抗抖档 */
|
||
} else if (_f > RECONNECT_Q_HI_F_KHZ) { /* 中Q 80~100k */
|
||
g_entry_need = ENTRY_CONFIRM_MID;
|
||
g_release_need = REL_CONFIRM_MID;
|
||
g_q_lo = 0;
|
||
} else { /* 高Q f<=80k (含 Origin=0 f=0) */
|
||
g_entry_need = ENTRY_CONFIRM_HI;
|
||
g_release_need = REL_CONFIRM_HI;
|
||
g_q_lo = 0;
|
||
}
|
||
}
|
||
|
||
#define RECONNECT_CONFIRM_TICKS 10 /* V4.36.2: 重连确认窗 10×10ms=100ms
|
||
* 载波连续恢复满窗才确认重连重置;
|
||
* 抖动断开↔恢复每掉线取消重新计 */
|
||
void loop_task_function(void *pvParameters)
|
||
{
|
||
g_sys_freq = g_crm_clocks_freq_struct.sclk_freq;
|
||
|
||
INIT_VD();
|
||
|
||
while (1) {
|
||
/* V4.36.24: 强制断开期(L<下限)恢复监测 — 无条件, 必须在 LOOP_OK 分支前 */
|
||
coil_monitor_recover();
|
||
|
||
/*--- V4.36.14: 主循环拍级 RF 上升沿检测 (不依赖 ISR LOOP_OK0) ---
|
||
* 原 !LOOP_OK0 依赖 ISR 5ms 拍与主循环 150ms 慢 tick 的竞态:
|
||
* 插头抖动(断开↔恢复几拍)可使 LOOP_OK0 卡 1 → 恢复时漏触发确认窗
|
||
* → CAPVD 不重置直接跑判定 → 插回瞬态假"有车"跳输出 (现场 21µH 复现)
|
||
* 现每 10ms 主循环拍自采样 LOOP_OK: 任何 0→1 恢复都强制进确认窗 */
|
||
if (loop1_LOOP_OK && !g_prev_ok) {
|
||
g_prev_ok = 1;
|
||
g_reconn_candidate = 1;
|
||
g_reconn_cnt = 0;
|
||
} else if (!loop1_LOOP_OK) {
|
||
g_prev_ok = 0;
|
||
}
|
||
|
||
if (loop1_LOOP_OK) {
|
||
/*--- 线圈重连: V4.36.2 确认窗 (载波连续恢复 ~100ms 才确认重置) ---
|
||
* RF 上升沿(顶部检测)仅进入候选; 期间保持 g_disconnect_active=1 → 黄灯仍快闪,
|
||
* 断开计数不重复; 抖动(断开↔恢复快速交替)每次掉线取消候选 → 免疫.
|
||
* 确认后才: 清断开标记 / CAPVD 强制重收敛(首样本此时已干净, 无斜率限幅绕过风险) */
|
||
if (g_reconn_candidate) {
|
||
g_reconn_cnt++;
|
||
if (g_reconn_cnt >= RECONNECT_CONFIRM_TICKS) {
|
||
g_reconn_candidate = 0;
|
||
g_reconn_cnt = 0;
|
||
g_disconnect_active = 0; // 确认重连: 清除断开标记
|
||
loop1_CAPVD = 0; // 强制 IIR 从首个有效值重新收敛
|
||
loop1_CAPVD_slow = 0; // 方案A: 释放慢滤波同步收敛
|
||
g_rel_smooth = 0; // V4.36.19: 释放平滑同步收敛
|
||
g_env_wait_cnt = 0; // V4.24: 断开重连后环境学习状态复位
|
||
g_relay_hold_cnt = 0; // V4.36.3: 保持计时复位
|
||
g_relay_hold_done = 0;
|
||
g_reconn_blank_ms = 0; // V4.36.5: 抑制倒计时(首样本直锁时按 Q 定档)
|
||
/* V4.36.15/16: 无车断开重连(运行中 stable=1) → 判稳门控(值稳定才放行);
|
||
* 有车断开重连(VD_FLAG=1, 保持路径输出中)不判稳: 重连后走离开判定;
|
||
* 上电/安全复位判稳(stable=0)不触发: 上电自检归绿灯慢闪, 黄灯不闪 */
|
||
if (loop1_VD_FLAG == 0 && g_loop_stable) {
|
||
g_reconn_stab = 1;
|
||
g_stab_ms = 0;
|
||
g_stab_step_tick = 0;
|
||
g_stab_ok_cnt = 0;
|
||
g_stab_base = 0; // 首样本直锁后置当前 CAPVD
|
||
PRINT("EVT|stab gate on (no-car reconnect)\n");
|
||
} else {
|
||
g_reconn_stab = 0;
|
||
}
|
||
env_resync_abort();
|
||
PRINT("EVT|loop_reconnect confirmed %dms\n",
|
||
RECONNECT_CONFIRM_TICKS * 10);
|
||
}
|
||
/* 确认窗内: 等载波稳定, 不跑检测 (黄灯由 disconnect_active=1 维持快闪) */
|
||
} else {
|
||
if (loop1_CAP_OK) {
|
||
loop1_CAP_OK = 0;
|
||
|
||
/* 首次成功测量 → 标记线圈已连接 */
|
||
if (!g_loop_power_up_state) {
|
||
g_loop_power_up_state = 1;
|
||
}
|
||
|
||
/* 核心检测算法 (确认窗结束后 CAPVD=0 → 首样本干净重建) */
|
||
vd1_task();
|
||
|
||
/* 拨码开关轮询 */
|
||
poll_sw_state();
|
||
}
|
||
}
|
||
} else {
|
||
/*--- 线圈断开 ---*/
|
||
/* V4.36.2: 掉线即取消重连候选 (抖动免疫: 每次掉线重新计确认窗) */
|
||
g_reconn_candidate = 0;
|
||
g_reconn_cnt = 0;
|
||
/* V4.36.3: 有车断开 → 继电器保持(ISR 计时, RELAY_HOLD_MS 兜底释放);
|
||
* 无车断开 → 立即释放 (原行为) */
|
||
if (!(loop1_VD_FLAG && g_loop_power_up_state)) {
|
||
RLY1_OFF;
|
||
RLY2_OFF;
|
||
}
|
||
/* 注意: 不清除 loop1_VD_FLAG,保留断开前的检测状态 */
|
||
|
||
/*
|
||
* 断开计数规则:
|
||
* - 从未连接过上电 (g_loop_power_up_state == 0): 不计入
|
||
* - g_disconnect_active == 0: 新的断开 → 计数+1
|
||
* - g_disconnect_active == 1: 同一次断开,不重复计数
|
||
*/
|
||
if (g_loop_power_up_state && !g_disconnect_active) {
|
||
g_disconnect_active = 1;
|
||
if (g_disconnect_count < 3) {
|
||
g_disconnect_count++;
|
||
}
|
||
g_fault_phase = 0;
|
||
g_fault_tick = 0;
|
||
PRINT("EVT|loop_disconnect cnt=%d Xn:%d CAPVD:%d Origin:%d f:%dkHz\n",
|
||
g_disconnect_count, loop1_Xn, loop1_CAPVD, loop1_Origin,
|
||
calc_freq_khz(loop1_CAPVD, loop1_LPCNT));
|
||
}
|
||
|
||
vTaskDelay(150);
|
||
}
|
||
|
||
/*--- 调试输出(每 2 秒): V4.20 重组 — 频率/采样/基准全量状态 ---*/
|
||
#ifdef DEBUG
|
||
{
|
||
static uint32_t _dbg_cnt = 0;
|
||
_dbg_cnt++;
|
||
if (_dbg_cnt >= 200) { // 200 × 10ms = 2s
|
||
uint32_t _t_ms = _dbg_cnt * 10; // V4.20: 先取时间再归零
|
||
_dbg_cnt = 0;
|
||
uint32_t _fk = calc_freq_khz(loop1_CAPVD, loop1_LPCNT);
|
||
/* V4.36.27 诊断: 释放路径现场 — QLO=低Q档 RSM=释放平滑 RLN=低Q释放线 RCN=释放确认计数
|
||
* (每 2s 一行, 不阻塞 10ms tick, 不屏蔽低Q 抖动复现) */
|
||
{
|
||
uint32_t _rln = vac_ref() - loop1_dlt_ORG;
|
||
if (g_q_lo)
|
||
_rln = vac_ref() - ((loop1_dlt_ORG * REL_BAND_LO_MULT) / 100); /* V4.36.31: 显示真空锚定线 */
|
||
PRINT("DBG|t=%ums f=%dkHz L=%luuH Xn=%d LPCNT=%d Value=%d CAPVD=%d slow=%d "
|
||
"Origin=%d dlt=%d SENS=%d VD=%d FRZ=%d/%d LIM=%d LOOP=%d STBL=%d "
|
||
"QLO=%d VEM=%d RSM=%d RLN=%d RCN=%d SUM=%lu\n",
|
||
_t_ms,
|
||
_fk,
|
||
coil_l_uH(_fk),
|
||
loop1_Xn, loop1_LPCNT, loop1_Value, loop1_CAPVD, loop1_CAPVD_slow,
|
||
loop1_Origin, loop1_dlt_ORG, loop1_SensLevel, loop1_VD_FLAG,
|
||
loop1_freeze_cnt, loop1_freeze_ref, g_slope_limit_cnt,
|
||
loop1_LOOP_OK, g_loop_stable,
|
||
g_q_lo, g_vacuum_ema, g_rel_smooth, _rln, loop1_cnt_release,
|
||
(unsigned long)loop1_ORG_SUM);
|
||
}
|
||
static uint16_t _adr_cnt = 0; /* V4.36.35: _t_ms 恒 2000, 原条件永不触发; 独立计数 5 次×2s=10s */
|
||
if (++_adr_cnt >= 5) {
|
||
_adr_cnt = 0;
|
||
PRINT("ADR|VEM=%08X ORGSUM=%08X Origin=%08X RSM=%08X\n",
|
||
(unsigned)&g_vacuum_ema, (unsigned)&loop1_ORG_SUM,
|
||
(unsigned)&loop1_Origin, (unsigned)&g_rel_smooth);
|
||
}
|
||
g_slope_limit_cnt = 0; // 限幅计数按周期清 (统计最近 2s)
|
||
}
|
||
}
|
||
#endif
|
||
|
||
poll_wdg(); // IWDT 喂狗 (500ms, TMR15 活性双保险)
|
||
vTaskDelay(10); // 10ms tick, M4 优化: 双路 IIR + 进入确认 + 斜率限幅
|
||
}
|
||
}
|