/******************************************************************************* * @file power_control.c * @brief Device power sequence control ******************************************************************************/ #include #include #include #if IS_ENABLED(CONFIG_BT_SMP) #include #include #endif #include "main.h" #include "power_control.h" #include "debug_print.h" #include "led_control.h" #include "ble_service.h" #include "battery_adc.h" #define POWER_HOLD_NODE DT_NODELABEL(pwr_hold) // 전원 래치 #define POWER_BTN_NODE DT_NODELABEL(button_check) // 전원 버튼 #define POWER_LOOP_INTERVAL 20 // 전원 시퀀스 타이머 간격(ms) #define POWER_ON_THRESHOLD 350 // power button hold threshold(off -> on) #define POWER_OFF_THRESHOLD 500 // power button hold threshold(on -> off) #define BOND_RESET_THRESHOLD (15000 / POWER_ON_DELAY) // 15s long press bond reset static const struct gpio_dt_spec power_hold = GPIO_DT_SPEC_GET(POWER_HOLD_NODE, gpios); // 전원 유지 출력 static const struct gpio_dt_spec power_btn = GPIO_DT_SPEC_GET(POWER_BTN_NODE, gpios); // 전원 버튼 입력 static struct k_timer m_power_on_delay_timer; // 전원 버튼 상태머신 타이머 static struct k_timer m_power_off_delay_timer; // 전원 OFF 지연 타이머 static struct k_timer m_power_timer; // 전원 시퀀스 반복 타이머 static struct k_work adv_start_work; // advertising 시작 work static struct k_work adv_stop_work; // advertising 중지 work static struct k_work bond_reset_work; // 전원 버튼 15초 롱프레스 본드 삭제 work static uint16_t cnt_s; // power button hold counter static bool device_on; // power latch state static bool boot_btn_released; // 부팅 후 버튼을 놓았는지 여부 static bool power_btn_suspended; // 측정 중 버튼 상태머신 일시 정지 static const char *power_off_pending_reason = "none"; // 예약된 전원 OFF 사유 static bool bond_reset_pending; // 본드 삭제 work 중복 예약 방지 static uint8_t p_order; // 전원 시퀀스 단계 static bool lock_check = false; // power sequence lock flag /* 시스템 워크에서 BLE advertising 시작 */ static void adv_start_work_handler(struct k_work *work) { ARG_UNUSED(work); ble_advertising_start(); } /* 시스템 워크에서 BLE advertising 중지 */ static void adv_stop_work_handler(struct k_work *work) { ARG_UNUSED(work); ble_advertising_stop(); } /* 전원 버튼 15초 롱프레스: BLE 본드 삭제 후 리부팅 */ static void bond_reset_work_handler(struct k_work *work) { ARG_UNUSED(work); DBG_ERR("[BTN] 15s long press -> BLE bond reset\r\n"); battery_timer_stop(); ble_advertising_stop(); #if IS_ENABLED(CONFIG_BT_SMP) int err = bt_unpair(BT_ID_DEFAULT, NULL); if (err) { DBG_ERR("[BTN] bt_unpair failed err=%d\r\n", err); } else { bond_data_delete = true; DBG_PRINTF("[BTN] BLE bond data deleted\r\n"); } #else bond_data_delete = true; DBG_PRINTF("[BTN] BLE bond delete skipped (BT_SMP disabled)\r\n"); #endif led_set_state(LED_STATE_BOND_DELETE); k_msleep(1500); sys_reboot(SYS_REBOOT_COLD); } /* 부팅 직후 전원 래치를 유지할지 결정 */ void power_control_latch_init(bool keep_on) { gpio_pin_configure_dt(&power_hold, keep_on ? GPIO_OUTPUT_ACTIVE : GPIO_OUTPUT_INACTIVE); } /* 전원 버튼 상태머신 초기값 */ void power_control_reset_state(void) { cnt_s = 0; device_on = false; boot_btn_released = false; power_btn_suspended = false; bond_reset_pending = false; power_off_pending_reason = "none"; } /* POWER_HOLD 핀으로 실제 전원 래치 ON/OFF 제어 */ static void power_control_handler(on_off_cont_t device_power_st, const char *reason) { ARG_UNUSED(reason); if (device_power_st == OFF) { gpio_pin_set_dt(&power_hold, 0); } else { gpio_pin_set_dt(&power_hold, 1); } } /* 전원 버튼 GPIO 초기화 */ static void power_gpio_init(void) { gpio_pin_configure_dt(&power_btn, GPIO_INPUT); DBG_PRINTF("[INIT] HW - GPIO OK (POWER BTN=%d)\r\n", gpio_pin_get_dt(&power_btn)); } /* 전원 OFF 타임아웃 콜백 */ static void t_power_off_timeout_handler(struct k_timer *timer) { ARG_UNUSED(timer); DBG_ERR("[PWR] OFF timeout reason=%s\r\n", power_off_pending_reason); led_set_state(LED_STATE_OFF); power_control_handler(OFF, "off-timeout"); } /* sleep/전원 OFF: LED 표시 후 지연 시간 뒤 전원 차단 */ static void power_off_schedule(const char *reason, led_state_t led_state) { power_off_pending_reason = reason ? reason : "unknown"; led_set_state(led_state); k_timer_start(&m_power_off_delay_timer, K_MSEC(POWER_OFF_DELAY), K_NO_WAIT); } /* 사유를 포함한 sleep 모드 진입 요청 */ void sleep_mode_enter_reason(const char *reason) { power_off_schedule(reason, LED_STATE_POWER_ON); } /* 기본 sleep 모드 진입 요청 */ void sleep_mode_enter(void) { sleep_mode_enter_reason("sleep_mode_enter"); } /* 기본 전원 OFF 요청 */ void device_power_off(void) { device_power_off_reason("device_power_off"); } /* 사유를 포함한 전원 OFF 요청 */ void device_power_off_reason(const char *reason) { power_off_schedule(reason, LED_STATE_POWER_OFF); } /* 외부 명령 처리 후 전원 래치 유지 */ void device_power_keep_on(void) { k_timer_stop(&m_power_off_delay_timer); device_on = true; boot_btn_released = (gpio_pin_get_dt(&power_btn) != 1); cnt_s = 0; power_control_handler(ON, "keep-on"); } /* 긴 측정 중 전원 버튼 상태머신 일시 정지 */ void power_button_suspend(bool suspend) { power_btn_suspended = suspend; cnt_s = 0; } /* 전원 버튼 상태머신 */ static void main_s(struct k_timer *timer) { ARG_UNUSED(timer); // 버튼 입력은 눌림 상태가 1 bool button_pressed = (gpio_pin_get_dt(&power_btn) == 1); // 측정 중에는 버튼 판단을 멈추고 다음 주기에 다시 확인 if (power_btn_suspended) { k_timer_start(&m_power_on_delay_timer, K_MSEC(POWER_ON_DELAY), K_NO_WAIT); return; } // 전원 OFF 상태: 버튼을 충분히 누르면 전원 래치 if (!device_on) { if (!button_pressed) // 2초 미만으로 놓으면 전원 유지 실패 처리 { power_control_handler(OFF, "boot-button-released-before-latch"); cnt_s = 0; } else { cnt_s++; if (cnt_s == POWER_ON_THRESHOLD) // 임계 시간 도달 시 전원 ON 확정 { device_on = true; power_control_handler(ON, "button-2s-latch"); led_set_state(LED_STATE_ADVERTISING); k_work_submit(&adv_start_work); battery_timer_start(); m_reset_status = 1; } } } else { // 전원 ON 상태: 부팅 때 누른 버튼을 먼저 놓아야 새 입력으로 봄 if (!boot_btn_released) { if (!button_pressed) // 부팅 때 누른 버튼을 놓으면 이후 새 입력으로 판단 { boot_btn_released = true; cnt_s = 0; } else { cnt_s++; if ((cnt_s >= BOND_RESET_THRESHOLD) && !bond_reset_pending) // 부팅 버튼 15초 유지 시 본드 삭제 { bond_reset_pending = true; k_work_submit(&bond_reset_work); return; } } } else if (button_pressed) // ON 상태에서 새로 길게 누르면 전원 OFF { cnt_s++; if (cnt_s >= POWER_OFF_THRESHOLD) { battery_timer_stop(); k_work_submit(&adv_stop_work); device_on = false; boot_btn_released = false; cnt_s = 0; sleep_mode_enter_reason("button-long-press"); return; } } else { // 버튼을 놓으면 짧은 입력은 무시하고 카운터를 초기화한다. if (cnt_s > 0) { DBG_PRINTF("[BTN] Short press (%d) -> ignored\r\n", cnt_s); } cnt_s = 0; } } k_timer_start(&m_power_on_delay_timer, K_MSEC(POWER_ON_DELAY), K_NO_WAIT); } /* 전원 시퀀스 타이머 만료 콜백 */ static void power_loop_expiry(struct k_timer *timer) { power_loop(timer); } /* 슬립 진입 전 처리 중 플래그 정리 */ int device_sleep_mode(void) { k_msleep(2); DBG_PRINTF("Device_Sleep_Mode OK!\r\n"); k_msleep(10); processing = false; return 0; } /* 최초 전원 활성화 시퀀스 시작 */ int device_activated(void) { p_order = 0; lock_check = true; power_timer_start(); return 0; } /* 전원 활성화 단계 처리 - 필요 시 다음 타이머 예약 */ void power_loop(struct k_timer *timer) { power_timer_stop(); // 센서 초기화는 각 측정 함수에서 필요 시 수행 p_order = 2; if (p_order < 2) { p_order++; power_timer_start(); } else { DBG_PRINTF("[PWR] Device Activated OK!\r\n"); } } /* 소프트 리셋/복귀 후 전원 시퀀스 다시 시작 */ int device_reactivated(void) { // 필요 시 복귀 전용 초기화 추가 k_msleep(10); lock_check = true; p_order = 0; power_timer_start(); return 0; } /* 전원 시퀀스 타이머 1회 예약 */ void power_timer_start(void) { k_timer_start(&m_power_timer, K_MSEC(POWER_LOOP_INTERVAL), K_NO_WAIT); } /* 예약된 전원 시퀀스 타이머 중지 */ void power_timer_stop(void) { k_timer_stop(&m_power_timer); } /* 전원 시퀀스 타이머 콜백 등록 */ void power_timer_init(void) { k_timer_init(&m_power_timer, power_loop_expiry, NULL); } /* 전원 관련 타이머/work 초기화 */ void power_control_init(void) { power_gpio_init(); k_timer_init(&m_power_on_delay_timer, main_s, NULL); k_timer_init(&m_power_off_delay_timer, t_power_off_timeout_handler, NULL); k_work_init(&adv_start_work, adv_start_work_handler); k_work_init(&adv_stop_work, adv_stop_work_handler); k_work_init(&bond_reset_work, bond_reset_work_handler); power_timer_init(); } /* 전원 버튼 상태머신 시작 */ void power_control_start(void) { k_timer_start(&m_power_on_delay_timer, K_MSEC(POWER_ON_DELAY), K_NO_WAIT); } /* soft reset/DFU reset/test image 부팅 시 버튼 없이 정상 ON 상태로 복구 */ void power_control_resume_after_reset(uint32_t reset_reason) { device_on = true; cnt_s = 0; boot_btn_released = (gpio_pin_get_dt(&power_btn) != 1); m_reset_status = 1; power_control_handler(ON, "resume-after-reset"); led_set_state(LED_STATE_ADVERTISING); battery_timer_start(); k_work_submit(&adv_start_work); DBG_CORE("[BOOT] Resume after resetreas=0x%08x\r\n", reset_reason); }