ADC까지, 신호 확인 필요(반사 신호 약함 or 뒤쪽을 찍고 있음)

This commit is contained in:
2026-05-08 18:09:04 +09:00
parent 17a78d4418
commit d7c5b34da3
23 changed files with 1992 additions and 83 deletions
+170 -26
View File
@@ -28,6 +28,15 @@
LOG_MODULE_REGISTER(vesiscan, LOG_LEVEL_INF);
#define BLE_CMD_MAX_LEN 256
#define BLE_CMD_WORKQ_STACK_SZ 8192
/*
* mbb?는 piezo sweep, ADC capture, BLE 응답 패킷 조립까지 한 스레드에서 처리한다.
* 초기 4096 바이트로는 "어떤 날은 되고 어떤 날은 리셋되는" 경계 상태가 날 수 있어서
* 디버깅 중에는 여유 있게 8192 바이트로 올려 둔다.
*/
#define BLE_CMD_WORKQ_PRIORITY 10
/*==============================================================================
* Devicetree GPOP Settings
*============================================================================*/
@@ -51,13 +60,14 @@ volatile bool processing = false; /* 센서 데이터 처리 중 플
bool bond_data_delete = true; /* 본딩 데이터 삭제 요청 */
uint32_t m_life_cycle = 0; /* 디바이스 수명 카운터 */
uint8_t m_reset_status = 1; /* 리셋 상태 코드 */
char SERIAL_NO[SERIAL_NO_LENGTH]; /* 시리얼 번호 */
char HW_NO[HW_NO_LENGTH]; /* 하드웨어 번호 */
char m_static_passkey[PASSKEY_LENGTH]; /* BLE 정적 패스키 */
char SERIAL_NO[SERIAL_NO_BUF_SIZE]; /* 시리얼 번호 */
char HW_NO[HW_NO_BUF_SIZE]; /* 하드웨어 번호 */
char m_static_passkey[PASSKEY_BUF_SIZE]; /* BLE 정적 패스키 */
static uint16_t cnt_s; /* 전원 버튼 폴링 카운터 (5ms 단위) */
static bool device_on = false; /* 디바이스 전원 상태 (래치 완료 여부) */
static bool boot_btn_released = false; /* 부팅 후 버튼 놓았는지 여부 */
static bool power_btn_suspended; /* 측정 중 전원 버튼 상태머신 일시 정지 */
/*
* BLE advertising 제어용 워크 아이템
@@ -72,6 +82,56 @@ static bool boot_btn_released = false; /* 부팅 후 버튼 놓았는지 여
static struct k_work adv_start_work;
static struct k_work adv_stop_work;
/*
* BLE RX 콜백 안에서 무거운 일을 바로 처리하지 않기 위해 별도 work queue를 둔다.
*
* 이유:
* - BLE 콜백은 "일단 빨리 빠져나오는 것"이 중요하다.
* - 그 안에서 mbb? 같은 긴 측정을 돌리면 BLE 스택 타이밍을 망칠 수 있다.
* - 그래서 수신 데이터는 일단 복사만 하고,
* 실제 파싱/측정은 아래 work queue 스레드에서 천천히 처리한다.
*
* 현재 구조는 "동시에 여러 명령 처리"가 아니라 "한 번에 하나만 처리" 모델이다.
* 즉, 긴 명령 하나가 끝날 때까지 다음 명령은 drop된다.
*/
static struct k_work_q ble_cmd_work_q;
K_THREAD_STACK_DEFINE(ble_cmd_workq_stack, BLE_CMD_WORKQ_STACK_SZ);
static struct k_work ble_cmd_work;
static struct k_spinlock ble_cmd_lock;
static uint8_t ble_cmd_buf[BLE_CMD_MAX_LEN];
static uint16_t ble_cmd_len;
static bool ble_cmd_pending;
static void ble_cmd_work_handler(struct k_work *work)
{
ARG_UNUSED(work);
/* work queue 스레드에서 쓸 로컬 복사본. 원본 공유 버퍼를 오래 잡고 있지 않기 위해 만든다. */
uint8_t local_buf[BLE_CMD_MAX_LEN];
uint16_t local_len = 0U;
k_spinlock_key_t key = k_spin_lock(&ble_cmd_lock);
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) {
DBG_ERR("[BLE RX] worker: empty\r\n");
return;
}
/* 여기서부터는 BLE 콜백 문맥이 아니라 일반 스레드 문맥이라 긴 작업을 해도 된다. */
DBG_CORE("[BLE RX] worker dispatch len=%u\r\n", local_len);
dr_parser(local_buf, local_len);
key = k_spin_lock(&ble_cmd_lock);
ble_cmd_pending = false;
ble_cmd_len = 0U;
k_spin_unlock(&ble_cmd_lock, key);
}
/* 시스템 워크큐에서 BLE advertising 시작 */
static void adv_start_work_handler(struct k_work *work)
{
@@ -91,17 +151,50 @@ static void adv_stop_work_handler(struct k_work *work)
*============================================================================*/
static void ble_rx_handler(const uint8_t *data, uint16_t len)
{
DBG_PRINTF("[BLE RX] %d bytes: ", len);
DBG_CORE("[BLE RX] %u bytes:", len);
for (uint16_t i = 0; i < len && i < 32; i++)
{
DBG_PRINTF("%02X ", data[i]);
DBG_CORE(" %02X", data[i]);
}
if (len > 32)
{
DBG_PRINTF("...");
DBG_CORE(" ...");
}
DBG_CORE("\r\n");
if (len > BLE_CMD_MAX_LEN)
{
DBG_ERR("[BLE RX] drop: len=%u exceeds %d\r\n", len, BLE_CMD_MAX_LEN);
return;
}
/*
* 이미 긴 명령 하나를 처리 중이면 새 명령은 받지 않는다.
* 지금은 안정성 우선이라 "큐에 여러 개 쌓기"보다 "한 번에 하나"가 더 안전하다.
*/
k_spinlock_key_t key = k_spin_lock(&ble_cmd_lock);
if (ble_cmd_pending)
{
k_spin_unlock(&ble_cmd_lock, key);
DBG_ERR("[BLE RX] drop: command busy\r\n");
return;
}
memcpy(ble_cmd_buf, data, len);
ble_cmd_len = len;
ble_cmd_pending = true;
k_spin_unlock(&ble_cmd_lock, key);
DBG_CORE("[BLE RX] queued len=%u\r\n", len);
int err = k_work_submit_to_queue(&ble_cmd_work_q, &ble_cmd_work);
if (err < 0)
{
key = k_spin_lock(&ble_cmd_lock);
ble_cmd_pending = false;
ble_cmd_len = 0U;
k_spin_unlock(&ble_cmd_lock, key);
DBG_ERR("[BLE RX] queue submit fail err=%d\r\n", err);
}
DBG_PRINTF("\r\n");
dr_parser(data, len);
}
/*==============================================================================
@@ -121,7 +214,6 @@ static void power_control_handler(on_off_cont_t device_power_st)
{
if (device_power_st == OFF) {
gpio_pin_set_dt(&power_hold, 0); /* P0.08 LOW → 전원 래치 해제 → 전원 차단 */
DBG_PRINTF("[PWR] OFF\r\n");
} else {
gpio_pin_set_dt(&power_hold, 1); /* P0.08 HIGH → 전원 유지 */
DBG_PRINTF("[PWR] ON\r\n");
@@ -143,16 +235,33 @@ static void gpio_init(void)
*============================================================================*/
static void load_default_config(void)
{
memset(SERIAL_NO, 0, SERIAL_NO_LENGTH);
memcpy(SERIAL_NO, SERIAL_NUMBER, strlen(SERIAL_NUMBER));
size_t serial_len = strlen(SERIAL_NUMBER);
size_t hw_len = strlen(HARDWARE_VERSION);
size_t passkey_len = strlen(DEFAULT_PASSKEY);
memset(m_static_passkey, 0, PASSKEY_LENGTH);
memcpy(m_static_passkey, DEFAULT_PASSKEY, PASSKEY_LENGTH);
if (serial_len > SERIAL_NO_LENGTH) {
serial_len = SERIAL_NO_LENGTH;
}
if (hw_len > HW_NO_LENGTH) {
hw_len = HW_NO_LENGTH;
}
if (passkey_len > PASSKEY_LENGTH) {
passkey_len = PASSKEY_LENGTH;
}
memset(SERIAL_NO, 0, sizeof(SERIAL_NO));
memcpy(SERIAL_NO, SERIAL_NUMBER, serial_len);
memset(HW_NO, 0, sizeof(HW_NO));
memcpy(HW_NO, HARDWARE_VERSION, hw_len);
memset(m_static_passkey, 0, sizeof(m_static_passkey));
memcpy(m_static_passkey, DEFAULT_PASSKEY, passkey_len);
m_reset_status = 1;
bond_data_delete = true;
DBG_PRINTF("[CFG] Default (S/N=%s)\r\n", SERIAL_NO);
DBG_CORE("[CFG] Default (S/N=%s)\r\n", SERIAL_NO);
}
/*==============================================================================
@@ -184,6 +293,18 @@ void device_power_off(void)
k_timer_start(&m_power_off_delay_timer, K_MSEC(POWER_OFF_DELAY), K_NO_WAIT);
}
void power_button_suspend(bool suspend)
{
/*
* maa?/mbb?처럼 측정 시간이 길 때 전원 버튼 상태머신이 끼어들지 않게 잠깐 멈춘다.
*
* 이 함수는 "부팅을 막는 함수"가 아니다.
* 이미 켜진 뒤, 특정 커맨드 처리 중에만 버튼 폴링 판단을 잠깐 쉬게 하는 역할이다.
*/
power_btn_suspended = suspend;
cnt_s = 0;
}
/*==============================================================================
* 전원 버튼 상태머신 (5ms 폴링)
*
@@ -209,7 +330,7 @@ static void main_s(struct k_timer *timer)
{
if (!button_pressed) /* 버튼 놓음 → 2초 미만이면 전원 OFF */
{
DBG_PRINTF("[BTN] Short press (%d) -> OFF\r\n", cnt_s);
//DBG_PRINTF("[BTN] Short press (%d) -> OFF\r\n", cnt_s);
power_control_handler(OFF);
cnt_s = 0;
}
@@ -281,6 +402,12 @@ static void timers_init(void)
k_timer_init(&m_power_off_delay_timer, t_power_off_timeout_handler, NULL);
k_work_init(&adv_start_work, adv_start_work_handler);
k_work_init(&adv_stop_work, adv_stop_work_handler);
k_work_init(&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();
}
@@ -299,11 +426,28 @@ int main(void)
power_hold_init();
cnt_s = 0;
DBG_PRINTF("\r\n========================================\r\n");
DBG_PRINTF(" VesiScan BASIC %s (Zephyr)\r\n", FIRMWARE_VERSION);
DBG_PRINTF("========================================\r\n");
/*
* 현재는 전원 버튼/mbb 문제를 같이 디버깅 중이라
* "버튼을 2초 이상 눌러야 래치 ON" 규칙이 오히려 부팅 자체를 막고 있다.
*
* 그래서 디버깅 동안만, MCU가 main()까지 올라오면 바로 P0.08 래치를 ON으로 잡는다.
*
* 의미:
* - 사용자는 전원 버튼을 "부팅이 시작될 만큼만" 누르면 된다.
* - 예전처럼 2초를 정확히 맞춰 길게 누를 필요가 없다.
* - 일단 켜진 뒤의 BLE/측정/로그 문제를 보기 쉬워진다.
*
* 나중에 전원 시퀀스 디버깅이 끝나면 이 블록은 다시 제거하면 된다.
*/
device_on = false;
boot_btn_released = false;
DBG_PRINTF("[1] HW Init\r\n");
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("========================================\r\n");
DBG_CORE("[1] HW Init\r\n");
gpio_init();
timers_init();
load_default_config();
@@ -312,25 +456,25 @@ int main(void)
battery_timer_init();
imu_init();
temp_init();
DBG_PRINTF(" gpio/timer/config/led/batt/imu/temp OK\r\n");
DBG_CORE(" gpio/timer/config/led/batt/imu/temp OK\r\n");
/*── Phase 2: BLE 스택 + NUS ──*/
DBG_PRINTF("[2] BLE Init\r\n");
DBG_CORE("[2] BLE Init\r\n");
if (ble_service_init(ble_rx_handler) == 0)
{
DBG_PRINTF(" ble/nus OK\r\n");
DBG_CORE(" ble/nus OK\r\n");
}
else
{
DBG_PRINTF(" ble FAIL\r\n");
DBG_ERR(" ble FAIL\r\n");
}
/*── Phase 3: FDS/NVS (TODO) ──*/
/*── Phase 4: 애플리케이션 (TODO) ──*/
DBG_PRINTF("\r\n========================================\r\n");
DBG_PRINTF(" READY [%s]\r\n", SERIAL_NO);
DBG_PRINTF("========================================\r\n\r\n");
DBG_CORE("\r\n========================================\r\n");
DBG_CORE(" READY [%s]\r\n", SERIAL_NO);
DBG_CORE("========================================\r\n\r\n");
/* 전원 버튼 상태머신 시작 (부팅 시 버튼이 눌려있는 상태) */
timers_start();