ADC까지, 신호 확인 필요(반사 신호 약함 or 뒤쪽을 찍고 있음)
This commit is contained in:
Vendored
+12
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "nrf-connect",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Launch blinky/build/blinky",
|
||||||
|
"config": "${workspaceFolder}/build/blinky",
|
||||||
|
"runToEntryPoint": "main"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Vendored
+5
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"nrf-connect.debugging.bindings": {
|
||||||
|
"${workspaceFolder}/build/blinky": "Launch blinky/build/blinky"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,8 +8,10 @@ target_include_directories(app PRIVATE
|
|||||||
src
|
src
|
||||||
src/ble
|
src/ble
|
||||||
src/drivers/battery
|
src/drivers/battery
|
||||||
|
src/drivers/echo_adc
|
||||||
src/drivers/led
|
src/drivers/led
|
||||||
src/drivers/imu
|
src/drivers/imu
|
||||||
|
src/drivers/piezo
|
||||||
src/drivers/temperature
|
src/drivers/temperature
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -19,7 +21,9 @@ target_sources(app PRIVATE
|
|||||||
src/power_control.c
|
src/power_control.c
|
||||||
src/ble/ble_service.c
|
src/ble/ble_service.c
|
||||||
src/drivers/battery/battery_adc.c
|
src/drivers/battery/battery_adc.c
|
||||||
|
src/drivers/echo_adc/echo_adc.c
|
||||||
src/drivers/led/led_control.c
|
src/drivers/led/led_control.c
|
||||||
src/drivers/imu/imu_i2c.c
|
src/drivers/imu/imu_i2c.c
|
||||||
|
src/drivers/piezo/piezo.c
|
||||||
src/drivers/temperature/tmp235.c
|
src/drivers/temperature/tmp235.c
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
CONFIG_BT_CTLR_TX_PWR_PLUS_8=y
|
||||||
@@ -26,22 +26,27 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* I2C0 핀 재설정: ICM42670P IMU (SCL=P1.14, SDA=P1.15) */
|
/* I2C0 핀 재설정: ICM42670P IMU (SCL=P1.14, SDA=P1.15) */
|
||||||
&i2c0_default {
|
&i2c0_default
|
||||||
group1 {
|
{
|
||||||
|
group1
|
||||||
|
{
|
||||||
psels = <NRF_PSEL(TWIM_SCL, 1, 14)>,
|
psels = <NRF_PSEL(TWIM_SCL, 1, 14)>,
|
||||||
<NRF_PSEL(TWIM_SDA, 1, 15)>;
|
<NRF_PSEL(TWIM_SDA, 1, 15)>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
&i2c0_sleep {
|
&i2c0_sleep
|
||||||
group1 {
|
{
|
||||||
|
group1
|
||||||
|
{
|
||||||
psels = <NRF_PSEL(TWIM_SCL, 1, 14)>,
|
psels = <NRF_PSEL(TWIM_SCL, 1, 14)>,
|
||||||
<NRF_PSEL(TWIM_SDA, 1, 15)>;
|
<NRF_PSEL(TWIM_SDA, 1, 15)>;
|
||||||
low-power-enable;
|
low-power-enable;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
&i2c0 {
|
&i2c0
|
||||||
|
{
|
||||||
status = "okay";
|
status = "okay";
|
||||||
clock-frequency = <I2C_BITRATE_STANDARD>;
|
clock-frequency = <I2C_BITRATE_STANDARD>;
|
||||||
};
|
};
|
||||||
@@ -128,5 +133,79 @@
|
|||||||
gpios = <&gpio1 8 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
|
gpios = <&gpio1 8 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
|
||||||
label = "Power Button";
|
label = "Power Button";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* 피에조 TX/RX 전원 레일 제어 (TMP235 온도센서도 이 레일 공유)
|
||||||
|
* HIGH = 전원 ON, LOW = 전원 OFF */
|
||||||
|
PIEZO_PWR: PIEZO_PWR
|
||||||
|
{
|
||||||
|
gpios = <&gpio1 9 GPIO_ACTIVE_HIGH>;
|
||||||
|
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";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
* VesiScan BASIC - nRF5340 DK application core overlay
|
||||||
|
*
|
||||||
|
* Keeps the same external signal mapping used by the nRF52840 build:
|
||||||
|
* POWER_HOLD: P0.08
|
||||||
|
* POWER_BTN: P1.08
|
||||||
|
* LED_BLE: P0.12
|
||||||
|
* LED_ORANGE: P0.29
|
||||||
|
* BATT_ADC: P0.04
|
||||||
|
* TEMP_ADC: P0.05
|
||||||
|
* IMU_SCL: P1.14
|
||||||
|
* IMU_SDA: P1.15
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <zephyr/dt-bindings/adc/adc.h>
|
||||||
|
#include <zephyr/dt-bindings/adc/nrf-saadc.h>
|
||||||
|
#include <zephyr/dt-bindings/pinctrl/nrf-pinctrl.h>
|
||||||
|
|
||||||
|
&pinctrl
|
||||||
|
{
|
||||||
|
i2c0_default: i2c0_default
|
||||||
|
{
|
||||||
|
group1
|
||||||
|
{
|
||||||
|
psels = <NRF_PSEL(TWIM_SCL, 1, 14)>,
|
||||||
|
<NRF_PSEL(TWIM_SDA, 1, 15)>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
i2c0_sleep: i2c0_sleep
|
||||||
|
{
|
||||||
|
group1
|
||||||
|
{
|
||||||
|
psels = <NRF_PSEL(TWIM_SCL, 1, 14)>,
|
||||||
|
<NRF_PSEL(TWIM_SDA, 1, 15)>;
|
||||||
|
low-power-enable;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
&i2c0
|
||||||
|
{
|
||||||
|
status = "okay";
|
||||||
|
clock-frequency = <I2C_BITRATE_STANDARD>;
|
||||||
|
pinctrl-0 = <&i2c0_default>;
|
||||||
|
pinctrl-1 = <&i2c0_sleep>;
|
||||||
|
pinctrl-names = "default", "sleep";
|
||||||
|
};
|
||||||
|
|
||||||
|
&adc
|
||||||
|
{
|
||||||
|
status = "okay";
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
|
||||||
|
channel@0
|
||||||
|
{
|
||||||
|
reg = <0>;
|
||||||
|
zephyr,gain = "ADC_GAIN_1_6";
|
||||||
|
zephyr,reference = "ADC_REF_INTERNAL";
|
||||||
|
zephyr,acquisition-time = <ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 40)>;
|
||||||
|
zephyr,input-positive = <NRF_SAADC_AIN0>;
|
||||||
|
zephyr,resolution = <12>;
|
||||||
|
zephyr,oversampling = <2>;
|
||||||
|
};
|
||||||
|
|
||||||
|
channel@1
|
||||||
|
{
|
||||||
|
reg = <1>;
|
||||||
|
zephyr,gain = "ADC_GAIN_1_6";
|
||||||
|
zephyr,reference = "ADC_REF_INTERNAL";
|
||||||
|
zephyr,acquisition-time = <ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 40)>;
|
||||||
|
zephyr,input-positive = <NRF_SAADC_AIN1>;
|
||||||
|
zephyr,resolution = <12>;
|
||||||
|
zephyr,oversampling = <2>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
&arduino_adc
|
||||||
|
{
|
||||||
|
status = "disabled";
|
||||||
|
};
|
||||||
|
|
||||||
|
/ {
|
||||||
|
zephyr,user
|
||||||
|
{
|
||||||
|
io-channels = <&adc 0>, <&adc 1>;
|
||||||
|
io-channel-names = "battery", "temperature";
|
||||||
|
};
|
||||||
|
|
||||||
|
led
|
||||||
|
{
|
||||||
|
compatible = "gpio-leds";
|
||||||
|
|
||||||
|
LED_BLE: LED_BLE
|
||||||
|
{
|
||||||
|
gpios = <&gpio0 12 GPIO_ACTIVE_LOW>;
|
||||||
|
label = "Green LED";
|
||||||
|
};
|
||||||
|
|
||||||
|
FUNCTION_LED: FUNCTION_LED
|
||||||
|
{
|
||||||
|
gpios = <&gpio0 29 GPIO_ACTIVE_LOW>;
|
||||||
|
label = "Orange LED";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
pin
|
||||||
|
{
|
||||||
|
compatible = "gpio-keys";
|
||||||
|
|
||||||
|
PWR_HOLD: PWR_HOLD
|
||||||
|
{
|
||||||
|
gpios = <&gpio0 8 GPIO_ACTIVE_HIGH>;
|
||||||
|
label = "Power Hold Latch";
|
||||||
|
};
|
||||||
|
|
||||||
|
BUTTON_CHECK: BUTTON_CHECK
|
||||||
|
{
|
||||||
|
gpios = <&gpio1 8 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
|
||||||
|
label = "Power Button";
|
||||||
|
};
|
||||||
|
|
||||||
|
PIEZO_PWR: PIEZO_PWR
|
||||||
|
{
|
||||||
|
gpios = <&gpio1 9 GPIO_ACTIVE_HIGH>;
|
||||||
|
label = "Piezo Power Enable";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
+118
@@ -0,0 +1,118 @@
|
|||||||
|
# TODO
|
||||||
|
|
||||||
|
현재 프로젝트에서 다음에 이어서 작업할 항목들을 정리한다.
|
||||||
|
|
||||||
|
## 현재 상태
|
||||||
|
|
||||||
|
- BLE 보안은 아직 개발 모드 기준이다.
|
||||||
|
- `CONFIG_BT_SMP=n`: [prj.conf](/C:/jhChun/blinky/prj.conf:42)
|
||||||
|
- `BLE_DEV_MODE = 1`: [src/ble/ble_service.h](/C:/jhChun/blinky/src/ble/ble_service.h:18)
|
||||||
|
- `Dev mode: no security / Production mode: bonding + passkey (TODO)`: [src/ble/ble_service.c](/C:/jhChun/blinky/src/ble/ble_service.c:10)
|
||||||
|
- 플래시 저장소는 아직 미구현이다.
|
||||||
|
- `Phase 3: FDS/NVS (TODO)`: [src/main.c](/C:/jhChun/blinky/src/main.c:328)
|
||||||
|
- DFU는 아직 미구현이다.
|
||||||
|
- `CONFIG_BOOTLOADER_MCUBOOT` 미설정: [build/blinky/zephyr/.config](/C:/jhChun/blinky/build/blinky/zephyr/.config:94)
|
||||||
|
- 파티션도 앱 단일 영역만 사용 중: [build/partitions.yml](/C:/jhChun/blinky/build/partitions.yml:1)
|
||||||
|
- `mcf?`는 현재 고정값 반환만 구현되어 있다.
|
||||||
|
- `freq=1`, `cycles=7`, `avg=10`, `delay=10`, `samples=100`: [src/parser.c](/C:/jhChun/blinky/src/parser.c:38)
|
||||||
|
- 실제 측정도 현재 위 고정값을 사용한다.
|
||||||
|
- `avg=10`: [src/parser.c](/C:/jhChun/blinky/src/parser.c:28)
|
||||||
|
- `cycles=7`, `delay=10us`: [src/drivers/piezo/piezo.h](/C:/jhChun/blinky/src/drivers/piezo/piezo.h:12)
|
||||||
|
- `samples=100`: [src/drivers/echo_adc/echo_adc.h](/C:/jhChun/blinky/src/drivers/echo_adc/echo_adc.h:11)
|
||||||
|
|
||||||
|
## 1. BLE 보안
|
||||||
|
|
||||||
|
목표:
|
||||||
|
- LESC
|
||||||
|
- MITM
|
||||||
|
- Fixed Passkey
|
||||||
|
- Bonding
|
||||||
|
|
||||||
|
해야 할 일:
|
||||||
|
- `CONFIG_BT_SMP=y`
|
||||||
|
- `CONFIG_BT_FIXED_PASSKEY=y`
|
||||||
|
- `CONFIG_BT_BONDABLE=y`
|
||||||
|
- `CONFIG_BT_SMP_ENFORCE_MITM=y`
|
||||||
|
- 필요 시 `CONFIG_BT_SMP_SC_ONLY=y` 또는 `CONFIG_BT_SMP_SC_PAIR_ONLY=y`
|
||||||
|
- `bt_conn_auth_cb` 등록
|
||||||
|
- 고정 패스키를 `m_static_passkey`와 연결
|
||||||
|
- 기본값 선언: [src/main.h](/C:/jhChun/blinky/src/main.h:21)
|
||||||
|
- RAM 보관: [src/main.c](/C:/jhChun/blinky/src/main.c:56)
|
||||||
|
- 본딩 키 복원을 위해 `BT_SETTINGS + SETTINGS + NVS` 연동
|
||||||
|
|
||||||
|
주의:
|
||||||
|
- Bonding은 저장소 없이 켜도 부팅 후 유지되지 않는다.
|
||||||
|
- 보안 설정을 올리면 앱 테스트 절차도 함께 바뀐다.
|
||||||
|
|
||||||
|
## 2. 플래시 저장소 (NVS)
|
||||||
|
|
||||||
|
목표:
|
||||||
|
- 예전 FDS 역할을 Zephyr/NCS 방식으로 대체
|
||||||
|
- 측정 파라미터, passkey, 필요 시 serial/HW info 저장
|
||||||
|
|
||||||
|
권장 방향:
|
||||||
|
- FDS를 다시 붙이기보다 `NVS` 사용
|
||||||
|
- Bluetooth bonding 키는 `BT_SETTINGS`가 저장하고
|
||||||
|
- 앱 파라미터는 별도 `NVS` 또는 `settings/NVS backend`로 저장
|
||||||
|
|
||||||
|
우선 저장할 후보:
|
||||||
|
- piezo config
|
||||||
|
- `freq`
|
||||||
|
- `cycles`
|
||||||
|
- `avg`
|
||||||
|
- `delay_us`
|
||||||
|
- `samples`
|
||||||
|
- passkey
|
||||||
|
- 필요 시 `SERIAL_NO`, `HW_NO`
|
||||||
|
|
||||||
|
해야 할 일:
|
||||||
|
- `piezo_config` 구조체 추가
|
||||||
|
- 부팅 시 기본값 로드 후 NVS 값이 있으면 덮어쓰기
|
||||||
|
- `mcf?`는 구조체 읽기
|
||||||
|
- `mcs?`는 구조체 갱신 + 저장
|
||||||
|
- 유효 범위 체크 추가
|
||||||
|
|
||||||
|
현재 메모:
|
||||||
|
- `mcf?`는 아직 고정 응답만 구현됨: [src/parser.c](/C:/jhChun/blinky/src/parser.c:442)
|
||||||
|
- `mcs?`는 아직 없음: [src/parser.c](/C:/jhChun/blinky/src/parser.c:503)
|
||||||
|
|
||||||
|
## 3. DFU
|
||||||
|
|
||||||
|
목표:
|
||||||
|
- BLE 기반 펌웨어 업데이트 지원
|
||||||
|
|
||||||
|
권장 방향:
|
||||||
|
- `MCUboot + MCUmgr SMP over BLE`
|
||||||
|
|
||||||
|
해야 할 일:
|
||||||
|
- `CONFIG_BOOTLOADER_MCUBOOT=y`
|
||||||
|
- `CONFIG_MCUMGR=y`
|
||||||
|
- `CONFIG_MCUMGR_TRANSPORT_BT=y`
|
||||||
|
- `CONFIG_IMG_MANAGER=y`
|
||||||
|
- 필요 시 `CONFIG_MCUMGR_GRP_IMG=y`
|
||||||
|
- 파티션 구성 추가
|
||||||
|
- `slot0`
|
||||||
|
- `slot1`
|
||||||
|
- 필요 시 `scratch` 또는 overwrite-only 전략
|
||||||
|
- 광고/서비스에 SMP transport가 정상 노출되는지 확인
|
||||||
|
|
||||||
|
주의:
|
||||||
|
- 지금 프로젝트는 NUS만 사용 중이라 일반적인 OTA DFU는 바로 안 된다.
|
||||||
|
- DFU를 넣으면 파티션 구조와 빌드 방식이 함께 바뀐다.
|
||||||
|
|
||||||
|
## 4. 권장 작업 순서
|
||||||
|
|
||||||
|
1. `piezo_config` 구조체 도입
|
||||||
|
2. `mcf?`를 고정값이 아닌 실제 구조체 반환으로 변경
|
||||||
|
3. `mcs?` 구현
|
||||||
|
4. `NVS` 저장/복원 구현
|
||||||
|
5. BLE 보안 적용
|
||||||
|
6. Bonding 저장 확인
|
||||||
|
7. 마지막에 DFU 적용
|
||||||
|
|
||||||
|
## 5. 체크 포인트
|
||||||
|
|
||||||
|
- `mcf?` 값과 실제 측정값이 항상 같아야 함
|
||||||
|
- `mcs?`로 바꾼 값이 재부팅 후에도 유지되어야 함
|
||||||
|
- 보안 적용 후 앱에서 pairing/passkey 흐름 확인
|
||||||
|
- DFU 적용 후 기존 NUS 기능과 충돌 없는지 확인
|
||||||
@@ -20,8 +20,8 @@
|
|||||||
| 1 | 1 | 0 | 1 | 0 |
|
| 1 | 1 | 0 | 1 | 0 |
|
||||||
| 2 | 1 | 0 | 0 | 1 |
|
| 2 | 1 | 0 | 0 | 1 |
|
||||||
| 3 | 1 | 0 | 1 | 1 |
|
| 3 | 1 | 0 | 1 | 1 |
|
||||||
| 4 | 0 | 1 | 1 | 1 |
|
| 4 | 0 | 1 | 0 | 0 |
|
||||||
| 5 | 0 | 1 | 0 | 1 |
|
| 5 | 0 | 1 | 1 | 0 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -405,7 +405,7 @@ rbb: ... [temp_L][temp_H] ... ← °C × 100 단위 (25.50°C = 2550)
|
|||||||
## 9. 다음 구현 순서
|
## 9. 다음 구현 순서
|
||||||
|
|
||||||
```
|
```
|
||||||
Step 1 ✅ 온도 센서 (tmp235.c) — 완료, 테스트 필요
|
Step 1 ✅ 온도 센서 (tmp235.c) — 완료
|
||||||
|
|
||||||
Step 2 피에조 드라이버 (piezo.c)
|
Step 2 피에조 드라이버 (piezo.c)
|
||||||
└─ GPIO 초기화, MUX, SW burst (NOP 기반, nrf HAL 직접 사용)
|
└─ GPIO 초기화, MUX, SW burst (NOP 기반, nrf HAL 직접 사용)
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ CONFIG_GPIO=y
|
|||||||
|
|
||||||
# Logging via RTT
|
# Logging via RTT
|
||||||
CONFIG_LOG=y
|
CONFIG_LOG=y
|
||||||
CONFIG_LOG_MODE_DEFERRED=y
|
CONFIG_LOG_MODE_IMMEDIATE=y
|
||||||
|
CONFIG_LOG_DEFAULT_LEVEL=3
|
||||||
|
CONFIG_LOG_BACKEND_SHOW_COLOR=n
|
||||||
CONFIG_LOG_BACKEND_RTT=y
|
CONFIG_LOG_BACKEND_RTT=y
|
||||||
CONFIG_LOG_BACKEND_UART=n
|
CONFIG_LOG_BACKEND_UART=n
|
||||||
CONFIG_USE_SEGGER_RTT=y
|
CONFIG_USE_SEGGER_RTT=y
|
||||||
@@ -15,11 +17,14 @@ CONFIG_CONSOLE=y
|
|||||||
CONFIG_RTT_CONSOLE=y
|
CONFIG_RTT_CONSOLE=y
|
||||||
CONFIG_SERIAL=n
|
CONFIG_SERIAL=n
|
||||||
CONFIG_UART_CONSOLE=n
|
CONFIG_UART_CONSOLE=n
|
||||||
|
CONFIG_LOG_BACKEND_RTT_MODE_BLOCK=y
|
||||||
|
CONFIG_LOG_BACKEND_RTT_OUTPUT_BUFFER_SIZE=1024
|
||||||
|
|
||||||
# BLE stack
|
# BLE stack
|
||||||
CONFIG_BT=y
|
CONFIG_BT=y
|
||||||
|
CONFIG_BT_LOG_LEVEL_WRN=y
|
||||||
CONFIG_BT_PERIPHERAL=y
|
CONFIG_BT_PERIPHERAL=y
|
||||||
CONFIG_BT_DEVICE_NAME="VB026030000"
|
CONFIG_BT_DEVICE_NAME="VB0260300ZZ" # Zephyr 기본 BLE 이름
|
||||||
CONFIG_BT_DEVICE_NAME_DYNAMIC=y
|
CONFIG_BT_DEVICE_NAME_DYNAMIC=y
|
||||||
CONFIG_BT_DEVICE_NAME_MAX=16
|
CONFIG_BT_DEVICE_NAME_MAX=16
|
||||||
|
|
||||||
@@ -38,9 +43,6 @@ CONFIG_BT_PERIPHERAL_PREF_MAX_INT=24
|
|||||||
CONFIG_BT_PERIPHERAL_PREF_LATENCY=0
|
CONFIG_BT_PERIPHERAL_PREF_LATENCY=0
|
||||||
CONFIG_BT_PERIPHERAL_PREF_TIMEOUT=400
|
CONFIG_BT_PERIPHERAL_PREF_TIMEOUT=400
|
||||||
|
|
||||||
# TX power
|
|
||||||
CONFIG_BT_CTLR_TX_PWR_PLUS_8=y
|
|
||||||
|
|
||||||
# Security (dev mode - disabled for now)
|
# Security (dev mode - disabled for now)
|
||||||
CONFIG_BT_SMP=n
|
CONFIG_BT_SMP=n
|
||||||
|
|
||||||
@@ -50,6 +52,12 @@ CONFIG_ADC=y
|
|||||||
# I2C (IMU: ICM42670P)
|
# I2C (IMU: ICM42670P)
|
||||||
CONFIG_I2C=y
|
CONFIG_I2C=y
|
||||||
|
|
||||||
|
# Direct nrfx SPIM3 driver for ADC121S051
|
||||||
|
CONFIG_NRFX_SPIM=y
|
||||||
|
|
||||||
# System
|
# System
|
||||||
CONFIG_HEAP_MEM_POOL_SIZE=2048
|
CONFIG_HEAP_MEM_POOL_SIZE=2048
|
||||||
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048
|
CONFIG_SYSTEM_WORKQUEUE_STACK_SIZE=2048
|
||||||
|
|
||||||
|
# BLE RX stack sizes (prevent stack overflow on BT RX WQ)
|
||||||
|
CONFIG_BT_RX_STACK_SIZE=4096
|
||||||
|
|||||||
+156
-15
@@ -30,8 +30,92 @@ LOG_MODULE_REGISTER(ble_svc, LOG_LEVEL_INF);
|
|||||||
static struct bt_conn *current_conn;
|
static struct bt_conn *current_conn;
|
||||||
static ble_data_rx_cb_t rx_callback;
|
static ble_data_rx_cb_t rx_callback;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NUS 전송은 "보냈다" 호출만 한다고 바로 끝나는 게 아니다.
|
||||||
|
* 내부 TX 버퍼에 들어간 뒤 실제로 무선으로 나가고,
|
||||||
|
* Zephyr가 sent 콜백을 줄 때 비로소 한 건이 끝났다고 볼 수 있다.
|
||||||
|
*
|
||||||
|
* 그래서 아래 2개를 같이 쓴다.
|
||||||
|
* - mutex: 여러 곳에서 동시에 NUS 전송을 시작하지 못하게 막음
|
||||||
|
* - sem : "직전 전송이 정말 끝났는지" 기다리는 신호
|
||||||
|
*
|
||||||
|
* mbb?처럼 큰 패킷을 여러 개 연속으로 보내는 경우 이 보호가 없으면
|
||||||
|
* - 중간에 -ENOMEM이 뜨거나
|
||||||
|
* - 몇 개는 나가고 몇 개는 실패하거나
|
||||||
|
* - 앱 쪽에서 응답이 꼬여 보일 수 있다.
|
||||||
|
*/
|
||||||
|
static struct k_mutex nus_tx_lock;
|
||||||
|
static struct k_sem nus_tx_done_sem;
|
||||||
|
|
||||||
/* BLE API는 ISR/콜백에서 직접 호출하면 안전하지 않을 수 있으므로 k_work 사용 */
|
/* BLE API는 ISR/콜백에서 직접 호출하면 안전하지 않을 수 있으므로 k_work 사용 */
|
||||||
static struct k_work adv_restart_work;
|
static struct k_work adv_restart_work;
|
||||||
|
static const bt_addr_le_t fixed_identity_addr = {
|
||||||
|
.type = BT_ADDR_LE_RANDOM,
|
||||||
|
.a = {
|
||||||
|
.val = { 0xF1, 0x8E, 0x81, 0xFA, 0x87, 0xC5 },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char *ble_addr_type_str(uint8_t type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case BT_ADDR_LE_PUBLIC:
|
||||||
|
return "public";
|
||||||
|
case BT_ADDR_LE_RANDOM:
|
||||||
|
return "random";
|
||||||
|
case BT_ADDR_LE_PUBLIC_ID:
|
||||||
|
return "public-id";
|
||||||
|
case BT_ADDR_LE_RANDOM_ID:
|
||||||
|
return "random-id";
|
||||||
|
default:
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ble_log_local_identity(void)
|
||||||
|
{
|
||||||
|
bt_addr_le_t addr = {0};
|
||||||
|
size_t count = 1;
|
||||||
|
char addr_str[BT_ADDR_LE_STR_LEN];
|
||||||
|
|
||||||
|
bt_id_get(&addr, &count);
|
||||||
|
if (count == 0U)
|
||||||
|
{
|
||||||
|
DBG_ERR("[BLE] No local identity address\r\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bt_addr_le_to_str(&addr, addr_str, sizeof(addr_str));
|
||||||
|
DBG_CORE("[BLE] Local identity: %s (%s)\r\n",
|
||||||
|
addr_str,
|
||||||
|
ble_addr_type_str(addr.type));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ble_configure_fixed_identity(void)
|
||||||
|
{
|
||||||
|
bt_addr_le_t addr = fixed_identity_addr;
|
||||||
|
char addr_str[BT_ADDR_LE_STR_LEN];
|
||||||
|
int id;
|
||||||
|
|
||||||
|
bt_addr_le_to_str(&addr, addr_str, sizeof(addr_str));
|
||||||
|
DBG_CORE("[BLE] Request fixed identity: %s\r\n", addr_str);
|
||||||
|
|
||||||
|
id = bt_id_create(&addr, NULL);
|
||||||
|
if (id < 0)
|
||||||
|
{
|
||||||
|
DBG_ERR("[BLE] bt_id_create failed (err %d)\r\n", id);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (id != BT_ID_DEFAULT)
|
||||||
|
{
|
||||||
|
DBG_ERR("[BLE] Unexpected identity slot %d\r\n", id);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void adv_restart_handler(struct k_work *work)
|
static void adv_restart_handler(struct k_work *work)
|
||||||
{
|
{
|
||||||
@@ -61,7 +145,7 @@ static void connected(struct bt_conn *conn, uint8_t err)
|
|||||||
{
|
{
|
||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
DBG_PRINTF("[BLE] Connect failed (err %d) -> re-adv\r\n", err);
|
DBG_ERR("[BLE] Connect failed (err %d) -> re-adv\r\n", err);
|
||||||
k_work_submit(&adv_restart_work);
|
k_work_submit(&adv_restart_work);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -72,7 +156,7 @@ static void connected(struct bt_conn *conn, uint8_t err)
|
|||||||
/* 연결 정보 출력 */
|
/* 연결 정보 출력 */
|
||||||
char addr_str[BT_ADDR_LE_STR_LEN];
|
char addr_str[BT_ADDR_LE_STR_LEN];
|
||||||
bt_addr_le_to_str(bt_conn_get_dst(conn), addr_str, sizeof(addr_str));
|
bt_addr_le_to_str(bt_conn_get_dst(conn), addr_str, sizeof(addr_str));
|
||||||
DBG_PRINTF("[BLE] Peer: %s\r\n", addr_str);
|
DBG_CORE("[BLE] Peer: %s\r\n", addr_str);
|
||||||
|
|
||||||
/* Connection parameters (30ms interval) */
|
/* Connection parameters (30ms interval) */
|
||||||
struct bt_le_conn_param conn_param =
|
struct bt_le_conn_param conn_param =
|
||||||
@@ -85,7 +169,7 @@ static void connected(struct bt_conn *conn, uint8_t err)
|
|||||||
bt_conn_le_param_update(conn, &conn_param);
|
bt_conn_le_param_update(conn, &conn_param);
|
||||||
|
|
||||||
led_set_state(LED_STATE_OFF);
|
led_set_state(LED_STATE_OFF);
|
||||||
DBG_PRINTF("[BLE] Connected\r\n");
|
DBG_CORE("[BLE] Connected\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void disconnected(struct bt_conn *conn, uint8_t reason)
|
static void disconnected(struct bt_conn *conn, uint8_t reason)
|
||||||
@@ -97,7 +181,7 @@ static void disconnected(struct bt_conn *conn, uint8_t reason)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ble_connection_st = false;
|
ble_connection_st = false;
|
||||||
DBG_PRINTF("[BLE] Disconnected (reason 0x%02x)\r\n", reason);
|
DBG_CORE("[BLE] Disconnected (reason 0x%02x)\r\n", reason);
|
||||||
|
|
||||||
/* 워크큐에서 advertising 재시작 */
|
/* 워크큐에서 advertising 재시작 */
|
||||||
k_work_submit(&adv_restart_work);
|
k_work_submit(&adv_restart_work);
|
||||||
@@ -122,6 +206,9 @@ BT_CONN_CB_DEFINE(conn_callbacks) =
|
|||||||
*============================================================================*/
|
*============================================================================*/
|
||||||
static void nus_received(struct bt_conn *conn, const uint8_t *data, uint16_t len)
|
static void nus_received(struct bt_conn *conn, const uint8_t *data, uint16_t len)
|
||||||
{
|
{
|
||||||
|
ARG_UNUSED(conn);
|
||||||
|
|
||||||
|
DBG_CORE("[NUS RX] len=%u\r\n", len);
|
||||||
if (rx_callback)
|
if (rx_callback)
|
||||||
{
|
{
|
||||||
rx_callback(data, len);
|
rx_callback(data, len);
|
||||||
@@ -130,7 +217,10 @@ static void nus_received(struct bt_conn *conn, const uint8_t *data, uint16_t len
|
|||||||
|
|
||||||
static void nus_sent(struct bt_conn *conn)
|
static void nus_sent(struct bt_conn *conn)
|
||||||
{
|
{
|
||||||
DBG_PRINTF("[NUS TX] complete\r\n");
|
ARG_UNUSED(conn);
|
||||||
|
/* 이 시점이 "직전 NUS 패킷 전송이 끝났다"는 완료 신호다. */
|
||||||
|
k_sem_give(&nus_tx_done_sem);
|
||||||
|
DBG_CORE("[NUS TX] complete\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct bt_nus_cb nus_cb =
|
static struct bt_nus_cb nus_cb =
|
||||||
@@ -147,26 +237,36 @@ int ble_service_init(ble_data_rx_cb_t rx_cb)
|
|||||||
int err;
|
int err;
|
||||||
|
|
||||||
rx_callback = rx_cb;
|
rx_callback = rx_cb;
|
||||||
|
k_mutex_init(&nus_tx_lock);
|
||||||
|
k_sem_init(&nus_tx_done_sem, 0, 1);
|
||||||
|
|
||||||
k_work_init(&adv_restart_work, adv_restart_handler);
|
k_work_init(&adv_restart_work, adv_restart_handler);
|
||||||
|
|
||||||
|
err = ble_configure_fixed_identity();
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
/* Enable BLE stack */
|
/* Enable BLE stack */
|
||||||
err = bt_enable(NULL);
|
err = bt_enable(NULL);
|
||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
DBG_PRINTF("[BLE] bt_enable failed (err %d)\r\n", err);
|
DBG_ERR("[BLE] bt_enable failed (err %d)\r\n", err);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
DBG_PRINTF("[BLE] Stack enabled\r\n");
|
DBG_CORE("[BLE] Stack enabled\r\n");
|
||||||
|
|
||||||
|
ble_log_local_identity();
|
||||||
|
|
||||||
/* Initialize NUS */
|
/* Initialize NUS */
|
||||||
err = bt_nus_init(&nus_cb);
|
err = bt_nus_init(&nus_cb);
|
||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
DBG_PRINTF("[BLE] NUS init failed (err %d)\r\n", err);
|
DBG_ERR("[BLE] NUS init failed (err %d)\r\n", err);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
DBG_PRINTF("[BLE] NUS initialized\r\n");
|
DBG_CORE("[BLE] NUS initialized\r\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -185,7 +285,7 @@ int ble_advertising_start(void)
|
|||||||
err = bt_le_adv_start(&adv_param, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
|
err = bt_le_adv_start(&adv_param, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
|
||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
DBG_PRINTF("[BLE] Adv start failed (err %d)\r\n", err);
|
DBG_ERR("[BLE] Adv start failed (err %d)\r\n", err);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,7 +298,7 @@ int ble_advertising_stop(void)
|
|||||||
int err = bt_le_adv_stop();
|
int err = bt_le_adv_stop();
|
||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
DBG_PRINTF("[BLE] Adv stop failed (err %d)\r\n", err);
|
DBG_ERR("[BLE] Adv stop failed (err %d)\r\n", err);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,18 +308,59 @@ int ble_advertising_stop(void)
|
|||||||
|
|
||||||
int ble_data_send(const uint8_t *data, uint16_t len)
|
int ble_data_send(const uint8_t *data, uint16_t len)
|
||||||
{
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
if (!current_conn)
|
if (!current_conn)
|
||||||
{
|
{
|
||||||
DBG_PRINTF("[NUS TX] not connected\r\n");
|
DBG_ERR("[NUS TX] not connected\r\n");
|
||||||
return -ENOTCONN;
|
return -ENOTCONN;
|
||||||
}
|
}
|
||||||
|
|
||||||
DBG_PRINTF("[NUS TX] %d bytes\r\n", len);
|
/*
|
||||||
int err = bt_nus_send(current_conn, data, len);
|
* NUS는 한 번에 한 패킷씩 질서 있게 보내는 편이 안전하다.
|
||||||
|
* 특히 mbb?는 rbb + reb x6 + raa처럼 여러 응답을 연속 전송하므로
|
||||||
|
* 먼저 들어온 전송이 끝나기 전 다음 전송이 겹치지 않게 잠근다.
|
||||||
|
*/
|
||||||
|
k_mutex_lock(&nus_tx_lock, K_FOREVER);
|
||||||
|
|
||||||
|
/* 혹시 남아 있는 완료 신호가 있으면 비워서 "이번 전송 전용" 상태로 만든다. */
|
||||||
|
while (k_sem_take(&nus_tx_done_sem, K_NO_WAIT) == 0) {
|
||||||
|
}
|
||||||
|
|
||||||
|
DBG_CORE("[NUS TX] send len=%u\r\n", len);
|
||||||
|
for (int retry = 0; ; retry++)
|
||||||
|
{
|
||||||
|
err = bt_nus_send(current_conn, data, len);
|
||||||
|
if (err != -ENOMEM || retry >= 20) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* -ENOMEM은 지금 당장 보낼 자리(TX 버퍼)가 없다는 뜻이다.
|
||||||
|
* 바로 포기하지 않고 짧게 쉬었다가 다시 시도한다.
|
||||||
|
*/
|
||||||
|
DBG_ERR("[NUS TX] busy, retry=%d\r\n", retry + 1);
|
||||||
|
k_msleep(5);
|
||||||
|
}
|
||||||
|
|
||||||
if (err)
|
if (err)
|
||||||
{
|
{
|
||||||
DBG_PRINTF("[NUS TX] failed (err %d)\r\n", err);
|
DBG_ERR("[NUS TX] failed (err %d)\r\n", err);
|
||||||
|
k_mutex_unlock(&nus_tx_lock);
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* bt_nus_send()가 성공했다고 해서 공중으로 다 나간 것은 아니다.
|
||||||
|
* sent 콜백이 올 때까지 조금 기다려서 "다음 패킷을 보내도 되는 시점"을 맞춘다.
|
||||||
|
*/
|
||||||
|
err = k_sem_take(&nus_tx_done_sem, K_MSEC(1000));
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
DBG_ERR("[NUS TX] completion timeout (err %d)\r\n", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
k_mutex_unlock(&nus_tx_lock);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+16
-9
@@ -8,16 +8,23 @@
|
|||||||
#include <zephyr/logging/log.h>
|
#include <zephyr/logging/log.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* DBG_PRINTF maps to printk for RTT/UART console output.
|
* 로그를 3단계로 나눠 쓴다.
|
||||||
* For module-level logging use LOG_INF/LOG_DBG etc.
|
*
|
||||||
|
* - DBG_PRINTF: 상세 흐름용
|
||||||
|
* 평소에는 가장 많이 쏟아지는 로그라서, RTT가 잘리거나 너무 시끄러울 때
|
||||||
|
* 이 매크로만 잠깐 꺼두면 된다.
|
||||||
|
*
|
||||||
|
* - DBG_CORE: 부팅/상태 전환처럼 "항상 보고 싶은 핵심 로그"
|
||||||
|
*
|
||||||
|
* - DBG_ERR: 실패/이상 상황 로그
|
||||||
|
*
|
||||||
|
* 지금은 RTT 로그가 중간에 끊기는 문제를 줄이기 위해
|
||||||
|
* DBG_PRINTF만 잠깐 비활성화한 상태다.
|
||||||
*/
|
*/
|
||||||
#define ENABLE_PRINTF 1
|
#include <zephyr/sys/printk.h>
|
||||||
|
|
||||||
#if ENABLE_PRINTF
|
#define DBG_PRINTF(...) printk(__VA_ARGS__)
|
||||||
#include <zephyr/sys/printk.h>
|
#define DBG_CORE(...) printk(__VA_ARGS__)
|
||||||
#define DBG_PRINTF(...) printk(__VA_ARGS__)
|
#define DBG_ERR(...) printk(__VA_ARGS__)
|
||||||
#else
|
|
||||||
#define DBG_PRINTF(...)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* DEBUG_PRINT_H */
|
#endif /* DEBUG_PRINT_H */
|
||||||
|
|||||||
@@ -0,0 +1,145 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* @file echo_adc.c
|
||||||
|
* @brief ADC121S051 echo capture driver using nrfx SPIM3
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <zephyr/drivers/gpio.h>
|
||||||
|
#include <zephyr/devicetree.h>
|
||||||
|
#include <zephyr/sys/util.h>
|
||||||
|
#include <nrfx_spim.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "echo_adc.h"
|
||||||
|
#include "debug_print.h"
|
||||||
|
|
||||||
|
#define ECHO_SCLK_NODE DT_NODELABEL(echo_sclk)
|
||||||
|
#define ECHO_MISO_NODE DT_NODELABEL(echo_miso)
|
||||||
|
#define ECHO_CS_NODE DT_NODELABEL(echo_cs)
|
||||||
|
|
||||||
|
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_cs = GPIO_DT_SPEC_GET(ECHO_CS_NODE, gpios);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 bool echo_adc_initialized;
|
||||||
|
static uint8_t raw_capture[ECHO_ADC_MAX_SAMPLES * 2];
|
||||||
|
|
||||||
|
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;
|
||||||
|
return NRF_GPIO_PIN_MAP(port, spec->pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int echo_adc_transfer_rx(uint8_t *buf, size_t len)
|
||||||
|
{
|
||||||
|
nrfx_spim_xfer_desc_t xfer = NRFX_SPIM_XFER_RX(buf, len);
|
||||||
|
int err = nrfx_spim_xfer(&echo_spim, &xfer, 0);
|
||||||
|
|
||||||
|
if (err != 0)
|
||||||
|
{
|
||||||
|
DBG_PRINTF("[ECHO] xfer fail err=%d\r\n", err);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int echo_adc_init(void)
|
||||||
|
{
|
||||||
|
if (echo_adc_initialized || nrfx_spim_init_check(&echo_spim))
|
||||||
|
{
|
||||||
|
echo_adc_initialized = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct gpio_dt_spec *pins[] = { &echo_sclk, &echo_miso, &echo_cs };
|
||||||
|
|
||||||
|
for (size_t i = 0; i < ARRAY_SIZE(pins); i++)
|
||||||
|
{
|
||||||
|
if (!device_is_ready(pins[i]->port))
|
||||||
|
{
|
||||||
|
DBG_PRINTF("[ECHO] GPIO device not ready (%d)\r\n", (int)i);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nrfx_spim_config_t config = NRFX_SPIM_DEFAULT_CONFIG(
|
||||||
|
gpio_abs_pin(&echo_sclk),
|
||||||
|
NRF_SPIM_PIN_NOT_CONNECTED,
|
||||||
|
gpio_abs_pin(&echo_miso),
|
||||||
|
gpio_abs_pin(&echo_cs));
|
||||||
|
|
||||||
|
config.frequency = NRFX_MHZ_TO_HZ(16);
|
||||||
|
config.mode = NRF_SPIM_MODE_3;
|
||||||
|
config.bit_order = NRF_SPIM_BIT_ORDER_MSB_FIRST;
|
||||||
|
config.orc = 0x00;
|
||||||
|
config.miso_pull = NRF_GPIO_PIN_NOPULL;
|
||||||
|
config.ss_active_high = false;
|
||||||
|
|
||||||
|
nrfx_err_t err = nrfx_spim_init(&echo_spim, &config, NULL, NULL);
|
||||||
|
if (err != NRFX_SUCCESS)
|
||||||
|
{
|
||||||
|
DBG_PRINTF("[ECHO] init fail err=%d\r\n", err);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo_adc_initialized = true;
|
||||||
|
DBG_PRINTF("[ECHO] SPIM3 init OK\r\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int echo_adc_wake(void)
|
||||||
|
{
|
||||||
|
int err = echo_adc_init();
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t discard[2];
|
||||||
|
return echo_adc_transfer_rx(discard, sizeof(discard));
|
||||||
|
}
|
||||||
|
|
||||||
|
int echo_adc_capture(uint16_t *samples, uint16_t num_samples)
|
||||||
|
{
|
||||||
|
if (samples == NULL)
|
||||||
|
{
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((num_samples == 0U) || (num_samples > ECHO_ADC_MAX_SAMPLES))
|
||||||
|
{
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int err = echo_adc_init();
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint16_t i = 0; i < num_samples; i++)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Keep the framing that matched the old firmware behavior:
|
||||||
|
* one 16-clock / 2-byte SPI frame per sample.
|
||||||
|
*/
|
||||||
|
err = echo_adc_transfer_rx(&raw_capture[i * 2U], 2U);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t raw16 = ((uint16_t)raw_capture[i * 2U] << 8) |
|
||||||
|
(uint16_t)raw_capture[i * 2U + 1U];
|
||||||
|
samples[i] = (raw16 >> 1) & 0x0FFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* @file echo_adc.h
|
||||||
|
* @brief ADC121S051 echo capture driver
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#ifndef ECHO_ADC_H__
|
||||||
|
#define ECHO_ADC_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define ECHO_ADC_MAX_SAMPLES 100
|
||||||
|
|
||||||
|
int echo_adc_init(void);
|
||||||
|
int echo_adc_wake(void);
|
||||||
|
int echo_adc_capture(uint16_t *samples, uint16_t num_samples);
|
||||||
|
|
||||||
|
#endif /* ECHO_ADC_H__ */
|
||||||
@@ -0,0 +1,219 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* @file piezo.c
|
||||||
|
* @brief Piezo TX burst, mux selection, and power control
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <zephyr/drivers/gpio.h>
|
||||||
|
#include <zephyr/devicetree.h>
|
||||||
|
#include <zephyr/sys/util.h>
|
||||||
|
#include <hal/nrf_gpio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "piezo.h"
|
||||||
|
#include "debug_print.h"
|
||||||
|
|
||||||
|
#define PIEZO_PWR_NODE DT_NODELABEL(piezo_pwr)
|
||||||
|
#define PIEZO_PE_NODE DT_NODELABEL(piezo_pe)
|
||||||
|
#define PIEZO_P_OUT_NODE DT_NODELABEL(piezo_p_out)
|
||||||
|
#define PIEZO_N_OUT_NODE DT_NODELABEL(piezo_n_out)
|
||||||
|
#define PIEZO_DMP_NODE DT_NODELABEL(piezo_dmp)
|
||||||
|
#define MUX_EN_A_NODE DT_NODELABEL(mux_en_a)
|
||||||
|
#define MUX_EN_B_NODE DT_NODELABEL(mux_en_b)
|
||||||
|
#define MUX_SEL0_NODE DT_NODELABEL(mux_sel0)
|
||||||
|
#define MUX_SEL1_NODE DT_NODELABEL(mux_sel1)
|
||||||
|
|
||||||
|
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_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_dmp = GPIO_DT_SPEC_GET(PIEZO_DMP_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_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);
|
||||||
|
|
||||||
|
struct mux_state {
|
||||||
|
uint8_t en_a;
|
||||||
|
uint8_t en_b;
|
||||||
|
uint8_t sel0;
|
||||||
|
uint8_t sel1;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct mux_state mux_map[PIEZO_NUM_CHANNELS] = {
|
||||||
|
{1, 0, 0, 0},
|
||||||
|
{1, 0, 1, 0},
|
||||||
|
{1, 0, 0, 1},
|
||||||
|
{1, 0, 1, 1},
|
||||||
|
{0, 1, 0, 0},
|
||||||
|
{0, 1, 1, 0},
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool piezo_initialized;
|
||||||
|
static uint32_t piezo_pe_abs;
|
||||||
|
static uint32_t piezo_p_out_abs;
|
||||||
|
static uint32_t piezo_n_out_abs;
|
||||||
|
static uint32_t piezo_dmp_abs;
|
||||||
|
static uint32_t piezo_pe_mask;
|
||||||
|
static uint32_t piezo_p_out_mask;
|
||||||
|
static uint32_t piezo_n_out_mask;
|
||||||
|
static uint32_t piezo_dmp_mask;
|
||||||
|
|
||||||
|
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;
|
||||||
|
return NRF_GPIO_PIN_MAP(port, spec->pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void burst_nop(void)
|
||||||
|
{
|
||||||
|
__asm__ volatile("nop");
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void nop_delay_9(void)
|
||||||
|
{
|
||||||
|
burst_nop(); burst_nop(); burst_nop();
|
||||||
|
burst_nop(); burst_nop(); burst_nop();
|
||||||
|
burst_nop(); burst_nop(); burst_nop();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void nop_delay_14(void)
|
||||||
|
{
|
||||||
|
burst_nop(); burst_nop(); burst_nop(); burst_nop();
|
||||||
|
burst_nop(); burst_nop(); burst_nop(); burst_nop();
|
||||||
|
burst_nop(); burst_nop(); burst_nop(); burst_nop();
|
||||||
|
burst_nop(); burst_nop();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void nop_delay_32(void)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 32; i++) {
|
||||||
|
burst_nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void piezo_drive_idle(void)
|
||||||
|
{
|
||||||
|
nrf_gpio_pin_clear(piezo_pe_abs);
|
||||||
|
nrf_gpio_pin_clear(piezo_p_out_abs);
|
||||||
|
nrf_gpio_pin_clear(piezo_n_out_abs);
|
||||||
|
nrf_gpio_pin_clear(piezo_dmp_abs);
|
||||||
|
|
||||||
|
gpio_pin_set_dt(&mux_en_a, 0);
|
||||||
|
gpio_pin_set_dt(&mux_en_b, 0);
|
||||||
|
gpio_pin_set_dt(&mux_sel0, 0);
|
||||||
|
gpio_pin_set_dt(&mux_sel1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int piezo_init(void)
|
||||||
|
{
|
||||||
|
if (piezo_initialized) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct gpio_dt_spec *pins[] = {
|
||||||
|
&piezo_pwr, &piezo_pe, &piezo_p_out, &piezo_n_out,
|
||||||
|
&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)) {
|
||||||
|
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) {
|
||||||
|
DBG_PRINTF("[PIEZO] GPIO init fail idx=%d err=%d\r\n", (int)i, err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
piezo_pe_abs = gpio_abs_pin(&piezo_pe);
|
||||||
|
piezo_p_out_abs = gpio_abs_pin(&piezo_p_out);
|
||||||
|
piezo_n_out_abs = gpio_abs_pin(&piezo_n_out);
|
||||||
|
piezo_dmp_abs = gpio_abs_pin(&piezo_dmp);
|
||||||
|
piezo_pe_mask = BIT(piezo_pe.pin);
|
||||||
|
piezo_p_out_mask = BIT(piezo_p_out.pin);
|
||||||
|
piezo_n_out_mask = BIT(piezo_n_out.pin);
|
||||||
|
piezo_dmp_mask = BIT(piezo_dmp.pin);
|
||||||
|
|
||||||
|
piezo_drive_idle();
|
||||||
|
piezo_initialized = true;
|
||||||
|
DBG_PRINTF("[PIEZO] Init OK\r\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void piezo_power_on(void)
|
||||||
|
{
|
||||||
|
if (!piezo_initialized && piezo_init() != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gpio_pin_set_dt(&piezo_pwr, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void piezo_power_off(void)
|
||||||
|
{
|
||||||
|
if (!piezo_initialized && piezo_init() != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
piezo_drive_idle();
|
||||||
|
gpio_pin_set_dt(&piezo_pwr, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int piezo_select_channel(uint8_t channel)
|
||||||
|
{
|
||||||
|
if (!piezo_initialized) {
|
||||||
|
int err = piezo_init();
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (channel >= PIEZO_NUM_CHANNELS) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
gpio_pin_set_dt(&mux_en_a, mux_map[channel].en_a);
|
||||||
|
gpio_pin_set_dt(&mux_en_b, mux_map[channel].en_b);
|
||||||
|
gpio_pin_set_dt(&mux_sel0, mux_map[channel].sel0);
|
||||||
|
gpio_pin_set_dt(&mux_sel1, mux_map[channel].sel1);
|
||||||
|
|
||||||
|
k_busy_wait(PIEZO_MUX_SETTLE_US);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void piezo_burst_sw(uint8_t cycles)
|
||||||
|
{
|
||||||
|
if (!piezo_initialized || cycles == 0U) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int key = irq_lock();
|
||||||
|
uint32_t saved_p1_out = NRF_P1->OUT;
|
||||||
|
uint32_t p1_ctrl_mask = piezo_p_out_mask | piezo_n_out_mask | piezo_dmp_mask;
|
||||||
|
uint32_t p1_all_low = saved_p1_out & ~p1_ctrl_mask;
|
||||||
|
uint32_t p1_p_high_n_low = p1_all_low | piezo_p_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;
|
||||||
|
|
||||||
|
NRF_P0->OUTCLR = piezo_pe_mask;
|
||||||
|
NRF_P1->OUT = p1_all_low;
|
||||||
|
NRF_P0->OUTSET = piezo_pe_mask;
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < cycles; i++) {
|
||||||
|
NRF_P1->OUT = p1_p_high_n_low;
|
||||||
|
nop_delay_14();
|
||||||
|
|
||||||
|
NRF_P1->OUT = p1_p_low_n_high;
|
||||||
|
nop_delay_9();
|
||||||
|
}
|
||||||
|
|
||||||
|
NRF_P1->OUT = p1_dmp_high;
|
||||||
|
nop_delay_32();
|
||||||
|
NRF_P1->OUT = p1_all_low;
|
||||||
|
NRF_P0->OUTCLR = piezo_pe_mask;
|
||||||
|
irq_unlock(key);
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* @file piezo.h
|
||||||
|
* @brief Piezo transmit and mux control driver
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#ifndef PIEZO_H__
|
||||||
|
#define PIEZO_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#define PIEZO_NUM_CHANNELS 6
|
||||||
|
#define PIEZO_SW_BURST_CYCLES 7
|
||||||
|
#define PIEZO_MUX_SETTLE_US 1300
|
||||||
|
#define PIEZO_POWER_STABILIZE_MS 3
|
||||||
|
#define PIEZO_BURST_TO_ADC_DELAY_US 10
|
||||||
|
|
||||||
|
int piezo_init(void);
|
||||||
|
void piezo_power_on(void);
|
||||||
|
void piezo_power_off(void);
|
||||||
|
int piezo_select_channel(uint8_t channel);
|
||||||
|
void piezo_burst_sw(uint8_t cycles);
|
||||||
|
|
||||||
|
#endif /* PIEZO_H__ */
|
||||||
@@ -57,13 +57,13 @@ static int16_t raw_to_cdeg(int16_t raw)
|
|||||||
int temp_init(void)
|
int temp_init(void)
|
||||||
{
|
{
|
||||||
if (!adc_is_ready_dt(&temp_adc)) {
|
if (!adc_is_ready_dt(&temp_adc)) {
|
||||||
DBG_PRINTF("[TEMP] FAIL — ADC device not ready\r\n");
|
DBG_ERR("[TEMP] FAIL — ADC device not ready\r\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int err = adc_channel_setup_dt(&temp_adc);
|
int err = adc_channel_setup_dt(&temp_adc);
|
||||||
if (err) {
|
if (err) {
|
||||||
DBG_PRINTF("[TEMP] FAIL — channel setup (err=%d)\r\n", err);
|
DBG_ERR("[TEMP] FAIL — channel setup (err=%d)\r\n", err);
|
||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ int16_t temp_read_cdeg(void)
|
|||||||
/* 매번 채널 재설정 (배터리 ADC와 SAADC 공유) */
|
/* 매번 채널 재설정 (배터리 ADC와 SAADC 공유) */
|
||||||
int err = adc_channel_setup_dt(&temp_adc);
|
int err = adc_channel_setup_dt(&temp_adc);
|
||||||
if (err) {
|
if (err) {
|
||||||
DBG_PRINTF("[TEMP] FAIL — channel setup (err=%d)\r\n", err);
|
DBG_ERR("[TEMP] FAIL — channel setup (err=%d)\r\n", err);
|
||||||
return INT16_MIN;
|
return INT16_MIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,7 +87,7 @@ int16_t temp_read_cdeg(void)
|
|||||||
|
|
||||||
err = adc_sequence_init_dt(&temp_adc, &seq);
|
err = adc_sequence_init_dt(&temp_adc, &seq);
|
||||||
if (err) {
|
if (err) {
|
||||||
DBG_PRINTF("[TEMP] FAIL — sequence init (err=%d)\r\n", err);
|
DBG_ERR("[TEMP] FAIL — sequence init (err=%d)\r\n", err);
|
||||||
return INT16_MIN;
|
return INT16_MIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ int16_t temp_read_cdeg(void)
|
|||||||
|
|
||||||
err = adc_read_dt(&temp_adc, &seq);
|
err = adc_read_dt(&temp_adc, &seq);
|
||||||
if (err) {
|
if (err) {
|
||||||
DBG_PRINTF("[TEMP] FAIL — read (err=%d)\r\n", err);
|
DBG_ERR("[TEMP] FAIL — read (err=%d)\r\n", err);
|
||||||
return INT16_MIN;
|
return INT16_MIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+170
-26
@@ -28,6 +28,15 @@
|
|||||||
|
|
||||||
LOG_MODULE_REGISTER(vesiscan, LOG_LEVEL_INF);
|
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
|
* Devicetree GPOP Settings
|
||||||
*============================================================================*/
|
*============================================================================*/
|
||||||
@@ -51,13 +60,14 @@ volatile bool processing = false; /* 센서 데이터 처리 중 플
|
|||||||
bool bond_data_delete = true; /* 본딩 데이터 삭제 요청 */
|
bool bond_data_delete = true; /* 본딩 데이터 삭제 요청 */
|
||||||
uint32_t m_life_cycle = 0; /* 디바이스 수명 카운터 */
|
uint32_t m_life_cycle = 0; /* 디바이스 수명 카운터 */
|
||||||
uint8_t m_reset_status = 1; /* 리셋 상태 코드 */
|
uint8_t m_reset_status = 1; /* 리셋 상태 코드 */
|
||||||
char SERIAL_NO[SERIAL_NO_LENGTH]; /* 시리얼 번호 */
|
char SERIAL_NO[SERIAL_NO_BUF_SIZE]; /* 시리얼 번호 */
|
||||||
char HW_NO[HW_NO_LENGTH]; /* 하드웨어 번호 */
|
char HW_NO[HW_NO_BUF_SIZE]; /* 하드웨어 번호 */
|
||||||
char m_static_passkey[PASSKEY_LENGTH]; /* BLE 정적 패스키 */
|
char m_static_passkey[PASSKEY_BUF_SIZE]; /* BLE 정적 패스키 */
|
||||||
|
|
||||||
static uint16_t cnt_s; /* 전원 버튼 폴링 카운터 (5ms 단위) */
|
static uint16_t cnt_s; /* 전원 버튼 폴링 카운터 (5ms 단위) */
|
||||||
static bool device_on = false; /* 디바이스 전원 상태 (래치 완료 여부) */
|
static bool device_on = false; /* 디바이스 전원 상태 (래치 완료 여부) */
|
||||||
static bool boot_btn_released = false; /* 부팅 후 버튼 놓았는지 여부 */
|
static bool boot_btn_released = false; /* 부팅 후 버튼 놓았는지 여부 */
|
||||||
|
static bool power_btn_suspended; /* 측정 중 전원 버튼 상태머신 일시 정지 */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* BLE advertising 제어용 워크 아이템
|
* BLE advertising 제어용 워크 아이템
|
||||||
@@ -72,6 +82,56 @@ static bool boot_btn_released = false; /* 부팅 후 버튼 놓았는지 여
|
|||||||
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;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 시작 */
|
/* 시스템 워크큐에서 BLE advertising 시작 */
|
||||||
static void adv_start_work_handler(struct k_work *work)
|
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)
|
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++)
|
for (uint16_t i = 0; i < len && i < 32; i++)
|
||||||
{
|
{
|
||||||
DBG_PRINTF("%02X ", data[i]);
|
DBG_CORE(" %02X", data[i]);
|
||||||
}
|
}
|
||||||
if (len > 32)
|
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) {
|
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 → 전원 래치 해제 → 전원 차단 */
|
||||||
DBG_PRINTF("[PWR] OFF\r\n");
|
|
||||||
} 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");
|
||||||
@@ -143,16 +235,33 @@ static void gpio_init(void)
|
|||||||
*============================================================================*/
|
*============================================================================*/
|
||||||
static void load_default_config(void)
|
static void load_default_config(void)
|
||||||
{
|
{
|
||||||
memset(SERIAL_NO, 0, SERIAL_NO_LENGTH);
|
size_t serial_len = strlen(SERIAL_NUMBER);
|
||||||
memcpy(SERIAL_NO, SERIAL_NUMBER, strlen(SERIAL_NUMBER));
|
size_t hw_len = strlen(HARDWARE_VERSION);
|
||||||
|
size_t passkey_len = strlen(DEFAULT_PASSKEY);
|
||||||
|
|
||||||
memset(m_static_passkey, 0, PASSKEY_LENGTH);
|
if (serial_len > SERIAL_NO_LENGTH) {
|
||||||
memcpy(m_static_passkey, DEFAULT_PASSKEY, PASSKEY_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;
|
m_reset_status = 1;
|
||||||
bond_data_delete = true;
|
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);
|
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 폴링)
|
* 전원 버튼 상태머신 (5ms 폴링)
|
||||||
*
|
*
|
||||||
@@ -209,7 +330,7 @@ static void main_s(struct k_timer *timer)
|
|||||||
{
|
{
|
||||||
if (!button_pressed) /* 버튼 놓음 → 2초 미만이면 전원 OFF */
|
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);
|
power_control_handler(OFF);
|
||||||
cnt_s = 0;
|
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_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_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_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();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,11 +426,28 @@ int main(void)
|
|||||||
power_hold_init();
|
power_hold_init();
|
||||||
cnt_s = 0;
|
cnt_s = 0;
|
||||||
|
|
||||||
DBG_PRINTF("\r\n========================================\r\n");
|
/*
|
||||||
DBG_PRINTF(" VesiScan BASIC %s (Zephyr)\r\n", FIRMWARE_VERSION);
|
* 현재는 전원 버튼/mbb 문제를 같이 디버깅 중이라
|
||||||
DBG_PRINTF("========================================\r\n");
|
* "버튼을 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();
|
gpio_init();
|
||||||
timers_init();
|
timers_init();
|
||||||
load_default_config();
|
load_default_config();
|
||||||
@@ -312,25 +456,25 @@ int main(void)
|
|||||||
battery_timer_init();
|
battery_timer_init();
|
||||||
imu_init();
|
imu_init();
|
||||||
temp_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 ──*/
|
/*── 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)
|
if (ble_service_init(ble_rx_handler) == 0)
|
||||||
{
|
{
|
||||||
DBG_PRINTF(" ble/nus OK\r\n");
|
DBG_CORE(" ble/nus OK\r\n");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
DBG_PRINTF(" ble FAIL\r\n");
|
DBG_ERR(" ble FAIL\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/*── Phase 3: FDS/NVS (TODO) ──*/
|
/*── Phase 3: FDS/NVS (TODO) ──*/
|
||||||
/*── Phase 4: 애플리케이션 (TODO) ──*/
|
/*── Phase 4: 애플리케이션 (TODO) ──*/
|
||||||
|
|
||||||
DBG_PRINTF("\r\n========================================\r\n");
|
DBG_CORE("\r\n========================================\r\n");
|
||||||
DBG_PRINTF(" READY [%s]\r\n", SERIAL_NO);
|
DBG_CORE(" READY [%s]\r\n", SERIAL_NO);
|
||||||
DBG_PRINTF("========================================\r\n\r\n");
|
DBG_CORE("========================================\r\n\r\n");
|
||||||
|
|
||||||
/* 전원 버튼 상태머신 시작 (부팅 시 버튼이 눌려있는 상태) */
|
/* 전원 버튼 상태머신 시작 (부팅 시 버튼이 눌려있는 상태) */
|
||||||
timers_start();
|
timers_start();
|
||||||
|
|||||||
+9
-5
@@ -15,9 +15,9 @@
|
|||||||
/*==============================================================================
|
/*==============================================================================
|
||||||
* Firmware identification
|
* Firmware identification
|
||||||
*============================================================================*/
|
*============================================================================*/
|
||||||
#define FIRMWARE_VERSION "VBTFW0102"
|
#define FIRMWARE_VERSION "TSTFW042"
|
||||||
#define HARDWARE_VERSION "VB0HW0000"
|
#define HARDWARE_VERSION "VB0HW0000"
|
||||||
#define SERIAL_NUMBER "VB026030000"
|
#define SERIAL_NUMBER "VB0260300ZZ" // 앱이 실제로 쓰는 시리얼/광고 이름
|
||||||
#define DEFAULT_PASSKEY "123456"
|
#define DEFAULT_PASSKEY "123456"
|
||||||
|
|
||||||
/*==============================================================================
|
/*==============================================================================
|
||||||
@@ -26,6 +26,9 @@
|
|||||||
#define SERIAL_NO_LENGTH 12
|
#define SERIAL_NO_LENGTH 12
|
||||||
#define HW_NO_LENGTH 12
|
#define HW_NO_LENGTH 12
|
||||||
#define PASSKEY_LENGTH 6
|
#define PASSKEY_LENGTH 6
|
||||||
|
#define SERIAL_NO_BUF_SIZE (SERIAL_NO_LENGTH + 1)
|
||||||
|
#define HW_NO_BUF_SIZE (HW_NO_LENGTH + 1)
|
||||||
|
#define PASSKEY_BUF_SIZE (PASSKEY_LENGTH + 1)
|
||||||
|
|
||||||
/*==============================================================================
|
/*==============================================================================
|
||||||
* Enumerations
|
* Enumerations
|
||||||
@@ -61,15 +64,16 @@ typedef enum
|
|||||||
|
|
||||||
void sleep_mode_enter(void);
|
void sleep_mode_enter(void);
|
||||||
void device_power_off(void);
|
void device_power_off(void);
|
||||||
|
void power_button_suspend(bool suspend);
|
||||||
|
|
||||||
/*==============================================================================
|
/*==============================================================================
|
||||||
* Global variables (extern)
|
* Global variables (extern)
|
||||||
*============================================================================*/
|
*============================================================================*/
|
||||||
extern volatile bool ble_connection_st;
|
extern volatile bool ble_connection_st;
|
||||||
extern volatile bool processing;
|
extern volatile bool processing;
|
||||||
extern char SERIAL_NO[SERIAL_NO_LENGTH];
|
extern char SERIAL_NO[SERIAL_NO_BUF_SIZE];
|
||||||
extern char HW_NO[HW_NO_LENGTH];
|
extern char HW_NO[HW_NO_BUF_SIZE];
|
||||||
extern char m_static_passkey[PASSKEY_LENGTH];
|
extern char m_static_passkey[PASSKEY_BUF_SIZE];
|
||||||
extern bool bond_data_delete;
|
extern bool bond_data_delete;
|
||||||
extern uint32_t m_life_cycle;
|
extern uint32_t m_life_cycle;
|
||||||
extern uint8_t m_reset_status;
|
extern uint8_t m_reset_status;
|
||||||
|
|||||||
+850
-10
@@ -9,6 +9,7 @@
|
|||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
#include <zephyr/kernel.h>
|
#include <zephyr/kernel.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
@@ -16,7 +17,41 @@
|
|||||||
#include "ble_service.h"
|
#include "ble_service.h"
|
||||||
#include "battery_adc.h"
|
#include "battery_adc.h"
|
||||||
#include "led_control.h"
|
#include "led_control.h"
|
||||||
|
#include "echo_adc.h"
|
||||||
#include "imu_i2c.h"
|
#include "imu_i2c.h"
|
||||||
|
#include "piezo.h"
|
||||||
|
#include "tmp235.h"
|
||||||
|
|
||||||
|
/*==============================================================================
|
||||||
|
* Piezo / echo measurement constants
|
||||||
|
*============================================================================*/
|
||||||
|
#define PIEZO_AVERAGE_COUNT 10
|
||||||
|
#define ECHO_STATUS_OK 0x0000
|
||||||
|
#define ECHO_STATUS_PIEZO 0x0001
|
||||||
|
#define ECHO_STATUS_ADC_INIT 0x0002
|
||||||
|
#define ECHO_STATUS_MUX 0x0003
|
||||||
|
#define ECHO_STATUS_CAPTURE 0x0004
|
||||||
|
#define ECHO_STATUS_BATT 0x0005
|
||||||
|
#define ECHO_STATUS_IMU 0x0006
|
||||||
|
#define ECHO_STATUS_TEMP 0x0007
|
||||||
|
|
||||||
|
#define PIEZO_CFG_FREQ_DEFAULT 1
|
||||||
|
#define PIEZO_CFG_CYCLES_DEFAULT 7
|
||||||
|
#define PIEZO_CFG_DELAY_DEFAULT 10
|
||||||
|
#define PIEZO_CFG_SAMPLES_DEFAULT 100
|
||||||
|
#define PIEZO_CFG_AVG_DEFAULT 10
|
||||||
|
#define PIEZO_POST_SELECT_SETTLE_US 500
|
||||||
|
#define PIEZO_AVG_INTER_BURST_GAP_US 500
|
||||||
|
|
||||||
|
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];
|
||||||
|
static uint8_t tx_u16_buf[8];
|
||||||
|
static uint8_t tx_imu_buf[18];
|
||||||
|
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];
|
||||||
|
|
||||||
/*==============================================================================
|
/*==============================================================================
|
||||||
* CRC16 (CRC-CCITT, Nordic SDK 호환)
|
* CRC16 (CRC-CCITT, Nordic SDK 호환)
|
||||||
@@ -36,14 +71,47 @@ static uint16_t dr_crc16_compute(const uint8_t *p_data, uint32_t size)
|
|||||||
return crc;
|
return crc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool get_data_u16_be(const uint8_t *data, uint8_t data_len,
|
||||||
|
uint8_t word_index, uint16_t *out)
|
||||||
|
{
|
||||||
|
uint8_t offset = (uint8_t)(word_index * 2U);
|
||||||
|
|
||||||
|
if ((offset + 1U) >= data_len) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out = ((uint16_t)data[offset] << 8) | (uint16_t)data[offset + 1U];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void copy_fixed_ascii(char *dst, size_t dst_len,
|
||||||
|
const char *src, size_t src_len)
|
||||||
|
{
|
||||||
|
if (src_len > dst_len) {
|
||||||
|
src_len = dst_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(dst, 0, dst_len);
|
||||||
|
memcpy(dst, src, src_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char ascii_to_lower(char ch)
|
||||||
|
{
|
||||||
|
if ((ch >= 'A') && (ch <= 'Z')) {
|
||||||
|
return (char)(ch - 'A' + 'a');
|
||||||
|
}
|
||||||
|
|
||||||
|
return ch;
|
||||||
|
}
|
||||||
|
|
||||||
/*==============================================================================
|
/*==============================================================================
|
||||||
* 응답 패킷 전송
|
* 응답 패킷 전송
|
||||||
*============================================================================*/
|
*============================================================================*/
|
||||||
|
|
||||||
/* TAG(4B) + uint16 값(2B) + CRC16(2B) = 8바이트 전송 */
|
/* TAG(4B) + uint16 값(2B) + CRC16(2B) = 8바이트 전송 */
|
||||||
static void send_response_u16(const char *tag, uint16_t value)
|
static int send_response_u16(const char *tag, uint16_t value)
|
||||||
{
|
{
|
||||||
uint8_t buf[8];
|
uint8_t *buf = tx_u16_buf;
|
||||||
|
|
||||||
buf[0] = tag[0];
|
buf[0] = tag[0];
|
||||||
buf[1] = tag[1];
|
buf[1] = tag[1];
|
||||||
@@ -56,7 +124,44 @@ static void send_response_u16(const char *tag, uint16_t value)
|
|||||||
buf[6] = (uint8_t)(crc & 0xFF);
|
buf[6] = (uint8_t)(crc & 0xFF);
|
||||||
buf[7] = (uint8_t)(crc >> 8);
|
buf[7] = (uint8_t)(crc >> 8);
|
||||||
|
|
||||||
ble_data_send(buf, 8);
|
return ble_data_send(buf, 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int send_response_ascii(const char *tag, const char *value, uint8_t value_len)
|
||||||
|
{
|
||||||
|
uint8_t *buf = tx_ascii_buf;
|
||||||
|
|
||||||
|
buf[0] = tag[0];
|
||||||
|
buf[1] = tag[1];
|
||||||
|
buf[2] = tag[2];
|
||||||
|
buf[3] = tag[3];
|
||||||
|
memcpy(&buf[4], value, value_len);
|
||||||
|
|
||||||
|
uint16_t crc = dr_crc16_compute(buf, (uint32_t)(4U + value_len));
|
||||||
|
buf[4 + value_len] = (uint8_t)(crc & 0xFF);
|
||||||
|
buf[5 + value_len] = (uint8_t)(crc >> 8);
|
||||||
|
|
||||||
|
return ble_data_send(buf, (uint16_t)(6U + value_len));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int send_response_tag_echo(const char *tag, const char *echo_tag)
|
||||||
|
{
|
||||||
|
uint8_t *buf = tx_ascii_buf;
|
||||||
|
|
||||||
|
buf[0] = tag[0];
|
||||||
|
buf[1] = tag[1];
|
||||||
|
buf[2] = tag[2];
|
||||||
|
buf[3] = tag[3];
|
||||||
|
buf[4] = echo_tag[0];
|
||||||
|
buf[5] = echo_tag[1];
|
||||||
|
buf[6] = echo_tag[2];
|
||||||
|
buf[7] = echo_tag[3];
|
||||||
|
|
||||||
|
uint16_t crc = dr_crc16_compute(buf, 8);
|
||||||
|
buf[8] = (uint8_t)(crc & 0xFF);
|
||||||
|
buf[9] = (uint8_t)(crc >> 8);
|
||||||
|
|
||||||
|
return ble_data_send(buf, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*==============================================================================
|
/*==============================================================================
|
||||||
@@ -65,9 +170,9 @@ static void send_response_u16(const char *tag, uint16_t value)
|
|||||||
|
|
||||||
/* TAG(4B) + int16×6 빅엔디안(12B) + CRC16(2B) = 18바이트 전송
|
/* TAG(4B) + int16×6 빅엔디안(12B) + CRC16(2B) = 18바이트 전송
|
||||||
* 기존 format_data() + dr_binary_tx_safe(buf, 8) 방식과 동일한 레이아웃 */
|
* 기존 format_data() + dr_binary_tx_safe(buf, 8) 방식과 동일한 레이아웃 */
|
||||||
static void send_response_imu(const int16_t accel[3], const int16_t gyro[3])
|
static int send_response_imu(const int16_t accel[3], const int16_t gyro[3])
|
||||||
{
|
{
|
||||||
uint8_t buf[18];
|
uint8_t *buf = tx_imu_buf;
|
||||||
|
|
||||||
buf[0] = 'r'; buf[1] = 's'; buf[2] = 'p'; buf[3] = ':';
|
buf[0] = 'r'; buf[1] = 's'; buf[2] = 'p'; buf[3] = ':';
|
||||||
|
|
||||||
@@ -84,7 +189,244 @@ static void send_response_imu(const int16_t accel[3], const int16_t gyro[3])
|
|||||||
buf[16] = (uint8_t)(crc & 0xFF);
|
buf[16] = (uint8_t)(crc & 0xFF);
|
||||||
buf[17] = (uint8_t)(crc >> 8);
|
buf[17] = (uint8_t)(crc >> 8);
|
||||||
|
|
||||||
ble_data_send(buf, 18);
|
return ble_data_send(buf, 18);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void send_response_echo(const uint16_t *samples, uint16_t num_samples)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* reb: 패킷은 최대 208바이트라서 스택 지역변수로 두면
|
||||||
|
* mbb?처럼 반복 호출할 때 워커 스택을 꽤 먹는다.
|
||||||
|
* 현재는 한 번에 하나의 명령만 처리하므로 정적 버퍼를 재사용한다.
|
||||||
|
*/
|
||||||
|
uint8_t *buf = tx_echo_buf;
|
||||||
|
|
||||||
|
buf[0] = 'r'; buf[1] = 'e'; buf[2] = 'b'; buf[3] = ':';
|
||||||
|
buf[4] = (uint8_t)(num_samples >> 8);
|
||||||
|
buf[5] = (uint8_t)(num_samples & 0xFF);
|
||||||
|
|
||||||
|
for (uint16_t i = 0; i < num_samples; i++)
|
||||||
|
{
|
||||||
|
buf[6 + i * 2] = (uint8_t)(samples[i] >> 8);
|
||||||
|
buf[7 + i * 2] = (uint8_t)(samples[i] & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t payload_len = 4 + 2 + (num_samples * 2);
|
||||||
|
uint16_t crc = dr_crc16_compute(buf, payload_len);
|
||||||
|
buf[payload_len] = (uint8_t)(crc & 0xFF);
|
||||||
|
buf[payload_len + 1] = (uint8_t)(crc >> 8);
|
||||||
|
|
||||||
|
ble_data_send(buf, payload_len + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void send_response_bundle(uint16_t batt_mv,
|
||||||
|
const int16_t accel[3],
|
||||||
|
const int16_t gyro[3],
|
||||||
|
int16_t temp_cdeg)
|
||||||
|
{
|
||||||
|
uint8_t *buf = tx_bundle_buf;
|
||||||
|
|
||||||
|
buf[0] = 'r'; buf[1] = 'b'; buf[2] = 'b'; buf[3] = ':';
|
||||||
|
buf[4] = (uint8_t)(batt_mv >> 8);
|
||||||
|
buf[5] = (uint8_t)(batt_mv & 0xFF);
|
||||||
|
|
||||||
|
const int16_t imu_vals[6] = {
|
||||||
|
accel[0], accel[1], accel[2],
|
||||||
|
gyro[0], gyro[1], gyro[2]
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < 6; i++)
|
||||||
|
{
|
||||||
|
buf[6 + i * 2] = (uint8_t)((uint16_t)imu_vals[i] >> 8);
|
||||||
|
buf[7 + i * 2] = (uint8_t)((uint16_t)imu_vals[i] & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[18] = (uint8_t)((uint16_t)temp_cdeg >> 8);
|
||||||
|
buf[19] = (uint8_t)((uint16_t)temp_cdeg & 0xFF);
|
||||||
|
|
||||||
|
uint16_t crc = dr_crc16_compute(buf, 20);
|
||||||
|
buf[20] = (uint8_t)(crc & 0xFF);
|
||||||
|
buf[21] = (uint8_t)(crc >> 8);
|
||||||
|
|
||||||
|
ble_data_send(buf, sizeof(tx_bundle_buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void send_response_piezo_config(uint16_t freq,
|
||||||
|
uint16_t cycles,
|
||||||
|
uint16_t avg,
|
||||||
|
uint16_t delay_us,
|
||||||
|
uint16_t samples)
|
||||||
|
{
|
||||||
|
uint8_t *buf = tx_cfg_buf;
|
||||||
|
|
||||||
|
buf[0] = 'r'; buf[1] = 'c'; buf[2] = 'f'; buf[3] = ':';
|
||||||
|
buf[4] = (uint8_t)(freq >> 8);
|
||||||
|
buf[5] = (uint8_t)(freq & 0xFF);
|
||||||
|
buf[6] = (uint8_t)(cycles >> 8);
|
||||||
|
buf[7] = (uint8_t)(cycles & 0xFF);
|
||||||
|
buf[8] = (uint8_t)(avg >> 8);
|
||||||
|
buf[9] = (uint8_t)(avg & 0xFF);
|
||||||
|
buf[10] = (uint8_t)(delay_us >> 8);
|
||||||
|
buf[11] = (uint8_t)(delay_us & 0xFF);
|
||||||
|
buf[12] = (uint8_t)(samples >> 8);
|
||||||
|
buf[13] = (uint8_t)(samples & 0xFF);
|
||||||
|
|
||||||
|
uint16_t crc = dr_crc16_compute(buf, 14);
|
||||||
|
buf[14] = (uint8_t)(crc & 0xFF);
|
||||||
|
buf[15] = (uint8_t)(crc >> 8);
|
||||||
|
|
||||||
|
ble_data_send(buf, sizeof(tx_cfg_buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int start_piezo_session(void)
|
||||||
|
{
|
||||||
|
DBG_PRINTF("[MBB] piezo session start\r\n");
|
||||||
|
|
||||||
|
int err = piezo_init();
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
DBG_PRINTF("[PIEZO] init fail err=%d\r\n", err);
|
||||||
|
return ECHO_STATUS_PIEZO;
|
||||||
|
}
|
||||||
|
|
||||||
|
piezo_power_on();
|
||||||
|
k_msleep(PIEZO_POWER_STABILIZE_MS);
|
||||||
|
|
||||||
|
err = echo_adc_init();
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
DBG_PRINTF("[ECHO] init fail err=%d\r\n", err);
|
||||||
|
return ECHO_STATUS_ADC_INIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = echo_adc_wake();
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
DBG_PRINTF("[ECHO] wake fail err=%d\r\n", err);
|
||||||
|
return ECHO_STATUS_ADC_INIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBG_PRINTF("[MBB] piezo session ready\r\n");
|
||||||
|
return ECHO_STATUS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int perform_piezo_sweep(void)
|
||||||
|
{
|
||||||
|
for (uint8_t ch = 0; ch < PIEZO_NUM_CHANNELS; ch++)
|
||||||
|
{
|
||||||
|
DBG_PRINTF("[MBB] sweep ch=%d start\r\n", ch);
|
||||||
|
|
||||||
|
int err = piezo_select_channel(ch);
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
DBG_PRINTF("[PIEZO] mux fail ch=%d err=%d\r\n", ch, err);
|
||||||
|
return ECHO_STATUS_MUX;
|
||||||
|
}
|
||||||
|
|
||||||
|
k_busy_wait(PIEZO_POST_SELECT_SETTLE_US);
|
||||||
|
|
||||||
|
err = echo_adc_wake();
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
DBG_PRINTF("[ECHO] dummy read fail ch=%d err=%d\r\n", ch, err);
|
||||||
|
return ECHO_STATUS_CAPTURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(echo_accum, 0, sizeof(echo_accum));
|
||||||
|
|
||||||
|
for (uint8_t avg = 0; avg < PIEZO_AVERAGE_COUNT; 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);
|
||||||
|
|
||||||
|
err = echo_adc_capture(echo_capture, ECHO_ADC_MAX_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++)
|
||||||
|
{
|
||||||
|
echo_accum[i] += echo_capture[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint16_t i = 0; i < ECHO_ADC_MAX_SAMPLES; i++)
|
||||||
|
{
|
||||||
|
piezo_channels[ch][i] = (uint16_t)(echo_accum[i] / PIEZO_AVERAGE_COUNT);
|
||||||
|
}
|
||||||
|
|
||||||
|
DBG_PRINTF("[MBB] sweep ch=%d done\r\n", ch);
|
||||||
|
}
|
||||||
|
|
||||||
|
DBG_PRINTF("[MBB] sweep done\r\n");
|
||||||
|
return ECHO_STATUS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int perform_single_piezo_capture(uint8_t cycles,
|
||||||
|
uint16_t delay_us,
|
||||||
|
uint16_t num_samples,
|
||||||
|
uint16_t averaging,
|
||||||
|
uint8_t channel)
|
||||||
|
{
|
||||||
|
if (channel >= PIEZO_NUM_CHANNELS) {
|
||||||
|
return ECHO_STATUS_MUX;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((num_samples == 0U) || (num_samples > ECHO_ADC_MAX_SAMPLES)) {
|
||||||
|
return ECHO_STATUS_CAPTURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (averaging == 0U) {
|
||||||
|
averaging = 1U;
|
||||||
|
}
|
||||||
|
|
||||||
|
int err = piezo_select_channel(channel);
|
||||||
|
if (err) {
|
||||||
|
return ECHO_STATUS_MUX;
|
||||||
|
}
|
||||||
|
|
||||||
|
k_busy_wait(PIEZO_POST_SELECT_SETTLE_US);
|
||||||
|
|
||||||
|
err = echo_adc_wake();
|
||||||
|
if (err) {
|
||||||
|
return ECHO_STATUS_CAPTURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(echo_accum, 0, sizeof(echo_accum));
|
||||||
|
|
||||||
|
for (uint16_t avg = 0; avg < averaging; avg++)
|
||||||
|
{
|
||||||
|
if (avg > 0U) {
|
||||||
|
k_busy_wait(PIEZO_AVG_INTER_BURST_GAP_US);
|
||||||
|
}
|
||||||
|
|
||||||
|
piezo_burst_sw(cycles);
|
||||||
|
k_busy_wait(delay_us);
|
||||||
|
|
||||||
|
err = echo_adc_capture(echo_capture, num_samples);
|
||||||
|
if (err) {
|
||||||
|
return ECHO_STATUS_CAPTURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint16_t i = 0; i < num_samples; i++)
|
||||||
|
{
|
||||||
|
echo_accum[i] += echo_capture[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint16_t i = 0; i < num_samples; i++)
|
||||||
|
{
|
||||||
|
echo_capture[i] = (uint16_t)(echo_accum[i] / averaging);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ECHO_STATUS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*==============================================================================
|
/*==============================================================================
|
||||||
@@ -95,6 +437,9 @@ static void send_response_imu(const int16_t accel[3], const int16_t gyro[3])
|
|||||||
|
|
||||||
static int cmd_msn(const uint8_t *data, uint8_t data_len)
|
static int cmd_msn(const uint8_t *data, uint8_t data_len)
|
||||||
{
|
{
|
||||||
|
ARG_UNUSED(data);
|
||||||
|
ARG_UNUSED(data_len);
|
||||||
|
|
||||||
int mv = battery_read_mv();
|
int mv = battery_read_mv();
|
||||||
if (mv < 0)
|
if (mv < 0)
|
||||||
{
|
{
|
||||||
@@ -109,6 +454,9 @@ static int cmd_msn(const uint8_t *data, uint8_t data_len)
|
|||||||
/* msp? → IMU 1회 측정 → rsp: + accel XYZ + gyro XYZ (각 int16 빅엔디안) */
|
/* msp? → IMU 1회 측정 → rsp: + accel XYZ + gyro XYZ (각 int16 빅엔디안) */
|
||||||
static int cmd_msp(const uint8_t *data, uint8_t data_len)
|
static int cmd_msp(const uint8_t *data, uint8_t data_len)
|
||||||
{
|
{
|
||||||
|
ARG_UNUSED(data);
|
||||||
|
ARG_UNUSED(data_len);
|
||||||
|
|
||||||
int16_t accel[3], gyro[3];
|
int16_t accel[3], gyro[3];
|
||||||
|
|
||||||
int ret = imu_read(accel, gyro);
|
int ret = imu_read(accel, gyro);
|
||||||
@@ -122,10 +470,464 @@ static int cmd_msp(const uint8_t *data, uint8_t data_len)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* mst? → 피에조 전원 ON → TMP235 온도 측정 → 전원 OFF → rso: + 온도(°C × 100, BE)
|
||||||
|
*
|
||||||
|
* TMP235가 피에조 레일을 공유하므로 ON/OFF 시퀀스를 한 커맨드에서 처리.
|
||||||
|
* 안정화 대기 10ms: TMP235 start-up(~2ms) + 레일 RC 필터 여유분.
|
||||||
|
* 에러 응답: 0xFFFF = ADC 읽기 실패 */
|
||||||
|
static int cmd_mst(const uint8_t *data, uint8_t data_len)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(data);
|
||||||
|
ARG_UNUSED(data_len);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* mst?는 "온도만 읽는 명령"처럼 보이지만,
|
||||||
|
* 실제로는 TMP235가 piezo 전원 레일을 같이 쓰기 때문에
|
||||||
|
* 전원 ON/OFF 시퀀스까지 같이 처리해야 한다.
|
||||||
|
*/
|
||||||
|
power_button_suspend(true);
|
||||||
|
|
||||||
|
if (piezo_init() != 0)
|
||||||
|
{
|
||||||
|
power_button_suspend(false);
|
||||||
|
send_response_u16("rso:", 0xFFFF);
|
||||||
|
DBG_PRINTF("[CMD] mst: piezo init fail\r\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 전원 ON → 센서 안정화 대기 */
|
||||||
|
piezo_power_on();
|
||||||
|
k_msleep(10);
|
||||||
|
|
||||||
|
int16_t t_cdeg = temp_read_cdeg();
|
||||||
|
|
||||||
|
/* 전원 OFF (측정 완료, 레일 끄기) */
|
||||||
|
piezo_power_off();
|
||||||
|
power_button_suspend(false);
|
||||||
|
|
||||||
|
/* ADC 읽기 실패 → 에러 코드 0xFFFF */
|
||||||
|
if (t_cdeg == INT16_MIN)
|
||||||
|
{
|
||||||
|
send_response_u16("rso:", 0xFFFF);
|
||||||
|
DBG_PRINTF("[CMD] mst: temp read fail\r\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 음수 온도도 2's complement로 그대로 전송 (앱이 int16로 해석) */
|
||||||
|
send_response_u16("rso:", (uint16_t)t_cdeg);
|
||||||
|
DBG_PRINTF("[CMD] mst -> %d.%02d C\r\n",
|
||||||
|
t_cdeg / 100,
|
||||||
|
(t_cdeg < 0 ? -t_cdeg : t_cdeg) % 100);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
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");
|
||||||
|
send_response_u16("rpa:", 0);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBG_CORE("[MPA] piezo_init ok\r\n");
|
||||||
|
piezo_power_on();
|
||||||
|
DBG_CORE("[MPA] power on\r\n");
|
||||||
|
send_response_u16("rpa:", 1);
|
||||||
|
DBG_CORE("[MPA] response sent\r\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_mpb(const uint8_t *data, uint8_t data_len)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(data);
|
||||||
|
ARG_UNUSED(data_len);
|
||||||
|
|
||||||
|
/* mpb?: piezo TX/RX 전원 레일을 끈다. */
|
||||||
|
DBG_CORE("[MPB] enter\r\n");
|
||||||
|
if (piezo_init() != 0) {
|
||||||
|
DBG_ERR("[MPB] piezo_init failed\r\n");
|
||||||
|
send_response_u16("rpb:", 0);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBG_CORE("[MPB] piezo_init ok\r\n");
|
||||||
|
piezo_power_off();
|
||||||
|
DBG_CORE("[MPB] power off\r\n");
|
||||||
|
send_response_u16("rpb:", 1);
|
||||||
|
DBG_CORE("[MPB] response sent\r\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_mpc(const uint8_t *data, uint8_t data_len)
|
||||||
|
{
|
||||||
|
uint16_t cycles = 5;
|
||||||
|
uint16_t freq_option = 1;
|
||||||
|
uint16_t piezo_ch = 0;
|
||||||
|
|
||||||
|
get_data_u16_be(data, data_len, 0, &cycles);
|
||||||
|
get_data_u16_be(data, data_len, 1, &freq_option);
|
||||||
|
get_data_u16_be(data, data_len, 2, &piezo_ch);
|
||||||
|
|
||||||
|
ARG_UNUSED(freq_option);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* mpc?: burst만 한 번 발생시키는 테스트 명령.
|
||||||
|
* echo를 읽지 않으므로 "초음파가 나가는지"만 빠르게 볼 때 쓴다.
|
||||||
|
*/
|
||||||
|
if ((cycles < 3U) || (cycles > 7U))
|
||||||
|
{
|
||||||
|
send_response_u16("rpc:", 2);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
power_button_suspend(true);
|
||||||
|
|
||||||
|
if (piezo_init() != 0) {
|
||||||
|
power_button_suspend(false);
|
||||||
|
send_response_u16("rpc:", 0);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
piezo_power_on();
|
||||||
|
|
||||||
|
if (piezo_select_channel((uint8_t)(piezo_ch % PIEZO_NUM_CHANNELS)) != 0) {
|
||||||
|
piezo_power_off();
|
||||||
|
power_button_suspend(false);
|
||||||
|
send_response_u16("rpc:", 0);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 현재 Zephyr 포팅본은 2.1MHz SW burst 하나만 구현되어 있다.
|
||||||
|
* 레거시의 freq_option 값은 받아두되, 아직은 같은 burst 함수로 처리한다.
|
||||||
|
*/
|
||||||
|
piezo_burst_sw((uint8_t)cycles);
|
||||||
|
piezo_power_off();
|
||||||
|
power_button_suspend(false);
|
||||||
|
|
||||||
|
send_response_u16("rpc:", cycles);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
uint16_t piezo_ch = 0;
|
||||||
|
|
||||||
|
get_data_u16_be(data, data_len, 0, &freq_option);
|
||||||
|
get_data_u16_be(data, data_len, 1, &delay_us);
|
||||||
|
get_data_u16_be(data, data_len, 2, &num_samples);
|
||||||
|
get_data_u16_be(data, data_len, 3, &cycles);
|
||||||
|
get_data_u16_be(data, data_len, 4, &averaging);
|
||||||
|
get_data_u16_be(data, data_len, 5, &piezo_ch);
|
||||||
|
|
||||||
|
ARG_UNUSED(freq_option);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* mec?: 단일 채널 burst + echo capture.
|
||||||
|
* maa?/mbb?보다 가벼워서 "한 채널만 먼저 살아 있는지" 확인하기 좋다.
|
||||||
|
*/
|
||||||
|
if (num_samples > ECHO_ADC_MAX_SAMPLES) {
|
||||||
|
num_samples = ECHO_ADC_MAX_SAMPLES;
|
||||||
|
}
|
||||||
|
if ((cycles < 3U) || (cycles > 7U)) {
|
||||||
|
cycles = PIEZO_SW_BURST_CYCLES;
|
||||||
|
}
|
||||||
|
if (averaging == 0U) {
|
||||||
|
averaging = 1U;
|
||||||
|
}
|
||||||
|
|
||||||
|
processing = true;
|
||||||
|
power_button_suspend(true);
|
||||||
|
|
||||||
|
int status = start_piezo_session();
|
||||||
|
if (status == ECHO_STATUS_OK)
|
||||||
|
{
|
||||||
|
status = perform_single_piezo_capture((uint8_t)cycles,
|
||||||
|
delay_us,
|
||||||
|
num_samples,
|
||||||
|
averaging,
|
||||||
|
(uint8_t)(piezo_ch % PIEZO_NUM_CHANNELS));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status == ECHO_STATUS_OK)
|
||||||
|
{
|
||||||
|
send_response_echo(echo_capture, num_samples);
|
||||||
|
}
|
||||||
|
|
||||||
|
piezo_power_off();
|
||||||
|
power_button_suspend(false);
|
||||||
|
send_response_u16("raa:", (uint16_t)status);
|
||||||
|
processing = false;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
int status = start_piezo_session();
|
||||||
|
if (status == ECHO_STATUS_OK)
|
||||||
|
{
|
||||||
|
status = perform_piezo_sweep();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status == ECHO_STATUS_OK)
|
||||||
|
{
|
||||||
|
for (uint8_t ch = 0; ch < PIEZO_NUM_CHANNELS; ch++)
|
||||||
|
{
|
||||||
|
send_response_echo(piezo_channels[ch], ECHO_ADC_MAX_SAMPLES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
piezo_power_off();
|
||||||
|
power_button_suspend(false);
|
||||||
|
send_response_u16("raa:", (uint16_t)status);
|
||||||
|
DBG_PRINTF("[CMD] maa status=0x%04X\r\n", status);
|
||||||
|
|
||||||
|
processing = false;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_mbb(const uint8_t *data, uint8_t data_len)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(data);
|
||||||
|
ARG_UNUSED(data_len);
|
||||||
|
|
||||||
|
int16_t accel[3];
|
||||||
|
int16_t gyro[3];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* mbb?는 이 프로젝트에서 가장 무거운 명령 중 하나다.
|
||||||
|
*
|
||||||
|
* 순서:
|
||||||
|
* 1. piezo/echo ADC 준비
|
||||||
|
* 2. 6채널 echo sweep
|
||||||
|
* 3. battery / imu / temp 추가 측정
|
||||||
|
* 4. rbb: 1개 전송
|
||||||
|
* 5. reb: 6개 전송
|
||||||
|
* 6. raa: 최종 상태 전송
|
||||||
|
*
|
||||||
|
* 중간에 하나라도 실패하면 status에 에러 코드를 넣고,
|
||||||
|
* 성공한 경우에만 묶음 응답(rbb + reb)을 보낸다.
|
||||||
|
*/
|
||||||
|
processing = true;
|
||||||
|
power_button_suspend(true);
|
||||||
|
DBG_PRINTF("[MBB] cmd start\r\n");
|
||||||
|
|
||||||
|
int status = start_piezo_session();
|
||||||
|
if (status == ECHO_STATUS_OK)
|
||||||
|
{
|
||||||
|
status = perform_piezo_sweep();
|
||||||
|
}
|
||||||
|
|
||||||
|
int batt_mv = -1;
|
||||||
|
int16_t temp_cdeg = INT16_MIN;
|
||||||
|
|
||||||
|
if (status == ECHO_STATUS_OK)
|
||||||
|
{
|
||||||
|
/* info 성격 데이터는 echo sweep이 정상 끝났을 때만 읽는다. */
|
||||||
|
DBG_PRINTF("[MBB] battery read\r\n");
|
||||||
|
batt_mv = battery_read_mv();
|
||||||
|
if (batt_mv < 0)
|
||||||
|
{
|
||||||
|
status = ECHO_STATUS_BATT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status == ECHO_STATUS_OK)
|
||||||
|
{
|
||||||
|
DBG_PRINTF("[MBB] imu read\r\n");
|
||||||
|
if (imu_read(accel, gyro) != 0)
|
||||||
|
{
|
||||||
|
status = ECHO_STATUS_IMU;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status == ECHO_STATUS_OK)
|
||||||
|
{
|
||||||
|
DBG_PRINTF("[MBB] temp read\r\n");
|
||||||
|
temp_cdeg = temp_read_cdeg();
|
||||||
|
if (temp_cdeg == INT16_MIN)
|
||||||
|
{
|
||||||
|
status = ECHO_STATUS_TEMP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status == ECHO_STATUS_OK)
|
||||||
|
{
|
||||||
|
/* rbb: 한 번에 보내는 "요약 정보 묶음" 패킷 */
|
||||||
|
DBG_PRINTF("[MBB] response tx start\r\n");
|
||||||
|
send_response_bundle((uint16_t)batt_mv, accel, gyro, temp_cdeg);
|
||||||
|
|
||||||
|
for (uint8_t ch = 0; ch < PIEZO_NUM_CHANNELS; ch++)
|
||||||
|
{
|
||||||
|
/* reb: 채널별 raw echo 파형 */
|
||||||
|
DBG_PRINTF("[MBB] tx reb ch=%d\r\n", ch);
|
||||||
|
send_response_echo(piezo_channels[ch], ECHO_ADC_MAX_SAMPLES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
piezo_power_off();
|
||||||
|
power_button_suspend(false);
|
||||||
|
DBG_PRINTF("[MBB] power off\r\n");
|
||||||
|
send_response_u16("raa:", (uint16_t)status);
|
||||||
|
DBG_PRINTF("[CMD] mbb status=0x%04X\r\n", status);
|
||||||
|
|
||||||
|
processing = false;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
/* mls? → LED 상태 변경 → rls: + state echo back
|
/* mls? → LED 상태 변경 → rls: + state echo back
|
||||||
* 파라미터: [state(2B LE)] — led_state_t enum 값
|
* 파라미터: [state(2B LE)] — led_state_t enum 값
|
||||||
* 0=OFF, 4=DETACH_WARNING, 5=ALIGN_SEARCHING, 6=ALIGN_COMPLETE, 7=ERROR
|
* 0=OFF, 4=DETACH_WARNING, 5=ALIGN_SEARCHING, 6=ALIGN_COMPLETE, 7=ERROR
|
||||||
* 에러 응답: 0xFFFF=파라미터 없음, 0xFFFE=범위 초과 */
|
* 에러 응답: 0xFFFF=파라미터 없음, 0xFFFE=범위 초과 */
|
||||||
|
static int cmd_mfv(const uint8_t *data, uint8_t data_len)
|
||||||
|
{
|
||||||
|
char fw_version[SERIAL_NO_LENGTH];
|
||||||
|
int err;
|
||||||
|
|
||||||
|
ARG_UNUSED(data);
|
||||||
|
ARG_UNUSED(data_len);
|
||||||
|
|
||||||
|
copy_fixed_ascii(fw_version, sizeof(fw_version), FIRMWARE_VERSION, strlen(FIRMWARE_VERSION));
|
||||||
|
err = send_response_ascii("rfv:", fw_version, sizeof(fw_version));
|
||||||
|
if (err)
|
||||||
|
{
|
||||||
|
DBG_ERR("[CMD] mfv tx failed err=%d\r\n", err);
|
||||||
|
}
|
||||||
|
DBG_PRINTF("[CMD] mfv read\r\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_mwh(const uint8_t *data, uint8_t data_len)
|
||||||
|
{
|
||||||
|
if (data_len < HW_NO_LENGTH)
|
||||||
|
{
|
||||||
|
send_response_u16("rwh:", 0xFFFF);
|
||||||
|
DBG_PRINTF("[CMD] mwh: insufficient data len=%u\r\n", data_len);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(HW_NO, 0, sizeof(HW_NO));
|
||||||
|
memcpy(HW_NO, data, HW_NO_LENGTH);
|
||||||
|
send_response_ascii("rwh:", HW_NO, HW_NO_LENGTH);
|
||||||
|
DBG_PRINTF("[CMD] mwh updated\r\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_mrh(const uint8_t *data, uint8_t data_len)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
ARG_UNUSED(data);
|
||||||
|
ARG_UNUSED(data_len);
|
||||||
|
|
||||||
|
err = send_response_ascii("rrh:", HW_NO, HW_NO_LENGTH);
|
||||||
|
if (err) {
|
||||||
|
DBG_ERR("[CMD] mrh tx failed err=%d\r\n", err);
|
||||||
|
}
|
||||||
|
DBG_PRINTF("[CMD] mrh read\r\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_mws(const uint8_t *data, uint8_t data_len)
|
||||||
|
{
|
||||||
|
if (data_len < SERIAL_NO_LENGTH)
|
||||||
|
{
|
||||||
|
send_response_u16("rws:", 0xFFFF);
|
||||||
|
DBG_PRINTF("[CMD] mws: insufficient data len=%u\r\n", data_len);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(SERIAL_NO, 0, sizeof(SERIAL_NO));
|
||||||
|
memcpy(SERIAL_NO, data, SERIAL_NO_LENGTH);
|
||||||
|
send_response_ascii("rws:", SERIAL_NO, SERIAL_NO_LENGTH);
|
||||||
|
DBG_PRINTF("[CMD] mws updated\r\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_mrs(const uint8_t *data, uint8_t data_len)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
ARG_UNUSED(data);
|
||||||
|
ARG_UNUSED(data_len);
|
||||||
|
|
||||||
|
err = send_response_ascii("rrs:", SERIAL_NO, SERIAL_NO_LENGTH);
|
||||||
|
if (err) {
|
||||||
|
DBG_ERR("[CMD] mrs tx failed err=%d\r\n", err);
|
||||||
|
}
|
||||||
|
DBG_PRINTF("[CMD] mrs read\r\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_mpz(const uint8_t *data, uint8_t data_len)
|
||||||
|
{
|
||||||
|
if (data_len < PASSKEY_LENGTH)
|
||||||
|
{
|
||||||
|
send_response_u16("rpz:", 0xFFFF);
|
||||||
|
DBG_PRINTF("[CMD] mpz: insufficient data len=%u\r\n", data_len);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(m_static_passkey, 0, sizeof(m_static_passkey));
|
||||||
|
memcpy(m_static_passkey, data, PASSKEY_LENGTH);
|
||||||
|
send_response_ascii("rpz:", m_static_passkey, PASSKEY_LENGTH);
|
||||||
|
DBG_PRINTF("[CMD] mpz updated\r\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_mqz(const uint8_t *data, uint8_t data_len)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(data);
|
||||||
|
ARG_UNUSED(data_len);
|
||||||
|
|
||||||
|
send_response_ascii("rqz:", m_static_passkey, PASSKEY_LENGTH);
|
||||||
|
DBG_PRINTF("[CMD] mqz read\r\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static int cmd_mls(const uint8_t *data, uint8_t data_len)
|
static int cmd_mls(const uint8_t *data, uint8_t data_len)
|
||||||
{
|
{
|
||||||
/* 파라미터 부족 → 에러 코드 0xFFFF 에코 */
|
/* 파라미터 부족 → 에러 코드 0xFFFF 에코 */
|
||||||
@@ -167,6 +969,21 @@ static const cmd_entry_t cmd_table[] =
|
|||||||
{ "msn?", cmd_msn },
|
{ "msn?", cmd_msn },
|
||||||
{ "mls?", cmd_mls },
|
{ "mls?", cmd_mls },
|
||||||
{ "msp?", cmd_msp },
|
{ "msp?", cmd_msp },
|
||||||
|
{ "mst?", cmd_mst },
|
||||||
|
{ "mpa?", cmd_mpa },
|
||||||
|
{ "mpb?", cmd_mpb },
|
||||||
|
{ "mpc?", cmd_mpc },
|
||||||
|
{ "mec?", cmd_mec },
|
||||||
|
{ "maa?", cmd_maa },
|
||||||
|
{ "mbb?", cmd_mbb },
|
||||||
|
{ "mcf?", cmd_mcf },
|
||||||
|
{ "mfv?", cmd_mfv },
|
||||||
|
{ "mwh?", cmd_mwh },
|
||||||
|
{ "mrh?", cmd_mrh },
|
||||||
|
{ "mws?", cmd_mws },
|
||||||
|
{ "mrs?", cmd_mrs },
|
||||||
|
{ "mpz?", cmd_mpz },
|
||||||
|
{ "mqz?", cmd_mqz },
|
||||||
};
|
};
|
||||||
|
|
||||||
#define CMD_TABLE_SIZE (sizeof(cmd_table) / sizeof(cmd_table[0]))
|
#define CMD_TABLE_SIZE (sizeof(cmd_table) / sizeof(cmd_table[0]))
|
||||||
@@ -176,28 +993,47 @@ static const cmd_entry_t cmd_table[] =
|
|||||||
*============================================================================*/
|
*============================================================================*/
|
||||||
int dr_parser(const uint8_t *buf, uint16_t len)
|
int dr_parser(const uint8_t *buf, uint16_t len)
|
||||||
{
|
{
|
||||||
|
DBG_CORE("[PARSER] enter len=%u\r\n", len);
|
||||||
|
DBG_CORE("[CMD] RX len=%u\r\n", len);
|
||||||
/* 최소 4바이트 TAG 필요 */
|
/* 최소 4바이트 TAG 필요 */
|
||||||
if (len < 4)
|
if (len < 4)
|
||||||
{
|
{
|
||||||
DBG_PRINTF("[CMD] Too short (%d)\r\n", len);
|
DBG_ERR("[CMD] Too short (%u)\r\n", len);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char raw_tag[5] = { buf[0], buf[1], buf[2], buf[3], '\0' };
|
||||||
|
|
||||||
/* CRC16 검증 (6바이트 이상이면 마지막 2바이트가 CRC) */
|
/* CRC16 검증 (6바이트 이상이면 마지막 2바이트가 CRC) */
|
||||||
if (len >= 6)
|
if (len >= 6)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
* 이 프로토콜은 끝 2바이트에 CRC16이 붙는다.
|
||||||
|
* 값이 다르면 "명령 이름은 맞아 보여도 데이터가 깨졌다"는 뜻이므로 바로 버린다.
|
||||||
|
*/
|
||||||
uint16_t calc_crc = dr_crc16_compute(buf, len - 2);
|
uint16_t calc_crc = dr_crc16_compute(buf, len - 2);
|
||||||
uint16_t recv_crc = (uint16_t)(buf[len - 2]) | ((uint16_t)(buf[len - 1]) << 8);
|
uint16_t recv_crc = (uint16_t)(buf[len - 2]) | ((uint16_t)(buf[len - 1]) << 8);
|
||||||
|
|
||||||
if (calc_crc != recv_crc)
|
if (calc_crc != recv_crc)
|
||||||
{
|
{
|
||||||
DBG_PRINTF("[CMD] CRC fail (calc=0x%04X recv=0x%04X)\r\n", calc_crc, recv_crc);
|
DBG_ERR("[CMD] CRC fail tag=%s calc=0x%04X recv=0x%04X\r\n",
|
||||||
|
raw_tag, calc_crc, recv_crc);
|
||||||
|
if (send_response_tag_echo("rxc:", raw_tag) != 0) {
|
||||||
|
DBG_ERR("[CMD] rxc tx failed\r\n");
|
||||||
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TAG 추출 (4바이트) */
|
/* TAG 추출 (4바이트) */
|
||||||
char tag[5] = { buf[0], buf[1], buf[2], buf[3], '\0' };
|
char tag[5] = {
|
||||||
|
ascii_to_lower((char)buf[0]),
|
||||||
|
ascii_to_lower((char)buf[1]),
|
||||||
|
ascii_to_lower((char)buf[2]),
|
||||||
|
ascii_to_lower((char)buf[3]),
|
||||||
|
'\0'
|
||||||
|
};
|
||||||
|
DBG_CORE("[CMD] tag=%s\r\n", tag);
|
||||||
|
|
||||||
/* 데이터 부분 (TAG 이후, CRC 이전) */
|
/* 데이터 부분 (TAG 이후, CRC 이전) */
|
||||||
const uint8_t *data = buf + 4;
|
const uint8_t *data = buf + 4;
|
||||||
@@ -208,10 +1044,14 @@ int dr_parser(const uint8_t *buf, uint16_t len)
|
|||||||
{
|
{
|
||||||
if (memcmp(tag, cmd_table[i].tag, 4) == 0)
|
if (memcmp(tag, cmd_table[i].tag, 4) == 0)
|
||||||
{
|
{
|
||||||
|
DBG_CORE("[CMD] dispatch -> %s\r\n", cmd_table[i].tag);
|
||||||
return cmd_table[i].handler(data, data_len);
|
return cmd_table[i].handler(data, data_len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DBG_PRINTF("[CMD] Unknown: %s\r\n", tag);
|
DBG_ERR("[CMD] Unknown: raw=%s normalized=%s\r\n", raw_tag, tag);
|
||||||
|
if (send_response_tag_echo("rxx:", raw_tag) != 0) {
|
||||||
|
DBG_ERR("[CMD] rxx tx failed\r\n");
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
if(SB_CONFIG_NETCORE_HCI_IPC)
|
||||||
|
set(
|
||||||
|
hci_ipc_EXTRA_CONF_FILE
|
||||||
|
"${APP_DIR}/sysbuild/hci_ipc.conf"
|
||||||
|
CACHE INTERNAL
|
||||||
|
"Extra configuration for the nRF5340 net core HCI image"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# nRF52840 build: no network core image.
|
||||||
|
# Re-enable NETCORE_HCI_IPC only for nRF5340 sysbuild configurations.
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
CONFIG_BT_CTLR_TX_PWR_PLUS_8=y
|
||||||
|
CONFIG_BT_CTLR_DATA_LENGTH_MAX=251
|
||||||
Reference in New Issue
Block a user