/* * GPIO控制模块 - v3.5 (中断+轮询版+组合键) * * 功能: * - 灯管开关控制(PB5) * - 高压输出控制(PB6) * - 按键A检测(PB4,下降沿中断触发) * - 按键B检测(PB3,下降沿中断触发) * - 中断记录时间,线程轮询电平判断长按/短按 * - 双按键组合长按5秒切换光控模式来源 * * Version: 3.5.2 */ /*********************↓ include ↓************************/ #define DBG_TAG "GPIO" #define DBG_LVL DBG_INFO #include #include #include #include #include #include "ecode.h" #include "ym310_protecl.h" /*********************↑ include ↑************************/ /*********************↓ define ↓************************/ /* 灯管开关 */ #define LAMP_PIN GET_PIN(B, 5) #define LAMP_ON PIN_HIGH #define LAMP_OFF PIN_LOW /* 高压输出 */ #define HV_PIN GET_PIN(B, 6) #define HV_ON PIN_HIGH #define HV_OFF PIN_LOW /* 按键 */ #define KEY_A_PIN GET_PIN(B, 4) /* PB4 - 按键A(开关键) */ #define KEY_B_PIN GET_PIN(B, 3) /* PB3 - 按键B(设置键) */ #define KEY_DEBOUNCE_MS 90 /* 防抖时间 */ #define KEY_LONG_MS 5000 /* 5秒长按 */ #define KEY_COMBINED_LONG_MS 5000 /* 组合键长按时间:5秒 */ #define KEY_COMBINED_WINDOW_MS 1000 /* 组合键时间窗口:1秒 */ /* 测试模式时长 */ #define TEST_MODE_MS (60 * 1000) /* 光控时长范围 */ #define LIGHT_DUR_DEFAULT 4 #define LIGHT_DUR_MAX 8 #define LIGHT_DUR_MIN 0 /* 4G电源 */ #define LTE_PIN GET_PIN(B, 2) #define LTE_ON PIN_HIGH #define LTE_OFF PIN_LOW /*********************↑ define ↑************************/ /*********************↓ 全局变量 ↓************************/ static rt_bool_t s_lamp_state = RT_FALSE; static rt_bool_t s_hv_state = RT_FALSE; /* 按键A */ static volatile rt_tick_t s_key_a_press_tick = 0; static volatile rt_uint8_t s_key_a_pending = 0; /* 按键B */ static volatile rt_tick_t s_key_b_press_tick = 0; static volatile rt_uint8_t s_key_b_pending = 0; static rt_timer_t s_test_timer = RT_NULL; volatile char KEY_IN_TEST_TIME = 0; /* 组合键检测 */ static volatile rt_tick_t s_key_combined_press_tick_a = 0; static volatile rt_tick_t s_key_combined_press_tick_b = 0; static volatile rt_uint8_t s_key_combined_pending = 0; static volatile rt_uint8_t s_key_a_pressed_in_combined = 0; static volatile rt_uint8_t s_key_b_pressed_in_combined = 0; static volatile rt_uint8_t s_key_combined_valid = 0; /* 组合键是否有效(时间窗口内) */ static volatile rt_uint8_t s_key_combined_mode = 0; /* 组合键模式激活,屏蔽单个按键 */ static volatile rt_uint8_t s_key_combined_matched = 0; /* 组合键已匹配(间隔<1秒),本次按下次不再处理单个按键 */ /*********************↑ 全局变量 ↑************************/ void gpio_lamp_ctrl(rt_bool_t on); void gpio_hv_ctrl(rt_bool_t on); void gpio_lte_ctrl(rt_bool_t on); void once_test_tig(void); /*********************↓ 外部函数 ↓************************/ extern void led_trigger_set_effect(void); extern void save_dev_info(void); extern void ym310_data_report(rt_uint8_t client_idx); extern int ym310_warn_report(const char *detail); extern uint8_t g_work_state; /*********************↑ 外部函数 ↑************************/ /*********************↓ 按键处理函数 ↓************************/ static void led_effect(void) { led_trigger_set_effect(); } static void test_mode_timeout(void *param) { rt_pin_write(LAMP_PIN, LAMP_OFF); rt_pin_write(HV_PIN, HV_OFF); s_lamp_state = RT_FALSE; s_hv_state = RT_FALSE; LOG_I("Test mode ended"); KEY_IN_TEST_TIME = 0; } /* 按键A:单击 - 切换开关机状态 */ static void key_a_short(void) { char warn_buf[128]; dev_info.power_state = (dev_info.power_state == 1) ? 0 : 1; save_dev_info(); led_effect(); rt_snprintf(warn_buf, sizeof(warn_buf), "开关键:开关机状态=%d", dev_info.power_state); LOG_I("%s", warn_buf); ym310_warn_report(warn_buf); report_now(); } /* 按键A:长按5秒 - 光控工作时长重置为4小时 */ static void key_a_long(void) { char warn_buf[128]; dev_info.light_work_duration = LIGHT_DUR_DEFAULT; save_dev_info(); led_effect(); rt_snprintf(warn_buf, sizeof(warn_buf), "开关键:复位光控时长=%d", dev_info.light_work_duration); LOG_I("%s", warn_buf); ym310_warn_report(warn_buf); if ((dev_info.power_state == 1) && (dev_info.time_ctrl_enable == 0) && (g_work_state != 0)) // 生效工作状态 { // rt_kprintf("g_work_state >> %d \n", g_work_state); dev_info.power_state = 0; rt_thread_mdelay(300); dev_info.power_state = 1; Recode_e_code(e_CMD_restartwork, 1, "按键指令:复位工作状态"); } report_now(); } /** * @brief 手动触发一次测试(高压和灯管通电60秒) */ void once_test_tig(void) { rt_pin_write(LAMP_PIN, LAMP_ON); rt_pin_write(HV_PIN, HV_ON); s_lamp_state = RT_TRUE; s_hv_state = RT_TRUE; KEY_IN_TEST_TIME = VALID; rt_kprintf("KEY_IN_TEST_TIME = VAILD\n"); rt_timer_start(s_test_timer); } /* 按键B:单击 - 开启测试模式 */ static void key_b_short(void) { char warn_buf[128]; once_test_tig(); led_effect(); rt_snprintf(warn_buf, sizeof(warn_buf), "设置键:开启测试模式"); LOG_I("%s", warn_buf); ym310_warn_report(warn_buf); } /* 按键B:长按5秒 - 光控时长+1(0-8循环) */ static void key_b_long(void) { char warn_buf[128]; uint16_t dur = dev_info.light_work_duration + 1; if (dur > LIGHT_DUR_MAX) dur = LIGHT_DUR_MIN; dev_info.light_work_duration = dur; save_dev_info(); led_effect(); rt_snprintf(warn_buf, sizeof(warn_buf), "设置键:光控时长=%d", dev_info.light_work_duration); LOG_I("%s", warn_buf); ym310_warn_report(warn_buf); if ((dev_info.power_state == 1) && (dev_info.time_ctrl_enable == 0) && (g_work_state != 0)) // 生效工作状态 { // rt_kprintf("g_work_state >> %d \n", g_work_state); dev_info.power_state = 0; rt_thread_mdelay(300); dev_info.power_state = 1; Recode_e_code(e_CMD_restartwork, 2, "按键指令:复位工作状态"); } report_now(); } /** * @brief 组合键长按回调(两个按键同时按下5秒) * 切换光控模式来源 */ static void key_combined_long(void) { char warn_buf[128]; if (dev_info.light_flag == 0) { dev_info.light_flag = 1; rt_snprintf(warn_buf, sizeof(warn_buf), "组合键:光控源=光敏管"); } else { dev_info.light_flag = 0; rt_snprintf(warn_buf, sizeof(warn_buf), "组合键:光控源=太阳能板"); } save_dev_info(); LOG_I("%s", warn_buf); ym310_warn_report(warn_buf); led_effect(); report_now(); } /*********************↑ 按键处理函数 ↑************************/ /*********************↓ 中断服务函数 ↓************************/ static void key_a_isr(void *args) { rt_tick_t now = rt_tick_get(); /* 记录按下时间,标记有待处理按键(pending=1表示等待消抖检测) */ s_key_a_press_tick = now; s_key_a_pending = 1; /* 记录组合键按下时间 */ s_key_combined_press_tick_a = now; s_key_a_pressed_in_combined = 1; /* 检查组合键有效性:两个按键按下的时间差是否在1秒内 */ rt_tick_t diff; if (s_key_b_pressed_in_combined) { diff = (now > s_key_combined_press_tick_b) ? (now - s_key_combined_press_tick_b) : (s_key_combined_press_tick_b - now); if (diff < rt_tick_from_millisecond(KEY_COMBINED_WINDOW_MS)) { s_key_combined_valid = 1; s_key_combined_pending = 1; s_key_combined_mode = 1; s_key_combined_matched = 1; /* 组合键已匹配,立即清除单个按键的pending,防止线程提前处理 */ s_key_a_pending = 0; s_key_b_pending = 0; } else { s_key_combined_valid = 0; } } } static void key_b_isr(void *args) { rt_tick_t now = rt_tick_get(); /* 记录按下时间,标记有待处理按键(pending=1表示等待消抖检测) */ s_key_b_press_tick = now; s_key_b_pending = 1; /* 记录组合键按下时间 */ s_key_combined_press_tick_b = now; s_key_b_pressed_in_combined = 1; /* 检查组合键有效性:两个按键按下的时间差是否在1秒内 */ rt_tick_t diff; if (s_key_a_pressed_in_combined) { diff = (now > s_key_combined_press_tick_a) ? (now - s_key_combined_press_tick_a) : (s_key_combined_press_tick_a - now); if (diff < rt_tick_from_millisecond(KEY_COMBINED_WINDOW_MS)) { s_key_combined_valid = 1; s_key_combined_pending = 1; s_key_combined_mode = 1; s_key_combined_matched = 1; /* 组合键已匹配,立即清除单个按键的pending,防止线程提前处理 */ s_key_a_pending = 0; s_key_b_pending = 0; } else { s_key_combined_valid = 0; } } } /*********************↑ 中断服务函数 ↑************************/ /*********************↓ 按键检测线程 ↓************************/ static void key_thread_entry(void *param) { rt_tick_t now; rt_tick_t duration; int pin_level; rt_uint8_t combined_active = 0; while (1) { combined_active = 0; /* ========== 优先处理组合键 ========== */ if (s_key_combined_pending && s_key_combined_valid) { /* 再次清空单个按键的pending,确保安全 */ s_key_a_pending = 0; s_key_b_pending = 0; /* 等待防抖时间后检测 */ rt_thread_mdelay(KEY_DEBOUNCE_MS); int pin_a_level = rt_pin_read(KEY_A_PIN); int pin_b_level = rt_pin_read(KEY_B_PIN); /* 防抖检测:两个电平都应该是低电平才是有效按下 */ if (pin_a_level == PIN_LOW && pin_b_level == PIN_LOW) { /* 两个按键都按着,组合键模式激活,等待5秒 */ while (rt_pin_read(KEY_A_PIN) == PIN_LOW && rt_pin_read(KEY_B_PIN) == PIN_LOW) { now = rt_tick_get(); /* 取两个按键中较晚按下的时间作为起始点 */ rt_tick_t press_start = (s_key_combined_press_tick_a > s_key_combined_press_tick_b) ? s_key_combined_press_tick_a : s_key_combined_press_tick_b; duration = now - press_start; if (duration >= rt_tick_from_millisecond(KEY_COMBINED_LONG_MS)) { key_combined_long(); s_key_combined_pending = 0; s_key_combined_valid = 0; s_key_combined_mode = 0; s_key_combined_matched = 0; LOG_D("Combined keys long press (%d ms)", duration); goto clear_flags; } rt_thread_mdelay(10); } /* 5秒内松开,取消组合键 */ LOG_I("Combined key cancelled: released before 5s"); s_key_combined_pending = 0; s_key_combined_valid = 0; s_key_combined_mode = 0; s_key_combined_matched = 0; } else { /* 防抖后电平已恢复,放弃此次组合键触发 */ LOG_I("Combined key debounce failed: released before validation"); s_key_combined_pending = 0; s_key_combined_valid = 0; s_key_combined_mode = 0; s_key_combined_matched = 0; } /* 跳过单个按键处理 */ goto clear_flags; } else if (s_key_combined_pending && !s_key_combined_valid) { /* 组合键无效(时间差超过1秒),清除组合键标记,不激活组合键模式 */ s_key_combined_pending = 0; s_key_combined_mode = 0; s_key_combined_matched = 0; /* 清除单个按键的待处理标记 */ s_key_a_pending = 0; s_key_b_pending = 0; } /* ========== 处理按键A(仅在非组合键模式下) ========== */ if (!s_key_combined_mode && s_key_a_pending) { /* 等待防抖时间 */ rt_thread_mdelay(KEY_DEBOUNCE_MS); /* 防抖后再次检查组合键模式,如果被激活则放弃 */ if (s_key_combined_mode) { s_key_a_pending = 0; goto skip_a; } if (rt_pin_read(KEY_A_PIN) == PIN_LOW) { /* 有效按下,等待松开或判断长按 */ while (rt_pin_read(KEY_A_PIN) == PIN_LOW) { /* 实时检查组合键模式,如果被激活则立即退出放弃 */ if (s_key_combined_mode) { s_key_a_pending = 0; goto skip_a; } now = rt_tick_get(); duration = now - s_key_a_press_tick; if (duration >= rt_tick_from_millisecond(KEY_LONG_MS)) { /* 长按前再次检查组合键模式 */ if (s_key_combined_mode) { s_key_a_pending = 0; goto skip_a; } key_a_long(); s_key_a_pending = 0; LOG_D("KeyA long press (%d ms)", duration); goto skip_a; } rt_thread_mdelay(10); } /* 松开后判断是否是短按(仅在非组合键模式下) */ if (s_key_a_pending && !s_key_combined_mode) { now = rt_tick_get(); duration = now - s_key_a_press_tick; if (duration < rt_tick_from_millisecond(KEY_LONG_MS)) { key_a_short(); LOG_D("KeyA short press (%d ms)", duration); } s_key_a_pending = 0; } else if (s_key_a_pending && s_key_combined_mode) { s_key_a_pending = 0; } } else { /* 防抖后电平已恢复,放弃此次触发 */ LOG_I("KeyA debounce failed: released before validation"); s_key_a_pending = 0; } } else if (s_key_combined_mode && s_key_a_pending) { /* 组合键模式下,清除A的待处理标记,不触发任何功能 */ s_key_a_pending = 0; } skip_a: /* ========== 处理按键B(仅在非组合键模式下) ========== */ if (!s_key_combined_mode && s_key_b_pending) { /* 等待防抖时间 */ rt_thread_mdelay(KEY_DEBOUNCE_MS); /* 防抖后再次检查组合键模式,如果被激活则放弃 */ if (s_key_combined_mode) { s_key_b_pending = 0; goto skip_b; } if (rt_pin_read(KEY_B_PIN) == PIN_LOW) { /* 有效按下,等待松开或判断长按 */ while (rt_pin_read(KEY_B_PIN) == PIN_LOW) { /* 实时检查组合键模式,如果被激活则立即退出放弃 */ if (s_key_combined_mode) { s_key_b_pending = 0; goto skip_b; } now = rt_tick_get(); duration = now - s_key_b_press_tick; if (duration >= rt_tick_from_millisecond(KEY_LONG_MS)) { /* 长按前再次检查组合键模式 */ if (s_key_combined_mode) { s_key_b_pending = 0; goto skip_b; } key_b_long(); s_key_b_pending = 0; LOG_D("KeyB long press (%d ms)", duration); goto skip_b; } rt_thread_mdelay(10); } /* 松开后判断是否是短按(仅在非组合键模式下) */ if (s_key_b_pending && !s_key_combined_mode) { now = rt_tick_get(); duration = now - s_key_b_press_tick; if (duration < rt_tick_from_millisecond(KEY_LONG_MS)) { key_b_short(); LOG_D("KeyB short press (%d ms)", duration); } s_key_b_pending = 0; } else if (s_key_b_pending && s_key_combined_mode) { s_key_b_pending = 0; } } else { /* 防抖后电平已恢复,放弃此次触发 */ LOG_I("KeyB debounce failed: released before validation"); s_key_b_pending = 0; } } else if (s_key_combined_mode && s_key_b_pending) { /* 组合键模式下,清除B的待处理标记,不触发任何功能 */ s_key_b_pending = 0; } skip_b: clear_flags: /* 组合键匹配有效期内:强制禁止单个按键处理(双重保险) */ if (s_key_combined_matched) { s_key_a_pending = 0; s_key_b_pending = 0; } /* 清除组合键按下状态标记(当按键松开时) */ if (rt_pin_read(KEY_A_PIN) == PIN_HIGH) { s_key_a_pressed_in_combined = 0; } if (rt_pin_read(KEY_B_PIN) == PIN_HIGH) { s_key_b_pressed_in_combined = 0; } /* 如果两个按键都已松开,清除有效性标记和组合键模式 */ if (s_key_a_pressed_in_combined == 0 && s_key_b_pressed_in_combined == 0) { s_key_combined_valid = 0; s_key_combined_mode = 0; s_key_combined_matched = 0; } rt_thread_mdelay(10); } } /*********************↑ 按键检测线程 ↑************************/ /*********************↓ 初始化 ↓************************/ static void gpio_init(void) { /* 初始化灯管 */ rt_pin_mode(LAMP_PIN, PIN_MODE_OUTPUT); rt_pin_write(LAMP_PIN, LAMP_OFF); /* 初始化高压 */ rt_pin_mode(HV_PIN, PIN_MODE_OUTPUT); rt_pin_write(HV_PIN, HV_OFF); /* 初始化4G */ rt_pin_mode(LTE_PIN, PIN_MODE_OUTPUT); rt_pin_write(LTE_PIN, LTE_ON); /* 初始化按键引脚(上拉输入) */ rt_pin_mode(KEY_A_PIN, PIN_MODE_INPUT_PULLUP); rt_pin_mode(KEY_B_PIN, PIN_MODE_INPUT_PULLUP); } static void key_init(void) { /* 绑定中断 */ rt_pin_attach_irq(KEY_A_PIN, PIN_IRQ_MODE_FALLING, key_a_isr, NULL); rt_pin_attach_irq(KEY_B_PIN, PIN_IRQ_MODE_FALLING, key_b_isr, NULL); /* 使能中断 */ rt_pin_irq_enable(KEY_A_PIN, PIN_IRQ_ENABLE); rt_pin_irq_enable(KEY_B_PIN, PIN_IRQ_ENABLE); /* 创建测试模式定时器 */ s_test_timer = rt_timer_create("test_mode", test_mode_timeout, NULL, TEST_MODE_MS, RT_TIMER_FLAG_ONE_SHOT); if (s_test_timer == RT_NULL) { LOG_E("Failed to create test timer"); } } static void key_thread_start(void) { rt_thread_t tid = rt_thread_create("key_mon", key_thread_entry, NULL, 2048, 15, 10); if (tid) rt_thread_startup(tid); } /*********************↑ 初始化 ↑************************/ /*********************↓ 公共API ↓************************/ void gpio_lamp_ctrl(rt_bool_t on) { rt_pin_write(LAMP_PIN, on ? LAMP_ON : LAMP_OFF); s_lamp_state = on; LOG_I("Lamp: %s", on ? "ON" : "OFF"); } void gpio_hv_ctrl(rt_bool_t on) { rt_pin_write(HV_PIN, on ? HV_ON : HV_OFF); s_hv_state = on; LOG_I("HV: %s", on ? "ON" : "OFF"); } void gpio_lte_ctrl(rt_bool_t on) { rt_pin_write(LTE_PIN, on ? LTE_ON : LTE_OFF); } rt_bool_t gpio_lamp_get_status(void) { return rt_pin_read(LAMP_PIN); } rt_bool_t gpio_hv_get_status(void) { return rt_pin_read(HV_PIN); } rt_bool_t gpio_lte_get_status(void) { return rt_pin_read(LTE_PIN); } void gpio_lamp_toggle(void) { gpio_lamp_ctrl(!s_lamp_state); } void gpio_hv_toggle(void) { gpio_hv_ctrl(!s_hv_state); } void_lte_power_on(void) { rt_kprintf("void_lte_power_on\n"); gpio_lte_ctrl(1); } void_lte_power_off(void) { rt_kprintf("void_lte_power_off\n"); gpio_lte_ctrl(0); } void charge_status(void) { rt_pin_mode(GET_PIN(C, 4), PIN_MODE_INPUT); rt_pin_mode(GET_PIN(C, 5), PIN_MODE_INPUT); rt_kprintf("charge_status is %d-%d\n", rt_pin_read(GET_PIN(C, 4)), rt_pin_read(GET_PIN(C, 5))); } MSH_CMD_EXPORT(void_lte_power_on, void_lte_power_on); MSH_CMD_EXPORT(void_lte_power_off, void_lte_power_off); MSH_CMD_EXPORT(charge_status, charge_status); /*********************↑ 公共API ↑************************/ /*********************↓ 自动初始化 ↓************************/ static int gpio_auto_init(void) { gpio_init(); key_init(); key_thread_start(); return 0; } INIT_APP_EXPORT(gpio_auto_init); /*********************↑ 自动初始化 ↑************************/ /*********************↓ MSH命令 ↓************************/ static void cmd_lamp(int argc, char **argv) { if (argc < 2) { rt_kprintf("Usage: lamp [on|off|toggle|status]\n"); return; } if (strcmp(argv[1], "on") == 0) gpio_lamp_ctrl(RT_TRUE); else if (strcmp(argv[1], "off") == 0) gpio_lamp_ctrl(RT_FALSE); else if (strcmp(argv[1], "toggle") == 0) gpio_lamp_toggle(); else if (strcmp(argv[1], "status") == 0) rt_kprintf("Lamp: %s\n", s_lamp_state ? "ON" : "OFF"); else rt_kprintf("Unknown: %s\n", argv[1]); } MSH_CMD_EXPORT(cmd_lamp, Control lamp); static void cmd_hv(int argc, char **argv) { if (argc < 2) { rt_kprintf("Usage: hv [on|off|toggle|status]\n"); return; } if (strcmp(argv[1], "on") == 0) gpio_hv_ctrl(RT_TRUE); else if (strcmp(argv[1], "off") == 0) gpio_hv_ctrl(RT_FALSE); else if (strcmp(argv[1], "toggle") == 0) gpio_hv_toggle(); else if (strcmp(argv[1], "status") == 0) rt_kprintf("HV: %s\n", s_hv_state ? "ON" : "OFF"); else rt_kprintf("Unknown: %s\n", argv[1]); } MSH_CMD_EXPORT(cmd_hv, Control HV output); static void cmd_gpio_status(int argc, char **argv) { rt_kprintf("\n=== GPIO STATUS ===\n"); rt_kprintf("LAMP (PB5): %s\n", s_lamp_state ? "ON" : "OFF"); rt_kprintf("HV (PB6): %s\n", s_hv_state ? "ON" : "OFF"); rt_kprintf("KeyA (PB4): %s\n", rt_pin_read(KEY_A_PIN) == PIN_LOW ? "PRESSED" : "RELEASED"); rt_kprintf("KeyB (PB3): %s\n", rt_pin_read(KEY_B_PIN) == PIN_LOW ? "PRESSED" : "RELEASED"); rt_kprintf("Power state: %s\n", dev_info.power_state ? "ON" : "OFF"); rt_kprintf("Light duration: %d hours\n", dev_info.light_work_duration); rt_kprintf("==================\n"); } MSH_CMD_EXPORT(cmd_gpio_status, Show GPIO status); static void cmd_test_combined_key(int argc, char **argv) { rt_kprintf("Simulating combined key long press...\n"); key_combined_long(); } MSH_CMD_EXPORT(cmd_test_combined_key, Test combined key long press); /*********************↑ MSH命令 ↑************************/