전원 모듈 분리
This commit is contained in:
@@ -0,0 +1,381 @@
|
||||
/*******************************************************************************
|
||||
* @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 BOOT_THRESHOLD 350 // power button hold threshold
|
||||
#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 == BOOT_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 >= BOOT_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);
|
||||
}
|
||||
|
||||
/* 소프트 리셋/DFU 리셋 후 버튼 없이 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);
|
||||
}
|
||||
Reference in New Issue
Block a user