/****************************************************************************** * @file app_relay.c * @brief 继电器:请求等过零执行;超时则关强断、开放弃 * @author cyWu <1917507415@qq.com> * @date 2026.09.04 * @version V1.1.0 * @history * - V1.1.0, 2026.09.04, cyWu, 去掉未使用计数,精简注释 * - V1.0.0, 2026.09.04, cyWu, 首次发布 ******************************************************************************/ #include "system/includes.h" #include "app_relay.h" #include "bsp_hw.h" #include "board_pin.h" #include "jb_product.h" #define LOG_TAG_CONST APP #define LOG_TAG "[RELAY]" #define LOG_INFO_ENABLE #include "debug.h" typedef struct { uint8_t request; /**< 上层意图:0=关 1=开 */ uint8_t actual; /**< 过零后的 GPIO 真值 */ } AppRelayCtx_t; static AppRelayCtx_t s_relay; /** * @brief 写继电器 GPIO 并更新 actual(过零 ISR / 超时路径) * @param on 1=吸合 0=断开 * @return 无 */ static void relay_apply(uint8_t on) { bsp_relay_set(on); s_relay.actual = on; } /** * @brief 初始化继电器:GPIO 上电默认关;P0 无 VM,请求=实际=关 * @return 无 */ void app_relay_init(void) { s_relay.request = 0; s_relay.actual = 0; bsp_relay_set(0); log_info("relay init, request=off actual=off\n"); } /** * @brief 请求加热开/关(手动/APP 写 DP0/定时/联动统一入口) * @param on 1=开 0=关 * @return 无 */ void app_relay_request(uint8_t on) { on = on ? 1 : 0; if (on == s_relay.request) { return; /* 意图没变:不重复发起(避免连续同向请求反复计超时) */ } s_relay.request = on; #if BOARD_ZC_ENABLE if (on == s_relay.actual) { /* 例如 PC3 已因超时强关(actual=0),收到关请求:无需等沿,直接同步 */ log_info("request %s but actual already %d, sync\n", on ? "ON" : "OFF", s_relay.actual); return; } log_info("request %s, wait zc edge\n", on ? "ON" : "OFF"); /* 等 app_zc_poll 计数 / bsp_zc 沿回调执行 */ #else /* 无过零(调试):开不吸合,关立即强断(规格书 7.2 / 4 章) */ if (on) { gDevData.fault_code = 2; log_info("zc disabled: open request rejected, stay off\n"); s_relay.request = 0; /* 放弃开请求,回落到实际(不吸合) */ } else { log_info("zc disabled: force relay off\n"); relay_apply(0); } #endif } /** * @brief 过零沿:有 pending 则切 GPIO * @note FPIN ISR 上下文,禁止 printf / 写 VM / 切 BLE * @return 无 */ void app_relay_zc_edge(void) { if (s_relay.request == s_relay.actual) { return; /* 无 pending,仅记录过零(由 app_zc 管超时) */ } relay_apply(s_relay.request); /* 7.2:下一次成功过零切变后清设备故障(RAM 写,ISR 安全) */ gDevData.fault_code = 0; } /** * @brief 过零超时:关则强断,开则放弃并保持关,fault_code=2 * @note 20ms 节拍调用;与 FPIN ISR 抢状态,关中断后复检 * @return 无 */ void app_relay_zc_timeout(void) { local_irq_disable(); if (s_relay.request == s_relay.actual) { local_irq_enable(); return; } gDevData.fault_code = 2; if (s_relay.request) { /* 开请求超时:禁止无过零吸合,放弃本次开请求,保持关 */ s_relay.request = 0; s_relay.actual = 0; bsp_relay_set(0); } else { /* 关请求超时:强制断开(安全路径,规格书 7.2) */ bsp_relay_set(0); s_relay.actual = 0; } local_irq_enable(); } /** * @brief 当前请求态 * @return 1=请求开,0=请求关 */ uint8_t app_relay_get_request(void) { return s_relay.request; } /** * @brief 当前实际吸合态 * @return 1=已吸合,0=已断开 */ uint8_t app_relay_get_actual(void) { return s_relay.actual; } /** * @brief 是否已同步(无 pending) * @return 1=request==actual */ uint8_t app_relay_synced(void) { return (s_relay.request == s_relay.actual) ? 1 : 0; }