전원 + BLE + 배터리
This commit is contained in:
+324
-32
@@ -1,48 +1,340 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Intel Corporation
|
||||
/*******************************************************************************
|
||||
* @file main.c
|
||||
* @brief VesiScan BASIC - Zephyr 포팅 (Stage 1: GPIO + 전원 + LED + BLE)
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
* 부팅 시퀀스:
|
||||
* Phase 1: 전원 유지 핀, GPIO, 타이머, LED 초기화
|
||||
* Phase 2: BLE 스택 + NUS 초기화, advertising 시작
|
||||
* Phase 3+: (이후 단계: 센서 등)
|
||||
*
|
||||
* 전원 버튼 상태머신 (5ms 폴링, k_timer):
|
||||
* [OFF→ON] 버튼 2초 이상 유지 → P0.08 래치 ON + 녹색 LED 깜빡임
|
||||
* 2초 미만 놓으면 전원 OFF (래치 안 됨)
|
||||
* [ON→OFF] 버튼 2초 이상 유지 → sleep_mode_enter() → 전원 차단
|
||||
******************************************************************************/
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/sys/printk.h>
|
||||
|
||||
/* 1000 msec = 1 sec */
|
||||
#define SLEEP_TIME_MS 1000
|
||||
#include "main.h"
|
||||
#include "debug_print.h"
|
||||
#include "power_control.h"
|
||||
#include "led_control.h"
|
||||
#include "ble_service.h"
|
||||
#include "battery_adc.h"
|
||||
#include "parser.h"
|
||||
|
||||
/* The devicetree node identifier for the "led0" alias. */
|
||||
#define LED0_NODE DT_ALIAS(led0)
|
||||
LOG_MODULE_REGISTER(vesiscan, LOG_LEVEL_INF);
|
||||
|
||||
/*==============================================================================
|
||||
* Devicetree GPOP Settings
|
||||
*============================================================================*/
|
||||
#define POWER_HOLD_NODE DT_NODELABEL(pwr_hold) // 전원 래치
|
||||
#define POWER_BTN_NODE DT_NODELABEL(button_check) // 전원 버튼
|
||||
|
||||
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);
|
||||
|
||||
/*==============================================================================
|
||||
* Timer
|
||||
*============================================================================*/
|
||||
static struct k_timer m_power_on_delay_timer; /* 전원 버튼 폴링용 (5ms 싱글샷) */
|
||||
static struct k_timer m_power_off_delay_timer; /* 전원 OFF 지연용 (3초 후 전원 차단) */
|
||||
|
||||
/*==============================================================================
|
||||
* 전역 변수
|
||||
*============================================================================*/
|
||||
volatile bool ble_connection_st = false; /* BLE 연결 상태 */
|
||||
volatile bool processing = false; /* 센서 데이터 처리 중 플래그 */
|
||||
bool bond_data_delete = true; /* 본딩 데이터 삭제 요청 */
|
||||
uint32_t m_life_cycle = 0; /* 디바이스 수명 카운터 */
|
||||
uint8_t m_reset_status = 1; /* 리셋 상태 코드 */
|
||||
char SERIAL_NO[SERIAL_NO_LENGTH]; /* 시리얼 번호 */
|
||||
char HW_NO[HW_NO_LENGTH]; /* 하드웨어 번호 */
|
||||
char m_static_passkey[PASSKEY_LENGTH]; /* BLE 정적 패스키 */
|
||||
|
||||
static uint16_t cnt_s; /* 전원 버튼 폴링 카운터 (5ms 단위) */
|
||||
static bool device_on = false; /* 디바이스 전원 상태 (래치 완료 여부) */
|
||||
static bool boot_btn_released = false; /* 부팅 후 버튼 놓았는지 여부 */
|
||||
|
||||
/*
|
||||
* A build error on this line means your board is unsupported.
|
||||
* See the sample documentation for information on how to fix this.
|
||||
* BLE advertising 제어용 워크 아이템
|
||||
*
|
||||
* BLE HCI 명령(bt_le_adv_start/stop 등)은 내부적으로 세마포어 대기가 필요하므로
|
||||
* 인터럽트 컨텍스트(타이머 콜백 등)에서 직접 호출하면 커널 oops가 발생한다.
|
||||
* (증상: "Controller unresponsive, command opcode 0x2006 timeout")
|
||||
*
|
||||
* k_work를 사용하면 시스템 워크큐 스레드에서 실행되므로 안전하게 BLE API 호출 가능.
|
||||
* main_s() 타이머 콜백에서 k_work_submit()으로 예약하여 사용한다.
|
||||
*/
|
||||
static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
|
||||
static struct k_work adv_start_work;
|
||||
static struct k_work adv_stop_work;
|
||||
|
||||
/* 시스템 워크큐에서 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();
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* BLE RX 콜백
|
||||
*============================================================================*/
|
||||
static void ble_rx_handler(const uint8_t *data, uint16_t len)
|
||||
{
|
||||
DBG_PRINTF("[BLE RX] %d bytes: ", len);
|
||||
for (uint16_t i = 0; i < len && i < 32; i++)
|
||||
{
|
||||
DBG_PRINTF("%02X ", data[i]);
|
||||
}
|
||||
if (len > 32)
|
||||
{
|
||||
DBG_PRINTF("...");
|
||||
}
|
||||
DBG_PRINTF("\r\n");
|
||||
dr_parser(data, len);
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* 전원 유지 (POWER_HOLD) 제어
|
||||
*============================================================================*/
|
||||
|
||||
/* 전원 유지 핀(P0.08) 초기화 - 아직 래치하지 않음
|
||||
* 버튼을 물리적으로 누르고 있는 동안만 전원 유지됨
|
||||
* 2초 후 main_s()에서 래치(HIGH) 설정 */
|
||||
static void power_hold_init(void)
|
||||
{
|
||||
gpio_pin_configure_dt(&power_hold, GPIO_OUTPUT_INACTIVE);
|
||||
}
|
||||
|
||||
/* 전원 ON/OFF 제어 - P0.08 핀으로 물리적 전원 래치/해제 */
|
||||
static void power_control_handler(on_off_cont_t device_power_st)
|
||||
{
|
||||
if (device_power_st == OFF) {
|
||||
gpio_pin_set_dt(&power_hold, 0); /* P0.08 LOW → 전원 래치 해제 → 전원 차단 */
|
||||
DBG_PRINTF("[PWR] OFF\r\n");
|
||||
} else {
|
||||
gpio_pin_set_dt(&power_hold, 1); /* P0.08 HIGH → 전원 유지 */
|
||||
DBG_PRINTF("[PWR] ON\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* GPIO 초기화
|
||||
*============================================================================*/
|
||||
static void gpio_init(void)
|
||||
{
|
||||
gpio_pin_configure_dt(&power_btn, GPIO_INPUT); /* 전원 버튼(P1.08) 입력 설정 */
|
||||
DBG_PRINTF("[GPIO] OK (BTN=%d)\r\n", gpio_pin_get_dt(&power_btn));
|
||||
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* 기본 설정값 로드
|
||||
*============================================================================*/
|
||||
static void load_default_config(void)
|
||||
{
|
||||
memset(SERIAL_NO, 0, SERIAL_NO_LENGTH);
|
||||
memcpy(SERIAL_NO, SERIAL_NUMBER, strlen(SERIAL_NUMBER));
|
||||
|
||||
memset(m_static_passkey, 0, PASSKEY_LENGTH);
|
||||
memcpy(m_static_passkey, DEFAULT_PASSKEY, PASSKEY_LENGTH);
|
||||
|
||||
m_reset_status = 1;
|
||||
bond_data_delete = true;
|
||||
|
||||
DBG_PRINTF("[CFG] Default (S/N=%s)\r\n", SERIAL_NO);
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* 전원 OFF 타임아웃 콜백
|
||||
* LED 표시 후 3초(POWER_OFF_DELAY) 경과 시 호출되어 물리적 전원 차단
|
||||
*============================================================================*/
|
||||
static void t_power_off_timeout_handler(struct k_timer *timer)
|
||||
{
|
||||
ARG_UNUSED(timer);
|
||||
DBG_PRINTF("[PWR] Off timeout\r\n");
|
||||
led_set_state(LED_STATE_OFF);
|
||||
power_control_handler(OFF);
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* 슬립 모드 / 전원 OFF
|
||||
* LED로 사용자에게 알린 후 3초 뒤 전원 차단
|
||||
*============================================================================*/
|
||||
void sleep_mode_enter(void)
|
||||
{
|
||||
led_set_state(LED_STATE_POWER_ON);
|
||||
DBG_PRINTF("[SYS] Sleep\r\n");
|
||||
k_timer_start(&m_power_off_delay_timer, K_MSEC(POWER_OFF_DELAY), K_NO_WAIT);
|
||||
}
|
||||
|
||||
void device_power_off(void)
|
||||
{
|
||||
led_set_state(LED_STATE_POWER_OFF);
|
||||
k_timer_start(&m_power_off_delay_timer, K_MSEC(POWER_OFF_DELAY), K_NO_WAIT);
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* 전원 버튼 상태머신 (5ms 폴링)
|
||||
*
|
||||
* [부팅 시퀀스] (device_on == false)
|
||||
* 1. 버튼 누름 → 전원 공급 → MCU 부팅 → 5ms마다 폴링 시작
|
||||
* 2. cnt_s < 400 (2초 미만)에서 놓으면 → 전원 OFF (래치 안 됨)
|
||||
* 3. cnt_s == 400 (2초) 도달 → P0.08 래치 ON + 녹색 LED 깜빡임
|
||||
* 4. 버튼 놓으면 → 깜빡임 유지, 폴링 계속
|
||||
*
|
||||
* [전원 OFF 시퀀스] (device_on == true)
|
||||
* 1. 버튼 2초 이상 누름 → sleep_mode_enter() → 전원 차단
|
||||
* 2. 버튼 2초 미만 놓으면 → 카운터 리셋, 무시
|
||||
*============================================================================*/
|
||||
#define BOOT_THRESHOLD 400 /* 5ms x 400 = 2초 */
|
||||
|
||||
static void main_s(struct k_timer *timer)
|
||||
{
|
||||
ARG_UNUSED(timer);
|
||||
|
||||
bool button_pressed = (gpio_pin_get_dt(&power_btn) == 1);
|
||||
|
||||
if (!device_on) /* ── 부팅 시퀀스 (OFF → ON) ── */
|
||||
{
|
||||
if (!button_pressed) /* 버튼 놓음 → 2초 미만이면 전원 OFF */
|
||||
{
|
||||
DBG_PRINTF("[BTN] Short press (%d) -> OFF\r\n", cnt_s);
|
||||
power_control_handler(OFF);
|
||||
cnt_s = 0;
|
||||
}
|
||||
else /* 버튼 계속 누르고 있음 */
|
||||
{
|
||||
cnt_s++;
|
||||
|
||||
if (cnt_s == BOOT_THRESHOLD) /* 2초 도달: 래치 + 부팅 완료 */
|
||||
{
|
||||
device_on = true;
|
||||
cnt_s = 0; /* 카운터 리셋: 안 하면 다음 틱에서 ON→OFF 분기가
|
||||
* cnt_s >= 400 조건을 즉시 만족하여 전원 OFF됨 */
|
||||
power_control_handler(ON);
|
||||
led_set_state(LED_STATE_ADVERTISING);
|
||||
k_work_submit(&adv_start_work);
|
||||
battery_timer_start();
|
||||
m_reset_status = 1;
|
||||
DBG_PRINTF("[BTN] 2s -> Power latched, LED blink\r\n");
|
||||
DBG_PRINTF("[BOOT] Complete, device ON\r\n");
|
||||
DBG_PRINTF("[DEV] device_on=%d\r\n", device_on);
|
||||
}
|
||||
}
|
||||
}
|
||||
else /* ── 전원 OFF 시퀀스 (ON → OFF) ── */
|
||||
{
|
||||
if (!boot_btn_released) /* 부팅 시 눌렀던 버튼을 아직 안 놓음 → 대기 */
|
||||
{
|
||||
if (!button_pressed)
|
||||
{
|
||||
boot_btn_released = true;
|
||||
DBG_PRINTF("[BTN] Boot button released\r\n");
|
||||
}
|
||||
}
|
||||
else if (button_pressed) /* 버튼 새로 누르고 있음 */
|
||||
{
|
||||
cnt_s++;
|
||||
|
||||
if (cnt_s >= BOOT_THRESHOLD) /* 2초 이상 → 전원 OFF */
|
||||
{
|
||||
DBG_PRINTF("[BTN] 2s long press -> Power OFF\r\n");
|
||||
battery_timer_stop();
|
||||
k_work_submit(&adv_stop_work);
|
||||
device_on = false;
|
||||
boot_btn_released = false;
|
||||
cnt_s = 0;
|
||||
sleep_mode_enter();
|
||||
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 timers_init(void)
|
||||
{
|
||||
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);
|
||||
power_timer_init();
|
||||
}
|
||||
|
||||
/* 전원 버튼 폴링 시작 (5ms 후 main_s 콜백) */
|
||||
static void timers_start(void)
|
||||
{
|
||||
k_timer_start(&m_power_on_delay_timer, K_MSEC(POWER_ON_DELAY), K_NO_WAIT);
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* 메인 함수
|
||||
*============================================================================*/
|
||||
int main(void)
|
||||
{
|
||||
int ret;
|
||||
bool led_state = true;
|
||||
/*── Phase 1: 하드웨어 기본 초기화 ──*/
|
||||
power_hold_init();
|
||||
cnt_s = 0;
|
||||
|
||||
if (!gpio_is_ready_dt(&led)) {
|
||||
return 0;
|
||||
}
|
||||
DBG_PRINTF("\r\n========================================\r\n");
|
||||
DBG_PRINTF(" VesiScan BASIC %s (Zephyr)\r\n", FIRMWARE_VERSION);
|
||||
DBG_PRINTF("========================================\r\n");
|
||||
|
||||
ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
|
||||
if (ret < 0) {
|
||||
return 0;
|
||||
}
|
||||
DBG_PRINTF("[1] HW Init\r\n");
|
||||
gpio_init();
|
||||
timers_init();
|
||||
load_default_config();
|
||||
led_init();
|
||||
battery_adc_init();
|
||||
battery_timer_init();
|
||||
DBG_PRINTF(" gpio/timer/config/led/batt OK\r\n");
|
||||
|
||||
while (1) {
|
||||
ret = gpio_pin_toggle_dt(&led);
|
||||
if (ret < 0) {
|
||||
return 0;
|
||||
}
|
||||
/*── Phase 2: BLE 스택 + NUS ──*/
|
||||
DBG_PRINTF("[2] BLE Init\r\n");
|
||||
if (ble_service_init(ble_rx_handler) == 0)
|
||||
{
|
||||
DBG_PRINTF(" ble/nus OK\r\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG_PRINTF(" ble FAIL\r\n");
|
||||
}
|
||||
|
||||
led_state = !led_state;
|
||||
printf("LED state: %s\n", led_state ? "ON" : "OFF");
|
||||
k_msleep(SLEEP_TIME_MS);
|
||||
}
|
||||
return 0;
|
||||
/*── Phase 3: FDS/NVS (TODO) ──*/
|
||||
/*── Phase 4: 애플리케이션 (TODO) ──*/
|
||||
|
||||
DBG_PRINTF("\r\n========================================\r\n");
|
||||
DBG_PRINTF(" READY [%s]\r\n", SERIAL_NO);
|
||||
DBG_PRINTF("========================================\r\n\r\n");
|
||||
|
||||
/* 전원 버튼 상태머신 시작 (부팅 시 버튼이 눌려있는 상태) */
|
||||
timers_start();
|
||||
|
||||
/* 메인 루프 - idle */
|
||||
for (;;) {
|
||||
k_msleep(100);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user