Files
VesiScan-Basic_Zephyr/src/power/power_control.c
T
jh.chun abb8d6078a 전원 버튼 ON/OFF 임계 카운트 분리
- 버튼 감지 타이밍이 초기화 등으로 런타임과 달라서 체감상 3초로 맞추기 위해 분리
2026-07-21 15:51:25 +09:00

382 lines
11 KiB
C

/*******************************************************************************
* @file power_control.c
* @brief Device power sequence control
******************************************************************************/
#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/sys/reboot.h>
#if IS_ENABLED(CONFIG_BT_SMP)
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/conn.h>
#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);
}