Compare commits
12 Commits
ca9d3e1a03
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 41137a1c72 | |||
| 79b5e2b7ce | |||
| 4ecec2f0f2 | |||
| abb8d6078a | |||
| b6d79ad16a | |||
| 0fc578fb50 | |||
| d3513e3985 | |||
| 14fba03b62 | |||
| e3225215c5 | |||
| b086b0f366 | |||
| b8fa7831c7 | |||
| a83c9d11fa |
+7
-2
@@ -10,6 +10,9 @@ target_include_directories(app PRIVATE
|
|||||||
src/command
|
src/command
|
||||||
src/command/handlers
|
src/command/handlers
|
||||||
src/ble
|
src/ble
|
||||||
|
src/power
|
||||||
|
src/dfu
|
||||||
|
src/nvs
|
||||||
src/drivers/battery
|
src/drivers/battery
|
||||||
src/drivers/echo_adc
|
src/drivers/echo_adc
|
||||||
src/drivers/led
|
src/drivers/led
|
||||||
@@ -20,15 +23,17 @@ target_include_directories(app PRIVATE
|
|||||||
|
|
||||||
target_sources(app PRIVATE
|
target_sources(app PRIVATE
|
||||||
src/main.c
|
src/main.c
|
||||||
src/app_nvs.c
|
src/nvs/app_nvs.c
|
||||||
src/command/parser.c
|
src/command/parser.c
|
||||||
|
src/command/ble_cmd_queue.c
|
||||||
src/command/cmd_common.c
|
src/command/cmd_common.c
|
||||||
src/command/cmd_table.c
|
src/command/cmd_table.c
|
||||||
src/command/handlers/cmd_device.c
|
src/command/handlers/cmd_device.c
|
||||||
src/command/handlers/cmd_info.c
|
src/command/handlers/cmd_info.c
|
||||||
src/command/handlers/cmd_piezo.c
|
src/command/handlers/cmd_piezo.c
|
||||||
src/command/handlers/cmd_sensor.c
|
src/command/handlers/cmd_sensor.c
|
||||||
src/power_control.c
|
src/power/power_control.c
|
||||||
|
src/dfu/dfu_manager.c
|
||||||
src/measurement/piezo_measure.c
|
src/measurement/piezo_measure.c
|
||||||
src/ble/ble_service.c
|
src/ble/ble_service.c
|
||||||
src/ble/ble_tx_power.c
|
src/ble/ble_tx_power.c
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ CONFIG_BOOTLOADER_MCUBOOT=y
|
|||||||
# Enable zcbor library used by mcumgr/SMP/DFU CBOR payloads.
|
# Enable zcbor library used by mcumgr/SMP/DFU CBOR payloads.
|
||||||
CONFIG_ZCBOR=y
|
CONFIG_ZCBOR=y
|
||||||
# Version string used by MCUboot imgtool signing and versioned DFU zip naming.
|
# Version string used by MCUboot imgtool signing and versioned DFU zip naming.
|
||||||
CONFIG_MCUBOOT_IMGTOOL_SIGN_VERSION="0.0.2+26"
|
CONFIG_MCUBOOT_IMGTOOL_SIGN_VERSION="0.0.0+0"
|
||||||
|
|
||||||
# Internal flash
|
# Internal flash
|
||||||
# Enable internal flash driver.
|
# Enable internal flash driver.
|
||||||
@@ -182,8 +182,3 @@ CONFIG_MCUMGR_GRP_OS=y
|
|||||||
CONFIG_IMG_MANAGER=y
|
CONFIG_IMG_MANAGER=y
|
||||||
# Write uploaded image data to flash as a stream.
|
# Write uploaded image data to flash as a stream.
|
||||||
CONFIG_STREAM_FLASH=y
|
CONFIG_STREAM_FLASH=y
|
||||||
|
|
||||||
# Netcore HCI IPC
|
|
||||||
# nRF52840 build does not use network core HCI IPC.
|
|
||||||
#CONFIG_SB_CONFIG_NETCORE_HCI_IPC=y
|
|
||||||
#SB_CONFIG_NETCORE_HCI_IPC=y
|
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* @file ble_cmd_queue.c
|
||||||
|
* @brief BLE RX command queue
|
||||||
|
******************************************************************************/
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
|
||||||
|
#include "ble_cmd_queue.h"
|
||||||
|
#include "debug_print.h"
|
||||||
|
#include "parser.h"
|
||||||
|
|
||||||
|
#define BLE_CMD_MAX_LEN 256
|
||||||
|
#define BLE_CMD_QUEUE_DEPTH 8 // RX command ring buffer depth
|
||||||
|
#define BLE_CMD_WORKQ_STACK_SZ 8192
|
||||||
|
#define BLE_CMD_WORKQ_PRIORITY 10
|
||||||
|
|
||||||
|
static struct k_work_q ble_cmd_work_q;
|
||||||
|
K_THREAD_STACK_DEFINE(ble_cmd_workq_stack, BLE_CMD_WORKQ_STACK_SZ);
|
||||||
|
static struct k_work ble_cmd_work; // BLE command worker
|
||||||
|
static struct k_spinlock ble_cmd_lock; // RX queue lock
|
||||||
|
|
||||||
|
static uint8_t ble_cmd_q_buf[BLE_CMD_QUEUE_DEPTH][BLE_CMD_MAX_LEN]; // command data
|
||||||
|
static uint16_t ble_cmd_q_len[BLE_CMD_QUEUE_DEPTH]; // command length
|
||||||
|
static uint8_t ble_cmd_q_head; // next pop slot
|
||||||
|
static uint8_t ble_cmd_q_tail; // next push slot
|
||||||
|
static uint8_t ble_cmd_q_count; // queued command count
|
||||||
|
|
||||||
|
/* Queue에 쌓인 BLE 명령을 parser로 전달 */
|
||||||
|
static void ble_cmd_work_handler(struct k_work *work)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(work);
|
||||||
|
|
||||||
|
uint8_t local_buf[BLE_CMD_MAX_LEN];
|
||||||
|
uint16_t local_len;
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
k_spinlock_key_t key = k_spin_lock(&ble_cmd_lock);
|
||||||
|
if (ble_cmd_q_count == 0U)
|
||||||
|
{
|
||||||
|
k_spin_unlock(&ble_cmd_lock, key);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
local_len = ble_cmd_q_len[ble_cmd_q_head];
|
||||||
|
memcpy(local_buf, ble_cmd_q_buf[ble_cmd_q_head], local_len);
|
||||||
|
ble_cmd_q_head = (uint8_t)((ble_cmd_q_head + 1U) % BLE_CMD_QUEUE_DEPTH);
|
||||||
|
ble_cmd_q_count--;
|
||||||
|
k_spin_unlock(&ble_cmd_lock, key);
|
||||||
|
|
||||||
|
if (local_len == 0U)
|
||||||
|
{
|
||||||
|
DBG_ERR("[BLE RX] worker: empty\r\n");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ble_cmd_dispatch(local_buf, local_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* BLE RX 명령 처리 queue 초기화 */
|
||||||
|
void ble_cmd_queue_init(void)
|
||||||
|
{
|
||||||
|
k_work_init(&ble_cmd_work, ble_cmd_work_handler);
|
||||||
|
k_work_queue_start(&ble_cmd_work_q, ble_cmd_workq_stack, K_THREAD_STACK_SIZEOF(ble_cmd_workq_stack), BLE_CMD_WORKQ_PRIORITY, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* BLE RX 콜백에서 호출되는 명령 enqueue 함수 */
|
||||||
|
void ble_cmd_queue_rx_handler(const uint8_t *data, uint16_t len)
|
||||||
|
{
|
||||||
|
if (len > BLE_CMD_MAX_LEN)
|
||||||
|
{
|
||||||
|
DBG_ERR("[BLE RX] drop: len=%u exceeds %d\r\n", len, BLE_CMD_MAX_LEN);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
k_spinlock_key_t key = k_spin_lock(&ble_cmd_lock);
|
||||||
|
if (ble_cmd_q_count >= BLE_CMD_QUEUE_DEPTH)
|
||||||
|
{
|
||||||
|
k_spin_unlock(&ble_cmd_lock, key);
|
||||||
|
DBG_ERR("[BLE RX] drop: queue full\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(ble_cmd_q_buf[ble_cmd_q_tail], data, len);
|
||||||
|
ble_cmd_q_len[ble_cmd_q_tail] = len;
|
||||||
|
ble_cmd_q_tail = (uint8_t)((ble_cmd_q_tail + 1U) % BLE_CMD_QUEUE_DEPTH);
|
||||||
|
ble_cmd_q_count++;
|
||||||
|
k_spin_unlock(&ble_cmd_lock, key);
|
||||||
|
|
||||||
|
int err = k_work_submit_to_queue(&ble_cmd_work_q, &ble_cmd_work);
|
||||||
|
if (err < 0)
|
||||||
|
{
|
||||||
|
DBG_ERR("[BLE RX] queue submit fail err=%d\r\n", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* @file ble_cmd_queue.h
|
||||||
|
* @brief BLE RX command queue
|
||||||
|
******************************************************************************/
|
||||||
|
#ifndef BLE_CMD_QUEUE_H__
|
||||||
|
#define BLE_CMD_QUEUE_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
void ble_cmd_queue_init(void);
|
||||||
|
void ble_cmd_queue_rx_handler(const uint8_t *data, uint16_t len);
|
||||||
|
|
||||||
|
#endif /* BLE_CMD_QUEUE_H__ */
|
||||||
@@ -277,7 +277,7 @@ int cmd_maa(const uint8_t *data, uint8_t data_len)
|
|||||||
|
|
||||||
if (status == ECHO_STATUS_OK) // Piezo GPIO, power, ADC initialization/wake succeeded
|
if (status == ECHO_STATUS_OK) // Piezo GPIO, power, ADC initialization/wake succeeded
|
||||||
{
|
{
|
||||||
DBG_PRINTF("[MBB] piezo sweep start\r\n");
|
DBG_PRINTF("[MAA] piezo sweep start\r\n");
|
||||||
status = piezo_measure_sweep();
|
status = piezo_measure_sweep();
|
||||||
if (status == ECHO_STATUS_OK)
|
if (status == ECHO_STATUS_OK)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,238 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* @file dfu_manager.c
|
||||||
|
* @brief DFU state, watchdog, and MCUboot image control
|
||||||
|
******************************************************************************/
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <zephyr/dfu/mcuboot.h>
|
||||||
|
#include <zephyr/mgmt/mcumgr/mgmt/callbacks.h>
|
||||||
|
#include <hal/nrf_power.h>
|
||||||
|
|
||||||
|
#include "main.h"
|
||||||
|
#include "dfu_manager.h"
|
||||||
|
#include "debug_print.h"
|
||||||
|
#include "led_control.h"
|
||||||
|
#include "ble_service.h"
|
||||||
|
|
||||||
|
#define DFU_STALL_TIMEOUT_SEC 30 // DFU upload stall disconnect threshold
|
||||||
|
#define IMG_MGMT_ID_STATE 0U
|
||||||
|
#define IMG_MGMT_ID_UPLOAD 1U
|
||||||
|
|
||||||
|
static bool dfu_led_active; // DFU upload green LED state
|
||||||
|
static struct k_work_delayable dfu_watchdog_work; // DFU upload stall watchdog
|
||||||
|
|
||||||
|
static void dfu_watchdog_handler(struct k_work *work);
|
||||||
|
|
||||||
|
static enum mgmt_cb_return dfu_status_cb(uint32_t event,
|
||||||
|
enum mgmt_cb_return prev_status,
|
||||||
|
int32_t *rc,
|
||||||
|
uint16_t *group,
|
||||||
|
bool *abort_more,
|
||||||
|
void *data,
|
||||||
|
size_t data_size);
|
||||||
|
|
||||||
|
static enum mgmt_cb_return smp_cmd_status_cb(uint32_t event,
|
||||||
|
enum mgmt_cb_return prev_status,
|
||||||
|
int32_t *rc,
|
||||||
|
uint16_t *group,
|
||||||
|
bool *abort_more,
|
||||||
|
void *data,
|
||||||
|
size_t data_size);
|
||||||
|
|
||||||
|
static struct mgmt_callback dfu_status_callback = {
|
||||||
|
.callback = dfu_status_cb,
|
||||||
|
.event_id = MGMT_EVT_OP_IMG_MGMT_DFU_STARTED |
|
||||||
|
MGMT_EVT_OP_IMG_MGMT_DFU_CHUNK |
|
||||||
|
MGMT_EVT_OP_IMG_MGMT_DFU_CHUNK_WRITE_COMPLETE |
|
||||||
|
MGMT_EVT_OP_IMG_MGMT_DFU_PENDING |
|
||||||
|
MGMT_EVT_OP_IMG_MGMT_DFU_STOPPED,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct mgmt_callback smp_cmd_status_callback = {
|
||||||
|
.callback = smp_cmd_status_cb,
|
||||||
|
.event_id = MGMT_EVT_OP_CMD_DONE,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* DFU watchdog와 mcumgr 콜백 초기화 */
|
||||||
|
void dfu_manager_init(void)
|
||||||
|
{
|
||||||
|
k_work_init_delayable(&dfu_watchdog_work, dfu_watchdog_handler);
|
||||||
|
mgmt_callback_register(&dfu_status_callback);
|
||||||
|
mgmt_callback_register(&smp_cmd_status_callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 현재 이미지가 confirm 대기 상태인지 확인 */
|
||||||
|
bool dfu_is_confirm_pending_boot(void)
|
||||||
|
{
|
||||||
|
#if defined(CONFIG_BOOTLOADER_MCUBOOT)
|
||||||
|
return !boot_is_img_confirmed();
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 실행 중 이미지 confirm 처리 */
|
||||||
|
void dfu_confirm_running_image(void)
|
||||||
|
{
|
||||||
|
#if defined(CONFIG_BOOTLOADER_MCUBOOT)
|
||||||
|
if (!boot_is_img_confirmed())
|
||||||
|
{
|
||||||
|
int err = boot_write_img_confirmed();
|
||||||
|
if (err == 0)
|
||||||
|
{
|
||||||
|
DBG_CORE("[DFU] Running image confirmed\r\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DBG_ERR("[DFU] Image confirm failed (err %d)\r\n", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DBG_CORE("[DFU] Running image already confirmed\r\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* MCUboot swap 상태 문자열 변환 */
|
||||||
|
static const char *swap_type_to_str(int swap_type)
|
||||||
|
{
|
||||||
|
switch (swap_type)
|
||||||
|
{
|
||||||
|
case BOOT_SWAP_TYPE_NONE:
|
||||||
|
return "none";
|
||||||
|
case BOOT_SWAP_TYPE_TEST:
|
||||||
|
return "test";
|
||||||
|
case BOOT_SWAP_TYPE_PERM:
|
||||||
|
return "perm";
|
||||||
|
case BOOT_SWAP_TYPE_REVERT:
|
||||||
|
return "revert";
|
||||||
|
case BOOT_SWAP_TYPE_FAIL:
|
||||||
|
return "fail";
|
||||||
|
default:
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* MCUboot 이미지 상태 로그 출력 */
|
||||||
|
void dfu_log_mcuboot_state(void)
|
||||||
|
{
|
||||||
|
#if defined(CONFIG_BOOTLOADER_MCUBOOT)
|
||||||
|
struct mcuboot_img_header header;
|
||||||
|
uint8_t active_slot = boot_fetch_active_slot();
|
||||||
|
int swap_type = mcuboot_swap_type();
|
||||||
|
int err = boot_read_bank_header(active_slot, &header, sizeof(header));
|
||||||
|
|
||||||
|
DBG_CORE("[DFU] active_slot=%u swap_type=%s(%d) confirmed=%d\r\n",
|
||||||
|
active_slot,
|
||||||
|
swap_type_to_str(swap_type),
|
||||||
|
swap_type,
|
||||||
|
boot_is_img_confirmed());
|
||||||
|
|
||||||
|
if (err == 0)
|
||||||
|
{
|
||||||
|
DBG_CORE("[DFU] image version %u.%u.%u+%u size=0x%x\r\n",
|
||||||
|
header.h.v1.sem_ver.major,
|
||||||
|
header.h.v1.sem_ver.minor,
|
||||||
|
header.h.v1.sem_ver.revision,
|
||||||
|
header.h.v1.sem_ver.build_num,
|
||||||
|
header.h.v1.image_size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DBG_ERR("[DFU] image header read failed (slot %u, err %d)\r\n",
|
||||||
|
active_slot,
|
||||||
|
err);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DFU 업로드 무진행 시 BLE 연결 강제 해제 */
|
||||||
|
static void dfu_watchdog_handler(struct k_work *work)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(work);
|
||||||
|
|
||||||
|
if (ble_is_connected())
|
||||||
|
{
|
||||||
|
DBG_CORE("[DFU] upload stalled (%ds), forcing disconnect\r\n", DFU_STALL_TIMEOUT_SEC);
|
||||||
|
(void)ble_disconnect_active();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DFU 업로드 상태에 따라 LED와 watchdog 제어 */
|
||||||
|
static enum mgmt_cb_return dfu_status_cb(uint32_t event, enum mgmt_cb_return prev_status, int32_t *rc, uint16_t *group, bool *abort_more, void *data, size_t data_size)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(prev_status);
|
||||||
|
ARG_UNUSED(rc);
|
||||||
|
ARG_UNUSED(group);
|
||||||
|
ARG_UNUSED(abort_more);
|
||||||
|
ARG_UNUSED(data);
|
||||||
|
ARG_UNUSED(data_size);
|
||||||
|
|
||||||
|
switch (event)
|
||||||
|
{
|
||||||
|
case MGMT_EVT_OP_IMG_MGMT_DFU_STARTED:
|
||||||
|
case MGMT_EVT_OP_IMG_MGMT_DFU_CHUNK:
|
||||||
|
case MGMT_EVT_OP_IMG_MGMT_DFU_CHUNK_WRITE_COMPLETE:
|
||||||
|
dfu_led_active = true;
|
||||||
|
led_ble_solid();
|
||||||
|
k_work_reschedule(&dfu_watchdog_work, K_SECONDS(DFU_STALL_TIMEOUT_SEC));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MGMT_EVT_OP_IMG_MGMT_DFU_PENDING:
|
||||||
|
dfu_led_active = true;
|
||||||
|
led_ble_solid();
|
||||||
|
k_work_cancel_delayable(&dfu_watchdog_work);
|
||||||
|
|
||||||
|
#if NRF_POWER_HAS_GPREGRET
|
||||||
|
nrf_power_gpregret_set(NRF_POWER, DFU_RESUME_GPREGRET_REG, DFU_RESUME_MAGIC);
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MGMT_EVT_OP_IMG_MGMT_DFU_STOPPED:
|
||||||
|
k_work_cancel_delayable(&dfu_watchdog_work);
|
||||||
|
if (dfu_led_active)
|
||||||
|
{
|
||||||
|
dfu_led_active = false;
|
||||||
|
led_set_state(LED_STATE_ADVERTISING);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MGMT_CB_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* DFU 명령 실패 시 LED와 resume 플래그 복구 */
|
||||||
|
static enum mgmt_cb_return smp_cmd_status_cb(uint32_t event, enum mgmt_cb_return prev_status, int32_t *rc, uint16_t *group, bool *abort_more, void *data, size_t data_size)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(event);
|
||||||
|
ARG_UNUSED(prev_status);
|
||||||
|
ARG_UNUSED(rc);
|
||||||
|
ARG_UNUSED(group);
|
||||||
|
ARG_UNUSED(abort_more);
|
||||||
|
|
||||||
|
if (data == NULL || data_size != sizeof(struct mgmt_evt_op_cmd_arg))
|
||||||
|
{
|
||||||
|
return MGMT_CB_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct mgmt_evt_op_cmd_arg *cmd = data;
|
||||||
|
|
||||||
|
if (cmd->group == MGMT_GROUP_ID_IMAGE &&
|
||||||
|
(cmd->id == IMG_MGMT_ID_UPLOAD || cmd->id == IMG_MGMT_ID_STATE) &&
|
||||||
|
cmd->err != MGMT_ERR_EOK)
|
||||||
|
{
|
||||||
|
DBG_ERR("[DFU] command failed id=%u err=%d\r\n", cmd->id, cmd->err);
|
||||||
|
|
||||||
|
dfu_led_active = false;
|
||||||
|
led_set_state(LED_STATE_ADVERTISING);
|
||||||
|
|
||||||
|
#if NRF_POWER_HAS_GPREGRET
|
||||||
|
nrf_power_gpregret_set(NRF_POWER, DFU_RESUME_GPREGRET_REG, 0U);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return MGMT_CB_OK;
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* @file dfu_manager.h
|
||||||
|
* @brief DFU state, watchdog, and MCUboot image control
|
||||||
|
******************************************************************************/
|
||||||
|
#ifndef DFU_MANAGER_H__
|
||||||
|
#define DFU_MANAGER_H__
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#define DFU_RESUME_MAGIC 0xD5U
|
||||||
|
#define DFU_RESUME_GPREGRET_REG 1U
|
||||||
|
|
||||||
|
void dfu_manager_init(void);
|
||||||
|
bool dfu_is_confirm_pending_boot(void);
|
||||||
|
void dfu_confirm_running_image(void);
|
||||||
|
void dfu_log_mcuboot_state(void);
|
||||||
|
|
||||||
|
#endif /* DFU_MANAGER_H__ */
|
||||||
@@ -688,7 +688,6 @@ int imu_fifo_read_latest(uint8_t *sample_bytes,
|
|||||||
|
|
||||||
{
|
{
|
||||||
record_count = imu_fifo_compact_records(record_count);
|
record_count = imu_fifo_compact_records(record_count);
|
||||||
DBG_PRINTF("[IMU FIFO] compact %u -> %u\r\n", raw_count, record_count);
|
|
||||||
}
|
}
|
||||||
if (record_count > max_samples)
|
if (record_count > max_samples)
|
||||||
{
|
{
|
||||||
|
|||||||
+47
-672
@@ -1,66 +1,31 @@
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* @file main.c
|
* @file main.c
|
||||||
* @brief VesiScan-Basic
|
* @brief VesiScan-Basic
|
||||||
|
*
|
||||||
|
* - 앱 공통 전역 상태 보관
|
||||||
|
* - 부팅 직후 전원 유지 판단
|
||||||
|
* - NVS 로드 전 기본값 초기화
|
||||||
|
* - 각 모듈 초기화 순서 조립
|
||||||
|
* - main loop 유지
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
#include <zephyr/kernel.h>
|
#include <zephyr/kernel.h>
|
||||||
#include <zephyr/drivers/gpio.h>
|
|
||||||
#include <zephyr/sys/printk.h>
|
#include <zephyr/sys/printk.h>
|
||||||
#include <zephyr/sys/reboot.h>
|
|
||||||
#include <zephyr/dfu/mcuboot.h>
|
|
||||||
#include <zephyr/mgmt/mcumgr/mgmt/callbacks.h>
|
|
||||||
#include <hal/nrf_power.h>
|
#include <hal/nrf_power.h>
|
||||||
#if IS_ENABLED(CONFIG_BT_SMP)
|
|
||||||
#include <zephyr/bluetooth/bluetooth.h>
|
|
||||||
#include <zephyr/bluetooth/conn.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "debug_print.h"
|
#include "debug_print.h"
|
||||||
#include "power_control.h"
|
#include "power_control.h"
|
||||||
|
#include "dfu_manager.h"
|
||||||
#include "led_control.h"
|
#include "led_control.h"
|
||||||
#include "ble_service.h"
|
#include "ble_service.h"
|
||||||
#include "battery_adc.h"
|
#include "battery_adc.h"
|
||||||
#include "parser.h"
|
#include "ble_cmd_queue.h"
|
||||||
#include "imu_i2c.h"
|
#include "imu_i2c.h"
|
||||||
#include "piezo_measure.h"
|
#include "piezo_measure.h"
|
||||||
|
|
||||||
LOG_MODULE_REGISTER(vesiscan, LOG_LEVEL_INF);
|
LOG_MODULE_REGISTER(vesiscan, LOG_LEVEL_INF);
|
||||||
|
|
||||||
#define BLE_CMD_MAX_LEN 256
|
|
||||||
#define BLE_CMD_QUEUE_DEPTH 8 // RX 명령 링버퍼 깊이(버스트 흡수)
|
|
||||||
#define BLE_CMD_WORKQ_STACK_SZ 8192
|
|
||||||
#define DFU_RESUME_MAGIC 0xD5U
|
|
||||||
#define DFU_RESUME_GPREGRET_REG 1U
|
|
||||||
#define DFU_STALL_TIMEOUT_SEC 30 // DFU 업로드 무진행 시 강제 disconnect 임계
|
|
||||||
#define IMG_MGMT_ID_STATE 0U
|
|
||||||
#define IMG_MGMT_ID_UPLOAD 1U
|
|
||||||
/*
|
|
||||||
* BLE 명령 처리 스택
|
|
||||||
* - piezo sweep
|
|
||||||
* - ADC capture
|
|
||||||
* - BLE 응답 패킷 조립
|
|
||||||
* - 디버깅 여유분 포함
|
|
||||||
*/
|
|
||||||
#define BLE_CMD_WORKQ_PRIORITY 10
|
|
||||||
|
|
||||||
/* Devicetree GPIO Settings */
|
|
||||||
#define POWER_HOLD_NODE DT_NODELABEL(pwr_hold) // 전원 래치
|
|
||||||
#define POWER_BTN_NODE DT_NODELABEL(button_check) // 전원 버튼
|
|
||||||
|
|
||||||
/* 전원 버튼 상태머신 (5ms 폴링) */
|
|
||||||
#define BOOT_THRESHOLD 350 // 5ms x 400 = 2초 -> 초기화 시간 고려 체감상 2초에 맞춤
|
|
||||||
#define BOND_RESET_THRESHOLD (15000 / POWER_ON_DELAY) // 전원 ON 상태에서 15초 롱프레스 → 본드 삭제
|
|
||||||
|
|
||||||
/* 전원 제어 GPIO */
|
|
||||||
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 ble_connection_st = false; // BLE 연결 상태
|
||||||
volatile bool processing = false; // 센서 데이터 처리 중 플래그
|
volatile bool processing = false; // 센서 데이터 처리 중 플래그
|
||||||
bool bond_data_delete = true; // 본딩 데이터 삭제 요청
|
bool bond_data_delete = true; // 본딩 데이터 삭제 요청
|
||||||
@@ -71,197 +36,23 @@ char HW_NO[HW_NO_BUF_SIZE]; // 하드웨어 번호
|
|||||||
char m_static_passkey[PASSKEY_BUF_SIZE]; // BLE 정적 패스키
|
char m_static_passkey[PASSKEY_BUF_SIZE]; // BLE 정적 패스키
|
||||||
uint8_t m_passkey_changed; // 패스키 변경은 출고 후 1회만 허용
|
uint8_t m_passkey_changed; // 패스키 변경은 출고 후 1회만 허용
|
||||||
|
|
||||||
static uint16_t cnt_s; // 전원 버튼 폴링 카운터 (5ms 단위)
|
|
||||||
static bool device_on = false; // 디바이스 전원 상태 (래치 완료 여부)
|
|
||||||
static bool boot_btn_released = false; // 부팅 후 버튼 놓았는지 여부
|
|
||||||
static bool power_btn_suspended; // 측정 중 전원 버튼 상태머신 일시 정지
|
|
||||||
|
|
||||||
/*
|
|
||||||
* BLE advertising 제어용 워크 아이템
|
|
||||||
* - main_s()에서 k_work_submit()으로 예약하여 사용
|
|
||||||
*/
|
|
||||||
static bool resume_without_power_button; // 리셋 후 버튼 없이 복귀
|
static bool resume_without_power_button; // 리셋 후 버튼 없이 복귀
|
||||||
static bool dfu_confirm_pending_boot; // DFU test 이미지 확인 전 자동 복귀
|
static bool dfu_confirm_pending_boot; // DFU test 이미지 확인 전 자동 복귀
|
||||||
static bool dfu_reset_resume_request; // DFU 완료 리셋 후 자동 복귀
|
static bool dfu_reset_resume_request; // DFU 완료 리셋 후 자동 복귀
|
||||||
static uint32_t boot_reset_reason; // RESETREAS 원본 값
|
static uint32_t boot_reset_reason; // RESETREAS 원본 값
|
||||||
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 bool dfu_led_active; // DFU 업로드 중 초록 LED ON
|
|
||||||
static const char *power_off_pending_reason = "none";
|
|
||||||
static bool bond_reset_pending; // 본드 삭제 work 중복 예약 방지
|
|
||||||
static struct k_work_delayable dfu_watchdog_work; // DFU 업로드 무진행 감시 work
|
|
||||||
static void dfu_watchdog_handler(struct k_work *work);
|
|
||||||
|
|
||||||
static enum mgmt_cb_return dfu_status_cb(uint32_t event,
|
/* 부팅 후 전원 유지 판단 및 모듈 초기화 순서 조립 */
|
||||||
enum mgmt_cb_return prev_status,
|
|
||||||
int32_t *rc,
|
|
||||||
uint16_t *group,
|
|
||||||
bool *abort_more,
|
|
||||||
void *data,
|
|
||||||
size_t data_size);
|
|
||||||
|
|
||||||
static enum mgmt_cb_return smp_cmd_status_cb(uint32_t event,
|
|
||||||
enum mgmt_cb_return prev_status,
|
|
||||||
int32_t *rc,
|
|
||||||
uint16_t *group,
|
|
||||||
bool *abort_more,
|
|
||||||
void *data,
|
|
||||||
size_t data_size);
|
|
||||||
|
|
||||||
static struct mgmt_callback dfu_status_callback = {
|
|
||||||
.callback = dfu_status_cb,
|
|
||||||
.event_id = MGMT_EVT_OP_IMG_MGMT_DFU_STARTED |
|
|
||||||
MGMT_EVT_OP_IMG_MGMT_DFU_CHUNK |
|
|
||||||
MGMT_EVT_OP_IMG_MGMT_DFU_CHUNK_WRITE_COMPLETE |
|
|
||||||
MGMT_EVT_OP_IMG_MGMT_DFU_PENDING |
|
|
||||||
MGMT_EVT_OP_IMG_MGMT_DFU_STOPPED,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct mgmt_callback smp_cmd_status_callback = {
|
|
||||||
.callback = smp_cmd_status_cb,
|
|
||||||
.event_id = MGMT_EVT_OP_CMD_DONE,
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* BLE RX 콜백 안에서 무거운 일을 바로 처리하지 않기 위해 별도 work queue를 둠
|
|
||||||
* - 연결 직후 초기화 버스트 등 명령이 몰릴 때 대비해 링버퍼(FIFO)로 흡수
|
|
||||||
* - 큐가 가득 차면(BLE_CMD_QUEUE_DEPTH) 그 때만 drop
|
|
||||||
*/
|
|
||||||
static struct k_work_q ble_cmd_work_q;
|
|
||||||
K_THREAD_STACK_DEFINE(ble_cmd_workq_stack, BLE_CMD_WORKQ_STACK_SZ);
|
|
||||||
static struct k_work ble_cmd_work; // BLE 명령 처리 work
|
|
||||||
static struct k_spinlock ble_cmd_lock; // RX 큐 보호
|
|
||||||
|
|
||||||
// RX 명령 링버퍼: 들어온 순서대로 쌓고 워커가 순차 처리 (head=꺼낼 위치, tail=넣을 위치)
|
|
||||||
static uint8_t ble_cmd_q_buf[BLE_CMD_QUEUE_DEPTH][BLE_CMD_MAX_LEN]; // 명령 데이터
|
|
||||||
static uint16_t ble_cmd_q_len[BLE_CMD_QUEUE_DEPTH]; // 각 명령 길이
|
|
||||||
static uint8_t ble_cmd_q_head; // 다음에 꺼낼 슬롯
|
|
||||||
static uint8_t ble_cmd_q_tail; // 다음에 넣을 슬롯
|
|
||||||
static uint8_t ble_cmd_q_count; // 큐에 쌓인 명령 수
|
|
||||||
|
|
||||||
static void ble_cmd_work_handler(struct k_work *work)
|
|
||||||
{
|
|
||||||
ARG_UNUSED(work);
|
|
||||||
|
|
||||||
// work queue 로컬 복사본
|
|
||||||
uint8_t local_buf[BLE_CMD_MAX_LEN];
|
|
||||||
uint16_t local_len;
|
|
||||||
|
|
||||||
// 큐가 빌 때까지 head에서 하나씩 꺼내 순차 처리 (dispatch는 락 밖에서)
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
k_spinlock_key_t key = k_spin_lock(&ble_cmd_lock);
|
|
||||||
if (ble_cmd_q_count == 0U)
|
|
||||||
{
|
|
||||||
k_spin_unlock(&ble_cmd_lock, key);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
local_len = ble_cmd_q_len[ble_cmd_q_head];
|
|
||||||
memcpy(local_buf, ble_cmd_q_buf[ble_cmd_q_head], local_len);
|
|
||||||
ble_cmd_q_head = (uint8_t)((ble_cmd_q_head + 1U) % BLE_CMD_QUEUE_DEPTH);
|
|
||||||
ble_cmd_q_count--;
|
|
||||||
k_spin_unlock(&ble_cmd_lock, key);
|
|
||||||
|
|
||||||
if (local_len == 0U)
|
|
||||||
{
|
|
||||||
DBG_ERR("[BLE RX] worker: empty\r\n");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 일반 스레드 문맥 명령 처리
|
|
||||||
//DBG_CORE("[BLE RX] worker dispatch len=%u\r\n", local_len);
|
|
||||||
ble_cmd_dispatch(local_buf, local_len);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 시스템 워크큐에서 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); // 삭제 완료 확인 주황 LED ON
|
|
||||||
k_msleep(1500);
|
|
||||||
sys_reboot(SYS_REBOOT_COLD);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* BLE RX 콜백 */
|
|
||||||
static void ble_rx_handler(const uint8_t *data, uint16_t len)
|
|
||||||
{
|
|
||||||
if (len > BLE_CMD_MAX_LEN)
|
|
||||||
{
|
|
||||||
DBG_ERR("[BLE RX] drop: len=%u exceeds %d\r\n", len, BLE_CMD_MAX_LEN);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 공유 RX 큐 보호: tail 슬롯에 명령 적재 (FIFO)
|
|
||||||
k_spinlock_key_t key = k_spin_lock(&ble_cmd_lock);
|
|
||||||
if (ble_cmd_q_count >= BLE_CMD_QUEUE_DEPTH)
|
|
||||||
{
|
|
||||||
k_spin_unlock(&ble_cmd_lock, key);
|
|
||||||
DBG_ERR("[BLE RX] drop: queue full\r\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 명령 버퍼 복사
|
|
||||||
memcpy(ble_cmd_q_buf[ble_cmd_q_tail], data, len);
|
|
||||||
ble_cmd_q_len[ble_cmd_q_tail] = len;
|
|
||||||
ble_cmd_q_tail = (uint8_t)((ble_cmd_q_tail + 1U) % BLE_CMD_QUEUE_DEPTH);
|
|
||||||
ble_cmd_q_count++;
|
|
||||||
k_spin_unlock(&ble_cmd_lock, key);
|
|
||||||
|
|
||||||
// 명령 처리 work 예약 (워커는 큐가 빌 때까지 처리하므로 중복 예약은 무해)
|
|
||||||
// 실패해도 항목은 큐에 유효하게 남아 있고, 다음 RX의 submit이 함께 처리하므로 롤백하지 않음
|
|
||||||
int err = k_work_submit_to_queue(&ble_cmd_work_q, &ble_cmd_work);
|
|
||||||
if (err < 0)
|
|
||||||
{
|
|
||||||
DBG_ERR("[BLE RX] queue submit fail err=%d\r\n", err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 전원 유지 (POWER_HOLD) 제어 */
|
|
||||||
static void boot_context_detect(void)
|
static void boot_context_detect(void)
|
||||||
{
|
{
|
||||||
// 부팅 원인 확인
|
// 부팅 원인 확인
|
||||||
boot_reset_reason = nrf_power_resetreas_get(NRF_POWER);
|
boot_reset_reason = nrf_power_resetreas_get(NRF_POWER);
|
||||||
|
|
||||||
// DFU 완료 직후 리셋인지 확인
|
// DFU rest (DFU 완료 후 재부팅)
|
||||||
|
// GPREGRET은 reset 후에도 유지되는 작은 상태 표시용 레지스터
|
||||||
#if NRF_POWER_HAS_GPREGRET
|
#if NRF_POWER_HAS_GPREGRET
|
||||||
dfu_reset_resume_request = (nrf_power_gpregret_get(NRF_POWER, DFU_RESUME_GPREGRET_REG) == DFU_RESUME_MAGIC);
|
dfu_reset_resume_request = (nrf_power_gpregret_get(NRF_POWER, DFU_RESUME_GPREGRET_REG) == DFU_RESUME_MAGIC);
|
||||||
|
|
||||||
|
// DFU reset 표시 확인 후 다음 부팅 오판 방지를 위해 정리
|
||||||
if (dfu_reset_resume_request)
|
if (dfu_reset_resume_request)
|
||||||
{
|
{
|
||||||
nrf_power_gpregret_set(NRF_POWER, DFU_RESUME_GPREGRET_REG, 0U);
|
nrf_power_gpregret_set(NRF_POWER, DFU_RESUME_GPREGRET_REG, 0U);
|
||||||
@@ -270,69 +61,25 @@ static void boot_context_detect(void)
|
|||||||
dfu_reset_resume_request = false;
|
dfu_reset_resume_request = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// 소프트 리셋 계열 자동 복귀
|
// soft reset 후 복귀 (전원 버튼 새 부팅 X)
|
||||||
resume_without_power_button = (boot_reset_reason & (NRF_POWER_RESETREAS_SREQ_MASK | NRF_POWER_RESETREAS_DOG_MASK | NRF_POWER_RESETREAS_LOCKUP_MASK | NRF_POWER_RESETREAS_RESETPIN_MASK)) != 0U;
|
resume_without_power_button = (boot_reset_reason & (NRF_POWER_RESETREAS_SREQ_MASK | NRF_POWER_RESETREAS_DOG_MASK | NRF_POWER_RESETREAS_LOCKUP_MASK | NRF_POWER_RESETREAS_RESETPIN_MASK)) != 0U;
|
||||||
|
|
||||||
|
// 다음 부팅 오판 방지를 위해 RESETREAS 플래그 정리
|
||||||
if (boot_reset_reason != 0U)
|
if (boot_reset_reason != 0U)
|
||||||
{
|
{
|
||||||
// RESETREAS 플래그 정리
|
|
||||||
nrf_power_resetreas_clear(NRF_POWER, boot_reset_reason);
|
nrf_power_resetreas_clear(NRF_POWER, boot_reset_reason);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void power_hold_init(void)
|
/* NVS 값이 없을 때 사용할 기본값 초기화 (앱 전역 상태 기본값) - NVS 값이 있는 경우 이후 NVS 값으로 덮어씀 */
|
||||||
{
|
|
||||||
#if defined(CONFIG_BOOTLOADER_MCUBOOT)
|
|
||||||
// DFU 이미지 확인 전 전원 유지
|
|
||||||
dfu_confirm_pending_boot = !boot_is_img_confirmed();
|
|
||||||
#else
|
|
||||||
dfu_confirm_pending_boot = false;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// 리셋 복귀/DFU 확인 대기 시 즉시 래치
|
|
||||||
gpio_pin_configure_dt(&power_hold, (resume_without_power_button || dfu_confirm_pending_boot || dfu_reset_resume_request) ? GPIO_OUTPUT_ACTIVE : GPIO_OUTPUT_INACTIVE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 전원 ON/OFF 제어 - P0.08 핀으로 물리적 전원 래치/해제 */
|
|
||||||
static void power_control_handler(on_off_cont_t device_power_st, const char *reason)
|
|
||||||
{
|
|
||||||
if (device_power_st == OFF)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
DBG_ERR("[PWR] OFF latch release reason=%s pending=%s device_on=%u btn=%d cnt=%u suspended=%u dfu_adv=%u dfu_confirm=%u dfu_resume=%u\r\n",
|
|
||||||
reason ? reason : "unknown",
|
|
||||||
power_off_pending_reason,
|
|
||||||
device_on ? 1U : 0U,
|
|
||||||
gpio_pin_get_dt(&power_btn),
|
|
||||||
cnt_s,
|
|
||||||
power_btn_suspended ? 1U : 0U,
|
|
||||||
ble_dfu_advertising_is_enabled() ? 1U : 0U,
|
|
||||||
dfu_confirm_pending_boot ? 1U : 0U,
|
|
||||||
dfu_reset_resume_request ? 1U : 0U);*/
|
|
||||||
gpio_pin_set_dt(&power_hold, 0); // P0.08 LOW → 전원 래치 해제 → 전원 차단
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
gpio_pin_set_dt(&power_hold, 1); // P0.08 HIGH → 전원 유지
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* GPIO 초기화 */
|
|
||||||
static void gpio_init(void)
|
|
||||||
{
|
|
||||||
gpio_pin_configure_dt(&power_btn, GPIO_INPUT); // 전원 버튼(P1.08) 입력 설정
|
|
||||||
DBG_PRINTF("[INIT] HW - GPIO OK (POWER BTN=%d)\r\n", gpio_pin_get_dt(&power_btn));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 기본 설정값 로드 */
|
|
||||||
static void load_default_config(void)
|
static void load_default_config(void)
|
||||||
{
|
{
|
||||||
// 기본 문자열 길이 제한
|
// 기본 문자열 길이 계산
|
||||||
size_t serial_len = strlen(SERIAL_NUMBER);
|
size_t serial_len = strlen(SERIAL_NUMBER);
|
||||||
size_t hw_len = strlen(HARDWARE_VERSION);
|
size_t hw_len = strlen(HARDWARE_VERSION);
|
||||||
size_t passkey_len = strlen(DEFAULT_PASSKEY);
|
size_t passkey_len = strlen(DEFAULT_PASSKEY);
|
||||||
|
|
||||||
|
// 버퍼 오버플로우 방지 - 기본 문자열 길이 제한 (긴 경우 잘라내기)
|
||||||
if (serial_len > SERIAL_NO_LENGTH)
|
if (serial_len > SERIAL_NO_LENGTH)
|
||||||
{
|
{
|
||||||
serial_len = SERIAL_NO_LENGTH;
|
serial_len = SERIAL_NO_LENGTH;
|
||||||
@@ -348,6 +95,8 @@ static void load_default_config(void)
|
|||||||
passkey_len = PASSKEY_LENGTH;
|
passkey_len = PASSKEY_LENGTH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 복사할 때 버퍼를 먼저 0으로 채우고 복사 -> 문자열 종료 NUL 보장
|
||||||
|
// 기본 Serial Number 복사
|
||||||
memset(SERIAL_NO, 0, sizeof(SERIAL_NO));
|
memset(SERIAL_NO, 0, sizeof(SERIAL_NO));
|
||||||
memcpy(SERIAL_NO, SERIAL_NUMBER, serial_len);
|
memcpy(SERIAL_NO, SERIAL_NUMBER, serial_len);
|
||||||
|
|
||||||
@@ -359,428 +108,54 @@ static void load_default_config(void)
|
|||||||
memset(m_static_passkey, 0, sizeof(m_static_passkey));
|
memset(m_static_passkey, 0, sizeof(m_static_passkey));
|
||||||
memcpy(m_static_passkey, DEFAULT_PASSKEY, passkey_len);
|
memcpy(m_static_passkey, DEFAULT_PASSKEY, passkey_len);
|
||||||
|
|
||||||
// NVS 로드 전 기본값은 변경 가능 상태
|
m_passkey_changed = 0U; // 패스키 변경 여부 초기화
|
||||||
m_passkey_changed = 0U;
|
|
||||||
|
|
||||||
// 기본 상태값
|
m_reset_status = 1; // 기본 reset 상태 코드 설정
|
||||||
m_reset_status = 1;
|
bond_data_delete = true; // 기본 상태는 bond 삭제 완료 삭제로 초기화
|
||||||
bond_data_delete = true;
|
|
||||||
|
|
||||||
DBG_CORE("[INIT] Default (S/N=%s)\r\n", SERIAL_NO);
|
DBG_CORE("[INIT] Default (S/N=%s)\r\n", SERIAL_NO);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 전원 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");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 슬립 모드 / 전원 OFF: LED 표시 후 3초 뒤 전원 차단 */
|
|
||||||
static void power_off_schedule(const char *reason, led_state_t led_state)
|
|
||||||
{
|
|
||||||
power_off_pending_reason = reason ? reason : "unknown";
|
|
||||||
//DBG_ERR("[PWR] OFF scheduled reason = %s\r\n", power_off_pending_reason);
|
|
||||||
led_set_state(led_state);
|
|
||||||
k_timer_start(&m_power_off_delay_timer, K_MSEC(POWER_OFF_DELAY), K_NO_WAIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sleep_mode_enter_reason(const char *reason)
|
|
||||||
{
|
|
||||||
power_off_schedule(reason, LED_STATE_POWER_ON);
|
|
||||||
}
|
|
||||||
|
|
||||||
void sleep_mode_enter(void)
|
|
||||||
{
|
|
||||||
sleep_mode_enter_reason("sleep_mode_enter");
|
|
||||||
}
|
|
||||||
|
|
||||||
void device_power_off(void)
|
|
||||||
{
|
|
||||||
device_power_off_reason("device_power_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);
|
|
||||||
|
|
||||||
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 → ON)
|
|
||||||
if (!device_on)
|
|
||||||
{
|
|
||||||
if (!button_pressed) // 버튼 놓음 → 2초 미만이면 전원 OFF
|
|
||||||
{
|
|
||||||
//DBG_PRINTF("[BTN] Short press (%d) -> OFF\r\n", cnt_s);
|
|
||||||
power_control_handler(OFF, "boot-button-released-before-latch");
|
|
||||||
cnt_s = 0;
|
|
||||||
}
|
|
||||||
else // 버튼 계속 누르고 있음
|
|
||||||
{
|
|
||||||
cnt_s++;
|
|
||||||
|
|
||||||
if (cnt_s == BOOT_THRESHOLD) // 2초 도달: 래치 + 부팅 완료
|
|
||||||
{
|
|
||||||
device_on = true;
|
|
||||||
// cnt_s는 리셋하지 않음: 부팅 누름을 놓기 전(!boot_btn_released)까지 15초 본드 삭제 카운트를 연속 유지(놓는 순간 아래 분기에서 0으로 리셋)
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* 전원 OFF 시퀀스 (ON → OFF) */
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!boot_btn_released) // 부팅 때 누른 버튼을 아직 안 놓음
|
|
||||||
{
|
|
||||||
if (!button_pressed) // 놓음 → 이후부터 새 누름을 전원 OFF로 인식
|
|
||||||
{
|
|
||||||
boot_btn_released = true;
|
|
||||||
cnt_s = 0;
|
|
||||||
}
|
|
||||||
else // 전원 ON부터 계속 유지 중 → 15초 넘으면 본드 삭제 + 리부트
|
|
||||||
{
|
|
||||||
cnt_s++;
|
|
||||||
|
|
||||||
if ((cnt_s >= BOND_RESET_THRESHOLD) && !bond_reset_pending)
|
|
||||||
{
|
|
||||||
bond_reset_pending = true;
|
|
||||||
k_work_submit(&bond_reset_work);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (button_pressed) // 켜진 뒤 새로 누름 → 2초 이상이면 자동 전원 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 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);
|
|
||||||
|
|
||||||
// BLE advertising work
|
|
||||||
k_work_init(&adv_start_work, adv_start_work_handler);
|
|
||||||
k_work_init(&adv_stop_work, adv_stop_work_handler);
|
|
||||||
|
|
||||||
// 전원 버튼 15초 롱프레스 본드 삭제 work
|
|
||||||
k_work_init(&bond_reset_work, bond_reset_work_handler);
|
|
||||||
|
|
||||||
// DFU 업로드 무진행 감시 work
|
|
||||||
k_work_init_delayable(&dfu_watchdog_work, dfu_watchdog_handler);
|
|
||||||
|
|
||||||
// BLE 명령 처리 work queue
|
|
||||||
k_work_init(&ble_cmd_work, ble_cmd_work_handler);
|
|
||||||
k_work_queue_start(&ble_cmd_work_q, ble_cmd_workq_stack, K_THREAD_STACK_SIZEOF(ble_cmd_workq_stack), BLE_CMD_WORKQ_PRIORITY, NULL);
|
|
||||||
|
|
||||||
// 전원 제어 타이머
|
|
||||||
power_timer_init();
|
|
||||||
|
|
||||||
// DFU 업로드 상태를 LED에 반영
|
|
||||||
mgmt_callback_register(&dfu_status_callback);
|
|
||||||
|
|
||||||
// DFU 명령 실패 시 LED 상태 복구
|
|
||||||
mgmt_callback_register(&smp_cmd_status_callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 전원 버튼 폴링 시작 (5ms 후 main_s 콜백) */
|
|
||||||
static void timers_start(void)
|
|
||||||
{
|
|
||||||
k_timer_start(&m_power_on_delay_timer, K_MSEC(POWER_ON_DELAY), K_NO_WAIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* DFU 업로드가 멈춘 채(청크 무진행) 링크만 살아있는 경우 강제 disconnect 후 SMP 재광고(10분 타임아웃) 전환 - 초록 LED 무한 점등/무한 대기 방지 */
|
|
||||||
static void dfu_watchdog_handler(struct k_work *work)
|
|
||||||
{
|
|
||||||
ARG_UNUSED(work);
|
|
||||||
|
|
||||||
if (ble_is_connected())
|
|
||||||
{
|
|
||||||
DBG_CORE("[DFU] upload stalled (%ds), forcing disconnect\r\n", DFU_STALL_TIMEOUT_SEC);
|
|
||||||
(void)ble_disconnect_active();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static enum mgmt_cb_return dfu_status_cb(uint32_t event, enum mgmt_cb_return prev_status, int32_t *rc, uint16_t *group, bool *abort_more, void *data, size_t data_size)
|
|
||||||
{
|
|
||||||
ARG_UNUSED(prev_status);
|
|
||||||
ARG_UNUSED(rc);
|
|
||||||
ARG_UNUSED(group);
|
|
||||||
ARG_UNUSED(abort_more);
|
|
||||||
ARG_UNUSED(data);
|
|
||||||
ARG_UNUSED(data_size);
|
|
||||||
|
|
||||||
switch (event)
|
|
||||||
{
|
|
||||||
case MGMT_EVT_OP_IMG_MGMT_DFU_STARTED:
|
|
||||||
case MGMT_EVT_OP_IMG_MGMT_DFU_CHUNK:
|
|
||||||
case MGMT_EVT_OP_IMG_MGMT_DFU_CHUNK_WRITE_COMPLETE:
|
|
||||||
dfu_led_active = true;
|
|
||||||
led_ble_solid(); // DFU 업로드 중에는 초록 LED ON
|
|
||||||
// 청크 수신/flash write 완료 때마다 무진행 감시 타이머 리셋
|
|
||||||
k_work_reschedule(&dfu_watchdog_work, K_SECONDS(DFU_STALL_TIMEOUT_SEC));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MGMT_EVT_OP_IMG_MGMT_DFU_PENDING:
|
|
||||||
dfu_led_active = true;
|
|
||||||
led_ble_solid(); // 업로드 완료 후 리셋 전까지 초록 LED ON
|
|
||||||
k_work_cancel_delayable(&dfu_watchdog_work); // 업로드 완료: 감시 종료
|
|
||||||
|
|
||||||
#if NRF_POWER_HAS_GPREGRET
|
|
||||||
nrf_power_gpregret_set(NRF_POWER, DFU_RESUME_GPREGRET_REG, DFU_RESUME_MAGIC); // DFU 리셋 후 버튼 없이 전원 ON
|
|
||||||
#endif
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MGMT_EVT_OP_IMG_MGMT_DFU_STOPPED:
|
|
||||||
k_work_cancel_delayable(&dfu_watchdog_work); // 중단/실패: 감시 종료
|
|
||||||
if (dfu_led_active)
|
|
||||||
{
|
|
||||||
dfu_led_active = false;
|
|
||||||
led_set_state(LED_STATE_ADVERTISING); // 실패나 중단 시 일반 광고 LED로 복귀
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return MGMT_CB_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static enum mgmt_cb_return smp_cmd_status_cb(uint32_t event, enum mgmt_cb_return prev_status, int32_t *rc, uint16_t *group, bool *abort_more, void *data, size_t data_size)
|
|
||||||
{
|
|
||||||
ARG_UNUSED(event);
|
|
||||||
ARG_UNUSED(prev_status);
|
|
||||||
ARG_UNUSED(rc);
|
|
||||||
ARG_UNUSED(group);
|
|
||||||
ARG_UNUSED(abort_more);
|
|
||||||
|
|
||||||
if (data == NULL || data_size != sizeof(struct mgmt_evt_op_cmd_arg))
|
|
||||||
{
|
|
||||||
return MGMT_CB_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
const struct mgmt_evt_op_cmd_arg *cmd = data;
|
|
||||||
|
|
||||||
// 이미지 업로드/test 명령 실패 시 DFU LED 고정 해제
|
|
||||||
if (cmd->group == MGMT_GROUP_ID_IMAGE &&
|
|
||||||
(cmd->id == IMG_MGMT_ID_UPLOAD || cmd->id == IMG_MGMT_ID_STATE) &&
|
|
||||||
cmd->err != MGMT_ERR_EOK)
|
|
||||||
{
|
|
||||||
DBG_ERR("[DFU] command failed id=%u err=%d\r\n", cmd->id, cmd->err);
|
|
||||||
|
|
||||||
dfu_led_active = false;
|
|
||||||
led_set_state(LED_STATE_ADVERTISING);
|
|
||||||
|
|
||||||
// 실패 후 리셋 복구 표식이 남지 않게 정리
|
|
||||||
#if NRF_POWER_HAS_GPREGRET
|
|
||||||
nrf_power_gpregret_set(NRF_POWER, DFU_RESUME_GPREGRET_REG, 0U);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
return MGMT_CB_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void resume_device_after_soft_reset(void)
|
|
||||||
{
|
|
||||||
// 일반 버튼 부팅이면 skip
|
|
||||||
if (!resume_without_power_button && !dfu_confirm_pending_boot && !dfu_reset_resume_request)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 리셋 복귀나 DFU test 부팅은 버튼 없이도 ON 상태로 복구
|
|
||||||
device_on = true;
|
|
||||||
cnt_s = 0;
|
|
||||||
boot_btn_released = (gpio_pin_get_dt(&power_btn) != 1);
|
|
||||||
m_reset_status = 1;
|
|
||||||
|
|
||||||
// 정상 ON 상태 서비스 재시작
|
|
||||||
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", boot_reset_reason);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void confirm_running_image(void)
|
|
||||||
{
|
|
||||||
#if defined(CONFIG_BOOTLOADER_MCUBOOT)
|
|
||||||
// 기본 초기화와 BLE 준비가 끝난 뒤 MCUboot revert 방지
|
|
||||||
if (!boot_is_img_confirmed())
|
|
||||||
{
|
|
||||||
int err = boot_write_img_confirmed();
|
|
||||||
if (err == 0)
|
|
||||||
{
|
|
||||||
DBG_CORE("[DFU] Running image confirmed\r\n");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DBG_ERR("[DFU] Image confirm failed (err %d)\r\n", err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DBG_CORE("[DFU] Running image already confirmed\r\n");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char *swap_type_to_str(int swap_type)
|
|
||||||
{
|
|
||||||
// MCUboot swap 상태 문자열
|
|
||||||
switch (swap_type)
|
|
||||||
{
|
|
||||||
case BOOT_SWAP_TYPE_NONE:
|
|
||||||
return "none";
|
|
||||||
case BOOT_SWAP_TYPE_TEST:
|
|
||||||
return "test";
|
|
||||||
case BOOT_SWAP_TYPE_PERM:
|
|
||||||
return "perm";
|
|
||||||
case BOOT_SWAP_TYPE_REVERT:
|
|
||||||
return "revert";
|
|
||||||
case BOOT_SWAP_TYPE_FAIL:
|
|
||||||
return "fail";
|
|
||||||
default:
|
|
||||||
return "unknown";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void log_mcuboot_state(void)
|
|
||||||
{
|
|
||||||
#if defined(CONFIG_BOOTLOADER_MCUBOOT)
|
|
||||||
// 현재 부팅 슬롯/교체 상태
|
|
||||||
struct mcuboot_img_header header;
|
|
||||||
uint8_t active_slot = boot_fetch_active_slot();
|
|
||||||
int swap_type = mcuboot_swap_type();
|
|
||||||
int err = boot_read_bank_header(active_slot, &header, sizeof(header));
|
|
||||||
|
|
||||||
DBG_CORE("[DFU] active_slot=%u swap_type=%s(%d) confirmed=%d\r\n",
|
|
||||||
active_slot,
|
|
||||||
swap_type_to_str(swap_type),
|
|
||||||
swap_type,
|
|
||||||
boot_is_img_confirmed());
|
|
||||||
|
|
||||||
if (err == 0)
|
|
||||||
{
|
|
||||||
/* 실행 이미지 버전 */
|
|
||||||
DBG_CORE("[DFU] image version %u.%u.%u+%u size=0x%x\r\n",
|
|
||||||
header.h.v1.sem_ver.major,
|
|
||||||
header.h.v1.sem_ver.minor,
|
|
||||||
header.h.v1.sem_ver.revision,
|
|
||||||
header.h.v1.sem_ver.build_num,
|
|
||||||
header.h.v1.image_size);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DBG_ERR("[DFU] image header read failed (slot %u, err %d)\r\n",
|
|
||||||
active_slot,
|
|
||||||
err);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 메인 함수 */
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
// 리셋/복귀 컨텍스트
|
|
||||||
boot_context_detect();
|
boot_context_detect();
|
||||||
|
|
||||||
// 하드웨어 기본 초기화
|
#if defined(CONFIG_BOOTLOADER_MCUBOOT)
|
||||||
power_hold_init();
|
dfu_confirm_pending_boot = dfu_is_confirm_pending_boot(); // MCUboot test 이미지 상태인지 확인
|
||||||
cnt_s = 0;
|
#else
|
||||||
|
dfu_confirm_pending_boot = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
// 버튼 부팅 기본 상태
|
power_control_latch_init(resume_without_power_button || dfu_confirm_pending_boot || dfu_reset_resume_request);
|
||||||
device_on = false;
|
power_control_reset_state();
|
||||||
boot_btn_released = false;
|
|
||||||
confirm_running_image();
|
|
||||||
|
|
||||||
DBG_CORE("\r\n========================================\r\n");
|
DBG_CORE("\r\n========================================\r\n");
|
||||||
DBG_CORE(" TEST BUILD %s (Zephyr)\r\n", FIRMWARE_VERSION);
|
DBG_CORE(" TEST BUILD %s (Zephyr)\r\n", FIRMWARE_VERSION);
|
||||||
DBG_CORE(" BUILD TAG: %s\r\n", CONFIG_MCUBOOT_IMGTOOL_SIGN_VERSION);
|
DBG_CORE(" BUILD TAG: %s\r\n", CONFIG_MCUBOOT_IMGTOOL_SIGN_VERSION);
|
||||||
DBG_CORE("========================================\r\n");
|
DBG_CORE("========================================\r\n");
|
||||||
|
|
||||||
DBG_CORE("[INIT] HW Init\r\n");
|
// 각 모듈 초기화
|
||||||
// 기본 하드웨어/센서 초기화
|
power_control_init();
|
||||||
gpio_init();
|
ble_cmd_queue_init();
|
||||||
timers_init();
|
dfu_manager_init();
|
||||||
log_mcuboot_state();
|
dfu_log_mcuboot_state();
|
||||||
load_default_config();
|
load_default_config();
|
||||||
led_init();
|
led_init();
|
||||||
battery_adc_init();
|
battery_adc_init();
|
||||||
battery_timer_init();
|
battery_timer_init();
|
||||||
imu_init();
|
imu_init();
|
||||||
piezo_config_init();
|
piezo_config_init();
|
||||||
DBG_CORE("[INIT] HW - gpio/timer/config/led/batt/imu/temp/piezo-cfg OK\r\n");
|
|
||||||
|
|
||||||
// BLE 스택 + NUS
|
// BLE Stack/NUS 초기화 (0=성공)
|
||||||
DBG_CORE("[INIT] BLE Init\r\n");
|
if (ble_service_init(ble_cmd_queue_rx_handler) == 0) // BLE RX 콜백은 command queue로 연결
|
||||||
if (ble_service_init(ble_rx_handler) == 0)
|
|
||||||
{
|
{
|
||||||
DBG_CORE("[INIT] BLE - ble/nus OK\r\n");
|
// 버튼 없이 정상 ON 상태로 복구하는 경우
|
||||||
// 소프트 리셋 후 서비스 복구
|
if (resume_without_power_button || dfu_confirm_pending_boot || dfu_reset_resume_request)
|
||||||
resume_device_after_soft_reset();
|
{
|
||||||
// BLE 준비까지 확인한 뒤 DFU test 이미지를 확정
|
power_control_resume_after_reset(boot_reset_reason);
|
||||||
confirm_running_image();
|
}
|
||||||
|
|
||||||
|
dfu_confirm_running_image(); // BLE 준비까지 확인한 뒤 DFU test 이미지 확정(confirm)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -791,10 +166,10 @@ int main(void)
|
|||||||
DBG_CORE(" READY [%s]\r\n", SERIAL_NO);
|
DBG_CORE(" READY [%s]\r\n", SERIAL_NO);
|
||||||
DBG_CORE("========================================\r\n");
|
DBG_CORE("========================================\r\n");
|
||||||
|
|
||||||
// 전원 버튼 상태머신 시작 (부팅 시 버튼이 눌려있는 상태)
|
power_control_start(); // 전원 버튼 상태머신 시작 (부팅 시 버튼이 눌려있는 상태)
|
||||||
timers_start();
|
|
||||||
|
|
||||||
// 메인 루프 - idle
|
// main loop - idle
|
||||||
|
// callback, work queue 기반 동작 유지
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
k_msleep(100);
|
k_msleep(100);
|
||||||
|
|||||||
@@ -39,7 +39,10 @@
|
|||||||
#define PIEZO_AVG_INTER_BURST_GAP_US 650
|
#define PIEZO_AVG_INTER_BURST_GAP_US 650
|
||||||
|
|
||||||
/* real capture 전에 같은 채널에서 버스트+ADC capture를 버리는 횟수(dummy) */
|
/* real capture 전에 같은 채널에서 버스트+ADC capture를 버리는 횟수(dummy) */
|
||||||
#define PIEZO_DUMMY_CAPTURE_COUNT 5
|
#define PIEZO_DUMMY_CAPTURE_COUNT 1
|
||||||
|
|
||||||
|
/* 채널 0 시작 전 안정을 위한 추가 딜레이 */
|
||||||
|
#define PIEZO_CH0_WARMUP_TO_REAL_SETTLE_US 3000
|
||||||
|
|
||||||
/* 6채널 sweep 결과 (각 채널의 평균 완료 후 최종만 저장) */
|
/* 6채널 sweep 결과 (각 채널의 평균 완료 후 최종만 저장) */
|
||||||
static uint16_t piezo_channels[PIEZO_NUM_CHANNELS][PIEZO_MEASURE_MAX_SAMPLES];
|
static uint16_t piezo_channels[PIEZO_NUM_CHANNELS][PIEZO_MEASURE_MAX_SAMPLES];
|
||||||
@@ -230,7 +233,8 @@ int piezo_measure_sweep(void)
|
|||||||
|
|
||||||
if (ch == 0U) // 채널 0 시작 전에만 dummy burst + ADC capture 수행
|
if (ch == 0U) // 채널 0 시작 전에만 dummy burst + ADC capture 수행
|
||||||
{
|
{
|
||||||
// Dummy burst - ADC caputre는 수행하지 않음
|
// Dummy burst + ADC caputre
|
||||||
|
/*
|
||||||
for (uint8_t dummy = 0; dummy < PIEZO_DUMMY_CAPTURE_COUNT; dummy++)
|
for (uint8_t dummy = 0; dummy < PIEZO_DUMMY_CAPTURE_COUNT; dummy++)
|
||||||
{
|
{
|
||||||
k_busy_wait(PIEZO_AVG_INTER_BURST_GAP_US);
|
k_busy_wait(PIEZO_AVG_INTER_BURST_GAP_US);
|
||||||
@@ -251,10 +255,20 @@ int piezo_measure_sweep(void)
|
|||||||
DBG_PRINTF("[ECHO] dummy capture fail ch=%d dummy=%d err=%d\r\n", ch, dummy, err);
|
DBG_PRINTF("[ECHO] dummy capture fail ch=%d dummy=%d err=%d\r\n", ch, dummy, err);
|
||||||
return ECHO_STATUS_CAPTURE;
|
return ECHO_STATUS_CAPTURE;
|
||||||
}
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
k_busy_wait(PIEZO_AVG_INTER_BURST_GAP_US);
|
||||||
|
piezo_burst_sw_freq(freq, cycles);
|
||||||
|
k_busy_wait(capture_delay_us);
|
||||||
|
err = echo_adc_capture(echo_capture, samples);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
//DBG_PRINTF("[ECHO] dummy capture fail ch=%d dummy=%d err=%d\r\n", ch, dummy, err);
|
||||||
|
return ECHO_STATUS_CAPTURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 마지막 dummy capture와 첫 real capture 사이에도 같은 회복 시간
|
// 마지막 dummy capture와 첫 real capture 사이에도 같은 회복 시간 + 추가 안정화
|
||||||
k_busy_wait(PIEZO_AVG_INTER_BURST_GAP_US + 3000);
|
k_busy_wait(PIEZO_AVG_INTER_BURST_GAP_US + PIEZO_CH0_WARMUP_TO_REAL_SETTLE_US);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 여기부터 실제 평균에 들어갈 capture
|
// 여기부터 실제 평균에 들어갈 capture
|
||||||
|
|||||||
@@ -0,0 +1,382 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* @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);
|
||||||
|
}
|
||||||
@@ -5,8 +5,17 @@
|
|||||||
#ifndef POWER_CONTROL_H__
|
#ifndef POWER_CONTROL_H__
|
||||||
#define POWER_CONTROL_H__
|
#define POWER_CONTROL_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
|
|
||||||
|
void power_control_latch_init(bool keep_on);
|
||||||
|
void power_control_reset_state(void);
|
||||||
|
void power_control_init(void);
|
||||||
|
void power_control_start(void);
|
||||||
|
void power_control_resume_after_reset(uint32_t reset_reason);
|
||||||
int device_sleep_mode(void);
|
int device_sleep_mode(void);
|
||||||
int device_activated(void);
|
int device_activated(void);
|
||||||
int device_reactivated(void);
|
int device_reactivated(void);
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* @file power_control.c
|
|
||||||
* @brief Device power sequence control
|
|
||||||
*
|
|
||||||
* Power-up sequence state machine with k_timer (single-shot 20ms intervals)
|
|
||||||
******************************************************************************/
|
|
||||||
#include <zephyr/kernel.h>
|
|
||||||
#include "main.h"
|
|
||||||
#include "power_control.h"
|
|
||||||
#include "debug_print.h"
|
|
||||||
|
|
||||||
#define POWER_LOOP_INTERVAL 20 /* ms */
|
|
||||||
|
|
||||||
static struct k_timer m_power_timer;
|
|
||||||
static uint8_t p_order;
|
|
||||||
static bool lock_check = false;
|
|
||||||
|
|
||||||
extern volatile bool processing;
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
// Sensor init not needed - imu_read_direct() handles it per measurement
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
// sw_i2c_init_once() will be added in Stage 3
|
|
||||||
k_msleep(10);
|
|
||||||
lock_check = true;
|
|
||||||
p_order = 0;
|
|
||||||
power_timer_start();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
@@ -75,3 +75,32 @@ if(EXISTS "${APP_PRJ_CONF}")
|
|||||||
)
|
)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Production HEX file name based on src/main.h FIRMWARE_VERSION.
|
||||||
|
set(APP_MAIN_H "${APP_DIR}/src/main.h")
|
||||||
|
if(EXISTS "${APP_MAIN_H}")
|
||||||
|
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${APP_MAIN_H}")
|
||||||
|
file(READ "${APP_MAIN_H}" APP_MAIN_H_TEXT)
|
||||||
|
|
||||||
|
if(APP_MAIN_H_TEXT MATCHES "#define[ \t]+FIRMWARE_VERSION[ \t]+\"([^\"]+)\"")
|
||||||
|
set(FW_HEX_VERSION "${CMAKE_MATCH_1}")
|
||||||
|
string(REGEX REPLACE "[^A-Za-z0-9_.+-]" "_" FW_HEX_VERSION "${FW_HEX_VERSION}")
|
||||||
|
|
||||||
|
set(FW_HEX_SOURCE "${CMAKE_BINARY_DIR}/merged.hex")
|
||||||
|
set(FW_HEX_RELEASE_DIR "${APP_DIR}/release")
|
||||||
|
set(FW_HEX_VERSIONED "${FW_HEX_RELEASE_DIR}/VesiScan-Basic_${FW_HEX_VERSION}.hex")
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT "${FW_HEX_VERSIONED}"
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E make_directory "${FW_HEX_RELEASE_DIR}"
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${FW_HEX_SOURCE}" "${FW_HEX_VERSIONED}"
|
||||||
|
DEPENDS "${FW_HEX_SOURCE}"
|
||||||
|
COMMENT "Generating firmware-versioned HEX ${FW_HEX_VERSIONED}"
|
||||||
|
VERBATIM
|
||||||
|
)
|
||||||
|
|
||||||
|
add_custom_target(firmware_versioned_hex ALL
|
||||||
|
DEPENDS "${FW_HEX_VERSIONED}"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|||||||
Reference in New Issue
Block a user