기타등등
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
#include <zephyr/bluetooth/conn.h>
|
||||
#include <zephyr/bluetooth/uuid.h>
|
||||
#include <zephyr/bluetooth/gatt.h>
|
||||
#include <zephyr/mgmt/mcumgr/transport/smp_bt.h>
|
||||
#include <bluetooth/services/nus.h>
|
||||
|
||||
#include "ble_service.h"
|
||||
@@ -135,7 +136,13 @@ static const struct bt_data ad[] =
|
||||
|
||||
static const struct bt_data sd[] =
|
||||
{
|
||||
BT_DATA_BYTES(BT_DATA_UUID128_ALL, BT_UUID_NUS_VAL),
|
||||
/*
|
||||
* Legacy NUS + DFU(SMP) UUIDs together exceed the 31-byte scan response
|
||||
* limit for legacy advertising. Keep the device name in the advertising
|
||||
* packet and expose only SMP here so DFU remains discoverable.
|
||||
|
||||
BT_DATA_BYTES(BT_DATA_UUID128_SOME, SMP_BT_SVC_UUID_VAL), // BLE DFU 동작을 위한 SMP UUDI 광고 */
|
||||
BT_DATA_BYTES(BT_DATA_UUID128_SOME, BT_UUID_NUS_VAL), // UUID에 NUS 안 넣으면 Web bluetooth 앱에서 안 보임
|
||||
};
|
||||
|
||||
/*==============================================================================
|
||||
|
||||
+26
-13
@@ -87,7 +87,8 @@ static inline void nop_delay_14(void)
|
||||
|
||||
static inline void nop_delay_32(void)
|
||||
{
|
||||
for (int i = 0; i < 32; i++) {
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
burst_nop();
|
||||
}
|
||||
}
|
||||
@@ -107,23 +108,28 @@ static void piezo_drive_idle(void)
|
||||
|
||||
int piezo_init(void)
|
||||
{
|
||||
if (piezo_initialized) {
|
||||
if (piezo_initialized)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct gpio_dt_spec *pins[] = {
|
||||
const struct gpio_dt_spec *pins[] =
|
||||
{
|
||||
&piezo_pwr, &piezo_pe, &piezo_p_out, &piezo_n_out,
|
||||
&piezo_dmp, &mux_en_a, &mux_en_b, &mux_sel0, &mux_sel1
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < ARRAY_SIZE(pins); i++) {
|
||||
if (!device_is_ready(pins[i]->port)) {
|
||||
for (size_t i = 0; i < ARRAY_SIZE(pins); i++)
|
||||
{
|
||||
if (!device_is_ready(pins[i]->port))
|
||||
{
|
||||
DBG_PRINTF("[PIEZO] GPIO device not ready (%d)\r\n", (int)i);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
int err = gpio_pin_configure_dt(pins[i], GPIO_OUTPUT_INACTIVE);
|
||||
if (err) {
|
||||
if (err)
|
||||
{
|
||||
DBG_PRINTF("[PIEZO] GPIO init fail idx=%d err=%d\r\n", (int)i, err);
|
||||
return err;
|
||||
}
|
||||
@@ -146,7 +152,8 @@ int piezo_init(void)
|
||||
|
||||
void piezo_power_on(void)
|
||||
{
|
||||
if (!piezo_initialized && piezo_init() != 0) {
|
||||
if (!piezo_initialized && piezo_init() != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -155,7 +162,8 @@ void piezo_power_on(void)
|
||||
|
||||
void piezo_power_off(void)
|
||||
{
|
||||
if (!piezo_initialized && piezo_init() != 0) {
|
||||
if (!piezo_initialized && piezo_init() != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -165,14 +173,17 @@ void piezo_power_off(void)
|
||||
|
||||
int piezo_select_channel(uint8_t channel)
|
||||
{
|
||||
if (!piezo_initialized) {
|
||||
if (!piezo_initialized)
|
||||
{
|
||||
int err = piezo_init();
|
||||
if (err) {
|
||||
if (err)
|
||||
{
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
if (channel >= PIEZO_NUM_CHANNELS) {
|
||||
if (channel >= PIEZO_NUM_CHANNELS)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -187,7 +198,8 @@ int piezo_select_channel(uint8_t channel)
|
||||
|
||||
void piezo_burst_sw(uint8_t cycles)
|
||||
{
|
||||
if (!piezo_initialized || cycles == 0U) {
|
||||
if (!piezo_initialized || cycles == 0U)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -203,7 +215,8 @@ void piezo_burst_sw(uint8_t cycles)
|
||||
NRF_P1->OUT = p1_all_low;
|
||||
NRF_P0->OUTSET = piezo_pe_mask;
|
||||
|
||||
for (uint8_t i = 0; i < cycles; i++) {
|
||||
for (uint8_t i = 0; i < cycles; i++)
|
||||
{
|
||||
NRF_P1->OUT = p1_p_high_n_low;
|
||||
nop_delay_14();
|
||||
|
||||
|
||||
+148
-30
@@ -15,6 +15,8 @@
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/sys/printk.h>
|
||||
#include <zephyr/dfu/mcuboot.h>
|
||||
#include <hal/nrf_power.h>
|
||||
|
||||
#include "main.h"
|
||||
#include "debug_print.h"
|
||||
@@ -79,6 +81,8 @@ static bool power_btn_suspended; /* 측정 중 전원 버튼 상태
|
||||
* k_work를 사용하면 시스템 워크큐 스레드에서 실행되므로 안전하게 BLE API 호출 가능.
|
||||
* main_s() 타이머 콜백에서 k_work_submit()으로 예약하여 사용한다.
|
||||
*/
|
||||
static bool resume_without_power_button;
|
||||
static uint32_t boot_reset_reason;
|
||||
static struct k_work adv_start_work;
|
||||
static struct k_work adv_stop_work;
|
||||
|
||||
@@ -111,13 +115,15 @@ static void ble_cmd_work_handler(struct k_work *work)
|
||||
uint16_t local_len = 0U;
|
||||
|
||||
k_spinlock_key_t key = k_spin_lock(&ble_cmd_lock);
|
||||
if (ble_cmd_pending) {
|
||||
if (ble_cmd_pending)
|
||||
{
|
||||
local_len = ble_cmd_len;
|
||||
memcpy(local_buf, ble_cmd_buf, local_len);
|
||||
}
|
||||
k_spin_unlock(&ble_cmd_lock, key);
|
||||
|
||||
if (local_len == 0U) {
|
||||
if (local_len == 0U)
|
||||
{
|
||||
DBG_ERR("[BLE RX] worker: empty\r\n");
|
||||
return;
|
||||
}
|
||||
@@ -204,17 +210,43 @@ static void ble_rx_handler(const uint8_t *data, uint16_t len)
|
||||
/* 전원 유지 핀(P0.08) 초기화 - 아직 래치하지 않음
|
||||
* 버튼을 물리적으로 누르고 있는 동안만 전원 유지됨
|
||||
* 2초 후 main_s()에서 래치(HIGH) 설정 */
|
||||
static void boot_context_detect(void)
|
||||
{
|
||||
boot_reset_reason = nrf_power_resetreas_get(NRF_POWER);
|
||||
resume_without_power_button =
|
||||
(boot_reset_reason & (NRF_POWER_RESETREAS_SREQ_MASK |
|
||||
NRF_POWER_RESETREAS_DOG_MASK |
|
||||
NRF_POWER_RESETREAS_LOCKUP_MASK |
|
||||
NRF_POWER_RESETREAS_RESETPIN_MASK)) != 0U;
|
||||
|
||||
if (boot_reset_reason != 0U)
|
||||
{
|
||||
nrf_power_resetreas_clear(NRF_POWER, boot_reset_reason);
|
||||
}
|
||||
}
|
||||
|
||||
static void power_hold_init(void)
|
||||
{
|
||||
gpio_pin_configure_dt(&power_hold, GPIO_OUTPUT_INACTIVE);
|
||||
#if defined(CONFIG_BOOTLOADER_MCUBOOT)
|
||||
bool dfu_confirm_pending = !boot_is_img_confirmed();
|
||||
#else
|
||||
bool dfu_confirm_pending = false;
|
||||
#endif
|
||||
|
||||
gpio_pin_configure_dt(&power_hold,
|
||||
(resume_without_power_button || dfu_confirm_pending) ? GPIO_OUTPUT_ACTIVE
|
||||
: GPIO_OUTPUT_INACTIVE);
|
||||
}
|
||||
|
||||
/* 전원 ON/OFF 제어 - P0.08 핀으로 물리적 전원 래치/해제 */
|
||||
static void power_control_handler(on_off_cont_t device_power_st)
|
||||
{
|
||||
if (device_power_st == OFF) {
|
||||
if (device_power_st == OFF)
|
||||
{
|
||||
gpio_pin_set_dt(&power_hold, 0); /* P0.08 LOW → 전원 래치 해제 → 전원 차단 */
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
gpio_pin_set_dt(&power_hold, 1); /* P0.08 HIGH → 전원 유지 */
|
||||
DBG_PRINTF("[PWR] ON\r\n");
|
||||
}
|
||||
@@ -239,13 +271,16 @@ static void load_default_config(void)
|
||||
size_t hw_len = strlen(HARDWARE_VERSION);
|
||||
size_t passkey_len = strlen(DEFAULT_PASSKEY);
|
||||
|
||||
if (serial_len > SERIAL_NO_LENGTH) {
|
||||
if (serial_len > SERIAL_NO_LENGTH)
|
||||
{
|
||||
serial_len = SERIAL_NO_LENGTH;
|
||||
}
|
||||
if (hw_len > HW_NO_LENGTH) {
|
||||
if (hw_len > HW_NO_LENGTH)
|
||||
{
|
||||
hw_len = HW_NO_LENGTH;
|
||||
}
|
||||
if (passkey_len > PASSKEY_LENGTH) {
|
||||
if (passkey_len > PASSKEY_LENGTH)
|
||||
{
|
||||
passkey_len = PASSKEY_LENGTH;
|
||||
}
|
||||
|
||||
@@ -403,11 +438,7 @@ static void timers_init(void)
|
||||
k_work_init(&adv_start_work, adv_start_work_handler);
|
||||
k_work_init(&adv_stop_work, adv_stop_work_handler);
|
||||
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);
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -417,59 +448,145 @@ static void timers_start(void)
|
||||
k_timer_start(&m_power_on_delay_timer, K_MSEC(POWER_ON_DELAY), K_NO_WAIT);
|
||||
}
|
||||
|
||||
static void resume_device_after_soft_reset(void)
|
||||
{
|
||||
if (!resume_without_power_button)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
device_on = true;
|
||||
cnt_s = 0;
|
||||
boot_btn_released = (gpio_pin_get_dt(&power_btn) != 1);
|
||||
m_reset_status = 1;
|
||||
|
||||
power_control_handler(ON);
|
||||
led_set_state(LED_STATE_ADVERTISING);
|
||||
battery_timer_start();
|
||||
k_work_submit(&adv_start_work);
|
||||
|
||||
DBG_CORE("[BOOT] Resume after resetreas=0x%08x\r\n", boot_reset_reason);
|
||||
}
|
||||
|
||||
static void confirm_running_image(void)
|
||||
{
|
||||
#if defined(CONFIG_BOOTLOADER_MCUBOOT)
|
||||
if (!boot_is_img_confirmed())
|
||||
{
|
||||
int err = boot_write_img_confirmed();
|
||||
if (err == 0)
|
||||
{
|
||||
DBG_CORE("[DFU] Running image confirmed\r\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG_ERR("[DFU] Image confirm failed (err %d)\r\n", err);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG_CORE("[DFU] Running image already confirmed\r\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static const char *swap_type_to_str(int swap_type)
|
||||
{
|
||||
switch (swap_type)
|
||||
{
|
||||
case BOOT_SWAP_TYPE_NONE:
|
||||
return "none";
|
||||
case BOOT_SWAP_TYPE_TEST:
|
||||
return "test";
|
||||
case BOOT_SWAP_TYPE_PERM:
|
||||
return "perm";
|
||||
case BOOT_SWAP_TYPE_REVERT:
|
||||
return "revert";
|
||||
case BOOT_SWAP_TYPE_FAIL:
|
||||
return "fail";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
static void log_mcuboot_state(void)
|
||||
{
|
||||
#if defined(CONFIG_BOOTLOADER_MCUBOOT)
|
||||
struct mcuboot_img_header header;
|
||||
uint8_t active_slot = boot_fetch_active_slot();
|
||||
int swap_type = mcuboot_swap_type();
|
||||
int err = boot_read_bank_header(active_slot, &header, sizeof(header));
|
||||
|
||||
DBG_CORE("[DFU] active_slot=%u swap_type=%s(%d) confirmed=%d\r\n",
|
||||
active_slot,
|
||||
swap_type_to_str(swap_type),
|
||||
swap_type,
|
||||
boot_is_img_confirmed());
|
||||
|
||||
if (err == 0)
|
||||
{
|
||||
DBG_CORE("[DFU] image version %u.%u.%u+%u size=0x%x\r\n",
|
||||
header.h.v1.sem_ver.major,
|
||||
header.h.v1.sem_ver.minor,
|
||||
header.h.v1.sem_ver.revision,
|
||||
header.h.v1.sem_ver.build_num,
|
||||
header.h.v1.image_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG_ERR("[DFU] image header read failed (slot %u, err %d)\r\n",
|
||||
active_slot,
|
||||
err);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* 메인 함수
|
||||
*============================================================================*/
|
||||
int main(void)
|
||||
{
|
||||
boot_context_detect();
|
||||
|
||||
/*── Phase 1: 하드웨어 기본 초기화 ──*/
|
||||
power_hold_init();
|
||||
cnt_s = 0;
|
||||
|
||||
/*
|
||||
* 현재는 전원 버튼/mbb 문제를 같이 디버깅 중이라
|
||||
* "버튼을 2초 이상 눌러야 래치 ON" 규칙이 오히려 부팅 자체를 막고 있다.
|
||||
*
|
||||
* 그래서 디버깅 동안만, MCU가 main()까지 올라오면 바로 P0.08 래치를 ON으로 잡는다.
|
||||
*
|
||||
* 의미:
|
||||
* - 사용자는 전원 버튼을 "부팅이 시작될 만큼만" 누르면 된다.
|
||||
* - 예전처럼 2초를 정확히 맞춰 길게 누를 필요가 없다.
|
||||
* - 일단 켜진 뒤의 BLE/측정/로그 문제를 보기 쉬워진다.
|
||||
*
|
||||
* 나중에 전원 시퀀스 디버깅이 끝나면 이 블록은 다시 제거하면 된다.
|
||||
*/
|
||||
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-042\r\n");
|
||||
DBG_CORE(" BUILD TAG: TEST-ADV-UNIT-000\r\n");
|
||||
DBG_CORE("========================================\r\n");
|
||||
|
||||
DBG_CORE("[1] HW Init\r\n");
|
||||
gpio_init();
|
||||
timers_init();
|
||||
log_mcuboot_state();
|
||||
load_default_config();
|
||||
led_init();
|
||||
battery_adc_init();
|
||||
battery_timer_init();
|
||||
imu_init();
|
||||
temp_init();
|
||||
DBG_CORE(" gpio/timer/config/led/batt/imu/temp OK\r\n");
|
||||
piezo_config_init();
|
||||
DBG_CORE(" gpio/timer/config/led/batt/imu/temp/piezo-cfg OK\r\n");
|
||||
|
||||
/*── Phase 2: BLE 스택 + NUS ──*/
|
||||
DBG_CORE("[2] BLE Init\r\n");
|
||||
if (ble_service_init(ble_rx_handler) == 0)
|
||||
{
|
||||
DBG_CORE(" ble/nus OK\r\n");
|
||||
resume_device_after_soft_reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG_ERR(" ble FAIL\r\n");
|
||||
}
|
||||
|
||||
/*── Phase 3: FDS/NVS (TODO) ──*/
|
||||
/*── Phase 3: FDS/NVS ──*/
|
||||
/*── Phase 4: 애플리케이션 (TODO) ──*/
|
||||
|
||||
DBG_CORE("\r\n========================================\r\n");
|
||||
@@ -480,7 +597,8 @@ int main(void)
|
||||
timers_start();
|
||||
|
||||
/* 메인 루프 - idle */
|
||||
for (;;) {
|
||||
for (;;)
|
||||
{
|
||||
k_msleep(100);
|
||||
}
|
||||
|
||||
|
||||
+242
-48
@@ -8,6 +8,11 @@
|
||||
* 현재 구현된 커맨드: msn? (배터리 측정), mls? (LED 상태 설정)
|
||||
******************************************************************************/
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
#if IS_ENABLED(CONFIG_SETTINGS)
|
||||
#include <zephyr/settings/settings.h>
|
||||
#endif
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
@@ -40,9 +45,15 @@
|
||||
#define PIEZO_CFG_DELAY_DEFAULT 10
|
||||
#define PIEZO_CFG_SAMPLES_DEFAULT 100
|
||||
#define PIEZO_CFG_AVG_DEFAULT 10
|
||||
#define PIEZO_CFG_AVG_MAX 100
|
||||
#define PIEZO_CFG_DELAY_MAX_US 1000
|
||||
#define PIEZO_POST_SELECT_SETTLE_US 500
|
||||
#define PIEZO_AVG_INTER_BURST_GAP_US 500
|
||||
|
||||
#define PIEZO_SETTINGS_NAME "vesiscan"
|
||||
#define PIEZO_SETTINGS_CFG_KEY "piezo"
|
||||
#define PIEZO_SETTINGS_FULL_KEY "vesiscan/piezo"
|
||||
|
||||
static uint16_t piezo_channels[PIEZO_NUM_CHANNELS][ECHO_ADC_MAX_SAMPLES];
|
||||
static uint16_t echo_capture[ECHO_ADC_MAX_SAMPLES];
|
||||
static uint32_t echo_accum[ECHO_ADC_MAX_SAMPLES];
|
||||
@@ -52,6 +63,140 @@ static uint8_t tx_echo_buf[4 + 2 + (ECHO_ADC_MAX_SAMPLES * 2) + 2];
|
||||
static uint8_t tx_bundle_buf[22];
|
||||
static uint8_t tx_cfg_buf[16];
|
||||
static uint8_t tx_ascii_buf[4 + HW_NO_LENGTH + 2];
|
||||
static piezo_config_t g_piezo_config = {
|
||||
.freq = PIEZO_CFG_FREQ_DEFAULT,
|
||||
.cycles = PIEZO_CFG_CYCLES_DEFAULT,
|
||||
.avg = PIEZO_CFG_AVG_DEFAULT,
|
||||
.delay_us = PIEZO_CFG_DELAY_DEFAULT,
|
||||
.samples = PIEZO_CFG_SAMPLES_DEFAULT,
|
||||
};
|
||||
|
||||
static bool piezo_config_validate(const piezo_config_t *cfg)
|
||||
{
|
||||
if (cfg == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cfg->freq != PIEZO_CFG_FREQ_DEFAULT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((cfg->cycles < 3U) || (cfg->cycles > 7U)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((cfg->avg == 0U) || (cfg->avg > PIEZO_CFG_AVG_MAX)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cfg->delay_us > PIEZO_CFG_DELAY_MAX_US) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((cfg->samples == 0U) || (cfg->samples > ECHO_ADC_MAX_SAMPLES)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void piezo_config_log(const char *prefix, const piezo_config_t *cfg)
|
||||
{
|
||||
DBG_PRINTF("%s freq=%u cycles=%u avg=%u delay=%u samples=%u\r\n",
|
||||
prefix,
|
||||
cfg->freq,
|
||||
cfg->cycles,
|
||||
cfg->avg,
|
||||
cfg->delay_us,
|
||||
cfg->samples);
|
||||
}
|
||||
|
||||
const piezo_config_t *piezo_config_get(void)
|
||||
{
|
||||
return &g_piezo_config;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_SETTINGS)
|
||||
static int piezo_settings_set(const char *name, size_t len,
|
||||
settings_read_cb read_cb, void *cb_arg)
|
||||
{
|
||||
const char *next;
|
||||
int rc;
|
||||
|
||||
if (settings_name_steq(name, PIEZO_SETTINGS_CFG_KEY, &next) && (next == NULL))
|
||||
{
|
||||
piezo_config_t loaded;
|
||||
|
||||
if (len != sizeof(loaded)) {
|
||||
DBG_ERR("[CFG] piezo settings size mismatch (%u)\r\n", (uint32_t)len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = read_cb(cb_arg, &loaded, sizeof(loaded));
|
||||
if (rc < 0) {
|
||||
DBG_ERR("[CFG] piezo settings read failed err=%d\r\n", rc);
|
||||
return rc;
|
||||
}
|
||||
if (rc != sizeof(loaded)) {
|
||||
DBG_ERR("[CFG] piezo settings short read (%d)\r\n", rc);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!piezo_config_validate(&loaded)) {
|
||||
DBG_ERR("[CFG] piezo settings invalid, keep defaults\r\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
g_piezo_config = loaded;
|
||||
piezo_config_log("[CFG] piezo restored", &g_piezo_config);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
SETTINGS_STATIC_HANDLER_DEFINE(piezo_settings,
|
||||
PIEZO_SETTINGS_NAME,
|
||||
NULL,
|
||||
piezo_settings_set,
|
||||
NULL,
|
||||
NULL);
|
||||
#endif
|
||||
|
||||
int piezo_config_init(void)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_SETTINGS)
|
||||
int err = settings_subsys_init();
|
||||
if (err && (err != -EALREADY)) {
|
||||
DBG_ERR("[CFG] settings init failed err=%d\r\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = settings_load_subtree(PIEZO_SETTINGS_NAME);
|
||||
if (err) {
|
||||
DBG_ERR("[CFG] piezo settings load failed err=%d\r\n", err);
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
piezo_config_log("[CFG] piezo active", &g_piezo_config);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int piezo_config_save(void)
|
||||
{
|
||||
#if IS_ENABLED(CONFIG_SETTINGS)
|
||||
int err = settings_save_one(PIEZO_SETTINGS_FULL_KEY,
|
||||
&g_piezo_config,
|
||||
sizeof(g_piezo_config));
|
||||
if (err) {
|
||||
DBG_ERR("[CFG] piezo settings save failed err=%d\r\n", err);
|
||||
}
|
||||
return err;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*==============================================================================
|
||||
* CRC16 (CRC-CCITT, Nordic SDK 호환)
|
||||
@@ -312,6 +457,13 @@ static int start_piezo_session(void)
|
||||
|
||||
static int perform_piezo_sweep(void)
|
||||
{
|
||||
const piezo_config_t *cfg = piezo_config_get();
|
||||
uint16_t capture_delay_us = cfg->delay_us;
|
||||
|
||||
if (capture_delay_us < PIEZO_BURST_TO_ADC_DELAY_US) {
|
||||
capture_delay_us = PIEZO_BURST_TO_ADC_DELAY_US;
|
||||
}
|
||||
|
||||
for (uint8_t ch = 0; ch < PIEZO_NUM_CHANNELS; ch++)
|
||||
{
|
||||
DBG_PRINTF("[MBB] sweep ch=%d start\r\n", ch);
|
||||
@@ -334,32 +486,32 @@ static int perform_piezo_sweep(void)
|
||||
|
||||
memset(echo_accum, 0, sizeof(echo_accum));
|
||||
|
||||
for (uint8_t avg = 0; avg < PIEZO_AVERAGE_COUNT; avg++)
|
||||
for (uint16_t avg = 0; avg < cfg->avg; avg++)
|
||||
{
|
||||
if (avg > 0U)
|
||||
{
|
||||
k_busy_wait(PIEZO_AVG_INTER_BURST_GAP_US);
|
||||
}
|
||||
|
||||
piezo_burst_sw(PIEZO_SW_BURST_CYCLES);
|
||||
k_busy_wait(PIEZO_BURST_TO_ADC_DELAY_US);
|
||||
piezo_burst_sw((uint8_t)cfg->cycles);
|
||||
k_busy_wait(capture_delay_us);
|
||||
|
||||
err = echo_adc_capture(echo_capture, ECHO_ADC_MAX_SAMPLES);
|
||||
err = echo_adc_capture(echo_capture, cfg->samples);
|
||||
if (err)
|
||||
{
|
||||
DBG_PRINTF("[ECHO] capture fail ch=%d avg=%d err=%d\r\n", ch, avg, err);
|
||||
return ECHO_STATUS_CAPTURE;
|
||||
}
|
||||
|
||||
for (uint16_t i = 0; i < ECHO_ADC_MAX_SAMPLES; i++)
|
||||
for (uint16_t i = 0; i < cfg->samples; i++)
|
||||
{
|
||||
echo_accum[i] += echo_capture[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (uint16_t i = 0; i < ECHO_ADC_MAX_SAMPLES; i++)
|
||||
for (uint16_t i = 0; i < cfg->samples; i++)
|
||||
{
|
||||
piezo_channels[ch][i] = (uint16_t)(echo_accum[i] / PIEZO_AVERAGE_COUNT);
|
||||
piezo_channels[ch][i] = (uint16_t)(echo_accum[i] / cfg->avg);
|
||||
}
|
||||
|
||||
DBG_PRINTF("[MBB] sweep ch=%d done\r\n", ch);
|
||||
@@ -521,12 +673,15 @@ static int cmd_mst(const uint8_t *data, uint8_t data_len)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 테스트용
|
||||
* mpa?: piezo TX/RX 전원 레일만 켬
|
||||
*/
|
||||
static int cmd_mpa(const uint8_t *data, uint8_t data_len)
|
||||
{
|
||||
ARG_UNUSED(data);
|
||||
ARG_UNUSED(data_len);
|
||||
|
||||
/* mpa?: piezo TX/RX 전원 레일만 켠다. 측정은 하지 않는다. */
|
||||
DBG_CORE("[MPA] enter\r\n");
|
||||
if (piezo_init() != 0) {
|
||||
DBG_ERR("[MPA] piezo_init failed\r\n");
|
||||
@@ -563,10 +718,16 @@ static int cmd_mpb(const uint8_t *data, uint8_t data_len)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 테스트용
|
||||
* mpc?: burst만 한 번 발생시키는 테스트 명령
|
||||
* echo를 읽지 않고 초음파가 나가는지만 볼 때 사용
|
||||
*/
|
||||
static int cmd_mpc(const uint8_t *data, uint8_t data_len)
|
||||
{
|
||||
uint16_t cycles = 5;
|
||||
uint16_t freq_option = 1;
|
||||
const piezo_config_t *cfg = piezo_config_get();
|
||||
uint16_t cycles = cfg->cycles;
|
||||
uint16_t freq_option = cfg->freq;
|
||||
uint16_t piezo_ch = 0;
|
||||
|
||||
get_data_u16_be(data, data_len, 0, &cycles);
|
||||
@@ -575,10 +736,6 @@ static int cmd_mpc(const uint8_t *data, uint8_t data_len)
|
||||
|
||||
ARG_UNUSED(freq_option);
|
||||
|
||||
/*
|
||||
* mpc?: burst만 한 번 발생시키는 테스트 명령.
|
||||
* echo를 읽지 않으므로 "초음파가 나가는지"만 빠르게 볼 때 쓴다.
|
||||
*/
|
||||
if ((cycles < 3U) || (cycles > 7U))
|
||||
{
|
||||
send_response_u16("rpc:", 2);
|
||||
@@ -603,8 +760,8 @@ static int cmd_mpc(const uint8_t *data, uint8_t data_len)
|
||||
}
|
||||
|
||||
/*
|
||||
* 현재 Zephyr 포팅본은 2.1MHz SW burst 하나만 구현되어 있다.
|
||||
* 레거시의 freq_option 값은 받아두되, 아직은 같은 burst 함수로 처리한다.
|
||||
* 현재 Zephyr 포팅본은 2.1MHz SW burst 하나만 구현되어 있음
|
||||
* 레거시의 freq_option 값은 받아두되, 아직은 같은 burst 함수로 처리
|
||||
*/
|
||||
piezo_burst_sw((uint8_t)cycles);
|
||||
piezo_power_off();
|
||||
@@ -616,11 +773,12 @@ static int cmd_mpc(const uint8_t *data, uint8_t data_len)
|
||||
|
||||
static int cmd_mec(const uint8_t *data, uint8_t data_len)
|
||||
{
|
||||
uint16_t freq_option = 1;
|
||||
uint16_t delay_us = PIEZO_BURST_TO_ADC_DELAY_US;
|
||||
uint16_t num_samples = ECHO_ADC_MAX_SAMPLES;
|
||||
uint16_t cycles = PIEZO_SW_BURST_CYCLES;
|
||||
uint16_t averaging = 1;
|
||||
const piezo_config_t *cfg = piezo_config_get();
|
||||
uint16_t freq_option = cfg->freq;
|
||||
uint16_t delay_us = cfg->delay_us;
|
||||
uint16_t num_samples = cfg->samples;
|
||||
uint16_t cycles = cfg->cycles;
|
||||
uint16_t averaging = cfg->avg;
|
||||
uint16_t piezo_ch = 0;
|
||||
|
||||
get_data_u16_be(data, data_len, 0, &freq_option);
|
||||
@@ -633,8 +791,8 @@ static int cmd_mec(const uint8_t *data, uint8_t data_len)
|
||||
ARG_UNUSED(freq_option);
|
||||
|
||||
/*
|
||||
* mec?: 단일 채널 burst + echo capture.
|
||||
* maa?/mbb?보다 가벼워서 "한 채널만 먼저 살아 있는지" 확인하기 좋다.
|
||||
* 테스트용
|
||||
* mec?: 단일 채널 burst + echo capture
|
||||
*/
|
||||
if (num_samples > ECHO_ADC_MAX_SAMPLES) {
|
||||
num_samples = ECHO_ADC_MAX_SAMPLES;
|
||||
@@ -671,19 +829,19 @@ static int cmd_mec(const uint8_t *data, uint8_t data_len)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 테스트용(이전 정렬모드)
|
||||
* maa?:
|
||||
* - piezo 6채널 burst 순서대로 쏘고
|
||||
* - echo 샘플을 채널별로 모은 뒤
|
||||
* - reb: 패킷 6개 전송
|
||||
* - raa: 상태값은 전체 작업 성공/실패 요약
|
||||
*/
|
||||
static int cmd_maa(const uint8_t *data, uint8_t data_len)
|
||||
{
|
||||
ARG_UNUSED(data);
|
||||
ARG_UNUSED(data_len);
|
||||
|
||||
/*
|
||||
* maa?:
|
||||
* - piezo 6채널을 순서대로 쏘고
|
||||
* - echo 샘플을 채널별로 모은 뒤
|
||||
* - reb: 패킷 6개를 보낸다.
|
||||
*
|
||||
* 마지막 raa: 상태값은 "전체 작업 성공/실패 요약"이다.
|
||||
*/
|
||||
processing = true;
|
||||
power_button_suspend(true);
|
||||
|
||||
@@ -697,7 +855,7 @@ static int cmd_maa(const uint8_t *data, uint8_t data_len)
|
||||
{
|
||||
for (uint8_t ch = 0; ch < PIEZO_NUM_CHANNELS; ch++)
|
||||
{
|
||||
send_response_echo(piezo_channels[ch], ECHO_ADC_MAX_SAMPLES);
|
||||
send_response_echo(piezo_channels[ch], piezo_config_get()->samples);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -710,6 +868,9 @@ static int cmd_maa(const uint8_t *data, uint8_t data_len)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 전체 측정 시 사용
|
||||
*/
|
||||
static int cmd_mbb(const uint8_t *data, uint8_t data_len)
|
||||
{
|
||||
ARG_UNUSED(data);
|
||||
@@ -747,7 +908,7 @@ static int cmd_mbb(const uint8_t *data, uint8_t data_len)
|
||||
|
||||
if (status == ECHO_STATUS_OK)
|
||||
{
|
||||
/* info 성격 데이터는 echo sweep이 정상 끝났을 때만 읽는다. */
|
||||
/* info 성격 데이터는 echo sweep이 정상 끝났을 때만 읽음 */
|
||||
DBG_PRINTF("[MBB] battery read\r\n");
|
||||
batt_mv = battery_read_mv();
|
||||
if (batt_mv < 0)
|
||||
@@ -777,7 +938,7 @@ static int cmd_mbb(const uint8_t *data, uint8_t data_len)
|
||||
|
||||
if (status == ECHO_STATUS_OK)
|
||||
{
|
||||
/* rbb: 한 번에 보내는 "요약 정보 묶음" 패킷 */
|
||||
/* rbb: 센서(배터리+IMU+온도) 정보 패킷 */
|
||||
DBG_PRINTF("[MBB] response tx start\r\n");
|
||||
send_response_bundle((uint16_t)batt_mv, accel, gyro, temp_cdeg);
|
||||
|
||||
@@ -785,7 +946,7 @@ static int cmd_mbb(const uint8_t *data, uint8_t data_len)
|
||||
{
|
||||
/* reb: 채널별 raw echo 파형 */
|
||||
DBG_PRINTF("[MBB] tx reb ch=%d\r\n", ch);
|
||||
send_response_echo(piezo_channels[ch], ECHO_ADC_MAX_SAMPLES);
|
||||
send_response_echo(piezo_channels[ch], piezo_config_get()->samples);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -799,22 +960,52 @@ static int cmd_mbb(const uint8_t *data, uint8_t data_len)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* mcf?: piezo 측정 파라미터 읽기
|
||||
* 응답 rcf: + 설정값 echo back
|
||||
*/
|
||||
static int cmd_mcf(const uint8_t *data, uint8_t data_len)
|
||||
{
|
||||
ARG_UNUSED(data);
|
||||
ARG_UNUSED(data_len);
|
||||
|
||||
send_response_piezo_config(PIEZO_CFG_FREQ_DEFAULT,
|
||||
PIEZO_CFG_CYCLES_DEFAULT,
|
||||
PIEZO_CFG_AVG_DEFAULT,
|
||||
PIEZO_CFG_DELAY_DEFAULT,
|
||||
PIEZO_CFG_SAMPLES_DEFAULT);
|
||||
DBG_PRINTF("[CMD] mcf -> freq=%d cycles=%d avg=%d delay=%d samples=%d\r\n",
|
||||
PIEZO_CFG_FREQ_DEFAULT,
|
||||
PIEZO_CFG_CYCLES_DEFAULT,
|
||||
PIEZO_CFG_AVG_DEFAULT,
|
||||
PIEZO_CFG_DELAY_DEFAULT,
|
||||
PIEZO_CFG_SAMPLES_DEFAULT);
|
||||
const piezo_config_t *cfg = piezo_config_get();
|
||||
send_response_piezo_config(cfg->freq, cfg->cycles, cfg->avg, cfg->delay_us, cfg->samples);
|
||||
piezo_config_log("[CMD] mcf ->", cfg);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* mcs?: piezo 측정 파라미터 쓰기
|
||||
*/
|
||||
static int cmd_mcs(const uint8_t *data, uint8_t data_len)
|
||||
{
|
||||
piezo_config_t cfg;
|
||||
|
||||
if (data_len < 10U)
|
||||
{
|
||||
send_response_u16("rcs:", 0xFFFF);
|
||||
DBG_PRINTF("[CMD] mcs: insufficient data len=%u\r\n", data_len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
get_data_u16_be(data, data_len, 0, &cfg.freq);
|
||||
get_data_u16_be(data, data_len, 1, &cfg.cycles);
|
||||
get_data_u16_be(data, data_len, 2, &cfg.avg);
|
||||
get_data_u16_be(data, data_len, 3, &cfg.delay_us);
|
||||
get_data_u16_be(data, data_len, 4, &cfg.samples);
|
||||
|
||||
if (!piezo_config_validate(&cfg))
|
||||
{
|
||||
send_response_u16("rcs:", 0xFFFE);
|
||||
piezo_config_log("[CMD] mcs invalid", &cfg);
|
||||
return 1;
|
||||
}
|
||||
|
||||
g_piezo_config = cfg;
|
||||
int err = piezo_config_save();
|
||||
send_response_u16("rcs:", (err == 0) ? 0U : 0xFFFD);
|
||||
piezo_config_log("[CMD] mcs saved", &g_piezo_config);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -977,6 +1168,7 @@ static const cmd_entry_t cmd_table[] =
|
||||
{ "maa?", cmd_maa },
|
||||
{ "mbb?", cmd_mbb },
|
||||
{ "mcf?", cmd_mcf },
|
||||
{ "mcs?", cmd_mcs },
|
||||
{ "mfv?", cmd_mfv },
|
||||
{ "mwh?", cmd_mwh },
|
||||
{ "mrh?", cmd_mrh },
|
||||
@@ -1026,7 +1218,8 @@ int dr_parser(const uint8_t *buf, uint16_t len)
|
||||
}
|
||||
|
||||
/* TAG 추출 (4바이트) */
|
||||
char tag[5] = {
|
||||
char tag[5] =
|
||||
{
|
||||
ascii_to_lower((char)buf[0]),
|
||||
ascii_to_lower((char)buf[1]),
|
||||
ascii_to_lower((char)buf[2]),
|
||||
@@ -1050,7 +1243,8 @@ int dr_parser(const uint8_t *buf, uint16_t len)
|
||||
}
|
||||
|
||||
DBG_ERR("[CMD] Unknown: raw=%s normalized=%s\r\n", raw_tag, tag);
|
||||
if (send_response_tag_echo("rxx:", raw_tag) != 0) {
|
||||
if (send_response_tag_echo("rxx:", raw_tag) != 0)
|
||||
{
|
||||
DBG_ERR("[CMD] rxx tx failed\r\n");
|
||||
}
|
||||
return 0;
|
||||
|
||||
@@ -9,6 +9,17 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint16_t freq;
|
||||
uint16_t cycles;
|
||||
uint16_t avg;
|
||||
uint16_t delay_us;
|
||||
uint16_t samples;
|
||||
} piezo_config_t;
|
||||
|
||||
int piezo_config_init(void);
|
||||
const piezo_config_t *piezo_config_get(void);
|
||||
int dr_parser(const uint8_t *buf, uint16_t len);
|
||||
|
||||
#endif /* CMD_PARSER_H__ */
|
||||
|
||||
Reference in New Issue
Block a user