| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629 |
- /*
- * HV Detect Module - v6.5 (Minimalist Pulse Capture)
- *
- * 设计哲学:一个标志位走天下
- * - capturing = 0 : 等待脉冲(电流<1500 或已锁定)
- * - capturing = 1 : 捕获脉冲(跟踪特征,直到回落或超时)
- *
- * 长放电防重复:超时退出时加锁,必须等电流回落才解锁
- * 快速连续:正常回落退出不加锁,立即可以开始新脉冲
- */
- /*********************↓ include ↓************************/
- #define DBG_TAG "HV"
- #define DBG_LVL DBG_INFO
- #include <rtdbg.h>
- #include <rtthread.h>
- #include <rtdevice.h>
- #include <board.h>
- #include <string.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include "ecode.h"
- #include "ym310_protecl.h"
- /*********************↑ include ↑************************/
- /*********************↓ define ↓************************/
- #define HV_BASE_MS 5
- /* 固定阈值(物理分界) */
- #define STANDBY_THRESHOLD 1500
- #define SPIKE_THRESHOLD 2000
- #define FLAT_MIN 1500
- #define FLAT_MAX 2000
- /* 脉冲完整性(基于基准,排除噪声) */
- #define PULSE_MIN_OFFSET 300
- /* 消抖 */
- #define FALL_CNT_MAX 1 /* 回落确认:30ms */
- #define MAX_DISCHARGE_MS 5000 /* 超时:5s */
- /* 时间 */
- #define MIN_INTERVAL_MS 50 /* 同模式最小间隔 */
- #define STARTUP_IGNORE_MS 3000 /* 启动冻结:3s */
- /* 基准 */
- #define BASE_INIT_DELAY_MS 2000
- #define BASE_SAMPLE_CNT 30
- #define BASE_DEFAULT 1135
- #define BASE_MIN 1000
- #define BASE_MAX 1300
- /* 日志 */
- #define UNCOUNT_CNT 32
- #define TRACE_CNT 100
- /* 实时打印间隔 */
- #define LIVE_PRINT_EVERY 5 /* 每5周期(50ms)打印一次 */
- /*********************↑ define ↑************************/
- /*********************↓ 外部声明 ↓***********************/
- extern SYS_STATUS sys_sta;
- extern RTC_HandleTypeDef hrtc;
- extern rt_bool_t gpio_hv_get_status(void);
- /*********************↑ 外部声明 ↑***********************/
- /*********************↓ 枚举 ↓************************/
- typedef enum
- {
- MODE_NONE = 0,
- MODE_SINGLE_SPIKE, /* peak>=2000, flat<2 */
- MODE_FLAT, /* peak<2000, flat>=3 */
- MODE_STANDARD, /* peak>=2000, flat>=2 */
- } hv_mode_t;
- typedef enum
- {
- UNCOUNT_NONE = 0,
- UNCOUNT_LOW_PEAK,
- UNCOUNT_INTERVAL,
- UNCOUNT_HV_OFF,
- UNCOUNT_BASE_NOT_READY,
- } uncount_reason_t;
- typedef enum
- {
- TRACE_WAIT = 0,
- TRACE_CAPTURE,
- TRACE_END,
- TRACE_TIMEOUT,
- TRACE_LOCKED,
- TRACE_HV_OFF,
- } trace_mark_t;
- /*********************↑ 枚举 ↑************************/
- /*********************↓ 结构体 ↓************************/
- typedef struct
- {
- uint32_t tick;
- uint16_t cur;
- uint16_t peak;
- uint8_t reason;
- uint16_t duration_ms;
- } uncount_rec_t;
- typedef struct
- {
- uint32_t tick;
- uint16_t cur;
- uint8_t capturing;
- uint8_t locked;
- uint8_t mark;
- } trace_rec_t;
- typedef struct
- {
- uint16_t peak;
- uint8_t has_spike;
- uint8_t flat_cnt;
- uint32_t start_tick;
- uint8_t falling_cnt;
- } pulse_win_t;
- typedef struct
- {
- uint8_t ready;
- uint16_t value;
- uint8_t waiting_init;
- uint32_t init_start_tick;
- uint32_t sample_sum;
- uint8_t sample_cnt;
- } base_t;
- /*********************↑ 结构体 ↑************************/
- /*********************↓ 静态变量 ↓************************/
- static rt_thread_t s_thread = RT_NULL;
- static uint8_t s_capturing = 0; /* 唯一状态标志:是否在捕获脉冲 */
- static uint8_t s_locked = 0; /* 长放电锁:超时后锁定,需回落解锁 */
- static pulse_win_t s_win = {0}; /* 当前脉冲窗口统计 */
- static base_t s_base = {.value = BASE_DEFAULT};
- static uint8_t s_hv_on = 0;
- static uint32_t s_hv_tick = 0;
- static uint32_t s_last_count_tick = 0;
- static uint32_t s_total_count = 0;
- static uint32_t s_total_uncount = 0;
- static uint8_t s_live_cnt = 0;
- static hv_mode_t s_last_mode = MODE_NONE;
- static uncount_rec_t s_uncount_log[UNCOUNT_CNT];
- static uint16_t s_uncount_head = 0;
- static uint16_t s_uncount_cnt = 0;
- static rt_mutex_t s_uncount_mtx = RT_NULL;
- static trace_rec_t s_trace_log[TRACE_CNT];
- static uint16_t s_trace_head = 0;
- static uint8_t s_trace_full = 0;
- /*********************↑ 静态变量 ↑************************/
- /*********************↓ 前向声明 ↓************************/
- static uint8_t is_hv_on(void);
- static void reset_all(void);
- static const char *mode_str(hv_mode_t m);
- static const char *uncount_str(uncount_reason_t r);
- static uint8_t can_count(hv_mode_t mode);
- static void end_pulse(uint32_t now, uint8_t timeout);
- static void log_uncount(uncount_reason_t r, uint16_t cur, uint16_t peak, uint32_t dur);
- static void push_trace(uint16_t cur, uint8_t cap, uint8_t lock, uint8_t mark);
- static void live_print(uint16_t cur, uint32_t now);
- static hv_mode_t judge_mode(uint16_t peak, uint8_t flat, uint8_t spike);
- void hv_save(uint32_t c);
- uint32_t hv_read(void);
- /*********************↑ 前向声明 ↑************************/
- /*********************↓ 辅助函数 ↓************************/
- static uint8_t is_hv_on(void) { return gpio_hv_get_status() == 1; }
- static void reset_all(void)
- {
- s_capturing = 0;
- s_locked = 0;
- memset(&s_win, 0, sizeof(s_win));
- s_base.ready = 0;
- s_base.waiting_init = 0;
- s_base.sample_sum = 0;
- s_base.sample_cnt = 0;
- s_hv_tick = 0;
- s_live_cnt = 0;
- }
- static const char *mode_str(hv_mode_t m)
- {
- switch (m)
- {
- case MODE_SINGLE_SPIKE:
- return "单尖峰";
- case MODE_FLAT:
- return "平缓";
- case MODE_STANDARD:
- return "标准";
- default:
- return "未知";
- }
- }
- static const char *uncount_str(uncount_reason_t r)
- {
- switch (r)
- {
- case UNCOUNT_LOW_PEAK:
- return "峰值过低";
- case UNCOUNT_INTERVAL:
- return "间隔去抖";
- case UNCOUNT_HV_OFF:
- return "高压关闭";
- case UNCOUNT_BASE_NOT_READY:
- return "基准未就绪";
- default:
- return "未知";
- }
- }
- static uint8_t can_count(hv_mode_t mode)
- {
- if (s_last_count_tick == 0)
- return 1;
- uint32_t elapsed = (rt_tick_get() - s_last_count_tick) * HV_BASE_MS;
- if (elapsed >= MIN_INTERVAL_MS)
- return 1;
- if (mode != s_last_mode)
- return 1;
- return 0;
- }
- static void log_uncount(uncount_reason_t r, uint16_t cur, uint16_t peak, uint32_t dur)
- {
- if (s_uncount_mtx == RT_NULL)
- return;
- rt_mutex_take(s_uncount_mtx, RT_WAITING_FOREVER);
- uint16_t idx = s_uncount_head % UNCOUNT_CNT;
- s_uncount_log[idx].tick = rt_tick_get();
- s_uncount_log[idx].cur = cur;
- s_uncount_log[idx].peak = peak;
- s_uncount_log[idx].reason = r;
- s_uncount_log[idx].duration_ms = (uint16_t)dur;
- s_uncount_head++;
- if (s_uncount_cnt < UNCOUNT_CNT)
- s_uncount_cnt++;
- rt_mutex_release(s_uncount_mtx);
- s_total_uncount++;
- LOG_W("[HV] 未计[%s] cur=%u peak=%u dur=%ums", uncount_str(r), cur, peak, dur);
- }
- static void push_trace(uint16_t cur, uint8_t cap, uint8_t lock, uint8_t mark)
- {
- uint16_t idx = s_trace_head % TRACE_CNT;
- s_trace_log[idx].tick = rt_tick_get();
- s_trace_log[idx].cur = cur;
- s_trace_log[idx].capturing = cap;
- s_trace_log[idx].locked = lock;
- s_trace_log[idx].mark = mark;
- s_trace_head++;
- if (s_trace_head >= TRACE_CNT)
- s_trace_full = 1;
- }
- static void live_print(uint16_t cur, uint32_t now)
- {
- if (++s_live_cnt >= LIVE_PRINT_EVERY)
- {
- s_live_cnt = 0;
- uint32_t dur = (now - s_win.start_tick) * HV_BASE_MS;
- LOG_D("[HV] LIVE cur=%u peak=%u flat=%u spike=%u dur=%ums",
- cur, s_win.peak, s_win.flat_cnt, s_win.has_spike, dur);
- }
- }
- static hv_mode_t judge_mode(uint16_t peak, uint8_t flat, uint8_t spike)
- {
- if (spike && flat < 2)
- return MODE_SINGLE_SPIKE;
- if (spike && flat >= 2)
- return MODE_STANDARD;
- if (flat >= 3)
- return MODE_FLAT;
- if (peak >= SPIKE_THRESHOLD)
- return MODE_SINGLE_SPIKE;
- return MODE_FLAT;
- }
- static void end_pulse(uint32_t now, uint8_t is_timeout)
- {
- uint32_t dur = (now - s_win.start_tick) * HV_BASE_MS;
- if (!s_base.ready)
- {
- log_uncount(UNCOUNT_BASE_NOT_READY, 0, s_win.peak, dur);
- goto cleanup;
- }
- if (s_win.peak < s_base.value + PULSE_MIN_OFFSET)
- {
- log_uncount(UNCOUNT_LOW_PEAK, 0, s_win.peak, dur);
- goto cleanup;
- }
- hv_mode_t mode = judge_mode(s_win.peak, s_win.flat_cnt, s_win.has_spike);
- if (mode == MODE_FLAT && s_win.peak <= FLAT_MIN)
- {
- log_uncount(UNCOUNT_LOW_PEAK, 0, s_win.peak, dur);
- goto cleanup;
- }
- LOG_I("[HV] 结束[%s] peak=%u flat=%u spike=%u dur=%ums%s",
- mode_str(mode), s_win.peak, s_win.flat_cnt, s_win.has_spike,
- dur, is_timeout ? " [超时]" : "");
- if (!can_count(mode))
- {
- log_uncount(UNCOUNT_INTERVAL, s_win.peak, s_win.peak, dur);
- goto cleanup;
- }
- s_last_count_tick = rt_tick_get();
- sys_sta.hv_count++;
- hv_save(sys_sta.hv_count);
- s_total_count++;
- s_last_mode = mode;
- LOG_I("[HV] [%s] +1=%u peak=%u dur=%ums", mode_str(mode), sys_sta.hv_count, s_win.peak, dur);
- cleanup:
- s_capturing = 0;
- memset(&s_win, 0, sizeof(s_win));
- if (is_timeout)
- {
- s_locked = 1;
- LOG_I("[HV] 窗口锁定,等待电流回落");
- }
- }
- uint32_t hv_read(void)
- {
- sys_sta.hv_count = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR6);
- return HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR6);
- }
- void hv_save(uint32_t c)
- {
- if (HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR6) != c)
- {
- HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR6, c);
- }
- sys_sta.hv_count = c;
- }
- /*********************↑ 辅助函数 ↑************************/
- /*********************↓ 核心线程 ↓************************/
- static void hv_thread(void *param)
- {
- uint16_t cur;
- uint32_t now;
- uint8_t hv;
- s_uncount_mtx = rt_mutex_create("hv_uncount", RT_IPC_FLAG_PRIO);
- // LOG_I("[HV] v6.5 极简双态 待机<%d 平缓[%d,%d) 尖峰>=%d 超时=%dms",
- // STANDBY_THRESHOLD, FLAT_MIN, FLAT_MAX, SPIKE_THRESHOLD, MAX_DISCHARGE_MS);
- hv_read();
- LOG_I("[HV] 保存计数=%u", sys_sta.hv_count);
- while (1)
- {
- now = rt_tick_get();
- cur = sys_sta.hv_current;
- hv = is_hv_on();
- if (!hv)
- {
- if (s_hv_on)
- {
- if (s_capturing)
- {
- log_uncount(UNCOUNT_HV_OFF, cur, s_win.peak,
- (now - s_win.start_tick) * HV_BASE_MS);
- }
- reset_all();
- s_hv_on = 0;
- }
- push_trace(cur, 0, 0, TRACE_HV_OFF);
- rt_thread_mdelay(HV_BASE_MS);
- continue;
- }
- if (!s_hv_on)
- {
- s_hv_on = 1;
- reset_all();
- s_hv_tick = now;
- s_base.waiting_init = 1;
- s_base.init_start_tick = now;
- //LOG_I("[HV] 开启,%dms内冻结", STARTUP_IGNORE_MS);
- }
- uint32_t hv_elapse = (now - s_hv_tick) * HV_BASE_MS;
- if (hv_elapse < STARTUP_IGNORE_MS)
- {
- push_trace(cur, 0, 0, TRACE_WAIT);
- rt_thread_mdelay(HV_BASE_MS);
- continue;
- }
- if (s_base.waiting_init && !s_base.ready)
- {
- uint32_t elapse = (now - s_base.init_start_tick) * HV_BASE_MS;
- if (elapse >= BASE_INIT_DELAY_MS)
- {
- s_base.sample_sum += cur;
- s_base.sample_cnt++;
- if (s_base.sample_cnt >= BASE_SAMPLE_CNT)
- {
- uint16_t nb = s_base.sample_sum / s_base.sample_cnt;
- s_base.value = (nb >= BASE_MIN && nb <= BASE_MAX) ? nb : BASE_DEFAULT;
- s_base.ready = 1;
- s_base.waiting_init = 0;
- LOG_I("[HV] 基准=%u", s_base.value);
- }
- }
- }
- if (!s_capturing)
- {
- if (cur < STANDBY_THRESHOLD)
- {
- if (s_locked)
- {
- s_locked = 0;
- LOG_I("[HV] 解锁");
- }
- push_trace(cur, 0, 0, TRACE_WAIT);
- }
- else if (!s_locked && s_base.ready)
- {
- s_capturing = 1;
- s_win.peak = cur;
- s_win.has_spike = (cur >= SPIKE_THRESHOLD);
- s_win.flat_cnt = (cur >= FLAT_MIN && cur < FLAT_MAX) ? 1 : 0;
- s_win.start_tick = now;
- s_win.falling_cnt = 0;
- s_live_cnt = 0;
- LOG_I("[HV] 捕获开始 cur=%u", cur);
- push_trace(cur, 1, 0, TRACE_CAPTURE);
- }
- else
- {
- push_trace(cur, 0, 1, TRACE_LOCKED);
- }
- }
- else
- {
- if (cur > s_win.peak)
- s_win.peak = cur;
- if (cur >= SPIKE_THRESHOLD)
- s_win.has_spike = 1;
- if (cur >= FLAT_MIN && cur < FLAT_MAX)
- s_win.flat_cnt++;
- live_print(cur, now);
- if (cur < STANDBY_THRESHOLD)
- {
- if (++s_win.falling_cnt >= FALL_CNT_MAX)
- {
- end_pulse(now, 0);
- push_trace(cur, 0, 0, TRACE_END);
- }
- else
- {
- push_trace(cur, 1, 0, TRACE_CAPTURE);
- }
- }
- else
- {
- s_win.falling_cnt = 0;
- if ((now - s_win.start_tick) * HV_BASE_MS >= MAX_DISCHARGE_MS)
- {
- end_pulse(now, 1);
- push_trace(cur, 0, 1, TRACE_TIMEOUT);
- }
- else
- {
- push_trace(cur, 1, 0, TRACE_CAPTURE);
- }
- }
- }
- rt_thread_mdelay(HV_BASE_MS);
- }
- }
- /*********************↑ 核心线程 ↑************************/
- /*********************↓ 初始化 ↓************************/
- static void hv_start(void)
- {
- if (s_thread)
- return;
- s_thread = rt_thread_create("hv_detect", hv_thread, NULL, 1024, 10, 10);
- if (s_thread)
- rt_thread_startup(s_thread);
- }
- static int hv_init(void)
- {
- hv_start();
- return 0;
- }
- INIT_APP_EXPORT(hv_init);
- /*********************↑ 初始化 ↑************************/
- /*********************↓ MSH命令 ↓************************/
- static void cmd_status(void)
- {
- uint32_t total = s_total_count + s_total_uncount;
- uint32_t rate = total ? (s_total_count * 100 / total) : 100;
- rt_kprintf("========== HV v6.5 极简双态 ==========\n");
- rt_kprintf("总计数: %u\n", sys_sta.hv_count);
- rt_kprintf("本次计数: %u\n", s_total_count);
- rt_kprintf("本次未计: %u\n", s_total_uncount);
- rt_kprintf("成功率: %u%%\n", rate);
- rt_kprintf("基准: %u (%s)\n", s_base.value, s_base.ready ? "就绪" : "未就绪");
- rt_kprintf("状态: %s %s\n", s_capturing ? "捕获中" : "等待中", s_locked ? "[锁定]" : "");
- rt_kprintf("阈值: 待机<%d 平缓[%d,%d) 尖峰>=%d\n",
- STANDBY_THRESHOLD, FLAT_MIN, FLAT_MAX, SPIKE_THRESHOLD);
- rt_kprintf("======================================\n");
- }
- MSH_CMD_EXPORT(cmd_status, hvsta);
- static void cmd_uncount(void)
- {
- if (s_uncount_mtx == RT_NULL)
- {
- rt_kprintf("未初始化\n");
- return;
- }
- rt_mutex_take(s_uncount_mtx, RT_WAITING_FOREVER);
- if (s_uncount_cnt == 0)
- {
- rt_kprintf("========== 未计数日志 ==========\n无记录\n================================\n");
- rt_mutex_release(s_uncount_mtx);
- return;
- }
- rt_kprintf("========== 未计数日志 (%d条) ==========\n", s_uncount_cnt);
- rt_kprintf("%-4s %-8s %-6s %-6s %-10s %-8s\n", "序号", "时间(s)", "电流", "峰值", "原因", "时长");
- rt_kprintf("----------------------------------------\n");
- uint16_t start = (s_uncount_head < s_uncount_cnt) ? 0 : (s_uncount_head - s_uncount_cnt);
- for (uint16_t i = 0; i < s_uncount_cnt; i++)
- {
- uint16_t idx = (start + i) % UNCOUNT_CNT;
- uncount_rec_t *r = &s_uncount_log[idx];
- rt_kprintf("%-4u %-8u %-6u %-6u %-10s %-7ums\n",
- i + 1, r->tick / 100, r->cur, r->peak,
- uncount_str((uncount_reason_t)r->reason), r->duration_ms);
- }
- rt_kprintf("================================\n");
- rt_mutex_release(s_uncount_mtx);
- }
- MSH_CMD_EXPORT(cmd_uncount, hvuncount);
- static void cmd_trace(int argc, char **argv)
- {
- uint16_t n = (argc >= 2) ? atoi(argv[1]) : 20;
- if (n > TRACE_CNT)
- n = TRACE_CNT;
- if (n == 0)
- n = 1;
- uint16_t total = s_trace_full ? TRACE_CNT : s_trace_head;
- if (n > total)
- n = total;
- if (total == 0)
- {
- rt_kprintf("Trace 为空\n");
- return;
- }
- rt_kprintf("========== Trace (最近%d条) ==========\n", n);
- rt_kprintf("%-5s %-6s %-6s %-4s %-4s %-6s\n", "序号", "Tick", "电流", "捕获", "锁定", "标记");
- rt_kprintf("----------------------------------------\n");
- uint16_t start = (s_trace_head + TRACE_CNT - n) % TRACE_CNT;
- for (uint16_t i = 0; i < n; i++)
- {
- uint16_t idx = (start + i) % TRACE_CNT;
- trace_rec_t *t = &s_trace_log[idx];
- rt_kprintf("%-5u %-6u %-6u %-4u %-4u %-6c\n",
- i + 1, t->tick % 10000, t->cur, t->capturing, t->locked,
- t->mark == TRACE_WAIT ? 'W' : t->mark == TRACE_CAPTURE ? 'C'
- : t->mark == TRACE_END ? 'E'
- : t->mark == TRACE_TIMEOUT ? 'T'
- : t->mark == TRACE_LOCKED ? 'L'
- : t->mark == TRACE_HV_OFF ? 'H'
- : '?');
- }
- rt_kprintf("================================\n");
- rt_kprintf("标记: W=等待 C=捕获 E=结束 T=超时 L=锁定 H=关闭\n");
- }
- MSH_CMD_EXPORT(cmd_trace, hvtrace);
- static void cmd_clear(void)
- {
- sys_sta.hv_count = 0;
- hv_save(0);
- reset_all();
- s_last_count_tick = 0;
- s_total_count = 0;
- s_total_uncount = 0;
- s_trace_head = 0;
- s_trace_full = 0;
- memset(s_trace_log, 0, sizeof(s_trace_log));
- if (s_uncount_mtx)
- {
- rt_mutex_take(s_uncount_mtx, RT_WAITING_FOREVER);
- s_uncount_head = 0;
- s_uncount_cnt = 0;
- memset(s_uncount_log, 0, sizeof(s_uncount_log));
- rt_mutex_release(s_uncount_mtx);
- }
- LOG_I("[HV] 已清零");
- rt_kprintf("已清零\n");
- }
- MSH_CMD_EXPORT(cmd_clear, hvclr);
- /*********************↑ MSH命令 ↑************************/
|