주석 추가

This commit is contained in:
2026-06-12 15:28:27 +09:00
parent 5c7ef4e534
commit f25ff3d750
3 changed files with 116 additions and 24 deletions
+23 -6
View File
@@ -18,30 +18,32 @@
#define ECHO_MISO_NODE DT_NODELABEL(echo_miso) #define ECHO_MISO_NODE DT_NODELABEL(echo_miso)
#define ECHO_CS_NODE DT_NODELABEL(echo_cs) #define ECHO_CS_NODE DT_NODELABEL(echo_cs)
/* 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_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); static const struct gpio_dt_spec echo_miso = GPIO_DT_SPEC_GET(ECHO_MISO_NODE, gpios);
static const struct gpio_dt_spec echo_cs = GPIO_DT_SPEC_GET(ECHO_CS_NODE, gpios); static const struct gpio_dt_spec echo_cs = GPIO_DT_SPEC_GET(ECHO_CS_NODE, gpios);
/* /* SPIM3 레지스터 인스턴스 */
* NRFX_SPIM_INSTANCE() takes the peripheral register symbol (for example
* NRF_SPIM3), not a numeric instance index. Passing 3 makes p_reg invalid
* and causes a fault inside nrfx_spim_init().
*/
static nrfx_spim_t echo_spim = NRFX_SPIM_INSTANCE(NRF_SPIM3); static nrfx_spim_t echo_spim = NRFX_SPIM_INSTANCE(NRF_SPIM3);
static bool echo_adc_initialized; static bool echo_adc_initialized;
/* ADC 원본 수신 버퍼 */
static uint8_t raw_capture[ECHO_ADC_MAX_SAMPLES * 2]; static uint8_t raw_capture[ECHO_ADC_MAX_SAMPLES * 2];
/* GPIO 포트 레지스터 선택 */
static NRF_GPIO_Type *gpio_reg_for_spec(const struct gpio_dt_spec *spec) static NRF_GPIO_Type *gpio_reg_for_spec(const struct gpio_dt_spec *spec)
{ {
return (spec->port == DEVICE_DT_GET(DT_NODELABEL(gpio1))) ? NRF_P1 : NRF_P0; return (spec->port == DEVICE_DT_GET(DT_NODELABEL(gpio1))) ? NRF_P1 : NRF_P0;
} }
/* 포트 포함 절대 핀 번호 */
static uint32_t gpio_abs_pin(const struct gpio_dt_spec *spec) static uint32_t gpio_abs_pin(const struct gpio_dt_spec *spec)
{ {
uint32_t port = (spec->port == DEVICE_DT_GET(DT_NODELABEL(gpio1))) ? 1U : 0U; uint32_t port = (spec->port == DEVICE_DT_GET(DT_NODELABEL(gpio1))) ? 1U : 0U;
return NRF_GPIO_PIN_MAP(port, spec->pin); return NRF_GPIO_PIN_MAP(port, spec->pin);
} }
/* SPIM RX 전송 */
static int echo_adc_transfer_rx(uint8_t *buf, size_t len) static int echo_adc_transfer_rx(uint8_t *buf, size_t len)
{ {
nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_XFER_RX(buf, len); nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_XFER_RX(buf, len);
@@ -56,6 +58,7 @@ static int echo_adc_transfer_rx(uint8_t *buf, size_t len)
return 0; return 0;
} }
/* 12비트 샘플 추출 */
static void echo_adc_unpack_samples(uint16_t *samples, uint16_t num_samples) static void echo_adc_unpack_samples(uint16_t *samples, uint16_t num_samples)
{ {
for (uint16_t i = 0; i < num_samples; i++) for (uint16_t i = 0; i < num_samples; i++)
@@ -66,6 +69,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) static bool echo_adc_samples_all_zero(const uint16_t *samples, uint16_t num_samples)
{ {
for (uint16_t i = 0; i < num_samples; i++) for (uint16_t i = 0; i < num_samples; i++)
@@ -79,6 +83,7 @@ static bool echo_adc_samples_all_zero(const uint16_t *samples, uint16_t num_samp
return true; return true;
} }
/* 안정 우선 캡처 */
static int echo_adc_capture_slow(uint16_t *samples, uint16_t num_samples) static int echo_adc_capture_slow(uint16_t *samples, uint16_t num_samples)
{ {
for (uint16_t i = 0; i < num_samples; i++) for (uint16_t i = 0; i < num_samples; i++)
@@ -94,28 +99,33 @@ static int echo_adc_capture_slow(uint16_t *samples, uint16_t num_samples)
return 0; return 0;
} }
/* 속도 우선 캡처 */
static int echo_adc_capture_fast(uint16_t *samples, uint16_t num_samples) static int echo_adc_capture_fast(uint16_t *samples, uint16_t num_samples)
{ {
NRF_SPIM_Type *spim = echo_spim.p_reg; NRF_SPIM_Type *spim = echo_spim.p_reg;
NRF_GPIO_Type *cs_port = gpio_reg_for_spec(&echo_cs); NRF_GPIO_Type *cs_port = gpio_reg_for_spec(&echo_cs);
uint32_t cs_mask = BIT(echo_cs.pin); uint32_t cs_mask = BIT(echo_cs.pin);
/* SPIM 직접 제어 */
nrf_spim_enable(spim); nrf_spim_enable(spim);
spim->RXD.MAXCNT = 2U; spim->RXD.MAXCNT = 2U;
for (uint16_t i = 0; i < num_samples; i++) for (uint16_t i = 0; i < num_samples; i++)
{ {
/* CS 하강 후 2바이트 수신 */
cs_port->OUTCLR = cs_mask; cs_port->OUTCLR = cs_mask;
spim->RXD.PTR = (uint32_t)&raw_capture[i * 2U]; spim->RXD.PTR = (uint32_t)&raw_capture[i * 2U];
spim->EVENTS_END = 0; spim->EVENTS_END = 0;
spim->TASKS_START = 1; spim->TASKS_START = 1;
/* 전송 완료 대기 */
uint32_t timeout = 10000U; uint32_t timeout = 10000U;
while (!spim->EVENTS_END && (timeout > 0U)) while (!spim->EVENTS_END && (timeout > 0U))
{ {
timeout--; timeout--;
} }
/* CS 복귀 */
cs_port->OUTSET = cs_mask; cs_port->OUTSET = cs_mask;
if (timeout == 0U) if (timeout == 0U)
@@ -131,12 +141,14 @@ static int echo_adc_capture_fast(uint16_t *samples, uint16_t num_samples)
int echo_adc_init(void) int echo_adc_init(void)
{ {
/* 중복 초기화 방지 */
if (echo_adc_initialized || nrfx_spim_init_check(&echo_spim)) if (echo_adc_initialized || nrfx_spim_init_check(&echo_spim))
{ {
echo_adc_initialized = true; echo_adc_initialized = true;
return 0; return 0;
} }
/* GPIO 준비 확인 */
const struct gpio_dt_spec *pins[] = { &echo_sclk, &echo_miso, &echo_cs }; const struct gpio_dt_spec *pins[] = { &echo_sclk, &echo_miso, &echo_cs };
for (size_t i = 0; i < ARRAY_SIZE(pins); i++) for (size_t i = 0; i < ARRAY_SIZE(pins); i++)
@@ -148,6 +160,7 @@ int echo_adc_init(void)
} }
} }
/* ADC SPI 설정 */
nrfx_spim_config_t config = NRFX_SPIM_DEFAULT_CONFIG( nrfx_spim_config_t config = NRFX_SPIM_DEFAULT_CONFIG(
gpio_abs_pin(&echo_sclk), gpio_abs_pin(&echo_sclk),
NRF_SPIM_PIN_NOT_CONNECTED, NRF_SPIM_PIN_NOT_CONNECTED,
@@ -162,7 +175,7 @@ int echo_adc_init(void)
config.ss_active_high = false; config.ss_active_high = false;
nrfx_err_t err = nrfx_spim_init(&echo_spim, &config, NULL, NULL); nrfx_err_t err = nrfx_spim_init(&echo_spim, &config, NULL, NULL);
if (err != NRFX_SUCCESS) if (err != 0)
{ {
DBG_PRINTF("[ECHO] init fail err=%d\r\n", err); DBG_PRINTF("[ECHO] init fail err=%d\r\n", err);
return -EIO; return -EIO;
@@ -175,6 +188,7 @@ int echo_adc_init(void)
int echo_adc_wake(void) int echo_adc_wake(void)
{ {
/* 더미 읽기 웨이크업 */
int err = echo_adc_init(); int err = echo_adc_init();
if (err) if (err)
{ {
@@ -187,6 +201,7 @@ int echo_adc_wake(void)
int echo_adc_capture(uint16_t *samples, uint16_t num_samples) int echo_adc_capture(uint16_t *samples, uint16_t num_samples)
{ {
/* 입력값 검사 */
if (samples == NULL) if (samples == NULL)
{ {
return -EINVAL; return -EINVAL;
@@ -203,12 +218,14 @@ int echo_adc_capture(uint16_t *samples, uint16_t num_samples)
return err; return err;
} }
/* 빠른 캡처 우선 */
err = echo_adc_capture_fast(samples, num_samples); err = echo_adc_capture_fast(samples, num_samples);
if ((err == 0) && !echo_adc_samples_all_zero(samples, num_samples)) if ((err == 0) && !echo_adc_samples_all_zero(samples, num_samples))
{ {
return 0; return 0;
} }
/* 실패 시 안정 캡처 */
DBG_PRINTF("[ECHO] fast capture fallback err=%d\r\n", err); DBG_PRINTF("[ECHO] fast capture fallback err=%d\r\n", err);
return echo_adc_capture_slow(samples, num_samples); return echo_adc_capture_slow(samples, num_samples);
} }
+38
View File
@@ -23,16 +23,20 @@
#define MUX_SEL0_NODE DT_NODELABEL(mux_sel0) #define MUX_SEL0_NODE DT_NODELABEL(mux_sel0)
#define MUX_SEL1_NODE DT_NODELABEL(mux_sel1) #define MUX_SEL1_NODE DT_NODELABEL(mux_sel1)
/* 피에조 전원/구동 GPIO */
static const struct gpio_dt_spec piezo_pwr = GPIO_DT_SPEC_GET(PIEZO_PWR_NODE, gpios); static const struct gpio_dt_spec piezo_pwr = GPIO_DT_SPEC_GET(PIEZO_PWR_NODE, gpios);
static const struct gpio_dt_spec piezo_pe = GPIO_DT_SPEC_GET(PIEZO_PE_NODE, gpios); static const struct gpio_dt_spec piezo_pe = GPIO_DT_SPEC_GET(PIEZO_PE_NODE, gpios);
static const struct gpio_dt_spec piezo_p_out = GPIO_DT_SPEC_GET(PIEZO_P_OUT_NODE, gpios); static const struct gpio_dt_spec piezo_p_out = GPIO_DT_SPEC_GET(PIEZO_P_OUT_NODE, gpios);
static const struct gpio_dt_spec piezo_n_out = GPIO_DT_SPEC_GET(PIEZO_N_OUT_NODE, gpios); static const struct gpio_dt_spec piezo_n_out = GPIO_DT_SPEC_GET(PIEZO_N_OUT_NODE, gpios);
static const struct gpio_dt_spec piezo_dmp = GPIO_DT_SPEC_GET(PIEZO_DMP_NODE, gpios); static const struct gpio_dt_spec piezo_dmp = GPIO_DT_SPEC_GET(PIEZO_DMP_NODE, gpios);
/* 채널 선택 MUX GPIO */
static const struct gpio_dt_spec mux_en_a = GPIO_DT_SPEC_GET(MUX_EN_A_NODE, gpios); static const struct gpio_dt_spec mux_en_a = GPIO_DT_SPEC_GET(MUX_EN_A_NODE, gpios);
static const struct gpio_dt_spec mux_en_b = GPIO_DT_SPEC_GET(MUX_EN_B_NODE, gpios); static const struct gpio_dt_spec mux_en_b = GPIO_DT_SPEC_GET(MUX_EN_B_NODE, gpios);
static const struct gpio_dt_spec mux_sel0 = GPIO_DT_SPEC_GET(MUX_SEL0_NODE, gpios); static const struct gpio_dt_spec mux_sel0 = GPIO_DT_SPEC_GET(MUX_SEL0_NODE, gpios);
static const struct gpio_dt_spec mux_sel1 = GPIO_DT_SPEC_GET(MUX_SEL1_NODE, gpios); static const struct gpio_dt_spec mux_sel1 = GPIO_DT_SPEC_GET(MUX_SEL1_NODE, gpios);
/* MUX 제어 상태 */
struct mux_state { struct mux_state {
uint8_t en_a; uint8_t en_a;
uint8_t en_b; uint8_t en_b;
@@ -40,6 +44,7 @@ struct mux_state {
uint8_t sel1; uint8_t sel1;
}; };
/* 채널별 MUX EN/SEL 조합 */
static const struct mux_state mux_map[PIEZO_NUM_CHANNELS] = { static const struct mux_state mux_map[PIEZO_NUM_CHANNELS] = {
{1, 0, 0, 0}, {1, 0, 0, 0},
{1, 0, 1, 0}, {1, 0, 1, 0},
@@ -50,26 +55,33 @@ static const struct mux_state mux_map[PIEZO_NUM_CHANNELS] = {
}; };
static bool piezo_initialized; static bool piezo_initialized;
/* 빠른 GPIO 제어용 절대 핀 */
static uint32_t piezo_pe_abs; static uint32_t piezo_pe_abs;
static uint32_t piezo_p_out_abs; static uint32_t piezo_p_out_abs;
static uint32_t piezo_n_out_abs; static uint32_t piezo_n_out_abs;
static uint32_t piezo_dmp_abs; static uint32_t piezo_dmp_abs;
/* 포트 OUT 직접 갱신용 비트 마스크 */
static uint32_t piezo_pe_mask; static uint32_t piezo_pe_mask;
static uint32_t piezo_p_out_mask; static uint32_t piezo_p_out_mask;
static uint32_t piezo_n_out_mask; static uint32_t piezo_n_out_mask;
static uint32_t piezo_dmp_mask; static uint32_t piezo_dmp_mask;
/* 포트 포함 절대 핀 번호 */
static uint32_t gpio_abs_pin(const struct gpio_dt_spec *spec) static uint32_t gpio_abs_pin(const struct gpio_dt_spec *spec)
{ {
uint32_t port = (spec->port == DEVICE_DT_GET(DT_NODELABEL(gpio1))) ? 1U : 0U; uint32_t port = (spec->port == DEVICE_DT_GET(DT_NODELABEL(gpio1))) ? 1U : 0U;
return NRF_GPIO_PIN_MAP(port, spec->pin); return NRF_GPIO_PIN_MAP(port, spec->pin);
} }
/* 버스트 타이밍용 1사이클 지연 */
static inline void burst_nop(void) static inline void burst_nop(void)
{ {
__asm__ volatile("nop"); __asm__ volatile("nop");
} }
/* 짧은 반주기 보정 지연 */
static inline void nop_delay_9(void) static inline void nop_delay_9(void)
{ {
burst_nop(); burst_nop(); burst_nop(); burst_nop(); burst_nop(); burst_nop();
@@ -77,6 +89,7 @@ static inline void nop_delay_9(void)
burst_nop(); burst_nop(); burst_nop(); burst_nop(); burst_nop(); burst_nop();
} }
/* 긴 반주기 보정 지연 */
static inline void nop_delay_14(void) static inline void nop_delay_14(void)
{ {
burst_nop(); burst_nop(); burst_nop(); burst_nop(); burst_nop(); burst_nop(); burst_nop(); burst_nop();
@@ -85,6 +98,7 @@ static inline void nop_delay_14(void)
burst_nop(); burst_nop(); burst_nop(); burst_nop();
} }
/* 댐핑 펄스 폭 지연 */
static inline void nop_delay_32(void) static inline void nop_delay_32(void)
{ {
for (int i = 0; i < 32; i++) for (int i = 0; i < 32; i++)
@@ -93,6 +107,7 @@ static inline void nop_delay_32(void)
} }
} }
/* 출력/MUX 안전 대기 상태 */
static void piezo_drive_idle(void) static void piezo_drive_idle(void)
{ {
nrf_gpio_pin_clear(piezo_pe_abs); nrf_gpio_pin_clear(piezo_pe_abs);
@@ -108,11 +123,13 @@ static void piezo_drive_idle(void)
int piezo_init(void) int piezo_init(void)
{ {
/* 중복 초기화 방지 */
if (piezo_initialized) if (piezo_initialized)
{ {
return 0; 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_pwr, &piezo_pe, &piezo_p_out, &piezo_n_out,
@@ -121,12 +138,14 @@ int piezo_init(void)
for (size_t i = 0; i < ARRAY_SIZE(pins); i++) for (size_t i = 0; i < ARRAY_SIZE(pins); i++)
{ {
/* GPIO 포트 준비 확인 */
if (!device_is_ready(pins[i]->port)) if (!device_is_ready(pins[i]->port))
{ {
DBG_PRINTF("[PIEZO] GPIO device not ready (%d)\r\n", (int)i); DBG_PRINTF("[PIEZO] GPIO device not ready (%d)\r\n", (int)i);
return -ENODEV; return -ENODEV;
} }
/* 초기 출력 Low */
int err = gpio_pin_configure_dt(pins[i], GPIO_OUTPUT_INACTIVE); int err = gpio_pin_configure_dt(pins[i], GPIO_OUTPUT_INACTIVE);
if (err) if (err)
{ {
@@ -135,6 +154,7 @@ int piezo_init(void)
} }
} }
/* 직접 제어용 핀/마스크 캐시 */
piezo_pe_abs = gpio_abs_pin(&piezo_pe); piezo_pe_abs = gpio_abs_pin(&piezo_pe);
piezo_p_out_abs = gpio_abs_pin(&piezo_p_out); piezo_p_out_abs = gpio_abs_pin(&piezo_p_out);
piezo_n_out_abs = gpio_abs_pin(&piezo_n_out); piezo_n_out_abs = gpio_abs_pin(&piezo_n_out);
@@ -152,27 +172,32 @@ int piezo_init(void)
void piezo_power_on(void) void piezo_power_on(void)
{ {
/* 필요 시 자동 초기화 */
if (!piezo_initialized && piezo_init() != 0) if (!piezo_initialized && piezo_init() != 0)
{ {
return; return;
} }
/* 피에조 전원 인가 */
gpio_pin_set_dt(&piezo_pwr, 1); gpio_pin_set_dt(&piezo_pwr, 1);
} }
void piezo_power_off(void) void piezo_power_off(void)
{ {
/* 필요 시 자동 초기화 */
if (!piezo_initialized && piezo_init() != 0) if (!piezo_initialized && piezo_init() != 0)
{ {
return; return;
} }
/* 출력 정지 후 전원 차단 */
piezo_drive_idle(); piezo_drive_idle();
gpio_pin_set_dt(&piezo_pwr, 0); gpio_pin_set_dt(&piezo_pwr, 0);
} }
int piezo_select_channel(uint8_t channel) int piezo_select_channel(uint8_t channel)
{ {
/* 필요 시 자동 초기화 */
if (!piezo_initialized) if (!piezo_initialized)
{ {
int err = piezo_init(); int err = piezo_init();
@@ -182,28 +207,35 @@ int piezo_select_channel(uint8_t channel)
} }
} }
/* 채널 범위 검사 */
if (channel >= PIEZO_NUM_CHANNELS) if (channel >= PIEZO_NUM_CHANNELS)
{ {
return -EINVAL; return -EINVAL;
} }
/* MUX 채널 선택 */
gpio_pin_set_dt(&mux_en_a, mux_map[channel].en_a); 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_en_b, mux_map[channel].en_b);
gpio_pin_set_dt(&mux_sel0, mux_map[channel].sel0); gpio_pin_set_dt(&mux_sel0, mux_map[channel].sel0);
gpio_pin_set_dt(&mux_sel1, mux_map[channel].sel1); gpio_pin_set_dt(&mux_sel1, mux_map[channel].sel1);
/* MUX 안정화 대기 */
k_busy_wait(PIEZO_MUX_SETTLE_US); k_busy_wait(PIEZO_MUX_SETTLE_US);
return 0; return 0;
} }
void piezo_burst_sw(uint8_t cycles) void piezo_burst_sw(uint8_t cycles)
{ {
/* 초기화/횟수 검사 */
if (!piezo_initialized || cycles == 0U) if (!piezo_initialized || cycles == 0U)
{ {
return; return;
} }
/* 타이밍 흔들림 방지 */
unsigned int key = irq_lock(); unsigned int key = irq_lock();
/* P1 비제어 핀 상태 보존 */
uint32_t saved_p1_out = NRF_P1->OUT; 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_ctrl_mask = piezo_p_out_mask | piezo_n_out_mask | piezo_dmp_mask;
uint32_t p1_all_low = saved_p1_out & ~p1_ctrl_mask; uint32_t p1_all_low = saved_p1_out & ~p1_ctrl_mask;
@@ -211,21 +243,27 @@ void piezo_burst_sw(uint8_t cycles)
uint32_t p1_p_low_n_high = p1_all_low | piezo_n_out_mask; 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; uint32_t p1_dmp_high = p1_all_low | piezo_dmp_mask;
/* 구동 Enable 전 초기 Low 정렬 */
NRF_P0->OUTCLR = piezo_pe_mask; NRF_P0->OUTCLR = piezo_pe_mask;
NRF_P1->OUT = p1_all_low; NRF_P1->OUT = p1_all_low;
NRF_P0->OUTSET = piezo_pe_mask; NRF_P0->OUTSET = piezo_pe_mask;
for (uint8_t i = 0; i < cycles; i++) for (uint8_t i = 0; i < cycles; i++)
{ {
/* P High / N Low */
NRF_P1->OUT = p1_p_high_n_low; NRF_P1->OUT = p1_p_high_n_low;
nop_delay_14(); nop_delay_14();
/* P Low / N High */
NRF_P1->OUT = p1_p_low_n_high; NRF_P1->OUT = p1_p_low_n_high;
nop_delay_9(); nop_delay_9();
} }
/* 잔류 전하 방전 */
NRF_P1->OUT = p1_dmp_high; NRF_P1->OUT = p1_dmp_high;
nop_delay_32(); nop_delay_32();
/* 출력 Low 복귀 */
NRF_P1->OUT = p1_all_low; NRF_P1->OUT = p1_all_low;
NRF_P0->OUTCLR = piezo_pe_mask; NRF_P0->OUTCLR = piezo_pe_mask;
irq_unlock(key); irq_unlock(key);
+55 -18
View File
@@ -33,18 +33,21 @@ LOG_MODULE_REGISTER(vesiscan, LOG_LEVEL_INF);
#define BLE_CMD_MAX_LEN 256 #define BLE_CMD_MAX_LEN 256
#define BLE_CMD_WORKQ_STACK_SZ 8192 #define BLE_CMD_WORKQ_STACK_SZ 8192
/* /*
* mbb?는 piezo sweep, ADC capture, BLE 응답 패킷 조립까지 한 스레드에서 처리한다. * BLE 명령 처리 스택
* 초기 4096 바이트로는 "어떤 날은 되고 어떤 날은 리셋되는" 경계 상태가 날 수 있어서 * - piezo sweep
* 디버깅 중에는 여유 있게 8192 바이트로 올려 둔다. * - ADC capture
* - BLE 응답 패킷 조립
* - 디버깅 여유분 포함
*/ */
#define BLE_CMD_WORKQ_PRIORITY 10 #define BLE_CMD_WORKQ_PRIORITY 10
/*============================================================================== /*==============================================================================
* Devicetree GPOP Settings * Devicetree GPIO Settings
*============================================================================*/ *============================================================================*/
#define POWER_HOLD_NODE DT_NODELABEL(pwr_hold) // 전원 래치 #define POWER_HOLD_NODE DT_NODELABEL(pwr_hold) /* 전원 래치 */
#define POWER_BTN_NODE DT_NODELABEL(button_check) // 전원 버튼 #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_hold = GPIO_DT_SPEC_GET(POWER_HOLD_NODE, gpios);
static const struct gpio_dt_spec power_btn = GPIO_DT_SPEC_GET(POWER_BTN_NODE, gpios); static const struct gpio_dt_spec power_btn = GPIO_DT_SPEC_GET(POWER_BTN_NODE, gpios);
@@ -81,10 +84,10 @@ static bool power_btn_suspended; /* 측정 중 전원 버튼 상태
* k_work를 사용하면 시스템 워크큐 스레드에서 실행되므로 안전하게 BLE API 호출 가능. * k_work를 사용하면 시스템 워크큐 스레드에서 실행되므로 안전하게 BLE API 호출 가능.
* main_s() 타이머 콜백에서 k_work_submit()으로 예약하여 사용한다. * main_s() 타이머 콜백에서 k_work_submit()으로 예약하여 사용한다.
*/ */
static bool resume_without_power_button; static bool resume_without_power_button; /* 리셋 후 버튼 없이 복귀 */
static uint32_t boot_reset_reason; static uint32_t boot_reset_reason; /* RESETREAS 원본 값 */
static struct k_work adv_start_work; static struct k_work adv_start_work; /* advertising 시작 work */
static struct k_work adv_stop_work; static struct k_work adv_stop_work; /* advertising 중지 work */
/* /*
* BLE RX 콜백 안에서 무거운 일을 바로 처리하지 않기 위해 별도 work queue를 둔다. * BLE RX 콜백 안에서 무거운 일을 바로 처리하지 않기 위해 별도 work queue를 둔다.
@@ -100,20 +103,21 @@ static struct k_work adv_stop_work;
*/ */
static struct k_work_q ble_cmd_work_q; static struct k_work_q ble_cmd_work_q;
K_THREAD_STACK_DEFINE(ble_cmd_workq_stack, BLE_CMD_WORKQ_STACK_SZ); K_THREAD_STACK_DEFINE(ble_cmd_workq_stack, BLE_CMD_WORKQ_STACK_SZ);
static struct k_work ble_cmd_work; static struct k_work ble_cmd_work; /* BLE 명령 처리 work */
static struct k_spinlock ble_cmd_lock; static struct k_spinlock ble_cmd_lock; /* RX 공유 버퍼 보호 */
static uint8_t ble_cmd_buf[BLE_CMD_MAX_LEN]; static uint8_t ble_cmd_buf[BLE_CMD_MAX_LEN];/* RX 명령 복사 버퍼 */
static uint16_t ble_cmd_len; static uint16_t ble_cmd_len; /* RX 명령 길이 */
static bool ble_cmd_pending; static bool ble_cmd_pending; /* 처리 대기 명령 있음 */
static void ble_cmd_work_handler(struct k_work *work) static void ble_cmd_work_handler(struct k_work *work)
{ {
ARG_UNUSED(work); ARG_UNUSED(work);
/* work queue 스레드에서 쓸 로컬 복사본. 원본 공유 버퍼를 오래 잡고 있지 않기 위해 만든다. */ /* work queue 로컬 복사본 */
uint8_t local_buf[BLE_CMD_MAX_LEN]; uint8_t local_buf[BLE_CMD_MAX_LEN];
uint16_t local_len = 0U; uint16_t local_len = 0U;
/* 공유 RX 버퍼 짧게 잠금 */
k_spinlock_key_t key = k_spin_lock(&ble_cmd_lock); k_spinlock_key_t key = k_spin_lock(&ble_cmd_lock);
if (ble_cmd_pending) if (ble_cmd_pending)
{ {
@@ -128,10 +132,11 @@ static void ble_cmd_work_handler(struct k_work *work)
return; return;
} }
/* 여기서부터는 BLE 콜백 문맥이 아니라 일반 스레드 문맥이라 긴 작업을 해도 된다. */ /* 일반 스레드 문맥 명령 처리 */
DBG_CORE("[BLE RX] worker dispatch len=%u\r\n", local_len); DBG_CORE("[BLE RX] worker dispatch len=%u\r\n", local_len);
dr_parser(local_buf, local_len); dr_parser(local_buf, local_len);
/* 명령 처리 완료 표시 */
key = k_spin_lock(&ble_cmd_lock); key = k_spin_lock(&ble_cmd_lock);
ble_cmd_pending = false; ble_cmd_pending = false;
ble_cmd_len = 0U; ble_cmd_len = 0U;
@@ -157,6 +162,7 @@ static void adv_stop_work_handler(struct k_work *work)
*============================================================================*/ *============================================================================*/
static void ble_rx_handler(const uint8_t *data, uint16_t len) static void ble_rx_handler(const uint8_t *data, uint16_t len)
{ {
/* RX 데이터 앞부분 로그 */
DBG_CORE("[BLE RX] %u bytes:", len); DBG_CORE("[BLE RX] %u bytes:", len);
for (uint16_t i = 0; i < len && i < 32; i++) for (uint16_t i = 0; i < len && i < 32; i++)
{ {
@@ -186,15 +192,18 @@ static void ble_rx_handler(const uint8_t *data, uint16_t len)
return; return;
} }
/* 명령 버퍼 복사 */
memcpy(ble_cmd_buf, data, len); memcpy(ble_cmd_buf, data, len);
ble_cmd_len = len; ble_cmd_len = len;
ble_cmd_pending = true; ble_cmd_pending = true;
k_spin_unlock(&ble_cmd_lock, key); k_spin_unlock(&ble_cmd_lock, key);
/* 명령 처리 work 예약 */
DBG_CORE("[BLE RX] queued len=%u\r\n", len); DBG_CORE("[BLE RX] queued len=%u\r\n", len);
int err = k_work_submit_to_queue(&ble_cmd_work_q, &ble_cmd_work); int err = k_work_submit_to_queue(&ble_cmd_work_q, &ble_cmd_work);
if (err < 0) if (err < 0)
{ {
/* 예약 실패 시 pending 복구 */
key = k_spin_lock(&ble_cmd_lock); key = k_spin_lock(&ble_cmd_lock);
ble_cmd_pending = false; ble_cmd_pending = false;
ble_cmd_len = 0U; ble_cmd_len = 0U;
@@ -212,7 +221,10 @@ static void ble_rx_handler(const uint8_t *data, uint16_t len)
* 2초 후 main_s()에서 래치(HIGH) 설정 */ * 2초 후 main_s()에서 래치(HIGH) 설정 */
static void boot_context_detect(void) static void boot_context_detect(void)
{ {
/* 부팅 원인 확인 */
boot_reset_reason = nrf_power_resetreas_get(NRF_POWER); boot_reset_reason = nrf_power_resetreas_get(NRF_POWER);
/* 소프트 리셋 계열 자동 복귀 */
resume_without_power_button = resume_without_power_button =
(boot_reset_reason & (NRF_POWER_RESETREAS_SREQ_MASK | (boot_reset_reason & (NRF_POWER_RESETREAS_SREQ_MASK |
NRF_POWER_RESETREAS_DOG_MASK | NRF_POWER_RESETREAS_DOG_MASK |
@@ -221,6 +233,7 @@ static void boot_context_detect(void)
if (boot_reset_reason != 0U) if (boot_reset_reason != 0U)
{ {
/* RESETREAS 플래그 정리 */
nrf_power_resetreas_clear(NRF_POWER, boot_reset_reason); nrf_power_resetreas_clear(NRF_POWER, boot_reset_reason);
} }
} }
@@ -228,11 +241,13 @@ static void boot_context_detect(void)
static void power_hold_init(void) static void power_hold_init(void)
{ {
#if defined(CONFIG_BOOTLOADER_MCUBOOT) #if defined(CONFIG_BOOTLOADER_MCUBOOT)
/* DFU 이미지 확인 전 전원 유지 */
bool dfu_confirm_pending = !boot_is_img_confirmed(); bool dfu_confirm_pending = !boot_is_img_confirmed();
#else #else
bool dfu_confirm_pending = false; bool dfu_confirm_pending = false;
#endif #endif
/* 리셋 복귀/DFU 확인 대기 시 즉시 래치 */
gpio_pin_configure_dt(&power_hold, gpio_pin_configure_dt(&power_hold,
(resume_without_power_button || dfu_confirm_pending) ? GPIO_OUTPUT_ACTIVE (resume_without_power_button || dfu_confirm_pending) ? GPIO_OUTPUT_ACTIVE
: GPIO_OUTPUT_INACTIVE); : GPIO_OUTPUT_INACTIVE);
@@ -267,6 +282,7 @@ static void gpio_init(void)
*============================================================================*/ *============================================================================*/
static void load_default_config(void) static void load_default_config(void)
{ {
/* 기본 문자열 길이 제한 */
size_t serial_len = strlen(SERIAL_NUMBER); size_t serial_len = strlen(SERIAL_NUMBER);
size_t hw_len = strlen(HARDWARE_VERSION); size_t hw_len = strlen(HARDWARE_VERSION);
size_t passkey_len = strlen(DEFAULT_PASSKEY); size_t passkey_len = strlen(DEFAULT_PASSKEY);
@@ -287,12 +303,15 @@ static void load_default_config(void)
memset(SERIAL_NO, 0, sizeof(SERIAL_NO)); memset(SERIAL_NO, 0, sizeof(SERIAL_NO));
memcpy(SERIAL_NO, SERIAL_NUMBER, serial_len); memcpy(SERIAL_NO, SERIAL_NUMBER, serial_len);
/* 기본 HW 버전 복사 */
memset(HW_NO, 0, sizeof(HW_NO)); memset(HW_NO, 0, sizeof(HW_NO));
memcpy(HW_NO, HARDWARE_VERSION, hw_len); memcpy(HW_NO, HARDWARE_VERSION, hw_len);
/* 기본 BLE 패스키 복사 */
memset(m_static_passkey, 0, sizeof(m_static_passkey)); memset(m_static_passkey, 0, sizeof(m_static_passkey));
memcpy(m_static_passkey, DEFAULT_PASSKEY, passkey_len); memcpy(m_static_passkey, DEFAULT_PASSKEY, passkey_len);
/* 기본 상태값 */
m_reset_status = 1; m_reset_status = 1;
bond_data_delete = true; bond_data_delete = true;
@@ -433,12 +452,19 @@ static void main_s(struct k_timer *timer)
*============================================================================*/ *============================================================================*/
static void timers_init(void) static void timers_init(void)
{ {
/* 전원 상태머신 타이머 */
k_timer_init(&m_power_on_delay_timer, main_s, NULL); k_timer_init(&m_power_on_delay_timer, main_s, NULL);
k_timer_init(&m_power_off_delay_timer, t_power_off_timeout_handler, NULL); k_timer_init(&m_power_off_delay_timer, t_power_off_timeout_handler, NULL);
/* BLE advertising work */
k_work_init(&adv_start_work, adv_start_work_handler); k_work_init(&adv_start_work, adv_start_work_handler);
k_work_init(&adv_stop_work, adv_stop_work_handler); k_work_init(&adv_stop_work, adv_stop_work_handler);
/* BLE 명령 처리 work queue */
k_work_init(&ble_cmd_work, ble_cmd_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(); power_timer_init();
} }
@@ -450,16 +476,19 @@ static void timers_start(void)
static void resume_device_after_soft_reset(void) static void resume_device_after_soft_reset(void)
{ {
/* 일반 버튼 부팅이면 skip */
if (!resume_without_power_button) if (!resume_without_power_button)
{ {
return; return;
} }
/* 전원 래치 상태 복구 */
device_on = true; device_on = true;
cnt_s = 0; cnt_s = 0;
boot_btn_released = (gpio_pin_get_dt(&power_btn) != 1); boot_btn_released = (gpio_pin_get_dt(&power_btn) != 1);
m_reset_status = 1; m_reset_status = 1;
/* 정상 ON 상태 서비스 재시작 */
power_control_handler(ON); power_control_handler(ON);
led_set_state(LED_STATE_ADVERTISING); led_set_state(LED_STATE_ADVERTISING);
battery_timer_start(); battery_timer_start();
@@ -471,6 +500,7 @@ static void resume_device_after_soft_reset(void)
static void confirm_running_image(void) static void confirm_running_image(void)
{ {
#if defined(CONFIG_BOOTLOADER_MCUBOOT) #if defined(CONFIG_BOOTLOADER_MCUBOOT)
/* MCUboot revert 방지 */
if (!boot_is_img_confirmed()) if (!boot_is_img_confirmed())
{ {
int err = boot_write_img_confirmed(); int err = boot_write_img_confirmed();
@@ -492,6 +522,7 @@ static void confirm_running_image(void)
static const char *swap_type_to_str(int swap_type) static const char *swap_type_to_str(int swap_type)
{ {
/* MCUboot swap 상태 문자열 */
switch (swap_type) switch (swap_type)
{ {
case BOOT_SWAP_TYPE_NONE: case BOOT_SWAP_TYPE_NONE:
@@ -512,6 +543,7 @@ static const char *swap_type_to_str(int swap_type)
static void log_mcuboot_state(void) static void log_mcuboot_state(void)
{ {
#if defined(CONFIG_BOOTLOADER_MCUBOOT) #if defined(CONFIG_BOOTLOADER_MCUBOOT)
/* 현재 부팅 슬롯/교체 상태 */
struct mcuboot_img_header header; struct mcuboot_img_header header;
uint8_t active_slot = boot_fetch_active_slot(); uint8_t active_slot = boot_fetch_active_slot();
int swap_type = mcuboot_swap_type(); int swap_type = mcuboot_swap_type();
@@ -525,6 +557,7 @@ static void log_mcuboot_state(void)
if (err == 0) if (err == 0)
{ {
/* 실행 이미지 버전 */
DBG_CORE("[DFU] image version %u.%u.%u+%u size=0x%x\r\n", 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.major,
header.h.v1.sem_ver.minor, header.h.v1.sem_ver.minor,
@@ -546,22 +579,25 @@ static void log_mcuboot_state(void)
*============================================================================*/ *============================================================================*/
int main(void) int main(void)
{ {
/* 리셋/복귀 컨텍스트 */
boot_context_detect(); boot_context_detect();
/*── Phase 1: 하드웨어 기본 초기화 ──*/ /*── Phase 1: 하드웨어 기본 초기화 ──*/
power_hold_init(); power_hold_init();
cnt_s = 0; cnt_s = 0;
/* 버튼 부팅 기본 상태 */
device_on = false; device_on = false;
boot_btn_released = false; boot_btn_released = false;
confirm_running_image(); confirm_running_image();
DBG_CORE("\r\n========================================\r\n"); DBG_CORE("\r\n========================================\r\n");
DBG_CORE(" TEST BUILD %s (Zephyr)\r\n", FIRMWARE_VERSION); DBG_CORE(" TEST BUILD %s (Zephyr)\r\n", FIRMWARE_VERSION);
DBG_CORE(" BUILD TAG: TEST-ADV-UNIT-000\r\n"); DBG_CORE(" BUILD TAG: TEST-ADV-UNIT-001\r\n");
DBG_CORE("========================================\r\n"); DBG_CORE("========================================\r\n");
DBG_CORE("[1] HW Init\r\n"); DBG_CORE("[1] HW Init\r\n");
/* 기본 하드웨어/센서 초기화 */
gpio_init(); gpio_init();
timers_init(); timers_init();
log_mcuboot_state(); log_mcuboot_state();
@@ -579,6 +615,7 @@ int main(void)
if (ble_service_init(ble_rx_handler) == 0) if (ble_service_init(ble_rx_handler) == 0)
{ {
DBG_CORE(" ble/nus OK\r\n"); DBG_CORE(" ble/nus OK\r\n");
/* 소프트 리셋 후 서비스 복구 */
resume_device_after_soft_reset(); resume_device_after_soft_reset();
} }
else else