기타등등

This commit is contained in:
2026-06-11 17:48:35 +09:00
parent d7c5b34da3
commit 1f7436e27d
14 changed files with 714 additions and 124 deletions
+101 -30
View File
@@ -1,6 +1,6 @@
# VesiScan BASIC - Zephyr (nRF Connect SDK) # VesiScan BASIC - Zephyr (nRF Connect SDK)
nRF52840 기반 VesiScan BASIC 펌웨어의 Zephyr 포팅 프로젝트입니다. nRF52840 기반 VesiScan BASIC 펌웨어의 Zephyr 포팅 프로젝트
## 현재 구현 상태 ## 현재 구현 상태
@@ -9,13 +9,16 @@ nRF52840 기반 VesiScan BASIC 펌웨어의 Zephyr 포팅 프로젝트입니다.
| 1 | GPIO + 전원 래치 + 버튼 상태머신 | 완료 | | 1 | GPIO + 전원 래치 + 버튼 상태머신 | 완료 |
| 1 | LED 제어 (green/orange 패턴) | 완료 | | 1 | LED 제어 (green/orange 패턴) | 완료 |
| 2 | BLE 스택 + NUS (advertising, 연결, 재연결) | 완료 | | 2 | BLE 스택 + NUS (advertising, 연결, 재연결) | 완료 |
| 3 | 배터리 ADC (AIN2, 12bit, 저전압 자동 OFF) | 진행중 | | 3 | 배터리 ADC (AIN2, 12bit) | 완료 |
| 3 | 커맨드 파서 (msn? 구현) | 진행중 | | 3 | 온도 센서 TMP235 (AIN3) | 완료 |
| - | Flash 저장소 (NVS) | 미구현 | | 3 | IMU ICM42670P (I2C0) | 완료 |
| - | 온도 센서 (TMP235, AIN3) | 미구현 | | 3 | 피에조 TX + mux 제어 | 완료 |
| - | IMU (ICM42670P, I2C) | 미구현 | | 3 | ADC121S051 echo capture (nrfx SPIM3) | 완료 |
| - | 피에조 + ADC121S051 | 미구현 | | 3 | BLE 커맨드 파서 (TAG+DATA+CRC16) | 진행중 |
| 4 | MCUboot + MCUmgr BLE DFU 설정 | 진행중 |
| 4 | Flash 저장소 (NVS/settings, piezo config) | 완료 |
| - | BLE 보안 (본딩, 패스키) | 미구현 | | - | BLE 보안 (본딩, 패스키) | 미구현 |
| - | HW/SN/Passkey 저장/복원 | 미구현 |
## 프로젝트 구조 ## 프로젝트 구조
@@ -36,14 +39,22 @@ blinky/
└── drivers/ └── drivers/
├── battery/ ├── battery/
│ └── battery_adc.c / .h # 배터리 전압 ADC (AIN2, 12bit) │ └── battery_adc.c / .h # 배터리 전압 ADC (AIN2, 12bit)
── led/ ── echo_adc/
└── led_control.c / .h # LED 패턴 제어 (k_timer 기반) └── echo_adc.c / .h # ADC121S051 echo capture (SPIM3)
├── imu/
│ └── imu_i2c.c / .h # ICM42670P IMU (I2C0)
├── led/
│ └── led_control.c / .h # LED 패턴 제어 (k_timer 기반)
├── piezo/
│ └── piezo.c / .h # 피에조 전원, mux, SW burst
└── temperature/
└── tmp235.c / .h # TMP235 온도 센서 (AIN3)
``` ```
## 빌드 방법 ## 빌드 방법
### 일반 빌드 ### 일반 빌드
소스 파일(.c/.h)만 변경한 경우. 소스 파일(.c/.h)만 변경한 경우
### Pristine Build ### Pristine Build
아래 파일을 변경한 경우 반드시 Pristine Build: 아래 파일을 변경한 경우 반드시 Pristine Build:
@@ -51,7 +62,7 @@ blinky/
- `boards/*.overlay` (Devicetree 핀 매핑) - `boards/*.overlay` (Devicetree 핀 매핑)
- `CMakeLists.txt` (소스/include 경로 추가) - `CMakeLists.txt` (소스/include 경로 추가)
nRF Connect for VS Code → Build 패널 → **Pristine Build** 또는 `build` 폴더 삭제 후 빌드. nRF Connect for VS Code → Build 패널 → **Pristine Build** 또는 `build` 폴더 삭제 후 빌드
## 핀 매핑 (Devicetree Overlay) ## 핀 매핑 (Devicetree Overlay)
@@ -62,6 +73,21 @@ nRF Connect for VS Code → Build 패널 → **Pristine Build** 또는 `build`
| `LED_BLE` | P0.12 | Output, Active LOW | 초록 LED | | `LED_BLE` | P0.12 | Output, Active LOW | 초록 LED |
| `FUNCTION_LED` | P0.29 | Output, Active LOW | 주황 LED | | `FUNCTION_LED` | P0.29 | Output, Active LOW | 주황 LED |
| ADC AIN2 | P0.04 | Analog Input | 배터리 전압 분압 | | ADC AIN2 | P0.04 | Analog Input | 배터리 전압 분압 |
| ADC AIN3 | P0.05 | Analog Input | TMP235 온도 센서 |
| I2C0 SCL | P1.14 | I2C | ICM42670P SCL |
| I2C0 SDA | P1.15 | I2C | ICM42670P SDA |
| `PIEZO_PWR` | P1.09 | Output, Active HIGH | 피에조/TMP235 전원 enable |
| `PIEZO_PE` | P0.25 | Output, Active HIGH | 피에조 pulse enable |
| `PIEZO_P_OUT` | P1.07 | Output | 피에조 P 출력 |
| `PIEZO_N_OUT` | P1.06 | Output | 피에조 N 출력 |
| `PIEZO_DMP` | P1.00 | Output | 피에조 dump |
| `MUX_EN_A` | P0.21 | Output | 피에조 mux enable A |
| `MUX_EN_B` | P0.23 | Output | 피에조 mux enable B |
| `MUX_SEL0` | P1.10 | Output | 피에조 mux select 0 |
| `MUX_SEL1` | P0.28 | Output | 피에조 mux select 1 |
| `ECHO_SCLK` | P0.14 | SPIM/GPIO | ADC121S051 SCLK |
| `ECHO_MISO` | P0.15 | SPIM/GPIO | ADC121S051 MISO |
| `ECHO_CS` | P0.19 | Output, Active LOW | ADC121S051 CS |
> UART0은 P0.08 충돌 방지를 위해 비활성화 (`status = "disabled"`) > UART0은 P0.08 충돌 방지를 위해 비활성화 (`status = "disabled"`)
@@ -89,30 +115,62 @@ nRF Connect for VS Code → Build 패널 → **Pristine Build** 또는 `build`
| 커맨드 | 응답 | 기능 | | 커맨드 | 응답 | 기능 |
|--------|------|------| |--------|------|------|
| `msn?` | `rsn:` + uint16 mV | 배터리 전압 측정 | | `msn?` | `rsn:` + uint16 mV | 배터리 전압 측정 |
| `mls?` | `rls:` + state | LED 상태 변경 |
| `msp?` | `rsp:` + accel/gyro | IMU 단발 측정 |
| `mst?` | `rso:` + int16 cdeg | TMP235 온도 측정 |
| `mpa?` | `rpa:` + status | 피에조 전원 ON |
| `mpb?` | `rpb:` + status | 피에조 전원 OFF |
| `mpc?` | `rpc:` + cycles/status | 피에조 burst 테스트 |
| `mec?` | `reb:` + `raa:` | 단일 채널 echo capture |
| `maa?` | `reb:` x6 + `raa:` | 6채널 echo sweep |
| `mbb?` | `rbb:` + `reb:` x6 + `raa:` | 배터리+IMU+온도+6채널 echo |
| `mcf?` | `rcf:` | 측정 파라미터 읽기(현재 설정값 반환) |
| `mcs?` | `rcs:` + status | 측정 파라미터 쓰기 + NVS 저장 |
| `mfv?` | `rfv:` | FW version 읽기 |
| `mrh?` | `rrh:` | HW version 읽기(RAM/default) |
| `mwh?` | `rwh:` | HW version 쓰기(RAM only) |
| `mrs?` | `rrs:` | Serial number 읽기(RAM/default) |
| `mws?` | `rws:` | Serial number 쓰기(RAM only) |
| `mqz?` | `rqz:` | Passkey 읽기(RAM/default) |
| `mpz?` | `rpz:` | Passkey 쓰기(RAM only) |
### 전체 커맨드 목록 (구현 예정) ### 전체 커맨드 목록
| # | 내용 | 명령 | 응답 | 비고 | | # | 내용 | 명령 | 응답 | 비고 |
|---|------|------|------|------| |---|------|------|------|------|
| 1 | 전원 OFF | `msq?` | `rsq:` | | | 1 | 전원 OFF | `msq?` | `rsq:` | 미구현 |
| 2 | 재부팅 | `mss?` | `rss:` | | | 2 | 재부팅 | `mss?` | `rss:` | 미구현 |
| 3 | 본딩 삭제 + 재부팅 | `msr?` | `rsr:` | | | 3 | 본딩 삭제 + 재부팅 | `msr?` | `rsr:` | 미구현 |
| 4 | LED 상태 | `mls?` | - | | | 4 | LED 상태 | `mls?` | `rls:` | 구현 완료 |
| 5 | HW Version 읽기 | `mrh?` | `rrh:` | FDS | | 5 | HW Version 읽기 | `mrh?` | `rrh:` | 구현 완료, RAM/default |
| 6 | HW Version 쓰기 | `mwh?` | `rwh:` | FDS | | 6 | HW Version 쓰기 | `mwh?` | `rwh:` | 구현 완료, RAM only |
| 7 | Serial Number 읽기 | `mrs?` | `rrs:` | FDS | | 7 | Serial Number 읽기 | `mrs?` | `rrs:` | 구현 완료, RAM/default |
| 8 | Serial Number 쓰기 | `mws?` | `rws:` | FDS | | 8 | Serial Number 쓰기 | `mws?` | `rws:` | 구현 완료, RAM only |
| 9 | FW Version 읽기 | `mfv?` | `rfv:` | | | 9 | FW Version 읽기 | `mfv?` | `rfv:` | 구현 완료 |
| 10 | Passkey 읽기 | `mqz?` | `rqz:` | FDS | | 10 | Passkey 읽기 | `mqz?` | `rqz:` | 구현 완료, RAM/default |
| 11 | Passkey 쓰기 | `mpz?` | `rpz:` | FDS | | 11 | Passkey 쓰기 | `mpz?` | `rpz:` | 구현 완료, RAM only |
| 12 | 배터리 측정 | `msn?` | `rsn:` | 구현 완료 | | 12 | 배터리 측정 | `msn?` | `rsn:` | 구현 완료 |
| 13 | IMU 단발 측정 | `msp?` | `rsp:` | | | 13 | IMU 단발 측정 | `msp?` | `rsp:` | 구현 완료 |
| 14 | 온도 측정 | `mso?` | `rso:` | | | 14 | 온도 측정 | `mst?` | `rso:` | 구현 완료 |
| 15 | 단일 채널 측정 | `mec?` | `reb:``raa:` | 테스트용 | | 15 | 피에조 전원 ON | `mpa?` | `rpa:` | 구현 완료 |
| 16 | 모든 채널(6) 측정 | `maa?` | `reb:`(6개) → `raa:` | | | 16 | 피에조 전원 OFF | `mpb?` | `rpb:` | 구현 완료 |
| 17 | 전체 측정 | `mbb?` | `rbb:` `reb:`(6개) → `raa:` | 배터리+IMU+온도+피에조 | | 17 | 피에조 burst 테스트 | `mpc?` | `rpc:` | 구현 완료, `freq_option`은 아직 미사용 |
| 18 | 측정 파라미터 읽기 | `mcf?` | `rcf:` | FDS | | 18 | 단일 채널 측정 | `mec?` | `reb:``raa:` | 구현 완료, 테스트용 |
| 19 | 측정 파라미터 쓰기 | `mcs?` | `rcs:` | FDS | | 19 | 모든 채널(6) 측정 | `maa?` | `reb:`(6개) → `raa:` | 구현 완료 |
| 20 | 전체 측정 | `mbb?` | `rbb:``reb:`(6개) → `raa:` | 구현 완료 |
| 21 | 측정 파라미터 읽기 | `mcf?` | `rcf:` | 구현 완료, 현재 설정값 반환 |
| 22 | 측정 파라미터 쓰기 | `mcs?` | `rcs:` | 구현 완료, NVS 저장 |
## 남은 포팅 작업
| 항목 | 현재 상태 | 다음 작업 |
|------|-----------|-----------|
| NVS/settings | piezo config 저장/복원 구현 완료 | HW/SN/Passkey 저장으로 확장 |
| 측정 파라미터 | `piezo_config` 구조체 사용 | 실제 하드웨어에서 값별 파형 검증 |
| `mcs?` | 구현 완료 | 앱/테스트툴에서 쓰기 후 재부팅 유지 확인 |
| HW/SN/Passkey 저장 | RAM only | `mwh?`, `mws?`, `mpz?` 값을 NVS에 저장 |
| BLE 보안 | `CONFIG_BT_SMP=n`, dev mode | bonding, fixed passkey, MITM/LESC 적용 |
| BLE DFU | MCUboot/mcumgr 설정 있음 | SMP 광고/업로드/리셋 후 confirm 검증 |
## 디버그 로그 (RTT) ## 디버그 로그 (RTT)
@@ -130,6 +188,11 @@ nRF Connect for VS Code → Build 패널 → **Pristine Build** 또는 `build`
| `[NUS TX]` | NUS 송신 | | `[NUS TX]` | NUS 송신 |
| `[CMD]` | 커맨드 파서 처리 결과 | | `[CMD]` | 커맨드 파서 처리 결과 |
| `[BATT]` | 배터리 ADC 측정값 | | `[BATT]` | 배터리 ADC 측정값 |
| `[IMU]` | IMU 초기화/측정 |
| `[ECHO]` | ADC121S051 echo capture |
| `[PIEZO]` | 피에조 mux/burst/power |
| `[MBB]` | 전체 측정 시퀀스 |
| `[DFU]` | MCUboot 이미지 상태 |
| `[SYS]` | 슬립 진입 | | `[SYS]` | 슬립 진입 |
## prj.conf 주요 설정 ## prj.conf 주요 설정
@@ -145,3 +208,11 @@ nRF Connect for VS Code → Build 패널 → **Pristine Build** 또는 `build`
| `CONFIG_BT_L2CAP_TX_MTU` | 247 | MTU 크기 | | `CONFIG_BT_L2CAP_TX_MTU` | 247 | MTU 크기 |
| `CONFIG_BT_CTLR_TX_PWR_PLUS_8` | y | TX power +8dBm | | `CONFIG_BT_CTLR_TX_PWR_PLUS_8` | y | TX power +8dBm |
| `CONFIG_ADC` | y | ADC 드라이버 (배터리, 온도) | | `CONFIG_ADC` | y | ADC 드라이버 (배터리, 온도) |
| `CONFIG_I2C` | y | IMU I2C 드라이버 |
| `CONFIG_NRFX_SPIM` | y | ADC121S051용 nrfx SPIM |
| `CONFIG_BOOTLOADER_MCUBOOT` | y | MCUboot 사용 |
| `CONFIG_MCUMGR_TRANSPORT_BT` | y | BLE mcumgr transport |
| `CONFIG_NVS` | y | NVS 플래시 저장소 |
| `CONFIG_SETTINGS` | y | Zephyr settings subsystem |
| `CONFIG_SETTINGS_NVS` | y | settings NVS backend |
| `CONFIG_BT_SMP` | n | BLE 보안 비활성화(개발 모드) |
+66
View File
@@ -126,5 +126,71 @@
gpios = <&gpio1 9 GPIO_ACTIVE_HIGH>; gpios = <&gpio1 9 GPIO_ACTIVE_HIGH>;
label = "Piezo Power Enable"; label = "Piezo Power Enable";
}; };
PIEZO_PE: PIEZO_PE
{
gpios = <&gpio0 25 GPIO_ACTIVE_HIGH>;
label = "Piezo Pulse Enable";
};
PIEZO_P_OUT: PIEZO_P_OUT
{
gpios = <&gpio1 7 GPIO_ACTIVE_HIGH>;
label = "Piezo P Out";
};
PIEZO_N_OUT: PIEZO_N_OUT
{
gpios = <&gpio1 6 GPIO_ACTIVE_HIGH>;
label = "Piezo N Out";
};
PIEZO_DMP: PIEZO_DMP
{
gpios = <&gpio1 0 GPIO_ACTIVE_HIGH>;
label = "Piezo Dump";
};
MUX_EN_A: MUX_EN_A
{
gpios = <&gpio0 21 GPIO_ACTIVE_HIGH>;
label = "Mux Enable A";
};
MUX_EN_B: MUX_EN_B
{
gpios = <&gpio0 23 GPIO_ACTIVE_HIGH>;
label = "Mux Enable B";
};
MUX_SEL0: MUX_SEL0
{
gpios = <&gpio1 10 GPIO_ACTIVE_HIGH>;
label = "Mux Select 0";
};
MUX_SEL1: MUX_SEL1
{
gpios = <&gpio0 28 GPIO_ACTIVE_HIGH>;
label = "Mux Select 1";
};
ECHO_SCLK: ECHO_SCLK
{
gpios = <&gpio0 14 0>;
label = "Echo ADC SCLK";
};
ECHO_MISO: ECHO_MISO
{
gpios = <&gpio0 15 0>;
label = "Echo ADC MISO";
};
ECHO_CS: ECHO_CS
{
gpios = <&gpio0 19 GPIO_ACTIVE_LOW>;
label = "Echo ADC CS";
};
}; };
}; };
+1 -1
View File
@@ -58,7 +58,7 @@ echo_adc_init()
│ ├─ PE OFF │ │ ├─ PE OFF │
│ └─ __enable_irq() │ │ └─ __enable_irq() │
│ │ │ │
│ delay_us 대기 (기본 20us) ← 버스트 → ADC 시작 딜레이 │ │ delay_us 대기 (기본 10us) ← 버스트 → ADC 시작 딜레이 │
│ │ │ │
│ echo_adc_capture(buf, num_samples) ← SPIM3 연속 읽기 │ │ echo_adc_capture(buf, num_samples) ← SPIM3 연속 읽기 │
│ └─ 100샘플 × 2byte = 200byte │ │ └─ 100샘플 × 2byte = 200byte │
+35
View File
@@ -0,0 +1,35 @@
app:
address: 0xc200
region: flash_primary
size: 0x78e00
mcuboot:
address: 0x0
region: flash_primary
size: 0xc000
mcuboot_pad:
address: 0xc000
region: flash_primary
size: 0x200
mcuboot_primary:
address: 0xc000
orig_span: &id001
- mcuboot_pad
- app
region: flash_primary
size: 0x79000
span: *id001
mcuboot_primary_app:
address: 0xc200
orig_span: &id002
- app
region: flash_primary
size: 0x78e00
span: *id002
mcuboot_secondary:
address: 0x85000
region: flash_primary
size: 0x79000
settings_storage:
address: 0xfe000
region: flash_primary
size: 0x2000
+39 -1
View File
@@ -56,8 +56,46 @@ CONFIG_I2C=y
CONFIG_NRFX_SPIM=y CONFIG_NRFX_SPIM=y
# System # System
CONFIG_HEAP_MEM_POOL_SIZE=2048 # 런타임 중 동적으로 확보해서 사용하는 메모리 크기
CONFIG_HEAP_MEM_POOL_SIZE=4096
# 백그라운드 작업 처리 스레드 작업 공간(workqueue) 크기
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048 CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048
# BLE RX stack sizes (prevent stack overflow on BT RX WQ) # BLE RX stack sizes (prevent stack overflow on BT RX WQ)
CONFIG_BT_RX_STACK_SIZE=4096 CONFIG_BT_RX_STACK_SIZE=4096
# DFU
# 애플리케이션이 MCUboot 환경(부트로더)에서 동작(MCUboot: FW 교체 판단, 서명 검증)
CONFIG_BOOTLOADER_MCUBOOT=y
CONFIG_ZCBOR=y
CONFIG_FLASH=y
CONFIG_FLASH_MAP=y
# Persistent application settings (FDS replacement)
CONFIG_NVS=y
CONFIG_SETTINGS=y
CONFIG_SETTINGS_NVS=y
CONFIG_SETTINGS_NVS_SECTOR_COUNT=2
CONFIG_PM_PARTITION_SIZE_SETTINGS_STORAGE=0x2000
# mcumgr 프레임워크 활성화
CONFIG_MCUMGR=y
# BLE를 통해 mcumgr 메시지 송수신
CONFIG_MCUMGR_TRANSPORT_BT=y
# BLE로 수신된 mcumgr 메시지에 대해 읽기/쓰기 권한 허용
CONFIG_MCUMGR=y
CONFIG_MCUMGR_TRANSPORT_BT=y
CONFIG_MCUMGR_TRANSPORT_BT_PERM_RW=y
# mcumgr 이미지 관리 그룹 활성화 (이미지 업로드, 테스트, 확인, 슬롯 상태 조회)
CONFIG_MCUMGR_GRP_IMG=y
# mcumgr OS 관리 그룹 활성화 (시스템 정보 조회, 재부팅 등 OS 관련 명령 지원)
CONFIG_MCUMGR_GRP_OS=y
# MCUboot 이미지 관리 활성화 (이미지 검증, 교체, 롤백 등)
CONFIG_IMG_MANAGER=y
# 플래시 메모리를 스트림으로 접근하여 이미지 업로드/교체 처리
CONFIG_STREAM_FLASH=y
# Netcore HCI IPC (BLE 통신을 위한 인터페이스) : 2 cores 간 IPC 활성화
#CONFIG_SB_CONFIG_NETCORE_HCI_IPC=y
#SB_CONFIG_NETCORE_HCI_IPC=y
+8 -1
View File
@@ -15,6 +15,7 @@
#include <zephyr/bluetooth/conn.h> #include <zephyr/bluetooth/conn.h>
#include <zephyr/bluetooth/uuid.h> #include <zephyr/bluetooth/uuid.h>
#include <zephyr/bluetooth/gatt.h> #include <zephyr/bluetooth/gatt.h>
#include <zephyr/mgmt/mcumgr/transport/smp_bt.h>
#include <bluetooth/services/nus.h> #include <bluetooth/services/nus.h>
#include "ble_service.h" #include "ble_service.h"
@@ -135,7 +136,13 @@ static const struct bt_data ad[] =
static const struct bt_data sd[] = 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
View File
@@ -87,7 +87,8 @@ static inline void nop_delay_14(void)
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++)
{
burst_nop(); burst_nop();
} }
} }
@@ -107,23 +108,28 @@ 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,
&piezo_dmp, &mux_en_a, &mux_en_b, &mux_sel0, &mux_sel1 &piezo_dmp, &mux_en_a, &mux_en_b, &mux_sel0, &mux_sel1
}; };
for (size_t i = 0; i < ARRAY_SIZE(pins); i++) { for (size_t i = 0; i < ARRAY_SIZE(pins); i++)
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;
} }
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)
{
DBG_PRINTF("[PIEZO] GPIO init fail idx=%d err=%d\r\n", (int)i, err); DBG_PRINTF("[PIEZO] GPIO init fail idx=%d err=%d\r\n", (int)i, err);
return err; return err;
} }
@@ -146,7 +152,8 @@ 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;
} }
@@ -155,7 +162,8 @@ void piezo_power_on(void)
void piezo_power_off(void) void piezo_power_off(void)
{ {
if (!piezo_initialized && piezo_init() != 0) { if (!piezo_initialized && piezo_init() != 0)
{
return; return;
} }
@@ -165,14 +173,17 @@ void piezo_power_off(void)
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();
if (err) { if (err)
{
return err; return err;
} }
} }
if (channel >= PIEZO_NUM_CHANNELS) { if (channel >= PIEZO_NUM_CHANNELS)
{
return -EINVAL; return -EINVAL;
} }
@@ -187,7 +198,8 @@ int piezo_select_channel(uint8_t channel)
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;
} }
@@ -203,7 +215,8 @@ void piezo_burst_sw(uint8_t cycles)
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++)
{
NRF_P1->OUT = p1_p_high_n_low; NRF_P1->OUT = p1_p_high_n_low;
nop_delay_14(); nop_delay_14();
+148 -30
View File
@@ -15,6 +15,8 @@
#include <zephyr/kernel.h> #include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h> #include <zephyr/drivers/gpio.h>
#include <zephyr/sys/printk.h> #include <zephyr/sys/printk.h>
#include <zephyr/dfu/mcuboot.h>
#include <hal/nrf_power.h>
#include "main.h" #include "main.h"
#include "debug_print.h" #include "debug_print.h"
@@ -79,6 +81,8 @@ 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 uint32_t boot_reset_reason;
static struct k_work adv_start_work; static struct k_work adv_start_work;
static struct k_work adv_stop_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; uint16_t local_len = 0U;
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)
{
local_len = ble_cmd_len; local_len = ble_cmd_len;
memcpy(local_buf, ble_cmd_buf, local_len); memcpy(local_buf, ble_cmd_buf, local_len);
} }
k_spin_unlock(&ble_cmd_lock, key); k_spin_unlock(&ble_cmd_lock, key);
if (local_len == 0U) { if (local_len == 0U)
{
DBG_ERR("[BLE RX] worker: empty\r\n"); DBG_ERR("[BLE RX] worker: empty\r\n");
return; return;
} }
@@ -204,17 +210,43 @@ static void ble_rx_handler(const uint8_t *data, uint16_t len)
/* 전원 유지 핀(P0.08) 초기화 - 아직 래치하지 않음 /* 전원 유지 핀(P0.08) 초기화 - 아직 래치하지 않음
* 버튼을 물리적으로 누르고 있는 동안만 전원 유지됨 * 버튼을 물리적으로 누르고 있는 동안만 전원 유지됨
* 2초 후 main_s()에서 래치(HIGH) 설정 */ * 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) 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 핀으로 물리적 전원 래치/해제 */ /* 전원 ON/OFF 제어 - P0.08 핀으로 물리적 전원 래치/해제 */
static void power_control_handler(on_off_cont_t device_power_st) 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 → 전원 래치 해제 → 전원 차단 */ gpio_pin_set_dt(&power_hold, 0); /* P0.08 LOW → 전원 래치 해제 → 전원 차단 */
} else { }
else
{
gpio_pin_set_dt(&power_hold, 1); /* P0.08 HIGH → 전원 유지 */ gpio_pin_set_dt(&power_hold, 1); /* P0.08 HIGH → 전원 유지 */
DBG_PRINTF("[PWR] ON\r\n"); 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 hw_len = strlen(HARDWARE_VERSION);
size_t passkey_len = strlen(DEFAULT_PASSKEY); size_t passkey_len = strlen(DEFAULT_PASSKEY);
if (serial_len > SERIAL_NO_LENGTH) { if (serial_len > SERIAL_NO_LENGTH)
{
serial_len = SERIAL_NO_LENGTH; serial_len = SERIAL_NO_LENGTH;
} }
if (hw_len > HW_NO_LENGTH) { if (hw_len > HW_NO_LENGTH)
{
hw_len = HW_NO_LENGTH; hw_len = HW_NO_LENGTH;
} }
if (passkey_len > PASSKEY_LENGTH) { if (passkey_len > PASSKEY_LENGTH)
{
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_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);
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, k_work_queue_start(&ble_cmd_work_q, ble_cmd_workq_stack, K_THREAD_STACK_SIZEOF(ble_cmd_workq_stack), BLE_CMD_WORKQ_PRIORITY, NULL);
ble_cmd_workq_stack,
K_THREAD_STACK_SIZEOF(ble_cmd_workq_stack),
BLE_CMD_WORKQ_PRIORITY,
NULL);
power_timer_init(); 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); 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) int main(void)
{ {
boot_context_detect();
/*── Phase 1: 하드웨어 기본 초기화 ──*/ /*── Phase 1: 하드웨어 기본 초기화 ──*/
power_hold_init(); power_hold_init();
cnt_s = 0; cnt_s = 0;
/*
* 현재는 전원 버튼/mbb 문제를 같이 디버깅 중이라
* "버튼을 2초 이상 눌러야 래치 ON" 규칙이 오히려 부팅 자체를 막고 있다.
*
* 그래서 디버깅 동안만, MCU가 main()까지 올라오면 바로 P0.08 래치를 ON으로 잡는다.
*
* 의미:
* - 사용자는 전원 버튼을 "부팅이 시작될 만큼만" 누르면 된다.
* - 예전처럼 2초를 정확히 맞춰 길게 누를 필요가 없다.
* - 일단 켜진 뒤의 BLE/측정/로그 문제를 보기 쉬워진다.
*
* 나중에 전원 시퀀스 디버깅이 끝나면 이 블록은 다시 제거하면 된다.
*/
device_on = false; device_on = false;
boot_btn_released = false; boot_btn_released = false;
confirm_running_image();
DBG_CORE("\r\n========================================\r\n"); DBG_CORE("\r\n========================================\r\n");
DBG_CORE(" TEST BUILD %s (Zephyr)\r\n", FIRMWARE_VERSION); DBG_CORE(" TEST BUILD %s (Zephyr)\r\n", FIRMWARE_VERSION);
DBG_CORE(" BUILD TAG: TEST-ADV-UNIT-042\r\n"); DBG_CORE(" BUILD TAG: TEST-ADV-UNIT-000\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();
load_default_config(); load_default_config();
led_init(); led_init();
battery_adc_init(); battery_adc_init();
battery_timer_init(); battery_timer_init();
imu_init(); imu_init();
temp_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 ──*/ /*── Phase 2: BLE 스택 + NUS ──*/
DBG_CORE("[2] BLE Init\r\n"); DBG_CORE("[2] BLE Init\r\n");
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();
} }
else else
{ {
DBG_ERR(" ble FAIL\r\n"); DBG_ERR(" ble FAIL\r\n");
} }
/*── Phase 3: FDS/NVS (TODO) ──*/ /*── Phase 3: FDS/NVS ──*/
/*── Phase 4: 애플리케이션 (TODO) ──*/ /*── Phase 4: 애플리케이션 (TODO) ──*/
DBG_CORE("\r\n========================================\r\n"); DBG_CORE("\r\n========================================\r\n");
@@ -480,7 +597,8 @@ int main(void)
timers_start(); timers_start();
/* 메인 루프 - idle */ /* 메인 루프 - idle */
for (;;) { for (;;)
{
k_msleep(100); k_msleep(100);
} }
+242 -48
View File
@@ -8,6 +8,11 @@
* 현재 구현된 커맨드: msn? (배터리 측정), mls? (LED 상태 설정) * 현재 구현된 커맨드: msn? (배터리 측정), mls? (LED 상태 설정)
******************************************************************************/ ******************************************************************************/
#include <zephyr/kernel.h> #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 <string.h>
#include <limits.h> #include <limits.h>
@@ -40,9 +45,15 @@
#define PIEZO_CFG_DELAY_DEFAULT 10 #define PIEZO_CFG_DELAY_DEFAULT 10
#define PIEZO_CFG_SAMPLES_DEFAULT 100 #define PIEZO_CFG_SAMPLES_DEFAULT 100
#define PIEZO_CFG_AVG_DEFAULT 10 #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_POST_SELECT_SETTLE_US 500
#define PIEZO_AVG_INTER_BURST_GAP_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 piezo_channels[PIEZO_NUM_CHANNELS][ECHO_ADC_MAX_SAMPLES];
static uint16_t echo_capture[ECHO_ADC_MAX_SAMPLES]; static uint16_t echo_capture[ECHO_ADC_MAX_SAMPLES];
static uint32_t echo_accum[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_bundle_buf[22];
static uint8_t tx_cfg_buf[16]; static uint8_t tx_cfg_buf[16];
static uint8_t tx_ascii_buf[4 + HW_NO_LENGTH + 2]; 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 호환) * CRC16 (CRC-CCITT, Nordic SDK 호환)
@@ -312,6 +457,13 @@ static int start_piezo_session(void)
static int perform_piezo_sweep(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++) for (uint8_t ch = 0; ch < PIEZO_NUM_CHANNELS; ch++)
{ {
DBG_PRINTF("[MBB] sweep ch=%d start\r\n", 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)); 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) if (avg > 0U)
{ {
k_busy_wait(PIEZO_AVG_INTER_BURST_GAP_US); k_busy_wait(PIEZO_AVG_INTER_BURST_GAP_US);
} }
piezo_burst_sw(PIEZO_SW_BURST_CYCLES); piezo_burst_sw((uint8_t)cfg->cycles);
k_busy_wait(PIEZO_BURST_TO_ADC_DELAY_US); 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) if (err)
{ {
DBG_PRINTF("[ECHO] capture fail ch=%d avg=%d err=%d\r\n", ch, avg, err); DBG_PRINTF("[ECHO] capture fail ch=%d avg=%d err=%d\r\n", ch, avg, err);
return ECHO_STATUS_CAPTURE; 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]; 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); 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; return 1;
} }
/**
* 테스트용
* mpa?: piezo TX/RX 전원 레일만 켬
*/
static int cmd_mpa(const uint8_t *data, uint8_t data_len) static int cmd_mpa(const uint8_t *data, uint8_t data_len)
{ {
ARG_UNUSED(data); ARG_UNUSED(data);
ARG_UNUSED(data_len); ARG_UNUSED(data_len);
/* mpa?: piezo TX/RX 전원 레일만 켠다. 측정은 하지 않는다. */
DBG_CORE("[MPA] enter\r\n"); DBG_CORE("[MPA] enter\r\n");
if (piezo_init() != 0) { if (piezo_init() != 0) {
DBG_ERR("[MPA] piezo_init failed\r\n"); 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; return 1;
} }
/**
* 테스트용
* mpc?: burst만 한 번 발생시키는 테스트 명령
* echo를 읽지 않고 초음파가 나가는지만 볼 때 사용
*/
static int cmd_mpc(const uint8_t *data, uint8_t data_len) static int cmd_mpc(const uint8_t *data, uint8_t data_len)
{ {
uint16_t cycles = 5; const piezo_config_t *cfg = piezo_config_get();
uint16_t freq_option = 1; uint16_t cycles = cfg->cycles;
uint16_t freq_option = cfg->freq;
uint16_t piezo_ch = 0; uint16_t piezo_ch = 0;
get_data_u16_be(data, data_len, 0, &cycles); 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); ARG_UNUSED(freq_option);
/*
* mpc?: burst만 한 번 발생시키는 테스트 명령.
* echo를 읽지 않으므로 "초음파가 나가는지"만 빠르게 볼 때 쓴다.
*/
if ((cycles < 3U) || (cycles > 7U)) if ((cycles < 3U) || (cycles > 7U))
{ {
send_response_u16("rpc:", 2); 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 하나만 구현되어 있다. * 현재 Zephyr 포팅본은 2.1MHz SW burst 하나만 구현되어 있
* 레거시의 freq_option 값은 받아두되, 아직은 같은 burst 함수로 처리한다. * 레거시의 freq_option 값은 받아두되, 아직은 같은 burst 함수로 처리
*/ */
piezo_burst_sw((uint8_t)cycles); piezo_burst_sw((uint8_t)cycles);
piezo_power_off(); 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) static int cmd_mec(const uint8_t *data, uint8_t data_len)
{ {
uint16_t freq_option = 1; const piezo_config_t *cfg = piezo_config_get();
uint16_t delay_us = PIEZO_BURST_TO_ADC_DELAY_US; uint16_t freq_option = cfg->freq;
uint16_t num_samples = ECHO_ADC_MAX_SAMPLES; uint16_t delay_us = cfg->delay_us;
uint16_t cycles = PIEZO_SW_BURST_CYCLES; uint16_t num_samples = cfg->samples;
uint16_t averaging = 1; uint16_t cycles = cfg->cycles;
uint16_t averaging = cfg->avg;
uint16_t piezo_ch = 0; uint16_t piezo_ch = 0;
get_data_u16_be(data, data_len, 0, &freq_option); 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); ARG_UNUSED(freq_option);
/* /*
* mec?: 단일 채널 burst + echo capture. * 테스트용
* maa?/mbb?보다 가벼워서 "한 채널만 먼저 살아 있는지" 확인하기 좋다. * mec?: 단일 채널 burst + echo capture
*/ */
if (num_samples > ECHO_ADC_MAX_SAMPLES) { if (num_samples > ECHO_ADC_MAX_SAMPLES) {
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; return 1;
} }
/**
* 테스트용(이전 정렬모드)
* maa?:
* - piezo 6채널 burst 순서대로 쏘고
* - echo 샘플을 채널별로 모은 뒤
* - reb: 패킷 6개 전송
* - raa: 상태값은 전체 작업 성공/실패 요약
*/
static int cmd_maa(const uint8_t *data, uint8_t data_len) static int cmd_maa(const uint8_t *data, uint8_t data_len)
{ {
ARG_UNUSED(data); ARG_UNUSED(data);
ARG_UNUSED(data_len); ARG_UNUSED(data_len);
/*
* maa?:
* - piezo 6채널을 순서대로 쏘고
* - echo 샘플을 채널별로 모은 뒤
* - reb: 패킷 6개를 보낸다.
*
* 마지막 raa: 상태값은 "전체 작업 성공/실패 요약"이다.
*/
processing = true; processing = true;
power_button_suspend(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++) 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; return 1;
} }
/**
* 전체 측정 시 사용
*/
static int cmd_mbb(const uint8_t *data, uint8_t data_len) static int cmd_mbb(const uint8_t *data, uint8_t data_len)
{ {
ARG_UNUSED(data); ARG_UNUSED(data);
@@ -747,7 +908,7 @@ static int cmd_mbb(const uint8_t *data, uint8_t data_len)
if (status == ECHO_STATUS_OK) if (status == ECHO_STATUS_OK)
{ {
/* info 성격 데이터는 echo sweep이 정상 끝났을 때만 읽는다. */ /* info 성격 데이터는 echo sweep이 정상 끝났을 때만 읽 */
DBG_PRINTF("[MBB] battery read\r\n"); DBG_PRINTF("[MBB] battery read\r\n");
batt_mv = battery_read_mv(); batt_mv = battery_read_mv();
if (batt_mv < 0) 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) if (status == ECHO_STATUS_OK)
{ {
/* rbb: 한 번에 보내는 "요약 정보 묶음" 패킷 */ /* rbb: 센서(배터리+IMU+온도) 정보 패킷 */
DBG_PRINTF("[MBB] response tx start\r\n"); DBG_PRINTF("[MBB] response tx start\r\n");
send_response_bundle((uint16_t)batt_mv, accel, gyro, temp_cdeg); 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 파형 */ /* reb: 채널별 raw echo 파형 */
DBG_PRINTF("[MBB] tx reb ch=%d\r\n", ch); 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; return 1;
} }
/**
* mcf?: piezo 측정 파라미터 읽기
* 응답 rcf: + 설정값 echo back
*/
static int cmd_mcf(const uint8_t *data, uint8_t data_len) static int cmd_mcf(const uint8_t *data, uint8_t data_len)
{ {
ARG_UNUSED(data); ARG_UNUSED(data);
ARG_UNUSED(data_len); ARG_UNUSED(data_len);
send_response_piezo_config(PIEZO_CFG_FREQ_DEFAULT, const piezo_config_t *cfg = piezo_config_get();
PIEZO_CFG_CYCLES_DEFAULT, send_response_piezo_config(cfg->freq, cfg->cycles, cfg->avg, cfg->delay_us, cfg->samples);
PIEZO_CFG_AVG_DEFAULT, piezo_config_log("[CMD] mcf ->", cfg);
PIEZO_CFG_DELAY_DEFAULT, return 1;
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, * mcs?: piezo 측정 파라미터 쓰기
PIEZO_CFG_AVG_DEFAULT, */
PIEZO_CFG_DELAY_DEFAULT, static int cmd_mcs(const uint8_t *data, uint8_t data_len)
PIEZO_CFG_SAMPLES_DEFAULT); {
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; return 1;
} }
@@ -977,6 +1168,7 @@ static const cmd_entry_t cmd_table[] =
{ "maa?", cmd_maa }, { "maa?", cmd_maa },
{ "mbb?", cmd_mbb }, { "mbb?", cmd_mbb },
{ "mcf?", cmd_mcf }, { "mcf?", cmd_mcf },
{ "mcs?", cmd_mcs },
{ "mfv?", cmd_mfv }, { "mfv?", cmd_mfv },
{ "mwh?", cmd_mwh }, { "mwh?", cmd_mwh },
{ "mrh?", cmd_mrh }, { "mrh?", cmd_mrh },
@@ -1026,7 +1218,8 @@ int dr_parser(const uint8_t *buf, uint16_t len)
} }
/* TAG 추출 (4바이트) */ /* TAG 추출 (4바이트) */
char tag[5] = { char tag[5] =
{
ascii_to_lower((char)buf[0]), ascii_to_lower((char)buf[0]),
ascii_to_lower((char)buf[1]), ascii_to_lower((char)buf[1]),
ascii_to_lower((char)buf[2]), 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); 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"); DBG_ERR("[CMD] rxx tx failed\r\n");
} }
return 0; return 0;
+11
View File
@@ -9,6 +9,17 @@
#include <stdint.h> #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); int dr_parser(const uint8_t *buf, uint16_t len);
#endif /* CMD_PARSER_H__ */ #endif /* CMD_PARSER_H__ */
+24
View File
@@ -6,3 +6,27 @@ if(SB_CONFIG_NETCORE_HCI_IPC)
"Extra configuration for the nRF5340 net core HCI image" "Extra configuration for the nRF5340 net core HCI image"
) )
endif() endif()
if(SB_CONFIG_BOOTLOADER_MCUBOOT)
list(APPEND mcuboot_EXTRA_CONF_FILE
"${APP_DIR}/sysbuild/mcuboot.conf"
)
list(REMOVE_DUPLICATES mcuboot_EXTRA_CONF_FILE)
set(
mcuboot_EXTRA_CONF_FILE
"${mcuboot_EXTRA_CONF_FILE}"
CACHE INTERNAL
"Extra configuration for the MCUboot image"
)
list(APPEND mcuboot_EXTRA_DTC_OVERLAY_FILE
"${APP_DIR}/sysbuild/mcuboot.overlay"
)
list(REMOVE_DUPLICATES mcuboot_EXTRA_DTC_OVERLAY_FILE)
set(
mcuboot_EXTRA_DTC_OVERLAY_FILE
"${mcuboot_EXTRA_DTC_OVERLAY_FILE}"
CACHE INTERNAL
"Extra devicetree overlay for the MCUboot image"
)
endif()
+2
View File
@@ -1,2 +1,4 @@
# nRF52840 build: no network core image. # nRF52840 build: no network core image.
# Re-enable NETCORE_HCI_IPC only for nRF5340 sysbuild configurations. # Re-enable NETCORE_HCI_IPC only for nRF5340 sysbuild configurations.
SB_CONFIG_BOOTLOADER_MCUBOOT=y
+1
View File
@@ -0,0 +1 @@
CONFIG_GPIO_HOGS=y
+10
View File
@@ -0,0 +1,10 @@
#include <zephyr/dt-bindings/gpio/gpio.h>
&gpio0 {
power_hold_hog: power_hold_hog {
gpio-hog;
gpios = <8 GPIO_ACTIVE_HIGH>;
output-high;
line-name = "POWER_HOLD";
};
};