Initial Commit
This commit is contained in:
+12
-12
@@ -22,14 +22,14 @@
|
||||
#define APP_NVS_KEY_HW_NO "hw"
|
||||
#define APP_NVS_KEY_SERIAL_NO "serial"
|
||||
#define APP_NVS_KEY_PASSKEY "passkey"
|
||||
#define APP_NVS_KEY_PASSKEY_CHANGED "passkey_changed" /* 패스키 1회 변경 완료 여부 */
|
||||
#define APP_NVS_KEY_PASSKEY_CHANGED "passkey_changed" // 패스키 1회 변경 완료 여부
|
||||
|
||||
/* settings_save_one에 넘기는 전체 키 */
|
||||
#define APP_NVS_FULL_KEY_PIEZO "vesiscan/piezo"
|
||||
#define APP_NVS_FULL_KEY_HW_NO "vesiscan/hw"
|
||||
#define APP_NVS_FULL_KEY_SERIAL_NO "vesiscan/serial"
|
||||
#define APP_NVS_FULL_KEY_PASSKEY "vesiscan/passkey"
|
||||
#define APP_NVS_FULL_KEY_PASSKEY_CHANGED "vesiscan/passkey_changed" /* 0은 변경 가능, 1은 변경 차단 */
|
||||
#define APP_NVS_FULL_KEY_PASSKEY_CHANGED "vesiscan/passkey_changed" // 0은 변경 가능, 1은 변경 차단
|
||||
|
||||
/* parser.c가 가진 피에조 설정을 NVS 로드 시 갱신하기 위한 포인터 */
|
||||
static piezo_config_t *m_piezo_cfg;
|
||||
@@ -60,7 +60,7 @@ static int app_nvs_settings_set(const char *name, size_t len, settings_read_cb r
|
||||
{
|
||||
const char *next;
|
||||
|
||||
/* 피에조 설정은 검증을 통과한 값만 기본값 대신 적용 */
|
||||
// 피에조 설정은 검증을 통과한 값만 기본값 대신 적용
|
||||
if (settings_name_steq(name, APP_NVS_KEY_PIEZO, &next) && (next == NULL))
|
||||
{
|
||||
piezo_config_t loaded;
|
||||
@@ -93,7 +93,7 @@ static int app_nvs_settings_set(const char *name, size_t len, settings_read_cb r
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* HW Version은 NUL 종료 없이 고정 길이 ASCII로 저장 */
|
||||
// HW Version은 NUL 종료 없이 고정 길이 ASCII로 저장
|
||||
if (settings_name_steq(name, APP_NVS_KEY_HW_NO, &next) && (next == NULL))
|
||||
{
|
||||
if (len != HW_NO_LENGTH)
|
||||
@@ -106,7 +106,7 @@ static int app_nvs_settings_set(const char *name, size_t len, settings_read_cb r
|
||||
return read_exact(read_cb, cb_arg, HW_NO, HW_NO_LENGTH, APP_NVS_KEY_HW_NO);
|
||||
}
|
||||
|
||||
/* Serial Number는 NUL 종료 없이 고정 길이 ASCII로 저장 */
|
||||
// Serial Number는 NUL 종료 없이 고정 길이 ASCII로 저장
|
||||
if (settings_name_steq(name, APP_NVS_KEY_SERIAL_NO, &next) && (next == NULL))
|
||||
{
|
||||
if (len != SERIAL_NO_LENGTH)
|
||||
@@ -119,7 +119,7 @@ static int app_nvs_settings_set(const char *name, size_t len, settings_read_cb r
|
||||
return read_exact(read_cb, cb_arg, SERIAL_NO, SERIAL_NO_LENGTH, APP_NVS_KEY_SERIAL_NO);
|
||||
}
|
||||
|
||||
/* 패스키는 mpz? 명령으로 최초 1회만 저장 */
|
||||
// 패스키는 mpz? 명령으로 최초 1회만 저장
|
||||
if (settings_name_steq(name, APP_NVS_KEY_PASSKEY, &next) && (next == NULL))
|
||||
{
|
||||
if (len != PASSKEY_LENGTH)
|
||||
@@ -153,7 +153,7 @@ SETTINGS_STATIC_HANDLER_DEFINE(app_nvs_settings, APP_NVS_SETTINGS_NAME, NULL, ap
|
||||
|
||||
int app_nvs_init(piezo_config_t *piezo_cfg, app_nvs_piezo_validate_fn piezo_validate, app_nvs_piezo_log_fn piezo_log)
|
||||
{
|
||||
/* settings_load_subtree가 호출되기 전에 복원용 포인터와 콜백 저장 */
|
||||
// settings_load_subtree가 호출되기 전에 복원용 포인터와 콜백 저장
|
||||
m_piezo_cfg = piezo_cfg;
|
||||
m_piezo_validate = piezo_validate;
|
||||
m_piezo_log = piezo_log;
|
||||
@@ -181,7 +181,7 @@ int app_nvs_init(piezo_config_t *piezo_cfg, app_nvs_piezo_validate_fn piezo_vali
|
||||
int app_nvs_save_piezo(const piezo_config_t *cfg)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_SETTINGS)
|
||||
/* 현재 측정 파라미터 저장 */
|
||||
// 현재 측정 파라미터 저장
|
||||
int err = settings_save_one(APP_NVS_FULL_KEY_PIEZO, cfg, sizeof(*cfg));
|
||||
if (err)
|
||||
{
|
||||
@@ -197,7 +197,7 @@ int app_nvs_save_piezo(const piezo_config_t *cfg)
|
||||
int app_nvs_save_hw_no(const char *hw_no)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_SETTINGS)
|
||||
/* 고정 길이 HW Version 저장 */
|
||||
// 고정 길이 HW Version 저장
|
||||
int err = settings_save_one(APP_NVS_FULL_KEY_HW_NO, hw_no, HW_NO_LENGTH);
|
||||
if (err)
|
||||
{
|
||||
@@ -213,7 +213,7 @@ int app_nvs_save_hw_no(const char *hw_no)
|
||||
int app_nvs_save_serial_no(const char *serial_no)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_SETTINGS)
|
||||
/* 고정 길이 Serial Number 저장 */
|
||||
// 고정 길이 Serial Number 저장
|
||||
int err = settings_save_one(APP_NVS_FULL_KEY_SERIAL_NO, serial_no, SERIAL_NO_LENGTH);
|
||||
if (err)
|
||||
{
|
||||
@@ -229,7 +229,7 @@ int app_nvs_save_serial_no(const char *serial_no)
|
||||
int app_nvs_save_passkey(const char *passkey)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_SETTINGS)
|
||||
/* mpz? 명령이 허용된 뒤 고정 길이 패스키 저장 */
|
||||
// mpz? 명령이 허용된 뒤 고정 길이 패스키 저장
|
||||
int err = settings_save_one(APP_NVS_FULL_KEY_PASSKEY, passkey, PASSKEY_LENGTH);
|
||||
if (err)
|
||||
{
|
||||
@@ -245,7 +245,7 @@ int app_nvs_save_passkey(const char *passkey)
|
||||
int app_nvs_save_passkey_changed(uint8_t changed)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_SETTINGS)
|
||||
/* 재부팅 후에도 mpz?(패스키 변경) 재실행을 막기 위한 플래그 저장(NVS) */
|
||||
// 재부팅 후에도 mpz?(패스키 변경) 재실행을 막기 위한 플래그 저장(NVS)
|
||||
int err = settings_save_one(APP_NVS_FULL_KEY_PASSKEY_CHANGED, &changed, sizeof(changed));
|
||||
if (err)
|
||||
{
|
||||
|
||||
+34
-75
@@ -31,9 +31,7 @@
|
||||
|
||||
LOG_MODULE_REGISTER(ble_svc, LOG_LEVEL_INF);
|
||||
|
||||
/*==============================================================================
|
||||
* Module variables
|
||||
*============================================================================*/
|
||||
/* Module variables */
|
||||
static struct bt_conn *current_conn;
|
||||
static ble_data_rx_cb_t rx_callback;
|
||||
static bool dfu_advertising_mode;
|
||||
@@ -43,6 +41,7 @@ static int64_t last_rx_conn_param_update_ms;
|
||||
|
||||
#define BLE_TX_POWER_NORMAL_DBM 4
|
||||
#define BLE_TX_POWER_STRESS_DBM 8
|
||||
#define BLE_RSSI_MONITOR_ENABLED 0
|
||||
#define BLE_RSSI_STRESS_DBM (-80)
|
||||
#define BLE_RSSI_STRESS_COUNT 3
|
||||
#define BLE_RSSI_MONITOR_INTERVAL_MS 500
|
||||
@@ -68,7 +67,7 @@ static uint32_t pairing_passkey = BT_PASSKEY_RAND;
|
||||
|
||||
/*
|
||||
* - mutex: 여러 곳에서 동시에 NUS 전송을 시작하지 못하게 막음
|
||||
* - sem : "직전 전송이 정말 끝났는지" 기다리는 신호
|
||||
* - sem : 직전 전송이 정말 끝났는지 기다리는 신호
|
||||
*/
|
||||
static struct k_mutex nus_tx_lock;
|
||||
static struct k_sem nus_tx_done_sem;
|
||||
@@ -195,6 +194,11 @@ static void tx_power_monitor_handler(struct k_work *work)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!BLE_RSSI_MONITOR_ENABLED)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
err = ble_read_conn_rssi(&rssi);
|
||||
if (err)
|
||||
{
|
||||
@@ -221,23 +225,6 @@ static void tx_power_monitor_handler(struct k_work *work)
|
||||
K_MSEC(BLE_RSSI_MONITOR_INTERVAL_MS));
|
||||
}
|
||||
|
||||
static const char *ble_addr_type_str(uint8_t type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case BT_ADDR_LE_PUBLIC:
|
||||
return "public";
|
||||
case BT_ADDR_LE_RANDOM:
|
||||
return "random";
|
||||
case BT_ADDR_LE_PUBLIC_ID:
|
||||
return "public-id";
|
||||
case BT_ADDR_LE_RANDOM_ID:
|
||||
return "random-id";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static void ble_log_local_identity(void)
|
||||
{
|
||||
bt_addr_le_t addr = {0};
|
||||
@@ -463,8 +450,7 @@ static int ble_request_preferred_conn_params(bool count_attempt)
|
||||
err = bt_conn_le_param_update(current_conn, &conn_param);
|
||||
if (err)
|
||||
{
|
||||
DBG_ERR("[BLE] Param update failed err=%d -> disconnect\r\n", err);
|
||||
(void)bt_conn_disconnect(current_conn, BT_HCI_ERR_UNACCEPT_CONN_PARAM);
|
||||
DBG_ERR("[BLE] Param update failed err=%d\r\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -493,25 +479,21 @@ static void conn_param_update_handler(struct k_work *work)
|
||||
}
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* Advertising data
|
||||
*============================================================================*/
|
||||
/* Advertising data */
|
||||
|
||||
static const struct bt_data sd[] =
|
||||
{
|
||||
/* 평상시 Web Bluetooth 앱이 찾을 수 있도록 NUS UUID 광고 */
|
||||
BT_DATA_BYTES(BT_DATA_UUID128_SOME, BT_UUID_NUS_VAL),
|
||||
// 평상시 Web Bluetooth 앱이 찾을 수 있도록 NUS UUID 광고
|
||||
BT_DATA_BYTES(BT_DATA_UUID128_ALL, BT_UUID_NUS_VAL),
|
||||
};
|
||||
|
||||
static const struct bt_data sd_dfu[] =
|
||||
{
|
||||
/* DFU 진입 후 mcumgr 앱이 찾을 수 있도록 SMP UUID 광고 */
|
||||
// DFU 진입 후 mcumgr 앱이 찾을 수 있도록 SMP UUID 광고
|
||||
BT_DATA_BYTES(BT_DATA_UUID128_SOME, SMP_BT_SVC_UUID_VAL),
|
||||
};
|
||||
|
||||
/*==============================================================================
|
||||
* Connection callbacks
|
||||
*============================================================================*/
|
||||
/* Connection callbacks */
|
||||
static void connected(struct bt_conn *conn, uint8_t err)
|
||||
{
|
||||
if (err)
|
||||
@@ -528,7 +510,10 @@ static void connected(struct bt_conn *conn, uint8_t err)
|
||||
low_rssi_count = 0U;
|
||||
slow_tx_count = 0U;
|
||||
(void)ble_set_conn_tx_power(BLE_TX_POWER_NORMAL_DBM, "connected");
|
||||
k_work_schedule(&tx_power_monitor_work, K_MSEC(BLE_RSSI_MONITOR_INTERVAL_MS));
|
||||
if (BLE_RSSI_MONITOR_ENABLED)
|
||||
{
|
||||
k_work_schedule(&tx_power_monitor_work, K_MSEC(BLE_RSSI_MONITOR_INTERVAL_MS));
|
||||
}
|
||||
|
||||
/* 연결 정보 출력 */
|
||||
char addr_str[BT_ADDR_LE_STR_LEN];
|
||||
@@ -537,8 +522,6 @@ static void connected(struct bt_conn *conn, uint8_t err)
|
||||
|
||||
conn_param_update_attempts = 0U;
|
||||
last_rx_conn_param_update_ms = 0;
|
||||
k_work_schedule(&conn_param_update_work,
|
||||
K_MSEC(BLE_CONN_PARAM_FIRST_DELAY_MS));
|
||||
|
||||
#if IS_ENABLED(CONFIG_BT_SMP)
|
||||
err = bt_conn_set_security(conn, BLE_REQUIRED_SECURITY_LEVEL);
|
||||
@@ -578,29 +561,14 @@ static void disconnected(struct bt_conn *conn, uint8_t reason)
|
||||
reason,
|
||||
advertising_unlimited ? "unlimited" : "10min");
|
||||
|
||||
/* 워크큐에서 advertising 재시작 */
|
||||
// 워크큐에서 advertising 재시작
|
||||
k_work_submit(&adv_restart_work);
|
||||
}
|
||||
|
||||
static bool le_param_req(struct bt_conn *conn, struct bt_le_conn_param *param)
|
||||
{
|
||||
ARG_UNUSED(conn);
|
||||
|
||||
if (!ble_conn_param_is_preferred(param->interval_min,
|
||||
param->latency,
|
||||
param->timeout) ||
|
||||
(param->interval_max != BLE_MAX_CONN_INTERVAL))
|
||||
{
|
||||
DBG_PRINTF("[BLE] Override requested params: min=%u max=%u latency=%u timeout=%u\r\n",
|
||||
param->interval_min,
|
||||
param->interval_max,
|
||||
param->latency,
|
||||
param->timeout);
|
||||
param->interval_min = BLE_MIN_CONN_INTERVAL;
|
||||
param->interval_max = BLE_MAX_CONN_INTERVAL;
|
||||
param->latency = BLE_SLAVE_LATENCY;
|
||||
param->timeout = BLE_CONN_SUP_TIMEOUT;
|
||||
}
|
||||
ARG_UNUSED(param);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -646,9 +614,7 @@ BT_CONN_CB_DEFINE(conn_callbacks) =
|
||||
#endif
|
||||
};
|
||||
|
||||
/*==============================================================================
|
||||
* NUS callbacks
|
||||
*============================================================================*/
|
||||
/* NUS callbacks */
|
||||
static void nus_received(struct bt_conn *conn, const uint8_t *data, uint16_t len)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_BT_SMP)
|
||||
@@ -661,18 +627,13 @@ static void nus_received(struct bt_conn *conn, const uint8_t *data, uint16_t len
|
||||
ARG_UNUSED(conn);
|
||||
#endif
|
||||
|
||||
DBG_CORE("[NUS RX] len=%u\r\n", len);
|
||||
//DBG_CORE("[NUS RX] len=%u\r\n", len);
|
||||
|
||||
if (current_conn == conn)
|
||||
{
|
||||
int64_t now_ms = k_uptime_get();
|
||||
|
||||
if ((last_rx_conn_param_update_ms == 0) ||
|
||||
((now_ms - last_rx_conn_param_update_ms) >= BLE_CONN_PARAM_RETRY_INTERVAL_MS))
|
||||
{
|
||||
last_rx_conn_param_update_ms = now_ms;
|
||||
(void)ble_request_preferred_conn_params(false);
|
||||
}
|
||||
ARG_UNUSED(now_ms);
|
||||
}
|
||||
|
||||
if (rx_callback)
|
||||
@@ -684,7 +645,7 @@ static void nus_received(struct bt_conn *conn, const uint8_t *data, uint16_t len
|
||||
static void nus_sent(struct bt_conn *conn)
|
||||
{
|
||||
ARG_UNUSED(conn);
|
||||
/* 직전 NUS 패킷 전송 완료 */
|
||||
// 직전 NUS 패킷 전송 완료
|
||||
k_sem_give(&nus_tx_done_sem);
|
||||
//DBG_CORE("[NUS TX] complete\r\n");
|
||||
}
|
||||
@@ -695,9 +656,7 @@ static struct bt_nus_cb nus_cb =
|
||||
.sent = nus_sent,
|
||||
};
|
||||
|
||||
/*==============================================================================
|
||||
* Public functions
|
||||
*============================================================================*/
|
||||
/* Public functions */
|
||||
int ble_service_init(ble_data_rx_cb_t rx_cb)
|
||||
{
|
||||
int err;
|
||||
@@ -711,7 +670,7 @@ int ble_service_init(ble_data_rx_cb_t rx_cb)
|
||||
k_work_init_delayable(&conn_param_update_work, conn_param_update_handler);
|
||||
|
||||
k_work_init_delayable(&tx_power_monitor_work, tx_power_monitor_handler);
|
||||
/* Enable BLE stack */
|
||||
// Enable BLE stack
|
||||
err = bt_enable(NULL);
|
||||
if (err)
|
||||
{
|
||||
@@ -740,7 +699,7 @@ int ble_service_init(ble_data_rx_cb_t rx_cb)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Initialize NUS */
|
||||
// Initialize NUS
|
||||
err = bt_nus_init(&nus_cb);
|
||||
if (err)
|
||||
{
|
||||
@@ -766,9 +725,9 @@ int ble_advertising_start(void)
|
||||
|
||||
struct bt_le_adv_param adv_param = BT_LE_ADV_PARAM_INIT(
|
||||
BT_LE_ADV_OPT_CONN,
|
||||
APP_ADV_INTERVAL, /* min interval */
|
||||
APP_ADV_INTERVAL, /* max interval */
|
||||
NULL /* undirected */
|
||||
APP_ADV_INTERVAL, // min interval
|
||||
APP_ADV_INTERVAL, // max interval
|
||||
NULL // undirected
|
||||
);
|
||||
|
||||
err = bt_set_name(SERIAL_NO);
|
||||
@@ -817,7 +776,7 @@ int ble_dfu_advertising_enable(void)
|
||||
return -EALREADY;
|
||||
}
|
||||
|
||||
/* mdf? 응답 전송 후 현재 연결을 끊고 SMP UUID 광고로 재시작 */
|
||||
// mdf? 응답 전송 후 현재 연결을 끊고 SMP UUID 광고로 재시작
|
||||
dfu_advertising_mode = true;
|
||||
DBG_PRINTF("[BLE] DFU advertising mode enabled\r\n");
|
||||
|
||||
@@ -860,10 +819,10 @@ int ble_data_send(const uint8_t *data, uint16_t len)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* 먼저 들어온 전송이 끝나기 전 다음 전송이 겹치지 않게 잠금 (NUS는 한 번에 한 패킷씩 보내는 것이 안전) */
|
||||
// 먼저 들어온 전송이 끝나기 전 다음 전송이 겹치지 않게 잠금 (NUS는 한 번에 한 패킷씩 보내는 것이 안전)
|
||||
k_mutex_lock(&nus_tx_lock, K_FOREVER);
|
||||
|
||||
/* 혹시 남아 있는 완료 신호가 있으면 비우고 이번 전송 전용 상태로 만듦 */
|
||||
// 혹시 남아 있는 완료 신호가 있으면 비우고 이번 전송 전용 상태로 만듦
|
||||
while (k_sem_take(&nus_tx_done_sem, K_NO_WAIT) == 0) {
|
||||
}
|
||||
|
||||
@@ -895,7 +854,7 @@ int ble_data_send(const uint8_t *data, uint16_t len)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* bt_nus_send() 성공 후 sent 콜백이 올 때까지 기다림 (다음 패킷을 보내도 되는 시점) */
|
||||
// bt_nus_send() 성공 후 sent 콜백이 올 때까지 기다림 (다음 패킷을 보내도 되는 시점)
|
||||
err = k_sem_take(&nus_tx_done_sem, K_MSEC(BLE_TX_STUCK_MS));
|
||||
if (err)
|
||||
{
|
||||
|
||||
+8
-16
@@ -3,8 +3,6 @@
|
||||
* @brief BLE NUS service module
|
||||
*
|
||||
* Nordic UART Service (NUS) based BLE communication
|
||||
* Original: SoftDevice S140 + ble_nus SDK module
|
||||
* Ported: Zephyr BLE + NCS bt_nus
|
||||
******************************************************************************/
|
||||
#ifndef BLE_SERVICE_H__
|
||||
#define BLE_SERVICE_H__
|
||||
@@ -12,29 +10,23 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/*==============================================================================
|
||||
* BLE configuration
|
||||
*============================================================================*/
|
||||
#define APP_ADV_INTERVAL 64 /* 64 x 0.625ms = 40ms */
|
||||
#define APP_ADV_DURATION 60000 /* 60000 x 10ms = 600s (10 min) */
|
||||
/* BLE configuration */
|
||||
#define APP_ADV_INTERVAL 64 // 64 x 0.625ms = 40ms
|
||||
#define APP_ADV_DURATION 60000 // 60000 x 10ms = 600s (10 min)
|
||||
|
||||
#define BLE_MIN_CONN_INTERVAL 12 /* 15ms / 1.25ms = 12 */
|
||||
#define BLE_MAX_CONN_INTERVAL 12 /* 15ms / 1.25ms = 12 */
|
||||
#define BLE_MIN_CONN_INTERVAL 12 // 15ms / 1.25ms = 12
|
||||
#define BLE_MAX_CONN_INTERVAL 12 // 15ms / 1.25ms = 12
|
||||
#define BLE_SLAVE_LATENCY 0
|
||||
#define BLE_CONN_SUP_TIMEOUT 1000 /* 10000ms / 10ms = 1000 */
|
||||
#define BLE_CONN_SUP_TIMEOUT 1000 // 10000ms / 10ms = 1000
|
||||
|
||||
#define BLE_CONN_PARAM_FIRST_DELAY_MS 5000
|
||||
#define BLE_CONN_PARAM_RETRY_INTERVAL_MS 30000
|
||||
#define BLE_CONN_PARAM_MAX_ATTEMPTS 3
|
||||
|
||||
/*==============================================================================
|
||||
* Callback types
|
||||
*============================================================================*/
|
||||
/* Callback types */
|
||||
typedef void (*ble_data_rx_cb_t)(const uint8_t *data, uint16_t len);
|
||||
|
||||
/*==============================================================================
|
||||
* Public functions
|
||||
*============================================================================*/
|
||||
/* Public functions */
|
||||
int ble_service_init(ble_data_rx_cb_t rx_cb);
|
||||
int ble_advertising_start(void);
|
||||
int ble_advertising_stop(void);
|
||||
|
||||
@@ -11,9 +11,7 @@
|
||||
#include "debug_print.h"
|
||||
#include "ble_service.h"
|
||||
|
||||
/*==============================================================================
|
||||
* Command response state
|
||||
*============================================================================*/
|
||||
/* Command response state */
|
||||
static uint8_t tx_u16_buf[8];
|
||||
static uint8_t tx_imu_buf[18];
|
||||
static uint8_t tx_echo_buf[4 + 2 + 2 + (PIEZO_MEASURE_MAX_SAMPLES * 2) + 2];
|
||||
@@ -35,9 +33,7 @@ void cmd_reboot_after_response(void)
|
||||
sys_reboot(SYS_REBOOT_COLD);
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* CRC16 (CRC-CCITT, Nordic SDK 호환)
|
||||
*============================================================================*/
|
||||
/* CRC16 (CRC-CCITT, Nordic SDK 호환) */
|
||||
uint16_t cmd_crc16_compute(const uint8_t *p_data, uint32_t size)
|
||||
{
|
||||
uint16_t crc = 0xFFFF;
|
||||
@@ -53,8 +49,7 @@ uint16_t cmd_crc16_compute(const uint8_t *p_data, uint32_t size)
|
||||
return crc;
|
||||
}
|
||||
|
||||
bool cmd_get_data_u16_be(const uint8_t *data, uint8_t data_len,
|
||||
uint8_t word_index, uint16_t *out)
|
||||
bool cmd_get_data_u16_be(const uint8_t *data, uint8_t data_len, uint8_t word_index, uint16_t *out)
|
||||
{
|
||||
uint8_t offset = (uint8_t)(word_index * 2U);
|
||||
|
||||
@@ -88,11 +83,7 @@ char cmd_ascii_to_lower(char ch)
|
||||
return ch;
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* 응답 패킷 전송
|
||||
*============================================================================*/
|
||||
|
||||
/* TAG(4B) + uint16 값(2B) + CRC16(2B) = 8바이트 전송 */
|
||||
/* 응답 패킷 전송: TAG(4B) + uint16 값(2B) + CRC16(2B) = 8바이트 전송 */
|
||||
int cmd_send_response_u16(const char *tag, uint16_t value)
|
||||
{
|
||||
uint8_t *buf = tx_u16_buf;
|
||||
@@ -170,12 +161,7 @@ int cmd_send_response_tag_echo(const char *tag, const char *echo_tag)
|
||||
return ble_data_send(buf, 10);
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* 응답 패킷 전송 (IMU)
|
||||
*============================================================================*/
|
||||
|
||||
/* TAG(4B) + int16×6 빅엔디안(12B) + CRC16(2B) = 18바이트 전송
|
||||
* 기존 format_data() + dr_binary_tx_safe(buf, 8) 방식과 동일한 레이아웃 */
|
||||
/*응답 패킷 전송 (IMU): TAG(4B) + int16×6 빅엔디안(12B) + CRC16(2B) = 18바이트 전송 */
|
||||
int cmd_send_response_imu(const int16_t accel[3], const int16_t gyro[3])
|
||||
{
|
||||
uint8_t *buf = tx_imu_buf;
|
||||
@@ -201,7 +187,7 @@ int cmd_send_response_imu(const int16_t accel[3], const int16_t gyro[3])
|
||||
|
||||
void cmd_send_response_echo(uint8_t session, uint8_t channel, const uint16_t *samples, uint16_t num_samples)
|
||||
{
|
||||
/* 정적 버퍼 사용 (한 번에 한 명령만 처리) */
|
||||
// 정적 버퍼 사용 (한 번에 한 명령만 처리)
|
||||
uint8_t *buf = tx_echo_buf;
|
||||
|
||||
buf[0] = 'r'; buf[1] = 'e'; buf[2] = 'b'; buf[3] = ':';
|
||||
@@ -302,10 +288,4 @@ void cmd_send_response_piezo_config(const char *tag, uint16_t freq, uint16_t cyc
|
||||
buf[15] = (uint8_t)(crc >> 8);
|
||||
|
||||
ble_data_send(buf, sizeof(tx_cfg_buf));
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* 커맨드 핸들러
|
||||
*============================================================================*/
|
||||
|
||||
/* msn? → 배터리 전압 측정 → rsn: + mV */
|
||||
}
|
||||
@@ -36,6 +36,7 @@ const cmd_entry_t cmd_table[] =
|
||||
{ "mrs?", cmd_mrs },
|
||||
{ "mpz?", cmd_mpz },
|
||||
{ "mqz?", cmd_mqz },
|
||||
{ "mim?", cmd_mim },
|
||||
};
|
||||
|
||||
const int cmd_table_size = (int)(sizeof(cmd_table) / sizeof(cmd_table[0]));
|
||||
|
||||
@@ -70,10 +70,6 @@ int cmd_msr(const uint8_t *data, uint8_t data_len)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* 현재 연결의 bond/security 정보를 먼저 지우면 같은 연결에서 rsr: 응답을 보낼 수 없음
|
||||
* 응답 전송 완료 후 bond 삭제와 reboot 진행 필요
|
||||
*/
|
||||
k_msleep(100);
|
||||
|
||||
#if IS_ENABLED(CONFIG_BT_SMP)
|
||||
@@ -112,7 +108,7 @@ int cmd_mdf(const uint8_t *data, uint8_t data_len)
|
||||
|
||||
uint16_t status = ble_dfu_advertising_is_enabled() ? 0xFFFF : 0x0000;
|
||||
|
||||
/* DFU advertising 전환 중 연결이 끊겨도 전원 래치가 유지되도록 먼저 ON 상태를 확정 */
|
||||
// DFU advertising 전환 중 연결이 끊겨도 전원 래치가 유지되도록 먼저 ON 상태를 확정
|
||||
device_power_keep_on();
|
||||
|
||||
int tx_err = cmd_send_response_u16("rdf:", status);
|
||||
@@ -124,7 +120,7 @@ int cmd_mdf(const uint8_t *data, uint8_t data_len)
|
||||
|
||||
if (status == 0x0000)
|
||||
{
|
||||
/* rdf: 응답이 나간 뒤 연결 해제와 SMP 광고 전환 실행 */
|
||||
// rdf: 응답이 나간 뒤 연결 해제와 SMP 광고 전환 실행
|
||||
k_msleep(100);
|
||||
int err = ble_dfu_advertising_enable();
|
||||
if (err)
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
#include "app_nvs.h"
|
||||
#include "cmd_info.h"
|
||||
|
||||
/*
|
||||
* mfv: firmware version read
|
||||
*/
|
||||
int cmd_mfv(const uint8_t *data, uint8_t data_len)
|
||||
{
|
||||
char fw_version[SERIAL_NO_LENGTH];
|
||||
@@ -28,6 +31,9 @@ int cmd_mfv(const uint8_t *data, uint8_t data_len)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* mid: device information(hw version + fw version + serial number) read
|
||||
*/
|
||||
int cmd_mid(const uint8_t *data, uint8_t data_len)
|
||||
{
|
||||
int err;
|
||||
@@ -43,6 +49,9 @@ int cmd_mid(const uint8_t *data, uint8_t data_len)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* mwh: hardware version write
|
||||
*/
|
||||
int cmd_mwh(const uint8_t *data, uint8_t data_len)
|
||||
{
|
||||
if (data_len < HW_NO_LENGTH)
|
||||
@@ -64,6 +73,9 @@ int cmd_mwh(const uint8_t *data, uint8_t data_len)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* mrh: hardware version read
|
||||
*/
|
||||
int cmd_mrh(const uint8_t *data, uint8_t data_len)
|
||||
{
|
||||
int err;
|
||||
@@ -79,6 +91,9 @@ int cmd_mrh(const uint8_t *data, uint8_t data_len)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* mws: serial number write
|
||||
*/
|
||||
int cmd_mws(const uint8_t *data, uint8_t data_len)
|
||||
{
|
||||
if (data_len < SERIAL_NO_LENGTH)
|
||||
@@ -100,6 +115,9 @@ int cmd_mws(const uint8_t *data, uint8_t data_len)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* mrs: serial number read
|
||||
*/
|
||||
int cmd_mrs(const uint8_t *data, uint8_t data_len)
|
||||
{
|
||||
int err;
|
||||
@@ -115,6 +133,9 @@ int cmd_mrs(const uint8_t *data, uint8_t data_len)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* mpz: passkey write
|
||||
*/
|
||||
int cmd_mpz(const uint8_t *data, uint8_t data_len)
|
||||
{
|
||||
if (data_len < PASSKEY_LENGTH)
|
||||
@@ -125,13 +146,13 @@ int cmd_mpz(const uint8_t *data, uint8_t data_len)
|
||||
|
||||
if (m_passkey_changed != 0U)
|
||||
{
|
||||
/* 패스키가 변경된 경우 다시 바꿀 수 없음(FFFF 반환) */
|
||||
// 패스키가 변경된 경우 다시 바꿀 수 없음(FFFF 반환)
|
||||
cmd_send_response_u16("rpz:", 0xFFFF);
|
||||
DBG_PRINTF("[CMD] mpz: passkey already changed\r\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* NVS 저장이 모두 성공한 뒤 RAM 값 확정 */
|
||||
// NVS 저장이 모두 성공한 뒤 RAM 값 확정
|
||||
char new_passkey[PASSKEY_BUF_SIZE];
|
||||
memset(new_passkey, 0, sizeof(new_passkey));
|
||||
memcpy(new_passkey, data, PASSKEY_LENGTH);
|
||||
@@ -143,7 +164,7 @@ int cmd_mpz(const uint8_t *data, uint8_t data_len)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 변경 완료 플래그 세트 : 다음 mpz? 명령 차단 */
|
||||
// 변경 완료 플래그 세트 : 다음 mpz? 명령 차단
|
||||
err = app_nvs_save_passkey_changed(1U);
|
||||
if (err)
|
||||
{
|
||||
@@ -159,6 +180,9 @@ int cmd_mpz(const uint8_t *data, uint8_t data_len)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* mqz: passkey read (삭제 예정)
|
||||
*/
|
||||
int cmd_mqz(const uint8_t *data, uint8_t data_len)
|
||||
{
|
||||
ARG_UNUSED(data);
|
||||
@@ -166,12 +190,4 @@ int cmd_mqz(const uint8_t *data, uint8_t data_len)
|
||||
|
||||
cmd_send_response_ascii("rqz:", m_static_passkey, PASSKEY_LENGTH);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* LED 상태 변경 명령
|
||||
* mls?: 앱에서 전달한 LED 상태값을 적용하고 rls로 echo back
|
||||
* - 파라미터 부족: 0xFFFF
|
||||
* - 범위 초과: 0xFFFE
|
||||
* - LED_STATE_OFF 진입 시 IMU FIFO 정지
|
||||
*/
|
||||
}
|
||||
+284
-253
@@ -17,8 +17,280 @@
|
||||
static uint8_t tx_rim_samples[IMU_FIFO_RIM_TARGET_SAMPLES * IMU_FIFO_SAMPLE_BYTES];
|
||||
|
||||
/*
|
||||
* 테스트용
|
||||
* mpa?: piezo 전원 ON
|
||||
* 주기 측정 명령
|
||||
* mbb?: piezo echo sweep + 배터리 + IMU + IMU 온도 측정
|
||||
* - 성공 시 rbb 1개, reb 6개, raa 상태값 전송
|
||||
* - processing=true 구간에서는 내부 모니터의 I2C 접근을 막는 기준으로 사용
|
||||
*
|
||||
* - 순서:
|
||||
* 1. battery / imu / temp 측정
|
||||
* 2. rbb: 1개 전송
|
||||
* 3. piezo / echo ADC 준비
|
||||
* 4. piezo 6채널 burst
|
||||
* 5. echo 샘플 채널별로 모으기
|
||||
* 6. reb: 6개 전송
|
||||
* 7. raa: 상태값 전송
|
||||
*/
|
||||
int cmd_mbb(const uint8_t *data, uint8_t data_len)
|
||||
{
|
||||
ARG_UNUSED(data);
|
||||
ARG_UNUSED(data_len);
|
||||
|
||||
uint8_t session = cmd_next_echo_session();
|
||||
|
||||
int16_t accel[3];
|
||||
int16_t gyro[3];
|
||||
|
||||
processing = true;
|
||||
power_button_suspend(true);
|
||||
DBG_PRINTF("[MBB] cmd start\r\n");
|
||||
|
||||
int batt_mv = -1;
|
||||
int16_t temp_cdeg = INT16_MIN;
|
||||
|
||||
/* Read battery first (idle measurement) so mbb? result matches msn? */
|
||||
DBG_PRINTF("[MBB] battery read (idle)\r\n");
|
||||
batt_mv = battery_read_mv();
|
||||
|
||||
int status = piezo_measure_start_session();
|
||||
|
||||
if (status == ECHO_STATUS_OK) // Piezo GPIO, power, ADC initialization/wake succeeded
|
||||
{
|
||||
DBG_PRINTF("[MBB] imu/temp read\r\n");
|
||||
imu_read_with_temperature(accel, gyro, &temp_cdeg);
|
||||
|
||||
battery_protection_record_continuous(batt_mv, temp_cdeg); // update protection counters for continuous monitoring
|
||||
|
||||
DBG_PRINTF("[MBB] response rbb: tx start\r\n");
|
||||
cmd_send_response_bundle((uint16_t)batt_mv, accel, gyro, temp_cdeg);
|
||||
|
||||
DBG_PRINTF("[MBB] piezo sweep start\r\n");
|
||||
status = piezo_measure_sweep();
|
||||
if (status == ECHO_STATUS_OK)
|
||||
{
|
||||
for (uint8_t ch = 0; ch < PIEZO_NUM_CHANNELS; ch++)
|
||||
{
|
||||
DBG_PRINTF("[MBB] response reb: tx start ch=%d\r\n", ch);
|
||||
cmd_send_response_echo(session, ch, piezo_measure_channel_buffer(ch), piezo_config_get()->samples);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
piezo_power_off();
|
||||
power_button_suspend(false);
|
||||
DBG_PRINTF("[MBB] done\r\n");
|
||||
cmd_send_response_u16("raa:", (uint16_t)status); // 최종 상태 raa: 전송
|
||||
DBG_PRINTF("[CMD] mbb status=0x%04X\r\n", status);
|
||||
|
||||
processing = false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* 정렬모드
|
||||
* mtb?: IMU FIFO + piezo echo sweep
|
||||
* - 순서:
|
||||
* 1. IMU FIFO start
|
||||
* 2. piezo 6채널 burst
|
||||
* 3. echo 샘플 채널별로 모으기
|
||||
* 4. reb: 패킷 6개 전송
|
||||
* 5. raa: 상태값 전송
|
||||
* 6. rim: IMU FIFO 15 샘플 전송
|
||||
*/
|
||||
int cmd_mtb(const uint8_t *data, uint8_t data_len)
|
||||
{
|
||||
ARG_UNUSED(data);
|
||||
ARG_UNUSED(data_len);
|
||||
|
||||
uint8_t session = cmd_next_echo_session();
|
||||
uint16_t rim_count = 0U;
|
||||
bool fifo_started = false;
|
||||
|
||||
processing = true;
|
||||
power_button_suspend(true);
|
||||
DBG_PRINTF("[MTB] cmd start\r\n");
|
||||
|
||||
int imu_ret = imu_fifo_start();
|
||||
if (imu_ret != 0)
|
||||
{
|
||||
DBG_PRINTF("[MTB] fifo start fail ret=%d\r\n", imu_ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
fifo_started = true;
|
||||
}
|
||||
|
||||
int status = piezo_measure_start_session();
|
||||
|
||||
if (status == ECHO_STATUS_OK) // Piezo GPIO, power, ADC initialization/wake succeeded
|
||||
{
|
||||
DBG_PRINTF("[MTB] piezo sweep start\r\n");
|
||||
status = piezo_measure_sweep();
|
||||
if (status == ECHO_STATUS_OK)
|
||||
{
|
||||
for (uint8_t ch = 0; ch < PIEZO_NUM_CHANNELS; ch++)
|
||||
{
|
||||
DBG_PRINTF("[MTB] response reb: tx start ch=%d\r\n", ch);
|
||||
cmd_send_response_echo(session, ch, piezo_measure_channel_buffer(ch), piezo_config_get()->samples);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cmd_send_response_u16("raa:", (uint16_t)status); // 최종 상태 raa: 전송
|
||||
|
||||
if (status == ECHO_STATUS_OK)
|
||||
{
|
||||
if (fifo_started)
|
||||
{
|
||||
int imu_ret = imu_fifo_read_latest(tx_rim_samples, IMU_FIFO_RIM_TARGET_SAMPLES, &rim_count);
|
||||
if (imu_ret != 0)
|
||||
{
|
||||
DBG_PRINTF("[MTB] fifo read fail ret=%d\r\n", imu_ret);
|
||||
rim_count = 0U;
|
||||
}
|
||||
}
|
||||
|
||||
cmd_send_response_rim(tx_rim_samples, rim_count); // IMU FIFO 샘플 rim: 전송
|
||||
}
|
||||
DBG_PRINTF("[CMD] mtb status=0x%04X\r\n", status);
|
||||
|
||||
piezo_power_off();
|
||||
power_button_suspend(false);
|
||||
DBG_PRINTF("[CMD] mtb status=0x%04X rim=%u\r\n", status, rim_count);
|
||||
|
||||
processing = false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* mcf?: piezo measurement parameter read
|
||||
*/
|
||||
int cmd_mcf(const uint8_t *data, uint8_t data_len)
|
||||
{
|
||||
ARG_UNUSED(data);
|
||||
ARG_UNUSED(data_len);
|
||||
|
||||
const piezo_config_t *cfg = piezo_config_get();
|
||||
cmd_send_response_piezo_config("rcf:", cfg->freq, cfg->cycles, cfg->avg, cfg->delay_us, cfg->samples);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* mcs?: piezo measurement parameter write
|
||||
*/
|
||||
int cmd_mcs(const uint8_t *data, uint8_t data_len)
|
||||
{
|
||||
piezo_config_t cfg;
|
||||
|
||||
if (data_len < 10U)
|
||||
{
|
||||
cmd_send_response_piezo_config("rcs:", 0xFFFF, 0U, 0U, 0U, 0U);
|
||||
return 1;
|
||||
}
|
||||
|
||||
cmd_get_data_u16_be(data, data_len, 0, &cfg.freq);
|
||||
cmd_get_data_u16_be(data, data_len, 1, &cfg.cycles);
|
||||
cmd_get_data_u16_be(data, data_len, 2, &cfg.avg);
|
||||
cmd_get_data_u16_be(data, data_len, 3, &cfg.delay_us);
|
||||
cmd_get_data_u16_be(data, data_len, 4, &cfg.samples);
|
||||
|
||||
if (!piezo_config_validate(&cfg))
|
||||
{
|
||||
cmd_send_response_piezo_config("rcs:", 0xFFFF, cfg.cycles, cfg.avg, cfg.delay_us, cfg.samples);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int err = piezo_config_set(&cfg);
|
||||
if (err)
|
||||
{
|
||||
cmd_send_response_piezo_config("rcs:", 0xFFFD, cfg.cycles, cfg.avg, cfg.delay_us, cfg.samples);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const piezo_config_t *saved_cfg = piezo_config_get();
|
||||
cmd_send_response_piezo_config("rcs:", saved_cfg->freq, saved_cfg->cycles, saved_cfg->avg, saved_cfg->delay_us, saved_cfg->samples);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// =============================== Test Command ===============================
|
||||
/* TEST
|
||||
* mim? : IMU FIFO 15 samples
|
||||
*/
|
||||
int cmd_mim(const uint8_t *data, uint8_t data_len)
|
||||
{
|
||||
ARG_UNUSED(data);
|
||||
ARG_UNUSED(data_len);
|
||||
|
||||
uint16_t rim_count = 0U;
|
||||
int imu_ret = imu_fifo_start();
|
||||
if (imu_ret != 0)
|
||||
{
|
||||
DBG_PRINTF("[MTB] fifo start fail ret=%d\r\n", imu_ret);
|
||||
cmd_send_response_rim(tx_rim_samples, 0U);
|
||||
return 1;
|
||||
}
|
||||
|
||||
k_msleep(320); // IMU FIFO is 50Hz, 15 samples need about 300ms
|
||||
|
||||
imu_ret = imu_fifo_read_latest(tx_rim_samples, IMU_FIFO_RIM_TARGET_SAMPLES, &rim_count);
|
||||
if (imu_ret != 0)
|
||||
{
|
||||
DBG_PRINTF("[MIM] fifo read fail ret=%d\r\n", imu_ret);
|
||||
rim_count = 0U;
|
||||
}
|
||||
|
||||
cmd_send_response_rim(tx_rim_samples, rim_count); // IMU FIFO 15 samples rim: 전송
|
||||
imu_fifo_stop();
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* TEST (이전 정렬모드)
|
||||
* maa?: piezo echo sweep
|
||||
* - 순서:
|
||||
* 1. piezo 6채널 burst
|
||||
* 2. echo 샘플 채널별로 모으기
|
||||
* 3. reb: 패킷 6개 전송
|
||||
* 4. raa: 상태값 전송
|
||||
*/
|
||||
int cmd_maa(const uint8_t *data, uint8_t data_len)
|
||||
{
|
||||
ARG_UNUSED(data);
|
||||
ARG_UNUSED(data_len);
|
||||
|
||||
uint8_t session = cmd_next_echo_session();
|
||||
|
||||
processing = true;
|
||||
power_button_suspend(true);
|
||||
|
||||
int status = piezo_measure_start_session();
|
||||
|
||||
if (status == ECHO_STATUS_OK) // Piezo GPIO, power, ADC initialization/wake succeeded
|
||||
{
|
||||
DBG_PRINTF("[MBB] piezo sweep start\r\n");
|
||||
status = piezo_measure_sweep();
|
||||
if (status == ECHO_STATUS_OK)
|
||||
{
|
||||
for (uint8_t ch = 0; ch < PIEZO_NUM_CHANNELS; ch++)
|
||||
{
|
||||
DBG_PRINTF("[MBB] response reb: tx start ch=%d\r\n", ch);
|
||||
cmd_send_response_echo(session, ch, piezo_measure_channel_buffer(ch), piezo_config_get()->samples);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
piezo_power_off();
|
||||
power_button_suspend(false);
|
||||
cmd_send_response_u16("raa:", (uint16_t)status); // 최종 상태 raa: 전송
|
||||
DBG_PRINTF("[CMD] maa status=0x%04X\r\n", status);
|
||||
|
||||
processing = false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* TEST
|
||||
* mpa?: piezo power ON
|
||||
*/
|
||||
int cmd_mpa(const uint8_t *data, uint8_t data_len)
|
||||
{
|
||||
@@ -42,8 +314,8 @@ int cmd_mpa(const uint8_t *data, uint8_t data_len)
|
||||
}
|
||||
|
||||
/*
|
||||
* 테스트용
|
||||
* mpb?: piezo 전원 OFF
|
||||
* TEST
|
||||
* mpb?: piezo power OFF
|
||||
*/
|
||||
int cmd_mpb(const uint8_t *data, uint8_t data_len)
|
||||
{
|
||||
@@ -67,9 +339,8 @@ int cmd_mpb(const uint8_t *data, uint8_t data_len)
|
||||
}
|
||||
|
||||
/*
|
||||
* 테스트용
|
||||
* mpc?: burst만 한 번 발생시키는 테스트 명령
|
||||
* echo를 읽지 않고 초음파가 나가는지만 볼 때 사용
|
||||
* TEST
|
||||
* mpc?: burst only
|
||||
*/
|
||||
int cmd_mpc(const uint8_t *data, uint8_t data_len)
|
||||
{
|
||||
@@ -109,10 +380,7 @@ int cmd_mpc(const uint8_t *data, uint8_t data_len)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* 현재 Zephyr 포팅본은 2.1MHz SW burst 하나만 구현되어 있음
|
||||
* 레거시의 freq_option 값은 받아두되, 아직은 같은 burst 함수로 처리
|
||||
*/
|
||||
// 현재 2.1MHz 고정
|
||||
piezo_burst_sw((uint8_t)cycles);
|
||||
piezo_power_off();
|
||||
power_button_suspend(false);
|
||||
@@ -122,8 +390,8 @@ int cmd_mpc(const uint8_t *data, uint8_t data_len)
|
||||
}
|
||||
|
||||
/*
|
||||
* 테스트용
|
||||
* mec?: 단일 채널 burst + echo capture
|
||||
* TEST
|
||||
* mec?: 1 channel burst + echo capture
|
||||
*/
|
||||
int cmd_mec(const uint8_t *data, uint8_t data_len)
|
||||
{
|
||||
@@ -180,11 +448,8 @@ int cmd_mec(const uint8_t *data, uint8_t data_len)
|
||||
}
|
||||
|
||||
/*
|
||||
* 테스트용
|
||||
* mad?: 단일 채널 echo capture만 수행
|
||||
* - piezo burst는 하지 않고 echo ADC만 수행
|
||||
* - echo 샘플을 모은 뒤 reb: 패킷 1개 전송
|
||||
* - raa: 상태값은 전체 작업 성공/실패 요약
|
||||
* TEST
|
||||
* mad?: 1 channel echo capture only
|
||||
*/
|
||||
int cmd_mad(const uint8_t *data, uint8_t data_len)
|
||||
{
|
||||
@@ -232,238 +497,4 @@ int cmd_mad(const uint8_t *data, uint8_t data_len)
|
||||
|
||||
processing = false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* 테스트용(이전 정렬모드)
|
||||
* maa?: piezo echo sweep
|
||||
* - 순서:
|
||||
* 1. piezo 6채널 burst
|
||||
* 2. echo 샘플 채널별로 모으기
|
||||
* 3. reb: 패킷 6개 전송
|
||||
* 4. raa: 상태값은 전체 작업 성공/실패 요약
|
||||
*/
|
||||
int cmd_maa(const uint8_t *data, uint8_t data_len)
|
||||
{
|
||||
ARG_UNUSED(data);
|
||||
ARG_UNUSED(data_len);
|
||||
|
||||
uint8_t session = cmd_next_echo_session();
|
||||
|
||||
processing = true;
|
||||
power_button_suspend(true);
|
||||
|
||||
int status = piezo_measure_start_session();
|
||||
|
||||
if (status == ECHO_STATUS_OK) // Piezo GPIO, 전원, ADC 초기화/wake 성공
|
||||
{
|
||||
DBG_PRINTF("[MBB] piezo sweep start\r\n");
|
||||
status = piezo_measure_sweep();
|
||||
|
||||
for (uint8_t ch = 0; ch < PIEZO_NUM_CHANNELS; ch++)
|
||||
{
|
||||
DBG_PRINTF("[MBB] response reb: tx start ch=%d\r\n", ch);
|
||||
cmd_send_response_echo(session, ch, piezo_measure_channel_buffer(ch), piezo_config_get()->samples);
|
||||
}
|
||||
}
|
||||
|
||||
piezo_power_off();
|
||||
power_button_suspend(false);
|
||||
cmd_send_response_u16("raa:", (uint16_t)status); // 최종 상태값 raa: 전송
|
||||
DBG_PRINTF("[CMD] maa status=0x%04X\r\n", status);
|
||||
|
||||
processing = false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* 전체 측정 명령
|
||||
* mbb?: piezo echo sweep + 배터리 + IMU + IMU 온도 측정
|
||||
* - 성공 시 rbb 1개, reb 6개, raa 상태값 전송
|
||||
* - processing=true 구간에서는 내부 모니터의 I2C 접근을 막는 기준으로 사용
|
||||
*
|
||||
* - 순서:
|
||||
* 1. battery / imu / temp 측정
|
||||
* 2. rbb: 1개 전송
|
||||
* 3. piezo / echo ADC 준비
|
||||
* 4. piezo 6채널 burst
|
||||
* 5. echo 샘플 채널별로 모으기
|
||||
* 6. reb: 6개 전송
|
||||
* 7. raa: 최종 상태 전송
|
||||
*/
|
||||
int cmd_mbb(const uint8_t *data, uint8_t data_len)
|
||||
{
|
||||
ARG_UNUSED(data);
|
||||
ARG_UNUSED(data_len);
|
||||
|
||||
uint8_t session = cmd_next_echo_session();
|
||||
|
||||
int16_t accel[3];
|
||||
int16_t gyro[3];
|
||||
|
||||
processing = true;
|
||||
power_button_suspend(true);
|
||||
DBG_PRINTF("[MBB] cmd start\r\n");
|
||||
|
||||
int status = piezo_measure_start_session();
|
||||
|
||||
int batt_mv = -1;
|
||||
int16_t temp_cdeg = INT16_MIN;
|
||||
|
||||
if (status == ECHO_STATUS_OK) // Piezo GPIO, 전원, ADC 초기화/wake 성공
|
||||
{
|
||||
DBG_PRINTF("[MBB] battery read\r\n");
|
||||
batt_mv = battery_read_mv();
|
||||
|
||||
DBG_PRINTF("[MBB] imu/temp read\r\n");
|
||||
imu_read_with_temperature(accel, gyro, &temp_cdeg);
|
||||
|
||||
DBG_PRINTF("[MBB] response rbb: tx start\r\n");
|
||||
cmd_send_response_bundle((uint16_t)batt_mv, accel, gyro, temp_cdeg);
|
||||
|
||||
DBG_PRINTF("[MBB] piezo sweep start\r\n");
|
||||
status = piezo_measure_sweep();
|
||||
|
||||
for (uint8_t ch = 0; ch < PIEZO_NUM_CHANNELS; ch++)
|
||||
{
|
||||
DBG_PRINTF("[MBB] response reb: tx start ch=%d\r\n", ch);
|
||||
cmd_send_response_echo(session, ch, piezo_measure_channel_buffer(ch), piezo_config_get()->samples);
|
||||
}
|
||||
}
|
||||
|
||||
piezo_power_off();
|
||||
power_button_suspend(false);
|
||||
DBG_PRINTF("[MBB] done\r\n");
|
||||
cmd_send_response_u16("raa:", (uint16_t)status); // 최종 상태값 raa: 전송
|
||||
DBG_PRINTF("[CMD] mbb status=0x%04X\r\n", status);
|
||||
|
||||
processing = false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* 정렬모드
|
||||
* mtb?: IMU FIFO + piezo echo sweep
|
||||
* - 순서:
|
||||
* 1. IMU FIFO start
|
||||
* 2. piezo 6채널 burst
|
||||
* 3. echo 샘플 채널별로 모으기
|
||||
* 4. reb: 패킷 6개 전송
|
||||
* 5. raa: 상태값은 전체 작업 성공/실패 요약
|
||||
* - IMU FIFO 샘플은 rim: 패킷으로 전송
|
||||
*/
|
||||
int cmd_mtb(const uint8_t *data, uint8_t data_len)
|
||||
{
|
||||
ARG_UNUSED(data);
|
||||
ARG_UNUSED(data_len);
|
||||
|
||||
uint8_t session = cmd_next_echo_session();
|
||||
uint16_t rim_count = 0U;
|
||||
bool fifo_started = false;
|
||||
|
||||
processing = true;
|
||||
power_button_suspend(true);
|
||||
DBG_PRINTF("[MTB] cmd start\r\n");
|
||||
|
||||
int imu_ret = imu_fifo_start();
|
||||
if (imu_ret != 0)
|
||||
{
|
||||
DBG_PRINTF("[MTB] fifo start fail ret=%d\r\n", imu_ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
fifo_started = true;
|
||||
}
|
||||
|
||||
int status = piezo_measure_start_session();
|
||||
|
||||
if (status == ECHO_STATUS_OK) // Piezo GPIO, 전원, ADC 초기화/wake 성공
|
||||
{
|
||||
DBG_PRINTF("[MTB] piezo sweep start\r\n");
|
||||
status = piezo_measure_sweep();
|
||||
|
||||
for (uint8_t ch = 0; ch < PIEZO_NUM_CHANNELS; ch++)
|
||||
{
|
||||
DBG_PRINTF("[MTB] response reb: tx start ch=%d\r\n", ch);
|
||||
cmd_send_response_echo(session, ch, piezo_measure_channel_buffer(ch), piezo_config_get()->samples);
|
||||
}
|
||||
}
|
||||
|
||||
cmd_send_response_u16("raa:", (uint16_t)status); // 최종 상태값 raa: 전송
|
||||
|
||||
if (fifo_started)
|
||||
{
|
||||
int imu_ret = imu_fifo_read_latest(tx_rim_samples, IMU_FIFO_RIM_TARGET_SAMPLES, &rim_count);
|
||||
if (imu_ret != 0)
|
||||
{
|
||||
DBG_PRINTF("[MTB] fifo read fail ret=%d\r\n", imu_ret);
|
||||
rim_count = 0U;
|
||||
}
|
||||
}
|
||||
|
||||
cmd_send_response_rim(tx_rim_samples, rim_count); // IMU FIFO 샘플 rim: 전송
|
||||
DBG_PRINTF("[CMD] mtb status=0x%04X\r\n", status);
|
||||
|
||||
piezo_power_off();
|
||||
power_button_suspend(false);
|
||||
DBG_PRINTF("[CMD] mtb status=0x%04X rim=%u\r\n", status, rim_count);
|
||||
|
||||
processing = false;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* mcf?: piezo 측정 파라미터 읽기
|
||||
* 응답 rcf: + 설정값 echo back
|
||||
*/
|
||||
int cmd_mcf(const uint8_t *data, uint8_t data_len)
|
||||
{
|
||||
ARG_UNUSED(data);
|
||||
ARG_UNUSED(data_len);
|
||||
|
||||
const piezo_config_t *cfg = piezo_config_get();
|
||||
cmd_send_response_piezo_config("rcf:", cfg->freq, cfg->cycles, cfg->avg, cfg->delay_us, cfg->samples);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* mcs?: piezo 측정 파라미터 쓰기
|
||||
*/
|
||||
int cmd_mcs(const uint8_t *data, uint8_t data_len)
|
||||
{
|
||||
piezo_config_t cfg;
|
||||
|
||||
if (data_len < 10U)
|
||||
{
|
||||
cmd_send_response_piezo_config("rcs:", 0xFFFF, 0U, 0U, 0U, 0U);
|
||||
return 1;
|
||||
}
|
||||
|
||||
cmd_get_data_u16_be(data, data_len, 0, &cfg.freq);
|
||||
cmd_get_data_u16_be(data, data_len, 1, &cfg.cycles);
|
||||
cmd_get_data_u16_be(data, data_len, 2, &cfg.avg);
|
||||
cmd_get_data_u16_be(data, data_len, 3, &cfg.delay_us);
|
||||
cmd_get_data_u16_be(data, data_len, 4, &cfg.samples);
|
||||
|
||||
if (!piezo_config_validate(&cfg))
|
||||
{
|
||||
cmd_send_response_piezo_config("rcs:", 0xFFFF, cfg.cycles, cfg.avg, cfg.delay_us, cfg.samples);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int err = piezo_config_set(&cfg);
|
||||
if (err)
|
||||
{
|
||||
cmd_send_response_piezo_config("rcs:", 0xFFFD, cfg.cycles, cfg.avg, cfg.delay_us, cfg.samples);
|
||||
return 1;
|
||||
}
|
||||
|
||||
const piezo_config_t *saved_cfg = piezo_config_get();
|
||||
cmd_send_response_piezo_config("rcs:", saved_cfg->freq, saved_cfg->cycles, saved_cfg->avg, saved_cfg->delay_us, saved_cfg->samples);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* mls? → LED 상태 변경 → rls: + state echo back
|
||||
* 파라미터: [state(2B LE)] — led_state_t enum 값
|
||||
* 0=OFF, 4=DETACH_WARNING, 5=ALIGN_SEARCHING, 6=ALIGN_COMPLETE, 7=ERROR
|
||||
* 에러 응답: 0xFFFF=파라미터 없음, 0xFFFE=범위 초과 */
|
||||
}
|
||||
@@ -17,5 +17,6 @@ int cmd_mbb(const uint8_t *data, uint8_t data_len);
|
||||
int cmd_mtb(const uint8_t *data, uint8_t data_len);
|
||||
int cmd_mcf(const uint8_t *data, uint8_t data_len);
|
||||
int cmd_mcs(const uint8_t *data, uint8_t data_len);
|
||||
int cmd_mim(const uint8_t *data, uint8_t data_len);
|
||||
|
||||
#endif /* CMD_PIEZO_H__ */
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
#include "led_control.h"
|
||||
#include "cmd_sensor.h"
|
||||
|
||||
/*
|
||||
* msn: battery voltage read
|
||||
*/
|
||||
int cmd_msn(const uint8_t *data, uint8_t data_len)
|
||||
{
|
||||
ARG_UNUSED(data);
|
||||
@@ -28,6 +31,9 @@ int cmd_msn(const uint8_t *data, uint8_t data_len)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* msp: IMU data direct read
|
||||
*/
|
||||
int cmd_msp(const uint8_t *data, uint8_t data_len)
|
||||
{
|
||||
ARG_UNUSED(data);
|
||||
@@ -47,8 +53,9 @@ int cmd_msp(const uint8_t *data, uint8_t data_len)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* mst? -> IMU internal temperature -> rso: + temperature (degC x 100, BE)
|
||||
* Error response: 0xFFFF = IMU temperature read failed. */
|
||||
/*
|
||||
* mst: IMU temperature direct read
|
||||
*/
|
||||
int cmd_mst(const uint8_t *data, uint8_t data_len)
|
||||
{
|
||||
ARG_UNUSED(data);
|
||||
@@ -68,19 +75,18 @@ int cmd_mst(const uint8_t *data, uint8_t data_len)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 음수 온도도 2's complement로 그대로 전송 (앱이 int16로 해석) */
|
||||
// 음수 온도도 2's complement로 그대로 전송 (앱이 int16로 해석)
|
||||
cmd_send_response_u16("rso:", (uint16_t)t_cdeg);
|
||||
DBG_PRINTF("[CMD] mst -> %d.%02d C\r\n", t_cdeg / 100, (t_cdeg < 0 ? -t_cdeg : t_cdeg) % 100);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* 테스트용
|
||||
* mpa?: piezo TX/RX 전원 레일만 켬
|
||||
* mls: LED status set
|
||||
*/
|
||||
int cmd_mls(const uint8_t *data, uint8_t data_len)
|
||||
{
|
||||
/* 파라미터 부족: 에러 코드 0xFFFF 에코 */
|
||||
// 파라미터 부족: 에러 코드 0xFFFF 에코
|
||||
if (data_len < 2)
|
||||
{
|
||||
cmd_send_response_u16("rls:", 0xFFFF);
|
||||
@@ -88,10 +94,10 @@ int cmd_mls(const uint8_t *data, uint8_t data_len)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* state 필드: 2바이트 big-endian */
|
||||
// state 필드: 2바이트 big-endian
|
||||
uint16_t state = ((uint16_t)data[0] << 8) | (uint16_t)data[1];
|
||||
|
||||
/* 범위 초과: 에러 코드 0xFFFE 에코 */
|
||||
// 범위 초과: 에러 코드 0xFFFE 에코
|
||||
if (state > LED_STATE_ERROR)
|
||||
{
|
||||
cmd_send_response_u16("rls:", 0xFFFE);
|
||||
@@ -101,14 +107,12 @@ int cmd_mls(const uint8_t *data, uint8_t data_len)
|
||||
|
||||
led_set_state((led_state_t)state);
|
||||
|
||||
/* 정렬 LED 상태와 보호 판정 정책 연결
|
||||
* - ALIGN_SEARCHING/ALIGN_COMPLETE 동안 보호 판정 차단
|
||||
* - 다른 LED 상태에서는 기존 조건에 따라 보호 판정 재개
|
||||
*/
|
||||
battery_protection_set_alignment_mode((state == LED_STATE_ALIGN_SEARCHING) ||
|
||||
(state == LED_STATE_ALIGN_COMPLETE));
|
||||
// 정렬 LED 상태와 보호 판정 정책 연결
|
||||
// - ALIGN_SEARCHING/ALIGN_COMPLETE 동안 보호 판정 차단
|
||||
// - 다른 LED 상태에서는 기존 조건에 따라 보호 판정 재개
|
||||
battery_protection_set_alignment_mode((state == LED_STATE_ALIGN_SEARCHING) || (state == LED_STATE_ALIGN_COMPLETE));
|
||||
|
||||
/* OFF 상태에서는 정렬/측정용 FIFO를 함께 정리 */
|
||||
// OFF 상태에서는 정렬/측정용 FIFO를 함께 정리
|
||||
if (state == LED_STATE_OFF)
|
||||
{
|
||||
(void)imu_fifo_stop();
|
||||
@@ -117,7 +121,3 @@ int cmd_mls(const uint8_t *data, uint8_t data_len)
|
||||
cmd_send_response_u16("rls:", state);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* 커맨드 테이블
|
||||
*============================================================================*/
|
||||
|
||||
@@ -12,9 +12,7 @@
|
||||
#include "cmd_table.h"
|
||||
#include "debug_print.h"
|
||||
|
||||
/*==============================================================================
|
||||
* 파서 엔트리
|
||||
*============================================================================*/
|
||||
/* 파서 엔트리 */
|
||||
int ble_cmd_dispatch(const uint8_t *buf, uint16_t len)
|
||||
{
|
||||
//DBG_CORE("[PARSER] enter len=%u\r\n", len);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @file parser.h
|
||||
* @brief BLE command parser
|
||||
*
|
||||
* 4바이트 TAG + DATA + CRC16 패킷 파싱 및 디스패치
|
||||
* Packet format : [TAG 4B] [DATA NB] [CRC16 2B]
|
||||
******************************************************************************/
|
||||
#ifndef CMD_PARSER_H__
|
||||
#define CMD_PARSER_H__
|
||||
|
||||
@@ -26,9 +26,7 @@
|
||||
#include "imu_i2c.h"
|
||||
#include "main.h"
|
||||
|
||||
/*==============================================================================
|
||||
* ADC 디바이스트리 설정
|
||||
*============================================================================*/
|
||||
/* ADC 디바이스트리 설정 */
|
||||
#define ZEPHYR_USER_NODE DT_PATH(zephyr_user)
|
||||
|
||||
/* 디바이스트리에서 ADC 채널 스펙 가져오기 (gain, ref, acq time, input, resolution, oversampling) */
|
||||
@@ -36,78 +34,79 @@ static const struct adc_dt_spec battery_adc = ADC_DT_SPEC_GET_BY_NAME(ZEPHYR_USE
|
||||
|
||||
static int16_t adc_buffer;
|
||||
|
||||
/*==============================================================================
|
||||
* Module variables
|
||||
*============================================================================*/
|
||||
/* Module variables */
|
||||
volatile bool battery_saadc_done = false;
|
||||
volatile uint16_t info_batt = 0;
|
||||
|
||||
static struct k_timer battery_timer;
|
||||
static struct k_work battery_work;
|
||||
|
||||
/* 60초 내부 모니터 전용 카운터
|
||||
* - 앱 측정 명령(mbb?)이 없는 일반 상태에서만 사용
|
||||
* - mbb? 성공 시 연속 측정 판정으로 넘어가므로 0으로 초기화
|
||||
/*
|
||||
* 60초 내부 모니터 전용 카운터
|
||||
* - 앱 측정 명령(mbb?/mtb?)이 없는 일반 상태에서만 사용
|
||||
* - mbb? 성공 시 mbb? 측정 판정으로 넘어가므로 0으로 초기화
|
||||
*/
|
||||
static uint8_t periodic_low_battery_cnt;
|
||||
static uint8_t periodic_high_temperature_cnt;
|
||||
|
||||
/* 연속 측정(mbb?) 응답값 전용 카운터
|
||||
/*
|
||||
* 주기 측정(mbb?) 응답값 전용 카운터
|
||||
* - mbb? 응답에 포함된 배터리/IMU 온도 값으로 판정
|
||||
* - 측정 실패 시 호출되지 않으므로 기존 카운트 유지
|
||||
*/
|
||||
static uint8_t continuous_low_battery_cnt;
|
||||
static uint8_t continuous_high_temperature_cnt;
|
||||
|
||||
/* 정렬 상태 보호 판정 차단 플래그
|
||||
/*
|
||||
* 정렬 상태 보호 판정 차단 플래그
|
||||
* - mls?로 ALIGN_SEARCHING/ALIGN_COMPLETE 진입 시 true
|
||||
* - 정렬 중에는 배터리/온도 보호 판정 자체를 진행하지 않음
|
||||
*/
|
||||
static bool alignment_mode_active;
|
||||
|
||||
/* 최근 mbb? 성공 이후 내부 60초 모니터를 잠시 막는 기준 시각
|
||||
* - 연속 측정 중에는 mbb? 응답값으로 보호 판정
|
||||
/*
|
||||
* 최근 mbb? 성공 이후 내부 60초 모니터를 잠시 막는 기준 시각
|
||||
* - 주기 측정 중에는 mbb? 응답값으로 보호 판정
|
||||
* - 이 값보다 현재 시간이 작으면 60초 내부 모니터는 skip
|
||||
*/
|
||||
static int64_t continuous_monitor_active_until_ms;
|
||||
|
||||
#define BATTERY_MONITOR_LIMIT_COUNT 5U
|
||||
#define BATTERY_CONTINUOUS_MONITOR_LIMIT_COUNT 30U
|
||||
#define HIGH_TEMPERATURE_CDEG 4000 /* 40.00 C */
|
||||
#define CONTINUOUS_MONITOR_ACTIVE_MS 15000 /* mbb? 10초 주기 대비 여유 */
|
||||
#define BATTERY_MONITOR_LIMIT_COUNT 5U // 정렬모드(mtb?)나 주기 측정(mbb?)이 아닌 경우 60초 주기 5회 연속 저전압 감지 시 전원 OFF(5분)
|
||||
#define BATTERY_CONTINUOUS_MONITOR_LIMIT_COUNT 10U // 주기 측정 10회 연속 저전압 감지 시 전원 OFF(100초)
|
||||
#define HIGH_TEMPERATURE_CDEG 4000 // 40.00 C
|
||||
#define CONTINUOUS_MONITOR_ACTIVE_MS 15000 // mbb? 10초 주기 대비 여유
|
||||
|
||||
#define BATTERY_MONITOR_INTERVAL_MS 60000 /* 60초 주기 */
|
||||
#define BATTERY_MONITOR_INTERVAL_MS 60000 // 정렬모드(mtb?)나 주기 측정(mbb?)이 아닌 경우 저전압/고온 모니터 주기 60초
|
||||
|
||||
/*==============================================================================
|
||||
/*
|
||||
* 전압 변환 (ADC raw → mV 변환)
|
||||
* 공식: (raw × 600 / 4095) × 6 × 1.42
|
||||
* 정수 연산: raw × 600 × 6 × 142 / (4095 × 100)
|
||||
*============================================================================*/
|
||||
*/
|
||||
static int adc_raw_to_mv(int16_t raw)
|
||||
{
|
||||
if (raw < 0)
|
||||
{
|
||||
raw = 0;
|
||||
}
|
||||
/* 오버플로 방지: 600 × 6 × 142 = 511,200 → raw 최대 4095 → 4095 × 511200 = ~2B → int32 OK */
|
||||
// 오버플로 방지: 600 × 6 × 142 = 511,200 → raw 최대 4095 → 4095 × 511200 = ~2B → int32 OK
|
||||
int32_t mv = ((int32_t)raw * 600 * 6 * 142) / (4095 * 100);
|
||||
return (int)mv;
|
||||
}
|
||||
|
||||
static bool continuous_monitor_is_active(void)
|
||||
{
|
||||
/* 마지막 mbb? 성공 이후 CONTINUOUS_MONITOR_ACTIVE_MS 안쪽이면 연속 측정 중으로 간주 */
|
||||
return (continuous_monitor_active_until_ms > 0) &&
|
||||
(k_uptime_get() < continuous_monitor_active_until_ms);
|
||||
// 마지막 mbb? 성공 이후 CONTINUOUS_MONITOR_ACTIVE_MS 안쪽이면 주기 측정(mbb?) 중으로 간주
|
||||
return (continuous_monitor_active_until_ms > 0) && (k_uptime_get() < continuous_monitor_active_until_ms);
|
||||
}
|
||||
|
||||
static void record_battery_value(int batt_mv, uint8_t *cnt,
|
||||
uint8_t limit, const char *mode)
|
||||
/*
|
||||
* mode별 카운터만 전달받아 공통 판정 로직 사용
|
||||
* - periodic: 60초 내부 모니터, limit 5
|
||||
* - continuous: mbb? 응답값 기반, limit 10
|
||||
*/
|
||||
static void record_battery_value(int batt_mv, uint8_t *cnt, uint8_t limit, const char *mode)
|
||||
{
|
||||
/* mode별 카운터만 전달받아 공통 판정 로직 사용
|
||||
* - periodic: 60초 내부 모니터, limit 5
|
||||
* - continuous: mbb? 응답값 기반, limit 30
|
||||
*/
|
||||
if (batt_mv <= LOW_BATTERY_VOLTAGE)
|
||||
{
|
||||
if (*cnt < UINT8_MAX)
|
||||
@@ -115,8 +114,7 @@ static void record_battery_value(int batt_mv, uint8_t *cnt,
|
||||
(*cnt)++;
|
||||
}
|
||||
|
||||
DBG_PRINTF("[BATT] LOW! mode=%s cnt=%u/%u mv=%d\r\n",
|
||||
mode, *cnt, limit, batt_mv);
|
||||
DBG_PRINTF("[BATT] LOW! mode=%s cnt=%u/%u mv=%d\r\n", mode, *cnt, limit, batt_mv);
|
||||
|
||||
if (*cnt >= limit)
|
||||
{
|
||||
@@ -131,14 +129,14 @@ static void record_battery_value(int batt_mv, uint8_t *cnt,
|
||||
}
|
||||
}
|
||||
|
||||
static void record_temperature_value(int16_t temp_cdeg, uint8_t *cnt,
|
||||
uint8_t limit, const char *mode)
|
||||
/*
|
||||
* IMU 온도는 centi-degree 단위
|
||||
* - 4000 = 40.00 C
|
||||
* - 40.00 C 이상이면 고온 카운트 증가
|
||||
* - 기준 미만이면 해당 mode 카운터 초기화
|
||||
*/
|
||||
static void record_temperature_value(int16_t temp_cdeg, uint8_t *cnt, uint8_t limit, const char *mode)
|
||||
{
|
||||
/* IMU 온도는 centi-degree 단위
|
||||
* - 4000 = 40.00 C
|
||||
* - 40.00 C 이상이면 고온 카운트 증가
|
||||
* - 기준 미만이면 해당 mode 카운터 초기화
|
||||
*/
|
||||
if (temp_cdeg >= HIGH_TEMPERATURE_CDEG)
|
||||
{
|
||||
if (*cnt < UINT8_MAX)
|
||||
@@ -166,20 +164,17 @@ static void record_temperature_value(int16_t temp_cdeg, uint8_t *cnt,
|
||||
}
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* Public functions
|
||||
*============================================================================*/
|
||||
|
||||
/* Public functions */
|
||||
void battery_adc_init(void)
|
||||
{
|
||||
/* ADC 디바이스 준비 상태 확인 */
|
||||
// ADC 디바이스 준비 상태 확인
|
||||
if (!adc_is_ready_dt(&battery_adc))
|
||||
{
|
||||
DBG_PRINTF("[BATT] ADC device not ready\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* 디바이스트리 설정으로 채널 구성 */
|
||||
// 디바이스트리 설정으로 채널 구성
|
||||
int err = adc_channel_setup_dt(&battery_adc);
|
||||
if (err)
|
||||
{
|
||||
@@ -195,7 +190,7 @@ void battery_adc_init(void)
|
||||
|
||||
int battery_read_mv(void)
|
||||
{
|
||||
/* 매번 채널 재설정 (다른 센서와 SAADC 공유 시 필요) */
|
||||
// 매번 채널 재설정 (다른 센서와 SAADC 공유 시 필요)
|
||||
int err = adc_channel_setup_dt(&battery_adc);
|
||||
if (err)
|
||||
{
|
||||
@@ -203,7 +198,7 @@ int battery_read_mv(void)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 디바이스트리에서 시퀀스 초기화 (channels, resolution, oversampling) */
|
||||
// 디바이스트리에서 시퀀스 초기화 (channels, resolution, oversampling)
|
||||
struct adc_sequence seq = {0};
|
||||
|
||||
err = adc_sequence_init_dt(&battery_adc, &seq);
|
||||
@@ -216,7 +211,7 @@ int battery_read_mv(void)
|
||||
seq.buffer = &adc_buffer;
|
||||
seq.buffer_size = sizeof(adc_buffer);
|
||||
|
||||
/* ADC 읽기 */
|
||||
// ADC 읽기
|
||||
err = adc_read_dt(&battery_adc, &seq);
|
||||
if (err)
|
||||
{
|
||||
@@ -233,13 +228,13 @@ int battery_read_mv(void)
|
||||
return mv;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 정렬 상태 진입/해제는 mls? 명령에서 전달
|
||||
* - enabled true: 보호 판정 중지 + 기존 누적값 초기화
|
||||
* - enabled false: 다음 조건부터 다시 판정 가능
|
||||
*/
|
||||
void battery_protection_set_alignment_mode(bool enabled)
|
||||
{
|
||||
/* 정렬 상태 진입/해제는 mls? 명령에서 전달
|
||||
* - enabled true: 보호 판정 중지 + 기존 누적값 초기화
|
||||
* - enabled false: 다음 조건부터 다시 판정 가능
|
||||
*/
|
||||
alignment_mode_active = enabled;
|
||||
if (enabled)
|
||||
{
|
||||
@@ -250,55 +245,43 @@ void battery_protection_set_alignment_mode(bool enabled)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* mbb? 측정 성공 시에만 호출
|
||||
* - 실패한 측정은 호출되지 않아서 continuous 카운트 유지
|
||||
* - 정렬 중에는 응답값이 있어도 보호 판정 제외
|
||||
* - mbb?가 들어온 직후에는 내부 60초 모니터 대신 응답값 기반 판정 사용
|
||||
*/
|
||||
void battery_protection_record_continuous(int batt_mv, int16_t temp_cdeg)
|
||||
{
|
||||
/* mbb? 측정 성공 시에만 호출
|
||||
* - 실패한 측정은 호출되지 않아서 continuous 카운트 유지
|
||||
* - 정렬 중에는 응답값이 있어도 보호 판정 제외
|
||||
*/
|
||||
// 정렬모드 중에는 continuous 보호 판정 제외
|
||||
if (alignment_mode_active)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* mbb?가 들어온 직후에는 내부 60초 모니터 대신 응답값 기반 판정 사용
|
||||
* - periodic 카운터는 섞이지 않게 초기화
|
||||
* - continuous 카운터는 아래 record_* 함수에서 값에 따라 증가/초기화
|
||||
*/
|
||||
// 15초 후 일반 60초 주기 모니터 재개
|
||||
continuous_monitor_active_until_ms = k_uptime_get() + CONTINUOUS_MONITOR_ACTIVE_MS;
|
||||
|
||||
// periodic 모니터 카운트 초기화
|
||||
periodic_low_battery_cnt = 0;
|
||||
periodic_high_temperature_cnt = 0;
|
||||
|
||||
record_battery_value(batt_mv,
|
||||
&continuous_low_battery_cnt,
|
||||
BATTERY_CONTINUOUS_MONITOR_LIMIT_COUNT,
|
||||
"continuous");
|
||||
record_temperature_value(temp_cdeg,
|
||||
&continuous_high_temperature_cnt,
|
||||
BATTERY_CONTINUOUS_MONITOR_LIMIT_COUNT,
|
||||
"continuous");
|
||||
record_battery_value(batt_mv, &continuous_low_battery_cnt, BATTERY_CONTINUOUS_MONITOR_LIMIT_COUNT, "continuous");
|
||||
record_temperature_value(temp_cdeg, &continuous_high_temperature_cnt, BATTERY_CONTINUOUS_MONITOR_LIMIT_COUNT, "continuous");
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* 주기 모니터링 타이머
|
||||
*============================================================================*/
|
||||
|
||||
/* k_work 핸들러: 스레드 컨텍스트에서 ADC 읽기 (adc_read는 블로킹이라 ISR 불가) */
|
||||
|
||||
static void battery_work_handler(struct k_work *work)
|
||||
{
|
||||
ARG_UNUSED(work);
|
||||
|
||||
/* 보호 판정 제외 구간
|
||||
* - 정렬 중: 정책상 보호 판정 미진행
|
||||
* - 최근 mbb? 성공 직후: mbb? 응답값 기반 continuous 판정이 담당
|
||||
*/
|
||||
// 일반 60초 주기 보호 판정 제외 구간: 정렬모드 또는 주기 측정
|
||||
if (alignment_mode_active || continuous_monitor_is_active())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* 측정 처리 중이거나 IMU FIFO 사용 중(정렬모드)이면 I2C 접근 충돌 방지 */
|
||||
// I2C 접근 충돌 방지: 긴 측정 명령 처리 중이거나 IMU FIFO 동작 중
|
||||
if (processing || imu_fifo_is_active())
|
||||
{
|
||||
return;
|
||||
@@ -311,14 +294,8 @@ static void battery_work_handler(struct k_work *work)
|
||||
return;
|
||||
}
|
||||
|
||||
/* 일반 상태의 60초 내부 모니터 판정
|
||||
* - 저전압이면 periodic_low_battery_cnt 증가
|
||||
* - 정상 전압이면 periodic_low_battery_cnt 초기화
|
||||
*/
|
||||
record_battery_value(batt,
|
||||
&periodic_low_battery_cnt,
|
||||
BATTERY_MONITOR_LIMIT_COUNT,
|
||||
"periodic");
|
||||
// 정렬모드나 주기 측정 중이 아닌 경우 60초 내부 모니터 판정(배터리)
|
||||
record_battery_value(batt, &periodic_low_battery_cnt, BATTERY_MONITOR_LIMIT_COUNT, "periodic");
|
||||
|
||||
int16_t temp_cdeg = 0;
|
||||
int temp_ret = imu_read_temperature_cdeg(&temp_cdeg);
|
||||
@@ -329,14 +306,8 @@ static void battery_work_handler(struct k_work *work)
|
||||
return;
|
||||
}
|
||||
|
||||
/* 일반 상태의 IMU 온도 판정
|
||||
* - 고온이면 periodic_high_temperature_cnt 증가
|
||||
* - 정상 온도이면 periodic_high_temperature_cnt 초기화
|
||||
*/
|
||||
record_temperature_value(temp_cdeg,
|
||||
&periodic_high_temperature_cnt,
|
||||
BATTERY_MONITOR_LIMIT_COUNT,
|
||||
"periodic");
|
||||
// 정렬모드나 주기 측정 중이 아닌 경우 60초 내부 모니터 판정(온도)
|
||||
record_temperature_value(temp_cdeg, &periodic_high_temperature_cnt, BATTERY_MONITOR_LIMIT_COUNT, "periodic");
|
||||
}
|
||||
|
||||
/* 타이머 ISR → k_work 예약 */
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
#define ECHO_MISO_NODE DT_NODELABEL(echo_miso)
|
||||
#define ECHO_CS_NODE DT_NODELABEL(echo_cs)
|
||||
|
||||
/* SPIM END event 대기 최대 반복 횟수 */
|
||||
#define ECHO_ADC_SPIM_END_TIMEOUT_LOOPS 100000U
|
||||
|
||||
/* ADC SPI 핀 */
|
||||
static const struct gpio_dt_spec echo_sclk = GPIO_DT_SPEC_GET(ECHO_SCLK_NODE, gpios);
|
||||
static const struct gpio_dt_spec echo_miso = GPIO_DT_SPEC_GET(ECHO_MISO_NODE, gpios);
|
||||
@@ -42,7 +45,7 @@ static uint32_t gpio_abs_pin(const struct gpio_dt_spec *spec)
|
||||
return NRF_GPIO_PIN_MAP(port, spec->pin);
|
||||
}
|
||||
|
||||
/* SPIM RX 전송 */
|
||||
/* SPIM RX 전송: ADC wake/dummy read용 nrfx 경로 */
|
||||
static int echo_adc_transfer_rx(uint8_t *buf, size_t len)
|
||||
{
|
||||
nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_XFER_RX(buf, len);
|
||||
@@ -68,37 +71,7 @@ static void echo_adc_unpack_samples(uint16_t *samples, uint16_t num_samples)
|
||||
}
|
||||
}
|
||||
|
||||
/* 무응답 샘플 확인 */
|
||||
static bool echo_adc_samples_all_zero(const uint16_t *samples, uint16_t num_samples)
|
||||
{
|
||||
for (uint16_t i = 0; i < num_samples; i++)
|
||||
{
|
||||
if (samples[i] != 0U)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* 안정 우선 캡처 */
|
||||
static int echo_adc_capture_slow(uint16_t *samples, uint16_t num_samples)
|
||||
{
|
||||
for (uint16_t i = 0; i < num_samples; i++)
|
||||
{
|
||||
int err = echo_adc_transfer_rx(&raw_capture[i * 2U], 2U);
|
||||
if (err)
|
||||
{
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
echo_adc_unpack_samples(samples, num_samples);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 속도 우선 캡처 */
|
||||
/* 샘플마다 CS를 직접 제어하고 SPIM RX 2바이트를 수행 */
|
||||
static int echo_adc_capture_fast(uint16_t *samples, uint16_t num_samples)
|
||||
{
|
||||
NRF_SPIM_Type *spim = echo_spim.p_reg;
|
||||
@@ -116,9 +89,19 @@ static int echo_adc_capture_fast(uint16_t *samples, uint16_t num_samples)
|
||||
cs_port->OUTCLR = cs_mask;
|
||||
spim->TASKS_START = 1;
|
||||
|
||||
/* 전송 완료 대기 */
|
||||
/* 전송 완료 대기: SPIM이 꼬인 경우 IRQ lock 상태로 영구 정지하지 않도록 timeout 처리 */
|
||||
uint32_t timeout = ECHO_ADC_SPIM_END_TIMEOUT_LOOPS;
|
||||
while (!spim->EVENTS_END)
|
||||
{
|
||||
if (--timeout == 0U)
|
||||
{
|
||||
spim->EVENTS_STOPPED = 0;
|
||||
spim->TASKS_STOP = 1;
|
||||
cs_port->OUTSET = cs_mask;
|
||||
spim->EVENTS_END = 0;
|
||||
DBG_PRINTF("[ECHO] SPIM END timeout sample=%u\r\n", i);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
|
||||
cs_port->OUTSET = cs_mask;
|
||||
@@ -130,14 +113,14 @@ static int echo_adc_capture_fast(uint16_t *samples, uint16_t num_samples)
|
||||
|
||||
int echo_adc_init(void)
|
||||
{
|
||||
/* 중복 초기화 방지 */
|
||||
// 중복 초기화 방지
|
||||
if (echo_adc_initialized || nrfx_spim_init_check(&echo_spim))
|
||||
{
|
||||
echo_adc_initialized = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* GPIO 준비 확인 */
|
||||
// GPIO 준비 확인
|
||||
const struct gpio_dt_spec *pins[] = { &echo_sclk, &echo_miso, &echo_cs };
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(pins); i++)
|
||||
@@ -190,7 +173,7 @@ int echo_adc_wake(void)
|
||||
|
||||
int echo_adc_capture(uint16_t *samples, uint16_t num_samples)
|
||||
{
|
||||
/* 입력값 검사 */
|
||||
// 입력값 검사
|
||||
if (samples == NULL)
|
||||
{
|
||||
return -EINVAL;
|
||||
@@ -207,14 +190,5 @@ int echo_adc_capture(uint16_t *samples, uint16_t num_samples)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* 빠른 캡처 우선 */
|
||||
err = echo_adc_capture_fast(samples, num_samples);
|
||||
if ((err == 0) && !echo_adc_samples_all_zero(samples, num_samples))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 실패 시 안정 캡처 */
|
||||
DBG_PRINTF("[ECHO] fast capture fallback err=%d\r\n", err);
|
||||
return echo_adc_capture_slow(samples, num_samples);
|
||||
return echo_adc_capture_fast(samples, num_samples);
|
||||
}
|
||||
|
||||
+36
-49
@@ -1,16 +1,7 @@
|
||||
/*******************************************************************************
|
||||
* @file imu_i2c.c
|
||||
* @brief ICM42670P IMU Driver (Zephyr I2C API)
|
||||
*
|
||||
* 동작 방식 (매 측정 시):
|
||||
* 1) GYRO_CONFIG0 = 0x09 → ±2000dps, 100Hz ODR
|
||||
* 2) ACCEL_CONFIG0 = 0x29 → ±4g, 100Hz ODR
|
||||
* 3) PWR_MGMT0 = 0x0F → accel(low-noise) + gyro(low-noise) ON
|
||||
* 4) 80ms 대기 (자이로 스타트업 최소 45ms + 여유)
|
||||
* 5) 0x0B부터 12바이트 연속 읽기: accel XYZ(6B) + gyro XYZ(6B)
|
||||
* 6) 빅엔디안 → int16_t 변환
|
||||
* 7) PWR_MGMT0 = 0x00 → 슬립 (전력 절감)
|
||||
*
|
||||
|
||||
* I2C 핀: SCL=P1.14, SDA=P1.15 (overlay에서 설정)
|
||||
******************************************************************************/
|
||||
|
||||
@@ -29,34 +20,34 @@
|
||||
* 디바이스트리 / I2C 설정
|
||||
*============================================================================*/
|
||||
#define IMU_I2C_NODE DT_NODELABEL(i2c0)
|
||||
#define IMU_I2C_ADDR 0x68 /* ICM42670P 기본 I2C 주소 (AD0=LOW) */
|
||||
#define IMU_I2C_ADDR 0x68 // ICM42670P 기본 I2C 주소 (AD0=LOW)
|
||||
|
||||
/*==============================================================================
|
||||
* ICM42670P 레지스터 주소
|
||||
*============================================================================*/
|
||||
#define REG_PWR_MGMT0 0x1F /* 전원 관리: accel/gyro 동작 모드 */
|
||||
#define REG_GYRO_CONFIG0 0x20 /* 자이로 FSR + ODR 설정 */
|
||||
#define REG_ACCEL_CONFIG0 0x21 /* 가속도 FSR + ODR 설정 */
|
||||
#define REG_GYRO_CONFIG1 0x23 /* 자이로 LN 필터 BW */
|
||||
#define REG_ACCEL_CONFIG1 0x24 /* 가속도 LN 필터 BW */
|
||||
#define REG_FIFO_CONFIG1 0x28 /* FIFO bypass/mode */
|
||||
#define REG_FIFO_CONFIG2 0x29 /* FIFO watermark LSB */
|
||||
#define REG_FIFO_CONFIG3 0x2A /* FIFO watermark MSB */
|
||||
#define REG_INTF_CONFIG0 0x35 /* FIFO count/endian 설정 */
|
||||
#define REG_FIFO_COUNTH 0x3D /* FIFO count high */
|
||||
#define REG_FIFO_DATA 0x3F /* FIFO data pop */
|
||||
#define REG_BLK_SEL_W 0x79 /* MREG write block */
|
||||
#define REG_MADDR_W 0x7A /* MREG write addr */
|
||||
#define REG_M_W 0x7B /* MREG write data */
|
||||
#define REG_BLK_SEL_R 0x7C /* MREG read block */
|
||||
#define REG_MADDR_R 0x7D /* MREG read addr */
|
||||
#define REG_M_R 0x7E /* MREG read data */
|
||||
#define REG_TEMP_DATA1 0x09 /* IMU 내부 온도 상위 바이트 */
|
||||
#define REG_ACCEL_DATA_X1 0x0B /* 가속도 X축 상위 바이트 (시작 레지스터) */
|
||||
#define REG_WHO_AM_I 0x75 /* WHOAMI 레지스터 */
|
||||
#define REG_MCLK_RDY 0x00 /* MCLK ready */
|
||||
#define REG_SIGNAL_PATH_RESET 0x02 /* FIFO flush */
|
||||
#define ICM_WHOAMI 0x67 /* ICM42670P 식별값 */
|
||||
#define REG_PWR_MGMT0 0x1F // 전원 관리: accel/gyro 동작 모드
|
||||
#define REG_GYRO_CONFIG0 0x20 // 자이로 FSR + ODR 설정
|
||||
#define REG_ACCEL_CONFIG0 0x21 // 가속도 FSR + ODR 설정
|
||||
#define REG_GYRO_CONFIG1 0x23 // 자이로 LN 필터 BW
|
||||
#define REG_ACCEL_CONFIG1 0x24 // 가속도 LN 필터 BW
|
||||
#define REG_FIFO_CONFIG1 0x28 // FIFO bypass/mode
|
||||
#define REG_FIFO_CONFIG2 0x29 // FIFO watermark LSB
|
||||
#define REG_FIFO_CONFIG3 0x2A // FIFO watermark MSB
|
||||
#define REG_INTF_CONFIG0 0x35 // FIFO count/endian 설정
|
||||
#define REG_FIFO_COUNTH 0x3D // FIFO count high
|
||||
#define REG_FIFO_DATA 0x3F // FIFO data pop
|
||||
#define REG_BLK_SEL_W 0x79 // MREG write block
|
||||
#define REG_MADDR_W 0x7A // MREG write addr
|
||||
#define REG_M_W 0x7B // MREG write data
|
||||
#define REG_BLK_SEL_R 0x7C // MREG read block
|
||||
#define REG_MADDR_R 0x7D // MREG read addr
|
||||
#define REG_M_R 0x7E // MREG read data
|
||||
#define REG_TEMP_DATA1 0x09 // IMU 내부 온도 상위 바이트
|
||||
#define REG_ACCEL_DATA_X1 0x0B // 가속도 X축 상위 바이트 (시작 레지스터)
|
||||
#define REG_WHO_AM_I 0x75 // WHOAMI 레지스터
|
||||
#define REG_MCLK_RDY 0x00 // MCLK ready
|
||||
#define REG_SIGNAL_PATH_RESET 0x02 // FIFO flush
|
||||
#define ICM_WHOAMI 0x67 // ICM42670P 식별값
|
||||
|
||||
/* 자이로 스타트업 대기 시간 (ms) — 스펙 최소 45ms, 80ms로 여유 확보 */
|
||||
#define IMU_GYRO_STARTUP_MS 80
|
||||
@@ -95,9 +86,7 @@ static const struct device *i2c_bus;
|
||||
static bool fifo_active;
|
||||
static uint8_t fifo_raw[IMU_FIFO_PACKET_BYTES * IMU_FIFO_MAX_RECORDS];
|
||||
|
||||
/*==============================================================================
|
||||
* 내부 I2C 래퍼
|
||||
*============================================================================*/
|
||||
/* 내부 I2C 래퍼 */
|
||||
|
||||
/* 레지스터 1바이트 쓰기: [reg, val] 2바이트 TX */
|
||||
static int imu_write_reg(uint8_t reg, uint8_t val)
|
||||
@@ -375,9 +364,7 @@ static uint16_t imu_fifo_compact_records(uint16_t record_count)
|
||||
return write_idx;
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* 공개 API
|
||||
*============================================================================*/
|
||||
/* 공개 API */
|
||||
|
||||
int imu_init(void)
|
||||
{
|
||||
@@ -407,31 +394,31 @@ int imu_read(int16_t accel[3], int16_t gyro[3])
|
||||
uint8_t raw[12];
|
||||
int ret;
|
||||
|
||||
/* 자이로 설정: ±2000dps FSR, 100Hz ODR */
|
||||
// 자이로 설정: ±2000dps FSR, 100Hz ODR
|
||||
ret = imu_write_reg(REG_GYRO_CONFIG0, 0x09);
|
||||
if (ret) { DBG_PRINTF("[IMU] FAIL — gyro config write (ret=%d)\r\n", ret); return -1; }
|
||||
|
||||
/* 가속도 설정: ±4g FSR, 100Hz ODR */
|
||||
// 가속도 설정: ±4g FSR, 100Hz ODR
|
||||
ret = imu_write_reg(REG_ACCEL_CONFIG0, 0x29);
|
||||
if (ret) { DBG_PRINTF("[IMU] FAIL — accel config write (ret=%d)\r\n", ret); return -1; }
|
||||
|
||||
ret = imu_set_data_big_endian();
|
||||
if (ret) { DBG_PRINTF("[IMU] FAIL — endian config write (ret=%d)\r\n", ret); return -1; }
|
||||
|
||||
/* 전원 ON: accel(저잡음) + gyro(저잡음) 모두 활성화 */
|
||||
// 전원 ON: accel(저잡음) + gyro(저잡음) 모두 활성화
|
||||
ret = imu_write_reg(REG_PWR_MGMT0, 0x0F);
|
||||
if (ret) { DBG_PRINTF("[IMU] FAIL — power on write (ret=%d)\r\n", ret); return -1; }
|
||||
|
||||
/* 자이로 스타트업 대기 */
|
||||
// 자이로 스타트업 대기
|
||||
k_msleep(IMU_GYRO_STARTUP_MS);
|
||||
|
||||
/* ACCEL_DATA_X1(0x0B)부터 12바이트 연속 읽기
|
||||
* [0..5] = accel X,Y,Z (MSB first)
|
||||
* [6..11] = gyro X,Y,Z (MSB first) */
|
||||
// ACCEL_DATA_X1(0x0B)부터 12바이트 연속 읽기
|
||||
// [0..5] = accel X,Y,Z (MSB first)
|
||||
// [6..11] = gyro X,Y,Z (MSB first)
|
||||
ret = imu_read_regs(REG_ACCEL_DATA_X1, raw, 12);
|
||||
if (ret) { DBG_PRINTF("[IMU] FAIL — data read (ret=%d)\r\n", ret); return -2; }
|
||||
|
||||
/* 빅엔디안 → int16_t 변환 */
|
||||
// 빅엔디안 → int16_t 변환
|
||||
accel[0] = (int16_t)((raw[0] << 8) | raw[1]);
|
||||
accel[1] = (int16_t)((raw[2] << 8) | raw[3]);
|
||||
accel[2] = (int16_t)((raw[4] << 8) | raw[5]);
|
||||
@@ -441,7 +428,7 @@ int imu_read(int16_t accel[3], int16_t gyro[3])
|
||||
|
||||
//DBG_PRINTF("[IMU] msp: A=(%6d,%6d,%6d) G=(%6d,%6d,%6d)\r\n", accel[0], accel[1], accel[2], gyro[0], gyro[1], gyro[2]);
|
||||
|
||||
/* 슬립 모드: 전력 절감 */
|
||||
// 슬립 모드: 전력 절감
|
||||
imu_write_reg(REG_PWR_MGMT0, 0x00);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -11,33 +11,25 @@
|
||||
#include "led_control.h"
|
||||
#include "debug_print.h"
|
||||
|
||||
/*==============================================================================
|
||||
* Devicetree LED specs
|
||||
*============================================================================*/
|
||||
/* Devicetree LED specs */
|
||||
#define LED_BLE_NODE DT_NODELABEL(led_ble)
|
||||
#define FUNCTION_LED_NODE DT_NODELABEL(function_led)
|
||||
|
||||
static const struct gpio_dt_spec led_ble = GPIO_DT_SPEC_GET(LED_BLE_NODE, gpios);
|
||||
static const struct gpio_dt_spec function_led = GPIO_DT_SPEC_GET(FUNCTION_LED_NODE, gpios);
|
||||
|
||||
/*==============================================================================
|
||||
* Color constants
|
||||
*============================================================================*/
|
||||
/* Color constants */
|
||||
#define COLOR_NONE 0
|
||||
#define COLOR_GREEN 1
|
||||
#define COLOR_ORANGE 2
|
||||
|
||||
/*==============================================================================
|
||||
* Error pattern constants (State 7)
|
||||
*============================================================================*/
|
||||
/* Error pattern constants (State 7) */
|
||||
#define ERROR_BLINK_ON_MS 166
|
||||
#define ERROR_BLINK_OFF_MS 166
|
||||
#define ERROR_BLINK_COUNT 3
|
||||
#define ERROR_PAUSE_MS 1000
|
||||
|
||||
/*==============================================================================
|
||||
* Pattern table
|
||||
*============================================================================*/
|
||||
/* Pattern table */
|
||||
typedef struct {
|
||||
uint32_t on_ms;
|
||||
uint32_t off_ms;
|
||||
@@ -56,9 +48,7 @@ static const led_pattern_t m_patterns[LED_STATE_COUNT] = {
|
||||
[LED_STATE_ERROR] = { 0, 0, COLOR_ORANGE, true },
|
||||
};
|
||||
|
||||
/*==============================================================================
|
||||
* Module variables
|
||||
*============================================================================*/
|
||||
/* Module variables */
|
||||
static struct k_timer m_led_timer;
|
||||
|
||||
static led_state_t m_current_state = LED_STATE_OFF;
|
||||
@@ -68,9 +58,7 @@ static bool m_phase_on;
|
||||
static uint8_t m_error_blink_cnt;
|
||||
static uint8_t m_error_phase;
|
||||
|
||||
/*==============================================================================
|
||||
* GPIO helpers
|
||||
*============================================================================*/
|
||||
/* GPIO helpers */
|
||||
static inline void led_ble_on(void) { gpio_pin_set_dt(&led_ble, 1); }
|
||||
static inline void led_ble_off(void) { gpio_pin_set_dt(&led_ble, 0); }
|
||||
static inline void function_led_on(void) { gpio_pin_set_dt(&function_led, 1); }
|
||||
@@ -89,18 +77,14 @@ static void led_color_on(uint8_t color)
|
||||
else if (color == COLOR_ORANGE) function_led_on();
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* Timer helper
|
||||
*============================================================================*/
|
||||
/* Timer helper */
|
||||
static void timer_start_ms(uint32_t ms)
|
||||
{
|
||||
if (ms == 0) return;
|
||||
k_timer_start(&m_led_timer, K_MSEC(ms), K_NO_WAIT);
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* Error pattern state machine
|
||||
*============================================================================*/
|
||||
/* Error pattern state machine */
|
||||
static void error_pattern_start(void)
|
||||
{
|
||||
m_error_blink_cnt = 0;
|
||||
@@ -113,12 +97,12 @@ static void error_pattern_tick(void)
|
||||
{
|
||||
switch (m_error_phase)
|
||||
{
|
||||
case 0: /* ON period done -> OFF */
|
||||
case 0: // ON period done -> OFF
|
||||
led_all_off();
|
||||
m_error_phase = 1;
|
||||
timer_start_ms(ERROR_BLINK_OFF_MS);
|
||||
break;
|
||||
case 1: /* OFF period done */
|
||||
case 1: // OFF period done
|
||||
m_error_blink_cnt++;
|
||||
if (m_error_blink_cnt < ERROR_BLINK_COUNT) {
|
||||
m_error_phase = 0;
|
||||
@@ -129,7 +113,7 @@ static void error_pattern_tick(void)
|
||||
timer_start_ms(ERROR_PAUSE_MS);
|
||||
}
|
||||
break;
|
||||
case 2: /* Pause done -> restart */
|
||||
case 2: // Pause done -> restart
|
||||
m_error_blink_cnt = 0;
|
||||
m_error_phase = 0;
|
||||
led_color_on(COLOR_ORANGE);
|
||||
@@ -140,9 +124,7 @@ static void error_pattern_tick(void)
|
||||
}
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* Timer callback
|
||||
*============================================================================*/
|
||||
/* Timer callback */
|
||||
static void led_timer_handler(struct k_timer *timer)
|
||||
{
|
||||
ARG_UNUSED(timer);
|
||||
@@ -157,7 +139,7 @@ static void led_timer_handler(struct k_timer *timer)
|
||||
|
||||
if (m_phase_on)
|
||||
{
|
||||
/* ON -> OFF transition */
|
||||
// ON -> OFF transition
|
||||
led_all_off();
|
||||
m_phase_on = false;
|
||||
|
||||
@@ -171,12 +153,12 @@ static void led_timer_handler(struct k_timer *timer)
|
||||
led_all_off();
|
||||
m_current_state = LED_STATE_OFF;
|
||||
}
|
||||
/* POWER_ON: stays lit, no timer */
|
||||
// POWER_ON: stays lit, no timer
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* OFF -> ON transition */
|
||||
// OFF -> ON transition
|
||||
if (p->repeat)
|
||||
{
|
||||
led_color_on(p->color);
|
||||
@@ -186,10 +168,7 @@ static void led_timer_handler(struct k_timer *timer)
|
||||
}
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* Public functions
|
||||
*============================================================================*/
|
||||
|
||||
/* Public functions */
|
||||
void led_init(void)
|
||||
{
|
||||
gpio_pin_configure_dt(&led_ble, GPIO_OUTPUT_INACTIVE);
|
||||
@@ -242,5 +221,5 @@ void led_ble_solid(void)
|
||||
k_timer_stop(&m_led_timer);
|
||||
led_all_off();
|
||||
led_ble_on();
|
||||
m_current_state = LED_STATE_OFF; /* no pattern running */
|
||||
m_current_state = LED_STATE_OFF; // no pattern running
|
||||
}
|
||||
|
||||
@@ -16,13 +16,13 @@
|
||||
typedef enum
|
||||
{
|
||||
LED_STATE_OFF = 0,
|
||||
LED_STATE_POWER_ON, /* 1: Green ON 2s -> stay on */
|
||||
LED_STATE_POWER_OFF, /* 2: Green ON 2s -> OFF */
|
||||
LED_STATE_ADVERTISING, /* 3: Green blink 500ms/500ms */
|
||||
LED_STATE_DETACH_WARNING, /* 4: Green 1s on / 3s off */
|
||||
LED_STATE_ALIGN_SEARCHING, /* 5: Orange blink 1s/1s */
|
||||
LED_STATE_ALIGN_COMPLETE, /* 6: Green 3s on / 1s off */
|
||||
LED_STATE_ERROR, /* 7: Orange 3Hz x3 / 1s off */
|
||||
LED_STATE_POWER_ON, // 1: Green ON 2s -> stay on
|
||||
LED_STATE_POWER_OFF, // 2: Green ON 2s -> OFF
|
||||
LED_STATE_ADVERTISING, // 3: Green blink 500ms/500ms
|
||||
LED_STATE_DETACH_WARNING, // 4: Green 1s on / 3s off
|
||||
LED_STATE_ALIGN_SEARCHING, // 5: Orange blink 1s/1s
|
||||
LED_STATE_ALIGN_COMPLETE, // 6: Green 3s on / 1s off
|
||||
LED_STATE_ERROR, // 7: Orange 3Hz x3 / 1s off
|
||||
LED_STATE_COUNT
|
||||
} led_state_t;
|
||||
|
||||
|
||||
+26
-24
@@ -123,13 +123,13 @@ static void piezo_drive_idle(void)
|
||||
|
||||
int piezo_init(void)
|
||||
{
|
||||
/* 중복 초기화 방지 */
|
||||
// 중복 초기화 방지
|
||||
if (piezo_initialized)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 모든 제어 핀 목록 */
|
||||
// 모든 제어 핀 목록
|
||||
const struct gpio_dt_spec *pins[] =
|
||||
{
|
||||
&piezo_pwr, &piezo_pe, &piezo_p_out, &piezo_n_out,
|
||||
@@ -138,14 +138,14 @@ int piezo_init(void)
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(pins); i++)
|
||||
{
|
||||
/* GPIO 포트 준비 확인 */
|
||||
// GPIO 포트 준비 확인
|
||||
if (!device_is_ready(pins[i]->port))
|
||||
{
|
||||
DBG_PRINTF("[PIEZO] GPIO device not ready (%d)\r\n", (int)i);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* 초기 출력 Low */
|
||||
// 초기 출력 Low
|
||||
int err = gpio_pin_configure_dt(pins[i], GPIO_OUTPUT_INACTIVE);
|
||||
if (err)
|
||||
{
|
||||
@@ -154,7 +154,7 @@ int piezo_init(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* 직접 제어용 핀/마스크 캐시 */
|
||||
// 직접 제어용 핀/마스크 캐시
|
||||
piezo_pe_abs = gpio_abs_pin(&piezo_pe);
|
||||
piezo_p_out_abs = gpio_abs_pin(&piezo_p_out);
|
||||
piezo_n_out_abs = gpio_abs_pin(&piezo_n_out);
|
||||
@@ -172,32 +172,33 @@ int piezo_init(void)
|
||||
|
||||
void piezo_power_on(void)
|
||||
{
|
||||
/* 필요 시 자동 초기화 */
|
||||
// 필요 시 자동 초기화
|
||||
if (!piezo_initialized && piezo_init() != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* 피에조 전원 인가 */
|
||||
// 피에조 전원 인가
|
||||
gpio_pin_set_dt(&piezo_pwr, 1);
|
||||
}
|
||||
|
||||
void piezo_power_off(void)
|
||||
{
|
||||
/* 필요 시 자동 초기화 */
|
||||
// 필요 시 자동 초기화
|
||||
if (!piezo_initialized && piezo_init() != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* 출력 정지 후 전원 차단 */
|
||||
// 출력 정지 후 전원 차단
|
||||
piezo_drive_idle();
|
||||
gpio_pin_set_dt(&piezo_pwr, 0);
|
||||
}
|
||||
|
||||
/* Piezo 채널 선택 */
|
||||
int piezo_select_channel(uint8_t channel)
|
||||
{
|
||||
/* 필요 시 자동 초기화 */
|
||||
// 필요 시 자동 초기화
|
||||
if (!piezo_initialized)
|
||||
{
|
||||
int err = piezo_init();
|
||||
@@ -207,35 +208,36 @@ int piezo_select_channel(uint8_t channel)
|
||||
}
|
||||
}
|
||||
|
||||
/* 채널 범위 검사 */
|
||||
// 채널 범위 검사
|
||||
if (channel >= PIEZO_NUM_CHANNELS)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* MUX 채널 선택 */
|
||||
// MUX 채널 선택
|
||||
gpio_pin_set_dt(&mux_en_a, mux_map[channel].en_a);
|
||||
gpio_pin_set_dt(&mux_en_b, mux_map[channel].en_b);
|
||||
gpio_pin_set_dt(&mux_sel0, mux_map[channel].sel0);
|
||||
gpio_pin_set_dt(&mux_sel1, mux_map[channel].sel1);
|
||||
|
||||
/* MUX 안정화 대기 */
|
||||
// MUX 안정화 대기
|
||||
k_busy_wait(PIEZO_MUX_SETTLE_US);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Piezo Burst Switching : 현재는 2.1MHz 고정, 1.8~2.2MHz 추가 예정 */
|
||||
void piezo_burst_sw(uint8_t cycles)
|
||||
{
|
||||
/* 초기화/횟수 검사 */
|
||||
// 초기화/횟수 검사
|
||||
if (!piezo_initialized || cycles == 0U)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* 타이밍 흔들림 방지 */
|
||||
// 타이밍 흔들림 방지
|
||||
unsigned int key = irq_lock();
|
||||
|
||||
/* P1 비제어 핀 상태 보존 */
|
||||
// P1 비제어 핀 상태 보존
|
||||
uint32_t saved_p1_out = NRF_P1->OUT;
|
||||
uint32_t p1_ctrl_mask = piezo_p_out_mask | piezo_n_out_mask | piezo_dmp_mask;
|
||||
uint32_t p1_all_low = saved_p1_out & ~p1_ctrl_mask;
|
||||
@@ -243,28 +245,28 @@ void piezo_burst_sw(uint8_t cycles)
|
||||
uint32_t p1_p_low_n_high = p1_all_low | piezo_n_out_mask;
|
||||
uint32_t p1_dmp_high = p1_all_low | piezo_dmp_mask;
|
||||
|
||||
/* 구동 Enable 전 초기 Low 정렬 */
|
||||
// 구동 Enable 전 초기 Low 정렬
|
||||
NRF_P0->OUTCLR = piezo_pe_mask;
|
||||
NRF_P1->OUT = p1_all_low;
|
||||
NRF_P0->OUTSET = piezo_pe_mask; /* PE High */
|
||||
NRF_P0->OUTSET = piezo_pe_mask; // PE High
|
||||
|
||||
for (uint8_t i = 0; i < cycles; i++)
|
||||
{
|
||||
/* P High / N Low */
|
||||
// P High / N Low
|
||||
NRF_P1->OUT = p1_p_high_n_low;
|
||||
nop_delay_14();
|
||||
|
||||
/* P Low / N High */
|
||||
// P Low / N High
|
||||
NRF_P1->OUT = p1_p_low_n_high;
|
||||
nop_delay_9();
|
||||
}
|
||||
|
||||
/* 잔류 전하 방전 */
|
||||
// 잔류 전하 방전
|
||||
NRF_P1->OUT = p1_dmp_high;
|
||||
nop_delay_32(); /* DMP High 폭 */
|
||||
nop_delay_32(); // DMP High 폭
|
||||
|
||||
/* 출력 Low 복귀 */
|
||||
// 출력 Low 복귀
|
||||
NRF_P1->OUT = p1_all_low;
|
||||
NRF_P0->OUTCLR = piezo_pe_mask; /* PE Low */
|
||||
NRF_P0->OUTCLR = piezo_pe_mask; // PE Low
|
||||
irq_unlock(key);
|
||||
}
|
||||
|
||||
+111
-167
@@ -1,27 +1,8 @@
|
||||
/*******************************************************************************
|
||||
* @file main.c
|
||||
* @brief VesiScan-Basic
|
||||
*
|
||||
* 부팅 흐름:
|
||||
* 1. 리셋/DFU 복구 상태 확인, 전원 유지 핀 초기 설정
|
||||
* 2. GPIO, 타이머, workqueue, LED, 배터리 ADC, IMU, piezo 설정 초기화
|
||||
* 3. BLE 스택 + NUS 초기화
|
||||
* 4. 전원 버튼 상태머신 시작
|
||||
*
|
||||
* 일반 버튼 부팅:
|
||||
* - 버튼을 BOOT_THRESHOLD 동안 유지하면 P0.08 전원 래치 ON
|
||||
* - 래치 후 LED advertising 패턴, 배터리 모니터, BLE advertising 시작
|
||||
* - 기준 시간 = POWER_ON_DELAY * BOOT_THRESHOLD
|
||||
*
|
||||
* 리셋/DFU 복구:
|
||||
* - 버튼 없이 전원 래치와 advertising을 자동 복구할 수 있음
|
||||
*
|
||||
* 전원 버튼 상태머신:
|
||||
* [OFF→ON] 2초 이상 버튼 유지 → 전원 래치 ON (초기화 시간 고려 체감상 2초, 실제 코드상 1초)
|
||||
* 임계 시간 전에 놓으면 전원 래치 해제
|
||||
* [ON→OFF] 버튼 유지 → advertising 중지 + POWER_OFF_DELAY 후 전원 차단
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/sys/printk.h>
|
||||
@@ -37,6 +18,7 @@
|
||||
#include "battery_adc.h"
|
||||
#include "parser.h"
|
||||
#include "imu_i2c.h"
|
||||
#include "piezo_measure.h"
|
||||
|
||||
LOG_MODULE_REGISTER(vesiscan, LOG_LEVEL_INF);
|
||||
|
||||
@@ -55,51 +37,45 @@ LOG_MODULE_REGISTER(vesiscan, LOG_LEVEL_INF);
|
||||
*/
|
||||
#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) /* 전원 버튼 */
|
||||
/* Devicetree GPIO Settings */
|
||||
#define POWER_HOLD_NODE DT_NODELABEL(pwr_hold) // 전원 래치
|
||||
#define POWER_BTN_NODE DT_NODELABEL(button_check) // 전원 버튼
|
||||
|
||||
/* 전원 제어 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초 후 전원 차단) */
|
||||
/* 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_BUF_SIZE]; /* 시리얼 번호 */
|
||||
char HW_NO[HW_NO_BUF_SIZE]; /* 하드웨어 번호 */
|
||||
char m_static_passkey[PASSKEY_BUF_SIZE]; /* BLE 정적 패스키 */
|
||||
uint8_t m_passkey_changed; /* 패스키 변경은 출고 후 1회만 허용 */
|
||||
/* 전역 변수 */
|
||||
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_BUF_SIZE]; // 시리얼 번호
|
||||
char HW_NO[HW_NO_BUF_SIZE]; // 하드웨어 번호
|
||||
char m_static_passkey[PASSKEY_BUF_SIZE]; // BLE 정적 패스키
|
||||
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; /* 측정 중 전원 버튼 상태머신 일시 정지 */
|
||||
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 dfu_confirm_pending_boot; /* DFU test 이미지 확인 전 자동 복귀 */
|
||||
static bool dfu_reset_resume_request; /* DFU 완료 리셋 후 자동 복귀 */
|
||||
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 bool dfu_led_active; /* DFU 업로드 중 초록 LED ON */
|
||||
static bool resume_without_power_button; // 리셋 후 버튼 없이 복귀
|
||||
static bool dfu_confirm_pending_boot; // DFU test 이미지 확인 전 자동 복귀
|
||||
static bool dfu_reset_resume_request; // DFU 완료 리셋 후 자동 복귀
|
||||
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 bool dfu_led_active; // DFU 업로드 중 초록 LED ON
|
||||
static const char *power_off_pending_reason = "none";
|
||||
|
||||
static enum mgmt_cb_return dfu_status_cb(uint32_t event,
|
||||
@@ -137,21 +113,21 @@ static struct mgmt_callback smp_cmd_status_callback = {
|
||||
*/
|
||||
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 공유 버퍼 보호 */
|
||||
static uint8_t ble_cmd_buf[BLE_CMD_MAX_LEN];/* RX 명령 복사 버퍼 */
|
||||
static uint16_t ble_cmd_len; /* RX 명령 길이 */
|
||||
static bool ble_cmd_pending; /* 처리 대기 명령 있음 */
|
||||
static struct k_work ble_cmd_work; // BLE 명령 처리 work
|
||||
static struct k_spinlock ble_cmd_lock; // RX 공유 버퍼 보호
|
||||
static uint8_t ble_cmd_buf[BLE_CMD_MAX_LEN];// RX 명령 복사 버퍼
|
||||
static uint16_t ble_cmd_len; // RX 명령 길이
|
||||
static bool ble_cmd_pending; // 처리 대기 명령 있음
|
||||
|
||||
static void ble_cmd_work_handler(struct k_work *work)
|
||||
{
|
||||
ARG_UNUSED(work);
|
||||
|
||||
/* work queue 로컬 복사본 */
|
||||
// work queue 로컬 복사본
|
||||
uint8_t local_buf[BLE_CMD_MAX_LEN];
|
||||
uint16_t local_len = 0U;
|
||||
|
||||
/* 공유 RX 명령 슬롯 보호: 현재 구조는 한 번에 한 명령만 보관 */
|
||||
// 공유 RX 명령 슬롯 보호: 현재 구조는 한 번에 한 명령만 보관
|
||||
k_spinlock_key_t key = k_spin_lock(&ble_cmd_lock);
|
||||
if (ble_cmd_pending)
|
||||
{
|
||||
@@ -166,11 +142,11 @@ static void ble_cmd_work_handler(struct k_work *work)
|
||||
return;
|
||||
}
|
||||
|
||||
/* 일반 스레드 문맥 명령 처리 */
|
||||
// 일반 스레드 문맥 명령 처리
|
||||
//DBG_CORE("[BLE RX] worker dispatch len=%u\r\n", local_len);
|
||||
ble_cmd_dispatch(local_buf, local_len);
|
||||
|
||||
/* 명령 처리 완료 표시 */
|
||||
// 명령 처리 완료 표시
|
||||
key = k_spin_lock(&ble_cmd_lock);
|
||||
ble_cmd_pending = false;
|
||||
ble_cmd_len = 0U;
|
||||
@@ -191,9 +167,7 @@ static void adv_stop_work_handler(struct k_work *work)
|
||||
ble_advertising_stop();
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* BLE RX 콜백
|
||||
*============================================================================*/
|
||||
/* BLE RX 콜백 */
|
||||
static void ble_rx_handler(const uint8_t *data, uint16_t len)
|
||||
{
|
||||
if (len > BLE_CMD_MAX_LEN)
|
||||
@@ -202,7 +176,7 @@ static void ble_rx_handler(const uint8_t *data, uint16_t len)
|
||||
return;
|
||||
}
|
||||
|
||||
/* 공유 RX 명령 슬롯 보호: 현재 구조는 한 번에 한 명령만 보관 */
|
||||
// 공유 RX 명령 슬롯 보호: 현재 구조는 한 번에 한 명령만 보관
|
||||
k_spinlock_key_t key = k_spin_lock(&ble_cmd_lock);
|
||||
if (ble_cmd_pending)
|
||||
{
|
||||
@@ -211,13 +185,13 @@ static void ble_rx_handler(const uint8_t *data, uint16_t len)
|
||||
return;
|
||||
}
|
||||
|
||||
/* 명령 버퍼 복사 */
|
||||
// 명령 버퍼 복사
|
||||
memcpy(ble_cmd_buf, data, len);
|
||||
ble_cmd_len = len;
|
||||
ble_cmd_pending = true;
|
||||
k_spin_unlock(&ble_cmd_lock, key);
|
||||
|
||||
/* 명령 처리 work 예약 */
|
||||
// 명령 처리 work 예약
|
||||
int err = k_work_submit_to_queue(&ble_cmd_work_q, &ble_cmd_work);
|
||||
if (err < 0)
|
||||
{
|
||||
@@ -230,15 +204,13 @@ static void ble_rx_handler(const uint8_t *data, uint16_t len)
|
||||
}
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* 전원 유지 (POWER_HOLD) 제어
|
||||
*============================================================================*/
|
||||
/* 전원 유지 (POWER_HOLD) 제어 */
|
||||
static void boot_context_detect(void)
|
||||
{
|
||||
/* 부팅 원인 확인 */
|
||||
// 부팅 원인 확인
|
||||
boot_reset_reason = nrf_power_resetreas_get(NRF_POWER);
|
||||
|
||||
/* DFU 완료 직후 리셋인지 확인 */
|
||||
// DFU 완료 직후 리셋인지 확인
|
||||
#if NRF_POWER_HAS_GPREGRET
|
||||
dfu_reset_resume_request = (nrf_power_gpregret_get(NRF_POWER, DFU_RESUME_GPREGRET_REG) == DFU_RESUME_MAGIC);
|
||||
if (dfu_reset_resume_request)
|
||||
@@ -249,7 +221,7 @@ static void boot_context_detect(void)
|
||||
dfu_reset_resume_request = false;
|
||||
#endif
|
||||
|
||||
/* 소프트 리셋 계열 자동 복귀 */
|
||||
// 소프트 리셋 계열 자동 복귀
|
||||
resume_without_power_button =
|
||||
(boot_reset_reason & (NRF_POWER_RESETREAS_SREQ_MASK |
|
||||
NRF_POWER_RESETREAS_DOG_MASK |
|
||||
@@ -258,7 +230,7 @@ static void boot_context_detect(void)
|
||||
|
||||
if (boot_reset_reason != 0U)
|
||||
{
|
||||
/* RESETREAS 플래그 정리 */
|
||||
// RESETREAS 플래그 정리
|
||||
nrf_power_resetreas_clear(NRF_POWER, boot_reset_reason);
|
||||
}
|
||||
}
|
||||
@@ -266,13 +238,13 @@ static void boot_context_detect(void)
|
||||
static void power_hold_init(void)
|
||||
{
|
||||
#if defined(CONFIG_BOOTLOADER_MCUBOOT)
|
||||
/* DFU 이미지 확인 전 전원 유지 */
|
||||
// DFU 이미지 확인 전 전원 유지
|
||||
dfu_confirm_pending_boot = !boot_is_img_confirmed();
|
||||
#else
|
||||
dfu_confirm_pending_boot = false;
|
||||
#endif
|
||||
|
||||
/* 리셋 복귀/DFU 확인 대기 시 즉시 래치 */
|
||||
// 리셋 복귀/DFU 확인 대기 시 즉시 래치
|
||||
gpio_pin_configure_dt(&power_hold,
|
||||
(resume_without_power_button ||
|
||||
dfu_confirm_pending_boot ||
|
||||
@@ -295,30 +267,26 @@ static void power_control_handler(on_off_cont_t device_power_st, const char *rea
|
||||
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 → 전원 래치 해제 → 전원 차단 */
|
||||
gpio_pin_set_dt(&power_hold, 0); // P0.08 LOW → 전원 래치 해제 → 전원 차단
|
||||
}
|
||||
else
|
||||
{
|
||||
gpio_pin_set_dt(&power_hold, 1); /* P0.08 HIGH → 전원 유지 */
|
||||
gpio_pin_set_dt(&power_hold, 1); // P0.08 HIGH → 전원 유지
|
||||
}
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* GPIO 초기화
|
||||
*============================================================================*/
|
||||
/* GPIO 초기화 */
|
||||
static void gpio_init(void)
|
||||
{
|
||||
gpio_pin_configure_dt(&power_btn, GPIO_INPUT); /* 전원 버튼(P1.08) 입력 설정 */
|
||||
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)
|
||||
{
|
||||
/* 기본 문자열 길이 제한 */
|
||||
// 기본 문자열 길이 제한
|
||||
size_t serial_len = strlen(SERIAL_NUMBER);
|
||||
size_t hw_len = strlen(HARDWARE_VERSION);
|
||||
size_t passkey_len = strlen(DEFAULT_PASSKEY);
|
||||
@@ -339,15 +307,15 @@ static void load_default_config(void)
|
||||
memset(SERIAL_NO, 0, sizeof(SERIAL_NO));
|
||||
memcpy(SERIAL_NO, SERIAL_NUMBER, serial_len);
|
||||
|
||||
/* 기본 HW Version 복사 */
|
||||
// 기본 HW Version 복사
|
||||
memset(HW_NO, 0, sizeof(HW_NO));
|
||||
memcpy(HW_NO, HARDWARE_VERSION, hw_len);
|
||||
|
||||
/* 기본 BLE 패스키 복사 */
|
||||
// 기본 BLE 패스키 복사
|
||||
memset(m_static_passkey, 0, sizeof(m_static_passkey));
|
||||
memcpy(m_static_passkey, DEFAULT_PASSKEY, passkey_len);
|
||||
|
||||
/* NVS 로드 전 기본값은 변경 가능 상태 */
|
||||
// NVS 로드 전 기본값은 변경 가능 상태
|
||||
m_passkey_changed = 0U;
|
||||
|
||||
/* 기본 상태값 */
|
||||
@@ -357,10 +325,7 @@ static void load_default_config(void)
|
||||
DBG_CORE("[INIT] Default (S/N=%s)\r\n", SERIAL_NO);
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* 전원 OFF 타임아웃 콜백
|
||||
* LED 표시 후 3초(POWER_OFF_DELAY) 경과 시 호출되어 물리적 전원 차단
|
||||
*============================================================================*/
|
||||
/* 전원 OFF 타임아웃 콜백 */
|
||||
static void t_power_off_timeout_handler(struct k_timer *timer)
|
||||
{
|
||||
ARG_UNUSED(timer);
|
||||
@@ -369,10 +334,7 @@ static void t_power_off_timeout_handler(struct k_timer *timer)
|
||||
power_control_handler(OFF, "off-timeout");
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* 슬립 모드 / 전원 OFF
|
||||
* LED로 사용자에게 알린 후 3초 뒤 전원 차단
|
||||
*============================================================================*/
|
||||
/* 슬립 모드 / 전원 OFF: LED 표시 후 3초 뒤 전원 차단 */
|
||||
static void power_off_schedule(const char *reason, led_state_t led_state)
|
||||
{
|
||||
power_off_pending_reason = reason ? reason : "unknown";
|
||||
@@ -412,25 +374,13 @@ void device_power_keep_on(void)
|
||||
|
||||
void power_button_suspend(bool suspend)
|
||||
{
|
||||
/* 측정 시간이 긴 커맨드 처리 중에만 버튼 폴링 판단 잠시 멈춤 */
|
||||
// 측정 시간이 긴 커맨드 처리 중에만 버튼 폴링 판단 잠시 멈춤
|
||||
power_btn_suspended = suspend;
|
||||
cnt_s = 0;
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* 전원 버튼 상태머신 (5ms 폴링)
|
||||
*
|
||||
* [부팅 시퀀스] (device_on == false)
|
||||
* 1. 버튼 누름 → 전원 공급 → MCU 부팅 → 5ms마다 폴링 시작
|
||||
* 2. cnt_s < 200 (2초 미만)에서 놓으면 → 전원 OFF (래치 안 됨)
|
||||
* 3. cnt_s == 200 (2초) 도달 → P0.08 래치 ON + 녹색 LED 깜빡임
|
||||
* 4. 버튼 놓으면 → 깜빡임 유지, 폴링 계속
|
||||
*
|
||||
* [전원 OFF 시퀀스] (device_on == true)
|
||||
* 1. 버튼 2초 이상 누름 → sleep_mode_enter() → 전원 차단
|
||||
* 2. 버튼 2초 미만 놓으면 → 카운터 리셋, 무시
|
||||
*============================================================================*/
|
||||
#define BOOT_THRESHOLD 200 /* 5ms x 400 = 2초 -> 초기화 시간 고려 체감상 2초에 맞춤 */
|
||||
/* 전원 버튼 상태머신 (5ms 폴링) */
|
||||
#define BOOT_THRESHOLD 200 // 5ms x 400 = 2초 -> 초기화 시간 고려 체감상 2초에 맞춤
|
||||
|
||||
static void main_s(struct k_timer *timer)
|
||||
{
|
||||
@@ -444,23 +394,23 @@ static void main_s(struct k_timer *timer)
|
||||
return;
|
||||
}
|
||||
|
||||
/* 부팅 시퀀스 (OFF → ON) */
|
||||
// 부팅 시퀀스 (OFF → ON)
|
||||
if (!device_on)
|
||||
{
|
||||
if (!button_pressed) /* 버튼 놓음 → 2초 미만이면 전원 OFF */
|
||||
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 /* 버튼 계속 누르고 있음 */
|
||||
else // 버튼 계속 누르고 있음
|
||||
{
|
||||
cnt_s++;
|
||||
|
||||
if (cnt_s == BOOT_THRESHOLD) /* 2초 도달: 래치 + 부팅 완료 */
|
||||
if (cnt_s == BOOT_THRESHOLD) // 2초 도달: 래치 + 부팅 완료
|
||||
{
|
||||
device_on = true;
|
||||
cnt_s = 0; /* 카운터 리셋: 안 하면 다음 틱에서 ON→OFF 분기가 cnt_s >= 200 조건을 즉시 만족하여 전원 OFF됨 */
|
||||
cnt_s = 0; // 카운터 리셋: 안 하면 다음 틱에서 ON→OFF 분기가 cnt_s >= 200 조건을 즉시 만족하여 전원 OFF됨
|
||||
power_control_handler(ON, "button-2s-latch");
|
||||
led_set_state(LED_STATE_ADVERTISING);
|
||||
k_work_submit(&adv_start_work);
|
||||
@@ -472,18 +422,18 @@ static void main_s(struct k_timer *timer)
|
||||
/* 전원 OFF 시퀀스 (ON → OFF) */
|
||||
else
|
||||
{
|
||||
if (!boot_btn_released) /* 부팅 시 눌렀던 버튼을 아직 안 놓음 → 대기 */
|
||||
if (!boot_btn_released) // 부팅 시 눌렀던 버튼을 아직 안 놓음 → 대기
|
||||
{
|
||||
if (!button_pressed)
|
||||
{
|
||||
boot_btn_released = true;
|
||||
}
|
||||
}
|
||||
else if (button_pressed) /* 버튼 새로 누르고 있음 */
|
||||
else if (button_pressed) // 버튼 새로 누르고 있음
|
||||
{
|
||||
cnt_s++;
|
||||
|
||||
if (cnt_s >= BOOT_THRESHOLD) /* 2초 이상 → 전원 OFF */
|
||||
if (cnt_s >= BOOT_THRESHOLD) // 2초 이상 → 전원 OFF
|
||||
{
|
||||
battery_timer_stop();
|
||||
k_work_submit(&adv_stop_work);
|
||||
@@ -494,7 +444,7 @@ static void main_s(struct k_timer *timer)
|
||||
return;
|
||||
}
|
||||
}
|
||||
else /* 버튼 놓음 → 카운터 리셋 */
|
||||
else // 버튼 놓음 → 카운터 리셋
|
||||
{
|
||||
if (cnt_s > 0)
|
||||
{
|
||||
@@ -507,30 +457,28 @@ static void main_s(struct k_timer *timer)
|
||||
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 */
|
||||
// BLE advertising work
|
||||
k_work_init(&adv_start_work, adv_start_work_handler);
|
||||
k_work_init(&adv_stop_work, adv_stop_work_handler);
|
||||
|
||||
/* BLE 명령 처리 work queue */
|
||||
// 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에 반영 */
|
||||
// DFU 업로드 상태를 LED에 반영
|
||||
mgmt_callback_register(&dfu_status_callback);
|
||||
|
||||
/* DFU 명령 실패 시 LED 상태 복구 */
|
||||
// DFU 명령 실패 시 LED 상태 복구
|
||||
mgmt_callback_register(&smp_cmd_status_callback);
|
||||
}
|
||||
|
||||
@@ -557,26 +505,26 @@ static enum mgmt_cb_return dfu_status_cb(uint32_t event,
|
||||
|
||||
switch (event)
|
||||
{
|
||||
case MGMT_EVT_OP_IMG_MGMT_DFU_STARTED:
|
||||
case MGMT_EVT_OP_IMG_MGMT_DFU_CHUNK:
|
||||
/* DFU 업로드 중에는 초록 LED ON */
|
||||
dfu_led_active = true;
|
||||
led_ble_solid();
|
||||
break;
|
||||
case MGMT_EVT_OP_IMG_MGMT_DFU_STARTED:
|
||||
case MGMT_EVT_OP_IMG_MGMT_DFU_CHUNK:
|
||||
// DFU 업로드 중에는 초록 LED ON
|
||||
dfu_led_active = true;
|
||||
led_ble_solid();
|
||||
break;
|
||||
|
||||
case MGMT_EVT_OP_IMG_MGMT_DFU_PENDING:
|
||||
/* 업로드 완료 후 리셋 전까지 초록 LED ON */
|
||||
dfu_led_active = true;
|
||||
led_ble_solid();
|
||||
case MGMT_EVT_OP_IMG_MGMT_DFU_PENDING:
|
||||
// 업로드 완료 후 리셋 전까지 초록 LED ON
|
||||
dfu_led_active = true;
|
||||
led_ble_solid();
|
||||
|
||||
/* DFU 리셋 후 버튼 없이 전원 ON */
|
||||
// DFU 리셋 후 버튼 없이 전원 ON
|
||||
#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:
|
||||
/* 실패나 중단 시 일반 광고 LED로 복귀 */
|
||||
// 실패나 중단 시 일반 광고 LED로 복귀
|
||||
if (dfu_led_active)
|
||||
{
|
||||
dfu_led_active = false;
|
||||
@@ -612,7 +560,7 @@ static enum mgmt_cb_return smp_cmd_status_cb(uint32_t event,
|
||||
|
||||
const struct mgmt_evt_op_cmd_arg *cmd = data;
|
||||
|
||||
/* 이미지 업로드/test 명령 실패 시 DFU LED 고정 해제 */
|
||||
// 이미지 업로드/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)
|
||||
@@ -622,7 +570,7 @@ static enum mgmt_cb_return smp_cmd_status_cb(uint32_t event,
|
||||
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
|
||||
@@ -633,21 +581,19 @@ static enum mgmt_cb_return smp_cmd_status_cb(uint32_t event,
|
||||
|
||||
static void resume_device_after_soft_reset(void)
|
||||
{
|
||||
/* 일반 버튼 부팅이면 skip */
|
||||
if (!resume_without_power_button &&
|
||||
!dfu_confirm_pending_boot &&
|
||||
!dfu_reset_resume_request)
|
||||
// 일반 버튼 부팅이면 skip
|
||||
if (!resume_without_power_button && !dfu_confirm_pending_boot && !dfu_reset_resume_request)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* 리셋 복귀나 DFU test 부팅은 버튼 없이도 ON 상태로 복구 */
|
||||
// 리셋 복귀나 DFU test 부팅은 버튼 없이도 ON 상태로 복구
|
||||
device_on = true;
|
||||
cnt_s = 0;
|
||||
boot_btn_released = (gpio_pin_get_dt(&power_btn) != 1);
|
||||
m_reset_status = 1;
|
||||
|
||||
/* 정상 ON 상태 서비스 재시작 */
|
||||
// 정상 ON 상태 서비스 재시작
|
||||
power_control_handler(ON, "resume-after-reset");
|
||||
led_set_state(LED_STATE_ADVERTISING);
|
||||
battery_timer_start();
|
||||
@@ -659,7 +605,7 @@ static void resume_device_after_soft_reset(void)
|
||||
static void confirm_running_image(void)
|
||||
{
|
||||
#if defined(CONFIG_BOOTLOADER_MCUBOOT)
|
||||
/* 기본 초기화와 BLE 준비가 끝난 뒤 MCUboot revert 방지 */
|
||||
// 기본 초기화와 BLE 준비가 끝난 뒤 MCUboot revert 방지
|
||||
if (!boot_is_img_confirmed())
|
||||
{
|
||||
int err = boot_write_img_confirmed();
|
||||
@@ -681,7 +627,7 @@ static void confirm_running_image(void)
|
||||
|
||||
static const char *swap_type_to_str(int swap_type)
|
||||
{
|
||||
/* MCUboot swap 상태 문자열 */
|
||||
// MCUboot swap 상태 문자열
|
||||
switch (swap_type)
|
||||
{
|
||||
case BOOT_SWAP_TYPE_NONE:
|
||||
@@ -702,7 +648,7 @@ static const char *swap_type_to_str(int swap_type)
|
||||
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();
|
||||
@@ -733,30 +679,28 @@ static void log_mcuboot_state(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* 메인 함수
|
||||
*============================================================================*/
|
||||
/* 메인 함수 */
|
||||
int main(void)
|
||||
{
|
||||
/* 리셋/복귀 컨텍스트 */
|
||||
// 리셋/복귀 컨텍스트
|
||||
boot_context_detect();
|
||||
|
||||
/* 하드웨어 기본 초기화 */
|
||||
// 하드웨어 기본 초기화
|
||||
power_hold_init();
|
||||
cnt_s = 0;
|
||||
|
||||
/* 버튼 부팅 기본 상태 */
|
||||
// 버튼 부팅 기본 상태
|
||||
device_on = false;
|
||||
boot_btn_released = false;
|
||||
confirm_running_image();
|
||||
|
||||
DBG_CORE("\r\n========================================\r\n");
|
||||
DBG_CORE(" TEST BUILD %s (Zephyr)\r\n", FIRMWARE_VERSION);
|
||||
DBG_CORE(" BUILD TAG: TEST-ADV-UNIT-002\r\n");
|
||||
DBG_CORE(" BUILD TAG: TEST-003 - tx power dynamic control\r\n");
|
||||
DBG_CORE("========================================\r\n");
|
||||
|
||||
DBG_CORE("[INIT] HW Init\r\n");
|
||||
/* 기본 하드웨어/센서 초기화 */
|
||||
// 기본 하드웨어/센서 초기화
|
||||
gpio_init();
|
||||
timers_init();
|
||||
log_mcuboot_state();
|
||||
@@ -768,14 +712,14 @@ int main(void)
|
||||
piezo_config_init();
|
||||
DBG_CORE("[INIT] HW - gpio/timer/config/led/batt/imu/temp/piezo-cfg OK\r\n");
|
||||
|
||||
/* BLE 스택 + NUS */
|
||||
// BLE 스택 + NUS
|
||||
DBG_CORE("[INIT] BLE Init\r\n");
|
||||
if (ble_service_init(ble_rx_handler) == 0)
|
||||
{
|
||||
DBG_CORE("[INIT] BLE - ble/nus OK\r\n");
|
||||
/* 소프트 리셋 후 서비스 복구 */
|
||||
// 소프트 리셋 후 서비스 복구
|
||||
resume_device_after_soft_reset();
|
||||
/* BLE 준비까지 확인한 뒤 DFU test 이미지를 확정 */
|
||||
// BLE 준비까지 확인한 뒤 DFU test 이미지를 확정
|
||||
confirm_running_image();
|
||||
}
|
||||
else
|
||||
@@ -787,10 +731,10 @@ int main(void)
|
||||
DBG_CORE(" READY [%s]\r\n", SERIAL_NO);
|
||||
DBG_CORE("========================================\r\n");
|
||||
|
||||
/* 전원 버튼 상태머신 시작 (부팅 시 버튼이 눌려있는 상태) */
|
||||
// 전원 버튼 상태머신 시작 (부팅 시 버튼이 눌려있는 상태)
|
||||
timers_start();
|
||||
|
||||
/* 메인 루프 - idle */
|
||||
// 메인 루프 - idle
|
||||
for (;;)
|
||||
{
|
||||
k_msleep(100);
|
||||
|
||||
@@ -23,13 +23,10 @@
|
||||
#define PIEZO_CFG_SAMPLES_MAX 117
|
||||
#define PIEZO_CFG_DELAY_MAX_US 50
|
||||
|
||||
/*
|
||||
* piezo_select_channel() 안에서 MUX settle 1.3 ms를 이미 기다린 뒤,
|
||||
* measurement layer에서 analog path/ADC 입력 안정화를 위해 0.5 ms를 한 번 더 기다린다.
|
||||
*/
|
||||
/* piezo_select_channel() 안에서 MUX settle 1.3 ms를 이미 기다린 뒤 analog path/ADC 입력 안정화를 위해 0.5 ms를 한 번 더 기다림 */
|
||||
#define PIEZO_POST_SELECT_SETTLE_US 500
|
||||
|
||||
/* 평균/dummy burst 사이의 회복 시간 */
|
||||
/* burst 사이의 회복 시간 */
|
||||
#define PIEZO_AVG_INTER_BURST_GAP_US 650
|
||||
|
||||
/* real capture 전에 같은 채널에서 버스트+ADC capture를 버리는 횟수(dummy) */
|
||||
@@ -83,8 +80,7 @@ bool piezo_config_validate(const piezo_config_t *cfg)
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((cfg->samples < PIEZO_CFG_SAMPLES_MIN) ||
|
||||
(cfg->samples > PIEZO_CFG_SAMPLES_MAX))
|
||||
if ((cfg->samples < PIEZO_CFG_SAMPLES_MIN) || (cfg->samples > PIEZO_CFG_SAMPLES_MAX))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -219,24 +215,20 @@ int piezo_measure_sweep(void)
|
||||
return ECHO_STATUS_CAPTURE;
|
||||
}
|
||||
|
||||
// Dummy burst + dummy capture - 결과는 버리고, piezo/mux/ADC 입력의 이전 상태가 real 평균에 섞이지 않도록 함
|
||||
for (uint8_t dummy = 0; dummy < PIEZO_DUMMY_CAPTURE_COUNT; dummy++)
|
||||
if (ch == 0U) // 채널 0 시작 전에만 dummy burst 수행 TESTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTT
|
||||
{
|
||||
k_busy_wait(PIEZO_AVG_INTER_BURST_GAP_US);
|
||||
piezo_burst_sw((uint8_t)cfg->cycles);
|
||||
k_busy_wait(capture_delay_us);
|
||||
|
||||
err = echo_adc_capture(echo_capture, cfg->samples);
|
||||
if (err)
|
||||
// Dummy burst - ADC caputre는 수행하지 않음
|
||||
for (uint8_t dummy = 0; dummy < PIEZO_DUMMY_CAPTURE_COUNT; dummy++)
|
||||
{
|
||||
DBG_PRINTF("[ECHO] dummy capture fail ch=%d dummy=%d err=%d\r\n", ch, dummy, err);
|
||||
return ECHO_STATUS_CAPTURE;
|
||||
k_busy_wait(PIEZO_AVG_INTER_BURST_GAP_US);
|
||||
piezo_burst_sw((uint8_t)cfg->cycles);
|
||||
k_busy_wait(capture_delay_us);
|
||||
}
|
||||
|
||||
// 마지막 dummy capture와 첫 real capture 사이에도 같은 회복 시간
|
||||
k_busy_wait(PIEZO_AVG_INTER_BURST_GAP_US + 3000);
|
||||
}
|
||||
|
||||
// 마지막 dummy capture와 첫 real capture 사이에도 같은 회복 시간
|
||||
k_busy_wait(PIEZO_AVG_INTER_BURST_GAP_US);
|
||||
|
||||
|
||||
// 여기부터 실제 평균에 들어갈 capture
|
||||
memset(echo_accum, 0, sizeof(echo_accum));
|
||||
|
||||
@@ -285,11 +277,7 @@ int piezo_measure_sweep(void)
|
||||
* 단일 채널 burst + echo capture
|
||||
* mec? 디버그/테스트 명령에서 사용하며, 결과는 echo_capture에 평균 저장
|
||||
*/
|
||||
int piezo_measure_single_capture(uint8_t cycles,
|
||||
uint16_t delay_us,
|
||||
uint16_t num_samples,
|
||||
uint16_t averaging,
|
||||
uint8_t channel)
|
||||
int piezo_measure_single_capture(uint8_t cycles, uint16_t delay_us, uint16_t num_samples, uint16_t averaging, uint8_t channel)
|
||||
{
|
||||
if (channel >= PIEZO_NUM_CHANNELS)
|
||||
{
|
||||
@@ -321,7 +309,7 @@ int piezo_measure_single_capture(uint8_t cycles,
|
||||
return ECHO_STATUS_CAPTURE;
|
||||
}
|
||||
|
||||
/* real capture 전에 같은 채널에서 dummy burst/capture를 수행하고 버림 */
|
||||
// real capture 전에 같은 채널에서 dummy burst/capture를 수행하고 버림
|
||||
for (uint8_t dummy = 0; dummy < PIEZO_DUMMY_CAPTURE_COUNT; dummy++)
|
||||
{
|
||||
k_busy_wait(PIEZO_AVG_INTER_BURST_GAP_US);
|
||||
@@ -335,7 +323,7 @@ int piezo_measure_single_capture(uint8_t cycles,
|
||||
}
|
||||
}
|
||||
|
||||
/* 마지막 dummy capture와 첫 real capture 사이에도 같은 회복 시간 */
|
||||
// 마지막 dummy capture와 첫 real capture 사이에도 같은 회복 시간
|
||||
k_busy_wait(PIEZO_AVG_INTER_BURST_GAP_US);
|
||||
|
||||
memset(echo_accum, 0, sizeof(echo_accum));
|
||||
@@ -377,9 +365,7 @@ int piezo_measure_single_capture(uint8_t cycles,
|
||||
* 단일 채널 ADC-only capture
|
||||
* piezo burst 없이 현재 echo ADC 입력만 평균해서 확인할 때 사용
|
||||
*/
|
||||
int piezo_measure_adc_only_capture(uint16_t num_samples,
|
||||
uint16_t averaging,
|
||||
uint8_t channel)
|
||||
int piezo_measure_adc_only_capture(uint16_t num_samples, uint16_t averaging, uint8_t channel)
|
||||
{
|
||||
if (channel >= PIEZO_NUM_CHANNELS)
|
||||
{
|
||||
@@ -419,7 +405,7 @@ int piezo_measure_adc_only_capture(uint16_t num_samples,
|
||||
k_busy_wait(PIEZO_AVG_INTER_BURST_GAP_US);
|
||||
}
|
||||
|
||||
/* burst가 없으므로 ADC capture 자체의 간섭만 줄이기 위해 짧게 IRQ를 잠금 */
|
||||
// burst가 없으므로 ADC capture 자체의 간섭만 줄이기 위해 짧게 IRQ를 잠금
|
||||
unsigned int key = irq_lock();
|
||||
err = echo_adc_capture(echo_capture, num_samples);
|
||||
irq_unlock(key);
|
||||
|
||||
+2
-2
@@ -43,7 +43,7 @@ void power_loop(struct k_timer *timer)
|
||||
{
|
||||
power_timer_stop();
|
||||
|
||||
/* Sensor init not needed - imu_read_direct() handles it per measurement */
|
||||
// Sensor init not needed - imu_read_direct() handles it per measurement
|
||||
p_order = 2;
|
||||
|
||||
if (p_order < 2)
|
||||
@@ -59,7 +59,7 @@ void power_loop(struct k_timer *timer)
|
||||
|
||||
int device_reactivated(void)
|
||||
{
|
||||
/* sw_i2c_init_once() will be added in Stage 3 */
|
||||
// sw_i2c_init_once() will be added in Stage 3
|
||||
k_msleep(10);
|
||||
lock_check = true;
|
||||
p_order = 0;
|
||||
|
||||
Reference in New Issue
Block a user