From 8129f55216aaaa8e143c401fa51bee4a5a434b8c Mon Sep 17 00:00:00 2001 From: Charles Kwon Date: Wed, 11 Mar 2026 10:40:20 +0900 Subject: [PATCH] VesiScan BASIC origin: Piezo + IMU firmware initial code - nRF52840 + SoftDevice S140 BLE firmware - Piezo ultrasound TX driver (2MHz, 8ch MUX) - ICM42670P IMU 6-axis driver - Echo AFE chain (ADA2200 + ADC121S051) - BLE NUS command parser (mpa/mpc/mdc/mec/maa/msp) - FDS flash config storage - pc_firm parser and ADC driver included Co-Authored-By: Claude Opus 4.6 --- .gitignore | 26 +- README.md | 126 + pc_firm/.gitignore | 33 + pc_firm/ble_security/ble_quick_security.c | 145 ++ pc_firm/ble_security/ble_quick_security.h | 56 + pc_firm/cmd.h | 1 + pc_firm/dr_adc121s051/dr_adc121s051.c | 1554 ++++++++++++ pc_firm/dr_adc121s051/dr_adc121s051.h | 505 ++++ pc_firm/dr_lua/dr_lua.c | 105 + pc_firm/dr_util/dr_util.c | 82 + pc_firm/dr_util/dr_util.h | 18 + pc_firm/f.bat | 2 + pc_firm/mt_nirs.code-workspace | 11 + pc_firm/parser.c | 1317 ++++++++++ pc_firm/parser.h | 24 + pc_firm/pc_firm.code-workspace | 11 + pc_firm/pcmain.c | 59 + pc_firm/source | 0 .../VesiScan.code-workspace | 11 + .../ble_app_bladder_patch/cmd/dr_adc121s051.c | 1515 ++++++++++++ .../ble_app_bladder_patch/cmd/dr_adc121s051.h | 505 ++++ .../ble_app_bladder_patch/cmd/dr_util.c | 82 + .../ble_app_bladder_patch/cmd/dr_util.h | 18 + .../ble_app_bladder_patch/cmd/parser.c | 1174 +++++++++ .../ble_app_bladder_patch/cmd/parser.h | 24 + .../ble_app_bladder_patch/cmd_parse.c | 177 +- .../ble_app_bladder_patch/debug_print.h | 4 +- .../ble_app_bladder_patch/fstorage.c | 221 +- .../ble_app_bladder_patch/fstorage.h | 38 +- .../ble_app_bladder_patch/hex/cpd.bat | 183 ++ .../ble_app_bladder_patch/hex/sett.bat | 2 +- .../icm42670p/app_raw/app_raw.c | 81 +- .../icm42670p/app_raw/app_raw.h | 6 + .../icm42670p/system_interface.c | 2 +- .../icm42670p/system_interface.h | 1 + .../ble_app_bladder_patch/main.c | 99 +- .../pca10056/s140/arm5_no_packs/JLinkLog.txt | 68 - ..._app_bladder_patch_s140.uvguix.CharlesKWON | 2112 ----------------- .../ble_app_bladder_patch_s140.uvoptx | 2 +- .../ble_app_bladder_patch/power_control.c | 199 +- .../ble_app_bladder_patch/power_control.h | 4 +- project/ble_peripheral/dr_piezo.rar | Bin 6026 -> 0 bytes project/ble_peripheral/dr_piezo/dr_piezo.c | 617 +++-- project/ble_peripheral/dr_piezo/dr_piezo.h | 59 +- project/dfu/secure_bootloader/main.c | 6 +- ...secure_bootloader_ble_s140_pca10056.uvoptx | 4 +- .../pca10056_s140_ble/config/sdk_config.h | 57 +- 47 files changed, 8459 insertions(+), 2887 deletions(-) create mode 100644 README.md create mode 100644 pc_firm/.gitignore create mode 100644 pc_firm/ble_security/ble_quick_security.c create mode 100644 pc_firm/ble_security/ble_quick_security.h create mode 100644 pc_firm/cmd.h create mode 100644 pc_firm/dr_adc121s051/dr_adc121s051.c create mode 100644 pc_firm/dr_adc121s051/dr_adc121s051.h create mode 100644 pc_firm/dr_lua/dr_lua.c create mode 100644 pc_firm/dr_util/dr_util.c create mode 100644 pc_firm/dr_util/dr_util.h create mode 100644 pc_firm/f.bat create mode 100644 pc_firm/mt_nirs.code-workspace create mode 100644 pc_firm/parser.c create mode 100644 pc_firm/parser.h create mode 100644 pc_firm/pc_firm.code-workspace create mode 100644 pc_firm/pcmain.c create mode 100644 pc_firm/source create mode 100644 project/ble_peripheral/ble_app_bladder_patch/VesiScan.code-workspace create mode 100644 project/ble_peripheral/ble_app_bladder_patch/cmd/dr_adc121s051.c create mode 100644 project/ble_peripheral/ble_app_bladder_patch/cmd/dr_adc121s051.h create mode 100644 project/ble_peripheral/ble_app_bladder_patch/cmd/dr_util.c create mode 100644 project/ble_peripheral/ble_app_bladder_patch/cmd/dr_util.h create mode 100644 project/ble_peripheral/ble_app_bladder_patch/cmd/parser.c create mode 100644 project/ble_peripheral/ble_app_bladder_patch/cmd/parser.h create mode 100644 project/ble_peripheral/ble_app_bladder_patch/hex/cpd.bat delete mode 100644 project/ble_peripheral/ble_app_bladder_patch/pca10056/s140/arm5_no_packs/JLinkLog.txt delete mode 100644 project/ble_peripheral/ble_app_bladder_patch/pca10056/s140/arm5_no_packs/ble_app_bladder_patch_s140.uvguix.CharlesKWON delete mode 100644 project/ble_peripheral/dr_piezo.rar diff --git a/.gitignore b/.gitignore index 2a3d69b..9d7f9a3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,15 @@ +# Archives +*.rar +*.zip +*.7z +*.tar +*.tar.gz +*.tgz + +# Claude AI +.claude/ +docs/ + # Compiled binaries *.o *.hex @@ -16,22 +28,26 @@ output/ *.swp *.swo *~ +*.uvguix.* +JLinkLog.txt +*.crf +*.d +*.htm +*.lnp +*.dep +*.axf # Segger/J-Link *.jlink # nRF specific -*.zip sdk_config.h.bak # Windows Thumbs.db desktop.ini +.DS_Store # Temporary files *.tmp *.bak - -# SDK (external dependencies) -# components/ -# external/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..a8f6364 --- /dev/null +++ b/README.md @@ -0,0 +1,126 @@ +# Medithings VesiScan BASIC + +nRF52840 기반 초음파(Piezo) + IMU 방광 모니터링 패치 펌웨어 + +## Author + +**Charles KWON** - charleskwon@medithings.co.kr + +## Overview + +VesiScan BASIC은 피에조 초음파 트랜스듀서와 IMU 센서를 이용하여 방광 상태를 측정하고, BLE를 통해 모바일 클라이언트로 데이터를 전송하는 웨어러블 패치 디바이스 펌웨어입니다. + +## Hardware + +| 구분 | 칩/센서 | 사양 | 인터페이스 | +|------|--------|------|-----------| +| MCU | nRF52840 | ARM Cortex-M4, SoftDevice S140 | - | +| Piezo TX | MD1822K6-G + TC7920K6-G | MOSFET Driver, +/-20V | GPIO (Timer+PPI) | +| Piezo MUX | 8ch MUX (MUXA/MUXB) | 채널 선택 (CH0~CH7) | GPIO | +| Echo AFE | ADA2200 | Lock-in Amplifier | SPI | +| Echo ADC | ADC121S051 | 12-bit, Echo 신호 샘플링 | SPI | +| DAC | MCP4725 | 12-bit, 바이어스 전압 | I2C (SW) | +| DigiPot | AD5272 | 가변저항, AGC 제어 | I2C (SW) | +| IMU | ICM42670P | 6축 가속도/자이로 | I2C (HW) | + +## BLE Commands (Piezo) + +| Command | Response | 설명 | +|---------|----------|------| +| `mpa?` | - | Piezo 시스템 활성화 (전원 ON + 드라이버 초기화) | +| `mpc? [cycles] [freq] [ch]` | - | Piezo 버스트 (주파수/채널 선택) | +| `mdc? [cycles] [delay] [samples]` | `rdb:` `rdd:` `rde:` | Piezo 버스트 + Echo 캡처 (12-bit packed) | +| `mec? [freq] [delay] [samples] [cycles] [avg] [ch]` | `rer:` | Piezo 버스트 + Echo 캡처 (16-bit raw) | +| `maa? [mode]` | `raa:` | 8채널 전체 캡처 (비동기, mode 0=raw) | + +### Piezo 주파수 옵션 + +| freq | 주파수 | +|------|--------| +| 0 (기본) | 2.1 MHz | +| 1 | 1.8 MHz | +| 2 | 2.0 MHz | +| 3 | 1.7 MHz | +| 4 | 2.2 MHz | +| 5 | 1.9 MHz | + +### 8채널 MUX 매핑 + +| Channel | MUX | SEL1 | SEL0 | EN_MUXA | EN_MUXB | +|---------|-----|------|------|---------|---------| +| CH0 | A0 | 0 | 0 | 1 | 0 | +| CH1 | A2 | 0 | 1 | 1 | 0 | +| CH2 | A1 | 1 | 0 | 1 | 0 | +| CH3 | A3 | 1 | 1 | 1 | 0 | +| CH4 | B0 | 1 | 1 | 0 | 1 | +| CH5 | B1 | 0 | 1 | 0 | 1 | +| CH6 | B2 | 1 | 0 | 0 | 1 | +| CH7 | B3 | 0 | 0 | 0 | 1 | + +### Echo 수신 체인 + +``` +Piezo → MUX(8ch) → ADA2200(AFE) → ADC121S051 → nRF52840 + ↑ + MCP4725(Bias) + AD5272(AGC) +``` + +## BLE Commands (IMU) + +| Command | Response | 설명 | +|---------|----------|------| +| `msp?` | `rsp:` | IMU 6축 데이터 (single shot, 레지스터 직접 읽기) | + +### IMU 사양 + +| 항목 | 설정 | +|------|------| +| 센서 | ICM42670P (6축: 3축 가속도 + 3축 자이로) | +| 인터페이스 | I2C (HW) | +| 모드 | Low-Noise Mode | +| 해상도 | 16-bit | + +## BLE Commands (측정) + +| Command | Response | 설명 | +|---------|----------|------| +| `scj?` | `rcj:` | PD-ADC M48 MODE 2 (Pressure + M48 + Battery + Temp + IMU) | +| `sdj?` | `rdj:` | PD-ADC M48 MODE 3 | +| `sej?` | `rej:` | PD-ADC M48 MODE 4 (M48 + Battery + IMU) | +| `ssj?` | `rsj:` | PD-ADC M48 MODE 0 (M48 + Battery/IMU combined) | +| `cmd? [v1] [v2] [v3]` | `rmd:` | ADC count / delay / 파라미터 설정 | + +## Build + +- **IDE**: Keil uVision5 (ARM5) +- **SDK**: nRF5 SDK + SoftDevice S140 + +```c +#define HARDWARE_VERSION "VB0HW0000" +#define FIRMWARE_VERSION "VB0FW0000" +#define FIRMWARE_SERIAL_NO "VB026030000" +``` + +## Project Structure + +``` +project/ +├── ble_peripheral/ +│ ├── ble_app_bladder_patch/ ← 메인 펌웨어 +│ │ ├── main.c ← 부트, BLE, 전원 제어 +│ │ ├── main_timer.c ← 메인 루프 (측정/제어) +│ │ ├── cmd_parse.c ← BLE 명령어 파싱 (레거시) +│ │ ├── icm42670p/ ← IMU 드라이버 +│ │ └── meas_pd_*.c ← PD Echo 측정 (모드별) +│ └── dr_piezo/ ← Piezo TX 드라이버 +└── dfu/ + └── secure_bootloader/ ← BLE DFU 부트로더 + +pc_firm/ ← 파서 및 ADC 드라이버 +├── parser.c ← BLE 명령어 파서 (신규) +└── dr_adc121s051/ ← Echo ADC 드라이버 +``` + +## License + +Proprietary - Medithings Co., Ltd. diff --git a/pc_firm/.gitignore b/pc_firm/.gitignore new file mode 100644 index 0000000..6bfcfb9 --- /dev/null +++ b/pc_firm/.gitignore @@ -0,0 +1,33 @@ +# Compiled binaries +*.o +*.hex +*.bin +*.elf +*.map +*.lst + +# Build directories +_build/ +build/ +output/ + +# IDE/Editor +.vscode/ +*.swp +*.swo +*~ + +# Segger/J-Link +*.jlink + +# nRF specific +*.zip +sdk_config.h.bak + +# Windows +Thumbs.db +desktop.ini + +# Temporary files +*.tmp +*.bak diff --git a/pc_firm/ble_security/ble_quick_security.c b/pc_firm/ble_security/ble_quick_security.c new file mode 100644 index 0000000..aef3bd7 --- /dev/null +++ b/pc_firm/ble_security/ble_quick_security.c @@ -0,0 +1,145 @@ +/** + * @file ble_quick_security.c + * @brief Ultra-simple BLE Security Configuration Implementation + * + * Compatible with existing debug_print.h system. + */ + +#include "ble_quick_security.h" +#include "peer_manager_handler.h" +#include "app_error.h" +#include + +// Use existing debug system +#include "debug_print.h" + +// Module state +static struct { + bool dev_mode; + bool bonds_delete_pending; +} m_state = {0}; + +/** + * @brief Initialize BLE security + */ +void ble_security_quick_init(bool development_mode) +{ + ret_code_t err_code; + ble_gap_sec_params_t sec_params; + + // Save mode + m_state.dev_mode = development_mode; + m_state.bonds_delete_pending = false; + + // Initialize Peer Manager FIRST + err_code = pm_init(); + APP_ERROR_CHECK(err_code); + + // Configure security parameters + memset(&sec_params, 0, sizeof(ble_gap_sec_params_t)); + + if (development_mode) { + // ===== DEVELOPMENT MODE: No security ===== + sec_params.bond = 0; // No bonding + sec_params.mitm = 0; // No MITM + sec_params.lesc = 0; // No LESC + sec_params.keypress = 0; + sec_params.io_caps = BLE_GAP_IO_CAPS_NONE; // No passkey + sec_params.oob = 0; + sec_params.min_key_size = 7; + sec_params.max_key_size = 16; + + DBG_PRINTF("DEV MODE: Security DISABLED - Fast connection\r\n"); + + // Delete all bonds (async) + err_code = pm_peers_delete(); + if (err_code == NRF_SUCCESS) { + m_state.bonds_delete_pending = true; + DBG_PRINTF("DEV MODE: Deleting all bonds...\r\n"); + } + } else { + // ===== PRODUCTION MODE: Full security ===== + sec_params.bond = 1; // Enable bonding + sec_params.mitm = 1; // Enable MITM + sec_params.lesc = 0; // Standard pairing + sec_params.keypress = 0; + sec_params.io_caps = BLE_GAP_IO_CAPS_DISPLAY_ONLY; // Show passkey + sec_params.oob = 0; + sec_params.min_key_size = 7; + sec_params.max_key_size = 16; + sec_params.kdist_own.enc = 1; + sec_params.kdist_own.id = 1; + sec_params.kdist_peer.enc = 1; + sec_params.kdist_peer.id = 1; + + DBG_PRINTF("PROD MODE: Security ENABLED - Full protection\r\n"); + } + + // Apply security parameters + err_code = pm_sec_params_set(&sec_params); + APP_ERROR_CHECK(err_code); +} + +/** + * @brief Get current mode + */ +bool ble_security_is_dev_mode(void) +{ + return m_state.dev_mode; +} + +/** + * @brief PM event handler + */ +void ble_security_quick_pm_handler(pm_evt_t const *p_evt) +{ + ret_code_t err_code; + + // Call standard handlers (required) + pm_handler_on_pm_evt(p_evt); + pm_handler_disconnect_on_sec_failure(p_evt); + pm_handler_flash_clean(p_evt); + + // Handle events + switch (p_evt->evt_id) { + case PM_EVT_CONN_SEC_SUCCEEDED: + if (m_state.dev_mode) { + DBG_PRINTF("DEV: Connected (no security)\r\n"); + } else { + DBG_PRINTF("PROD: Link secured (bonded)\r\n"); + } + break; + + case PM_EVT_CONN_SEC_FAILED: + DBG_PRINTF("Security failed: error=%d\r\n", + p_evt->params.conn_sec_failed.error); + + // Auto-retry if key missing + if (p_evt->params.conn_sec_failed.error == PM_CONN_SEC_ERROR_PIN_OR_KEY_MISSING) { + err_code = pm_conn_secure(p_evt->conn_handle, true); + if (err_code != NRF_ERROR_INVALID_STATE) { + APP_ERROR_CHECK(err_code); + } + } + break; + + case PM_EVT_CONN_SEC_CONFIG_REQ: + { + pm_conn_sec_config_t config = { + .allow_repairing = m_state.dev_mode + }; + pm_conn_sec_config_reply(p_evt->conn_handle, &config); + } + break; + + case PM_EVT_PEERS_DELETE_SUCCEEDED: + if (m_state.bonds_delete_pending) { + m_state.bonds_delete_pending = false; + DBG_PRINTF("DEV MODE: Bonds cleared!\r\n"); + } + break; + + default: + break; + } +} diff --git a/pc_firm/ble_security/ble_quick_security.h b/pc_firm/ble_security/ble_quick_security.h new file mode 100644 index 0000000..fdc107a --- /dev/null +++ b/pc_firm/ble_security/ble_quick_security.h @@ -0,0 +1,56 @@ + /** + * @file ble_quick_security.h + * @brief Ultra-simple BLE Security Configuration + * + * ONE function call to control entire security behavior. + * Works with existing debug_print.h system. + */ + +#ifndef BLE_QUICK_SECURITY_H +#define BLE_QUICK_SECURITY_H + +#include +#include +#include "peer_manager.h" + +/** + * @brief Initialize BLE security with ONE simple parameter + * + * @param[in] development_mode true (1) = Fast development (no security) + * false (0) = Production (full security) + * + * Development mode (1): + * - No pairing/bonding required + * - Auto-deletes all bonds on startup + * - Instant connection + * - Fast iteration + * + * Production mode (0): + * - Full security with passkey + * - Bonding preserved + * - MITM protection + * - Secure deployment + * + * Usage in main.c: + * #define BLE_DEV_MODE 1 // or 0 + * ble_security_quick_init(BLE_DEV_MODE); + */ +void ble_security_quick_init(bool development_mode); + +/** + * @brief Get current mode + * @return true if in development mode, false if production + */ +bool ble_security_is_dev_mode(void); + +/** + * @brief Peer Manager event handler + * + * Call this from your pm_evt_handler() function. + * It handles all security events automatically. + * + * @param[in] p_evt Peer Manager event + */ +void ble_security_quick_pm_handler(pm_evt_t const *p_evt); + +#endif // BLE_QUICK_SECURITY_H diff --git a/pc_firm/cmd.h b/pc_firm/cmd.h new file mode 100644 index 0000000..3b351a7 --- /dev/null +++ b/pc_firm/cmd.h @@ -0,0 +1 @@ +void battery_level_meas(void); diff --git a/pc_firm/dr_adc121s051/dr_adc121s051.c b/pc_firm/dr_adc121s051/dr_adc121s051.c new file mode 100644 index 0000000..6f5d80c --- /dev/null +++ b/pc_firm/dr_adc121s051/dr_adc121s051.c @@ -0,0 +1,1554 @@ +/******************************************************************************* + * @file dr_adc121s051.c + * @brief ADC121S051 12-bit ADC Driver for nRF52840 + * For 1.2MHz Piezo Echo Envelope Detection + * @author Charles KWON + * @date 2025-12-15 + * + * @details Software SPI (bit-bang) implementation for ADC121S051. + * Optimized for reading envelope-detected echo signals. + * + * ADC121S051 Serial Interface: + * + * CS ────────┐ ┌──────── + * (HIGH) └──────────────────────────────┘ (HIGH) + * + * SCLK ────────┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ──────── + * (HIGH) └──┘ └──┘ └── ... ┘ └──┘ └──┘ (HIGH) + * 1 2 3 14 15 16 + * + * SDATA -------...------ + * |<- 3 zeros ->|<-- 12 data bits -->| + * + * - CS idle HIGH, SCLK idle HIGH + * - CS falling edge: starts conversion, samples VIN + * - Data clocked out on SCLK falling edge + * - MSB first, straight binary output + * + * @note WARNING: Never hardcode pin numbers! + * Hardcoding may save a developer's time momentarily, + * but it will also shorten their lifespan. + ******************************************************************************/ + +#include "dr_adc121s051.h" +#include "dr_util.h" +#include "nrf_gpio.h" +#include "nrf_delay.h" +#include /* memset */ + +/* BLE connection state from main.c */ +extern volatile bool data_tx_in_progress; +extern volatile bool ble_connection_st; + +/*============================================================================== + * EXTERNAL PIEZO BURST FUNCTIONS + *============================================================================*/ +extern void dr_piezo_burst_sw(uint8_t cycles); +extern void dr_piezo_burst_sw_18mhz(uint8_t cycles); +extern void dr_piezo_burst_sw_20mhz(uint8_t cycles); +extern void dr_piezo_burst_sw_17mhz(uint8_t cycles); +extern void dr_piezo_burst_sw_22mhz(uint8_t cycles); +extern void dr_piezo_burst_sw_19mhz(uint8_t cycles); + +/*============================================================================== + * DEBUG CONFIGURATION + *============================================================================*/ +#ifdef DEBUG_ADC +#include "nrf_log.h" +#define ADC_LOG(...) NRF_LOG_INFO(__VA_ARGS__) +#else +#define ADC_LOG(...) +#endif + +/*============================================================================== + * PRIVATE DEFINES + *============================================================================*/ + +/* Extract pin number from NRF_GPIO_PIN_MAP (lower 5 bits) */ +#define DR_PIN_NUM(pin) ((pin) & 0x1F) +#define DR_PIN_PORT(pin) (((pin) >> 5) & 0x01) + +/* SPI bit masks for direct register access */ +#define CS_MASK (1UL << DR_PIN_NUM(DR_ADC_PIN_CS)) +#define SCLK_MASK (1UL << DR_PIN_NUM(DR_ADC_PIN_SCLK)) +#define SDATA_MASK (1UL << DR_PIN_NUM(DR_ADC_PIN_SDATA)) + +/* Get port register based on pin */ +#define DR_GET_PORT(pin) (DR_PIN_PORT(pin) ? NRF_P1 : NRF_P0) + +/* ADC timing constants */ +#define SCLK_CYCLES 16 /**< Clock cycles per conversion */ + +/*============================================================================== + * PRIVATE VARIABLES + *============================================================================*/ + +static bool m_initialized = false; +static uint32_t m_vref_mv = DR_ADC_VREF_MV; + +/* Echo capture buffer (module-level for external access) */ +static uint16_t m_echo_buffer[DR_ADC_ECHO_SAMPLES_MAX]; + +/* Port registers for fast access (currently unused - using direct NRF_P0) */ +/* static NRF_GPIO_Type *m_port_cs; */ +/* static NRF_GPIO_Type *m_port_sclk; */ +/* static NRF_GPIO_Type *m_port_sdata; */ + +/*============================================================================== + * PRIVATE FUNCTION PROTOTYPES + *============================================================================*/ + +static void adc_gpio_init(void); +/* adc_transfer is always_inline, no prototype needed */ + +/*============================================================================== + * INLINE GPIO FUNCTIONS (for speed) + *============================================================================*/ + +/* cs_low() unused - using ADC_TRANSFER_16BITS macro with direct register access +static inline void cs_low(void) +{ + NRF_P0->OUTCLR = CS_MASK; +} +*/ + +static inline void cs_high(void) +{ + NRF_P0->OUTSET = CS_MASK; +} + +/* sclk_low() unused - using direct register access in macro +static inline void sclk_low(void) +{ + NRF_P0->OUTCLR = SCLK_MASK; +} +*/ + +static inline void sclk_high(void) +{ + NRF_P0->OUTSET = SCLK_MASK; +} + +/* read_sdata() unused - using direct register access in macro +static inline uint32_t read_sdata(void) +{ + return (NRF_P0->IN & SDATA_MASK) ? 1 : 0; +} +*/ + +/*============================================================================== + * PRIVATE FUNCTIONS + *============================================================================*/ + +/** + * @brief Initialize GPIO pins for ADC + */ +static void adc_gpio_init(void) +{ + /* Port pointers removed - using direct NRF_P0 access for speed */ + + /* CS: Output, HIGH (idle) */ + nrf_gpio_cfg_output(DR_ADC_PIN_CS); + nrf_gpio_pin_set(DR_ADC_PIN_CS); + + /* SCLK: Output, HIGH (idle) - per Figure 4 */ + nrf_gpio_cfg_output(DR_ADC_PIN_SCLK); + nrf_gpio_pin_set(DR_ADC_PIN_SCLK); + + /* SDATA: Input (ADC has push-pull output) */ + nrf_gpio_cfg_input(DR_ADC_PIN_SDATA, NRF_GPIO_PIN_NOPULL); + + ADC_LOG("ADC GPIO: CS=P%d.%02d SCLK=P%d.%02d SDATA=P%d.%02d", + DR_PIN_PORT(DR_ADC_PIN_CS), DR_PIN_NUM(DR_ADC_PIN_CS), + DR_PIN_PORT(DR_ADC_PIN_SCLK), DR_PIN_NUM(DR_ADC_PIN_SCLK), + DR_PIN_PORT(DR_ADC_PIN_SDATA), DR_PIN_NUM(DR_ADC_PIN_SDATA)); +} + +/** + * @brief Perform one ADC conversion (16 clock cycles) + * @return Raw 16-bit data (includes leading zeros) + * + * @details Timing optimized for maximum speed (~500ksps). + * At 64MHz CPU: 1 NOP = 15.625ns + * + * ADC specs (@ 3.3V): + * - tACC (data access time): 40ns max + * - tH (hold time): 7ns min + * - fSCLK: 4-10 MHz + * + * MUST be inlined to eliminate function call overhead for consistent timing. + */ +/* + * ADC_TRANSFER_BIT macro - one SCLK cycle for bit-bang SPI + * Captures data on falling edge, processes on rising edge + * + * Target: 10MHz (100ns cycle) = 50ns LOW + 50ns HIGH + * ADC121S051 timing requirements: + * - tCL (SCLK low time): 40ns min + * - tCH (SCLK high time): 40ns min + * - tACC (data access): 40ns max (data valid after SCLK falling) + * + * LOW phase: OUTCLR(~15ns) + NOP(~16ns) + IN read(~30ns) = ~61ns (meets tCL, tACC) + * HIGH phase: OUTSET(~15ns) + shift/OR(~40ns) = ~55ns (meets tCH) + * Total: ~116ns (~8.6MHz) + * + * Note: Removing HIGH phase NOP to reduce cycle time + */ +#define ADC_TRANSFER_BIT(data, in_reg) \ + do { \ + NRF_P0->OUTCLR = SCLK_MASK; \ + __NOP(); \ + in_reg = NRF_P0->IN; \ + NRF_P0->OUTSET = SCLK_MASK; \ + data = (data << 1) | ((in_reg & SDATA_MASK) ? 1 : 0); \ + } while(0) + +#define ADC_TRANSFER_BIT_LAST(data, in_reg) \ + do { \ + NRF_P0->OUTCLR = SCLK_MASK; \ + __NOP(); \ + in_reg = NRF_P0->IN; \ + NRF_P0->OUTSET = SCLK_MASK; \ + data = (data << 1) | ((in_reg & SDATA_MASK) ? 1 : 0); \ + } while(0) + +/* + * ADC_TRANSFER_16BITS macro - Complete 16-bit transfer + * Use this instead of adc_transfer() function for consistent first bit timing. + * Variables data and in_reg MUST be declared and initialized before calling. + * + * IMPORTANT: Call ADC_PREHEAT(data, in_reg) before cs_low() to warm up pipeline. + */ +#define ADC_TRANSFER_16BITS(data, in_reg) \ + do { \ + ADC_TRANSFER_BIT(data, in_reg); /* Bit 15 */ \ + ADC_TRANSFER_BIT(data, in_reg); /* Bit 14 */ \ + ADC_TRANSFER_BIT(data, in_reg); /* Bit 13 */ \ + ADC_TRANSFER_BIT(data, in_reg); /* Bit 12 */ \ + ADC_TRANSFER_BIT(data, in_reg); /* Bit 11 */ \ + ADC_TRANSFER_BIT(data, in_reg); /* Bit 10 */ \ + ADC_TRANSFER_BIT(data, in_reg); /* Bit 9 */ \ + ADC_TRANSFER_BIT(data, in_reg); /* Bit 8 */ \ + ADC_TRANSFER_BIT(data, in_reg); /* Bit 7 */ \ + ADC_TRANSFER_BIT(data, in_reg); /* Bit 6 */ \ + ADC_TRANSFER_BIT(data, in_reg); /* Bit 5 */ \ + ADC_TRANSFER_BIT(data, in_reg); /* Bit 4 */ \ + ADC_TRANSFER_BIT(data, in_reg); /* Bit 3 */ \ + ADC_TRANSFER_BIT(data, in_reg); /* Bit 2 */ \ + ADC_TRANSFER_BIT(data, in_reg); /* Bit 1 */ \ + ADC_TRANSFER_BIT_LAST(data, in_reg); /* Bit 0 */ \ + NRF_P0->OUTSET = CS_MASK; \ + __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); \ + __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); \ + __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); \ + __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); \ + } while(0) + +/* + * ADC_PREHEAT - Warm up instruction pipeline before timing-critical section + * This prevents the first SCLK cycle from being longer than others. + * + * CRITICAL: Must perform ACTUAL SCLK toggle to prime the GPIO write path! + * CS is still HIGH (idle), so this dummy toggle doesn't affect ADC. + * + * Sequence: + * 1. Dummy SCLK LOW/HIGH cycle (exact same code as ADC_TRANSFER_BIT) + * 2. SCLK remains HIGH (ready for cs_low) + */ +#define ADC_PREHEAT(data, in_reg) \ + do { \ + /* Dummy SCLK cycle - CS still HIGH, so ADC ignores this */ \ + /* Must match ADC_TRANSFER_BIT timing exactly */ \ + NRF_P0->OUTCLR = SCLK_MASK; \ + __NOP(); \ + in_reg = NRF_P0->IN; \ + NRF_P0->OUTSET = SCLK_MASK; \ + data = (data << 1) | ((in_reg & SDATA_MASK) ? 1 : 0); \ + /* Second dummy cycle for better pipeline warm-up */ \ + NRF_P0->OUTCLR = SCLK_MASK; \ + __NOP(); \ + in_reg = NRF_P0->IN; \ + NRF_P0->OUTSET = SCLK_MASK; \ + data = (data << 1) | ((in_reg & SDATA_MASK) ? 1 : 0); \ + /* Reset data after priming */ \ + data = 0; \ + } while(0) + +/* adc_transfer() function replaced by ADC_TRANSFER_16BITS macro for speed + * Kept here for reference but commented out to eliminate warning + */ +#if 0 +/** + * @brief Perform one ADC conversion (16 clock cycles) + * @param data Pre-initialized to 0 by caller + * @param in_reg Pre-declared register variable by caller + * @return Raw 16-bit data (includes leading zeros) + */ +__attribute__((always_inline)) +static inline uint16_t adc_transfer(uint16_t data, register uint32_t in_reg) +{ + /* Implementation moved to ADC_TRANSFER_16BITS macro */ + return data; +} +#endif + +/*============================================================================== + * PUBLIC FUNCTIONS - INITIALIZATION + *============================================================================*/ + +dr_adc_err_t dr_adc_init(void) +{ + ADC_LOG("ADC121S051 init..."); + + /* Initialize GPIO */ + adc_gpio_init(); + + /* Ensure idle state (SCLK idle HIGH per Figure 4) */ + cs_high(); + sclk_high(); + + /* Wait for power stabilization */ + nrf_delay_us(10); + + /* Dummy read to wake up and clear stale data */ + { + uint16_t dummy_data = 0; + register uint32_t dummy_reg = 0; + ADC_PREHEAT(dummy_data, dummy_reg); + NRF_P0->OUTCLR = CS_MASK; + __NOP(); __NOP(); __NOP(); __NOP(); /* tSU: CS setup time */ + ADC_TRANSFER_16BITS(dummy_data, dummy_reg); + } + + /* Quiet time between conversions */ + __NOP(); __NOP(); __NOP(); __NOP(); + + m_initialized = true; + + ADC_LOG("ADC121S051 ready (VREF=%dmV)", m_vref_mv); + + return DR_ADC_OK; +} + +void dr_adc_uninit(void) +{ + if (!m_initialized) return; + + cs_high(); + sclk_high(); /* Idle HIGH per Figure 4 */ + + nrf_gpio_cfg_default(DR_ADC_PIN_CS); + nrf_gpio_cfg_default(DR_ADC_PIN_SCLK); + nrf_gpio_cfg_default(DR_ADC_PIN_SDATA); + + m_initialized = false; + + ADC_LOG("ADC121S051 uninitialized"); +} + +bool dr_adc_is_initialized(void) +{ + return m_initialized; +} + +/*============================================================================== + * PUBLIC FUNCTIONS - BASIC READ + *============================================================================*/ + +dr_adc_err_t dr_adc_read_raw(uint16_t *raw_value) +{ + if (!m_initialized) return DR_ADC_ERR_NOT_INIT; + if (raw_value == NULL) return DR_ADC_ERR_INVALID_PARAM; + + uint16_t data = 0; + register uint32_t in_reg = 0; + + /* Critical section for precise timing */ + __disable_irq(); + + /* Preheat pipeline before timing-critical section */ + ADC_PREHEAT(data, in_reg); + + /* CS LOW - starts conversion, samples VIN on this edge */ + NRF_P0->OUTCLR = CS_MASK; + __NOP(); /* Match timing with other SCLK cycles */ + + /* Transfer 16 bits + CS HIGH (macro for consistent timing) */ + ADC_TRANSFER_16BITS(data, in_reg); + + __enable_irq(); + + /* + * Extract 12-bit data from 16-bit frame: + * Bit 15-13: Leading zeros (Z2, Z1, Z0) - must be 0 + * Bit 12-1: Data bits (D11-D0) + * Bit 0: Don't care (trailing) + * + * Result = (data >> 1) & 0x0FFF + */ + + /* Verify leading zeros (upper 3 bits should be 0) */ + if ((data & 0xE000) != 0) + { + ADC_LOG("ADC frame error: leading zeros invalid (0x%04X)", data); + } + + *raw_value = (data >> 1) & 0x0FFF; + + return DR_ADC_OK; +} + +dr_adc_err_t dr_adc_read(dr_adc_result_t *result) +{ + if (!m_initialized) return DR_ADC_ERR_NOT_INIT; + if (result == NULL) return DR_ADC_ERR_INVALID_PARAM; + + dr_adc_err_t err = dr_adc_read_raw(&result->raw); + if (err != DR_ADC_OK) return err; + + result->voltage_mv = dr_adc_raw_to_mv(result->raw); + + return DR_ADC_OK; +} + +dr_adc_err_t dr_adc_read_averaged(dr_adc_result_t *result, uint16_t num_samples) +{ + if (!m_initialized) return DR_ADC_ERR_NOT_INIT; + if (result == NULL) return DR_ADC_ERR_INVALID_PARAM; + if (num_samples == 0 || num_samples > DR_ADC_ECHO_SAMPLES_MAX) + return DR_ADC_ERR_INVALID_PARAM; + + uint32_t sum = 0; + uint16_t raw; + + for (uint16_t i = 0; i < num_samples; i++) + { + dr_adc_err_t err = dr_adc_read_raw(&raw); + if (err != DR_ADC_OK) return err; + sum += raw; + } + + result->raw = (uint16_t)(sum / num_samples); + result->voltage_mv = dr_adc_raw_to_mv(result->raw); + + return DR_ADC_OK; +} + +/*============================================================================== + * PUBLIC FUNCTIONS - ECHO DETECTION + *============================================================================*/ + +dr_adc_err_t dr_adc_capture_echo(uint16_t *buffer, uint16_t num_samples) +{ + if (!m_initialized) return DR_ADC_ERR_NOT_INIT; + if (buffer == NULL) return DR_ADC_ERR_INVALID_PARAM; + if (num_samples == 0 || num_samples > DR_ADC_ECHO_SAMPLES_MAX) + return DR_ADC_ERR_INVALID_PARAM; + + uint16_t data = 0; + register uint32_t in_reg = 0; + + /* Disable interrupts for continuous high-speed capture */ + __disable_irq(); + + /* Preheat pipeline once before the loop */ + ADC_PREHEAT(data, in_reg); + + for (uint16_t i = 0; i < num_samples; i++) + { + data = 0; /* Reset for each sample */ + + /* CS LOW - starts conversion, samples VIN */ + NRF_P0->OUTCLR = CS_MASK; + __NOP(); /* Match timing with other SCLK cycles */ + + /* Transfer 16 bits + CS HIGH (macro for consistent timing) */ + ADC_TRANSFER_16BITS(data, in_reg); + + /* Store 12-bit value */ + buffer[i] = (data >> 1) & 0x0FFF; + } + + __enable_irq(); + + return DR_ADC_OK; +} + +dr_adc_err_t dr_adc_measure_echo(dr_adc_echo_t *echo, const dr_adc_echo_config_t *config) +{ + if (!m_initialized) return DR_ADC_ERR_NOT_INIT; + if (echo == NULL) return DR_ADC_ERR_INVALID_PARAM; + + /* Use default config if not provided */ + dr_adc_echo_config_t cfg; + if (config != NULL) + { + cfg = *config; + } + else + { + cfg.num_samples = DR_ADC_ECHO_SAMPLES_DEFAULT; + cfg.threshold_raw = 100; /* ~80mV threshold */ + cfg.delay_us = 0; + } + + /* Validate config */ + if (cfg.num_samples == 0 || cfg.num_samples > DR_ADC_ECHO_SAMPLES_MAX) + return DR_ADC_ERR_INVALID_PARAM; + + /* Use module-level buffer (16KB too large for stack) */ + + /* Optional delay before capture */ + if (cfg.delay_us > 0) + { + nrf_delay_us(cfg.delay_us); + } + + /* Capture echo samples into module-level buffer */ + dr_adc_err_t err = dr_adc_capture_echo(m_echo_buffer, cfg.num_samples); + if (err != DR_ADC_OK) return err; + + /* Analyze captured data */ + return dr_adc_analyze_echo(m_echo_buffer, cfg.num_samples, echo, cfg.threshold_raw); +} + +dr_adc_err_t dr_adc_burst_and_capture(uint8_t cycles, uint16_t delay_us, + uint16_t num_samples, dr_adc_echo_t *echo) +{ + (void)cycles; /* Not used - ADC test only */ + + if (echo == NULL) return DR_ADC_ERR_INVALID_PARAM; + if (num_samples == 0 || num_samples > DR_ADC_ECHO_SAMPLES_MAX) + return DR_ADC_ERR_INVALID_PARAM; + + /* Initialize echo structure */ + echo->peak_raw = 0; + echo->peak_mv = 0; + echo->peak_index = 0; + echo->peak_time_us = 0; + echo->baseline_raw = 0; + echo->num_samples = 0; + + /* Initialize ADC if not ready */ + if (!m_initialized) + { + dr_adc_err_t init_err = dr_adc_init(); + if (init_err != DR_ADC_OK) return init_err; + } + + /* Settling time for ADC power */ + nrf_delay_us(100); + + /* Optional delay before capture */ + if (delay_us > 0) + { + nrf_delay_us(delay_us); + } + + /* Capture ADC samples into module-level buffer */ + dr_adc_err_t err = dr_adc_capture_echo(m_echo_buffer, num_samples); + if (err != DR_ADC_OK) return err; + + /* Analyze captured data */ + return dr_adc_analyze_echo(m_echo_buffer, num_samples, echo, 100); +} + +const uint16_t* dr_adc_get_echo_buffer(void) +{ + return m_echo_buffer; +} + +dr_adc_err_t dr_adc_analyze_echo(const uint16_t *buffer, uint16_t num_samples, + dr_adc_echo_t *echo, uint16_t threshold) +{ + if (buffer == NULL || echo == NULL) return DR_ADC_ERR_INVALID_PARAM; + if (num_samples == 0) return DR_ADC_ERR_INVALID_PARAM; + + /* Calculate baseline from first few samples */ + uint16_t baseline_samples = (num_samples > 8) ? 8 : num_samples; + echo->baseline_raw = dr_adc_calc_baseline(buffer, baseline_samples); + + /* Find peak */ + dr_adc_find_peak(buffer, num_samples, &echo->peak_raw, &echo->peak_index); + + /* Convert to voltage and time */ + echo->peak_mv = dr_adc_raw_to_mv(echo->peak_raw); + /* peak_time_us: sample_index * 0.116us (8.6MHz sample rate) */ + echo->peak_time_us = (uint32_t)((float)echo->peak_index * DR_ADC_SAMPLE_INTERVAL_US); + echo->num_samples = num_samples; + + /* Check if valid echo detected */ + if (echo->peak_raw < threshold || + echo->peak_raw <= echo->baseline_raw) + { + return DR_ADC_ERR_NO_ECHO; + } + + return DR_ADC_OK; +} + +void dr_adc_find_peak(const uint16_t *buffer, uint16_t num_samples, + uint16_t *peak_value, uint16_t *peak_index) +{ + /* + * Posterior wall detection for bladder measurement + * - Skip initial decay zone (index 0-29) where transducer coupling causes high values + * - Search in range [30, 70] where posterior wall echo is expected + * - Ignore values > 1500 (likely noise/saturation) + */ + #define SEARCH_START 30 + #define SEARCH_END 70 + #define MAX_VALID_VALUE 1500 /* Values above this are likely noise */ + + uint16_t max_val = 0; + uint16_t max_idx = 0; + + uint16_t start = (num_samples > SEARCH_START) ? SEARCH_START : 0; + uint16_t end = (num_samples > SEARCH_END) ? SEARCH_END : num_samples; + + for (uint16_t i = start; i < end; i++) + { + uint16_t val = buffer[i]; + /* Skip abnormally high values (noise/saturation) */ + if (val > MAX_VALID_VALUE) continue; + + if (val > max_val) + { + max_val = val; + max_idx = i; + } + } + + if (peak_value != NULL) *peak_value = max_val; + if (peak_index != NULL) *peak_index = max_idx; +} + +uint16_t dr_adc_calc_baseline(const uint16_t *buffer, uint16_t num_samples) +{ + if (buffer == NULL || num_samples == 0) return 0; + + uint32_t sum = 0; + for (uint16_t i = 0; i < num_samples; i++) + { + sum += buffer[i]; + } + + return (uint16_t)(sum / num_samples); +} + +/*============================================================================== + * PUBLIC FUNCTIONS - UTILITY + *============================================================================*/ + +uint32_t dr_adc_raw_to_mv(uint16_t raw_value) +{ + /* V_mv = (raw * VREF_mv) / 4096 */ + return ((uint32_t)raw_value * m_vref_mv) / (DR_ADC_MAX_VALUE + 1); +} + +void dr_adc_set_vref(uint32_t vref_mv) +{ + m_vref_mv = vref_mv; + ADC_LOG("VREF set to %d mV", vref_mv); +} + +uint32_t dr_adc_get_vref(void) +{ + return m_vref_mv; +} + +/*============================================================================== + * PUBLIC FUNCTIONS - DEBUG + *============================================================================*/ + +bool dr_adc_test(void) +{ + if (!m_initialized) + { + ADC_LOG("Test FAIL: not initialized"); + return false; + } + + uint16_t raw; + dr_adc_err_t err = dr_adc_read_raw(&raw); + + if (err != DR_ADC_OK) + { + ADC_LOG("Test FAIL: read error %d", err); + return false; + } + + if (raw > DR_ADC_MAX_VALUE) + { + ADC_LOG("Test FAIL: invalid value %d", raw); + return false; + } + + ADC_LOG("Test PASS: raw=%d (%dmV)", raw, dr_adc_raw_to_mv(raw)); + return true; +} + +void dr_adc_print_buffer(const uint16_t *buffer, uint16_t num_samples) +{ +#ifdef DEBUG_ADC + if (buffer == NULL || num_samples == 0) return; + + ADC_LOG("Echo buffer (%d samples):", num_samples); + + for (uint16_t i = 0; i < num_samples; i++) + { + ADC_LOG("[%3d] %4d (%4dmV)", i, buffer[i], dr_adc_raw_to_mv(buffer[i])); + } +#else + (void)buffer; + (void)num_samples; +#endif +} + +/*============================================================================== + * BLE TRANSMISSION + *============================================================================*/ + +/* External BLE NUS functions and variables from main.c */ +extern void binary_tx_handler(uint8_t const *ble_bin_buff, uint16_t length); +extern void dr_binary_tx_safe(uint8_t const *ble_bin_buff, uint16_t length); +extern void dr_sd_delay_ms(uint32_t ms); + +/*============================================================================== + * INTEGRATED BURST + CAPTURE + TRANSMIT + *============================================================================*/ + +/* External piezo burst function */ +extern void dr_piezo_burst_sw(uint8_t cycles); + +/* BLE packet constants */ +#define BLE_MTU_SIZE 240 +#define BLE_FIRST_HEADER_LEN 14 /* "rec:" + header */ +#define BLE_CONT_HEADER_LEN 6 /* "red:" + packet_index */ +#define BLE_FIRST_DATA_LEN (BLE_MTU_SIZE - BLE_FIRST_HEADER_LEN) +#define BLE_CONT_DATA_LEN (BLE_MTU_SIZE - BLE_CONT_HEADER_LEN) +#define BLE_PACKET_DELAY_MS 100 /* Inter-packet delay - allow BLE TX buffer to drain */ +#define BLE_FIRST_PACKET_DELAY 5 /* Minimal delay */ + +/* Protocol version markers (OR'd with packet count in reb: header) */ +#define MEC_VERSION_MARKER 0xD000 /* vD: raa only (no ree) - ree causes packet loss */ +#define MAA_VERSION_MARKER 0xF000 /* vF: maa? hardcoded 8-channel debug */ + +/*============================================================================== + * PIEZO CHANNEL SELECTION + *============================================================================*/ + +/* Piezo MUX pins (8ch) */ +#define DR_PIEZO_EN_MUXA NRF_GPIO_PIN_MAP(0, 21) /**< MUXA Enable */ +#define DR_PIEZO_EN_MUXB NRF_GPIO_PIN_MAP(0, 23) /**< MUXB Enable */ +#define DR_PIEZO_MUX_SEL1 NRF_GPIO_PIN_MAP(0, 28) /**< MUX Select 1 */ +#define DR_PIEZO_MUX_SEL0 NRF_GPIO_PIN_MAP(1, 10) /**< MUX Select 0 */ + + /* + * Channel mapping (8ch) jhChun 26.01.29 + + * | EN MUXA | EN MUXB | SEL 0 | SEL 1 + * ---------------------------------------------- + * CH A0 | 1 | 0 | 0 | 0 + * CH A1 | 1 | 0 | 0 | 1 + * CH A2 | 1 | 0 | 1 | 0 + * CH A3 | 1 | 0 | 1 | 1 + * ---------------------------------------------- + * CH B3 | 0 | 1 | 0 | 0 + * CH B2 | 0 | 1 | 0 | 1 + * CH B1 | 0 | 1 | 1 | 0 + * CH B0 | 0 | 1 | 1 | 1 + */ + +/* dr_piezo_select_channel is defined in dr_piezo.c */ +extern void dr_piezo_select_channel(uint8_t channel); + +/*============================================================================== + * INTEGRATED BURST + CAPTURE + TRANSMIT + *============================================================================*/ + +/** + * @brief Integrated burst + capture + BLE transmit (16-bit raw data) + * + * PROTOCOL v3: Small header packet + separate data packets with delays + * (Large first packets were being dropped by BLE stack) + * + * Response format: + * Packet 1 (reb:): header only (14 bytes) + * Packet 2~N (red:): pkt_idx(2) + raw_data(up to 234 bytes = 117 samples) + * Final (ree:): total_packets(2) + * + * With 140 samples: 280 bytes = reb:(14) + red:(6+234) + red:(6+46) + ree:(6) + */ +dr_adc_err_t dr_adc_burst_capture_transmit(uint8_t freq_option, uint16_t delay_us, + uint16_t num_samples, uint8_t cycles, + uint16_t averaging, uint8_t piezo_ch, + uint8_t *ble_buffer, uint8_t skip_raa) +{ + if (ble_buffer == NULL) return DR_ADC_ERR_INVALID_PARAM; + if (num_samples == 0 || num_samples > DR_ADC_ECHO_SAMPLES_MAX) + return DR_ADC_ERR_INVALID_PARAM; + if (freq_option > 9) freq_option = 0; /* Invalid -> default 1.8MHz */ + if (cycles < 3 || cycles > 9) cycles = 5; /* Valid range: 3~9, default 5 */ + if (averaging == 0) averaging = 1; /* Minimum 1 */ + if (averaging > 1000) averaging = 1000; /* Maximum 1000 */ + if (piezo_ch > 7) piezo_ch = 0; /* Validate piezo channel: 0~7 jhChun 26.01.29 */ + + dr_adc_echo_t echo; + echo.peak_raw = 0; + echo.peak_mv = 0; + echo.peak_index = 0; + echo.peak_time_us = 0; + echo.baseline_raw = 0; + echo.num_samples = 0; + + /* Accumulator buffer for averaging (32-bit to prevent overflow) */ + static uint32_t accum_buffer[DR_ADC_ECHO_SAMPLES_MAX]; + + /* CRITICAL: Clear entire accumulator buffer at start of each mec call */ + memset(accum_buffer, 0, sizeof(accum_buffer)); + + if (!m_initialized) + { + dr_adc_err_t init_err = dr_adc_init(); + if (init_err != DR_ADC_OK) { + return init_err; + } + } + + /* Settling time for ADC power */ + nrf_delay_us(100); + + /*--- Step 2: Select piezo channel ---*/ + dr_piezo_select_channel(piezo_ch); + + /*--- Step 3~5: Burst + Delay + Capture ---*/ + if (averaging == 1) { + /*=== SINGLE MEASUREMENT (no averaging) - direct path ===*/ + + /* Execute piezo burst based on frequency option */ + switch (freq_option) { + case 0: + default: + dr_piezo_burst_sw_18mhz(cycles); /* 1.8MHz (default) */ + break; + case 1: + dr_piezo_burst_sw(cycles); /* 2.1MHz */ + break; + case 2: + dr_piezo_burst_sw_20mhz(cycles); /* 2.0MHz */ + break; + case 3: + dr_piezo_burst_sw_17mhz(cycles); /* 1.7MHz */ + break; + case 4: + dr_piezo_burst_sw_22mhz(cycles); /* 2.2MHz */ + break; + case 9: + dr_piezo_burst_sw_19mhz(cycles); /* 1.9MHz */ + break; + } + + /* Delay before capture (configurable, default 20us) */ + if (delay_us > 0) { + nrf_delay_us(delay_us); + } + + /* Capture ADC samples directly to m_echo_buffer */ + dr_adc_err_t err = dr_adc_capture_echo(m_echo_buffer, num_samples); + if (err != DR_ADC_OK) return err; + + ADC_LOG("mec: single measurement (no averaging)"); + } + else { + /*=== MULTIPLE MEASUREMENTS (with averaging) ===*/ + /* Note: accum_buffer already cleared by memset at function start */ + + for (uint16_t avg_iter = 0; avg_iter < averaging; avg_iter++) { + /* Wait for previous echo to decay before next measurement + * 1ms = ~77cm round-trip decay time (sound speed 1.54mm/us) + * Skip delay on first iteration */ + if (avg_iter > 0) { + nrf_delay_us(500); /* 500us between measurements */ + } + + /* Re-select piezo channel before each burst + * (burst functions may modify P1 port state) */ + dr_piezo_select_channel(piezo_ch); + + /* Execute piezo burst based on frequency option */ + switch (freq_option) { + case 0: + default: + dr_piezo_burst_sw_18mhz(cycles); /* 1.8MHz (default) */ + break; + case 1: + dr_piezo_burst_sw(cycles); /* 2.1MHz */ + break; + case 2: + dr_piezo_burst_sw_20mhz(cycles); /* 2.0MHz */ + break; + case 3: + dr_piezo_burst_sw_17mhz(cycles); /* 1.7MHz */ + break; + case 4: + dr_piezo_burst_sw_22mhz(cycles); /* 2.2MHz */ + break; + case 9: + dr_piezo_burst_sw_19mhz(cycles); /* 1.9MHz */ + break; + } + + /* Delay before capture (configurable, default 20us) */ + if (delay_us > 0) { + nrf_delay_us(delay_us); + } + + /* Capture ADC samples */ + dr_adc_err_t err = dr_adc_capture_echo(m_echo_buffer, num_samples); + if (err != DR_ADC_OK) return err; + + /* Accumulate samples */ + for (uint16_t i = 0; i < num_samples; i++) { + accum_buffer[i] += m_echo_buffer[i]; + } + } + + /* Calculate average and store back to m_echo_buffer */ + for (uint16_t i = 0; i < num_samples; i++) { + m_echo_buffer[i] = (uint16_t)(accum_buffer[i] / averaging); + } + + ADC_LOG("mec: averaged %u measurements", averaging); + } + + /*--- Step 6: Analyze averaged data ---*/ + dr_adc_analyze_echo(m_echo_buffer, num_samples, &echo, 100); + + /*--- Step 6: Transmit via BLE - PROTOCOL v3 ---*/ + /* Packet 1: reb:(4) + header(10) = 14 bytes ONLY (no data) */ + /* Packet 2~N: red:(4) + pkt_idx(2) + data(up to 234 bytes) */ + /* Final: ree:(4) + total_pkts(2) = 6 bytes */ + + uint16_t total_data_bytes = echo.num_samples * 2; + uint16_t cont_pkt_data_cap = BLE_MTU_SIZE - BLE_CONT_HEADER_LEN; /* 234 bytes */ + + /* Calculate data packets needed (header packet doesn't carry data) */ + uint16_t data_packets = (total_data_bytes + cont_pkt_data_cap - 1) / cont_pkt_data_cap; + + /* Version marker: select based on skip_raa (0=mec, 1=maa) */ + uint16_t version_marker = skip_raa ? MAA_VERSION_MARKER : MEC_VERSION_MARKER; + uint16_t pkts_with_ver = data_packets | version_marker; + + ADC_LOG("mec v6: samples=%u data=%u bytes, packets=%u", echo.num_samples, total_data_bytes, data_packets); + + /* Packet 1: reb: header ONLY (14 bytes) - small packet won't be dropped */ + ble_buffer[0] = 'r'; + ble_buffer[1] = 'e'; + ble_buffer[2] = 'b'; + ble_buffer[3] = ':'; + ble_buffer[4] = (uint8_t)(pkts_with_ver & 0xFF); + ble_buffer[5] = (uint8_t)(pkts_with_ver >> 8); + ble_buffer[6] = (uint8_t)(echo.peak_raw & 0xFF); + ble_buffer[7] = (uint8_t)(echo.peak_raw >> 8); + ble_buffer[8] = (uint8_t)(echo.peak_index & 0xFF); + ble_buffer[9] = (uint8_t)(echo.peak_index >> 8); + ble_buffer[10] = (uint8_t)(echo.baseline_raw & 0xFF); + ble_buffer[11] = (uint8_t)(echo.baseline_raw >> 8); + ble_buffer[12] = (uint8_t)(echo.num_samples & 0xFF); + ble_buffer[13] = (uint8_t)(echo.num_samples >> 8); + + dr_binary_tx_safe(ble_buffer, 7); /* Send header only: 14 bytes = 7 words */ + dr_sd_delay_ms(BLE_PACKET_DELAY_MS); /* Wait for BLE stack */ + + /* Data packets (red:) - starting from pkt index 0 */ + uint16_t src_idx = 0; + for (uint16_t pkt = 0; pkt < data_packets; pkt++) { + ble_buffer[0] = 'r'; + ble_buffer[1] = 'e'; + ble_buffer[2] = 'd'; + ble_buffer[3] = ':'; + ble_buffer[4] = (uint8_t)(pkt & 0xFF); + ble_buffer[5] = (uint8_t)(pkt >> 8); + + uint16_t dst_idx = 6; + uint16_t bytes_this_pkt = 0; + while (src_idx < echo.num_samples && bytes_this_pkt < cont_pkt_data_cap) { + uint16_t sample = m_echo_buffer[src_idx++] & 0x0FFF; + ble_buffer[dst_idx++] = (uint8_t)(sample & 0xFF); + ble_buffer[dst_idx++] = (uint8_t)(sample >> 8); + bytes_this_pkt += 2; + } + + dr_binary_tx_safe(ble_buffer, dst_idx / 2); /* bytes to words */ + dr_sd_delay_ms(BLE_PACKET_DELAY_MS); /* Inter-packet delay */ + } + + /* vD: Send raa: only - ree: causes subsequent packet loss */ + /* DrBench now saves channel data when receiving raa: */ + /* skip_raa: 0=send raa (mec), 1=skip raa (maa - caller sends final raa) */ + if (!skip_raa) { + ble_buffer[0] = 'r'; + ble_buffer[1] = 'a'; + ble_buffer[2] = 'a'; + ble_buffer[3] = ':'; + ble_buffer[4] = 0x00; + ble_buffer[5] = 0x00; + dr_binary_tx_safe(ble_buffer, 3); /* 6 bytes = 3 words */ + } + + ADC_LOG("mec v6: complete - reb + red*%u + skip_raa=%u (%u samples)", data_packets, skip_raa, echo.num_samples); + + return DR_ADC_OK; +} + + +/*============================================================================== + * 4-CHANNEL CAPTURE FUNCTIONS (maa? command support) + *============================================================================*/ + +dr_adc_err_t dr_adc_capture_channel_only(uint8_t freq_option, uint16_t delay_us, + uint16_t num_samples, uint8_t cycles, + uint16_t averaging, uint8_t piezo_ch, + dr_maa_channel_t *out_channel) +{ + if (out_channel == NULL) return DR_ADC_ERR_INVALID_PARAM; + if (num_samples == 0 || num_samples > MAA_SAMPLES_MAX) + return DR_ADC_ERR_INVALID_PARAM; + if (freq_option > 3) freq_option = 0; + if (cycles < 3 || cycles > 9) cycles = 5; + if (averaging == 0) averaging = 1; + if (averaging > 1000) averaging = 1000; + if (piezo_ch > (MAA_NUM_CHANNELS - 1)) piezo_ch = 0; + + /* Accumulator buffer for averaging */ + static uint32_t accum_buffer[MAA_SAMPLES_MAX]; + memset(accum_buffer, 0, num_samples * sizeof(uint32_t)); + + if (!m_initialized) { + dr_adc_err_t init_err = dr_adc_init(); + if (init_err != DR_ADC_OK) return init_err; + } + nrf_delay_us(100); + + /* Select piezo channel */ + dr_piezo_select_channel(piezo_ch); + + /* Capture with averaging */ + for (uint16_t avg_iter = 0; avg_iter < averaging; avg_iter++) { + if (avg_iter > 0) { + nrf_delay_us(500); /* Echo decay time */ + } + + dr_piezo_select_channel(piezo_ch); + + /* Execute piezo burst */ + switch (freq_option) { + case 0: + default: + dr_piezo_burst_sw_18mhz(cycles); + break; + case 1: + dr_piezo_burst_sw(cycles); + break; + case 2: + dr_piezo_burst_sw_20mhz(cycles); + break; + case 3: + dr_piezo_burst_sw_17mhz(cycles); + break; + } + + if (delay_us > 0) { + nrf_delay_us(delay_us); + } + + /* Capture to internal buffer */ + dr_adc_err_t err = dr_adc_capture_echo(m_echo_buffer, num_samples); + if (err != DR_ADC_OK) return err; + + /* Accumulate */ + for (uint16_t i = 0; i < num_samples; i++) { + accum_buffer[i] += m_echo_buffer[i]; + } + } + + /* Calculate average and copy to output */ + for (uint16_t i = 0; i < num_samples; i++) { + out_channel->samples[i] = (uint16_t)(accum_buffer[i] / averaging); + } + out_channel->num_samples = num_samples; + + /* Analyze echo data */ + dr_adc_echo_t echo; + dr_adc_analyze_echo(out_channel->samples, num_samples, &echo, 100); + out_channel->peak_raw = echo.peak_raw; + out_channel->peak_index = echo.peak_index; + out_channel->baseline_raw = echo.baseline_raw; + + ADC_LOG("maa capture CH%u: peak=%u idx=%u baseline=%u", + piezo_ch, out_channel->peak_raw, out_channel->peak_index, out_channel->baseline_raw); + + return DR_ADC_OK; +} + + +dr_adc_err_t dr_adc_transmit_channel(const dr_maa_channel_t *ch_data, + uint8_t *ble_buffer) +{ + /* Debug BEFORE null check to see if we even enter the function */ + dr_ble_debug(0x00FF, 0xAAAA); /* Function called marker */ + + if (ch_data == NULL || ble_buffer == NULL) { + dr_ble_debug(0x00FE, (ch_data == NULL ? 0x0001 : 0) | (ble_buffer == NULL ? 0x0002 : 0)); + return DR_ADC_ERR_INVALID_PARAM; + } + + dr_ble_debug(0x0100, ch_data->num_samples); /* Function entry - params valid */ + + uint16_t total_data_bytes = ch_data->num_samples * 2; + uint16_t cont_pkt_data_cap = BLE_MTU_SIZE - BLE_CONT_HEADER_LEN; /* 234 bytes */ + uint16_t data_packets = (total_data_bytes + cont_pkt_data_cap - 1) / cont_pkt_data_cap; + uint16_t total_packets = 1 + data_packets; + + /* Version marker: MAA_VERSION_MARKER | total_packets */ + uint16_t pkt_with_ver = total_packets | MAA_VERSION_MARKER; + + dr_ble_debug(0x0101, total_packets); /* Before reb: */ + + /* Packet 1: reb: header (14 bytes) */ + ble_buffer[0] = 'r'; + ble_buffer[1] = 'e'; + ble_buffer[2] = 'b'; + ble_buffer[3] = ':'; + ble_buffer[4] = (uint8_t)(pkt_with_ver & 0xFF); + ble_buffer[5] = (uint8_t)(pkt_with_ver >> 8); + ble_buffer[6] = (uint8_t)(ch_data->peak_raw & 0xFF); + ble_buffer[7] = (uint8_t)(ch_data->peak_raw >> 8); + ble_buffer[8] = (uint8_t)(ch_data->peak_index & 0xFF); + ble_buffer[9] = (uint8_t)(ch_data->peak_index >> 8); + ble_buffer[10] = (uint8_t)(ch_data->baseline_raw & 0xFF); + ble_buffer[11] = (uint8_t)(ch_data->baseline_raw >> 8); + ble_buffer[12] = (uint8_t)(ch_data->num_samples & 0xFF); + ble_buffer[13] = (uint8_t)(ch_data->num_samples >> 8); + + /* Send reb: header */ + /* Use dr_binary_tx_safe like mec? does, with dr_sd_delay_ms for SoftDevice */ + dr_binary_tx_safe(ble_buffer, 7); + dr_sd_delay_ms(BLE_PACKET_DELAY_MS); + + dr_ble_debug(0x0102, data_packets); /* After reb: */ + + /* Data packets (red:) */ + uint16_t src_idx = 0; + for (uint16_t pkt = 0; pkt < data_packets; pkt++) { + ble_buffer[0] = 'r'; + ble_buffer[1] = 'e'; + ble_buffer[2] = 'd'; + ble_buffer[3] = ':'; + ble_buffer[4] = (uint8_t)(pkt & 0xFF); + ble_buffer[5] = (uint8_t)(pkt >> 8); + + uint16_t dst_idx = 6; + uint16_t bytes_this_pkt = 0; + while (src_idx < ch_data->num_samples && bytes_this_pkt < cont_pkt_data_cap) { + uint16_t sample = ch_data->samples[src_idx++] & 0x0FFF; + ble_buffer[dst_idx++] = (uint8_t)(sample & 0xFF); + ble_buffer[dst_idx++] = (uint8_t)(sample >> 8); + bytes_this_pkt += 2; + } + + dr_binary_tx_safe(ble_buffer, dst_idx / 2); + dr_sd_delay_ms(BLE_PACKET_DELAY_MS); + + dr_ble_debug(0x0103, pkt); /* red: packet sent */ + } + + dr_ble_debug(0x0104, 0); /* Before ree: */ + + /* Final packet: ree: */ + ble_buffer[0] = 'r'; + ble_buffer[1] = 'e'; + ble_buffer[2] = 'e'; + ble_buffer[3] = ':'; + ble_buffer[4] = (uint8_t)(total_packets & 0xFF); + ble_buffer[5] = (uint8_t)(total_packets >> 8); + dr_binary_tx_safe(ble_buffer, 3); + + dr_ble_debug(0x010F, 0); /* Function complete */ + + return DR_ADC_OK; +} + + +/*============================================================================== + * DELTA COMPRESSION FUNCTIONS + *============================================================================*/ + +dr_adc_err_t dr_adc_delta_compress(const uint16_t *samples, uint16_t num_samples, + uint8_t *out_buffer, uint16_t *out_size) +{ + if (samples == NULL || out_buffer == NULL || out_size == NULL) + return DR_ADC_ERR_INVALID_PARAM; + if (num_samples == 0) { + *out_size = 0; + return DR_ADC_OK; + } + + uint16_t idx = 0; + + /* First sample: 16-bit raw value */ + out_buffer[idx++] = (uint8_t)(samples[0] & 0xFF); + out_buffer[idx++] = (uint8_t)(samples[0] >> 8); + + /* Delta encode remaining samples */ + for (uint16_t i = 1; i < num_samples; i++) { + int16_t delta = (int16_t)samples[i] - (int16_t)samples[i-1]; + + if (delta >= -127 && delta <= 127) { + /* Normal delta: fits in signed 8-bit */ + out_buffer[idx++] = (int8_t)delta; + } else { + /* Out of range: escape + 16-bit raw value */ + out_buffer[idx++] = DELTA_ESCAPE_BYTE; + out_buffer[idx++] = (uint8_t)(samples[i] & 0xFF); + out_buffer[idx++] = (uint8_t)(samples[i] >> 8); + } + } + + *out_size = idx; + return DR_ADC_OK; +} + + +dr_adc_err_t dr_adc_transmit_channel_delta(const dr_maa_channel_t *ch_data, + uint8_t *ble_buffer) +{ + if (ch_data == NULL || ble_buffer == NULL) return DR_ADC_ERR_INVALID_PARAM; + + /* Compress data first */ + static uint8_t delta_buffer[400]; /* Worst case: 140*3 = 420 bytes */ + uint16_t compressed_size = 0; + + dr_adc_err_t err = dr_adc_delta_compress(ch_data->samples, ch_data->num_samples, + delta_buffer, &compressed_size); + if (err != DR_ADC_OK) return err; + + ADC_LOG("maa delta: %u samples -> %u bytes (%.0f%%)", + ch_data->num_samples, compressed_size, + 100.0f * compressed_size / (ch_data->num_samples * 2)); + + uint16_t cont_pkt_data_cap = BLE_MTU_SIZE - BLE_CONT_HEADER_LEN; /* 234 bytes */ + uint16_t data_packets = (compressed_size + cont_pkt_data_cap - 1) / cont_pkt_data_cap; + uint16_t total_packets = 1 + data_packets; + + /* Version marker for delta mode: 0xC000 | total_packets */ + uint16_t pkt_with_ver = total_packets | 0xC000; /* v2 delta marker */ + + /* Packet 1: rdb: header (16 bytes) - includes compressed_size */ + ble_buffer[0] = 'r'; + ble_buffer[1] = 'd'; + ble_buffer[2] = 'b'; + ble_buffer[3] = ':'; + ble_buffer[4] = (uint8_t)(pkt_with_ver & 0xFF); + ble_buffer[5] = (uint8_t)(pkt_with_ver >> 8); + ble_buffer[6] = (uint8_t)(ch_data->peak_raw & 0xFF); + ble_buffer[7] = (uint8_t)(ch_data->peak_raw >> 8); + ble_buffer[8] = (uint8_t)(ch_data->peak_index & 0xFF); + ble_buffer[9] = (uint8_t)(ch_data->peak_index >> 8); + ble_buffer[10] = (uint8_t)(ch_data->baseline_raw & 0xFF); + ble_buffer[11] = (uint8_t)(ch_data->baseline_raw >> 8); + ble_buffer[12] = (uint8_t)(ch_data->num_samples & 0xFF); + ble_buffer[13] = (uint8_t)(ch_data->num_samples >> 8); + ble_buffer[14] = (uint8_t)(compressed_size & 0xFF); + ble_buffer[15] = (uint8_t)(compressed_size >> 8); + + dr_binary_tx_safe(ble_buffer, 8); /* 16 bytes = 8 words */ + dr_sd_delay_ms(BLE_PACKET_DELAY_MS); + + /* Data packets (rdd:) */ + uint16_t src_idx = 0; + for (uint16_t pkt = 0; pkt < data_packets; pkt++) { + ble_buffer[0] = 'r'; + ble_buffer[1] = 'd'; + ble_buffer[2] = 'd'; + ble_buffer[3] = ':'; + ble_buffer[4] = (uint8_t)(pkt & 0xFF); + ble_buffer[5] = (uint8_t)(pkt >> 8); + + uint16_t dst_idx = 6; + uint16_t bytes_this_pkt = 0; + while (src_idx < compressed_size && bytes_this_pkt < cont_pkt_data_cap) { + ble_buffer[dst_idx++] = delta_buffer[src_idx++]; + bytes_this_pkt++; + } + + /* Pad to word boundary if needed */ + if (dst_idx & 1) { + ble_buffer[dst_idx++] = 0; + } + + dr_binary_tx_safe(ble_buffer, dst_idx / 2); + dr_sd_delay_ms(BLE_PACKET_DELAY_MS); + } + + /* Final packet: rde: */ + ble_buffer[0] = 'r'; + ble_buffer[1] = 'd'; + ble_buffer[2] = 'e'; + ble_buffer[3] = ':'; + ble_buffer[4] = (uint8_t)(total_packets & 0xFF); + ble_buffer[5] = (uint8_t)(total_packets >> 8); + dr_binary_tx_safe(ble_buffer, 3); + dr_sd_delay_ms(BLE_PACKET_DELAY_MS); + + return DR_ADC_OK; +} + + +/*============================================================================== + * ASYNC MAA - Non-blocking 8-channel capture + * + * State machine driven by BLE TX complete events (BLE_NUS_EVT_TX_RDY) + * This prevents blocking the main loop and allows SoftDevice to process events. + *============================================================================*/ + +/* Global async context */ +static maa_async_ctx_t g_maa_ctx = { .state = MAA_ASYNC_IDLE }; + +/* Version marker for async MAA */ +#define MAA_ASYNC_VERSION_MARKER 0xC000 /* vC: async 8-channel + raa delay fix + cleanup */ + +/** + * @brief Capture one channel (internal helper) + */ +static dr_adc_err_t maa_async_capture_channel(uint8_t ch) +{ + if (ch > (MAA_NUM_CHANNELS - 1)) return DR_ADC_ERR_INVALID_PARAM; + + return dr_adc_capture_channel_only( + g_maa_ctx.freq_option, + g_maa_ctx.delay_us, + g_maa_ctx.num_samples, + g_maa_ctx.cycles, + g_maa_ctx.averaging, + ch, + &g_maa_ctx.channels[ch] + ); +} + +/** + * @brief Send reb: header for current channel + */ +static void maa_async_send_header(void) +{ + dr_maa_channel_t *ch = &g_maa_ctx.channels[g_maa_ctx.current_ch]; + uint8_t *buf = g_maa_ctx.ble_buffer; + + uint16_t total_data_bytes = ch->num_samples * 2; + uint16_t cont_pkt_data_cap = BLE_MTU_SIZE - BLE_CONT_HEADER_LEN; + g_maa_ctx.data_packets = (total_data_bytes + cont_pkt_data_cap - 1) / cont_pkt_data_cap; + g_maa_ctx.total_packets = 1 + g_maa_ctx.data_packets; + + uint16_t pkts_with_ver = g_maa_ctx.total_packets | MAA_ASYNC_VERSION_MARKER; + + /* reb: header (14 bytes) */ + buf[0] = 'r'; buf[1] = 'e'; buf[2] = 'b'; buf[3] = ':'; + buf[4] = (uint8_t)(pkts_with_ver & 0xFF); + buf[5] = (uint8_t)(pkts_with_ver >> 8); + buf[6] = (uint8_t)(ch->peak_raw & 0xFF); + buf[7] = (uint8_t)(ch->peak_raw >> 8); + buf[8] = (uint8_t)(ch->peak_index & 0xFF); + buf[9] = (uint8_t)(ch->peak_index >> 8); + buf[10] = (uint8_t)(ch->baseline_raw & 0xFF); + buf[11] = (uint8_t)(ch->baseline_raw >> 8); + buf[12] = (uint8_t)(ch->num_samples & 0xFF); + buf[13] = (uint8_t)(ch->num_samples >> 8); + + dr_binary_tx_safe(buf, 7); /* 14 bytes = 7 words */ + dr_sd_delay_ms(50); /* Allow BLE stack to process TX */ + + g_maa_ctx.current_pkt = 0; + g_maa_ctx.data_offset = 0; + g_maa_ctx.state = MAA_ASYNC_TX_DATA; + + ADC_LOG("maa_async: CH%u reb: sent (pkts=%u)", g_maa_ctx.current_ch, g_maa_ctx.total_packets); +} + +/** + * @brief Send next red: data packet + * @return true if more packets to send, false if done with current channel + */ +static bool maa_async_send_data_packet(void) +{ + dr_maa_channel_t *ch = &g_maa_ctx.channels[g_maa_ctx.current_ch]; + uint8_t *buf = g_maa_ctx.ble_buffer; + + uint16_t total_data_bytes = ch->num_samples * 2; + if (g_maa_ctx.data_offset >= total_data_bytes) { + return false; /* All data sent */ + } + + uint16_t pkt_idx = g_maa_ctx.current_pkt; + uint16_t remaining = total_data_bytes - g_maa_ctx.data_offset; + uint16_t chunk_size = (remaining > BLE_CONT_DATA_LEN) ? BLE_CONT_DATA_LEN : remaining; + + /* red: packet header */ + buf[0] = 'r'; buf[1] = 'e'; buf[2] = 'd'; buf[3] = ':'; + buf[4] = (uint8_t)(pkt_idx & 0xFF); + buf[5] = (uint8_t)(pkt_idx >> 8); + + /* Copy sample data */ + uint16_t src_sample_idx = g_maa_ctx.data_offset / 2; + uint16_t dst_idx = 6; + for (uint16_t i = 0; i < chunk_size / 2 && src_sample_idx < ch->num_samples; i++, src_sample_idx++) { + uint16_t sample = ch->samples[src_sample_idx]; + buf[dst_idx++] = (uint8_t)(sample & 0xFF); + buf[dst_idx++] = (uint8_t)(sample >> 8); + } + + dr_binary_tx_safe(buf, dst_idx / 2); + dr_sd_delay_ms(50); /* Allow BLE stack to process TX */ + + g_maa_ctx.data_offset += chunk_size; + g_maa_ctx.current_pkt++; + + ADC_LOG("maa_async: CH%u red:%u (%u/%u bytes)", + g_maa_ctx.current_ch, pkt_idx, g_maa_ctx.data_offset, total_data_bytes); + + return (g_maa_ctx.data_offset < total_data_bytes); +} + +/** + * @brief Send raa: completion marker + */ +static void maa_async_send_completion(uint16_t status) +{ + uint8_t *buf = g_maa_ctx.ble_buffer; + + /* Wait for previous TX to complete before sending raa: */ + dr_sd_delay_ms(50); + + buf[0] = 'r'; buf[1] = 'a'; buf[2] = 'a'; buf[3] = ':'; + buf[4] = (uint8_t)(status & 0xFF); + buf[5] = (uint8_t)(status >> 8); + + dr_binary_tx_safe(buf, 3); + + g_maa_ctx.state = MAA_ASYNC_IDLE; + ADC_LOG("maa_async: complete, status=0x%04X", status); +} + +/*============================================================================== + * PUBLIC ASYNC API + *============================================================================*/ + +dr_adc_err_t maa_async_start(uint8_t freq_option, uint16_t delay_us, + uint16_t num_samples, uint8_t cycles, + uint16_t averaging, uint8_t *ble_buffer) +{ + if (g_maa_ctx.state != MAA_ASYNC_IDLE) { + ADC_LOG("maa_async_start: busy"); + return DR_ADC_ERR_NOT_INIT; /* Already running */ + } + + if (ble_buffer == NULL) return DR_ADC_ERR_INVALID_PARAM; + if (num_samples == 0 || num_samples > DR_ADC_ECHO_SAMPLES_MAX) + return DR_ADC_ERR_INVALID_PARAM; + + /* Initialize context */ + g_maa_ctx.freq_option = freq_option; + g_maa_ctx.delay_us = delay_us; + g_maa_ctx.num_samples = num_samples; + g_maa_ctx.cycles = (cycles < 3 || cycles > 9) ? 5 : cycles; + g_maa_ctx.averaging = (averaging == 0) ? 1 : ((averaging > 1000) ? 1000 : averaging); + g_maa_ctx.ble_buffer = ble_buffer; + g_maa_ctx.current_ch = 0; + g_maa_ctx.current_pkt = 0; + g_maa_ctx.data_offset = 0; + + ADC_LOG("maa_async_start: freq=%u delay=%u samples=%u cycles=%u avg=%u", + freq_option, delay_us, num_samples, g_maa_ctx.cycles, g_maa_ctx.averaging); + + /* Capture CH0 */ + g_maa_ctx.state = MAA_ASYNC_CAPTURING; + dr_adc_err_t err = maa_async_capture_channel(0); + if (err != DR_ADC_OK) { + ADC_LOG("maa_async_start: CH0 capture failed (%d)", err); + maa_async_send_completion(0xFFF0); + return err; + } + + /* Send CH0 header - this will trigger TX_RDY for subsequent packets */ + maa_async_send_header(); + + return DR_ADC_OK; +} + +bool maa_async_on_tx_ready(void) +{ + if (g_maa_ctx.state == MAA_ASYNC_IDLE) { + return false; + } + + switch (g_maa_ctx.state) { + case MAA_ASYNC_TX_DATA: + /* Send next data packet */ + if (!maa_async_send_data_packet()) { + /* Current channel done, move to next */ + g_maa_ctx.current_ch++; + if (g_maa_ctx.current_ch >= MAA_NUM_CHANNELS) { + /* All channels done */ + g_maa_ctx.state = MAA_ASYNC_COMPLETE; + maa_async_send_completion(0x0000); + return false; + } else { + /* Capture next channel */ + g_maa_ctx.state = MAA_ASYNC_CAPTURING; + dr_adc_err_t err = maa_async_capture_channel(g_maa_ctx.current_ch); + if (err != DR_ADC_OK) { + ADC_LOG("maa_async: CH%u capture failed", g_maa_ctx.current_ch); + maa_async_send_completion(0xFFF0 | g_maa_ctx.current_ch); + return false; + } + /* Send header for new channel */ + maa_async_send_header(); + } + } + return true; + + case MAA_ASYNC_TX_HEADER: + /* Header sent, start sending data */ + g_maa_ctx.state = MAA_ASYNC_TX_DATA; + maa_async_send_data_packet(); + return true; + + case MAA_ASYNC_CAPTURING: + /* Shouldn't happen - capture is synchronous */ + return true; + + case MAA_ASYNC_COMPLETE: + case MAA_ASYNC_IDLE: + default: + return false; + } +} + +bool maa_async_is_busy(void) +{ + return (g_maa_ctx.state != MAA_ASYNC_IDLE); +} + +maa_async_state_t maa_async_get_state(void) +{ + return g_maa_ctx.state; +} + +void maa_async_abort(void) +{ + if (g_maa_ctx.state != MAA_ASYNC_IDLE) { + ADC_LOG("maa_async_abort: aborting from state %d", g_maa_ctx.state); + g_maa_ctx.state = MAA_ASYNC_IDLE; + } +} + diff --git a/pc_firm/dr_adc121s051/dr_adc121s051.h b/pc_firm/dr_adc121s051/dr_adc121s051.h new file mode 100644 index 0000000..6c129c3 --- /dev/null +++ b/pc_firm/dr_adc121s051/dr_adc121s051.h @@ -0,0 +1,505 @@ +/******************************************************************************* + * @file dr_adc121s051.h + * @brief ADC121S051 12-bit ADC Driver for nRF52840 + * For 1.2MHz Piezo Echo Envelope Detection + * @author Charles KWON + * @date 2025-12-15 + * + * @details This driver reads the envelope-detected DC level from piezo echo. + * + * Signal Flow: + * + * [Piezo TX] [Echo RX] [Envelope] [ADC] [MCU] + * 1.2MHz --> Reflect --> Detector --> DC Level --> Digital + * burst signal (hardware) reading value + * + * The envelope detector circuit converts the 1.2MHz echo burst + * into a DC voltage proportional to the echo amplitude. + * ADC samples this DC level for amplitude measurement. + * + * @note Hardware: Texas Instruments ADC121S051 + * - 12-bit resolution (0-4095) + * - Sample rate: 200-500 ksps + * - Input range: 0V to VA + * - SPI interface (software bit-bang) + ******************************************************************************/ + +#ifndef DR_ADC121S051_H +#define DR_ADC121S051_H + +#include +#include +#include "nrf_gpio.h" + +/*============================================================================== + * PIN CONFIGURATION + * + * WARNING: Never hardcode pin numbers! + * Hardcoding may save a developer's time momentarily, + * but it will also shorten their lifespan. + *============================================================================*/ +#define DR_ADC_PIN_SCLK NRF_GPIO_PIN_MAP(0, 14) /**< Serial Clock */ +#define DR_ADC_PIN_SDATA NRF_GPIO_PIN_MAP(0, 15) /**< Serial Data (MISO) */ +#define DR_ADC_PIN_CS NRF_GPIO_PIN_MAP(0, 19) /**< Chip Select P0.13 -> P0.19 */ + +/*============================================================================== + * ADC SPECIFICATIONS + *============================================================================*/ +#define DR_ADC_RESOLUTION 12 /**< Bits */ +#define DR_ADC_MAX_VALUE 4095 /**< 2^12 - 1 */ +#define DR_ADC_VREF_MV 3300 /**< Reference voltage (mV) */ + +/*============================================================================== + * ECHO DETECTION CONFIGURATION + * + * Bladder Measurement Requirements: + * - Target measurement range: 20cm (200mm) + * - SCLK frequency: 8.6MHz (bit-bang SPI) + * - ADC121S051 requires 16 SCLK cycles per sample + * - Actual sample rate: 8.6MHz / 16 = 0.5375MHz = 537.5kHz + * - Actual sample interval: 16 / 8.6MHz = 1.86us + * - Sound speed in tissue: 1540m/s = 1.54mm/us + * + * Formula: samples = distance(mm) * 2 / (1.86us * 1.54mm/us) + * = distance(mm) * 2 / 2.86 + * = distance(mm) * 0.7 + * + * 10cm = 100mm -> 100 * 0.7 = 70 samples (round-trip 130us) + * 17cm = 170mm -> 170 * 0.7 = 119 samples (round-trip 221us) + * 20cm = 200mm -> 200 * 0.7 = 140 samples (round-trip 260us) + * + * Buffer size: 200 samples * 2 bytes = 400 bytes (RAM 256KB, OK) + * BLE transmission: 140 samples * 2 bytes = 280 bytes (16-bit raw, no packing) + *============================================================================*/ +#define DR_ADC_SCLK_MHZ 8.6f /**< SPI bit-bang SCLK frequency */ +#define DR_ADC_CLOCKS_PER_SAMPLE 16 /**< ADC121S051: 16 SCLK per sample */ +#define DR_ADC_ECHO_SAMPLES_MAX 100 /**< Maximum samples */ +#define DR_ADC_ECHO_SAMPLES_DEFAULT 100 /**< Default samples */ +#define DR_ADC_SAMPLE_INTERVAL_US 1.86f /**< 16 / 8.6MHz = 1.86us per sample */ +#define DR_ADC_SOUND_SPEED_MM_US 1.54f /**< Sound speed in tissue (mm/us) */ + +/*============================================================================== + * ERROR CODES + *============================================================================*/ +typedef enum { + DR_ADC_OK = 0, + DR_ADC_ERR_NOT_INIT, + DR_ADC_ERR_INVALID_PARAM, + DR_ADC_ERR_NO_ECHO +} dr_adc_err_t; + +/*============================================================================== + * DATA STRUCTURES + *============================================================================*/ + +/** + * @brief Single ADC reading result + */ +typedef struct { + uint16_t raw; /**< Raw 12-bit value (0-4095) */ + uint32_t voltage_mv; /**< Voltage in millivolts */ +} dr_adc_result_t; + +/** + * @brief Echo measurement result + */ +typedef struct { + uint16_t peak_raw; /**< Peak amplitude (raw) */ + uint32_t peak_mv; /**< Peak amplitude (mV) */ + uint16_t peak_index; /**< Sample index of peak */ + uint32_t peak_time_us; /**< Time to peak (us) */ + uint16_t baseline_raw; /**< Baseline level before echo */ + uint16_t num_samples; /**< Number of samples captured */ +} dr_adc_echo_t; + +/** + * @brief Echo capture configuration + */ +typedef struct { + uint16_t num_samples; /**< Samples to capture (1-200) */ + uint16_t threshold_raw; /**< Minimum peak threshold */ + uint16_t delay_us; /**< Delay before capture starts */ +} dr_adc_echo_config_t; + +/*============================================================================== + * INITIALIZATION + *============================================================================*/ + +/** + * @brief Initialize ADC driver + * @return dr_adc_err_t Error code + */ +dr_adc_err_t dr_adc_init(void); + +/** + * @brief Uninitialize ADC driver + */ +void dr_adc_uninit(void); + +/** + * @brief Check if initialized + */ +bool dr_adc_is_initialized(void); + +/*============================================================================== + * BASIC READ FUNCTIONS + *============================================================================*/ + +/** + * @brief Read single ADC value + * @param result Pointer to result structure + * @return dr_adc_err_t Error code + */ +dr_adc_err_t dr_adc_read(dr_adc_result_t *result); + +/** + * @brief Read raw 12-bit value only + * @param raw_value Pointer to store value + * @return dr_adc_err_t Error code + */ +dr_adc_err_t dr_adc_read_raw(uint16_t *raw_value); + +/** + * @brief Read averaged value + * @param result Pointer to result structure + * @param num_samples Number of samples to average (1-256) + * @return dr_adc_err_t Error code + */ +dr_adc_err_t dr_adc_read_averaged(dr_adc_result_t *result, uint16_t num_samples); + +/*============================================================================== + * ECHO DETECTION FUNCTIONS + *============================================================================*/ + +/** + * @brief Capture echo envelope after piezo burst + * @param buffer Array to store samples (must be pre-allocated) + * @param num_samples Number of samples to capture + * @return dr_adc_err_t Error code + * + * @note Call this immediately after dr_piezo_burst_sw() + */ +dr_adc_err_t dr_adc_capture_echo(uint16_t *buffer, uint16_t num_samples); + +/** + * @brief Capture and analyze echo in one call + * @param echo Pointer to echo result structure + * @param config Pointer to capture configuration (NULL for defaults) + * @return dr_adc_err_t Error code + */ +dr_adc_err_t dr_adc_measure_echo(dr_adc_echo_t *echo, const dr_adc_echo_config_t *config); + +/** + * @brief Piezo burst + Echo capture in one call + * @param cycles Number of burst cycles (3~9) + * @param delay_us Delay before capture (us) + * @param num_samples Number of samples to capture + * @param echo Pointer to echo result structure + * @return dr_adc_err_t Error code + */ +dr_adc_err_t dr_adc_burst_and_capture(uint8_t cycles, uint16_t delay_us, + uint16_t num_samples, dr_adc_echo_t *echo); + +/** + * @brief Get pointer to last captured echo buffer + * @return Pointer to internal buffer (valid until next capture) + * @note Buffer contains num_samples values from last burst_and_capture call + */ +const uint16_t* dr_adc_get_echo_buffer(void); + +/** + * @brief Analyze captured echo buffer + * @param buffer Sample buffer + * @param num_samples Number of samples in buffer + * @param echo Pointer to echo result structure + * @param threshold Minimum threshold for valid peak + * @return dr_adc_err_t Error code + */ +dr_adc_err_t dr_adc_analyze_echo(const uint16_t *buffer, uint16_t num_samples, + dr_adc_echo_t *echo, uint16_t threshold); + +/** + * @brief Find peak in buffer + * @param buffer Sample buffer + * @param num_samples Number of samples + * @param peak_value Pointer to store peak value + * @param peak_index Pointer to store peak index (can be NULL) + */ +void dr_adc_find_peak(const uint16_t *buffer, uint16_t num_samples, + uint16_t *peak_value, uint16_t *peak_index); + +/** + * @brief Calculate baseline (average of first N samples) + * @param buffer Sample buffer + * @param num_samples Number of samples to average for baseline + * @return Baseline value + */ +uint16_t dr_adc_calc_baseline(const uint16_t *buffer, uint16_t num_samples); + +/*============================================================================== + * UTILITY FUNCTIONS + *============================================================================*/ + +/** + * @brief Convert raw value to millivolts + * @param raw_value Raw 12-bit value + * @return Voltage in millivolts + */ +uint32_t dr_adc_raw_to_mv(uint16_t raw_value); + +/** + * @brief Set reference voltage + * @param vref_mv Reference voltage in millivolts + */ +void dr_adc_set_vref(uint32_t vref_mv); + +/** + * @brief Get reference voltage + * @return Reference voltage in millivolts + */ +uint32_t dr_adc_get_vref(void); + +/*============================================================================== + * DEBUG FUNCTIONS + *============================================================================*/ + +/** + * @brief Test ADC communication + * @return true if OK + */ +bool dr_adc_test(void); + +/** + * @brief Print echo buffer to debug output + * @param buffer Sample buffer + * @param num_samples Number of samples + */ +void dr_adc_print_buffer(const uint16_t *buffer, uint16_t num_samples); + +/*============================================================================== + * POWER CONTROL + *============================================================================*/ + + +/*============================================================================== + * BLE TRANSMISSION CALLBACK + *============================================================================*/ + +/*============================================================================== + * INTEGRATED BURST + CAPTURE + TRANSMIT + *============================================================================*/ + +/** + * @brief Piezo burst + ADC capture + BLE transmission (all-in-one) + * + * This function performs the complete measurement cycle internally: + * 1. Power on ADC + * 2. Select piezo channel (0~7) + * 3. Execute piezo burst (frequency based on freq_option) + * 4. Capture echo samples (after delay_us) - repeated 'averaging' times + * 5. Average the captured samples (firmware-level noise reduction) + * 6. Analyze peak/baseline + * 7. Transmit data via BLE with proper packet timing + * + * @param freq_option Frequency option: 0=1.8MHz (default), 1=2.1MHz, 2=2.0MHz, 3=1.7MHz + * @param delay_us Delay before capture (us), default 20 + * @param num_samples Number of samples to capture (1~200) + * @param cycles Number of burst cycles (3~7), default 5 + * @param averaging Number of measurements to average (1~1000), default 1 + * @param piezo_ch Piezo channel to use (0~7), default 0 + * @param ble_buffer Working buffer for BLE packets (must be >= 240 bytes) + * @return dr_adc_err_t Error code + * + * @note Must call dr_adc_register_ble_tx() before using this function + * @note BLE packets: reb: (header), red: (data), ree: (end) + * @note Higher averaging reduces noise but increases measurement time (~0.3ms per avg) + */ +dr_adc_err_t dr_adc_burst_capture_transmit(uint8_t freq_option, uint16_t delay_us, + uint16_t num_samples, uint8_t cycles, + uint16_t averaging, uint8_t piezo_ch, + uint8_t *ble_buffer, uint8_t skip_raa); + +/** + * @brief Select piezo channel (0~7) + * @param channel Piezo channel number (0~7) + * + * @note Hardware-dependent: requires MUX or individual GPIO control + * Currently uses placeholder - implement based on actual hardware + */ +void dr_piezo_select_channel(uint8_t channel); + +/*============================================================================== + * 4-CHANNEL CAPTURE (maa? command support) + *============================================================================*/ + +/** + * @brief 8-channel echo buffer for maa? command + * Memory: 140 samples × 2 bytes × 8 channels = 2,240 bytes + */ +#define MAA_NUM_CHANNELS 8 /* 4 -> 8 jhChun 26.02.12*/ +#define MAA_SAMPLES_MAX 200 + +/** + * @brief Echo data for one channel + */ +typedef struct { + uint16_t samples[MAA_SAMPLES_MAX]; /**< Raw sample data */ + uint16_t num_samples; /**< Actual sample count */ + uint16_t peak_raw; /**< Peak amplitude */ + uint16_t peak_index; /**< Peak sample index */ + uint16_t baseline_raw; /**< Baseline level */ +} dr_maa_channel_t; + +/** + * @brief Capture echo from one channel (no BLE transmission) + * + * Captures averaged echo data for a single channel and stores + * in the provided channel buffer. Does NOT transmit via BLE. + * + * @param freq_option Frequency: 0=1.8MHz, 1=2.1MHz, 2=2.0MHz, 3=1.7MHz + * @param delay_us Delay before capture (us) + * @param num_samples Number of samples (1~200) + * @param cycles Burst cycles (3~7) + * @param averaging Number of averages (1~1000) + * @param piezo_ch Piezo channel (0~7) + * @param out_channel Output channel data structure + * @return dr_adc_err_t Error code + */ +dr_adc_err_t dr_adc_capture_channel_only(uint8_t freq_option, uint16_t delay_us, + uint16_t num_samples, uint8_t cycles, + uint16_t averaging, uint8_t piezo_ch, + dr_maa_channel_t *out_channel); + +/** + * @brief Transmit captured channel data via BLE + * + * Sends previously captured channel data using reb:/red:/ree: protocol. + * + * @param ch_data Pointer to captured channel data + * @param ble_buffer Working buffer for BLE packets (>= 240 bytes) + * @return dr_adc_err_t Error code + */ +dr_adc_err_t dr_adc_transmit_channel(const dr_maa_channel_t *ch_data, + uint8_t *ble_buffer); + +/*============================================================================== + * DELTA COMPRESSION (maa? mode=1) + * + * Format: + * Byte 0-1: First sample (16-bit, little endian) + * Byte 2+: Delta values (8-bit signed) + * If delta > 127 or < -127: escape (0x80) + 16-bit value + * + * Expected compression: ~50% (280 bytes -> ~140 bytes) + *============================================================================*/ + +#define DELTA_ESCAPE_BYTE 0x80 /**< Escape marker for out-of-range delta */ + +/** + * @brief Compress sample data using delta encoding + * + * @param samples Input sample array (16-bit values) + * @param num_samples Number of samples + * @param out_buffer Output buffer for compressed data + * @param out_size Output: number of bytes written + * @return dr_adc_err_t Error code + */ +dr_adc_err_t dr_adc_delta_compress(const uint16_t *samples, uint16_t num_samples, + uint8_t *out_buffer, uint16_t *out_size); + +/** + * @brief Transmit captured channel data via BLE with delta compression + * + * Uses rdb:/rdd:/rde: protocol (delta variant of reb:/red:/ree:) + * + * @param ch_data Pointer to captured channel data + * @param ble_buffer Working buffer for BLE packets (>= 240 bytes) + * @return dr_adc_err_t Error code + */ +dr_adc_err_t dr_adc_transmit_channel_delta(const dr_maa_channel_t *ch_data, + uint8_t *ble_buffer); + +/*============================================================================== + * ASYNC MAA - Non-blocking 8-channel capture + * + * Design: State machine driven by BLE TX complete events + * Flow: + * maa? cmd -> maa_async_start() -> capture CH0 -> TX reb: -> TX red: ... + * BLE_NUS_EVT_TX_RDY -> maa_async_continue() -> TX next packet or next channel + * All done -> TX raa: -> state=IDLE + *============================================================================*/ + +/** @brief MAA async state machine states */ +typedef enum { + MAA_ASYNC_IDLE = 0, /**< Not active */ + MAA_ASYNC_CAPTURING, /**< ADC capture in progress */ + MAA_ASYNC_TX_HEADER, /**< Sending reb: header */ + MAA_ASYNC_TX_DATA, /**< Sending red: data packets */ + MAA_ASYNC_NEXT_CHANNEL, /**< Preparing next channel */ + MAA_ASYNC_COMPLETE /**< Sending raa: and finishing */ +} maa_async_state_t; + +/** @brief MAA async context */ +typedef struct { + maa_async_state_t state; /**< Current state */ + uint8_t current_ch; /**< Current channel (0~7) */ + uint8_t current_pkt; /**< Current packet index */ + uint16_t data_offset; /**< Bytes sent so far for current channel */ + uint8_t freq_option; /**< Frequency option */ + uint16_t delay_us; /**< Capture delay */ + uint16_t num_samples; /**< Samples per channel */ + uint8_t cycles; /**< Burst cycles */ + uint16_t averaging; /**< Averaging count */ + uint8_t *ble_buffer; /**< Working buffer for BLE packets */ + dr_maa_channel_t channels[MAA_NUM_CHANNELS]; /**< Captured data for each channel */ + uint16_t total_packets; /**< Total packets for current channel */ + uint16_t data_packets; /**< Data packets for current channel */ +} maa_async_ctx_t; + +/** + * @brief Start async MAA 8-channel capture + * + * Initiates the async state machine. Captures CH0 and begins transmission. + * Subsequent packets are sent when maa_async_on_tx_ready() is called. + * + * @param freq_option Frequency: 0=1.8MHz, 1=2.1MHz, 2=2.0MHz, 3=1.7MHz + * @param delay_us Capture delay (us) + * @param num_samples Samples per channel (1~200) + * @param cycles Burst cycles (3~9) + * @param averaging Averaging count (1~1000) + * @param ble_buffer Working buffer (>= 240 bytes) + * @return dr_adc_err_t DR_ADC_OK if started successfully + */ +dr_adc_err_t maa_async_start(uint8_t freq_option, uint16_t delay_us, + uint16_t num_samples, uint8_t cycles, + uint16_t averaging, uint8_t *ble_buffer); + +/** + * @brief Handle BLE TX ready event + * + * Called from BLE_NUS_EVT_TX_RDY handler. Sends next packet or + * transitions to next state. + * + * @return true if more work pending, false if complete or idle + */ +bool maa_async_on_tx_ready(void); + +/** + * @brief Check if async MAA is active + * @return true if state != IDLE + */ +bool maa_async_is_busy(void); + +/** + * @brief Get current async state (for debugging) + * @return Current state + */ +maa_async_state_t maa_async_get_state(void); + +/** + * @brief Abort async MAA operation + */ +void maa_async_abort(void); + +#endif /* DR_ADC121S051_H */ + diff --git a/pc_firm/dr_lua/dr_lua.c b/pc_firm/dr_lua/dr_lua.c new file mode 100644 index 0000000..5c658b1 --- /dev/null +++ b/pc_firm/dr_lua/dr_lua.c @@ -0,0 +1,105 @@ +/* + * dr_lua.c + * + * Author : Charles KWON + * + * This module provides a simple and reusable interface layer + * between C firmware code and embedded Lua scripts. + * It is designed to make calling Lua functions from C firmware + * straightforward and safe. + * + * Example: + * Lua function: add3(x) = x + 3 + * C function : dr_lua_add3(int num1) + */ + +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" + +/* ------------------------------------------------- + * External output function (UART / RTT, etc.) + * This function must be implemented by the project. + * ------------------------------------------------- */ +extern void board_print(const char *s); + +/* ------------------------------------------------- + * Global Lua state + * ------------------------------------------------- */ +static lua_State *g_L = NULL; + +/* ------------------------------------------------- + * Initialize Lua VM + * ------------------------------------------------- */ +void dr_lua_init(void) +{ + g_L = luaL_newstate(); + + /* Load base library only */ + luaL_requiref(g_L, "_G", luaopen_base, 1); + lua_pop(g_L, 1); +} + +/* ------------------------------------------------- + * Load Lua script + * Defines: + * function add3(x) + * return x + 3 + * end + * ------------------------------------------------- */ +void dr_lua_load_script(void) +{ + const char *lua_code = + "function add3(x)\n" + " return x + 3\n" + "end"; + + if (luaL_dostring(g_L, lua_code) != LUA_OK) { + const char *err = lua_tostring(g_L, -1); + board_print(err); + lua_pop(g_L, 1); + } +} + +/* ------------------------------------------------- + * Core interface function + * Calls Lua function add3 from C firmware + * + * @param num1 Input integer value + * @return Result of (num1 + 3) + * -1 if Lua function is not found + * -2 if Lua call fails + * ------------------------------------------------- */ +int dr_lua_add3(int num1) +{ + int result = 0; + + /* 1. Get Lua function: add3 */ + lua_getglobal(g_L, "add3"); + + if (!lua_isfunction(g_L, -1)) { + lua_pop(g_L, 1); + return -1; /* Function not found */ + } + + /* 2. Push argument */ + lua_pushinteger(g_L, num1); + + /* 3. Call Lua function (1 argument, 1 return value) */ + if (lua_pcall(g_L, 1, 1, 0) != LUA_OK) { + const char *err = lua_tostring(g_L, -1); + board_print(err); + lua_pop(g_L, 1); + return -2; /* Call failed */ + } + + /* 4. Read return value */ + if (lua_isinteger(g_L, -1)) { + result = (int)lua_tointeger(g_L, -1); + } + + /* 5. Clean up Lua stack */ + lua_pop(g_L, 1); + + return result; /* num1 + 3 */ +} diff --git a/pc_firm/dr_util/dr_util.c b/pc_firm/dr_util/dr_util.c new file mode 100644 index 0000000..11317c6 --- /dev/null +++ b/pc_firm/dr_util/dr_util.c @@ -0,0 +1,82 @@ +#include "dr_util.h" +#include "parser.h" + +extern void single_format_data(uint8_t *buffer, const char *tag, uint16_t value); +extern void format_data(uint8_t *buffer, const char *tag, uint16_t *data, uint8_t length); +extern void binary_tx_handler(uint8_t *buffer, uint8_t length); +extern uint8_t ble_bin_buffer[]; + +/* Use dr_binary_tx_safe from main.c - has retry logic for BLE TX queue */ +extern void dr_binary_tx_safe(uint8_t const *ble_bin_buff, uint16_t length); + +void dr_ble_return_1(const char *tag, uint16_t value) +{ + single_format_data(ble_bin_buffer, tag, value); + dr_binary_tx_safe(ble_bin_buffer, 3); /* Use safe TX with retry */ +} + +void dr_ble_return_2(const char *tag, uint16_t v1, uint16_t v2) +{ + ble_bin_buffer[0] = tag[0]; + ble_bin_buffer[1] = tag[1]; + ble_bin_buffer[2] = tag[2]; + ble_bin_buffer[3] = tag[3]; + + ble_bin_buffer[4] = (uint8_t)(v1 >> 8); + ble_bin_buffer[5] = (uint8_t)(v1 & 0xFF); + ble_bin_buffer[6] = (uint8_t)(v2 >> 8); + ble_bin_buffer[7] = (uint8_t)(v2 & 0xFF); + + dr_binary_tx_safe(ble_bin_buffer, 4); /* Use safe TX with retry */ +} + +void dr_ble_return_3(const char *tag, uint16_t v1, uint16_t v2, uint16_t v3) +{ + ble_bin_buffer[0] = tag[0]; + ble_bin_buffer[1] = tag[1]; + ble_bin_buffer[2] = tag[2]; + ble_bin_buffer[3] = tag[3]; + + ble_bin_buffer[4] = (uint8_t)(v1 >> 8); + ble_bin_buffer[5] = (uint8_t)(v1 & 0xFF); + ble_bin_buffer[6] = (uint8_t)(v2 >> 8); + ble_bin_buffer[7] = (uint8_t)(v2 & 0xFF); + ble_bin_buffer[8] = (uint8_t)(v3 >> 8); + ble_bin_buffer[9] = (uint8_t)(v3 & 0xFF); + + dr_binary_tx_safe(ble_bin_buffer, 5); /* Use safe TX with retry */ +} + +void dr_ble_debug(uint16_t point_id, uint16_t value) +{ + /* Use dedicated buffer to avoid conflicts with ble_bin_buffer */ + static uint8_t dbg_buffer[8]; + + dbg_buffer[0] = 'd'; + dbg_buffer[1] = 'b'; + dbg_buffer[2] = 'g'; + dbg_buffer[3] = ':'; + + dbg_buffer[4] = (uint8_t)(point_id >> 8); + dbg_buffer[5] = (uint8_t)(point_id & 0xFF); + dbg_buffer[6] = (uint8_t)(value >> 8); + dbg_buffer[7] = (uint8_t)(value & 0xFF); + + dr_binary_tx_safe(dbg_buffer, 4); +} + +void dr_ble_return_piezo_1(const char *tag, uint16_t value) +{ + /* Use dedicated buffer for piezo responses to avoid conflicts with ble_bin_buffer */ + static uint8_t piezo_buffer[8]; + + piezo_buffer[0] = tag[0]; + piezo_buffer[1] = tag[1]; + piezo_buffer[2] = tag[2]; + piezo_buffer[3] = tag[3]; + + piezo_buffer[4] = (uint8_t)(value >> 8); + piezo_buffer[5] = (uint8_t)(value & 0xFF); + + dr_binary_tx_safe(piezo_buffer, 3); /* 6 bytes = 3 words */ +} diff --git a/pc_firm/dr_util/dr_util.h b/pc_firm/dr_util/dr_util.h new file mode 100644 index 0000000..4cf8078 --- /dev/null +++ b/pc_firm/dr_util/dr_util.h @@ -0,0 +1,18 @@ +#ifndef DR_UTIL_H +#define DR_UTIL_H + +#include + +void dr_ble_return_1(const char *tag, uint16_t value); +void dr_ble_return_2(const char *tag, uint16_t v1, uint16_t v2); +void dr_ble_return_3(const char *tag, uint16_t v1, uint16_t v2, uint16_t v3); +void dr_ble_return_3_be(const char *tag, uint16_t v1, uint16_t v2, uint16_t v3); + +/* Piezo dedicated BLE return - uses separate buffer to avoid conflicts */ +void dr_ble_return_piezo_1(const char *tag, uint16_t value); + +/* BLE debug output - sends "dbg:" + point_id + value */ +void dr_ble_debug(uint16_t point_id, uint16_t value); + +#endif /* DR_UTIL_H */ + diff --git a/pc_firm/f.bat b/pc_firm/f.bat new file mode 100644 index 0000000..bd86db5 --- /dev/null +++ b/pc_firm/f.bat @@ -0,0 +1,2 @@ +gcc -std=c99 -Wall -Wextra -o parser.exe parser.c pcmain.c + diff --git a/pc_firm/mt_nirs.code-workspace b/pc_firm/mt_nirs.code-workspace new file mode 100644 index 0000000..79d4694 --- /dev/null +++ b/pc_firm/mt_nirs.code-workspace @@ -0,0 +1,11 @@ +{ + "folders": [ + { + "path": "../mt_nirs" + }, + { + "path": "." + } + ], + "settings": {} +} \ No newline at end of file diff --git a/pc_firm/parser.c b/pc_firm/parser.c new file mode 100644 index 0000000..62c638a --- /dev/null +++ b/pc_firm/parser.c @@ -0,0 +1,1317 @@ +/* + * 2025-12-08 power loop bug fix + * 2025-12-07 msn, mta, mqq + * 2025-12-04 by Charles KWON + * parser.c : Common parser + command table + handlers + * - Firmware/PC shared + * - Hardware-dependent parts left as TODO + * - Added CRC16 validation support + */ + +#include "parser.h" +#include + +#include "nrf_gpio.h" +#include "nrf_delay.h" +#include "dr_piezo.h" +#include "dr_util.h" +#include "dr_adc121s051.h" + + + +// ======================================== +// External function declarations +// ======================================== + +/* Sensor functions */ +extern void battery_level_meas(void); +extern void pressure_all_level_meas(void); +extern void tmp235_voltage_level_meas(void); + + +/* Device control functions */ +extern int device_activated(void); +extern int device_sleep_mode(void); + +/* Error handling */ +extern void param_error(const char *cmd); + +/* BLE transmission */ +extern void single_format_data(uint8_t *buffer, const char *tag, uint16_t value); +extern void ascii_format_data(uint8_t *buffer, const char *tag, const char *ascii, uint8_t len); +extern void binary_tx_handler(const uint8_t *buffer, uint16_t length); +extern void dr_sd_delay_ms(uint32_t ms); /* Softdevice-friendly delay */ + +/* FDS config (fstorage) */ +#include "fstorage.h" +extern char SERIAL_NO[12]; +extern char HW_NO[12]; + + + + +// ======================================== +// External variables +// ======================================== + +extern volatile bool processing; +extern bool device_status; +extern uint8_t resetCount; +extern uint8_t ble_bin_buffer[]; + +extern uint8_t simple_samples_in_buffer; +extern uint8_t m_pd_adc_cnt; +extern bool con_single; +extern bool lock_check; + + +extern bool info4; // addtional info +extern bool ble_got_new_data; // BLE data flag +extern uint8_t m_pd_adc_cnt; // PD ADC count +extern bool go_batt; // battery +extern bool motion_data_once; // IMU data flag +extern bool motion_raw_data_enabled; // IMU continuous flag +extern int imu_read_direct(void); // IMU direct register read + BLE send +extern uint8_t ADC_PD_MODE; // PD ADC mode +extern bool pd_adc_m48_start; // PD ADC M48 start flag +extern uint8_t m48_samples_in_buffer; // M48 sample count + +extern void pressure_all_level_meas(void); // pressure sensor +extern void battery_timer_stop(void); // battery timer +extern void main_timer_start(void); // main timer +extern void hw_i2c_init_once(void); // I2C init for IMU + +/* AGC_GAIN_SW is a macro in measurements.h - replicate here */ +#include "nrf_gpio.h" +#define GAIN_SW_PIN NRF_GPIO_PIN_MAP(0, 20) +#define AGC_GAIN_SW(x) do { if(x) nrf_gpio_pin_set(GAIN_SW_PIN); else nrf_gpio_pin_clear(GAIN_SW_PIN); } while(0) + + +extern void dr_piezo_power_on( void ); +extern void dr_piezo_burst_sw(uint8_t cycles); +extern void dr_piezo_burst_sw_18mhz(uint8_t cycles); +extern void dr_piezo_burst_sw_20mhz(uint8_t cycles); +extern void dr_piezo_burst_sw_17mhz(uint8_t cycles); + +/* ---- Global variable definitions (extern in header) ---- */ +dr_platform_if_t g_plat = { 0, 0, 0 }; +bool g_log_enable = false; + +/* ---- Internal constants/structures ---- */ + +#define DR_MAX_DATA 128 /* Max data length after TAG */ + +typedef struct { + char tag[5]; /* "sta?" etc 4 chars + '\0' */ + uint8_t data[DR_MAX_DATA]; /* Raw data after TAG */ + uint8_t data_len; /* Length of data[] */ +} ParsedCmd; + +/* ---- Internal utility functions ---- */ + +/* Copy TAG */ +static void dr_copy_tag(const uint8_t *buf, char *tag_out) +{ + tag_out[0] = (char)buf[0]; + tag_out[1] = (char)buf[1]; + tag_out[2] = (char)buf[2]; + tag_out[3] = (char)buf[3]; + tag_out[4] = '\0'; +} + +/* TAG comparison (4 chars) */ +static bool dr_tag_eq(const char *tag, const char *key4) +{ + return (tag[0] == key4[0] && + tag[1] == key4[1] && + tag[2] == key4[2] && + tag[3] == key4[3]); +} + +/* Extract uint16 little endian: word_index based (0 -> data[0], data[1]) */ +/* Extract uint16 BIG endian: word_index based (0 -> data[0], data[1]) */ +/* Extract uint16 LITTLE endian: word_index based (0 -> data[0], data[1]) */ +static bool dr_get_u16(const ParsedCmd *cmd, uint8_t word_index, uint16_t *out) +{ + uint8_t pos = (uint8_t)(word_index * 2); + if (cmd->data_len < (uint8_t)(pos + 2)) { + return false; + } + + // Little Endian: data[pos] = low byte, data[pos+1] = high byte + *out = (uint16_t)cmd->data[pos] + | (uint16_t)((uint16_t)cmd->data[pos + 1] << 8); + + return true; +} + +/* Extract ASCII: data[offset..offset+len] -> out, '\0' terminated */ +/* EEPROM에서 텍스트 쓸 때 ASCII 문자열 추출 -> 추후 Flash Memory 커맨드에서 재활용 가능 */ +static void dr_get_ascii(const ParsedCmd *cmd, uint8_t offset, + char *out, uint8_t max_len) +{ + uint8_t i; + uint8_t remain; + + if (offset >= cmd->data_len) { + out[0] = '\0'; + return; + } + + remain = (uint8_t)(cmd->data_len - offset); + if (remain > max_len) { + remain = max_len; + } + + for (i = 0; i < remain; i++) { + out[i] = (char)cmd->data[offset + i]; + } + out[remain] = '\0'; +} + +/* ---- CRC16 functions ---- */ + +/* CRC16 computation - matches Nordic SDK crc16_compute */ +static uint16_t dr_crc16_compute(const uint8_t *p_data, uint32_t size, const uint16_t *p_crc) +{ + uint32_t i; + uint16_t crc = (p_crc == NULL) ? 0xFFFF : *p_crc; + + for (i = 0; i < size; i++) + { + crc = (uint8_t)(crc >> 8) | (crc << 8); + crc ^= p_data[i]; + crc ^= (uint8_t)(crc & 0xFF) >> 4; + crc ^= (crc << 8) << 4; + crc ^= ((crc & 0xFF) << 4) << 1; + } + + return crc; +} + +/* CRC16 check: compare computed vs expected */ +static bool dr_crc16_check(const uint8_t *p_data, uint32_t data_len, uint16_t expected_crc) +{ + uint16_t computed_crc = dr_crc16_compute(p_data, data_len, NULL); + return (computed_crc == expected_crc); +} + +/* CRC16 packet check: last 2 bytes are CRC (little endian) */ +static bool dr_crc16_check_packet(const uint8_t *packet, uint32_t packet_len) +{ + uint16_t expected_crc; + uint32_t data_len; + + if (packet_len < 2) { + return false; + } + + data_len = packet_len - 2; + + /* Extract CRC: little endian (low byte first) */ + expected_crc = (uint16_t)packet[packet_len - 2] + | ((uint16_t)packet[packet_len - 1] << 8); + + if (g_plat.log && g_log_enable) { + g_plat.log("CRC check: expected=0x%04X\n", expected_crc); + } + + return dr_crc16_check(packet, data_len, expected_crc); +} + +/* ---- Raw buffer -> ParsedCmd ---- */ + +static bool dr_parse_cmd(const uint8_t *buffer, uint8_t length, ParsedCmd *out) +{ + uint8_t i; + + if (length < 4) { + return false; /* Not even TAG received */ + } + + /* CRC check if enabled */ + if (g_plat.crc_check) { + if (!dr_crc16_check_packet(buffer, length)) { + if (g_plat.log && g_log_enable) { + g_plat.log("CRC check FAILED!\n"); + } + return false; + } + + /* CRC validated - remove CRC bytes from data */ + length = (uint8_t)(length - 2); + + if (g_plat.log && g_log_enable) { + g_plat.log("CRC check OK\n"); + } + } + + dr_copy_tag(buffer, out->tag); + + out->data_len = (length > 4) ? (uint8_t)(length - 4) : 0; + if (out->data_len > DR_MAX_DATA) { + out->data_len = DR_MAX_DATA; + } + + for (i = 0; i < out->data_len; i++) { + out->data[i] = buffer[4 + i]; + } + + if (g_plat.log && g_log_enable) { + g_plat.log("parse_cmd: TAG='%s', data_len=%u\n", + out->tag, out->data_len); + } + + return true; +} + + + +/* ---- Handler prototypes (Harbour style: int return) ---- */ + +/* A. Device Status */ +static int Cmd_mta(const ParsedCmd *cmd); +static int Cmd_sta(const ParsedCmd *cmd); +static int Cmd_str(const ParsedCmd *cmd); + +/* F. PD-ADC M48 Full Measurement Series */ +static int Cmd_mcj(const ParsedCmd *cmd); +static int Cmd_sej(const ParsedCmd *cmd); +static int Cmd_ssj(const ParsedCmd *cmd); + +/* I. Sensor Measurements */ +static int Cmd_msn(const ParsedCmd *cmd); +static int Cmd_spn(const ParsedCmd *cmd); +static int Cmd_sso(const ParsedCmd *cmd); +static int Cmd_ssp(const ParsedCmd *cmd); + +/* J. Power / Reset / Version / Security */ +static int Cmd_ssq(const ParsedCmd *cmd); +static int Cmd_ssr(const ParsedCmd *cmd); +static int Cmd_sss(const ParsedCmd *cmd); +static int Cmd_sst(const ParsedCmd *cmd); +static int Cmd_ssv(const ParsedCmd *cmd); + +static int Cmd_mpa(const ParsedCmd *cmd); +static int Cmd_msp(const ParsedCmd *cmd); /* IMU 6-axis raw data (single shot) */ +static int Cmd_mpc(const ParsedCmd *cmd); +static int Cmd_mdc(const ParsedCmd *cmd); +static int Cmd_mec(const ParsedCmd *cmd); +static int Cmd_maa(const ParsedCmd *cmd); /* 8-channel all capture */ +static int Cmd_cmd(const ParsedCmd *cmd); +static int Cmd_mwh(const ParsedCmd *cmd); /* Write HW Number to FDS */ +static int Cmd_mws(const ParsedCmd *cmd); /* Write Serial Number to FDS */ +static int Cmd_mrh(const ParsedCmd *cmd); /* Read HW Number from FDS */ +static int Cmd_mrs(const ParsedCmd *cmd); /* Read Serial Number from FDS */ + +/* ---- Command Table ---- */ + +typedef struct { + char tag[5]; /* "sta?" */ + bool enabled; /* false = handler won't be called */ + int (*handler)(const ParsedCmd *cmd); /* 1=success, 0=fail */ +} CmdEntry; + +static CmdEntry g_cmd_table[] = { + + /* sudo command */ + { "cmd?", true, Cmd_cmd }, // Piezo Activate + + /* Piezo command */ + { "mpa?", true, Cmd_mpa }, // Piezo Activate + { "mpc?", true, Cmd_mpc }, // Piezo Cycles control command, 3,4,5,6,7 + { "mdc?", true, Cmd_mdc }, // Piezo burst + Echo capture (12-bit packed) + { "mec?", true, Cmd_mec }, // Piezo burst + Echo capture (16-bit raw) + { "maa?", true, Cmd_maa }, // 8-channel all capture (mode: 0=raw, 1=delta) + { "msp?", true, Cmd_msp }, // IMU 6-axis raw data (single shot) + + /* Config: HW/Serial Number (FDS) */ + { "mwh?", true, Cmd_mwh }, // Write HW Number + { "mws?", true, Cmd_mws }, // Write Serial Number + { "mrh?", true, Cmd_mrh }, // Read HW Number + { "mrs?", true, Cmd_mrs }, // Read Serial Number + + /* A. Device Status */ + { "mta?", true, Cmd_mta }, + { "sta?", true, Cmd_sta }, + { "str?", false, Cmd_str }, + /* F. PD-ADC M48 Full Measurement Series */ + { "mcj?", true, Cmd_mcj }, + { "scj?", true, Cmd_mcj }, + + { "sej?", true, Cmd_sej }, + { "ssj?", false, Cmd_ssj }, + /* I. Sensor Measurements */ + { "msn?", true, Cmd_msn }, + { "ssn?", true, Cmd_msn }, // snn compatible command for battery check + + { "spn?", false, Cmd_spn }, + { "sso?", false, Cmd_sso }, + { "ssp?", true, Cmd_ssp }, + + /* J. Power / Reset / Version / Security */ + { "ssq?", false, Cmd_ssq }, + { "ssr?", false, Cmd_ssr }, + { "sss?", false, Cmd_sss }, + { "sst?", false, Cmd_sst }, + { "ssv?", false, Cmd_ssv }, + +}; + +static const uint16_t g_cmd_count = + (uint16_t)(sizeof(g_cmd_table) / sizeof(g_cmd_table[0])); + +/* ---- Command dispatcher ---- */ +static int dr_cmd_dispatch(const ParsedCmd *cmd) +{ + uint16_t i; + char tag_lower[5]; + + /* tag command convert to lower case */ + for (i = 0; i < 4 && cmd->tag[i]; i++) { + tag_lower[i] = (cmd->tag[i] >= 'A' && cmd->tag[i] <= 'Z') + ? (cmd->tag[i] + 32) : cmd->tag[i]; + } + tag_lower[i] = '\0'; + + for (i = 0; i < g_cmd_count; i++) { + if (dr_tag_eq(tag_lower, g_cmd_table[i].tag)) { + + if (!g_cmd_table[i].enabled) { + if (g_plat.log && g_log_enable) { + g_plat.log("Command '%s' disabled\n", cmd->tag); + } + return 0; + } + + if (g_plat.log && g_log_enable) { + g_plat.log("Run handler '%s'\n", cmd->tag); + } + + return g_cmd_table[i].handler(cmd); + } + } + + if (g_plat.log && g_log_enable) { + g_plat.log("Unknown TAG '%s'\n", cmd->tag); + } + return 0; +} + +/* +Main Parser called from external code +*/ + +int dr_cmd_parser(const uint8_t *buf, uint8_t len) +{ + ParsedCmd cmd; + + if (g_plat.log) g_plat.log("[PARSER] in len=%u crc=%u\r\n", len, g_plat.crc_check); + + if (!dr_parse_cmd(buf, len, &cmd)) { + if (g_plat.log) g_plat.log("[PARSER] PARSE FAIL\r\n"); + + /* CRC 실패 시 에러 응답 전송 */ + if (g_plat.crc_check && g_plat.tx_bin) { + single_format_data(ble_bin_buffer, "crc!", 65530); + binary_tx_handler(ble_bin_buffer, 3); + } + return -1; /* CRC 실패 또는 파싱 실패 → 음수로 old parser에 위임 */ + } + + if (g_plat.log) g_plat.log("[PARSER] tag=%s\r\n", cmd.tag); + return dr_cmd_dispatch(&cmd); + + + + +} + +/* ---- Each Command Handler implementation (Stub) ---- */ +/* In actual firmware, replace TODO sections with hardware/EEPROM integration */ + +/* A. Device Status */ +static int Cmd_mta(const ParsedCmd *cmd) +{ + uint16_t mode = 0; + + // Reset count (sta? replacement ) + resetCount = 0; + + // Extract mode + (void)dr_get_u16(cmd, 0, &mode); + + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_mta] mode=%u\r\n", mode); + } + + // Process mode ( mta?1 ) + if (mode == 1) { + if (device_activated() == 0) { + device_status = true; + } + } + else if (mode == 0) { // mta?0 + if (device_status == true) { + if (device_sleep_mode() == 0) { + device_status = false; + } + } + } + + if (g_plat.tx_bin) { + single_format_data(ble_bin_buffer, "rta:", mode); + binary_tx_handler(ble_bin_buffer, 3); + } + + return 1; +} + + +/* ---- Each Command Handler implementation (Stub) ---- */ +/* In actual firmware, replace TODO sections with hardware/EEPROM integration */ + +/* A. Device Status */ +static int Cmd_sta(const ParsedCmd *cmd) +{ + uint16_t mode = 0; + + // Reset count (sta? replacement ) + resetCount = 0; + + // Extract mode + (void)dr_get_u16(cmd, 0, &mode); + + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_mta] mode=%u\r\n", mode); + } + + // Process mode ( mta?1 ) + if (mode == 1) { + if (device_activated() == 0) { + device_status = true; + } + } + else if (mode == 0) { // mta?0 + if (device_status == true) { + if (device_sleep_mode() == 0) { + device_status = false; + } + } + } + + if (g_plat.tx_bin) { + single_format_data(ble_bin_buffer, "sta:", mode); + binary_tx_handler(ble_bin_buffer, 3); + } + + return 1; +} + + +static int Cmd_str(const ParsedCmd *cmd) +{ + (void)cmd; + /* TODO: read actual device_status */ + uint8_t status = 1; + + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_str] read status=%u\n", status); + } + + if (g_plat.tx_bin) { + uint8_t resp[4] = { 'r','t','r', status }; + g_plat.tx_bin(resp, 4); + } + return 1; +} + + +/** + * @brief PD-ADC M48 Full Measurement - MODE 2 (scj? ? mcj?) + * + * Original: scj? + * New: mcj? + * Response: rcj: (from m48 measurement callback) + * + * MODE 2: Pressure + M48 Full Measurement + * - Pressure sensor measurement (pressure1 + pressure2) + * - 48 LED-PD ADC measurement + * - Battery, Temperature, IMU data + * + * Preconditions: + * - Device must be activated (device_status == true) + * - Not currently processing + */ +static int Cmd_mcj(const ParsedCmd *cmd) +{ + (void)cmd; + + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_mcj] PD-ADC M48 MODE=2 (Press + M48)\r\n"); + } + + /* Check device activation status */ + if (device_status != true) { + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_mcj] ERROR: Device not activated\r\n"); + } + + if (g_plat.tx_bin) { + param_error("mcj?"); + } + return 1; + } + + info4 = true; + ble_got_new_data = false; + processing = true; + + /* Start pressure measurement */ + pressure_all_level_meas(); + + battery_timer_stop(); + + /* Enable battery, temperature, IMU measurement */ + go_batt = true; + motion_data_once = true; + + /* Start main timer */ + main_timer_start(); + + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_mcj] Measurement started\r\n"); + } + + return 1; +} + +static int Cmd_sej(const ParsedCmd *cmd) +{ + (void)cmd; + + ADC_PD_MODE = 4; + info4 = true; + + ble_got_new_data = false; + processing = true; + + AGC_GAIN_SW(false); + m48_samples_in_buffer = m_pd_adc_cnt; + pd_adc_m48_start = true; + battery_timer_stop(); + go_batt = true; + main_timer_start(); + + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_sej] MODE=4 (M48 + batt + IMU) started\r\n"); + } + return 1; +} + +static int Cmd_ssj(const ParsedCmd *cmd) +{ + (void)cmd; + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_ssj] MODE=0 (M48 + batt/IMU combined)\n"); + } + return 1; +} + +/* I. Sensor Measurements */ +static int Cmd_msn(const ParsedCmd *cmd) +{ + (void)cmd; + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_msn] Measure battery level\n"); + } + battery_level_meas(); + return 1; +} + + + + +static int Cmd_spn(const ParsedCmd *cmd) +{ + (void)cmd; + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_spn] Measure pressure1 & 2\n"); + } + return 1; +} + +static int Cmd_sso(const ParsedCmd *cmd) +{ + (void)cmd; + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_sso] Measure LED temperature\n"); + } + return 1; +} + +static int Cmd_ssp(const ParsedCmd *cmd) +{ + hw_i2c_init_once(); + + motion_raw_data_enabled = true; + ble_got_new_data = false; + + /* 'c' = continuous, otherwise single shot */ + if (cmd->data_len > 0 && (char)cmd->data[0] == 'c') { + motion_data_once = false; + } else { + motion_data_once = true; + } + + main_timer_start(); + + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_ssp] Motion sensor raw, once=%u\r\n", motion_data_once); + } + return 1; +} + +/* J. Power / Reset / Version / Security */ +static int Cmd_ssq(const ParsedCmd *cmd) +{ + (void)cmd; + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_ssq] Power off\n"); + } + return 1; +} + +static int Cmd_ssr(const ParsedCmd *cmd) +{ + (void)cmd; + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_ssr] Bond delete\n"); + } + return 1; +} + +static int Cmd_sss(const ParsedCmd *cmd) +{ + (void)cmd; + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_sss] Device reset\n"); + } + return 1; +} + +static int Cmd_sst(const ParsedCmd *cmd) +{ + (void)cmd; + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_sst] Ready\n"); + } + return 1; +} + +static int Cmd_ssv(const ParsedCmd *cmd) +{ + (void)cmd; + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_ssv] Read firmware version\n"); + } + return 1; +} + +static int Cmd_mpa(const ParsedCmd *cmd) +{ + (void)cmd; + + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_mpa] Piezo Activation\n"); + } + + dr_piezo_power_on(); + dr_piezo_system_init(); + + if (g_plat.tx_bin) { + single_format_data(ble_bin_buffer, "rpa:", 1); + binary_tx_handler(ble_bin_buffer, 3); + } + + return 1; +} + + +/** + * @brief Piezo burst command with frequency option + * @param cmd->data[0-1]: cycles (3~9), default=5 + * @param cmd->data[2-3]: freq_option (0=1.8MHz, 1=2.1MHz, 2=2.0MHz, 3=1.7MHz), default=1 + * @return Response: rpc:cycles + * + * Usage from Harbour: + * mpc?[cycles][freq] where freq: 0=1.8MHz, 1=2.1MHz (default), 2=2.0MHz, 3=1.7MHz, 4=2.2MHz + * Example: mpc?0x05,0x00,0x01,0x00 -> 5 cycles at 2.1MHz + * Example: mpc?0x05,0x00,0x00,0x00 -> 5 cycles at 1.8MHz + * Example: mpc?0x05,0x00,0x02,0x00 -> 5 cycles at 2.0MHz + * Example: mpc?0x05,0x00,0x03,0x00 -> 5 cycles at 1.7MHz + * Example: mpc?0x05,0x00,0x04,0x00 -> 5 cycles at 2.2MHz + */ +static int Cmd_mpc(const ParsedCmd *cmd) +{ + uint16_t cycles = 5; /* default */ + uint16_t freq_option = 1; /* 0=1.8MHz, 1=2.1MHz, 2=2.0MHz, 3=1.7MHz, 4=2.2MHz */ + uint16_t piezo_ch = 0; /* Piezo channel 0~7, default 0 */ + + /* Extract cycles (word 0) */ + (void)dr_get_u16(cmd, 0, &cycles); + + /* Extract frequency option (word 1) */ + (void)dr_get_u16(cmd, 1, &freq_option); + + /* Extract piezo channel (word 2) */ + (void)dr_get_u16(cmd, 2, &piezo_ch); + + /* Validate piezo channel: 0~7 jhChun 26.01.29 */ + if (piezo_ch > 7) piezo_ch = 0; + + if (g_plat.log && g_log_enable) { + const char *freq_str = (freq_option == 0) ? "1.8MHz" : + (freq_option == 1) ? "2.1MHz" : + (freq_option == 2) ? "2.0MHz" : + (freq_option == 3) ? "1.7MHz" : + (freq_option == 4) ? "2.2MHz" : + (freq_option == 9) ? "1.9MHz" : "unknown"; + g_plat.log("[Cmd_mpc] cycles=%u, freq=%u (%s), piezo=%u\r\n", + cycles, freq_option, freq_str, piezo_ch); + } + + /* Range check: 3~9 */ + if (cycles < 3 || cycles > 9) { + dr_ble_return_1("rpc:", 2); /* Error: out of range */ + return 1; + } + + /* Select piezo channel */ + dr_piezo_select_channel((uint8_t)piezo_ch); + + /* Execute burst based on frequency option */ + switch (freq_option) { + case 0: + dr_piezo_burst_sw_18mhz((uint8_t)cycles); + break; + case 2: + dr_piezo_burst_sw_20mhz((uint8_t)cycles); + break; + case 3: + dr_piezo_burst_sw_17mhz((uint8_t)cycles); + break; + case 4: + dr_piezo_burst_sw_22mhz((uint8_t)cycles); + break; + case 9: + dr_piezo_burst_sw_19mhz((uint8_t)cycles); + break; + case 1: + default: + dr_piezo_burst_sw((uint8_t)cycles); /* 2.1MHz */ + break; + } + + /* Response */ + dr_ble_return_1("rpc:", (uint8_t)cycles); + + return 1; +} + + +/** + * @brief Piezo burst + Echo capture command + * @param cmd->data[0]: cycles (3~9), default=5 + * @param cmd->data[1-2]: delay_us (0~65535), default=1000 + * @param cmd->data[3-4]: num_samples (1~8192), default=4096 + * @return Multiple packets with 12-bit packed data + * + * Response format (multi-packet, 12-bit packed): + * + * First packet ("rdc:"): + * [0-3]: "rdc:" + * [4-5]: peak_raw (uint16_t, little-endian) + * [6-7]: peak_index (uint16_t) + * [8-9]: baseline_raw (uint16_t) + * [10-11]: num_samples (uint16_t) + * [12-13]: total_packets (uint16_t) + * [14...]: 12-bit packed ADC data (first chunk) + * + * Continuation packets ("rdd:"): + * [0-3]: "rdd:" + * [4-5]: packet_index (uint16_t, 1-based) + * [6...]: 12-bit packed ADC data (continuation) + * + * Last packet marker ("rde:"): + * [0-3]: "rde:" + * [4-5]: total_bytes_sent (uint16_t) + * + * 12-bit packing: 2 samples (24 bits) -> 3 bytes + * Byte0 = sample0[7:0] + * Byte1 = sample1[3:0] << 4 | sample0[11:8] + * Byte2 = sample1[11:4] + */ +#define MDC_BLE_MTU_SIZE 240 /* Safe BLE packet size */ +#define MDC_FIRST_HEADER_LEN 14 /* "rdc:" + header */ +#define MDC_CONT_HEADER_LEN 6 /* "rdd:" + packet_index */ +#define MDC_FIRST_DATA_LEN (MDC_BLE_MTU_SIZE - MDC_FIRST_HEADER_LEN) +#define MDC_CONT_DATA_LEN (MDC_BLE_MTU_SIZE - MDC_CONT_HEADER_LEN) + +/** + * @brief Piezo burst + Echo capture command (12-bit packed) + * + * PROTOCOL v3: Small header packet + separate data packets with delays + * This avoids large packet loss in BLE stack. + * + * Response format: + * Packet 1 (rdb:): header only (14 bytes) + * Packet 2~N (rdd:): pkt_idx(2) + packed_data (up to 234 bytes) + * Final (rde:): total_packets(2) + */ +static int Cmd_mdc(const ParsedCmd *cmd) +{ + uint16_t cycles = 5; + uint16_t delay_us = 1000; + uint16_t num_samples = 4096; + + /* Extract parameters */ + (void)dr_get_u16(cmd, 0, &cycles); + (void)dr_get_u16(cmd, 1, &delay_us); + (void)dr_get_u16(cmd, 2, &num_samples); + + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_mdc] cycles=%u, delay=%uus, samples=%u\r\n", + cycles, delay_us, num_samples); + } + + /* Range check */ + if (cycles < 3) cycles = 3; + if (cycles > 9) cycles = 9; + if (num_samples > 8192) num_samples = 8192; + if (num_samples < 1) num_samples = 1; + + /* Execute burst + echo capture */ + dr_adc_echo_t echo; + dr_adc_err_t err = dr_adc_burst_and_capture( + (uint8_t)cycles, delay_us, num_samples, &echo); + + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_mdc] err=%d, peak=%u@%u, base=%u, n=%u\r\n", + err, echo.peak_raw, echo.peak_index, echo.baseline_raw, echo.num_samples); + } + + if (g_plat.tx_bin) { + const uint16_t *raw_buffer = dr_adc_get_echo_buffer(); + + /* Calculate packed data size: 2 samples -> 3 bytes */ + uint16_t packed_data_bytes = ((echo.num_samples + 1) / 2) * 3; + uint16_t data_per_packet = MDC_BLE_MTU_SIZE - MDC_CONT_HEADER_LEN; /* 234 bytes */ + uint16_t data_packets = (packed_data_bytes + data_per_packet - 1) / data_per_packet; + if (data_packets == 0) data_packets = 1; + uint16_t total_packets = 1 + data_packets; /* header + data packets */ + + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_mdc] packed=%u bytes, packets=%u\r\n", packed_data_bytes, total_packets); + } + + /* Packet 1: rdb: header only (14 bytes) */ + ble_bin_buffer[0] = 'r'; + ble_bin_buffer[1] = 'd'; + ble_bin_buffer[2] = 'b'; + ble_bin_buffer[3] = ':'; + ble_bin_buffer[4] = (uint8_t)(total_packets & 0xFF); + ble_bin_buffer[5] = (uint8_t)(total_packets >> 8); + ble_bin_buffer[6] = (uint8_t)(echo.peak_raw & 0xFF); + ble_bin_buffer[7] = (uint8_t)(echo.peak_raw >> 8); + ble_bin_buffer[8] = (uint8_t)(echo.peak_index & 0xFF); + ble_bin_buffer[9] = (uint8_t)(echo.peak_index >> 8); + ble_bin_buffer[10] = (uint8_t)(echo.baseline_raw & 0xFF); + ble_bin_buffer[11] = (uint8_t)(echo.baseline_raw >> 8); + ble_bin_buffer[12] = (uint8_t)(echo.num_samples & 0xFF); + ble_bin_buffer[13] = (uint8_t)(echo.num_samples >> 8); + binary_tx_handler(ble_bin_buffer, 7); /* 14 bytes = 7 words */ + nrf_delay_ms(100); /* Wait for BLE stack */ + + /* Packet 2~N: rdd: data packets with packed 12-bit samples */ + uint16_t src_idx = 0; + for (uint16_t pkt = 0; pkt < data_packets; pkt++) { + ble_bin_buffer[0] = 'r'; + ble_bin_buffer[1] = 'd'; + ble_bin_buffer[2] = 'd'; + ble_bin_buffer[3] = ':'; + ble_bin_buffer[4] = (uint8_t)(pkt & 0xFF); + ble_bin_buffer[5] = (uint8_t)(pkt >> 8); + + uint16_t dst_idx = 6; + uint16_t bytes_this_pkt = 0; + while (src_idx < echo.num_samples && bytes_this_pkt < data_per_packet) { + uint16_t s0 = raw_buffer[src_idx++] & 0x0FFF; + uint16_t s1 = (src_idx < echo.num_samples) ? (raw_buffer[src_idx++] & 0x0FFF) : 0; + + ble_bin_buffer[dst_idx++] = (uint8_t)(s0 & 0xFF); + ble_bin_buffer[dst_idx++] = (uint8_t)(((s1 & 0x0F) << 4) | ((s0 >> 8) & 0x0F)); + ble_bin_buffer[dst_idx++] = (uint8_t)(s1 >> 4); + bytes_this_pkt += 3; + } + + /* Ensure even byte count for word alignment */ + if (dst_idx & 1) ble_bin_buffer[dst_idx++] = 0; + binary_tx_handler(ble_bin_buffer, dst_idx / 2); /* bytes to words */ + nrf_delay_ms(100); /* Inter-packet delay */ + } + + /* Final packet: rde: end marker */ + ble_bin_buffer[0] = 'r'; + ble_bin_buffer[1] = 'd'; + ble_bin_buffer[2] = 'e'; + ble_bin_buffer[3] = ':'; + ble_bin_buffer[4] = (uint8_t)(total_packets & 0xFF); + ble_bin_buffer[5] = (uint8_t)(total_packets >> 8); + binary_tx_handler(ble_bin_buffer, 3); /* 6 bytes = 3 words */ + + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_mdc] sent rdb+rdd*%u+rde (%u samples)\r\n", + total_packets, echo.num_samples); + } + } + + return 1; +} + + +/* + +// The following code demonstrates an example of how to receive data from a client +// and echo the same data back to the client. + + + +static int Cmd_cmd(const ParsedCmd *cmd) +{ + uint16_t v1, v2, v3; + + if (cmd->data_len < 6) { + dr_ble_return_1("rmd:", 0); + return 1; + } + + // Little Endian ?? (PC? LE? ??) + v1 = (uint16_t)cmd->data[0] | ((uint16_t)cmd->data[1] << 8); + v2 = (uint16_t)cmd->data[2] | ((uint16_t)cmd->data[3] << 8); + v3 = (uint16_t)cmd->data[4] | ((uint16_t)cmd->data[5] << 8); + + dr_ble_return_3("rmd:", v1, v2, v3); + + return 1; +} + +*/ + +/** + * @brief Piezo burst + Echo capture with 16-bit raw data (no compression) + * @param cmd->data[0]: cycles (3~9), default=5 + * @param cmd->data[1-2]: delay_us (0~65535), default=100 + * @param cmd->data[3-4]: num_samples (1~200), default=140 (20cm target) + * @return Multiple packets with 16-bit raw data + * + * Response format (multi-packet, 16-bit raw - no compression): + * + * First packet ("rec:"): + * [0-3]: "rec:" + * [4-5]: peak_raw (uint16_t, little-endian) + * [6-7]: peak_index (uint16_t) + * [8-9]: baseline_raw (uint16_t) + * [10-11]: num_samples (uint16_t) + * [12-13]: total_packets (uint16_t) + * [14...]: 16-bit raw ADC data (first chunk, little-endian) + * + * Continuation packets ("red:"): + * [0-3]: "red:" + * [4-5]: packet_index (uint16_t, 1-based) + * [6...]: 16-bit raw ADC data (continuation) + * + * Last packet marker ("ree:"): + * [0-3]: "ree:" + * [4-5]: total_bytes_sent (uint16_t) + * + * 16-bit raw format: Each sample = 2 bytes (uint16, little-endian) + * Byte0 = Sample[7:0] + * Byte1 = Sample[15:8] + * + * Example: 140 samples (20cm) = 280 bytes = 2 packets + */ +static int Cmd_mec(const ParsedCmd *cmd) +{ + uint16_t freq_option = 0; /* 0=1.8MHz (default), 1=2.1MHz, 2=2.0MHz, 3=1.7MHz */ + uint16_t delay_us = 20; /* Default 20us */ + uint16_t num_samples = 140; /* Default for 20cm target */ + uint16_t cycles = 5; /* Default 5 cycles (valid: 3~7) */ + uint16_t averaging = 1; /* Default 1 (no averaging), max 1000 */ + uint16_t piezo_ch = 0; /* Default piezo channel 0 (valid: 0~7) */ + + /* Extract parameters: mec [freq_option] [delay_us] [num_samples] [cycles] [averaging] [piezo_ch] */ + (void)dr_get_u16(cmd, 0, &freq_option); + (void)dr_get_u16(cmd, 1, &delay_us); + (void)dr_get_u16(cmd, 2, &num_samples); + (void)dr_get_u16(cmd, 3, &cycles); + (void)dr_get_u16(cmd, 4, &averaging); + (void)dr_get_u16(cmd, 5, &piezo_ch); + + /* Validate averaging: 1~1000, default 1 */ + if (averaging == 0) averaging = 1; + if (averaging > 1000) averaging = 1000; + + /* Validate piezo channel: 0~7 jhChun 26.01.29 */ + if (piezo_ch > 7) piezo_ch = 0; + + if (g_plat.log && g_log_enable) { + const char *freq_str = (freq_option == 0) ? "1.8MHz" : + (freq_option == 1) ? "2.1MHz" : + (freq_option == 2) ? "2.0MHz" : + (freq_option == 3) ? "1.7MHz" : + (freq_option == 4) ? "2.2MHz" : + (freq_option == 9) ? "1.9MHz" : "unknown"; + g_plat.log("[Cmd_mec] freq=%u (%s), delay=%uus, samples=%u, cycles=%u, avg=%u, piezo=%u\r\n", + freq_option, freq_str, delay_us, num_samples, cycles, averaging, piezo_ch); + } + + /* Use integrated burst + capture + transmit function + * This function handles: + * 1. ADC power on + * 2. Select piezo channel (0~7) + * 3. Piezo burst (frequency based on freq_option, cycles from param) + * 4. ADC capture (after delay_us) - repeated 'averaging' times + * 5. Average the captured samples + * 6. BLE transmission with proper packet timing + */ + dr_adc_err_t err = dr_adc_burst_capture_transmit( + (uint8_t)freq_option, delay_us, num_samples, (uint8_t)cycles, + (uint16_t)averaging, (uint8_t)piezo_ch, ble_bin_buffer, 0); /* 0=send raa */ + + if (err != DR_ADC_OK) { + dr_ble_return_2("rer:", 0xEE00 | (uint16_t)err, num_samples); + } + + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_mec] result=%d\r\n", err); + } + + return 1; +} + + +static int Cmd_cmd(const ParsedCmd *cmd) +{ + uint16_t v1, v2, v3; + uint32_t pin_number; + + if (cmd->data_len < 6) { + dr_ble_return_1("rmd:", 0); + return 1; + } + + // Little Endian from PC to LE + v1 = (uint16_t)cmd->data[0] | ((uint16_t)cmd->data[1] << 8); // port + v2 = (uint16_t)cmd->data[2] | ((uint16_t)cmd->data[3] << 8); // pin + v3 = (uint16_t)cmd->data[4] | ((uint16_t)cmd->data[5] << 8); // 1=HIGH, 0=LOW + + // -- GPIO Test + + // Pin No: NRF_GPIO_PIN_MAP(port, pin) + pin_number = NRF_GPIO_PIN_MAP(v1, v2); + + // output + nrf_gpio_cfg_output(pin_number); + + // HIGH or LOW + if (v3 == 1) { + nrf_gpio_pin_set(pin_number); // HIGH + } else { + nrf_gpio_pin_clear(pin_number); // LOW + } + + // return : port, pin, state + dr_ble_return_3("rmd:", v1, v2, v3); + + return 1; +} + + +/** + * @brief 4-Channel All Capture Command (maa?) - ASYNC VERSION + * @param cmd->data[0-1]: mode (0=raw 16-bit async) + * @return Immediately. Data sent asynchronously via BLE_NUS_EVT_TX_RDY + * + * ASYNC ARCHITECTURE: + * - maa_async_start() initiates capture and sends CH0 header + * - BLE_NUS_EVT_TX_RDY callback drives remaining transmission + * - No blocking - SoftDevice can process events normally + * - Prevents BLE TX buffer overflow that caused firmware brick + * + * Hardcoded parameters: + * freq_option = 0 (1.8MHz) + * delay_us = 10 + * num_samples = 140 + * cycles = 7 + * averaging = 5 + * + * Response format: + * For each channel (CH0~CH3): + * reb: [total_pkts(2)] [peak(2)] [idx(2)] [baseline(2)] [samples(2)] + * red: [pkt_idx(2)] [data...] + * Final: + * raa: [status(2)] + * + * Version marker: 0xA000 (vA) = async 8-channel + */ +#define MAA_FREQ_OPTION 0 /* 1.8MHz */ +#define MAA_DELAY_US 10 /* 10us post-burst delay */ +#define MAA_NUM_SAMPLES 140 /* 140 samples (~25cm) */ +#define MAA_CYCLES 7 /* 7 cycles burst */ +#define MAA_AVERAGING 5 /* 5x averaging */ + +static int Cmd_maa(const ParsedCmd *cmd) +{ + uint16_t mode = 0; + dr_adc_err_t err; + + /* Extract mode parameter */ + (void)dr_get_u16(cmd, 0, &mode); + + /* Mode validation - only mode 0 (async raw) supported */ + if (mode > 0) { + dr_ble_return_1("raa:", 0xFFFF); + return 1; + } + + /* Check if already busy */ + if (maa_async_is_busy()) { + dr_ble_return_1("raa:", 0xFFFE); /* Busy */ + return 1; + } + + /*======================================================================= + * ASYNC 4-CHANNEL CAPTURE + * - maa_async_start() captures CH0 and sends first header + * - Subsequent packets sent via BLE_NUS_EVT_TX_RDY callback + * - No blocking delays - SoftDevice can process events normally + *=======================================================================*/ + err = maa_async_start( + (uint8_t)MAA_FREQ_OPTION, + MAA_DELAY_US, + MAA_NUM_SAMPLES, + (uint8_t)MAA_CYCLES, + (uint16_t)MAA_AVERAGING, + ble_bin_buffer + ); + + if (err != DR_ADC_OK) { + /* Start failed - error already sent by maa_async_start */ + return 1; + } + + /* Return immediately - async transmission in progress */ + /* raa: will be sent by state machine when complete */ + return 1; +} + + +/*============================================================================== + * CONFIG: HW/Serial Number (FDS) + *============================================================================*/ + +/* mwh? - Write HW Number to FDS + * Data: 12 bytes ASCII HW number + */ +static int Cmd_mwh(const ParsedCmd *cmd) +{ + char buf[13]; + + if (cmd->data_len < 12) { + dr_ble_return_1("rwh:", 0xFFFF); /* Error: insufficient data */ + return 1; + } + + dr_get_ascii(cmd, 0, buf, 12); + memcpy(HW_NO, buf, 12); + memcpy(m_config.hw_no, buf, 12); + config_save(); + + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_mwh] HW=%.12s saved to FDS\r\n", m_config.hw_no); + } + + ascii_format_data(ble_bin_buffer, "rwh:", buf, 12); + binary_tx_handler(ble_bin_buffer, 8); + return 1; +} + +/* mws? - Write Serial Number to FDS + * Data: 12 bytes ASCII serial number + */ +static int Cmd_mws(const ParsedCmd *cmd) +{ + char buf[13]; + + if (cmd->data_len < 12) { + dr_ble_return_1("rws:", 0xFFFF); /* Error: insufficient data */ + return 1; + } + + dr_get_ascii(cmd, 0, buf, 12); + memcpy(SERIAL_NO, buf, 12); + memcpy(m_config.serial_no, buf, 12); + config_save(); + + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_mws] S/N=%.12s saved to FDS\r\n", m_config.serial_no); + } + + ascii_format_data(ble_bin_buffer, "rws:", buf, 12); + binary_tx_handler(ble_bin_buffer, 8); + return 1; +} + +/* mrh? - Read HW Number from FDS */ +static int Cmd_mrh(const ParsedCmd *cmd) +{ + (void)cmd; + memcpy(HW_NO, m_config.hw_no, 12); + ascii_format_data(ble_bin_buffer, "rrh:", HW_NO, 12); + binary_tx_handler(ble_bin_buffer, 8); + return 1; +} + +/* mrs? - Read Serial Number from FDS */ +static int Cmd_mrs(const ParsedCmd *cmd) +{ + (void)cmd; + memcpy(SERIAL_NO, m_config.serial_no, 12); + ascii_format_data(ble_bin_buffer, "rrs:", SERIAL_NO, 12); + binary_tx_handler(ble_bin_buffer, 8); + return 1; +} + + +/*============================================================================== + * IMU: 6-axis raw data (single shot) + *============================================================================*/ + +/* msp? - Read IMU accel(xyz) + gyro(xyz) raw data, single shot + * Response: rsp: + 6 x uint16_t (accel_x, accel_y, accel_z, gyro_x, gyro_y, gyro_z) + */ +static int Cmd_msp(const ParsedCmd *cmd) +{ + (void)cmd; + + if (g_plat.log) g_plat.log("[MSP] enter\r\n"); + + hw_i2c_init_once(); + + /* Direct register read — no timer, no DRDY, no callback */ + int rc = imu_read_direct(); + + if (g_plat.log) g_plat.log("[MSP] rc=%d\r\n", rc); + return 1; +} diff --git a/pc_firm/parser.h b/pc_firm/parser.h new file mode 100644 index 0000000..565fe12 --- /dev/null +++ b/pc_firm/parser.h @@ -0,0 +1,24 @@ +/* parser.h */ +#ifndef PARSER_H +#define PARSER_H + +#include +#include + +/* Platform-dependent function pointer set */ +typedef struct { + void (*log)(const char *fmt, ...); + void (*tx_bin)(const uint8_t *buf, uint16_t len); + bool crc_check; +} dr_platform_if_t; + + +/* Global interface & log flag */ +extern dr_platform_if_t g_plat; +extern bool g_log_enable; + +/* Main parser entry point */ +int dr_cmd_parser(const uint8_t *buf, uint8_t len); + +#endif /* PARSER_H */ + diff --git a/pc_firm/pc_firm.code-workspace b/pc_firm/pc_firm.code-workspace new file mode 100644 index 0000000..349eb51 --- /dev/null +++ b/pc_firm/pc_firm.code-workspace @@ -0,0 +1,11 @@ +{ + "folders": [ + { + "path": "." + }, + { + "path": "../mt_nirs" + } + ], + "settings": {} +} \ No newline at end of file diff --git a/pc_firm/pcmain.c b/pc_firm/pcmain.c new file mode 100644 index 0000000..09d14e2 --- /dev/null +++ b/pc_firm/pcmain.c @@ -0,0 +1,59 @@ +/* pcmain.c : PC에서 parser 테스트용 main + * - 펌웨어에서는 이 파일 사용하지 않음 + */ + +#include +#include +#include "parser.h" + +/* PC용 로그 함수 */ +static void pc_log(const char *fmt, ...) +{ + if (!g_log_enable) { + return; + } + va_list ap; + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); +} + +/* PC용 바이너리 전송 함수 */ +static void pc_tx_bin(const uint8_t *buf, uint16_t len) +{ + uint16_t i; + printf("[TX] "); + for (i = 0; i < len; i++) { + printf("%02X ", buf[i]); + } + printf("\n"); +} + +int main(void) +{ + /* 플랫폼 함수 설정 */ + g_plat.log = pc_log; + g_plat.tx_bin = pc_tx_bin; + g_log_enable = true; /* 필요시 false로 전체 로그 끄기 */ + + /* 테스트 1: sta? (mode=1) */ + { + uint8_t buf[6] = { 's','s','n','?' }; /* value0=1 */ + dr_cmd_parser(buf, 4); + } + + /* 테스트 2: ssz? (Serial 12바이트) */ + { + const char *serial = "ABC123456789"; + uint8_t buf[4 + 12]; + uint8_t i; + + buf[0] = 's'; buf[1] = 's'; buf[2] = 'z'; buf[3] = '?'; + for (i = 0; i < 12; i++) { + buf[4 + i] = (uint8_t)serial[i]; + } + dr_cmd_parser(buf, 4 + 12); + } + + return 0; +} diff --git a/pc_firm/source b/pc_firm/source new file mode 100644 index 0000000..e69de29 diff --git a/project/ble_peripheral/ble_app_bladder_patch/VesiScan.code-workspace b/project/ble_peripheral/ble_app_bladder_patch/VesiScan.code-workspace new file mode 100644 index 0000000..6b6e3ba --- /dev/null +++ b/project/ble_peripheral/ble_app_bladder_patch/VesiScan.code-workspace @@ -0,0 +1,11 @@ +{ + "folders": [ + { + "path": "../../.." + }, + { + "path": "../../../../pc_firm" + } + ], + "settings": {} +} \ No newline at end of file diff --git a/project/ble_peripheral/ble_app_bladder_patch/cmd/dr_adc121s051.c b/project/ble_peripheral/ble_app_bladder_patch/cmd/dr_adc121s051.c new file mode 100644 index 0000000..0446a22 --- /dev/null +++ b/project/ble_peripheral/ble_app_bladder_patch/cmd/dr_adc121s051.c @@ -0,0 +1,1515 @@ +/******************************************************************************* + * @file dr_adc121s051.c + * @brief ADC121S051 12-bit ADC Driver for nRF52840 + * For 1.2MHz Piezo Echo Envelope Detection + * @author Charles KWON + * @date 2025-12-15 + * + * @details Software SPI (bit-bang) implementation for ADC121S051. + * Optimized for reading envelope-detected echo signals. + * + * ADC121S051 Serial Interface: + * + * CS ────────┐ ┌──────── + * (HIGH) └──────────────────────────────┘ (HIGH) + * + * SCLK ────────┐ ┌──┐ ┌──┐ ┌──┐ ┌──┐ ──────── + * (HIGH) └──┘ └──┘ └── ... ┘ └──┘ └──┘ (HIGH) + * 1 2 3 14 15 16 + * + * SDATA -------...------ + * |<- 3 zeros ->|<-- 12 data bits -->| + * + * - CS idle HIGH, SCLK idle HIGH + * - CS falling edge: starts conversion, samples VIN + * - Data clocked out on SCLK falling edge + * - MSB first, straight binary output + * + * @note WARNING: Never hardcode pin numbers! + * Hardcoding may save a developer's time momentarily, + * but it will also shorten their lifespan. + ******************************************************************************/ + +#include "dr_adc121s051.h" +#include "dr_util.h" +#include "nrf_gpio.h" +#include "nrf_delay.h" +#include /* memset */ + +/* BLE connection state from main.c */ +extern volatile bool data_tx_in_progress; +extern volatile bool ble_connection_st; + +/*============================================================================== + * EXTERNAL PIEZO BURST FUNCTIONS + *============================================================================*/ +extern void dr_piezo_burst_sw(uint8_t cycles); +extern void dr_piezo_burst_sw_18mhz(uint8_t cycles); +extern void dr_piezo_burst_sw_20mhz(uint8_t cycles); +extern void dr_piezo_burst_sw_17mhz(uint8_t cycles); + +/*============================================================================== + * DEBUG CONFIGURATION + *============================================================================*/ +#ifdef DEBUG_ADC +#include "nrf_log.h" +#define ADC_LOG(...) NRF_LOG_INFO(__VA_ARGS__) +#else +#define ADC_LOG(...) +#endif + +/*============================================================================== + * PRIVATE DEFINES + *============================================================================*/ + +/* Extract pin number from NRF_GPIO_PIN_MAP (lower 5 bits) */ +#define DR_PIN_NUM(pin) ((pin) & 0x1F) +#define DR_PIN_PORT(pin) (((pin) >> 5) & 0x01) + +/* SPI bit masks for direct register access */ +#define CS_MASK (1UL << DR_PIN_NUM(DR_ADC_PIN_CS)) +#define SCLK_MASK (1UL << DR_PIN_NUM(DR_ADC_PIN_SCLK)) +#define SDATA_MASK (1UL << DR_PIN_NUM(DR_ADC_PIN_SDATA)) + +/* Get port register based on pin */ +#define DR_GET_PORT(pin) (DR_PIN_PORT(pin) ? NRF_P1 : NRF_P0) + +/* ADC timing constants */ +#define SCLK_CYCLES 16 /**< Clock cycles per conversion */ + +/*============================================================================== + * PRIVATE VARIABLES + *============================================================================*/ + +static bool m_initialized = false; +static uint32_t m_vref_mv = DR_ADC_VREF_MV; + +/* Echo capture buffer (module-level for external access) */ +static uint16_t m_echo_buffer[DR_ADC_ECHO_SAMPLES_MAX]; + +/* Port registers for fast access (currently unused - using direct NRF_P0) */ +/* static NRF_GPIO_Type *m_port_cs; */ +/* static NRF_GPIO_Type *m_port_sclk; */ +/* static NRF_GPIO_Type *m_port_sdata; */ + +/*============================================================================== + * PRIVATE FUNCTION PROTOTYPES + *============================================================================*/ + +static void adc_gpio_init(void); +/* adc_transfer is always_inline, no prototype needed */ + +/*============================================================================== + * INLINE GPIO FUNCTIONS (for speed) + *============================================================================*/ + +/* cs_low() unused - using ADC_TRANSFER_16BITS macro with direct register access +static inline void cs_low(void) +{ + NRF_P0->OUTCLR = CS_MASK; +} +*/ + +static inline void cs_high(void) +{ + NRF_P0->OUTSET = CS_MASK; +} + +/* sclk_low() unused - using direct register access in macro +static inline void sclk_low(void) +{ + NRF_P0->OUTCLR = SCLK_MASK; +} +*/ + +static inline void sclk_high(void) +{ + NRF_P0->OUTSET = SCLK_MASK; +} + +/* read_sdata() unused - using direct register access in macro +static inline uint32_t read_sdata(void) +{ + return (NRF_P0->IN & SDATA_MASK) ? 1 : 0; +} +*/ + +/*============================================================================== + * PRIVATE FUNCTIONS + *============================================================================*/ + +/** + * @brief Initialize GPIO pins for ADC + */ +static void adc_gpio_init(void) +{ + /* Port pointers removed - using direct NRF_P0 access for speed */ + + /* CS: Output, HIGH (idle) */ + nrf_gpio_cfg_output(DR_ADC_PIN_CS); + nrf_gpio_pin_set(DR_ADC_PIN_CS); + + /* SCLK: Output, HIGH (idle) - per Figure 4 */ + nrf_gpio_cfg_output(DR_ADC_PIN_SCLK); + nrf_gpio_pin_set(DR_ADC_PIN_SCLK); + + /* SDATA: Input (ADC has push-pull output) */ + nrf_gpio_cfg_input(DR_ADC_PIN_SDATA, NRF_GPIO_PIN_NOPULL); + + ADC_LOG("ADC GPIO: CS=P%d.%02d SCLK=P%d.%02d SDATA=P%d.%02d", + DR_PIN_PORT(DR_ADC_PIN_CS), DR_PIN_NUM(DR_ADC_PIN_CS), + DR_PIN_PORT(DR_ADC_PIN_SCLK), DR_PIN_NUM(DR_ADC_PIN_SCLK), + DR_PIN_PORT(DR_ADC_PIN_SDATA), DR_PIN_NUM(DR_ADC_PIN_SDATA)); +} + +/** + * @brief Perform one ADC conversion (16 clock cycles) + * @return Raw 16-bit data (includes leading zeros) + * + * @details Timing optimized for maximum speed (~500ksps). + * At 64MHz CPU: 1 NOP = 15.625ns + * + * ADC specs (@ 3.3V): + * - tACC (data access time): 40ns max + * - tH (hold time): 7ns min + * - fSCLK: 4-10 MHz + * + * MUST be inlined to eliminate function call overhead for consistent timing. + */ +/* + * ADC_TRANSFER_BIT macro - one SCLK cycle for bit-bang SPI + * Captures data on falling edge, processes on rising edge + * + * Target: 10MHz (100ns cycle) = 50ns LOW + 50ns HIGH + * ADC121S051 timing requirements: + * - tCL (SCLK low time): 40ns min + * - tCH (SCLK high time): 40ns min + * - tACC (data access): 40ns max (data valid after SCLK falling) + * + * LOW phase: OUTCLR(~15ns) + NOP(~16ns) + IN read(~30ns) = ~61ns (meets tCL, tACC) + * HIGH phase: OUTSET(~15ns) + shift/OR(~40ns) = ~55ns (meets tCH) + * Total: ~116ns (~8.6MHz) + * + * Note: Removing HIGH phase NOP to reduce cycle time + */ +#define ADC_TRANSFER_BIT(data, in_reg) \ + do { \ + NRF_P0->OUTCLR = SCLK_MASK; \ + __NOP(); \ + in_reg = NRF_P0->IN; \ + NRF_P0->OUTSET = SCLK_MASK; \ + data = (data << 1) | ((in_reg & SDATA_MASK) ? 1 : 0); \ + } while(0) + +#define ADC_TRANSFER_BIT_LAST(data, in_reg) \ + do { \ + NRF_P0->OUTCLR = SCLK_MASK; \ + __NOP(); \ + in_reg = NRF_P0->IN; \ + NRF_P0->OUTSET = SCLK_MASK; \ + data = (data << 1) | ((in_reg & SDATA_MASK) ? 1 : 0); \ + } while(0) + +/* + * ADC_TRANSFER_16BITS macro - Complete 16-bit transfer + * Use this instead of adc_transfer() function for consistent first bit timing. + * Variables data and in_reg MUST be declared and initialized before calling. + * + * IMPORTANT: Call ADC_PREHEAT(data, in_reg) before cs_low() to warm up pipeline. + */ +#define ADC_TRANSFER_16BITS(data, in_reg) \ + do { \ + ADC_TRANSFER_BIT(data, in_reg); /* Bit 15 */ \ + ADC_TRANSFER_BIT(data, in_reg); /* Bit 14 */ \ + ADC_TRANSFER_BIT(data, in_reg); /* Bit 13 */ \ + ADC_TRANSFER_BIT(data, in_reg); /* Bit 12 */ \ + ADC_TRANSFER_BIT(data, in_reg); /* Bit 11 */ \ + ADC_TRANSFER_BIT(data, in_reg); /* Bit 10 */ \ + ADC_TRANSFER_BIT(data, in_reg); /* Bit 9 */ \ + ADC_TRANSFER_BIT(data, in_reg); /* Bit 8 */ \ + ADC_TRANSFER_BIT(data, in_reg); /* Bit 7 */ \ + ADC_TRANSFER_BIT(data, in_reg); /* Bit 6 */ \ + ADC_TRANSFER_BIT(data, in_reg); /* Bit 5 */ \ + ADC_TRANSFER_BIT(data, in_reg); /* Bit 4 */ \ + ADC_TRANSFER_BIT(data, in_reg); /* Bit 3 */ \ + ADC_TRANSFER_BIT(data, in_reg); /* Bit 2 */ \ + ADC_TRANSFER_BIT(data, in_reg); /* Bit 1 */ \ + ADC_TRANSFER_BIT_LAST(data, in_reg); /* Bit 0 */ \ + NRF_P0->OUTSET = CS_MASK; \ + __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); \ + __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); \ + __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); \ + __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); \ + } while(0) + +/* + * ADC_PREHEAT - Warm up instruction pipeline before timing-critical section + * This prevents the first SCLK cycle from being longer than others. + * + * CRITICAL: Must perform ACTUAL SCLK toggle to prime the GPIO write path! + * CS is still HIGH (idle), so this dummy toggle doesn't affect ADC. + * + * Sequence: + * 1. Dummy SCLK LOW/HIGH cycle (exact same code as ADC_TRANSFER_BIT) + * 2. SCLK remains HIGH (ready for cs_low) + */ +#define ADC_PREHEAT(data, in_reg) \ + do { \ + /* Dummy SCLK cycle - CS still HIGH, so ADC ignores this */ \ + /* Must match ADC_TRANSFER_BIT timing exactly */ \ + NRF_P0->OUTCLR = SCLK_MASK; \ + __NOP(); \ + in_reg = NRF_P0->IN; \ + NRF_P0->OUTSET = SCLK_MASK; \ + data = (data << 1) | ((in_reg & SDATA_MASK) ? 1 : 0); \ + /* Second dummy cycle for better pipeline warm-up */ \ + NRF_P0->OUTCLR = SCLK_MASK; \ + __NOP(); \ + in_reg = NRF_P0->IN; \ + NRF_P0->OUTSET = SCLK_MASK; \ + data = (data << 1) | ((in_reg & SDATA_MASK) ? 1 : 0); \ + /* Reset data after priming */ \ + data = 0; \ + } while(0) + +/* adc_transfer() function replaced by ADC_TRANSFER_16BITS macro for speed + * Kept here for reference but commented out to eliminate warning + */ +#if 0 +/** + * @brief Perform one ADC conversion (16 clock cycles) + * @param data Pre-initialized to 0 by caller + * @param in_reg Pre-declared register variable by caller + * @return Raw 16-bit data (includes leading zeros) + */ +__attribute__((always_inline)) +static inline uint16_t adc_transfer(uint16_t data, register uint32_t in_reg) +{ + /* Implementation moved to ADC_TRANSFER_16BITS macro */ + return data; +} +#endif + +/*============================================================================== + * PUBLIC FUNCTIONS - INITIALIZATION + *============================================================================*/ + +dr_adc_err_t dr_adc_init(void) +{ + ADC_LOG("ADC121S051 init..."); + + /* Initialize GPIO */ + adc_gpio_init(); + + /* Ensure idle state (SCLK idle HIGH per Figure 4) */ + cs_high(); + sclk_high(); + + /* Wait for power stabilization */ + nrf_delay_us(10); + + /* Dummy read to wake up and clear stale data */ + { + uint16_t dummy_data = 0; + register uint32_t dummy_reg = 0; + ADC_PREHEAT(dummy_data, dummy_reg); + NRF_P0->OUTCLR = CS_MASK; + __NOP(); __NOP(); __NOP(); __NOP(); /* tSU: CS setup time */ + ADC_TRANSFER_16BITS(dummy_data, dummy_reg); + } + + /* Quiet time between conversions */ + __NOP(); __NOP(); __NOP(); __NOP(); + + m_initialized = true; + + ADC_LOG("ADC121S051 ready (VREF=%dmV)", m_vref_mv); + + return DR_ADC_OK; +} + +void dr_adc_uninit(void) +{ + if (!m_initialized) return; + + cs_high(); + sclk_high(); /* Idle HIGH per Figure 4 */ + + nrf_gpio_cfg_default(DR_ADC_PIN_CS); + nrf_gpio_cfg_default(DR_ADC_PIN_SCLK); + nrf_gpio_cfg_default(DR_ADC_PIN_SDATA); + + m_initialized = false; + + ADC_LOG("ADC121S051 uninitialized"); +} + +bool dr_adc_is_initialized(void) +{ + return m_initialized; +} + +/*============================================================================== + * PUBLIC FUNCTIONS - BASIC READ + *============================================================================*/ + +dr_adc_err_t dr_adc_read_raw(uint16_t *raw_value) +{ + if (!m_initialized) return DR_ADC_ERR_NOT_INIT; + if (raw_value == NULL) return DR_ADC_ERR_INVALID_PARAM; + + uint16_t data = 0; + register uint32_t in_reg = 0; + + /* Critical section for precise timing */ + __disable_irq(); + + /* Preheat pipeline before timing-critical section */ + ADC_PREHEAT(data, in_reg); + + /* CS LOW - starts conversion, samples VIN on this edge */ + NRF_P0->OUTCLR = CS_MASK; + __NOP(); /* Match timing with other SCLK cycles */ + + /* Transfer 16 bits + CS HIGH (macro for consistent timing) */ + ADC_TRANSFER_16BITS(data, in_reg); + + __enable_irq(); + + /* + * Extract 12-bit data from 16-bit frame: + * Bit 15-13: Leading zeros (Z2, Z1, Z0) - must be 0 + * Bit 12-1: Data bits (D11-D0) + * Bit 0: Don't care (trailing) + * + * Result = (data >> 1) & 0x0FFF + */ + + /* Verify leading zeros (upper 3 bits should be 0) */ + if ((data & 0xE000) != 0) + { + ADC_LOG("ADC frame error: leading zeros invalid (0x%04X)", data); + } + + *raw_value = (data >> 1) & 0x0FFF; + + return DR_ADC_OK; +} + +dr_adc_err_t dr_adc_read(dr_adc_result_t *result) +{ + if (!m_initialized) return DR_ADC_ERR_NOT_INIT; + if (result == NULL) return DR_ADC_ERR_INVALID_PARAM; + + dr_adc_err_t err = dr_adc_read_raw(&result->raw); + if (err != DR_ADC_OK) return err; + + result->voltage_mv = dr_adc_raw_to_mv(result->raw); + + return DR_ADC_OK; +} + +dr_adc_err_t dr_adc_read_averaged(dr_adc_result_t *result, uint16_t num_samples) +{ + if (!m_initialized) return DR_ADC_ERR_NOT_INIT; + if (result == NULL) return DR_ADC_ERR_INVALID_PARAM; + if (num_samples == 0 || num_samples > DR_ADC_ECHO_SAMPLES_MAX) + return DR_ADC_ERR_INVALID_PARAM; + + uint32_t sum = 0; + uint16_t raw; + + for (uint16_t i = 0; i < num_samples; i++) + { + dr_adc_err_t err = dr_adc_read_raw(&raw); + if (err != DR_ADC_OK) return err; + sum += raw; + } + + result->raw = (uint16_t)(sum / num_samples); + result->voltage_mv = dr_adc_raw_to_mv(result->raw); + + return DR_ADC_OK; +} + +/*============================================================================== + * PUBLIC FUNCTIONS - ECHO DETECTION + *============================================================================*/ + +dr_adc_err_t dr_adc_capture_echo(uint16_t *buffer, uint16_t num_samples) +{ + if (!m_initialized) return DR_ADC_ERR_NOT_INIT; + if (buffer == NULL) return DR_ADC_ERR_INVALID_PARAM; + if (num_samples == 0 || num_samples > DR_ADC_ECHO_SAMPLES_MAX) + return DR_ADC_ERR_INVALID_PARAM; + + uint16_t data = 0; + register uint32_t in_reg = 0; + + /* Disable interrupts for continuous high-speed capture */ + __disable_irq(); + + /* Preheat pipeline once before the loop */ + ADC_PREHEAT(data, in_reg); + + for (uint16_t i = 0; i < num_samples; i++) + { + data = 0; /* Reset for each sample */ + + /* CS LOW - starts conversion, samples VIN */ + NRF_P0->OUTCLR = CS_MASK; + __NOP(); /* Match timing with other SCLK cycles */ + + /* Transfer 16 bits + CS HIGH (macro for consistent timing) */ + ADC_TRANSFER_16BITS(data, in_reg); + + /* Store 12-bit value */ + buffer[i] = (data >> 1) & 0x0FFF; + } + + __enable_irq(); + + return DR_ADC_OK; +} + +dr_adc_err_t dr_adc_measure_echo(dr_adc_echo_t *echo, const dr_adc_echo_config_t *config) +{ + if (!m_initialized) return DR_ADC_ERR_NOT_INIT; + if (echo == NULL) return DR_ADC_ERR_INVALID_PARAM; + + /* Use default config if not provided */ + dr_adc_echo_config_t cfg; + if (config != NULL) + { + cfg = *config; + } + else + { + cfg.num_samples = DR_ADC_ECHO_SAMPLES_DEFAULT; + cfg.threshold_raw = 100; /* ~80mV threshold */ + cfg.delay_us = 0; + } + + /* Validate config */ + if (cfg.num_samples == 0 || cfg.num_samples > DR_ADC_ECHO_SAMPLES_MAX) + return DR_ADC_ERR_INVALID_PARAM; + + /* Use module-level buffer (16KB too large for stack) */ + + /* Optional delay before capture */ + if (cfg.delay_us > 0) + { + nrf_delay_us(cfg.delay_us); + } + + /* Capture echo samples into module-level buffer */ + dr_adc_err_t err = dr_adc_capture_echo(m_echo_buffer, cfg.num_samples); + if (err != DR_ADC_OK) return err; + + /* Analyze captured data */ + return dr_adc_analyze_echo(m_echo_buffer, cfg.num_samples, echo, cfg.threshold_raw); +} + +dr_adc_err_t dr_adc_burst_and_capture(uint8_t cycles, uint16_t delay_us, + uint16_t num_samples, dr_adc_echo_t *echo) +{ + (void)cycles; /* Not used - ADC test only */ + + if (echo == NULL) return DR_ADC_ERR_INVALID_PARAM; + if (num_samples == 0 || num_samples > DR_ADC_ECHO_SAMPLES_MAX) + return DR_ADC_ERR_INVALID_PARAM; + + /* Initialize echo structure */ + echo->peak_raw = 0; + echo->peak_mv = 0; + echo->peak_index = 0; + echo->peak_time_us = 0; + echo->baseline_raw = 0; + echo->num_samples = 0; + + /* Initialize ADC if not ready */ + if (!m_initialized) + { + dr_adc_err_t init_err = dr_adc_init(); + if (init_err != DR_ADC_OK) return init_err; + } + + /* Settling time for ADC power */ + nrf_delay_us(100); + + /* Optional delay before capture */ + if (delay_us > 0) + { + nrf_delay_us(delay_us); + } + + /* Capture ADC samples into module-level buffer */ + dr_adc_err_t err = dr_adc_capture_echo(m_echo_buffer, num_samples); + if (err != DR_ADC_OK) return err; + + /* Analyze captured data */ + return dr_adc_analyze_echo(m_echo_buffer, num_samples, echo, 100); +} + +const uint16_t* dr_adc_get_echo_buffer(void) +{ + return m_echo_buffer; +} + +dr_adc_err_t dr_adc_analyze_echo(const uint16_t *buffer, uint16_t num_samples, + dr_adc_echo_t *echo, uint16_t threshold) +{ + if (buffer == NULL || echo == NULL) return DR_ADC_ERR_INVALID_PARAM; + if (num_samples == 0) return DR_ADC_ERR_INVALID_PARAM; + + /* Calculate baseline from first few samples */ + uint16_t baseline_samples = (num_samples > 8) ? 8 : num_samples; + echo->baseline_raw = dr_adc_calc_baseline(buffer, baseline_samples); + + /* Find peak */ + dr_adc_find_peak(buffer, num_samples, &echo->peak_raw, &echo->peak_index); + + /* Convert to voltage and time */ + echo->peak_mv = dr_adc_raw_to_mv(echo->peak_raw); + /* peak_time_us: sample_index * 0.116us (8.6MHz sample rate) */ + echo->peak_time_us = (uint32_t)((float)echo->peak_index * DR_ADC_SAMPLE_INTERVAL_US); + echo->num_samples = num_samples; + + /* Check if valid echo detected */ + if (echo->peak_raw < threshold || + echo->peak_raw <= echo->baseline_raw) + { + return DR_ADC_ERR_NO_ECHO; + } + + return DR_ADC_OK; +} + +void dr_adc_find_peak(const uint16_t *buffer, uint16_t num_samples, + uint16_t *peak_value, uint16_t *peak_index) +{ + /* + * Posterior wall detection for bladder measurement + * - Skip initial decay zone (index 0-29) where transducer coupling causes high values + * - Search in range [30, 70] where posterior wall echo is expected + * - Ignore values > 1500 (likely noise/saturation) + */ + #define SEARCH_START 30 + #define SEARCH_END 70 + #define MAX_VALID_VALUE 1500 /* Values above this are likely noise */ + + uint16_t max_val = 0; + uint16_t max_idx = 0; + + uint16_t start = (num_samples > SEARCH_START) ? SEARCH_START : 0; + uint16_t end = (num_samples > SEARCH_END) ? SEARCH_END : num_samples; + + for (uint16_t i = start; i < end; i++) + { + uint16_t val = buffer[i]; + /* Skip abnormally high values (noise/saturation) */ + if (val > MAX_VALID_VALUE) continue; + + if (val > max_val) + { + max_val = val; + max_idx = i; + } + } + + if (peak_value != NULL) *peak_value = max_val; + if (peak_index != NULL) *peak_index = max_idx; +} + +uint16_t dr_adc_calc_baseline(const uint16_t *buffer, uint16_t num_samples) +{ + if (buffer == NULL || num_samples == 0) return 0; + + uint32_t sum = 0; + for (uint16_t i = 0; i < num_samples; i++) + { + sum += buffer[i]; + } + + return (uint16_t)(sum / num_samples); +} + +/*============================================================================== + * PUBLIC FUNCTIONS - UTILITY + *============================================================================*/ + +uint32_t dr_adc_raw_to_mv(uint16_t raw_value) +{ + /* V_mv = (raw * VREF_mv) / 4096 */ + return ((uint32_t)raw_value * m_vref_mv) / (DR_ADC_MAX_VALUE + 1); +} + +void dr_adc_set_vref(uint32_t vref_mv) +{ + m_vref_mv = vref_mv; + ADC_LOG("VREF set to %d mV", vref_mv); +} + +uint32_t dr_adc_get_vref(void) +{ + return m_vref_mv; +} + +/*============================================================================== + * PUBLIC FUNCTIONS - DEBUG + *============================================================================*/ + +bool dr_adc_test(void) +{ + if (!m_initialized) + { + ADC_LOG("Test FAIL: not initialized"); + return false; + } + + uint16_t raw; + dr_adc_err_t err = dr_adc_read_raw(&raw); + + if (err != DR_ADC_OK) + { + ADC_LOG("Test FAIL: read error %d", err); + return false; + } + + if (raw > DR_ADC_MAX_VALUE) + { + ADC_LOG("Test FAIL: invalid value %d", raw); + return false; + } + + ADC_LOG("Test PASS: raw=%d (%dmV)", raw, dr_adc_raw_to_mv(raw)); + return true; +} + +void dr_adc_print_buffer(const uint16_t *buffer, uint16_t num_samples) +{ +#ifdef DEBUG_ADC + if (buffer == NULL || num_samples == 0) return; + + ADC_LOG("Echo buffer (%d samples):", num_samples); + + for (uint16_t i = 0; i < num_samples; i++) + { + ADC_LOG("[%3d] %4d (%4dmV)", i, buffer[i], dr_adc_raw_to_mv(buffer[i])); + } +#else + (void)buffer; + (void)num_samples; +#endif +} + +/*============================================================================== + * BLE TRANSMISSION + *============================================================================*/ + +/* External BLE NUS functions and variables from main.c */ +extern void binary_tx_handler(uint8_t const *ble_bin_buff, uint16_t length); +extern void dr_binary_tx_safe(uint8_t const *ble_bin_buff, uint16_t length); +extern void dr_sd_delay_ms(uint32_t ms); + +/*============================================================================== + * INTEGRATED BURST + CAPTURE + TRANSMIT + *============================================================================*/ + +/* External piezo burst function */ +extern void dr_piezo_burst_sw(uint8_t cycles); + +/* BLE packet constants */ +#define BLE_MTU_SIZE 240 +#define BLE_FIRST_HEADER_LEN 14 /* "rec:" + header */ +#define BLE_CONT_HEADER_LEN 6 /* "red:" + packet_index */ +#define BLE_FIRST_DATA_LEN (BLE_MTU_SIZE - BLE_FIRST_HEADER_LEN) +#define BLE_CONT_DATA_LEN (BLE_MTU_SIZE - BLE_CONT_HEADER_LEN) +#define BLE_PACKET_DELAY_MS 100 /* Inter-packet delay - allow BLE TX buffer to drain */ +#define BLE_FIRST_PACKET_DELAY 5 /* Minimal delay */ + +/* Protocol version markers (OR'd with packet count in reb: header) */ +#define MEC_VERSION_MARKER 0xD000 /* vD: raa only (no ree) - ree causes packet loss */ +#define MAA_VERSION_MARKER 0xF000 /* vF: maa? hardcoded 8-channel debug */ + +/*============================================================================== + * PIEZO CHANNEL SELECTION - implemented in dr_piezo.c + *============================================================================*/ +extern void dr_piezo_select_channel(uint8_t channel); + +/*============================================================================== + * INTEGRATED BURST + CAPTURE + TRANSMIT + *============================================================================*/ + +/** + * @brief Integrated burst + capture + BLE transmit (16-bit raw data) + * + * PROTOCOL v3: Small header packet + separate data packets with delays + * (Large first packets were being dropped by BLE stack) + * + * Response format: + * Packet 1 (reb:): header only (14 bytes) + * Packet 2~N (red:): pkt_idx(2) + raw_data(up to 234 bytes = 117 samples) + * Final (ree:): total_packets(2) + * + * With 140 samples: 280 bytes = reb:(14) + red:(6+234) + red:(6+46) + ree:(6) + */ +dr_adc_err_t dr_adc_burst_capture_transmit(uint8_t freq_option, uint16_t delay_us, + uint16_t num_samples, uint8_t cycles, + uint16_t averaging, uint8_t piezo_ch, + uint8_t *ble_buffer, uint8_t skip_raa) +{ + if (ble_buffer == NULL) return DR_ADC_ERR_INVALID_PARAM; + if (num_samples == 0 || num_samples > DR_ADC_ECHO_SAMPLES_MAX) + return DR_ADC_ERR_INVALID_PARAM; + if (freq_option > 3) freq_option = 0; /* Invalid -> default 1.8MHz */ + if (cycles < 3 || cycles > 9) cycles = 5; /* Valid range: 3~9, default 5 */ + if (averaging == 0) averaging = 1; /* Minimum 1 */ + if (averaging > 1000) averaging = 1000; /* Maximum 1000 */ + if (piezo_ch > 7) piezo_ch = 0; /* Validate piezo channel: 0~7 jhChun 26.01.29 */ + + dr_adc_echo_t echo; + echo.peak_raw = 0; + echo.peak_mv = 0; + echo.peak_index = 0; + echo.peak_time_us = 0; + echo.baseline_raw = 0; + echo.num_samples = 0; + + /* Accumulator buffer for averaging (32-bit to prevent overflow) */ + static uint32_t accum_buffer[DR_ADC_ECHO_SAMPLES_MAX]; + + /* CRITICAL: Clear entire accumulator buffer at start of each mec call */ + memset(accum_buffer, 0, sizeof(accum_buffer)); + + if (!m_initialized) + { + dr_adc_err_t init_err = dr_adc_init(); + if (init_err != DR_ADC_OK) return init_err; + } + + /* Settling time for ADC power */ + nrf_delay_us(100); + + /*--- Step 2: Select piezo channel ---*/ + dr_piezo_select_channel(piezo_ch); + + /*--- Step 3~5: Burst + Delay + Capture ---*/ + if (averaging == 1) { + /*=== SINGLE MEASUREMENT (no averaging) - direct path ===*/ + + /* Execute piezo burst based on frequency option */ + switch (freq_option) { + case 0: + default: + dr_piezo_burst_sw_18mhz(cycles); /* 1.8MHz (default) */ + break; + case 1: + dr_piezo_burst_sw(cycles); /* 2.1MHz */ + break; + case 2: + dr_piezo_burst_sw_20mhz(cycles); /* 2.0MHz */ + break; + case 3: + dr_piezo_burst_sw_17mhz(cycles); /* 1.7MHz */ + break; + } + + /* Delay before capture (configurable, default 20us) */ + if (delay_us > 0) { + nrf_delay_us(delay_us); + } + + /* Capture ADC samples directly to m_echo_buffer */ + dr_adc_err_t err = dr_adc_capture_echo(m_echo_buffer, num_samples); + if (err != DR_ADC_OK) return err; + + ADC_LOG("mec: single measurement (no averaging)"); + } + else { + /*=== MULTIPLE MEASUREMENTS (with averaging) ===*/ + /* Note: accum_buffer already cleared by memset at function start */ + + for (uint16_t avg_iter = 0; avg_iter < averaging; avg_iter++) { + /* Wait for previous echo to decay before next measurement + * 1ms = ~77cm round-trip decay time (sound speed 1.54mm/us) + * Skip delay on first iteration */ + if (avg_iter > 0) { + nrf_delay_us(500); /* 500us between measurements */ + } + + /* Re-select piezo channel before each burst + * (burst functions may modify P1 port state) */ + dr_piezo_select_channel(piezo_ch); + + /* Execute piezo burst based on frequency option */ + switch (freq_option) { + case 0: + default: + dr_piezo_burst_sw_18mhz(cycles); /* 1.8MHz (default) */ + break; + case 1: + dr_piezo_burst_sw(cycles); /* 2.1MHz */ + break; + case 2: + dr_piezo_burst_sw_20mhz(cycles); /* 2.0MHz */ + break; + case 3: + dr_piezo_burst_sw_17mhz(cycles); /* 1.7MHz */ + break; + } + + /* Delay before capture (configurable, default 20us) */ + if (delay_us > 0) { + nrf_delay_us(delay_us); + } + + /* Capture ADC samples */ + dr_adc_err_t err = dr_adc_capture_echo(m_echo_buffer, num_samples); + if (err != DR_ADC_OK) return err; + + /* Accumulate samples */ + for (uint16_t i = 0; i < num_samples; i++) { + accum_buffer[i] += m_echo_buffer[i]; + } + } + + /* Calculate average and store back to m_echo_buffer */ + for (uint16_t i = 0; i < num_samples; i++) { + m_echo_buffer[i] = (uint16_t)(accum_buffer[i] / averaging); + } + + ADC_LOG("mec: averaged %u measurements", averaging); + } + + /*--- Step 6: Analyze averaged data ---*/ + dr_adc_analyze_echo(m_echo_buffer, num_samples, &echo, 100); + + /*--- Step 6: Transmit via BLE - PROTOCOL v3 ---*/ + /* Packet 1: reb:(4) + header(10) = 14 bytes ONLY (no data) */ + /* Packet 2~N: red:(4) + pkt_idx(2) + data(up to 234 bytes) */ + /* Final: ree:(4) + total_pkts(2) = 6 bytes */ + + uint16_t total_data_bytes = echo.num_samples * 2; + uint16_t cont_pkt_data_cap = BLE_MTU_SIZE - BLE_CONT_HEADER_LEN; /* 234 bytes */ + + /* Calculate total data packets needed (header packet doesn't carry data) */ + uint16_t data_packets = (total_data_bytes + cont_pkt_data_cap - 1) / cont_pkt_data_cap; + uint16_t total_packets = 1 + data_packets; /* header + data packets */ + + /* Version marker: select based on skip_raa (0=mec, 1=maa) */ + uint16_t version_marker = skip_raa ? MAA_VERSION_MARKER : MEC_VERSION_MARKER; + uint16_t pkts_with_ver = total_packets | version_marker; + + ADC_LOG("mec v6: samples=%u data=%u bytes, packets=%u", echo.num_samples, total_data_bytes, total_packets); + + /* Packet 1: reb: header ONLY (14 bytes) - small packet won't be dropped */ + ble_buffer[0] = 'r'; + ble_buffer[1] = 'e'; + ble_buffer[2] = 'b'; + ble_buffer[3] = ':'; + ble_buffer[4] = (uint8_t)(pkts_with_ver & 0xFF); + ble_buffer[5] = (uint8_t)(pkts_with_ver >> 8); + ble_buffer[6] = (uint8_t)(echo.peak_raw & 0xFF); + ble_buffer[7] = (uint8_t)(echo.peak_raw >> 8); + ble_buffer[8] = (uint8_t)(echo.peak_index & 0xFF); + ble_buffer[9] = (uint8_t)(echo.peak_index >> 8); + ble_buffer[10] = (uint8_t)(echo.baseline_raw & 0xFF); + ble_buffer[11] = (uint8_t)(echo.baseline_raw >> 8); + ble_buffer[12] = (uint8_t)(echo.num_samples & 0xFF); + ble_buffer[13] = (uint8_t)(echo.num_samples >> 8); + + dr_binary_tx_safe(ble_buffer, 7); /* Send header only: 14 bytes = 7 words */ + dr_sd_delay_ms(BLE_PACKET_DELAY_MS); /* Wait for BLE stack */ + + /* Data packets (red:) - starting from pkt index 0 */ + uint16_t src_idx = 0; + for (uint16_t pkt = 0; pkt < data_packets; pkt++) { + ble_buffer[0] = 'r'; + ble_buffer[1] = 'e'; + ble_buffer[2] = 'd'; + ble_buffer[3] = ':'; + ble_buffer[4] = (uint8_t)(pkt & 0xFF); + ble_buffer[5] = (uint8_t)(pkt >> 8); + + uint16_t dst_idx = 6; + uint16_t bytes_this_pkt = 0; + while (src_idx < echo.num_samples && bytes_this_pkt < cont_pkt_data_cap) { + uint16_t sample = m_echo_buffer[src_idx++] & 0x0FFF; + ble_buffer[dst_idx++] = (uint8_t)(sample & 0xFF); + ble_buffer[dst_idx++] = (uint8_t)(sample >> 8); + bytes_this_pkt += 2; + } + + dr_binary_tx_safe(ble_buffer, dst_idx / 2); /* bytes to words */ + dr_sd_delay_ms(BLE_PACKET_DELAY_MS); /* Inter-packet delay */ + } + + /* vD: Send raa: only - ree: causes subsequent packet loss */ + /* DrBench now saves channel data when receiving raa: */ + /* skip_raa: 0=send raa (mec), 1=skip raa (maa - caller sends final raa) */ + if (!skip_raa) { + ble_buffer[0] = 'r'; + ble_buffer[1] = 'a'; + ble_buffer[2] = 'a'; + ble_buffer[3] = ':'; + ble_buffer[4] = 0x00; + ble_buffer[5] = 0x00; + dr_binary_tx_safe(ble_buffer, 3); /* 6 bytes = 3 words */ + } + + ADC_LOG("mec v6: complete - reb + red*%u + skip_raa=%u (%u samples)", data_packets, skip_raa, echo.num_samples); + + return DR_ADC_OK; +} + + +/*============================================================================== + * 4-CHANNEL CAPTURE FUNCTIONS (maa? command support) + *============================================================================*/ + +dr_adc_err_t dr_adc_capture_channel_only(uint8_t freq_option, uint16_t delay_us, + uint16_t num_samples, uint8_t cycles, + uint16_t averaging, uint8_t piezo_ch, + dr_maa_channel_t *out_channel) +{ + if (out_channel == NULL) return DR_ADC_ERR_INVALID_PARAM; + if (num_samples == 0 || num_samples > MAA_SAMPLES_MAX) + return DR_ADC_ERR_INVALID_PARAM; + if (freq_option > 3) freq_option = 0; + if (cycles < 3 || cycles > 9) cycles = 5; + if (averaging == 0) averaging = 1; + if (averaging > 1000) averaging = 1000; + if (piezo_ch > (MAA_NUM_CHANNELS - 1)) piezo_ch = 0; + + /* Accumulator buffer for averaging */ + static uint32_t accum_buffer[MAA_SAMPLES_MAX]; + memset(accum_buffer, 0, num_samples * sizeof(uint32_t)); + + if (!m_initialized) { + dr_adc_err_t init_err = dr_adc_init(); + if (init_err != DR_ADC_OK) return init_err; + } + nrf_delay_us(100); + + /* Select piezo channel */ + dr_piezo_select_channel(piezo_ch); + + /* Capture with averaging */ + for (uint16_t avg_iter = 0; avg_iter < averaging; avg_iter++) { + if (avg_iter > 0) { + nrf_delay_us(500); /* Echo decay time */ + } + + dr_piezo_select_channel(piezo_ch); + + /* Execute piezo burst */ + switch (freq_option) { + case 0: + default: + dr_piezo_burst_sw_18mhz(cycles); + break; + case 1: + dr_piezo_burst_sw(cycles); + break; + case 2: + dr_piezo_burst_sw_20mhz(cycles); + break; + case 3: + dr_piezo_burst_sw_17mhz(cycles); + break; + } + + if (delay_us > 0) { + nrf_delay_us(delay_us); + } + + /* Capture to internal buffer */ + dr_adc_err_t err = dr_adc_capture_echo(m_echo_buffer, num_samples); + if (err != DR_ADC_OK) return err; + + /* Accumulate */ + for (uint16_t i = 0; i < num_samples; i++) { + accum_buffer[i] += m_echo_buffer[i]; + } + } + + /* Calculate average and copy to output */ + for (uint16_t i = 0; i < num_samples; i++) { + out_channel->samples[i] = (uint16_t)(accum_buffer[i] / averaging); + } + out_channel->num_samples = num_samples; + + /* Analyze echo data */ + dr_adc_echo_t echo; + dr_adc_analyze_echo(out_channel->samples, num_samples, &echo, 100); + out_channel->peak_raw = echo.peak_raw; + out_channel->peak_index = echo.peak_index; + out_channel->baseline_raw = echo.baseline_raw; + + ADC_LOG("maa capture CH%u: peak=%u idx=%u baseline=%u", + piezo_ch, out_channel->peak_raw, out_channel->peak_index, out_channel->baseline_raw); + + return DR_ADC_OK; +} + + +dr_adc_err_t dr_adc_transmit_channel(const dr_maa_channel_t *ch_data, + uint8_t *ble_buffer) +{ + /* Debug BEFORE null check to see if we even enter the function */ + dr_ble_debug(0x00FF, 0xAAAA); /* Function called marker */ + + if (ch_data == NULL || ble_buffer == NULL) { + dr_ble_debug(0x00FE, (ch_data == NULL ? 0x0001 : 0) | (ble_buffer == NULL ? 0x0002 : 0)); + return DR_ADC_ERR_INVALID_PARAM; + } + + dr_ble_debug(0x0100, ch_data->num_samples); /* Function entry - params valid */ + + uint16_t total_data_bytes = ch_data->num_samples * 2; + uint16_t cont_pkt_data_cap = BLE_MTU_SIZE - BLE_CONT_HEADER_LEN; /* 234 bytes */ + uint16_t data_packets = (total_data_bytes + cont_pkt_data_cap - 1) / cont_pkt_data_cap; + uint16_t total_packets = 1 + data_packets; + + /* Version marker: MAA_VERSION_MARKER | total_packets */ + uint16_t pkt_with_ver = total_packets | MAA_VERSION_MARKER; + + dr_ble_debug(0x0101, total_packets); /* Before reb: */ + + /* Packet 1: reb: header (14 bytes) */ + ble_buffer[0] = 'r'; + ble_buffer[1] = 'e'; + ble_buffer[2] = 'b'; + ble_buffer[3] = ':'; + ble_buffer[4] = (uint8_t)(pkt_with_ver & 0xFF); + ble_buffer[5] = (uint8_t)(pkt_with_ver >> 8); + ble_buffer[6] = (uint8_t)(ch_data->peak_raw & 0xFF); + ble_buffer[7] = (uint8_t)(ch_data->peak_raw >> 8); + ble_buffer[8] = (uint8_t)(ch_data->peak_index & 0xFF); + ble_buffer[9] = (uint8_t)(ch_data->peak_index >> 8); + ble_buffer[10] = (uint8_t)(ch_data->baseline_raw & 0xFF); + ble_buffer[11] = (uint8_t)(ch_data->baseline_raw >> 8); + ble_buffer[12] = (uint8_t)(ch_data->num_samples & 0xFF); + ble_buffer[13] = (uint8_t)(ch_data->num_samples >> 8); + + /* Send reb: header */ + /* Use dr_binary_tx_safe like mec? does, with dr_sd_delay_ms for SoftDevice */ + dr_binary_tx_safe(ble_buffer, 7); + dr_sd_delay_ms(BLE_PACKET_DELAY_MS); + + dr_ble_debug(0x0102, data_packets); /* After reb: */ + + /* Data packets (red:) */ + uint16_t src_idx = 0; + for (uint16_t pkt = 0; pkt < data_packets; pkt++) { + ble_buffer[0] = 'r'; + ble_buffer[1] = 'e'; + ble_buffer[2] = 'd'; + ble_buffer[3] = ':'; + ble_buffer[4] = (uint8_t)(pkt & 0xFF); + ble_buffer[5] = (uint8_t)(pkt >> 8); + + uint16_t dst_idx = 6; + uint16_t bytes_this_pkt = 0; + while (src_idx < ch_data->num_samples && bytes_this_pkt < cont_pkt_data_cap) { + uint16_t sample = ch_data->samples[src_idx++] & 0x0FFF; + ble_buffer[dst_idx++] = (uint8_t)(sample & 0xFF); + ble_buffer[dst_idx++] = (uint8_t)(sample >> 8); + bytes_this_pkt += 2; + } + + dr_binary_tx_safe(ble_buffer, dst_idx / 2); + dr_sd_delay_ms(BLE_PACKET_DELAY_MS); + + dr_ble_debug(0x0103, pkt); /* red: packet sent */ + } + + dr_ble_debug(0x0104, 0); /* Before ree: */ + + /* Final packet: ree: */ + ble_buffer[0] = 'r'; + ble_buffer[1] = 'e'; + ble_buffer[2] = 'e'; + ble_buffer[3] = ':'; + ble_buffer[4] = (uint8_t)(total_packets & 0xFF); + ble_buffer[5] = (uint8_t)(total_packets >> 8); + dr_binary_tx_safe(ble_buffer, 3); + + dr_ble_debug(0x010F, 0); /* Function complete */ + + return DR_ADC_OK; +} + + +/*============================================================================== + * DELTA COMPRESSION FUNCTIONS + *============================================================================*/ + +dr_adc_err_t dr_adc_delta_compress(const uint16_t *samples, uint16_t num_samples, + uint8_t *out_buffer, uint16_t *out_size) +{ + if (samples == NULL || out_buffer == NULL || out_size == NULL) + return DR_ADC_ERR_INVALID_PARAM; + if (num_samples == 0) { + *out_size = 0; + return DR_ADC_OK; + } + + uint16_t idx = 0; + + /* First sample: 16-bit raw value */ + out_buffer[idx++] = (uint8_t)(samples[0] & 0xFF); + out_buffer[idx++] = (uint8_t)(samples[0] >> 8); + + /* Delta encode remaining samples */ + for (uint16_t i = 1; i < num_samples; i++) { + int16_t delta = (int16_t)samples[i] - (int16_t)samples[i-1]; + + if (delta >= -127 && delta <= 127) { + /* Normal delta: fits in signed 8-bit */ + out_buffer[idx++] = (int8_t)delta; + } else { + /* Out of range: escape + 16-bit raw value */ + out_buffer[idx++] = DELTA_ESCAPE_BYTE; + out_buffer[idx++] = (uint8_t)(samples[i] & 0xFF); + out_buffer[idx++] = (uint8_t)(samples[i] >> 8); + } + } + + *out_size = idx; + return DR_ADC_OK; +} + + +dr_adc_err_t dr_adc_transmit_channel_delta(const dr_maa_channel_t *ch_data, + uint8_t *ble_buffer) +{ + if (ch_data == NULL || ble_buffer == NULL) return DR_ADC_ERR_INVALID_PARAM; + + /* Compress data first */ + static uint8_t delta_buffer[400]; /* Worst case: 140*3 = 420 bytes */ + uint16_t compressed_size = 0; + + dr_adc_err_t err = dr_adc_delta_compress(ch_data->samples, ch_data->num_samples, + delta_buffer, &compressed_size); + if (err != DR_ADC_OK) return err; + + ADC_LOG("maa delta: %u samples -> %u bytes (%.0f%%)", + ch_data->num_samples, compressed_size, + 100.0f * compressed_size / (ch_data->num_samples * 2)); + + uint16_t cont_pkt_data_cap = BLE_MTU_SIZE - BLE_CONT_HEADER_LEN; /* 234 bytes */ + uint16_t data_packets = (compressed_size + cont_pkt_data_cap - 1) / cont_pkt_data_cap; + uint16_t total_packets = 1 + data_packets; + + /* Version marker for delta mode: 0xC000 | total_packets */ + uint16_t pkt_with_ver = total_packets | 0xC000; /* v2 delta marker */ + + /* Packet 1: rdb: header (16 bytes) - includes compressed_size */ + ble_buffer[0] = 'r'; + ble_buffer[1] = 'd'; + ble_buffer[2] = 'b'; + ble_buffer[3] = ':'; + ble_buffer[4] = (uint8_t)(pkt_with_ver & 0xFF); + ble_buffer[5] = (uint8_t)(pkt_with_ver >> 8); + ble_buffer[6] = (uint8_t)(ch_data->peak_raw & 0xFF); + ble_buffer[7] = (uint8_t)(ch_data->peak_raw >> 8); + ble_buffer[8] = (uint8_t)(ch_data->peak_index & 0xFF); + ble_buffer[9] = (uint8_t)(ch_data->peak_index >> 8); + ble_buffer[10] = (uint8_t)(ch_data->baseline_raw & 0xFF); + ble_buffer[11] = (uint8_t)(ch_data->baseline_raw >> 8); + ble_buffer[12] = (uint8_t)(ch_data->num_samples & 0xFF); + ble_buffer[13] = (uint8_t)(ch_data->num_samples >> 8); + ble_buffer[14] = (uint8_t)(compressed_size & 0xFF); + ble_buffer[15] = (uint8_t)(compressed_size >> 8); + + dr_binary_tx_safe(ble_buffer, 8); /* 16 bytes = 8 words */ + dr_sd_delay_ms(BLE_PACKET_DELAY_MS); + + /* Data packets (rdd:) */ + uint16_t src_idx = 0; + for (uint16_t pkt = 0; pkt < data_packets; pkt++) { + ble_buffer[0] = 'r'; + ble_buffer[1] = 'd'; + ble_buffer[2] = 'd'; + ble_buffer[3] = ':'; + ble_buffer[4] = (uint8_t)(pkt & 0xFF); + ble_buffer[5] = (uint8_t)(pkt >> 8); + + uint16_t dst_idx = 6; + uint16_t bytes_this_pkt = 0; + while (src_idx < compressed_size && bytes_this_pkt < cont_pkt_data_cap) { + ble_buffer[dst_idx++] = delta_buffer[src_idx++]; + bytes_this_pkt++; + } + + /* Pad to word boundary if needed */ + if (dst_idx & 1) { + ble_buffer[dst_idx++] = 0; + } + + dr_binary_tx_safe(ble_buffer, dst_idx / 2); + dr_sd_delay_ms(BLE_PACKET_DELAY_MS); + } + + /* Final packet: rde: */ + ble_buffer[0] = 'r'; + ble_buffer[1] = 'd'; + ble_buffer[2] = 'e'; + ble_buffer[3] = ':'; + ble_buffer[4] = (uint8_t)(total_packets & 0xFF); + ble_buffer[5] = (uint8_t)(total_packets >> 8); + dr_binary_tx_safe(ble_buffer, 3); + dr_sd_delay_ms(BLE_PACKET_DELAY_MS); + + return DR_ADC_OK; +} + + +/*============================================================================== + * ASYNC MAA - Non-blocking 8-channel capture + * + * State machine driven by BLE TX complete events (BLE_NUS_EVT_TX_RDY) + * This prevents blocking the main loop and allows SoftDevice to process events. + *============================================================================*/ + +/* Global async context */ +static maa_async_ctx_t g_maa_ctx = { .state = MAA_ASYNC_IDLE }; + +/* Version marker for async MAA */ +#define MAA_ASYNC_VERSION_MARKER 0xC000 /* vC: async 8-channel + raa delay fix + cleanup */ + +/** + * @brief Capture one channel (internal helper) + */ +static dr_adc_err_t maa_async_capture_channel(uint8_t ch) +{ + if (ch > (MAA_NUM_CHANNELS - 1)) return DR_ADC_ERR_INVALID_PARAM; + + return dr_adc_capture_channel_only( + g_maa_ctx.freq_option, + g_maa_ctx.delay_us, + g_maa_ctx.num_samples, + g_maa_ctx.cycles, + g_maa_ctx.averaging, + ch, + &g_maa_ctx.channels[ch] + ); +} + +/** + * @brief Send reb: header for current channel + */ +static void maa_async_send_header(void) +{ + dr_maa_channel_t *ch = &g_maa_ctx.channels[g_maa_ctx.current_ch]; + uint8_t *buf = g_maa_ctx.ble_buffer; + + uint16_t total_data_bytes = ch->num_samples * 2; + uint16_t cont_pkt_data_cap = BLE_MTU_SIZE - BLE_CONT_HEADER_LEN; + g_maa_ctx.data_packets = (total_data_bytes + cont_pkt_data_cap - 1) / cont_pkt_data_cap; + g_maa_ctx.total_packets = 1 + g_maa_ctx.data_packets; + + uint16_t pkts_with_ver = g_maa_ctx.total_packets | MAA_ASYNC_VERSION_MARKER; + + /* reb: header (14 bytes) */ + buf[0] = 'r'; buf[1] = 'e'; buf[2] = 'b'; buf[3] = ':'; + buf[4] = (uint8_t)(pkts_with_ver & 0xFF); + buf[5] = (uint8_t)(pkts_with_ver >> 8); + buf[6] = (uint8_t)(ch->peak_raw & 0xFF); + buf[7] = (uint8_t)(ch->peak_raw >> 8); + buf[8] = (uint8_t)(ch->peak_index & 0xFF); + buf[9] = (uint8_t)(ch->peak_index >> 8); + buf[10] = (uint8_t)(ch->baseline_raw & 0xFF); + buf[11] = (uint8_t)(ch->baseline_raw >> 8); + buf[12] = (uint8_t)(ch->num_samples & 0xFF); + buf[13] = (uint8_t)(ch->num_samples >> 8); + + dr_binary_tx_safe(buf, 7); /* 14 bytes = 7 words */ + dr_sd_delay_ms(50); /* Allow BLE stack to process TX */ + + g_maa_ctx.current_pkt = 0; + g_maa_ctx.data_offset = 0; + g_maa_ctx.state = MAA_ASYNC_TX_DATA; + + ADC_LOG("maa_async: CH%u reb: sent (pkts=%u)", g_maa_ctx.current_ch, g_maa_ctx.total_packets); +} + +/** + * @brief Send next red: data packet + * @return true if more packets to send, false if done with current channel + */ +static bool maa_async_send_data_packet(void) +{ + dr_maa_channel_t *ch = &g_maa_ctx.channels[g_maa_ctx.current_ch]; + uint8_t *buf = g_maa_ctx.ble_buffer; + + uint16_t total_data_bytes = ch->num_samples * 2; + if (g_maa_ctx.data_offset >= total_data_bytes) { + return false; /* All data sent */ + } + + uint16_t pkt_idx = g_maa_ctx.current_pkt; + uint16_t remaining = total_data_bytes - g_maa_ctx.data_offset; + uint16_t chunk_size = (remaining > BLE_CONT_DATA_LEN) ? BLE_CONT_DATA_LEN : remaining; + + /* red: packet header */ + buf[0] = 'r'; buf[1] = 'e'; buf[2] = 'd'; buf[3] = ':'; + buf[4] = (uint8_t)(pkt_idx & 0xFF); + buf[5] = (uint8_t)(pkt_idx >> 8); + + /* Copy sample data */ + uint16_t src_sample_idx = g_maa_ctx.data_offset / 2; + uint16_t dst_idx = 6; + for (uint16_t i = 0; i < chunk_size / 2 && src_sample_idx < ch->num_samples; i++, src_sample_idx++) { + uint16_t sample = ch->samples[src_sample_idx]; + buf[dst_idx++] = (uint8_t)(sample & 0xFF); + buf[dst_idx++] = (uint8_t)(sample >> 8); + } + + dr_binary_tx_safe(buf, dst_idx / 2); + dr_sd_delay_ms(50); /* Allow BLE stack to process TX */ + + g_maa_ctx.data_offset += chunk_size; + g_maa_ctx.current_pkt++; + + ADC_LOG("maa_async: CH%u red:%u (%u/%u bytes)", + g_maa_ctx.current_ch, pkt_idx, g_maa_ctx.data_offset, total_data_bytes); + + return (g_maa_ctx.data_offset < total_data_bytes); +} + +/** + * @brief Send raa: completion marker + */ +static void maa_async_send_completion(uint16_t status) +{ + uint8_t *buf = g_maa_ctx.ble_buffer; + + /* Wait for previous TX to complete before sending raa: */ + dr_sd_delay_ms(50); + + buf[0] = 'r'; buf[1] = 'a'; buf[2] = 'a'; buf[3] = ':'; + buf[4] = (uint8_t)(status & 0xFF); + buf[5] = (uint8_t)(status >> 8); + + dr_binary_tx_safe(buf, 3); + + g_maa_ctx.state = MAA_ASYNC_IDLE; + ADC_LOG("maa_async: complete, status=0x%04X", status); +} + +/*============================================================================== + * PUBLIC ASYNC API + *============================================================================*/ + +dr_adc_err_t maa_async_start(uint8_t freq_option, uint16_t delay_us, + uint16_t num_samples, uint8_t cycles, + uint16_t averaging, uint8_t *ble_buffer) +{ + if (g_maa_ctx.state != MAA_ASYNC_IDLE) { + ADC_LOG("maa_async_start: busy"); + return DR_ADC_ERR_NOT_INIT; /* Already running */ + } + + if (ble_buffer == NULL) return DR_ADC_ERR_INVALID_PARAM; + if (num_samples == 0 || num_samples > DR_ADC_ECHO_SAMPLES_MAX) + return DR_ADC_ERR_INVALID_PARAM; + + /* Initialize context */ + g_maa_ctx.freq_option = freq_option; + g_maa_ctx.delay_us = delay_us; + g_maa_ctx.num_samples = num_samples; + g_maa_ctx.cycles = (cycles < 3 || cycles > 9) ? 5 : cycles; + g_maa_ctx.averaging = (averaging == 0) ? 1 : ((averaging > 1000) ? 1000 : averaging); + g_maa_ctx.ble_buffer = ble_buffer; + g_maa_ctx.current_ch = 0; + g_maa_ctx.current_pkt = 0; + g_maa_ctx.data_offset = 0; + + ADC_LOG("maa_async_start: freq=%u delay=%u samples=%u cycles=%u avg=%u", + freq_option, delay_us, num_samples, g_maa_ctx.cycles, g_maa_ctx.averaging); + + /* Capture CH0 */ + g_maa_ctx.state = MAA_ASYNC_CAPTURING; + dr_adc_err_t err = maa_async_capture_channel(0); + if (err != DR_ADC_OK) { + ADC_LOG("maa_async_start: CH0 capture failed (%d)", err); + maa_async_send_completion(0xFFF0); + return err; + } + + /* Send CH0 header - this will trigger TX_RDY for subsequent packets */ + maa_async_send_header(); + + return DR_ADC_OK; +} + +bool maa_async_on_tx_ready(void) +{ + if (g_maa_ctx.state == MAA_ASYNC_IDLE) { + return false; + } + + switch (g_maa_ctx.state) { + case MAA_ASYNC_TX_DATA: + /* Send next data packet */ + if (!maa_async_send_data_packet()) { + /* Current channel done, move to next */ + g_maa_ctx.current_ch++; + if (g_maa_ctx.current_ch >= MAA_NUM_CHANNELS) { + /* All channels done */ + g_maa_ctx.state = MAA_ASYNC_COMPLETE; + maa_async_send_completion(0x0000); + return false; + } else { + /* Capture next channel */ + g_maa_ctx.state = MAA_ASYNC_CAPTURING; + dr_adc_err_t err = maa_async_capture_channel(g_maa_ctx.current_ch); + if (err != DR_ADC_OK) { + ADC_LOG("maa_async: CH%u capture failed", g_maa_ctx.current_ch); + maa_async_send_completion(0xFFF0 | g_maa_ctx.current_ch); + return false; + } + /* Send header for new channel */ + maa_async_send_header(); + } + } + return true; + + case MAA_ASYNC_TX_HEADER: + /* Header sent, start sending data */ + g_maa_ctx.state = MAA_ASYNC_TX_DATA; + maa_async_send_data_packet(); + return true; + + case MAA_ASYNC_CAPTURING: + /* Shouldn't happen - capture is synchronous */ + return true; + + case MAA_ASYNC_COMPLETE: + case MAA_ASYNC_IDLE: + default: + return false; + } +} + +bool maa_async_is_busy(void) +{ + return (g_maa_ctx.state != MAA_ASYNC_IDLE); +} + +maa_async_state_t maa_async_get_state(void) +{ + return g_maa_ctx.state; +} + +void maa_async_abort(void) +{ + if (g_maa_ctx.state != MAA_ASYNC_IDLE) { + ADC_LOG("maa_async_abort: aborting from state %d", g_maa_ctx.state); + g_maa_ctx.state = MAA_ASYNC_IDLE; + } +} + diff --git a/project/ble_peripheral/ble_app_bladder_patch/cmd/dr_adc121s051.h b/project/ble_peripheral/ble_app_bladder_patch/cmd/dr_adc121s051.h new file mode 100644 index 0000000..f4edec9 --- /dev/null +++ b/project/ble_peripheral/ble_app_bladder_patch/cmd/dr_adc121s051.h @@ -0,0 +1,505 @@ +/******************************************************************************* + * @file dr_adc121s051.h + * @brief ADC121S051 12-bit ADC Driver for nRF52840 + * For 1.2MHz Piezo Echo Envelope Detection + * @author Charles KWON + * @date 2025-12-15 + * + * @details This driver reads the envelope-detected DC level from piezo echo. + * + * Signal Flow: + * + * [Piezo TX] [Echo RX] [Envelope] [ADC] [MCU] + * 1.2MHz --> Reflect --> Detector --> DC Level --> Digital + * burst signal (hardware) reading value + * + * The envelope detector circuit converts the 1.2MHz echo burst + * into a DC voltage proportional to the echo amplitude. + * ADC samples this DC level for amplitude measurement. + * + * @note Hardware: Texas Instruments ADC121S051 + * - 12-bit resolution (0-4095) + * - Sample rate: 200-500 ksps + * - Input range: 0V to VA + * - SPI interface (software bit-bang) + ******************************************************************************/ + +#ifndef DR_ADC121S051_H +#define DR_ADC121S051_H + +#include +#include +#include "nrf_gpio.h" + +/*============================================================================== + * PIN CONFIGURATION + * + * WARNING: Never hardcode pin numbers! + * Hardcoding may save a developer's time momentarily, + * but it will also shorten their lifespan. + *============================================================================*/ +#define DR_ADC_PIN_SCLK NRF_GPIO_PIN_MAP(0, 14) /**< Serial Clock */ +#define DR_ADC_PIN_SDATA NRF_GPIO_PIN_MAP(0, 15) /**< Serial Data (MISO) */ +#define DR_ADC_PIN_CS NRF_GPIO_PIN_MAP(0, 19) /**< Chip Select P0.13 -> P0.19 */ + +/*============================================================================== + * ADC SPECIFICATIONS + *============================================================================*/ +#define DR_ADC_RESOLUTION 12 /**< Bits */ +#define DR_ADC_MAX_VALUE 4095 /**< 2^12 - 1 */ +#define DR_ADC_VREF_MV 3300 /**< Reference voltage (mV) */ + +/*============================================================================== + * ECHO DETECTION CONFIGURATION + * + * Bladder Measurement Requirements: + * - Target measurement range: 20cm (200mm) + * - SCLK frequency: 8.6MHz (bit-bang SPI) + * - ADC121S051 requires 16 SCLK cycles per sample + * - Actual sample rate: 8.6MHz / 16 = 0.5375MHz = 537.5kHz + * - Actual sample interval: 16 / 8.6MHz = 1.86us + * - Sound speed in tissue: 1540m/s = 1.54mm/us + * + * Formula: samples = distance(mm) * 2 / (1.86us * 1.54mm/us) + * = distance(mm) * 2 / 2.86 + * = distance(mm) * 0.7 + * + * 10cm = 100mm -> 100 * 0.7 = 70 samples (round-trip 130us) + * 17cm = 170mm -> 170 * 0.7 = 119 samples (round-trip 221us) + * 20cm = 200mm -> 200 * 0.7 = 140 samples (round-trip 260us) + * + * Buffer size: 200 samples * 2 bytes = 400 bytes (RAM 256KB, OK) + * BLE transmission: 140 samples * 2 bytes = 280 bytes (16-bit raw, no packing) + *============================================================================*/ +#define DR_ADC_SCLK_MHZ 8.6f /**< SPI bit-bang SCLK frequency */ +#define DR_ADC_CLOCKS_PER_SAMPLE 16 /**< ADC121S051: 16 SCLK per sample */ +#define DR_ADC_ECHO_SAMPLES_MAX 200 /**< Maximum samples (20cm+ with margin) */ +#define DR_ADC_ECHO_SAMPLES_DEFAULT 140 /**< Default samples (20cm depth target) */ +#define DR_ADC_SAMPLE_INTERVAL_US 1.86f /**< 16 / 8.6MHz = 1.86us per sample */ +#define DR_ADC_SOUND_SPEED_MM_US 1.54f /**< Sound speed in tissue (mm/us) */ + +/*============================================================================== + * ERROR CODES + *============================================================================*/ +typedef enum { + DR_ADC_OK = 0, + DR_ADC_ERR_NOT_INIT, + DR_ADC_ERR_INVALID_PARAM, + DR_ADC_ERR_NO_ECHO +} dr_adc_err_t; + +/*============================================================================== + * DATA STRUCTURES + *============================================================================*/ + +/** + * @brief Single ADC reading result + */ +typedef struct { + uint16_t raw; /**< Raw 12-bit value (0-4095) */ + uint32_t voltage_mv; /**< Voltage in millivolts */ +} dr_adc_result_t; + +/** + * @brief Echo measurement result + */ +typedef struct { + uint16_t peak_raw; /**< Peak amplitude (raw) */ + uint32_t peak_mv; /**< Peak amplitude (mV) */ + uint16_t peak_index; /**< Sample index of peak */ + uint32_t peak_time_us; /**< Time to peak (us) */ + uint16_t baseline_raw; /**< Baseline level before echo */ + uint16_t num_samples; /**< Number of samples captured */ +} dr_adc_echo_t; + +/** + * @brief Echo capture configuration + */ +typedef struct { + uint16_t num_samples; /**< Samples to capture (1-200) */ + uint16_t threshold_raw; /**< Minimum peak threshold */ + uint16_t delay_us; /**< Delay before capture starts */ +} dr_adc_echo_config_t; + +/*============================================================================== + * INITIALIZATION + *============================================================================*/ + +/** + * @brief Initialize ADC driver + * @return dr_adc_err_t Error code + */ +dr_adc_err_t dr_adc_init(void); + +/** + * @brief Uninitialize ADC driver + */ +void dr_adc_uninit(void); + +/** + * @brief Check if initialized + */ +bool dr_adc_is_initialized(void); + +/*============================================================================== + * BASIC READ FUNCTIONS + *============================================================================*/ + +/** + * @brief Read single ADC value + * @param result Pointer to result structure + * @return dr_adc_err_t Error code + */ +dr_adc_err_t dr_adc_read(dr_adc_result_t *result); + +/** + * @brief Read raw 12-bit value only + * @param raw_value Pointer to store value + * @return dr_adc_err_t Error code + */ +dr_adc_err_t dr_adc_read_raw(uint16_t *raw_value); + +/** + * @brief Read averaged value + * @param result Pointer to result structure + * @param num_samples Number of samples to average (1-256) + * @return dr_adc_err_t Error code + */ +dr_adc_err_t dr_adc_read_averaged(dr_adc_result_t *result, uint16_t num_samples); + +/*============================================================================== + * ECHO DETECTION FUNCTIONS + *============================================================================*/ + +/** + * @brief Capture echo envelope after piezo burst + * @param buffer Array to store samples (must be pre-allocated) + * @param num_samples Number of samples to capture + * @return dr_adc_err_t Error code + * + * @note Call this immediately after dr_piezo_burst_sw() + */ +dr_adc_err_t dr_adc_capture_echo(uint16_t *buffer, uint16_t num_samples); + +/** + * @brief Capture and analyze echo in one call + * @param echo Pointer to echo result structure + * @param config Pointer to capture configuration (NULL for defaults) + * @return dr_adc_err_t Error code + */ +dr_adc_err_t dr_adc_measure_echo(dr_adc_echo_t *echo, const dr_adc_echo_config_t *config); + +/** + * @brief Piezo burst + Echo capture in one call + * @param cycles Number of burst cycles (3~9) + * @param delay_us Delay before capture (us) + * @param num_samples Number of samples to capture + * @param echo Pointer to echo result structure + * @return dr_adc_err_t Error code + */ +dr_adc_err_t dr_adc_burst_and_capture(uint8_t cycles, uint16_t delay_us, + uint16_t num_samples, dr_adc_echo_t *echo); + +/** + * @brief Get pointer to last captured echo buffer + * @return Pointer to internal buffer (valid until next capture) + * @note Buffer contains num_samples values from last burst_and_capture call + */ +const uint16_t* dr_adc_get_echo_buffer(void); + +/** + * @brief Analyze captured echo buffer + * @param buffer Sample buffer + * @param num_samples Number of samples in buffer + * @param echo Pointer to echo result structure + * @param threshold Minimum threshold for valid peak + * @return dr_adc_err_t Error code + */ +dr_adc_err_t dr_adc_analyze_echo(const uint16_t *buffer, uint16_t num_samples, + dr_adc_echo_t *echo, uint16_t threshold); + +/** + * @brief Find peak in buffer + * @param buffer Sample buffer + * @param num_samples Number of samples + * @param peak_value Pointer to store peak value + * @param peak_index Pointer to store peak index (can be NULL) + */ +void dr_adc_find_peak(const uint16_t *buffer, uint16_t num_samples, + uint16_t *peak_value, uint16_t *peak_index); + +/** + * @brief Calculate baseline (average of first N samples) + * @param buffer Sample buffer + * @param num_samples Number of samples to average for baseline + * @return Baseline value + */ +uint16_t dr_adc_calc_baseline(const uint16_t *buffer, uint16_t num_samples); + +/*============================================================================== + * UTILITY FUNCTIONS + *============================================================================*/ + +/** + * @brief Convert raw value to millivolts + * @param raw_value Raw 12-bit value + * @return Voltage in millivolts + */ +uint32_t dr_adc_raw_to_mv(uint16_t raw_value); + +/** + * @brief Set reference voltage + * @param vref_mv Reference voltage in millivolts + */ +void dr_adc_set_vref(uint32_t vref_mv); + +/** + * @brief Get reference voltage + * @return Reference voltage in millivolts + */ +uint32_t dr_adc_get_vref(void); + +/*============================================================================== + * DEBUG FUNCTIONS + *============================================================================*/ + +/** + * @brief Test ADC communication + * @return true if OK + */ +bool dr_adc_test(void); + +/** + * @brief Print echo buffer to debug output + * @param buffer Sample buffer + * @param num_samples Number of samples + */ +void dr_adc_print_buffer(const uint16_t *buffer, uint16_t num_samples); + +/*============================================================================== + * POWER CONTROL + *============================================================================*/ + + +/*============================================================================== + * BLE TRANSMISSION CALLBACK + *============================================================================*/ + +/*============================================================================== + * INTEGRATED BURST + CAPTURE + TRANSMIT + *============================================================================*/ + +/** + * @brief Piezo burst + ADC capture + BLE transmission (all-in-one) + * + * This function performs the complete measurement cycle internally: + * 1. Power on ADC + * 2. Select piezo channel (0~7) + * 3. Execute piezo burst (frequency based on freq_option) + * 4. Capture echo samples (after delay_us) - repeated 'averaging' times + * 5. Average the captured samples (firmware-level noise reduction) + * 6. Analyze peak/baseline + * 7. Transmit data via BLE with proper packet timing + * + * @param freq_option Frequency option: 0=1.8MHz (default), 1=2.1MHz, 2=2.0MHz, 3=1.7MHz + * @param delay_us Delay before capture (us), default 20 + * @param num_samples Number of samples to capture (1~200) + * @param cycles Number of burst cycles (3~7), default 5 + * @param averaging Number of measurements to average (1~1000), default 1 + * @param piezo_ch Piezo channel to use (0~7), default 0 + * @param ble_buffer Working buffer for BLE packets (must be >= 240 bytes) + * @return dr_adc_err_t Error code + * + * @note Must call dr_adc_register_ble_tx() before using this function + * @note BLE packets: reb: (header), red: (data), ree: (end) + * @note Higher averaging reduces noise but increases measurement time (~0.3ms per avg) + */ +dr_adc_err_t dr_adc_burst_capture_transmit(uint8_t freq_option, uint16_t delay_us, + uint16_t num_samples, uint8_t cycles, + uint16_t averaging, uint8_t piezo_ch, + uint8_t *ble_buffer, uint8_t skip_raa); + +/** + * @brief Select piezo channel (0~7) + * @param channel Piezo channel number (0~7) + * + * @note Hardware-dependent: requires MUX or individual GPIO control + * Currently uses placeholder - implement based on actual hardware + */ +void dr_piezo_select_channel(uint8_t channel); + +/*============================================================================== + * 4-CHANNEL CAPTURE (maa? command support) + *============================================================================*/ + +/** + * @brief 8-channel echo buffer for maa? command + * Memory: 140 samples × 2 bytes × 8 channels = 2,240 bytes + */ +#define MAA_NUM_CHANNELS 8 /* 4 -> 8 jhChun 26.02.12*/ +#define MAA_SAMPLES_MAX 200 + +/** + * @brief Echo data for one channel + */ +typedef struct { + uint16_t samples[MAA_SAMPLES_MAX]; /**< Raw sample data */ + uint16_t num_samples; /**< Actual sample count */ + uint16_t peak_raw; /**< Peak amplitude */ + uint16_t peak_index; /**< Peak sample index */ + uint16_t baseline_raw; /**< Baseline level */ +} dr_maa_channel_t; + +/** + * @brief Capture echo from one channel (no BLE transmission) + * + * Captures averaged echo data for a single channel and stores + * in the provided channel buffer. Does NOT transmit via BLE. + * + * @param freq_option Frequency: 0=1.8MHz, 1=2.1MHz, 2=2.0MHz, 3=1.7MHz + * @param delay_us Delay before capture (us) + * @param num_samples Number of samples (1~200) + * @param cycles Burst cycles (3~7) + * @param averaging Number of averages (1~1000) + * @param piezo_ch Piezo channel (0~7) + * @param out_channel Output channel data structure + * @return dr_adc_err_t Error code + */ +dr_adc_err_t dr_adc_capture_channel_only(uint8_t freq_option, uint16_t delay_us, + uint16_t num_samples, uint8_t cycles, + uint16_t averaging, uint8_t piezo_ch, + dr_maa_channel_t *out_channel); + +/** + * @brief Transmit captured channel data via BLE + * + * Sends previously captured channel data using reb:/red:/ree: protocol. + * + * @param ch_data Pointer to captured channel data + * @param ble_buffer Working buffer for BLE packets (>= 240 bytes) + * @return dr_adc_err_t Error code + */ +dr_adc_err_t dr_adc_transmit_channel(const dr_maa_channel_t *ch_data, + uint8_t *ble_buffer); + +/*============================================================================== + * DELTA COMPRESSION (maa? mode=1) + * + * Format: + * Byte 0-1: First sample (16-bit, little endian) + * Byte 2+: Delta values (8-bit signed) + * If delta > 127 or < -127: escape (0x80) + 16-bit value + * + * Expected compression: ~50% (280 bytes -> ~140 bytes) + *============================================================================*/ + +#define DELTA_ESCAPE_BYTE 0x80 /**< Escape marker for out-of-range delta */ + +/** + * @brief Compress sample data using delta encoding + * + * @param samples Input sample array (16-bit values) + * @param num_samples Number of samples + * @param out_buffer Output buffer for compressed data + * @param out_size Output: number of bytes written + * @return dr_adc_err_t Error code + */ +dr_adc_err_t dr_adc_delta_compress(const uint16_t *samples, uint16_t num_samples, + uint8_t *out_buffer, uint16_t *out_size); + +/** + * @brief Transmit captured channel data via BLE with delta compression + * + * Uses rdb:/rdd:/rde: protocol (delta variant of reb:/red:/ree:) + * + * @param ch_data Pointer to captured channel data + * @param ble_buffer Working buffer for BLE packets (>= 240 bytes) + * @return dr_adc_err_t Error code + */ +dr_adc_err_t dr_adc_transmit_channel_delta(const dr_maa_channel_t *ch_data, + uint8_t *ble_buffer); + +/*============================================================================== + * ASYNC MAA - Non-blocking 8-channel capture + * + * Design: State machine driven by BLE TX complete events + * Flow: + * maa? cmd -> maa_async_start() -> capture CH0 -> TX reb: -> TX red: ... + * BLE_NUS_EVT_TX_RDY -> maa_async_continue() -> TX next packet or next channel + * All done -> TX raa: -> state=IDLE + *============================================================================*/ + +/** @brief MAA async state machine states */ +typedef enum { + MAA_ASYNC_IDLE = 0, /**< Not active */ + MAA_ASYNC_CAPTURING, /**< ADC capture in progress */ + MAA_ASYNC_TX_HEADER, /**< Sending reb: header */ + MAA_ASYNC_TX_DATA, /**< Sending red: data packets */ + MAA_ASYNC_NEXT_CHANNEL, /**< Preparing next channel */ + MAA_ASYNC_COMPLETE /**< Sending raa: and finishing */ +} maa_async_state_t; + +/** @brief MAA async context */ +typedef struct { + maa_async_state_t state; /**< Current state */ + uint8_t current_ch; /**< Current channel (0~7) */ + uint8_t current_pkt; /**< Current packet index */ + uint16_t data_offset; /**< Bytes sent so far for current channel */ + uint8_t freq_option; /**< Frequency option */ + uint16_t delay_us; /**< Capture delay */ + uint16_t num_samples; /**< Samples per channel */ + uint8_t cycles; /**< Burst cycles */ + uint16_t averaging; /**< Averaging count */ + uint8_t *ble_buffer; /**< Working buffer for BLE packets */ + dr_maa_channel_t channels[MAA_NUM_CHANNELS]; /**< Captured data for each channel */ + uint16_t total_packets; /**< Total packets for current channel */ + uint16_t data_packets; /**< Data packets for current channel */ +} maa_async_ctx_t; + +/** + * @brief Start async MAA 8-channel capture + * + * Initiates the async state machine. Captures CH0 and begins transmission. + * Subsequent packets are sent when maa_async_on_tx_ready() is called. + * + * @param freq_option Frequency: 0=1.8MHz, 1=2.1MHz, 2=2.0MHz, 3=1.7MHz + * @param delay_us Capture delay (us) + * @param num_samples Samples per channel (1~200) + * @param cycles Burst cycles (3~9) + * @param averaging Averaging count (1~1000) + * @param ble_buffer Working buffer (>= 240 bytes) + * @return dr_adc_err_t DR_ADC_OK if started successfully + */ +dr_adc_err_t maa_async_start(uint8_t freq_option, uint16_t delay_us, + uint16_t num_samples, uint8_t cycles, + uint16_t averaging, uint8_t *ble_buffer); + +/** + * @brief Handle BLE TX ready event + * + * Called from BLE_NUS_EVT_TX_RDY handler. Sends next packet or + * transitions to next state. + * + * @return true if more work pending, false if complete or idle + */ +bool maa_async_on_tx_ready(void); + +/** + * @brief Check if async MAA is active + * @return true if state != IDLE + */ +bool maa_async_is_busy(void); + +/** + * @brief Get current async state (for debugging) + * @return Current state + */ +maa_async_state_t maa_async_get_state(void); + +/** + * @brief Abort async MAA operation + */ +void maa_async_abort(void); + +#endif /* DR_ADC121S051_H */ + diff --git a/project/ble_peripheral/ble_app_bladder_patch/cmd/dr_util.c b/project/ble_peripheral/ble_app_bladder_patch/cmd/dr_util.c new file mode 100644 index 0000000..11317c6 --- /dev/null +++ b/project/ble_peripheral/ble_app_bladder_patch/cmd/dr_util.c @@ -0,0 +1,82 @@ +#include "dr_util.h" +#include "parser.h" + +extern void single_format_data(uint8_t *buffer, const char *tag, uint16_t value); +extern void format_data(uint8_t *buffer, const char *tag, uint16_t *data, uint8_t length); +extern void binary_tx_handler(uint8_t *buffer, uint8_t length); +extern uint8_t ble_bin_buffer[]; + +/* Use dr_binary_tx_safe from main.c - has retry logic for BLE TX queue */ +extern void dr_binary_tx_safe(uint8_t const *ble_bin_buff, uint16_t length); + +void dr_ble_return_1(const char *tag, uint16_t value) +{ + single_format_data(ble_bin_buffer, tag, value); + dr_binary_tx_safe(ble_bin_buffer, 3); /* Use safe TX with retry */ +} + +void dr_ble_return_2(const char *tag, uint16_t v1, uint16_t v2) +{ + ble_bin_buffer[0] = tag[0]; + ble_bin_buffer[1] = tag[1]; + ble_bin_buffer[2] = tag[2]; + ble_bin_buffer[3] = tag[3]; + + ble_bin_buffer[4] = (uint8_t)(v1 >> 8); + ble_bin_buffer[5] = (uint8_t)(v1 & 0xFF); + ble_bin_buffer[6] = (uint8_t)(v2 >> 8); + ble_bin_buffer[7] = (uint8_t)(v2 & 0xFF); + + dr_binary_tx_safe(ble_bin_buffer, 4); /* Use safe TX with retry */ +} + +void dr_ble_return_3(const char *tag, uint16_t v1, uint16_t v2, uint16_t v3) +{ + ble_bin_buffer[0] = tag[0]; + ble_bin_buffer[1] = tag[1]; + ble_bin_buffer[2] = tag[2]; + ble_bin_buffer[3] = tag[3]; + + ble_bin_buffer[4] = (uint8_t)(v1 >> 8); + ble_bin_buffer[5] = (uint8_t)(v1 & 0xFF); + ble_bin_buffer[6] = (uint8_t)(v2 >> 8); + ble_bin_buffer[7] = (uint8_t)(v2 & 0xFF); + ble_bin_buffer[8] = (uint8_t)(v3 >> 8); + ble_bin_buffer[9] = (uint8_t)(v3 & 0xFF); + + dr_binary_tx_safe(ble_bin_buffer, 5); /* Use safe TX with retry */ +} + +void dr_ble_debug(uint16_t point_id, uint16_t value) +{ + /* Use dedicated buffer to avoid conflicts with ble_bin_buffer */ + static uint8_t dbg_buffer[8]; + + dbg_buffer[0] = 'd'; + dbg_buffer[1] = 'b'; + dbg_buffer[2] = 'g'; + dbg_buffer[3] = ':'; + + dbg_buffer[4] = (uint8_t)(point_id >> 8); + dbg_buffer[5] = (uint8_t)(point_id & 0xFF); + dbg_buffer[6] = (uint8_t)(value >> 8); + dbg_buffer[7] = (uint8_t)(value & 0xFF); + + dr_binary_tx_safe(dbg_buffer, 4); +} + +void dr_ble_return_piezo_1(const char *tag, uint16_t value) +{ + /* Use dedicated buffer for piezo responses to avoid conflicts with ble_bin_buffer */ + static uint8_t piezo_buffer[8]; + + piezo_buffer[0] = tag[0]; + piezo_buffer[1] = tag[1]; + piezo_buffer[2] = tag[2]; + piezo_buffer[3] = tag[3]; + + piezo_buffer[4] = (uint8_t)(value >> 8); + piezo_buffer[5] = (uint8_t)(value & 0xFF); + + dr_binary_tx_safe(piezo_buffer, 3); /* 6 bytes = 3 words */ +} diff --git a/project/ble_peripheral/ble_app_bladder_patch/cmd/dr_util.h b/project/ble_peripheral/ble_app_bladder_patch/cmd/dr_util.h new file mode 100644 index 0000000..4cf8078 --- /dev/null +++ b/project/ble_peripheral/ble_app_bladder_patch/cmd/dr_util.h @@ -0,0 +1,18 @@ +#ifndef DR_UTIL_H +#define DR_UTIL_H + +#include + +void dr_ble_return_1(const char *tag, uint16_t value); +void dr_ble_return_2(const char *tag, uint16_t v1, uint16_t v2); +void dr_ble_return_3(const char *tag, uint16_t v1, uint16_t v2, uint16_t v3); +void dr_ble_return_3_be(const char *tag, uint16_t v1, uint16_t v2, uint16_t v3); + +/* Piezo dedicated BLE return - uses separate buffer to avoid conflicts */ +void dr_ble_return_piezo_1(const char *tag, uint16_t value); + +/* BLE debug output - sends "dbg:" + point_id + value */ +void dr_ble_debug(uint16_t point_id, uint16_t value); + +#endif /* DR_UTIL_H */ + diff --git a/project/ble_peripheral/ble_app_bladder_patch/cmd/parser.c b/project/ble_peripheral/ble_app_bladder_patch/cmd/parser.c new file mode 100644 index 0000000..98c6c25 --- /dev/null +++ b/project/ble_peripheral/ble_app_bladder_patch/cmd/parser.c @@ -0,0 +1,1174 @@ +/* + * 2025-12-08 power loop bug fix + * 2025-12-07 msn, mta, mqq + * 2025-12-04 by Charles KWON + * parser.c : Common parser + command table + handlers + * - Firmware/PC shared + * - Hardware-dependent parts left as TODO + * - Added CRC16 validation support + */ + +#include "parser.h" +#include + +#include "nrf_gpio.h" +#include "nrf_delay.h" +#include "dr_piezo.h" +#include "dr_util.h" +#include "dr_adc121s051.h" + + + +// ======================================== +// External function declarations +// ======================================== + +/* Sensor functions */ +extern void battery_level_meas(void); +extern void pressure_all_level_meas(void); +extern void tmp235_voltage_level_meas(void); + + +/* Device control functions */ +extern int device_activated(void); +extern int device_sleep_mode(void); + +/* Error handling */ +extern void param_error(const char *cmd); + +/* BLE transmission */ +extern void single_format_data(uint8_t *buffer, const char *tag, uint16_t value); +extern void binary_tx_handler(const uint8_t *buffer, uint16_t length); +extern void dr_sd_delay_ms(uint32_t ms); /* Softdevice-friendly delay */ + + + + +// ======================================== +// External variables +// ======================================== + +extern volatile bool processing; +extern bool device_status; +extern uint8_t resetCount; +extern uint8_t ble_bin_buffer[]; + +extern uint8_t simple_samples_in_buffer; +extern uint8_t m_pd_adc_cnt; +extern bool con_single; +extern bool lock_check; + + +extern bool info4; // addtional info +extern bool ble_got_new_data; // BLE data flag +extern uint8_t m_pd_adc_cnt; // PD ADC count +extern bool go_batt; // battery +extern bool motion_data_once; // IMU data flag +extern int imu_read_direct(void); // IMU direct register read + BLE send + +extern void pressure_all_level_meas(void); // pressure sensor +extern void battery_timer_stop(void); // battery timer +extern void main_timer_start(void); // main timer + + +extern void dr_piezo_power_on( void ); +extern void dr_piezo_burst_sw(uint8_t cycles); +extern void dr_piezo_burst_sw_18mhz(uint8_t cycles); +extern void dr_piezo_burst_sw_20mhz(uint8_t cycles); +extern void dr_piezo_burst_sw_17mhz(uint8_t cycles); + +/* ---- Global variable definitions (extern in header) ---- */ +dr_platform_if_t g_plat = { 0, 0, 0 }; +bool g_log_enable = false; + +/* ---- Internal constants/structures ---- */ + +#define DR_MAX_DATA 128 /* Max data length after TAG */ + +typedef struct { + char tag[5]; /* "sta?" etc 4 chars + '\0' */ + uint8_t data[DR_MAX_DATA]; /* Raw data after TAG */ + uint8_t data_len; /* Length of data[] */ +} ParsedCmd; + +/* ---- Internal utility functions ---- */ + +/* Copy TAG */ +static void dr_copy_tag(const uint8_t *buf, char *tag_out) +{ + tag_out[0] = (char)buf[0]; + tag_out[1] = (char)buf[1]; + tag_out[2] = (char)buf[2]; + tag_out[3] = (char)buf[3]; + tag_out[4] = '\0'; +} + +/* TAG comparison (4 chars) */ +static bool dr_tag_eq(const char *tag, const char *key4) +{ + return (tag[0] == key4[0] && + tag[1] == key4[1] && + tag[2] == key4[2] && + tag[3] == key4[3]); +} + +/* Extract uint16 little endian: word_index based (0 -> data[0], data[1]) */ +/* Extract uint16 BIG endian: word_index based (0 -> data[0], data[1]) */ +/* Extract uint16 LITTLE endian: word_index based (0 -> data[0], data[1]) */ +static bool dr_get_u16(const ParsedCmd *cmd, uint8_t word_index, uint16_t *out) +{ + uint8_t pos = (uint8_t)(word_index * 2); + if (cmd->data_len < (uint8_t)(pos + 2)) { + return false; + } + + // Little Endian: data[pos] = low byte, data[pos+1] = high byte + *out = (uint16_t)cmd->data[pos] + | (uint16_t)((uint16_t)cmd->data[pos + 1] << 8); + + return true; +} + +/* Extract ASCII: data[offset..offset+len] -> out, '\0' terminated */ +/* EEPROM에서 텍스트 쓸 때 ASCII 문자열 추출 -> 추후 Flash Memory 커맨드에서 재활용 가능 */ +static void dr_get_ascii(const ParsedCmd *cmd, uint8_t offset, + char *out, uint8_t max_len) +{ + uint8_t i; + uint8_t remain; + + if (offset >= cmd->data_len) { + out[0] = '\0'; + return; + } + + remain = (uint8_t)(cmd->data_len - offset); + if (remain > max_len) { + remain = max_len; + } + + for (i = 0; i < remain; i++) { + out[i] = (char)cmd->data[offset + i]; + } + out[remain] = '\0'; +} + +/* ---- CRC16 functions ---- */ + +/* CRC16 computation - matches Nordic SDK crc16_compute */ +static uint16_t dr_crc16_compute(const uint8_t *p_data, uint32_t size, const uint16_t *p_crc) +{ + uint32_t i; + uint16_t crc = (p_crc == NULL) ? 0xFFFF : *p_crc; + + for (i = 0; i < size; i++) + { + crc = (uint8_t)(crc >> 8) | (crc << 8); + crc ^= p_data[i]; + crc ^= (uint8_t)(crc & 0xFF) >> 4; + crc ^= (crc << 8) << 4; + crc ^= ((crc & 0xFF) << 4) << 1; + } + + return crc; +} + +/* CRC16 check: compare computed vs expected */ +static bool dr_crc16_check(const uint8_t *p_data, uint32_t data_len, uint16_t expected_crc) +{ + uint16_t computed_crc = dr_crc16_compute(p_data, data_len, NULL); + return (computed_crc == expected_crc); +} + +/* CRC16 packet check: last 2 bytes are CRC (little endian) */ +static bool dr_crc16_check_packet(const uint8_t *packet, uint32_t packet_len) +{ + uint16_t expected_crc; + uint32_t data_len; + + if (packet_len < 2) { + return false; + } + + data_len = packet_len - 2; + + /* Extract CRC: little endian (low byte first) */ + expected_crc = (uint16_t)packet[packet_len - 2] + | ((uint16_t)packet[packet_len - 1] << 8); + + if (g_plat.log && g_log_enable) { + g_plat.log("CRC check: expected=0x%04X\n", expected_crc); + } + + return dr_crc16_check(packet, data_len, expected_crc); +} + +/* ---- Raw buffer -> ParsedCmd ---- */ + +static bool dr_parse_cmd(const uint8_t *buffer, uint8_t length, ParsedCmd *out) +{ + uint8_t i; + + if (length < 4) { + return false; /* Not even TAG received */ + } + + /* CRC check if enabled */ + if (g_plat.crc_check) { + if (!dr_crc16_check_packet(buffer, length)) { + if (g_plat.log && g_log_enable) { + g_plat.log("CRC check FAILED!\n"); + } + return false; + } + + /* CRC validated - remove CRC bytes from data */ + length = (uint8_t)(length - 2); + + if (g_plat.log && g_log_enable) { + g_plat.log("CRC check OK\n"); + } + } + + dr_copy_tag(buffer, out->tag); + + out->data_len = (length > 4) ? (uint8_t)(length - 4) : 0; + if (out->data_len > DR_MAX_DATA) { + out->data_len = DR_MAX_DATA; + } + + for (i = 0; i < out->data_len; i++) { + out->data[i] = buffer[4 + i]; + } + + if (g_plat.log && g_log_enable) { + g_plat.log("parse_cmd: TAG='%s', data_len=%u\n", + out->tag, out->data_len); + } + + return true; +} + + + +/* ---- Handler prototypes (Harbour style: int return) ---- */ + +/* A. Device Status */ +static int Cmd_mta(const ParsedCmd *cmd); +static int Cmd_sta(const ParsedCmd *cmd); +static int Cmd_str(const ParsedCmd *cmd); + +/* F. PD-ADC M48 Full Measurement Series */ +static int Cmd_mcj(const ParsedCmd *cmd); +static int Cmd_sej(const ParsedCmd *cmd); +static int Cmd_ssj(const ParsedCmd *cmd); + +/* I. Sensor Measurements */ +static int Cmd_msn(const ParsedCmd *cmd); +static int Cmd_spn(const ParsedCmd *cmd); +static int Cmd_sso(const ParsedCmd *cmd); +static int Cmd_ssp(const ParsedCmd *cmd); + +/* J. Power / Reset / Version / Security */ +static int Cmd_ssq(const ParsedCmd *cmd); +static int Cmd_ssr(const ParsedCmd *cmd); +static int Cmd_sss(const ParsedCmd *cmd); +static int Cmd_sst(const ParsedCmd *cmd); +static int Cmd_ssv(const ParsedCmd *cmd); + +static int Cmd_mpa(const ParsedCmd *cmd); +static int Cmd_mpc(const ParsedCmd *cmd); +static int Cmd_mdc(const ParsedCmd *cmd); +static int Cmd_mec(const ParsedCmd *cmd); +static int Cmd_maa(const ParsedCmd *cmd); /* 8-channel all capture */ +static int Cmd_msp(const ParsedCmd *cmd); /* IMU 6-axis raw data (single shot) */ +static int Cmd_cmd(const ParsedCmd *cmd); + +/* ---- Command Table ---- */ + +typedef struct { + char tag[5]; /* "sta?" */ + bool enabled; /* false = handler won't be called */ + int (*handler)(const ParsedCmd *cmd); /* 1=success, 0=fail */ +} CmdEntry; + +static CmdEntry g_cmd_table[] = { + + /* sudo command */ + { "cmd?", true, Cmd_cmd }, // Piezo Activate + + /* Piezo command */ + { "mpa?", true, Cmd_mpa }, // Piezo Activate + { "mpc?", true, Cmd_mpc }, // Piezo Cycles control command, 3,4,5,6,7 + { "mdc?", true, Cmd_mdc }, // Piezo burst + Echo capture (12-bit packed) + { "mec?", true, Cmd_mec }, // Piezo burst + Echo capture (16-bit raw) + { "maa?", true, Cmd_maa }, // 8-channel all capture (mode: 0=raw, 1=delta) + { "msp?", true, Cmd_msp }, // IMU 6-axis raw data (single shot) + + /* A. Device Status */ + { "mta?", true, Cmd_mta }, + { "sta?", true, Cmd_sta }, + { "str?", false, Cmd_str }, + /* F. PD-ADC M48 Full Measurement Series */ + { "mcj?", true, Cmd_mcj }, + { "scj?", true, Cmd_mcj }, + + { "sej?", false, Cmd_sej }, + { "ssj?", false, Cmd_ssj }, + /* I. Sensor Measurements */ + { "msn?", true, Cmd_msn }, + { "ssn?", true, Cmd_msn }, // snn compatible command for battery check + + { "spn?", false, Cmd_spn }, + { "sso?", false, Cmd_sso }, + { "ssp?", false, Cmd_ssp }, + + /* J. Power / Reset / Version / Security */ + { "ssq?", false, Cmd_ssq }, + { "ssr?", false, Cmd_ssr }, + { "sss?", false, Cmd_sss }, + { "sst?", false, Cmd_sst }, + { "ssv?", false, Cmd_ssv }, + +}; + +static const uint16_t g_cmd_count = + (uint16_t)(sizeof(g_cmd_table) / sizeof(g_cmd_table[0])); + +/* ---- Command dispatcher ---- */ +static int dr_cmd_dispatch(const ParsedCmd *cmd) +{ + uint16_t i; + char tag_lower[5]; + + /* tag command convert to lower case */ + for (i = 0; i < 4 && cmd->tag[i]; i++) { + tag_lower[i] = (cmd->tag[i] >= 'A' && cmd->tag[i] <= 'Z') + ? (cmd->tag[i] + 32) : cmd->tag[i]; + } + tag_lower[i] = '\0'; + + for (i = 0; i < g_cmd_count; i++) { + if (dr_tag_eq(tag_lower, g_cmd_table[i].tag)) { + + if (!g_cmd_table[i].enabled) { + if (g_plat.log && g_log_enable) { + g_plat.log("Command '%s' disabled\n", cmd->tag); + } + return 0; + } + + if (g_plat.log && g_log_enable) { + g_plat.log("Run handler '%s'\n", cmd->tag); + } + + return g_cmd_table[i].handler(cmd); + } + } + + if (g_plat.log && g_log_enable) { + g_plat.log("Unknown TAG '%s'\n", cmd->tag); + } + return 0; +} + +/* +Main Parser called from external code +*/ + +int dr_cmd_parser(const uint8_t *buf, uint8_t len) +{ + ParsedCmd cmd; + + if (!dr_parse_cmd(buf, len, &cmd)) { + if (g_plat.log && g_log_enable) { + g_plat.log("parse_cmd failed (len=%u)\n", len); + } + + /* CRC 실패 시 에러 응답 전송 */ + if (g_plat.crc_check && g_plat.tx_bin) { + single_format_data(ble_bin_buffer, "crc!", 65530); + binary_tx_handler(ble_bin_buffer, 3); + } + return 9; /* CRC 실패 또는 파싱 실패 */ + } + + return dr_cmd_dispatch(&cmd); + + + + +} + +/* ---- Each Command Handler implementation (Stub) ---- */ +/* In actual firmware, replace TODO sections with hardware/EEPROM integration */ + +/* A. Device Status */ +static int Cmd_mta(const ParsedCmd *cmd) +{ + uint16_t mode = 0; + + // Reset count (sta? replacement ) + resetCount = 0; + + // Extract mode + (void)dr_get_u16(cmd, 0, &mode); + + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_mta] mode=%u\r\n", mode); + } + + // Process mode ( mta?1 ) + if (mode == 1) { + if (device_activated() == 0) { + device_status = true; + } + } + else if (mode == 0) { // mta?0 + if (device_status == true) { + if (device_sleep_mode() == 0) { + device_status = false; + } + } + } + + if (g_plat.tx_bin) { + single_format_data(ble_bin_buffer, "rta:", mode); + binary_tx_handler(ble_bin_buffer, 3); + } + + return 1; +} + + +/* ---- Each Command Handler implementation (Stub) ---- */ +/* In actual firmware, replace TODO sections with hardware/EEPROM integration */ + +/* A. Device Status */ +static int Cmd_sta(const ParsedCmd *cmd) +{ + uint16_t mode = 0; + + // Reset count (sta? replacement ) + resetCount = 0; + + // Extract mode + (void)dr_get_u16(cmd, 0, &mode); + + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_mta] mode=%u\r\n", mode); + } + + // Process mode ( mta?1 ) + if (mode == 1) { + if (device_activated() == 0) { + device_status = true; + } + } + else if (mode == 0) { // mta?0 + if (device_status == true) { + if (device_sleep_mode() == 0) { + device_status = false; + } + } + } + + if (g_plat.tx_bin) { + single_format_data(ble_bin_buffer, "sta:", mode); + binary_tx_handler(ble_bin_buffer, 3); + } + + return 1; +} + + +static int Cmd_str(const ParsedCmd *cmd) +{ + (void)cmd; + /* TODO: read actual device_status */ + uint8_t status = 1; + + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_str] read status=%u\n", status); + } + + if (g_plat.tx_bin) { + uint8_t resp[4] = { 'r','t','r', status }; + g_plat.tx_bin(resp, 4); + } + return 1; +} + + +/** + * @brief PD-ADC M48 Full Measurement - MODE 2 (scj? ? mcj?) + * + * Original: scj? + * New: mcj? + * Response: rcj: (from m48 measurement callback) + * + * MODE 2: Pressure + M48 Full Measurement + * - Pressure sensor measurement (pressure1 + pressure2) + * - 48 LED-PD ADC measurement + * - Battery, Temperature, IMU data + * + * Preconditions: + * - Device must be activated (device_status == true) + * - Not currently processing + */ +static int Cmd_mcj(const ParsedCmd *cmd) +{ + (void)cmd; + + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_mcj] PD-ADC M48 MODE=2 (Press + M48)\r\n"); + } + + /* Check device activation status */ + if (device_status != true) { + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_mcj] ERROR: Device not activated\r\n"); + } + + if (g_plat.tx_bin) { + param_error("mcj?"); + } + return 1; + } + + info4 = true; + ble_got_new_data = false; + processing = true; + + /* Start pressure measurement */ + pressure_all_level_meas(); + + battery_timer_stop(); + + /* Enable battery, temperature, IMU measurement */ + go_batt = true; + motion_data_once = true; + + /* Start main timer */ + main_timer_start(); + + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_mcj] Measurement started\r\n"); + } + + return 1; +} + +static int Cmd_sej(const ParsedCmd *cmd) +{ + (void)cmd; + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_sej] MODE=4 (M48 + batt + IMU)\n"); + } + return 1; +} + +static int Cmd_ssj(const ParsedCmd *cmd) +{ + (void)cmd; + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_ssj] MODE=0 (M48 + batt/IMU combined)\n"); + } + return 1; +} + +/* I. Sensor Measurements */ +static int Cmd_msn(const ParsedCmd *cmd) +{ + (void)cmd; + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_msn] Measure battery level\n"); + } + battery_level_meas(); + return 1; +} + + + + +static int Cmd_spn(const ParsedCmd *cmd) +{ + (void)cmd; + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_spn] Measure pressure1 & 2\n"); + } + return 1; +} + +static int Cmd_sso(const ParsedCmd *cmd) +{ + (void)cmd; + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_sso] Measure LED temperature\n"); + } + return 1; +} + +static int Cmd_ssp(const ParsedCmd *cmd) +{ + char mode = '1'; + if (cmd->data_len > 0) { + mode = (char)cmd->data[0]; + } + + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_ssp] Motion sensor raw, mode='%c'\n", mode); + } + return 1; +} + +/* J. Power / Reset / Version / Security */ +static int Cmd_ssq(const ParsedCmd *cmd) +{ + (void)cmd; + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_ssq] Power off\n"); + } + return 1; +} + +static int Cmd_ssr(const ParsedCmd *cmd) +{ + (void)cmd; + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_ssr] Bond delete\n"); + } + return 1; +} + +static int Cmd_sss(const ParsedCmd *cmd) +{ + (void)cmd; + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_sss] Device reset\n"); + } + return 1; +} + +static int Cmd_sst(const ParsedCmd *cmd) +{ + (void)cmd; + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_sst] Ready\n"); + } + return 1; +} + +static int Cmd_ssv(const ParsedCmd *cmd) +{ + (void)cmd; + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_ssv] Read firmware version\n"); + } + return 1; +} + +static int Cmd_mpa(const ParsedCmd *cmd) +{ + (void)cmd; + + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_mpa] Piezo Activation\n"); + } + + dr_piezo_power_on(); + dr_piezo_system_init(); + + if (g_plat.tx_bin) { + single_format_data(ble_bin_buffer, "rpa:", 1); + binary_tx_handler(ble_bin_buffer, 3); + } + + return 1; +} + + +/** + * @brief Piezo burst command with frequency option + * @param cmd->data[0-1]: cycles (3~9), default=5 + * @param cmd->data[2-3]: freq_option (0=1.8MHz, 1=2.1MHz, 2=2.0MHz, 3=1.7MHz), default=1 + * @return Response: rpc:cycles + * + * Usage from Harbour: + * mpc?[cycles][freq] where freq: 0=1.8MHz, 1=2.1MHz (default), 2=2.0MHz, 3=1.7MHz + * Example: mpc?0x05,0x00,0x01,0x00 -> 5 cycles at 2.1MHz + * Example: mpc?0x05,0x00,0x00,0x00 -> 5 cycles at 1.8MHz + * Example: mpc?0x05,0x00,0x02,0x00 -> 5 cycles at 2.0MHz + * Example: mpc?0x05,0x00,0x03,0x00 -> 5 cycles at 1.7MHz + */ +static int Cmd_mpc(const ParsedCmd *cmd) +{ + uint16_t cycles = 5; /* default */ + uint16_t freq_option = 1; /* 0=1.8MHz, 1=2.1MHz, 2=2.0MHz, 3=1.7MHz */ + uint16_t piezo_ch = 0; /* Piezo channel 0~7, default 0 */ + + /* Extract cycles (word 0) */ + (void)dr_get_u16(cmd, 0, &cycles); + + /* Extract frequency option (word 1) */ + (void)dr_get_u16(cmd, 1, &freq_option); + + /* Extract piezo channel (word 2) */ + (void)dr_get_u16(cmd, 2, &piezo_ch); + + /* Validate piezo channel: 0~7 jhChun 26.01.29 */ + if (piezo_ch > 7) piezo_ch = 0; + + if (g_plat.log && g_log_enable) { + const char *freq_str = (freq_option == 0) ? "1.8MHz" : + (freq_option == 1) ? "2.1MHz" : + (freq_option == 2) ? "2.0MHz" : "1.7MHz"; + g_plat.log("[Cmd_mpc] cycles=%u, freq=%u (%s), piezo=%u\r\n", + cycles, freq_option, freq_str, piezo_ch); + } + + /* Range check: 3~9 */ + if (cycles < 3 || cycles > 9) { + dr_ble_return_1("rpc:", 2); /* Error: out of range */ + return 1; + } + + /* Select piezo channel */ + dr_piezo_select_channel((uint8_t)piezo_ch); + + /* Execute burst based on frequency option */ + switch (freq_option) { + case 0: + dr_piezo_burst_sw_18mhz((uint8_t)cycles); + break; + case 2: + dr_piezo_burst_sw_20mhz((uint8_t)cycles); + break; + case 3: + dr_piezo_burst_sw_17mhz((uint8_t)cycles); + break; + case 1: + default: + dr_piezo_burst_sw((uint8_t)cycles); /* 2.1MHz */ + break; + } + + /* Response */ + dr_ble_return_1("rpc:", (uint8_t)cycles); + + return 1; +} + + +/** + * @brief Piezo burst + Echo capture command + * @param cmd->data[0]: cycles (3~9), default=5 + * @param cmd->data[1-2]: delay_us (0~65535), default=1000 + * @param cmd->data[3-4]: num_samples (1~8192), default=4096 + * @return Multiple packets with 12-bit packed data + * + * Response format (multi-packet, 12-bit packed): + * + * First packet ("rdc:"): + * [0-3]: "rdc:" + * [4-5]: peak_raw (uint16_t, little-endian) + * [6-7]: peak_index (uint16_t) + * [8-9]: baseline_raw (uint16_t) + * [10-11]: num_samples (uint16_t) + * [12-13]: total_packets (uint16_t) + * [14...]: 12-bit packed ADC data (first chunk) + * + * Continuation packets ("rdd:"): + * [0-3]: "rdd:" + * [4-5]: packet_index (uint16_t, 1-based) + * [6...]: 12-bit packed ADC data (continuation) + * + * Last packet marker ("rde:"): + * [0-3]: "rde:" + * [4-5]: total_bytes_sent (uint16_t) + * + * 12-bit packing: 2 samples (24 bits) -> 3 bytes + * Byte0 = sample0[7:0] + * Byte1 = sample1[3:0] << 4 | sample0[11:8] + * Byte2 = sample1[11:4] + */ +#define MDC_BLE_MTU_SIZE 240 /* Safe BLE packet size */ +#define MDC_FIRST_HEADER_LEN 14 /* "rdc:" + header */ +#define MDC_CONT_HEADER_LEN 6 /* "rdd:" + packet_index */ +#define MDC_FIRST_DATA_LEN (MDC_BLE_MTU_SIZE - MDC_FIRST_HEADER_LEN) +#define MDC_CONT_DATA_LEN (MDC_BLE_MTU_SIZE - MDC_CONT_HEADER_LEN) + +/** + * @brief Piezo burst + Echo capture command (12-bit packed) + * + * PROTOCOL v3: Small header packet + separate data packets with delays + * This avoids large packet loss in BLE stack. + * + * Response format: + * Packet 1 (rdb:): header only (14 bytes) + * Packet 2~N (rdd:): pkt_idx(2) + packed_data (up to 234 bytes) + * Final (rde:): total_packets(2) + */ +static int Cmd_mdc(const ParsedCmd *cmd) +{ + uint16_t cycles = 5; + uint16_t delay_us = 1000; + uint16_t num_samples = 4096; + + /* Extract parameters */ + (void)dr_get_u16(cmd, 0, &cycles); + (void)dr_get_u16(cmd, 1, &delay_us); + (void)dr_get_u16(cmd, 2, &num_samples); + + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_mdc] cycles=%u, delay=%uus, samples=%u\r\n", + cycles, delay_us, num_samples); + } + + /* Range check */ + if (cycles < 3) cycles = 3; + if (cycles > 9) cycles = 9; + if (num_samples > 8192) num_samples = 8192; + if (num_samples < 1) num_samples = 1; + + /* Execute burst + echo capture */ + dr_adc_echo_t echo; + dr_adc_err_t err = dr_adc_burst_and_capture( + (uint8_t)cycles, delay_us, num_samples, &echo); + + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_mdc] err=%d, peak=%u@%u, base=%u, n=%u\r\n", + err, echo.peak_raw, echo.peak_index, echo.baseline_raw, echo.num_samples); + } + + if (g_plat.tx_bin) { + const uint16_t *raw_buffer = dr_adc_get_echo_buffer(); + + /* Calculate packed data size: 2 samples -> 3 bytes */ + uint16_t packed_data_bytes = ((echo.num_samples + 1) / 2) * 3; + uint16_t data_per_packet = MDC_BLE_MTU_SIZE - MDC_CONT_HEADER_LEN; /* 234 bytes */ + uint16_t data_packets = (packed_data_bytes + data_per_packet - 1) / data_per_packet; + if (data_packets == 0) data_packets = 1; + uint16_t total_packets = 1 + data_packets; /* header + data packets */ + + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_mdc] packed=%u bytes, packets=%u\r\n", packed_data_bytes, total_packets); + } + + /* Packet 1: rdb: header only (14 bytes) */ + ble_bin_buffer[0] = 'r'; + ble_bin_buffer[1] = 'd'; + ble_bin_buffer[2] = 'b'; + ble_bin_buffer[3] = ':'; + ble_bin_buffer[4] = (uint8_t)(total_packets & 0xFF); + ble_bin_buffer[5] = (uint8_t)(total_packets >> 8); + ble_bin_buffer[6] = (uint8_t)(echo.peak_raw & 0xFF); + ble_bin_buffer[7] = (uint8_t)(echo.peak_raw >> 8); + ble_bin_buffer[8] = (uint8_t)(echo.peak_index & 0xFF); + ble_bin_buffer[9] = (uint8_t)(echo.peak_index >> 8); + ble_bin_buffer[10] = (uint8_t)(echo.baseline_raw & 0xFF); + ble_bin_buffer[11] = (uint8_t)(echo.baseline_raw >> 8); + ble_bin_buffer[12] = (uint8_t)(echo.num_samples & 0xFF); + ble_bin_buffer[13] = (uint8_t)(echo.num_samples >> 8); + binary_tx_handler(ble_bin_buffer, 7); /* 14 bytes = 7 words */ + nrf_delay_ms(100); /* Wait for BLE stack */ + + /* Packet 2~N: rdd: data packets with packed 12-bit samples */ + uint16_t src_idx = 0; + for (uint16_t pkt = 0; pkt < data_packets; pkt++) { + ble_bin_buffer[0] = 'r'; + ble_bin_buffer[1] = 'd'; + ble_bin_buffer[2] = 'd'; + ble_bin_buffer[3] = ':'; + ble_bin_buffer[4] = (uint8_t)(pkt & 0xFF); + ble_bin_buffer[5] = (uint8_t)(pkt >> 8); + + uint16_t dst_idx = 6; + uint16_t bytes_this_pkt = 0; + while (src_idx < echo.num_samples && bytes_this_pkt < data_per_packet) { + uint16_t s0 = raw_buffer[src_idx++] & 0x0FFF; + uint16_t s1 = (src_idx < echo.num_samples) ? (raw_buffer[src_idx++] & 0x0FFF) : 0; + + ble_bin_buffer[dst_idx++] = (uint8_t)(s0 & 0xFF); + ble_bin_buffer[dst_idx++] = (uint8_t)(((s1 & 0x0F) << 4) | ((s0 >> 8) & 0x0F)); + ble_bin_buffer[dst_idx++] = (uint8_t)(s1 >> 4); + bytes_this_pkt += 3; + } + + /* Ensure even byte count for word alignment */ + if (dst_idx & 1) ble_bin_buffer[dst_idx++] = 0; + binary_tx_handler(ble_bin_buffer, dst_idx / 2); /* bytes to words */ + nrf_delay_ms(100); /* Inter-packet delay */ + } + + /* Final packet: rde: end marker */ + ble_bin_buffer[0] = 'r'; + ble_bin_buffer[1] = 'd'; + ble_bin_buffer[2] = 'e'; + ble_bin_buffer[3] = ':'; + ble_bin_buffer[4] = (uint8_t)(total_packets & 0xFF); + ble_bin_buffer[5] = (uint8_t)(total_packets >> 8); + binary_tx_handler(ble_bin_buffer, 3); /* 6 bytes = 3 words */ + + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_mdc] sent rdb+rdd*%u+rde (%u samples)\r\n", + total_packets, echo.num_samples); + } + } + + return 1; +} + + +/* + +// The following code demonstrates an example of how to receive data from a client +// and echo the same data back to the client. + + + +static int Cmd_cmd(const ParsedCmd *cmd) +{ + uint16_t v1, v2, v3; + + if (cmd->data_len < 6) { + dr_ble_return_1("rmd:", 0); + return 1; + } + + // Little Endian ?? (PC? LE? ??) + v1 = (uint16_t)cmd->data[0] | ((uint16_t)cmd->data[1] << 8); + v2 = (uint16_t)cmd->data[2] | ((uint16_t)cmd->data[3] << 8); + v3 = (uint16_t)cmd->data[4] | ((uint16_t)cmd->data[5] << 8); + + dr_ble_return_3("rmd:", v1, v2, v3); + + return 1; +} + +*/ + +/** + * @brief Piezo burst + Echo capture with 16-bit raw data (no compression) + * @param cmd->data[0]: cycles (3~9), default=5 + * @param cmd->data[1-2]: delay_us (0~65535), default=100 + * @param cmd->data[3-4]: num_samples (1~200), default=140 (20cm target) + * @return Multiple packets with 16-bit raw data + * + * Response format (multi-packet, 16-bit raw - no compression): + * + * First packet ("rec:"): + * [0-3]: "rec:" + * [4-5]: peak_raw (uint16_t, little-endian) + * [6-7]: peak_index (uint16_t) + * [8-9]: baseline_raw (uint16_t) + * [10-11]: num_samples (uint16_t) + * [12-13]: total_packets (uint16_t) + * [14...]: 16-bit raw ADC data (first chunk, little-endian) + * + * Continuation packets ("red:"): + * [0-3]: "red:" + * [4-5]: packet_index (uint16_t, 1-based) + * [6...]: 16-bit raw ADC data (continuation) + * + * Last packet marker ("ree:"): + * [0-3]: "ree:" + * [4-5]: total_bytes_sent (uint16_t) + * + * 16-bit raw format: Each sample = 2 bytes (uint16, little-endian) + * Byte0 = Sample[7:0] + * Byte1 = Sample[15:8] + * + * Example: 140 samples (20cm) = 280 bytes = 2 packets + */ +static int Cmd_mec(const ParsedCmd *cmd) +{ + uint16_t freq_option = 0; /* 0=1.8MHz (default), 1=2.1MHz, 2=2.0MHz, 3=1.7MHz */ + uint16_t delay_us = 20; /* Default 20us */ + uint16_t num_samples = 140; /* Default for 20cm target */ + uint16_t cycles = 5; /* Default 5 cycles (valid: 3~7) */ + uint16_t averaging = 1; /* Default 1 (no averaging), max 1000 */ + uint16_t piezo_ch = 0; /* Default piezo channel 0 (valid: 0~7) */ + + /* Extract parameters: mec [freq_option] [delay_us] [num_samples] [cycles] [averaging] [piezo_ch] */ + (void)dr_get_u16(cmd, 0, &freq_option); + (void)dr_get_u16(cmd, 1, &delay_us); + (void)dr_get_u16(cmd, 2, &num_samples); + (void)dr_get_u16(cmd, 3, &cycles); + (void)dr_get_u16(cmd, 4, &averaging); + (void)dr_get_u16(cmd, 5, &piezo_ch); + + /* Validate averaging: 1~1000, default 1 */ + if (averaging == 0) averaging = 1; + if (averaging > 1000) averaging = 1000; + + /* Validate piezo channel: 0~7 jhChun 26.01.29 */ + if (piezo_ch > 7) piezo_ch = 0; + + if (g_plat.log && g_log_enable) { + const char *freq_str = (freq_option == 0) ? "1.8MHz" : + (freq_option == 1) ? "2.1MHz" : + (freq_option == 2) ? "2.0MHz" : "1.7MHz"; + g_plat.log("[Cmd_mec] freq=%u (%s), delay=%uus, samples=%u, cycles=%u, avg=%u, piezo=%u\r\n", + freq_option, freq_str, delay_us, num_samples, cycles, averaging, piezo_ch); + } + + /* Use integrated burst + capture + transmit function + * This function handles: + * 1. ADC power on + * 2. Select piezo channel (0~7) + * 3. Piezo burst (frequency based on freq_option, cycles from param) + * 4. ADC capture (after delay_us) - repeated 'averaging' times + * 5. Average the captured samples + * 6. BLE transmission with proper packet timing + */ + dr_adc_err_t err = dr_adc_burst_capture_transmit( + (uint8_t)freq_option, delay_us, num_samples, (uint8_t)cycles, + (uint16_t)averaging, (uint8_t)piezo_ch, ble_bin_buffer, 0); /* 0=send raa */ + + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_mec] result=%d\r\n", err); + } + + return 1; +} + + +static int Cmd_cmd(const ParsedCmd *cmd) +{ + uint16_t v1, v2, v3; + uint32_t pin_number; + + if (cmd->data_len < 6) { + dr_ble_return_1("rmd:", 0); + return 1; + } + + // Little Endian from PC to LE + v1 = (uint16_t)cmd->data[0] | ((uint16_t)cmd->data[1] << 8); // port + v2 = (uint16_t)cmd->data[2] | ((uint16_t)cmd->data[3] << 8); // pin + v3 = (uint16_t)cmd->data[4] | ((uint16_t)cmd->data[5] << 8); // 1=HIGH, 0=LOW + + // -- GPIO Test + + // Pin No: NRF_GPIO_PIN_MAP(port, pin) + pin_number = NRF_GPIO_PIN_MAP(v1, v2); + + // output + nrf_gpio_cfg_output(pin_number); + + // HIGH or LOW + if (v3 == 1) { + nrf_gpio_pin_set(pin_number); // HIGH + } else { + nrf_gpio_pin_clear(pin_number); // LOW + } + + // return : port, pin, state + dr_ble_return_3("rmd:", v1, v2, v3); + + return 1; +} + + +/** + * @brief 4-Channel All Capture Command (maa?) - ASYNC VERSION + * @param cmd->data[0-1]: mode (0=raw 16-bit async) + * @return Immediately. Data sent asynchronously via BLE_NUS_EVT_TX_RDY + * + * ASYNC ARCHITECTURE: + * - maa_async_start() initiates capture and sends CH0 header + * - BLE_NUS_EVT_TX_RDY callback drives remaining transmission + * - No blocking - SoftDevice can process events normally + * - Prevents BLE TX buffer overflow that caused firmware brick + * + * Hardcoded parameters: + * freq_option = 0 (1.8MHz) + * delay_us = 10 + * num_samples = 140 + * cycles = 7 + * averaging = 5 + * + * Response format: + * For each channel (CH0~CH3): + * reb: [total_pkts(2)] [peak(2)] [idx(2)] [baseline(2)] [samples(2)] + * red: [pkt_idx(2)] [data...] + * Final: + * raa: [status(2)] + * + * Version marker: 0xA000 (vA) = async 8-channel + */ +#define MAA_FREQ_OPTION 0 /* 1.8MHz */ +#define MAA_DELAY_US 10 /* 10us post-burst delay */ +#define MAA_NUM_SAMPLES 140 /* 140 samples (~25cm) */ +#define MAA_CYCLES 7 /* 7 cycles burst */ +#define MAA_AVERAGING 5 /* 5x averaging */ + +static int Cmd_maa(const ParsedCmd *cmd) +{ + uint16_t mode = 0; + dr_adc_err_t err; + + /* Extract mode parameter */ + (void)dr_get_u16(cmd, 0, &mode); + + /* Mode validation - only mode 0 (async raw) supported */ + if (mode > 0) { + dr_ble_return_1("raa:", 0xFFFF); + return 1; + } + + /* Check if already busy */ + if (maa_async_is_busy()) { + dr_ble_return_1("raa:", 0xFFFE); /* Busy */ + return 1; + } + + /*======================================================================= + * ASYNC 4-CHANNEL CAPTURE + * - maa_async_start() captures CH0 and sends first header + * - Subsequent packets sent via BLE_NUS_EVT_TX_RDY callback + * - No blocking delays - SoftDevice can process events normally + *=======================================================================*/ + err = maa_async_start( + (uint8_t)MAA_FREQ_OPTION, + MAA_DELAY_US, + MAA_NUM_SAMPLES, + (uint8_t)MAA_CYCLES, + (uint16_t)MAA_AVERAGING, + ble_bin_buffer + ); + + if (err != DR_ADC_OK) { + /* Start failed - error already sent by maa_async_start */ + return 1; + } + + /* Return immediately - async transmission in progress */ + /* raa: will be sent by state machine when complete */ + return 1; +} + + +/* msp? - Read IMU accel(xyz) + gyro(xyz) raw data, single shot + * Response: rsp: + 6 x uint16_t (accel_x, accel_y, accel_z, gyro_x, gyro_y, gyro_z) + */ +static int Cmd_msp(const ParsedCmd *cmd) +{ + (void)cmd; + + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_msp] ENTER\r\n"); + } + + hw_i2c_init_once(); + + /* Direct register read — no timer, no DRDY, no callback */ + int rc = imu_read_direct(); + + if (g_plat.log && g_log_enable) { + g_plat.log("[Cmd_msp] imu_read_direct rc=%d\r\n", rc); + } + + return 1; +} diff --git a/project/ble_peripheral/ble_app_bladder_patch/cmd/parser.h b/project/ble_peripheral/ble_app_bladder_patch/cmd/parser.h new file mode 100644 index 0000000..565fe12 --- /dev/null +++ b/project/ble_peripheral/ble_app_bladder_patch/cmd/parser.h @@ -0,0 +1,24 @@ +/* parser.h */ +#ifndef PARSER_H +#define PARSER_H + +#include +#include + +/* Platform-dependent function pointer set */ +typedef struct { + void (*log)(const char *fmt, ...); + void (*tx_bin)(const uint8_t *buf, uint16_t len); + bool crc_check; +} dr_platform_if_t; + + +/* Global interface & log flag */ +extern dr_platform_if_t g_plat; +extern bool g_log_enable; + +/* Main parser entry point */ +int dr_cmd_parser(const uint8_t *buf, uint8_t len); + +#endif /* PARSER_H */ + diff --git a/project/ble_peripheral/ble_app_bladder_patch/cmd_parse.c b/project/ble_peripheral/ble_app_bladder_patch/cmd_parse.c index e0dc624..f5d248e 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/cmd_parse.c +++ b/project/ble_peripheral/ble_app_bladder_patch/cmd_parse.c @@ -12,6 +12,7 @@ v1.17 p1,p2 parssurer #include #include "debug_print.h" +#include "fstorage.h" #define DEVICE_VERSION "FW25LIT2B102" #define DEVICE_NAME "MEDIDEV_2004" @@ -1452,7 +1453,7 @@ else if((scmd.tag[1] == 'r') && (scmd.tag[2] == 'k')){ // Set the number of } // ssl -else if((scmd.tag[1] == 's') && (scmd.tag[2] == 'l')){ // Set the delay for PD stabilization. 0s ~ FFFF(65535)s +else if((scmd.tag[1] == 's') && (scmd.tag[2] == 'l')){ // Set the delay for PD stabilization. 0�s ~ FFFF(65535)�s // ret_code_t err_code; @@ -1485,7 +1486,7 @@ else if((scmd.tag[1] == 's') && (scmd.tag[2] == 'l')){ // Set the delay for } } // srl -else if((scmd.tag[1] == 'r') && (scmd.tag[2] == 'l')){ // Set the delay for PD stabilization. 0s ~ FFFF(65535)s +else if((scmd.tag[1] == 'r') && (scmd.tag[2] == 'l')){ // Set the delay for PD stabilization. 0�s ~ FFFF(65535)�s @@ -1655,69 +1656,30 @@ else if((scmd.tag[1] == 's') && (scmd.tag[2] == 'v')){ // Auto Gain Control } -// ssz -else if((scmd.tag[1] == 's') && (scmd.tag[2] == 'z')){ //Write, Serial Number +// ssz - Write Serial Number (FDS only) +else if((scmd.tag[1] == 's') && (scmd.tag[2] == 'z')){ if(length_error(scmd.tag,18,length)==false) { return; } - uint8_t tx_data[EEPROM_PAGE_SIZE]; - uint8_t rx_data[EEPROM_PAGE_SIZE]; - uint8_t raw_data[EEPROM_PAGE_SIZE]; if(cmd_t == CMD_UART) { DBG_PRINTF("Tz0\r\n\r\n"); } else if(cmd_t == CMD_BLE) { - -//eeprom_write_encrypted(0x0000, secret_data, strlen((char *)secret_data)); - -//eeprom_read_decrypted(0x0000, decrypted, strlen((char *)secret_data)); -//DBG_PRINTF("Decrypted: %s\n", decrypted); - memcpy(SERIAL_NO, scmd.value_ascii, 12); - //eeprom_write_bytes(uint16_t mem_address, const uint8_t *data, size_t length) - for (uint8_t i=0 ; i<12 ;i++) - { - tx_data[i] = (uint8_t)(scmd.value_ascii[i]); - } - -// if (eeprom_write_bytes(0x0030 , tx_data ,12) !=NRF_SUCCESS) -// { -// DBG_PRINTF("ERR!!! EEP_Serial_number 12\r\n\r\n"); -// } - if(eeprom_write_encrypted(0x0030, tx_data, 12)!= NRF_SUCCESS) - { - DBG_PRINTF("ERR!!! EEP_Serial_number 12\r\n\r\n");; - } - - if(eeprom_read_bytes(0x0030, raw_data, 12)!= NRF_SUCCESS) - { - DBG_PRINTF("ERR!!! EEP_Serial_number 12\r\n\r\n");; - } - - nrf_delay_ms(10); - DBG_PRINTF("encrypted: %s\n", raw_data); - if(eeprom_read_decrypted(0x0030, rx_data, 12)!= NRF_SUCCESS) - { - DBG_PRINTF("ERR!!! EEP_Serial_number 12\r\n\r\n");; - } - DBG_PRINTF("Decrypted: %s\n", rx_data); - + memcpy(m_config.serial_no, scmd.value_ascii, 12); + config_save(); + DBG_PRINTF("[ssz] S/N=%s saved to FDS\r\n", m_config.serial_no); ascii_format_data(ble_bin_buffer, "rsz:", scmd.value_ascii,12); - binary_tx_handler(ble_bin_buffer,8); + binary_tx_handler(ble_bin_buffer,8); } - - - else{ DBG_PRINTF("ERR!!! Serial_number 12\r\n\r\n"); if(cmd_t == CMD_UART) { DBG_PRINTF("Tz0FF\r\n\r\n"); - } else if(cmd_t == CMD_BLE) { + } else if(cmd_t == CMD_BLE) { param_error(scmd.tag ); } - } - } // spz else if((scmd.tag[1] == 'p') && (scmd.tag[2] == 'z')){ //Write, passkey @@ -1808,33 +1770,14 @@ else if((scmd.tag[1] == 'q') && (scmd.tag[2] == 'z')){ // Read, Seri } } -// srz -else if((scmd.tag[1] == 'r') && (scmd.tag[2] == 'z')){ // Read, Serial Number - - uint8_t rx_data[EEPROM_PAGE_SIZE]; - +// srz - Read Serial Number (FDS only) +else if((scmd.tag[1] == 'r') && (scmd.tag[2] == 'z')){ if(cmd_t == CMD_UART) { DBG_PRINTF("Tz1,%s\r\n\r\n", SERIAL_NO); - } else if(cmd_t == CMD_BLE) { - - - if(eeprom_read_decrypted(0x0030, rx_data, 12)!= NRF_SUCCESS) - { - DBG_PRINTF("ERR!!! EEP_Serial_number 12\r\n\r\n");; - } - DBG_PRINTF("Decrypted: %s\n", rx_data); -// if (eeprom_read_bytes(0x0030 , rx_data ,12) !=NRF_SUCCESS) -// { -// DBG_PRINTF("ERR!!! EEP_Serial_number 12\r\n\r\n"); -// } -// sprintf(ble_tx_buffer, "Tz1,%s\r\n", m_config.serial_number); -// data_tx_handler(ble_tx_buffer); - for (uint8_t i=0 ; i<12 ;i++) - { - (SERIAL_NO[i]) = (char)rx_data[i] ; - } - ascii_format_data(ble_bin_buffer, "rrz:", SERIAL_NO,12); - binary_tx_handler(ble_bin_buffer,8); + } else if(cmd_t == CMD_BLE) { + memcpy(SERIAL_NO, m_config.serial_no, 12); + ascii_format_data(ble_bin_buffer, "rrz:", SERIAL_NO, 12); + binary_tx_handler(ble_bin_buffer, 8); } #if 0 // ===== EEPROM start block ===== @@ -2024,99 +1967,43 @@ else if((scmd.tag[1] == 'g') && (scmd.tag[2] == 'z')){ // Read, DCP } } -// siz -else if((scmd.tag[1] == 'i') && (scmd.tag[2] == 'z')){ // Read, HW - - hw_i2c_init_once(); - uint8_t rx_data[EEPROM_PAGE_SIZE]; - +// siz - Read HW Number (FDS only) +else if((scmd.tag[1] == 'i') && (scmd.tag[2] == 'z')){ if(cmd_t == CMD_UART) { DBG_PRINTF("Tz1,%s\r\n\r\n", HW_NO); - } else if(cmd_t == CMD_BLE) { - - - if(eeprom_read_decrypted(0x0010, rx_data, 12)!= NRF_SUCCESS) - { - DBG_PRINTF("ERR!!! EEP_Serial_number 12\r\n\r\n");; - } - DBG_PRINTF("Decrypted: %s\n", rx_data); -// if (eeprom_read_bytes(0x0030 , rx_data ,12) !=NRF_SUCCESS) -// { -// DBG_PRINTF("ERR!!! EEP_Serial_number 12\r\n\r\n"); -// } -// sprintf(ble_tx_buffer, "Tz1,%s\r\n", m_config.serial_number); -// data_tx_handler(ble_tx_buffer); - for (uint8_t i=0 ; i<12 ;i++) - { - (HW_NO[i]) = (char)rx_data[i] ; - } - ascii_format_data(ble_bin_buffer, "riz:", HW_NO,12); - binary_tx_handler(ble_bin_buffer,8); + } else if(cmd_t == CMD_BLE) { + memcpy(HW_NO, m_config.hw_no, 12); + ascii_format_data(ble_bin_buffer, "riz:", HW_NO, 12); + binary_tx_handler(ble_bin_buffer, 8); } - - - - } -// shz -else if((scmd.tag[1] == 'h') && (scmd.tag[2] == 'z')){ //Write, HW +// shz - Write HW Number (FDS only) +else if((scmd.tag[1] == 'h') && (scmd.tag[2] == 'z')){ if(length_error(scmd.tag,18,length)==false) { return; } - hw_i2c_init_once(); - uint8_t tx_data[EEPROM_PAGE_SIZE]; - uint8_t rx_data[EEPROM_PAGE_SIZE]; - uint8_t raw_data[EEPROM_PAGE_SIZE]; if(cmd_t == CMD_UART) { DBG_PRINTF("Tz0\r\n\r\n"); - } else if(cmd_t == CMD_BLE) { - + } else if(cmd_t == CMD_BLE) { memcpy(HW_NO, scmd.value_ascii, 12); - //eeprom_write_bytes(uint16_t mem_address, const uint8_t *data, size_t length) - for (uint8_t i=0 ; i<12 ;i++) - { - tx_data[i] = (uint8_t)(scmd.value_ascii[i]); - } - - - if(eeprom_write_encrypted(0x0010, tx_data, 12)!= NRF_SUCCESS) - { - DBG_PRINTF("ERR!!! EEP_Serial_number 12\r\n\r\n");; - } - - if(eeprom_read_bytes(0x0010, raw_data, 12)!= NRF_SUCCESS) - { - DBG_PRINTF("ERR!!! EEP_Serial_number 12\r\n\r\n");; - } - - nrf_delay_ms(10); - DBG_PRINTF("encrypted: %s\n", raw_data); - if(eeprom_read_decrypted(0x0010, rx_data, 12)!= NRF_SUCCESS) - { - DBG_PRINTF("ERR!!! EEP_Serial_number 12\r\n\r\n");; - } - DBG_PRINTF("Decrypted: %s\n", rx_data); - + memcpy(m_config.hw_no, scmd.value_ascii, 12); + config_save(); + DBG_PRINTF("[shz] HW=%s saved to FDS\r\n", m_config.hw_no); ascii_format_data(ble_bin_buffer, "rhz:", scmd.value_ascii,12); - binary_tx_handler(ble_bin_buffer,8); + binary_tx_handler(ble_bin_buffer,8); } - - - else{ - DBG_PRINTF("ERR!!! passkey 6\r\n\r\n"); + DBG_PRINTF("ERR!!! HW_NO 12\r\n\r\n"); if(cmd_t == CMD_UART) { DBG_PRINTF("Tpz0FF\r\n\r\n"); - } else if(cmd_t == CMD_BLE) { + } else if(cmd_t == CMD_BLE) { param_error(scmd.tag ); } - } - } // sxz -else if((scmd.tag[1] == 'x') && (scmd.tag[2] == 'z')){ // Set the delay for PD stabilization. 0s ~ FFFF(65535)s +else if((scmd.tag[1] == 'x') && (scmd.tag[2] == 'z')){ // Set the delay for PD stabilization. 0�s ~ FFFF(65535)�s m_life_cycle = ((uint16_t)scmd.value0 << 16) | ((uint16_t)scmd.value1 & 0xFFFF); result_data[0] = scmd.value0; diff --git a/project/ble_peripheral/ble_app_bladder_patch/debug_print.h b/project/ble_peripheral/ble_app_bladder_patch/debug_print.h index 8a20dd6..1f513db 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/debug_print.h +++ b/project/ble_peripheral/ble_app_bladder_patch/debug_print.h @@ -5,8 +5,8 @@ #define ENABLE_PRINTF 1 // Set to 0 to disable globally #if ENABLE_PRINTF - #include - #define DBG_PRINTF(...) printf(__VA_ARGS__) + #include "SEGGER_RTT.h" + #define DBG_PRINTF(...) SEGGER_RTT_printf(0, __VA_ARGS__) #else #define DBG_PRINTF(...) // Do nothing #endif diff --git a/project/ble_peripheral/ble_app_bladder_patch/fstorage.c b/project/ble_peripheral/ble_app_bladder_patch/fstorage.c index 5bb2336..9665249 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/fstorage.c +++ b/project/ble_peripheral/ble_app_bladder_patch/fstorage.c @@ -23,8 +23,8 @@ #include "fstorage.h" #include "nrf_pwr_mgmt.h" -#include "measurements.h" #include "main.h" +#include "debug_print.h" /* File ID and Key used for the configuration record. */ @@ -54,114 +54,39 @@ static fds_record_t const m_dummy_record = }; -/* 공장 입력 항목 1 */ - - -/* 공장 입력 항목 2 */ -//char serial_number_dflt[12] = "2025AAMAY0FF"; -//uint16_t pd_delay_us = 0; -//int8_t reset_status=0; - -//uint32_t pd_adc_calibration_dflt_PD0[M_LED_NUM] = -// {1000, 1000, 1000, 1000, 1000, 1000,1000, 1000, 1000, 1000, 1000, 1000,1000, 1000, 1000, 1000, 1000, 1000,1000, 1000, 1000, 1000, 1000, 1000, /* PD01 */ -//}; -//uint32_t pd_adc_calibration_dflt_PD1[M_LED_NUM] = -// {1000, 1000, 1000, 1000, 1000, 1000,1000, 1000, 1000, 1000, 1000, 1000,1000, 1000, 1000, 1000, 1000, 1000,1000, 1000, 1000, 1000, 1000, 1000, /* PD1 */ -//}; -//uint32_t pd_adc_calibration_dflt_PD2[M_LED_NUM] = -// {1000, 1000, 1000, 1000, 1000, 1000,1000, 1000, 1000, 1000, 1000, 1000,1000, 1000, 1000, 1000, 1000, 1000,1000, 1000, 1000, 1000, 1000, 1000, /* MOD1 */ -//}; - -///* 공장 입력 항목 5 */ -//uint32_t dark_noise_for_pd_dflt[PD_NUM] = /*PD0, PD1, PD2, PD3, PD4, PD5, PD6, PD7, PD8, PD9, PD10, PD11, PD12, PD13, PD14, PD15, PD16, PD17, PD18, PD19, PD20, */ -// {1111, 1111}; - - -/* 공장 입력 항목 8 */ -//int8_t pd_adc_cnt_dflt = 8; /* 8, 16, 24, 32 */ -int8_t reset_status_dflt =99; -/* 공장 입력 항목 9 */ -//uint16_t led_delay_us_dflt = 8000; /* 0 ~ 65535 */ - -/* 공장 입력 항목 10 */ -//uint16_t pd_delay_us_dflt = 8000; /* 0 ~ 65535 */ - -#if FEATURE_SECURE_CONNECTION -/* Bonding information delete */ -//bool bond_data_delete_dflt = true;// - -/* Static Passkey */ +int8_t reset_status_dflt = 99; uint8_t static_passkey_dflt[6] = "123456"; -#endif - - - -// -//uint16_t led_power_dp_dflt[48] = { -// //NULL, 42, 29, 41, 31, 28, 28, 42, 28, 39, 29, 27, 27, 42, 29, 40, 30, 28, 28, 40, 27, 38, 28, 25, 22 -// 25,25,25,18,19,24,24,25,25,18,19,25,23,25,25,18,19,24,24,25,25,19,20,25,24,25,25,25,18,19,24,24,25,25,18,19,25,23,25,25,18,19,24,24,25,25,19,20 -//}; - void fds_default_value_set(void) { + /* HW Number - empty (set via BLE command) */ + memset(m_config.hw_no, 0, 12); - /* Serial Number */ - //memcpy(m_config.serial_number, serial_number_dflt, 12); - - /* PD-ADC Calibration Value */ -// for(uint8_t j = 0; j < M_LED_NUM; j++){ -// m_config.pd_adc_calibration_PD0[j] = pd_adc_calibration_dflt_PD0[j]; -// } -// for(uint8_t j = 0; j < M_LED_NUM; j++){ -// m_config.pd_adc_calibration_PD1[j] = pd_adc_calibration_dflt_PD1[j]; -// } -// for(uint8_t j = 0; j < M_LED_NUM; j++){ -// m_config.pd_adc_calibration_PD2[j] = pd_adc_calibration_dflt_PD2[j]; -// } - - -// /* Dark Noise of PD */ -// for(uint8_t i = 0; i < PD_NUM; i++){ -// m_config.dark_noise_for_pd[i] = dark_noise_for_pd_dflt[i]; -// } - - /* PD-ADC Cycle for 8, 16, 24, 32 */ -// m_config.pd_adc_cnt = pd_adc_cnt_dflt; - - /* LED ON Delay for usec */ -// m_config.led_delay_us = led_delay_us_dflt; - - /* PD ON Delay for usec */ -// m_config.pd_delay_us = pd_delay_us_dflt; - - - m_config.reset_status = reset_status_dflt; - -#if FEATURE_SECURE_CONNECTION - /* Bonding information delete */ -// m_config.bond_data_delete = bond_data_delete_dflt; + /* Serial Number - default from FIRMWARE_SERIAL_NO */ + memset(m_config.serial_no, 0, 12); + memcpy(m_config.serial_no, "VB026030000", 11); /* Static Passkey */ memcpy(m_config.static_passkey, static_passkey_dflt, 6); - -#endif - //for(uint8_t i = 0; i < PD_NUM; i++){ -// for(uint8_t j = 0; j < LED_NUM; j++){ -// m_config.led_pd_dac_v[j] = 1000; -// } -// } - - -// for(uint8_t j = 0; j < LED_NUM; j++){ -// m_config.led_power_dp[j] = led_power_dp_dflt[j] ; -// } -// - } + /* Bond data delete */ + m_config.bond_data_delete = 1; + + /* Reset status */ + m_config.reset_status = reset_status_dflt; + + /* Measurement parameters */ + m_config.pd_adc_cnt = 8; + m_config.pd_delay_us = 8000; +} + + +static volatile uint8_t fds_last_evt = 0xFF; static void fds_evt_handler( fds_evt_t const *p_evt ) { + fds_last_evt = p_evt->id; + switch( p_evt->id ) { case FDS_EVT_INIT: @@ -191,20 +116,20 @@ static void fds_evt_handler( fds_evt_t const *p_evt ) } if(go_NVIC_SystemReset == true) { /* After flash writing completed, System Reset */ - printf("Off FDS_ENVET\r\n"); - NVIC_SystemReset(); //0112 + DBG_PRINTF("Off FDS_EVENT\r\n"); + NVIC_SystemReset(); } } break; case FDS_EVT_DEL_RECORD: - break; + break; case FDS_EVT_DEL_FILE: break; case FDS_EVT_GC: - break; + break; default: break; @@ -215,9 +140,16 @@ static void fds_evt_handler( fds_evt_t const *p_evt ) /**@brief Wait for fds to initialize. */ static void wait_for_fds_ready( void ) { + uint32_t timeout = 0; while( !m_fds_initialized ) { nrf_pwr_mgmt_run(); + nrf_delay_ms(1); + timeout++; + if (timeout > 3000) { /* 3 second timeout */ + DBG_PRINTF("[FDS] TIMEOUT!\r\n"); + break; + } } } @@ -227,11 +159,21 @@ void config_load( void ) ret_code_t rc; fds_record_desc_t desc = { 0 }; fds_find_token_t tok = { 0 }; + uint8_t cfg_retry = 0; cfg_load_start: memset((char *)&desc, 0, sizeof(desc)); memset((char *)&tok, 0, sizeof(tok)); rc = fds_record_find(CONFIG_FILE, CONFIG_REC_KEY, &desc, &tok); + DBG_PRINTF("[FDS] find rc=%u\r\n", rc); + + /* FDS may not be fully ready yet - retry before writing defaults */ + if (rc != NRF_SUCCESS && cfg_retry < 10) { + cfg_retry++; + DBG_PRINTF("[FDS] retry %u/10\r\n", cfg_retry); + nrf_delay_ms(100); + goto cfg_load_start; + } if( rc == NRF_SUCCESS ) { @@ -240,21 +182,32 @@ void config_load( void ) /* Open the record and read its contents. */ rc = fds_record_open(&desc, &config); - APP_ERROR_CHECK(rc); + if (rc != NRF_SUCCESS) { + /* CRC error or corrupt record - delete and use defaults */ + DBG_PRINTF("[FDS] open ERR=%u, deleting\r\n", rc); + (void)fds_record_delete(&desc); + fds_gc(); + fds_default_value_set(); + goto cfg_load_write_new; + } - /* Copy the configuration from flash into m_dummy_cfg. */ + /* Copy the configuration from flash into m_config. */ memcpy(&m_config, config.p_data, sizeof(config_data_t)); /* Close the record when done reading. */ rc = fds_record_close(&desc); APP_ERROR_CHECK(rc); + DBG_PRINTF("[FDS] magic=0x%08X (expect 0x%08X)\r\n", + m_config.magic_number, CONFIG_MAGIC_NUMBER_VALUE); + if( m_config.magic_number != (uint32_t)CONFIG_MAGIC_NUMBER_VALUE ) { // first init + DBG_PRINTF("[FDS] FORMAT! overwriting with defaults\r\n"); rc = fds_record_delete(&desc); APP_ERROR_CHECK(rc); m_config.magic_number = CONFIG_MAGIC_NUMBER_VALUE; - + // default.... fds_default_value_set(); @@ -271,16 +224,24 @@ void config_load( void ) } goto cfg_load_start; } + DBG_PRINTF("[FDS] Loaded OK\r\n"); } else { - /* System config not found; write a new one. */ + cfg_load_write_new: + DBG_PRINTF("[FDS] New - writing defaults\r\n"); + /* System config not found (or corrupt); write a new one. */ m_config.magic_number = CONFIG_MAGIC_NUMBER_VALUE; // default.... fds_default_value_set(); + fds_flag_write = true; rc = fds_record_write(&desc, &m_dummy_record); + if (rc != NRF_SUCCESS) { + DBG_PRINTF("[FDS] Write ERR=%u\r\n", rc); + fds_flag_write = false; + } while( fds_flag_write ) { nrf_pwr_mgmt_run(); @@ -306,44 +267,58 @@ void config_save( void ) fds_record_desc_t desc = { 0 }; fds_find_token_t tok = { 0 }; - rc = fds_gc(); - APP_ERROR_CHECK(rc); + DBG_PRINTF("[CFG_SAVE] start\r\n"); + + /* Skip if a previous FDS operation is still in progress (non-blocking) */ + if (fds_flag_write) { + DBG_PRINTF("[CFG_SAVE] busy, skipped\r\n"); + return; + } if( m_config.magic_number != (uint32_t)CONFIG_MAGIC_NUMBER_VALUE ) { m_config.magic_number = CONFIG_MAGIC_NUMBER_VALUE; } - config_save_start: - - fds_flag_write = true; - memset((char *)&desc, 0, sizeof(desc)); memset((char *)&tok, 0, sizeof(tok)); rc = fds_record_find(CONFIG_FILE, CONFIG_REC_KEY, &desc, &tok); + DBG_PRINTF("[CFG_SAVE] find rc=%u\r\n", rc); if( rc == NRF_SUCCESS ) { - /* Write the updated record to flash. */ + fds_flag_write = true; rc = fds_record_update(&desc, &m_dummy_record); - if( (rc != NRF_SUCCESS) && (rc == FDS_ERR_NO_SPACE_IN_FLASH) ) + DBG_PRINTF("[CFG_SAVE] update rc=%u\r\n", rc); + if( rc == FDS_ERR_NO_SPACE_IN_FLASH ) { + fds_flag_write = false; rc = fds_gc(); - APP_ERROR_CHECK(rc); - goto config_save_start; + DBG_PRINTF("[CFG_SAVE] gc rc=%u, retry\r\n", rc); + fds_flag_write = true; + rc = fds_record_update(&desc, &m_dummy_record); + DBG_PRINTF("[CFG_SAVE] retry rc=%u\r\n", rc); } - else + if( rc != NRF_SUCCESS ) { - APP_ERROR_CHECK(rc); + DBG_PRINTF("[CFG_SAVE] FAIL rc=%u\r\n", rc); + fds_flag_write = false; } } else { + DBG_PRINTF("[CFG_SAVE] not found, writing new\r\n"); + fds_flag_write = true; rc = fds_record_write(&desc, &m_dummy_record); - APP_ERROR_CHECK(rc); - - goto config_save_start; + DBG_PRINTF("[CFG_SAVE] write rc=%u\r\n", rc); + if( rc != NRF_SUCCESS ) + { + DBG_PRINTF("[CFG_SAVE] FAIL rc=%u\r\n", rc); + fds_flag_write = false; + } } + + DBG_PRINTF("[CFG_SAVE] done\r\n"); } @@ -359,12 +334,16 @@ void fs_storage_init(void) /* Register first to receive an event when initialization is complete. */ rc = fds_register(fds_evt_handler); APP_ERROR_CHECK(rc); + rc = fds_init(); APP_ERROR_CHECK(rc); + /* Wait for fds to initialize. */ wait_for_fds_ready(); + fds_stat_t stat = { 0 }; rc = fds_stat(&stat); APP_ERROR_CHECK(rc); -} + DBG_PRINTF("[FDS] OK\r\n"); +} diff --git a/project/ble_peripheral/ble_app_bladder_patch/fstorage.h b/project/ble_peripheral/ble_app_bladder_patch/fstorage.h index 2f92888..4fcb89b 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/fstorage.h +++ b/project/ble_peripheral/ble_app_bladder_patch/fstorage.h @@ -13,39 +13,19 @@ #include "nordic_common.h" #include -#include "measurements.h" -#define LED_NUM 48 /* LED0 사용하지 않음, 대신 NULL값 입력 */ -#define PD_NUM 2 /* PD0 사용하지 않음, 대신 NULL값 입력 * 0,1 data 2 mod 3 off*/ -#define mW 13 /* LED mW 설정 파워 값 */ -#define M_LED_NUM 24 /*측정할 L*/ #pragma pack(1) typedef struct { - uint32_t magic_number; - //char serial_number[13]; - -// uint32_t pd_adc_calibration_PD0[M_LED_NUM]; -// uint32_t pd_adc_calibration_PD1[M_LED_NUM]; -// uint32_t pd_adc_calibration_PD2[M_LED_NUM]; - - -// uint32_t dark_noise_for_pd[PD_NUM]; -// int8_t pd_adc_cnt; - int8_t reset_status; -// uint16_t led_delay_us; -// uint16_t pd_delay_us; - -#if FEATURE_SECURE_CONNECTION -// bool bond_data_delete; - uint8_t static_passkey[6]; - -#endif - -// uint16_t led_pd_dac_v[LED_NUM]; -// uint16_t led_power_dp[LED_NUM]; - -} config_data_t; /* Flash에 저장하는 값 */ + uint32_t magic_number; /* 4B - 0x20231226 */ + char hw_no[12]; /* 12B - HW Number */ + char serial_no[12]; /* 12B - Serial Number */ + uint8_t static_passkey[6]; /* 6B - BLE Passkey */ + uint8_t bond_data_delete; /* 1B - Bond delete flag */ + int8_t reset_status; /* 1B - Reset status */ + uint8_t pd_adc_cnt; /* 1B - ADC sample count */ + uint16_t pd_delay_us; /* 2B - PD delay (us) */ +} config_data_t; /* Total: 41 bytes - FDS에 저장하는 디바이스 설정 */ extern config_data_t m_config; diff --git a/project/ble_peripheral/ble_app_bladder_patch/hex/cpd.bat b/project/ble_peripheral/ble_app_bladder_patch/hex/cpd.bat new file mode 100644 index 0000000..e4cebce --- /dev/null +++ b/project/ble_peripheral/ble_app_bladder_patch/hex/cpd.bat @@ -0,0 +1,183 @@ +@echo off +setlocal enabledelayedexpansion + +echo ========================================== +echo MEDiThings Bladder Patch Programming +echo (DEBUG MODE - No Readback Protection) +echo ========================================== + +REM ----------------------------------------------------- +REM Set script directory as base path +REM ----------------------------------------------------- +cd /d "%~dp0" +echo Working directory: %CD% + +REM ----------------------------------------------------- +REM Create hex output folder +REM ----------------------------------------------------- +if not exist hex mkdir hex + +REM ----------------------------------------------------- +REM 1. Copy HEX files (with verification) +REM ----------------------------------------------------- +echo [1/6] Copying HEX files... + +set "APP_SRC=..\pca10056\s140\arm5_no_packs\_build\nrf52840_xxaa.hex" +set "BOOT_SRC=..\..\..\dfu\secure_bootloader\pca10056_s140_ble\arm5_no_packs\_build\nrf52840_xxaa_s140.hex" + +REM Check source files exist +if not exist "%APP_SRC%" ( + echo ERROR: Application HEX not found: %APP_SRC% + pause + exit /b 1 +) + +if not exist "%BOOT_SRC%" ( + echo ERROR: Bootloader HEX not found: %BOOT_SRC% + pause + exit /b 1 +) + +REM Copy with /Y to overwrite without prompt +copy /Y "%APP_SRC%" hex\app.hex +if %errorlevel% neq 0 ( + echo ERROR: Failed to copy app.hex + pause + exit /b 1 +) +echo - app.hex copied OK + +copy /Y "%BOOT_SRC%" hex\boot.hex +if %errorlevel% neq 0 ( + echo ERROR: Failed to copy boot.hex + pause + exit /b 1 +) +echo - boot.hex copied OK + +REM ----------------------------------------------------- +REM 2. Generate Bootloader DFU Settings +REM ----------------------------------------------------- +echo [2/6] Generating Bootloader DFU settings... +nrfutil settings generate ^ + --family NRF52840 ^ + --application hex\app.hex ^ + --application-version 1 ^ + --bootloader-version 1 ^ + --bl-settings-version 2 ^ + hex\settings.hex + +if %errorlevel% neq 0 ( + echo ERROR: nrfutil settings failed. + pause + exit /b 1 +) + +REM ----------------------------------------------------- +REM 3. Merge HEX (SoftDevice + Application) +REM ----------------------------------------------------- +echo [3/6] Merging SoftDevice + Application... +mergehex.exe --merge s140_nrf52_7.2.0_softdevice.hex hex\app.hex --output hex\part1.hex + +if %errorlevel% neq 0 ( + echo ERROR: mergehex part1 failed. + pause + exit /b 1 +) + +REM ----------------------------------------------------- +REM 4. Merge HEX (Bootloader + Settings) +REM ----------------------------------------------------- +echo [4/6] Merging Bootloader + Settings... +mergehex.exe --merge hex\boot.hex hex\settings.hex --output hex\part2.hex + +if %errorlevel% neq 0 ( + echo ERROR: mergehex part2 failed. + pause + exit /b 1 +) + +REM ----------------------------------------------------- +REM 5. Final HEX merge (Combine everything) +REM ----------------------------------------------------- +echo [5/6] Creating final combined HEX... +mergehex.exe --merge hex\part1.hex hex\part2.hex --output hex\firmware_all.hex + +if %errorlevel% neq 0 ( + echo ERROR: mergehex final merge failed. + pause + exit /b 1 +) + +echo Final merged HEX: hex\firmware_all.hex + +REM ----------------------------------------------------- +REM 6. Detect device SERIAL NUMBER and Flash +REM ----------------------------------------------------- +echo [6/6] Detecting device serial number... + +for /f %%A in (' + powershell -Command "(nrfutil device list --json | Select-String '\"type\":\"info\"' | ConvertFrom-Json).data.devices[0].serialNumber" +') do set SERIALNUMBER=%%A + +if "%SERIALNUMBER%"=="" ( + echo ERROR: No serial number found. + pause + exit /b 1 +) + +echo Using Serial Number: %SERIALNUMBER% +echo Flashing: program → reset (NO erase, FDS preserved) + +REM recover - SKIP to preserve internal flash data +REM erase - SKIP to preserve FDS/fstorage data + +REM program (sector-erase only for hex regions, FDS pages untouched) +nrfutil device program --firmware hex\firmware_all.hex --serial-number %SERIALNUMBER% +if %errorlevel% neq 0 ( + echo. + echo [WARNING] Program failed - device may have Readback Protection enabled. + echo Recover will ERASE ALL flash including FDS data. + echo. + set /p RECOVER_YN="Recover and retry? (Y/N): " + if /i "!RECOVER_YN!"=="Y" ( + echo Recovering device... + nrfutil device recover --serial-number %SERIALNUMBER% + if %errorlevel% neq 0 goto flash_fail + echo Re-programming... + nrfutil device program --firmware hex\firmware_all.hex --serial-number %SERIALNUMBER% + if %errorlevel% neq 0 goto flash_fail + ) else ( + goto flash_fail + ) +) + +REM reset +nrfutil device reset --serial-number %SERIALNUMBER% +if %errorlevel% neq 0 goto flash_fail + +goto flash_success + +:flash_fail +echo ERROR: Flashing failed. +pause +exit /b 1 + +:flash_success + +REM ----------------------------------------------------- +REM NOTE: Readback Protection SKIPPED for debugging +REM ----------------------------------------------------- +echo ========================================== +echo Programming Complete! (DEBUG MODE) +echo No Readback Protection Applied +echo Internal Flash (FDS) Preserved +echo %date% %time% +echo ========================================== + +REM ----------------------------------------------------- +REM Open J-Link RTT Viewer for RTT debugging +REM ----------------------------------------------------- +echo Starting J-Link RTT Viewer... + +endlocal diff --git a/project/ble_peripheral/ble_app_bladder_patch/hex/sett.bat b/project/ble_peripheral/ble_app_bladder_patch/hex/sett.bat index 730de41..87f456f 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/hex/sett.bat +++ b/project/ble_peripheral/ble_app_bladder_patch/hex/sett.bat @@ -1,3 +1,3 @@ set path=d:\nrfutil\bin;%path% d: -cd D:\mt_project\mt_firmware\project\ble_peripheral\ble_app_bladder_patch\hex \ No newline at end of file +cd D:\mt_project\vesiscan\project\ble_peripheral\ble_app_bladder_patch\hex \ No newline at end of file diff --git a/project/ble_peripheral/ble_app_bladder_patch/icm42670p/app_raw/app_raw.c b/project/ble_peripheral/ble_app_bladder_patch/icm42670p/app_raw/app_raw.c index cda8c3e..1cc72e7 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/icm42670p/app_raw/app_raw.c +++ b/project/ble_peripheral/ble_app_bladder_patch/icm42670p/app_raw/app_raw.c @@ -19,6 +19,8 @@ #include "main.h" #include "meas_pd_48.h" #include +#include "debug_print.h" +#include "nrf_delay.h" /* @@ -312,7 +314,7 @@ static void apply_mounting_matrix(const int32_t matrix[9], int32_t raw[3]) { unsigned i; int64_t data_q30[3]; - + for(i = 0; i < 3; i++) { data_q30[i] = ((int64_t)matrix[3*i+0] * raw[0]); data_q30[i] += ((int64_t)matrix[3*i+1] * raw[1]); @@ -322,3 +324,80 @@ static void apply_mounting_matrix(const int32_t matrix[9], int32_t raw[3]) raw[1] = (int32_t)(data_q30[1]>>30); raw[2] = (int32_t)(data_q30[2]>>30); } + + +/* Raw I2C read from ICM42670P — bypasses driver API entirely */ +#include "system_interface.h" +#include "nrfx_twi.h" + +extern const nrfx_twi_t m_twi_icm42670; + +#define IMU_I2C_ADDR 0x68 +#define REG_ACCEL_X1 0x0B /* ACCEL_DATA_X1 */ + +/* Direct IMU register read — raw I2C, no DRDY, sends rsp: via BLE */ +int imu_read_direct(void) +{ + uint8_t raw[12]; /* 6 accel + 6 gyro */ + int32_t accel[3], gyro[3]; + uint8_t reg; + uint32_t ret; + + static bool twi_ready = false; + + DBG_PRINTF("[IMU] enter\r\n"); + + /* Ensure ICM42670P TWI is initialized (once only) */ + if (!twi_ready) { + inv_i2c_master_uninitialize(); + inv_i2c_master_initialize(); + twi_ready = true; + } + + /* Enable accel (low-noise) + gyro (low-noise): PWR_MGMT0 = 0x0F */ + { + uint8_t pwr_cmd[2] = { 0x1F, 0x0F }; /* reg=0x1F, val=0x0F */ + icm42670_twi_tx(IMU_I2C_ADDR, pwr_cmd, 2, false); + nrf_delay_ms(2); /* wait for sensor startup */ + } + + /* Read 12 bytes starting from ACCEL_DATA_X1 (0x0B..0x16) */ + reg = REG_ACCEL_X1; + ret = icm42670_twi_tx(IMU_I2C_ADDR, ®, 1, true); + if (ret) { + DBG_PRINTF("[IMU] tx FAIL %u\r\n", ret); + return -1; + } + ret = icm42670_twi_rx(IMU_I2C_ADDR, raw, 12); + if (ret) { + DBG_PRINTF("[IMU] rx FAIL %u\r\n", ret); + return -2; + } + + /* Big-Endian register layout: [0..5]=accel, [6..11]=gyro */ + accel[0] = (int16_t)((raw[0] << 8) | raw[1]); + accel[1] = (int16_t)((raw[2] << 8) | raw[3]); + accel[2] = (int16_t)((raw[4] << 8) | raw[5]); + gyro[0] = (int16_t)((raw[6] << 8) | raw[7]); + gyro[1] = (int16_t)((raw[8] << 8) | raw[9]); + gyro[2] = (int16_t)((raw[10] << 8) | raw[11]); + + apply_mounting_matrix(icm_mounting_matrix, accel); + apply_mounting_matrix(icm_mounting_matrix, gyro); + + DBG_PRINTF("[IMU] A:%d,%d,%d G:%d,%d,%d\r\n", + accel[0], accel[1], accel[2], gyro[0], gyro[1], gyro[2]); + + ssp_data[0] = (uint16_t)accel[0]; + ssp_data[1] = (uint16_t)accel[1]; + ssp_data[2] = (uint16_t)accel[2]; + ssp_data[3] = (uint16_t)gyro[0]; + ssp_data[4] = (uint16_t)gyro[1]; + ssp_data[5] = (uint16_t)gyro[2]; + + format_data(imu_bin_buffer, "rsp:", ssp_data, 12); + binary_tx_handler(imu_bin_buffer, 8); + + DBG_PRINTF("[IMU] sent OK\r\n"); + return 0; +} diff --git a/project/ble_peripheral/ble_app_bladder_patch/icm42670p/app_raw/app_raw.h b/project/ble_peripheral/ble_app_bladder_patch/icm42670p/app_raw/app_raw.h index cad2d05..956f1cd 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/icm42670p/app_raw/app_raw.h +++ b/project/ble_peripheral/ble_app_bladder_patch/icm42670p/app_raw/app_raw.h @@ -71,5 +71,11 @@ int get_imu_data(void); */ void imu_callback(inv_imu_sensor_event_t *event); +/** + * \brief Direct IMU register read — bypasses DRDY, sends rsp: via BLE. + * \return 0 on success, negative value on error. + */ +int imu_read_direct(void); + #endif /* !_APP_RAW_H_ */ diff --git a/project/ble_peripheral/ble_app_bladder_patch/icm42670p/system_interface.c b/project/ble_peripheral/ble_app_bladder_patch/icm42670p/system_interface.c index 2e49a88..aca9e30 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/icm42670p/system_interface.c +++ b/project/ble_peripheral/ble_app_bladder_patch/icm42670p/system_interface.c @@ -34,7 +34,7 @@ void inv_i2c_master_uninitialize(void){ nrfx_twi_uninit(&m_twi_icm42670); } -static void inv_i2c_master_initialize(void){ +void inv_i2c_master_initialize(void){ ret_code_t err_code; const nrfx_twi_config_t twi_icm42670_config = { diff --git a/project/ble_peripheral/ble_app_bladder_patch/icm42670p/system_interface.h b/project/ble_peripheral/ble_app_bladder_patch/icm42670p/system_interface.h index 9c0bb50..76ae19f 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/icm42670p/system_interface.h +++ b/project/ble_peripheral/ble_app_bladder_patch/icm42670p/system_interface.h @@ -32,6 +32,7 @@ uint32_t icm42670_twi_rx( uint8_t device_id, uint8_t cat_read (uint8_t device_id, uint8_t address, uint8_t *data); void cat_write (uint8_t device_id, uint8_t address, uint8_t *data); void inv_i2c_master_uninitialize(void); +void inv_i2c_master_initialize(void); int inv_io_hal_init(struct inv_imu_serif *serif); int inv_io_hal_read_reg(struct inv_imu_serif *serif, uint8_t reg, uint8_t * rbuffer, uint32_t rlen); int inv_io_hal_write_reg(struct inv_imu_serif *serif, uint8_t reg, const uint8_t * wbuffer, uint32_t wlen); diff --git a/project/ble_peripheral/ble_app_bladder_patch/main.c b/project/ble_peripheral/ble_app_bladder_patch/main.c index 62d2bdb..27b0af6 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/main.c +++ b/project/ble_peripheral/ble_app_bladder_patch/main.c @@ -1,6 +1,6 @@ /******************************************************************************* * @file main.c - * @brief Medithings Bladder Patch Firmware + * @brief Medithings VesiScan BASIC * @version 1.17 * @date 2025-12-09 * @author Charles KWON @@ -93,6 +93,13 @@ #include "nrf_crypto.h" #include #include "debug_print.h" +#include "fstorage.h" + + +#define HARDWARE_VERSION "VB0HW0000" +#define FIRMWARE_VERSION "VB0FW0000" +#define FIRMWARE_SERIAL_NO "VB026030000" + /*============================================================================== * BUILD CONFIGURATION @@ -319,19 +326,71 @@ static void full_gpio_init(void) static void load_default_config(void) { memset(SERIAL_NO, 0, 16); - memcpy(SERIAL_NO, "2025MEDIP0001", 13); - + memcpy(SERIAL_NO, FIRMWARE_SERIAL_NO, strlen(FIRMWARE_SERIAL_NO)); + memset(m_static_passkey, 0, 16); memcpy(m_static_passkey, "123456", 6); - + m_pd_delay_us = 8000; m_pd_adc_cnt = 8; m_reset_status = 1; bond_data_delete = 1; - + DBG_PRINTF("[CFG] Default (S/N=%s)\r\n", SERIAL_NO); } +/**@brief Load configuration from internal Flash (FDS) into global variables. + * + * Must be called AFTER ble_stack_init() → fs_storage_init() → config_load(). + * Reads m_config (populated by config_load) and copies to runtime globals. + */ +static bool m_need_save_defaults = false; + +static void load_flash_config(void) +{ + m_need_save_defaults = false; + + /* Hardware Number — fill default if empty */ + if (m_config.hw_no[0] == 0 || m_config.hw_no[0] == (char)0xFF) { + memset(m_config.hw_no, 0, 12); + memcpy(m_config.hw_no, HARDWARE_VERSION, strlen(HARDWARE_VERSION)); + DBG_PRINTF("[CFG] HW empty, set default: %s\r\n", HARDWARE_VERSION); + m_need_save_defaults = true; + } + + /* Serial Number — fill default if empty */ + if (m_config.serial_no[0] == 0 || m_config.serial_no[0] == (char)0xFF) { + memset(m_config.serial_no, 0, 12); + memcpy(m_config.serial_no, FIRMWARE_SERIAL_NO, strlen(FIRMWARE_SERIAL_NO)); + DBG_PRINTF("[CFG] S/N empty, set default: %s\r\n", FIRMWARE_SERIAL_NO); + m_need_save_defaults = true; + } + + /* Serial Number → BLE device name */ + memset(SERIAL_NO, 0, 16); + memcpy(SERIAL_NO, m_config.serial_no, 12); + + /* Passkey */ + memset(m_static_passkey, 0, 16); + memcpy(m_static_passkey, m_config.static_passkey, 6); + + /* Bond data delete flag */ + bond_data_delete = m_config.bond_data_delete; + + /* Reset status */ + m_reset_status = m_config.reset_status; + + /* Measurement parameters (with validation) */ + m_pd_adc_cnt = (m_config.pd_adc_cnt > 0 && m_config.pd_adc_cnt <= 32) + ? m_config.pd_adc_cnt : 8; + m_pd_delay_us = (m_config.pd_delay_us > 0) + ? m_config.pd_delay_us : 8000; + + DBG_PRINTF("[CFG] HW=%.12s S/N=%s passkey=%.6s bond=%d rst=%d adc=%d delay=%u\r\n", + m_config.hw_no, SERIAL_NO, m_static_passkey, bond_data_delete, + m_reset_status, m_pd_adc_cnt, m_pd_delay_us); +} + #if !DEBUG_MINIMAL_BOOT static void load_eeprom_config(void) { @@ -345,13 +404,13 @@ static void load_eeprom_config(void) #if BLE_DEV_MODE memset(SERIAL_NO, 0, 16); - memcpy(SERIAL_NO, "2005MEDIP001", 12); + memcpy(SERIAL_NO, FIRMWARE_SERIAL_NO, strlen(FIRMWARE_SERIAL_NO)); DBG_PRINTF("[CFG] DEV S/N=%s\r\n", SERIAL_NO); #else if (!is_valid_serial_no(SERIAL_NO)) { memset(SERIAL_NO, 0, 16); - memcpy(SERIAL_NO, "2025AAAAP000", 12); + memcpy(SERIAL_NO, FIRMWARE_SERIAL_NO, strlen(FIRMWARE_SERIAL_NO)); } char const init_pass[6] = "123456"; @@ -1441,23 +1500,19 @@ int main(void) DBG_PRINTF("[2] Timers\r\n"); timers_init(); - // PHASE 4: 설정 - DBG_PRINTF("[3] Config\r\n"); -#if DEBUG_MINIMAL_BOOT + // PHASE 4: 설정 (기본값 로드) + DBG_PRINTF("[3] Config (defaults)\r\n"); load_default_config(); -#else - load_eeprom_config(); -#endif // PHASE 5: 버튼/LED DBG_PRINTF("[4] Buttons\r\n"); buttons_leds_init(&erase_bonds_local); - + #if FEATURE_SECURE_CONNECTION erase_bonds = erase_bonds_local; #endif - // PHASE 6: BLE + // PHASE 6: BLE 스택 DBG_PRINTF("[5] BLE\r\n"); power_management_init(); @@ -1466,6 +1521,13 @@ int main(void) ble_stack_init(); DBG_PRINTF(" stack OK\r\n"); + // PHASE 6.5: 내장 Flash (FDS) - BLE 스택 이후에 초기화해야 함 + DBG_PRINTF("[5.5] FDS\r\n"); + fs_storage_init(); + config_load(); + load_flash_config(); + DBG_PRINTF(" fds OK\r\n"); + gap_params_init(); DBG_PRINTF(" gap OK\r\n"); @@ -1487,6 +1549,13 @@ int main(void) peer_manager_init(); #endif + // PHASE 7.5: FDS 기본값 저장 (advertising 이후) + if (m_need_save_defaults) { + DBG_PRINTF("[FDS] Saving defaults after ADV\r\n"); + config_save(); + m_need_save_defaults = false; + } + // PHASE 8: 완료 DBG_PRINTF("\r\n========================================\r\n"); DBG_PRINTF(" READY [%s]\r\n", SERIAL_NO); diff --git a/project/ble_peripheral/ble_app_bladder_patch/pca10056/s140/arm5_no_packs/JLinkLog.txt b/project/ble_peripheral/ble_app_bladder_patch/pca10056/s140/arm5_no_packs/JLinkLog.txt deleted file mode 100644 index 22f3a99..0000000 --- a/project/ble_peripheral/ble_app_bladder_patch/pca10056/s140/arm5_no_packs/JLinkLog.txt +++ /dev/null @@ -1,68 +0,0 @@ -T17578 000:016.299 SEGGER J-Link V7.22b Log File -T17578 000:016.864 DLL Compiled: Jun 17 2021 17:22:49 -T17578 000:016.869 Logging started @ 2026-01-24 07:30 -T17578 000:016.873 - 16.875ms -T17578 000:017.062 JLINK_SetWarnOutHandler(...) -T17578 000:017.200 - 0.139ms -T17578 000:017.206 JLINK_OpenEx(...) -T17578 000:021.737 Firmware: J-Link OB-SAM3U128-V2-NordicSemi compiled Jul 8 2025 10:14:41 -T17578 000:021.932 Firmware: J-Link OB-SAM3U128-V2-NordicSemi compiled Jul 8 2025 10:14:41 -T17578 000:023.526 Hardware: V1.00 -T17578 000:023.541 S/N: 682060510 -T17578 000:023.553 OEM: SEGGER -T17578 000:023.584 Feature(s): RDI, FlashBP, FlashDL, JFlash, GDB -T17578 000:024.858 TELNET listener socket opened on port 19021 -T17578 000:025.175 WEBSRV Starting webserver -T17578 000:025.338 WEBSRV Webserver running on local port 19080 -T17578 000:025.348 - 8.144ms returns "O.K." -T17578 000:025.367 JLINK_GetEmuCaps() -T17578 000:025.371 - 0.007ms returns 0xB8EA5A33 -T17578 000:025.659 JLINK_TIF_GetAvailable(...) -T17578 000:025.792 - 0.142ms -T17578 000:025.810 JLINK_SetErrorOutHandler(...) -T17578 000:025.815 - 0.006ms -T17578 000:026.191 JLINK_ExecCommand("ProjectFile = "D:\mt_project\mt_firmware\project\ble_peripheral\ble_app_bladder_patch\pca10056\s140\arm5_no_packs\JLinkSettings.ini"", ...). -T17578 000:042.853 - 16.685ms returns 0x00 -T17578 000:050.030 JLINK_ExecCommand("Device = nRF52840_xxAA", ...). -T17578 000:056.080 Device "NRF52840_XXAA" selected. -T17578 000:056.499 - 6.440ms returns 0x00 -T17578 000:056.983 JLINK_GetHardwareVersion() -T17578 000:056.998 - 0.017ms returns 10000 -T17578 000:057.003 JLINK_GetDLLVersion() -T17578 000:057.007 - 0.005ms returns 72202 -T17578 000:057.013 JLINK_GetOEMString(...) -T17578 000:057.018 JLINK_GetFirmwareString(...) -T17578 000:057.027 - 0.011ms -T17578 000:067.210 JLINK_GetDLLVersion() -T17578 000:067.242 - 0.034ms returns 72202 -T17578 000:067.248 JLINK_GetCompileDateTime() -T17578 000:067.253 - 0.006ms -T17578 000:070.610 JLINK_GetFirmwareString(...) -T17578 000:070.630 - 0.022ms -T17578 000:076.118 JLINK_GetHardwareVersion() -T17578 000:076.149 - 0.036ms returns 10000 -T17578 000:081.686 JLINK_GetSN() -T17578 000:081.735 - 0.054ms returns 682060510 -T17578 000:086.288 JLINK_GetOEMString(...) -T17578 000:096.973 JLINK_TIF_Select(JLINKARM_TIF_SWD) -T17578 000:097.628 - 0.671ms returns 0x00 -T17578 000:097.653 JLINK_HasError() -T17578 000:099.547 JLINK_SetSpeed(5000) -T17578 000:099.820 - 0.282ms -T17578 000:099.835 JLINK_GetId() -T17578 000:105.067 InitTarget() start -T17578 000:105.093 J-Link Script File: Executing InitTarget() -T17578 000:108.737 Looking for J-Link GUI Server exe at: C:\Keil_v5\ARM\Segger\JLinkGUIServer.exe -T17578 000:108.890 Looking for J-Link GUI Server exe at: C:\Program Files\SEGGER\JLink_V818\JLinkGUIServer.exe -T17578 000:108.954 Forking J-Link GUI Server: C:\Program Files\SEGGER\JLink_V818\JLinkGUIServer.exe -T17578 000:181.575 J-Link GUI Server info: "J-Link GUI server V8.18 " -T17578 002:406.198 Device will be unsecured now. -T17578 002:631.975 InitTarget() end -T17578 002:796.797 InitTarget() start -T17578 002:796.830 J-Link Script File: Executing InitTarget() -T17578 002:911.808 InitTarget() end -T17578 003:014.808 - 2915.023ms returns 0x00000000 -T17578 006:160.609 JLINK_Close() -T17578 006:185.982 - 25.395ms -T17578 006:186.008 -T17578 006:186.012 Closed diff --git a/project/ble_peripheral/ble_app_bladder_patch/pca10056/s140/arm5_no_packs/ble_app_bladder_patch_s140.uvguix.CharlesKWON b/project/ble_peripheral/ble_app_bladder_patch/pca10056/s140/arm5_no_packs/ble_app_bladder_patch_s140.uvguix.CharlesKWON deleted file mode 100644 index 6ccfe24..0000000 --- a/project/ble_peripheral/ble_app_bladder_patch/pca10056/s140/arm5_no_packs/ble_app_bladder_patch_s140.uvguix.CharlesKWON +++ /dev/null @@ -1,2112 +0,0 @@ - - - - -6.1 - -
### uVision Project, (C) Keil Software
- - - D:\mt_project\pc_firm\dr_adc121s051 - - - - - - - 38003 - Registers - 188 49 - - - 346 - Code Coverage - 1410 160 - - - 204 - Performance Analyzer - 1570 - - - - - - 35141 - Event Statistics - - 200 50 700 - - - 1506 - Symbols - - 106 106 106 - - - 1936 - Watch 1 - - 200 133 133 - - - 1937 - Watch 2 - - 200 133 133 - - - 1935 - Call Stack + Locals - - 200 133 133 - - - 2506 - Trace Data - - 75 135 130 95 70 230 200 150 - - - 466 - Source Browser - 500 - 300 - - - - - - - - 1 - 1 - 0 - 0 - -1 - - - - - - - 44 - 0 - 1 - - -32000 - -32000 - - - -1 - -1 - - - 229 - 866 - 2157 - 1333 - - - - 0 - - 2887 - 0100000004000000010000000100000001000000010000000000000002000000000000000100000001000000000000002800000028000000010000001B0000001A0000000100000054443A5C6D745F70726F6A6563745C6D745F6669726D776172655C70726F6A6563745C626C655F7065726970686572616C5C626C655F6170705F626C61646465725F70617463685C6932635F6D616E616765722E63000000000D6932635F6D616E616765722E6300000000C5D4F200FFFFFFFF4D443A5C6D745F70726F6A6563745C6D745F6669726D776172655C70726F6A6563745C626C655F7065726970686572616C5C626C655F6170705F626C61646465725F70617463685C6D61696E2E6300000000066D61696E2E6300000000FFDC7800FFFFFFFF3D443A5C6D745F70726F6A6563745C6D745F6669726D776172655C6D6F64756C65735C6E7266785C647269766572735C7372635C6E7266785F7477692E63000000000A6E7266785F7477692E6300000000BECEA100FFFFFFFF41443A5C6D745F70726F6A6563745C6D745F6669726D776172655C636F6D706F6E656E74735C626C655C636F6D6D6F6E5C626C655F636F6E6E5F706172616D732E630000000011626C655F636F6E6E5F706172616D732E6300000000F0A0A100FFFFFFFF4D443A5C6D745F70726F6A6563745C6D745F6669726D776172655C70726F6A6563745C626C655F7065726970686572616C5C626C655F6170705F626C61646465725F70617463685C6D61696E2E6800000000066D61696E2E6800000000BCA8E100FFFFFFFF1E443A5C6D745F70726F6A6563745C70635F6669726D5C7061727365722E6300000000087061727365722E63000000009CC1B600FFFFFFFF47443A5C6D745F70726F6A6563745C6D745F6669726D776172655C636F6D706F6E656E74735C626C655C626C655F73657276696365735C626C655F6E75735C626C655F6E75732E630000000009626C655F6E75732E6300000000F7B88600FFFFFFFF41443A5C6D745F70726F6A6563745C6D745F6669726D776172655C636F6D706F6E656E74735C6C69627261726965735C74696D65725C6170705F74696D6572322E63000000000C6170705F74696D6572322E6300000000D9ADC200FFFFFFFF35443A5C6D745F70726F6A6563745C6D745F6669726D776172655C6D6F64756C65735C6E7266785C68616C5C6E72665F6770696F2E68000000000A6E72665F6770696F2E6800000000A5C2D700FFFFFFFF4D443A5C6D745F70726F6A6563745C6D745F6669726D776172655C636F6D706F6E656E74735C6C69627261726965735C6C6F675C6E72665F6C6F675F64656661756C745F6261636B656E64732E68000000001A6E72665F6C6F675F64656661756C745F6261636B656E64732E6800000000B3A6BE00FFFFFFFF53443A5C6D745F70726F6A6563745C6D745F6669726D776172655C70726F6A6563745C626C655F7065726970686572616C5C626C655F6170705F626C61646465725F70617463685C6D61696E5F74696D65722E63000000000C6D61696E5F74696D65722E6300000000EAD6A300FFFFFFFF55443A5C6D745F70726F6A6563745C6D745F6669726D776172655C70726F6A6563745C626C655F7065726970686572616C5C626C655F6170705F626C61646465725F70617463685C6D6561737572656D656E74732E63000000000E6D6561737572656D656E74732E6300000000F6FA7D00FFFFFFFF54443A5C6D745F70726F6A6563745C6D745F6669726D776172655C70726F6A6563745C626C655F7065726970686572616C5C626C655F6170705F626C61646465725F70617463685C616461323230305F7370692E63000000000D616461323230305F7370692E6300000000B5E99D00FFFFFFFF3F443A5C6D745F70726F6A6563745C6D745F6669726D776172655C696E746567726174696F6E5C6E7266785C6C65676163795C6E72665F6472765F7070692E68000000000D6E72665F6472765F7070692E68000000005FC3CF00FFFFFFFF1E443A5C6D745F70726F6A6563745C70635F6669726D5C7061727365722E6800000000087061727365722E6800000000C1838300FFFFFFFF52443A5C6D745F70726F6A6563745C6D745F6669726D776172655C70726F6A6563745C626C655F7065726970686572616C5C626C655F6170705F626C61646465725F70617463685C70756C73655F67656E2E68000000000B70756C73655F67656E2E6800000000CACAD500FFFFFFFF51443A5C6D745F70726F6A6563745C6D745F6669726D776172655C70726F6A6563745C626C655F7065726970686572616C5C626C655F6170705F626C61646465725F70617463685C66756C6C5F6167632E63000000000A66756C6C5F6167632E6300000000C5D4F200FFFFFFFF55443A5C6D745F70726F6A6563745C6D745F6669726D776172655C70726F6A6563745C626C655F7065726970686572616C5C626C655F6170705F626C61646465725F70617463685C6D6561737572656D656E74732E68000000000E6D6561737572656D656E74732E6800000000FFDC7800FFFFFFFF44443A5C6D745F70726F6A6563745C6D745F6669726D776172655C70726F6A6563745C626C655F7065726970686572616C5C64725F7069657A6F5C64725F7069657A6F2E63000000000A64725F7069657A6F2E6300000000BECEA100FFFFFFFF40443A5C6D745F70726F6A6563745C6D745F6669726D776172655C636F6D706F6E656E74735C6C69627261726965735C64656C61795C6E72665F64656C61792E68000000000B6E72665F64656C61792E6800000000F0A0A100FFFFFFFF44443A5C6D745F70726F6A6563745C6D745F6669726D776172655C70726F6A6563745C626C655F7065726970686572616C5C64725F7069657A6F5C64725F7069657A6F2E68000000000A64725F7069657A6F2E6800000000BCA8E100FFFFFFFF3F443A5C6D745F70726F6A6563745C6D745F6669726D776172655C636F6D706F6E656E74735C6C69627261726965735C7574696C5C6170705F6572726F722E68000000000B6170705F6572726F722E68000000009CC1B600FFFFFFFF68443A5C6D745F70726F6A6563745C6D745F6669726D776172655C70726F6A6563745C626C655F7065726970686572616C5C626C655F6170705F626C61646465725F70617463685C70636131303035365C733134305C636F6E6669675C73646B5F636F6E6669672E68000000000C73646B5F636F6E6669672E6800000000F7B88600FFFFFFFF76433A5C55736572735C436861726C65734B574F4E5C417070446174615C4C6F63616C5C41726D5C5061636B735C4E6F7264696353656D69636F6E647563746F725C6E52465F44657669636546616D696C795061636B5C382E34302E335C4465766963655C496E636C7564655C6E726635323834302E68000000000A6E726635323834302E6800000000D9ADC200FFFFFFFF27443A5C6D745F70726F6A6563745C70635F6669726D5C64725F7574696C5C64725F7574696C2E63000000000964725F7574696C2E6300000000A5C2D700FFFFFFFF56443A5C6D745F70726F6A6563745C6D745F6669726D776172655C70726F6A6563745C626C655F7065726970686572616C5C626C655F6170705F626C61646465725F70617463685C706F7765725F636F6E74726F6C2E63000000000F706F7765725F636F6E74726F6C2E6300000000B3A6BE00FFFFFFFF33443A5C6D745F70726F6A6563745C70635F6669726D5C64725F616463313231733035315C64725F616463313231733035312E63000000000F64725F616463313231733035312E6300000000EAD6A300FFFFFFFF0100000010000000C5D4F200FFDC7800BECEA100F0A0A100BCA8E1009CC1B600F7B88600D9ADC200A5C2D700B3A6BE00EAD6A300F6FA7D00B5E99D005FC3CF00C1838300CACAD50001000000000000000200000065040000530100006508000041040000 - - - - 0 - Build - - -1 - -1 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 440100004F0000007007000013010000 - - - 16 - AE04000053010000DA0A000017020000 - - - - 1005 - 1005 - 1 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 0300000066000000F400000024030000 - - - 16 - 2200000039000000620100004A010000 - - - - 109 - 109 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 0300000066000000F400000024030000 - - - 16 - 22000000390000009E0100004D030000 - - - - 1465 - 1465 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 03000000040300006D070000AC030000 - - - 16 - 2200000039000000DA030000FD000000 - - - - 1466 - 1466 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 03000000040300006D070000AC030000 - - - 16 - 2200000039000000DA030000FD000000 - - - - 1467 - 1467 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 03000000040300006D070000AC030000 - - - 16 - 2200000039000000DA030000FD000000 - - - - 1468 - 1468 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 03000000040300006D070000AC030000 - - - 16 - 2200000039000000DA030000FD000000 - - - - 1506 - 1506 - 0 - 0 - 0 - 0 - 32767 - 0 - 16384 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 1913 - 1913 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 47010000660000006D070000FA000000 - - - 16 - 2200000039000000DA030000FD000000 - - - - 1935 - 1935 - 0 - 0 - 0 - 0 - 32767 - 0 - 32768 - 0 - - 16 - 03000000040300006D070000AC030000 - - - 16 - 2200000039000000620100004A010000 - - - - 1936 - 1936 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 03000000040300006D070000AC030000 - - - 16 - 2200000039000000620100004A010000 - - - - 1937 - 1937 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 03000000040300006D070000AC030000 - - - 16 - 2200000039000000620100004A010000 - - - - 1939 - 1939 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 03000000040300006D070000AC030000 - - - 16 - 2200000039000000DA030000FD000000 - - - - 1940 - 1940 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 03000000040300006D070000AC030000 - - - 16 - 2200000039000000DA030000FD000000 - - - - 1941 - 1941 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 03000000040300006D070000AC030000 - - - 16 - 2200000039000000DA030000FD000000 - - - - 1942 - 1942 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 03000000040300006D070000AC030000 - - - 16 - 2200000039000000DA030000FD000000 - - - - 195 - 195 - 1 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 0300000066000000F400000024030000 - - - 16 - 22000000390000009E0100004D030000 - - - - 196 - 196 - 1 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 0300000066000000F400000024030000 - - - 16 - 22000000390000009E0100004D030000 - - - - 197 - 197 - 1 - 0 - 0 - 0 - 32767 - 0 - 32768 - 0 - - 16 - 0000000055030000FB04000016040000 - - - 16 - 2200000039000000DA030000FD000000 - - - - 198 - 198 - 0 - 0 - 0 - 0 - 32767 - 0 - 32768 - 0 - - 16 - 00000000ED02000070070000C5030000 - - - 16 - 2200000039000000DA030000FD000000 - - - - 199 - 199 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 0300000058030000F8040000FD030000 - - - 16 - 2200000039000000DA030000FD000000 - - - - 203 - 203 - 0 - 0 - 0 - 0 - 32767 - 0 - 8192 - 0 - - 16 - 47010000660000006D070000FA000000 - - - 16 - 2200000039000000DA030000FD000000 - - - - 204 - 204 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 47010000660000006D070000FA000000 - - - 16 - 2200000039000000DA030000FD000000 - - - - 221 - 221 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 00000000000000000000000000000000 - - - 16 - 0A0000000A0000006E0000006E000000 - - - - 2506 - 2506 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 2507 - 2507 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 03000000040300006D070000AC030000 - - - 16 - 2200000039000000DA030000FD000000 - - - - 343 - 343 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 47010000660000006D070000FA000000 - - - 16 - 2200000039000000DA030000FD000000 - - - - 346 - 346 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 47010000660000006D070000FA000000 - - - 16 - 2200000039000000DA030000FD000000 - - - - 35141 - 35141 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 47010000660000006D070000FA000000 - - - 16 - 2200000039000000620100004A010000 - - - - 35824 - 35824 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 47010000660000006D070000FA000000 - - - 16 - 2200000039000000DA030000FD000000 - - - - 35885 - 35885 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 35886 - 35886 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 35887 - 35887 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 35888 - 35888 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 35889 - 35889 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 35890 - 35890 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 35891 - 35891 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 35892 - 35892 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 35893 - 35893 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 35894 - 35894 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 35895 - 35895 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 35896 - 35896 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 35897 - 35897 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 35898 - 35898 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 35899 - 35899 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 35900 - 35900 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 35901 - 35901 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 35902 - 35902 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 35903 - 35903 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 35904 - 35904 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 35905 - 35905 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 38003 - 38003 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 0300000066000000F400000024030000 - - - 16 - 22000000390000009E0100004D030000 - - - - 38007 - 38007 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 0300000058030000F8040000FD030000 - - - 16 - 2200000039000000DA030000FD000000 - - - - 436 - 436 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 0300000058030000F8040000FD030000 - - - 16 - 22000000390000009E0100004D030000 - - - - 437 - 437 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 03000000040300006D070000AC030000 - - - 16 - 2200000039000000620100004A010000 - - - - 440 - 440 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 03000000040300006D070000AC030000 - - - 16 - 2200000039000000620100004A010000 - - - - 463 - 463 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 0300000058030000F8040000FD030000 - - - 16 - 22000000390000009E0100004D030000 - - - - 466 - 466 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 0300000058030000F8040000FD030000 - - - 16 - 22000000390000009E0100004D030000 - - - - 470 - 470 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 47010000660000006D070000FA000000 - - - 16 - 2200000039000000DA030000FD000000 - - - - 50000 - 50000 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 50001 - 50001 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 50002 - 50002 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 50003 - 50003 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 50004 - 50004 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 50005 - 50005 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 50006 - 50006 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 50007 - 50007 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 50008 - 50008 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 50009 - 50009 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 50010 - 50010 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 50011 - 50011 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 50012 - 50012 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 50013 - 50013 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 50014 - 50014 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 50015 - 50015 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 50016 - 50016 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 50017 - 50017 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 50018 - 50018 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 50019 - 50019 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 33060000660000006D070000E4020000 - - - 16 - 2200000039000000620100004A010000 - - - - 59392 - 59392 - 1 - 0 - 0 - 0 - 966 - 0 - 8192 - 0 - - 16 - 0000000000000000D10300001C000000 - - - 16 - 0A0000000A0000006E0000006E000000 - - - - 59393 - 0 - 1 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 0000000016040000FB04000029040000 - - - 16 - 0A0000000A0000006E0000006E000000 - - - - 59399 - 59399 - 1 - 0 - 0 - 0 - 476 - 0 - 8192 - 1 - - 16 - 000000001C000000E701000038000000 - - - 16 - 0A0000000A0000006E0000006E000000 - - - - 59400 - 59400 - 0 - 0 - 0 - 0 - 612 - 0 - 8192 - 2 - - 16 - 00000000380000006F02000054000000 - - - 16 - 0A0000000A0000006E0000006E000000 - - - - 824 - 824 - 0 - 0 - 0 - 0 - 32767 - 0 - 4096 - 0 - - 16 - 03000000040300006D070000AC030000 - - - 16 - 2200000039000000620100004A010000 - - - - 3312 - 000000000B000000000000000020000000000000FFFFFFFFFFFFFFFF44010000130100007007000017010000000000000100000004000000010000000000000000000000FFFFFFFF08000000CB00000057010000CC000000F08B00005A01000079070000D601000045890000FFFF02000B004354616262656450616E650020000000000000AE04000053010000DA0A000017020000440100004F00000070070000130100000000000040280046080000000B446973617373656D626C7900000000CB00000001000000FFFFFFFFFFFFFFFF14506572666F726D616E636520416E616C797A6572000000005701000001000000FFFFFFFFFFFFFFFF14506572666F726D616E636520416E616C797A657200000000CC00000001000000FFFFFFFFFFFFFFFF0E4C6F67696320416E616C797A657200000000F08B000001000000FFFFFFFFFFFFFFFF0D436F646520436F766572616765000000005A01000001000000FFFFFFFFFFFFFFFF11496E737472756374696F6E205472616365000000007907000001000000FFFFFFFFFFFFFFFF0F53797374656D20416E616C797A657200000000D601000001000000FFFFFFFFFFFFFFFF104576656E742053746174697374696373000000004589000001000000FFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000000000000000000001000000FFFFFFFFCB00000001000000FFFFFFFFCB000000000000000040000000000000FFFFFFFFFFFFFFFF2C0600004F00000030060000FD020000000000000200000004000000010000000000000000000000FFFFFFFF2B000000E2050000CA0900002D8C00002E8C00002F8C0000308C0000318C0000328C0000338C0000348C0000358C0000368C0000378C0000388C0000398C00003A8C00003B8C00003C8C00003D8C00003E8C00003F8C0000408C0000418C000050C3000051C3000052C3000053C3000054C3000055C3000056C3000057C3000058C3000059C300005AC300005BC300005CC300005DC300005EC300005FC3000060C3000061C3000062C3000063C30000018000400000000000009A09000053010000DA0A000001040000300600004F00000070070000FD02000000000000404100462B0000000753796D626F6C7300000000E205000001000000FFFFFFFFFFFFFFFF0A5472616365204461746100000000CA09000001000000FFFFFFFFFFFFFFFF00000000002D8C000001000000FFFFFFFFFFFFFFFF00000000002E8C000001000000FFFFFFFFFFFFFFFF00000000002F8C000001000000FFFFFFFFFFFFFFFF0000000000308C000001000000FFFFFFFFFFFFFFFF0000000000318C000001000000FFFFFFFFFFFFFFFF0000000000328C000001000000FFFFFFFFFFFFFFFF0000000000338C000001000000FFFFFFFFFFFFFFFF0000000000348C000001000000FFFFFFFFFFFFFFFF0000000000358C000001000000FFFFFFFFFFFFFFFF0000000000368C000001000000FFFFFFFFFFFFFFFF0000000000378C000001000000FFFFFFFFFFFFFFFF0000000000388C000001000000FFFFFFFFFFFFFFFF0000000000398C000001000000FFFFFFFFFFFFFFFF00000000003A8C000001000000FFFFFFFFFFFFFFFF00000000003B8C000001000000FFFFFFFFFFFFFFFF00000000003C8C000001000000FFFFFFFFFFFFFFFF00000000003D8C000001000000FFFFFFFFFFFFFFFF00000000003E8C000001000000FFFFFFFFFFFFFFFF00000000003F8C000001000000FFFFFFFFFFFFFFFF0000000000408C000001000000FFFFFFFFFFFFFFFF0000000000418C000001000000FFFFFFFFFFFFFFFF000000000050C3000001000000FFFFFFFFFFFFFFFF000000000051C3000001000000FFFFFFFFFFFFFFFF000000000052C3000001000000FFFFFFFFFFFFFFFF000000000053C3000001000000FFFFFFFFFFFFFFFF000000000054C3000001000000FFFFFFFFFFFFFFFF000000000055C3000001000000FFFFFFFFFFFFFFFF000000000056C3000001000000FFFFFFFFFFFFFFFF000000000057C3000001000000FFFFFFFFFFFFFFFF000000000058C3000001000000FFFFFFFFFFFFFFFF000000000059C3000001000000FFFFFFFFFFFFFFFF00000000005AC3000001000000FFFFFFFFFFFFFFFF00000000005BC3000001000000FFFFFFFFFFFFFFFF00000000005CC3000001000000FFFFFFFFFFFFFFFF00000000005DC3000001000000FFFFFFFFFFFFFFFF00000000005EC3000001000000FFFFFFFFFFFFFFFF00000000005FC3000001000000FFFFFFFFFFFFFFFF000000000060C3000001000000FFFFFFFFFFFFFFFF000000000061C3000001000000FFFFFFFFFFFFFFFF000000000062C3000001000000FFFFFFFFFFFFFFFF000000000063C3000001000000FFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000000000000000000000001000000FFFFFFFFE205000001000000FFFFFFFFE2050000000000000010000001000000FFFFFFFFFFFFFFFFF70000004F000000FB0000003D0300000100000002000010040000000100000081FEFFFFD2010000FFFFFFFF05000000ED0300006D000000C3000000C400000073940000018000100000010000006A030000530100006104000041040000000000004F000000F70000003D0300000000000040410056050000000750726F6A65637401000000ED03000001000000FFFFFFFFFFFFFFFF05426F6F6B73000000006D00000001000000FFFFFFFFFFFFFFFF0946756E6374696F6E7301000000C300000001000000FFFFFFFFFFFFFFFF0954656D706C6174657301000000C400000001000000FFFFFFFFFFFFFFFF09526567697374657273000000007394000001000000FFFFFFFFFFFFFFFF00000000000000000000000000000000000000000000000001000000FFFFFFFFED03000001000000FFFFFFFFED030000000000000080000000000000FFFFFFFFFFFFFFFF00000000E902000070070000ED02000000000000010000000400000001000000000000000000000000000000000000000000000001000000C6000000FFFFFFFF0F0000008F070000930700009407000095070000960700009007000091070000B5010000B801000038030000B9050000BA050000BB050000BC050000CB090000018000800000000000006A030000F1030000DA0A0000C904000000000000ED02000070070000C503000000000000404100460F0000001343616C6C20537461636B202B204C6F63616C73000000008F07000001000000FFFFFFFFFFFFFFFF0755415254202331000000009307000001000000FFFFFFFFFFFFFFFF0755415254202332000000009407000001000000FFFFFFFFFFFFFFFF0755415254202333000000009507000001000000FFFFFFFFFFFFFFFF15446562756720287072696E74662920566965776572000000009607000001000000FFFFFFFFFFFFFFFF0757617463682031000000009007000001000000FFFFFFFFFFFFFFFF0757617463682032000000009107000001000000FFFFFFFFFFFFFFFF10547261636520457863657074696F6E7300000000B501000001000000FFFFFFFFFFFFFFFF0E4576656E7420436F756E7465727300000000B801000001000000FFFFFFFFFFFFFFFF09554C494E4B706C7573000000003803000001000000FFFFFFFFFFFFFFFF084D656D6F7279203100000000B905000001000000FFFFFFFFFFFFFFFF084D656D6F7279203200000000BA05000001000000FFFFFFFFFFFFFFFF084D656D6F7279203300000000BB05000001000000FFFFFFFFFFFFFFFF084D656D6F7279203400000000BC05000001000000FFFFFFFFFFFFFFFF105472616365204E617669676174696F6E00000000CB09000001000000FFFFFFFFFFFFFFFFFFFFFFFF0000000001000000000000000000000001000000FFFFFFFFB8030000ED020000BC030000C503000000000000020000000400000000000000000000000000000000000000000000000000000002000000C6000000FFFFFFFF8F07000001000000FFFFFFFF8F07000001000000C6000000000000000080000001000000FFFFFFFFFFFFFFFF000000003D030000FB040000410300000100000001000010040000000100000011FDFFFF88000000FFFFFFFF06000000C5000000C7000000B4010000D2010000CF01000077940000018000800000010000006A03000045040000650800001A0500000000000041030000FB040000160400000000000040820056060000000C4275696C64204F757470757401000000C500000001000000FFFFFFFFFFFFFFFF0D46696E6420496E2046696C657300000000C700000001000000FFFFFFFFFFFFFFFF0A4572726F72204C69737400000000B401000001000000FFFFFFFFFFFFFFFF0E536F757263652042726F7773657200000000D201000001000000FFFFFFFFFFFFFFFF0E416C6C205265666572656E63657300000000CF01000001000000FFFFFFFFFFFFFFFF0742726F77736572000000007794000001000000FFFFFFFFFFFFFFFF00000000000000000000000000000000000000000000000001000000FFFFFFFFC500000001000000FFFFFFFFC5000000000000000000000000000000 - - - 59392 - File - - 2584 - 00200000010000002800FFFF01001100434D4643546F6F6C426172427574746F6E00E100000000000000000000000000000000000000000000000100000001000000018001E100000000000001000000000000000000000000000000000100000001000000018003E1000000000000020000000000000000000000000000000001000000010000000180CD7F0000000000000300000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF000000000000000000000000000100000001000000018023E100000000040004000000000000000000000000000000000100000001000000018022E100000000040005000000000000000000000000000000000100000001000000018025E10000000000000600000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF00000000000000000000000000010000000100000001802BE10000000004000700000000000000000000000000000000010000000100000001802CE10000000004000800000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF00000000000000000000000000010000000100000001807A8A0000000000000900000000000000000000000000000000010000000100000001807B8A0000000004000A00000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180D3B00000000000000B000000000000000000000000000000000100000001000000018015B10000000004000C0000000000000000000000000000000001000000010000000180F4B00000000004000D000000000000000000000000000000000100000001000000018036B10000000004000E00000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180FF88000000000400460000000000000000000000000000000001000000010000000180FE880000000004004500000000000000000000000000000000010000000100000001800B810000000004001300000000000000000000000000000000010000000100000001800C810000000004001400000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180F0880000020000000F000000000000000000000000000000000100000001000000FFFF0100120043555646696E64436F6D626F427574746F6EE8030000000000000000000000000000000000000000000000010000000100000096000000020020500000000007636D645F6D706196000000000000000300124D41415F56455253494F4E5F4D41524B45520007636D645F6D7061000000000000000000000000018024E10000000000001100000000000000000000000000000000010000000100000001800A810000000000001200000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF000000000000000000000000000100000001000000FFFF01001500434D4643546F6F6C4261724D656E75427574746F6E2280000002000000150000002153746172742F53746F70202644656275672053657373696F6E094374726C2B46350000000000000000000000000100000001000000000000000000000001000000020021802280000000000000150000002153746172742F53746F70202644656275672053657373696F6E094374726C2B4635000000000000000000000000010000000100000000000000000000000100000000002180E0010000000000007500000021456E65726779204D6561737572656D656E742026776974686F75742044656275670000000000000000000000000100000001000000000000000000000001000000000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180C488000000000000160000000000000000000000000000000001000000010000000180C988000000000400180000000000000000000000000000000001000000010000000180C788000000000000190000000000000000000000000000000001000000010000002180C8880000000000001700000027264B696C6C20416C6C20427265616B706F696E747320696E2043757272656E7420546172676574000000000000000000000000010000000100000000000000000000000100000003002180C8880000000000001700000027264B696C6C20416C6C20427265616B706F696E747320696E2043757272656E7420546172676574000000000000000000000000010000000100000000000000000000000100000000002180E50100000000000078000000264B696C6C20416C6C20427265616B706F696E747320696E204163746976652050726F6A656374000000000000000000000000010000000100000000000000000000000100000000002180E601000000000000790000002F4B696C6C20416C6C20427265616B706F696E747320696E204D756C74692D50726F6A65637420576F726B73706163650000000000000000000000000100000001000000000000000000000001000000000001800000000001000000FFFFFFFF00000000000000000000000000010000000100000021804C010000020001001A0000000F2650726F6A6563742057696E646F77000000000000000000000000010000000100000000000000000000000100000008002180DD880000000000001A0000000750726F6A656374000000000000000000000000010000000100000000000000000000000100000000002180DC8B0000000000003A00000005426F6F6B73000000000000000000000000010000000100000000000000000000000100000000002180E18B0000000000003B0000000946756E6374696F6E73000000000000000000000000010000000100000000000000000000000100000000002180E28B000000000000400000000954656D706C6174657300000000000000000000000001000000010000000000000000000000010000000000218018890000000000003D0000000E536F757263652042726F777365720000000000000000000000000100000001000000000000000000000001000000000021800000000000000400FFFFFFFF00000000000000000001000000000000000100000000000000000000000100000000002180D988000000000000390000000C4275696C64204F7574707574000000000000000000000000010000000100000000000000000000000100000000002180E38B000000000000410000000B46696E64204F75747075740000000000000000000000000100000001000000000000000000000001000000000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180FB7F0000000000001B000000000000000000000000000000000100000001000000000000000446696C65C6030000 - - - 1423 - 2800FFFF01001100434D4643546F6F6C426172427574746F6E00E1000000000000FFFFFFFF000100000000000000010000000000000001000000018001E1000000000000FFFFFFFF000100000000000000010000000000000001000000018003E1000000000000FFFFFFFF0001000000000000000100000000000000010000000180CD7F000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF000000000000000000010000000000000001000000018023E1000000000000FFFFFFFF000100000000000000010000000000000001000000018022E1000000000000FFFFFFFF000100000000000000010000000000000001000000018025E1000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF00000000000000000001000000000000000100000001802BE1000000000000FFFFFFFF00010000000000000001000000000000000100000001802CE1000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF00000000000000000001000000000000000100000001807A8A000000000000FFFFFFFF00010000000000000001000000000000000100000001807B8A000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF0000000000000000000100000000000000010000000180D3B0000000000000FFFFFFFF000100000000000000010000000000000001000000018015B1000000000000FFFFFFFF0001000000000000000100000000000000010000000180F4B0000000000000FFFFFFFF000100000000000000010000000000000001000000018036B1000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF0000000000000000000100000000000000010000000180FF88000000000000FFFFFFFF0001000000000000000100000000000000010000000180FE88000000000000FFFFFFFF00010000000000000001000000000000000100000001800B81000000000000FFFFFFFF00010000000000000001000000000000000100000001800C81000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF0000000000000000000100000000000000010000000180F088000000000000FFFFFFFF0001000000000000000100000000000000010000000180EE7F000000000000FFFFFFFF000100000000000000010000000000000001000000018024E1000000000000FFFFFFFF00010000000000000001000000000000000100000001800A81000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF00000000000000000001000000000000000100000001802280000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF0000000000000000000100000000000000010000000180C488000000000000FFFFFFFF0001000000000000000100000000000000010000000180C988000000000000FFFFFFFF0001000000000000000100000000000000010000000180C788000000000000FFFFFFFF0001000000000000000100000000000000010000000180C888000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF0000000000000000000100000000000000010000000180DD88000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF0000000000000000000100000000000000010000000180FB7F000000000000FFFFFFFF000100000000000000010000000000000001000000 - - - 1423 - 2800FFFF01001100434D4643546F6F6C426172427574746F6E00E100000000000000000000000000000000000000000000000100000001000000018001E100000000000001000000000000000000000000000000000100000001000000018003E1000000000000020000000000000000000000000000000001000000010000000180CD7F0000000000000300000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF000000000000000000000000000100000001000000018023E100000000000004000000000000000000000000000000000100000001000000018022E100000000000005000000000000000000000000000000000100000001000000018025E10000000000000600000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF00000000000000000000000000010000000100000001802BE10000000000000700000000000000000000000000000000010000000100000001802CE10000000000000800000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF00000000000000000000000000010000000100000001807A8A0000000000000900000000000000000000000000000000010000000100000001807B8A0000000000000A00000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180D3B00000000000000B000000000000000000000000000000000100000001000000018015B10000000000000C0000000000000000000000000000000001000000010000000180F4B00000000000000D000000000000000000000000000000000100000001000000018036B10000000000000E00000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180FF880000000000000F0000000000000000000000000000000001000000010000000180FE880000000000001000000000000000000000000000000000010000000100000001800B810000000000001100000000000000000000000000000000010000000100000001800C810000000000001200000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180F088000000000000130000000000000000000000000000000001000000010000000180EE7F00000000000014000000000000000000000000000000000100000001000000018024E10000000000001500000000000000000000000000000000010000000100000001800A810000000000001600000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF000000000000000000000000000100000001000000018022800000000000001700000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180C488000000000000180000000000000000000000000000000001000000010000000180C988000000000000190000000000000000000000000000000001000000010000000180C7880000000000001A0000000000000000000000000000000001000000010000000180C8880000000000001B00000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180DD880000000000001C00000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180FB7F0000000000001D000000000000000000000000000000000100000001000000 - - - - 59399 - Build - - 1024 - 00200000010000001000FFFF01001100434D4643546F6F6C426172427574746F6ECF7F0000000000001C0000000000000000000000000000000001000000010000000180D07F0000000000001D000000000000000000000000000000000100000001000000018030800000000000001E000000000000000000000000000000000100000001000000FFFF01001500434D4643546F6F6C4261724D656E75427574746F6EC7040000000000006A0000000C4261746368204275696C2664000000000000000000000000010000000100000000000000000000000100000004000580C7040000000000006A0000000C4261746368204275696C266400000000000000000000000001000000010000000000000000000000010000000000058046070000000000006B0000000D42617463682052656275696C640000000000000000000000000100000001000000000000000000000001000000000005804707000000000000FFFFFFFF0B426174636820436C65616E0100000000000000000000000100000001000000000000000000000001000000000005809E8A0000000000001F0000000F4261746326682053657475702E2E2E000000000000000000000000010000000100000000000000000000000100000000000180D17F0000000004002000000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF00000000000000000000000000010000000100000001804C8A0000000000002100000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF000000000000000000000000000100000001000000FFFF01001900434D4643546F6F6C426172436F6D626F426F78427574746F6EBA00000000000000000000000000000000000000000000000001000000010000009600000003002050000000000D6E726635323834305F78786161960000000000000002000D6E726635323834305F7878616121666C6173685F733134305F6E726635325F372E322E305F736F667464657669636500000000000000000180EB880000000000002200000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180C07F000000000000230000000000000000000000000000000001000000010000000180B08A000000000400240000000000000000000000000000000001000000010000000180A8010000000000004E00000000000000000000000000000000010000000100000001807202000000000000530000000000000000000000000000000001000000010000000180BE010000000000005000000000000000000000000000000000010000000100000000000000054275696C64DC010000 - - - 583 - 1000FFFF01001100434D4643546F6F6C426172427574746F6ECF7F000000000000FFFFFFFF0001000000000000000100000000000000010000000180D07F000000000000FFFFFFFF00010000000000000001000000000000000100000001803080000000000000FFFFFFFF00010000000000000001000000000000000100000001809E8A000000000000FFFFFFFF0001000000000000000100000000000000010000000180D17F000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF00000000000000000001000000000000000100000001804C8A000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF00000000000000000001000000000000000100000001806680000000000000FFFFFFFF0001000000000000000100000000000000010000000180EB88000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF0000000000000000000100000000000000010000000180C07F000000000000FFFFFFFF0001000000000000000100000000000000010000000180B08A000000000000FFFFFFFF0001000000000000000100000000000000010000000180A801000000000000FFFFFFFF00010000000000000001000000000000000100000001807202000000000000FFFFFFFF0001000000000000000100000000000000010000000180BE01000000000000FFFFFFFF000100000000000000010000000000000001000000 - - - 583 - 1000FFFF01001100434D4643546F6F6C426172427574746F6ECF7F000000000000000000000000000000000000000000000001000000010000000180D07F00000000000001000000000000000000000000000000000100000001000000018030800000000000000200000000000000000000000000000000010000000100000001809E8A000000000000030000000000000000000000000000000001000000010000000180D17F0000000000000400000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF00000000000000000000000000010000000100000001804C8A0000000000000500000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF00000000000000000000000000010000000100000001806680000000000000060000000000000000000000000000000001000000010000000180EB880000000000000700000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180C07F000000000000080000000000000000000000000000000001000000010000000180B08A000000000000090000000000000000000000000000000001000000010000000180A8010000000000000A000000000000000000000000000000000100000001000000018072020000000000000B0000000000000000000000000000000001000000010000000180BE010000000000000C000000000000000000000000000000000100000001000000 - - - - 59400 - Debug - - 2373 - 00200000000000001900FFFF01001100434D4643546F6F6C426172427574746F6ECC880000000000002500000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF000000000000000000000000000100000001000000018017800000000000002600000000000000000000000000000000010000000100000001801D800000000000002700000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF00000000000000000000000000010000000100000001801A800000000000002800000000000000000000000000000000010000000100000001801B80000000000000290000000000000000000000000000000001000000010000000180E57F0000000000002A00000000000000000000000000000000010000000100000001801C800000000000002B00000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF000000000000000000000000000100000001000000018000890000000000002C00000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180E48B0000000000002D0000000000000000000000000000000001000000010000000180F07F0000000000002E0000000000000000000000000000000001000000010000000180E8880000000000003700000000000000000000000000000000010000000100000001803B010000000000002F0000000000000000000000000000000001000000010000000180BB8A00000000000030000000000000000000000000000000000100000001000000FFFF01001500434D4643546F6F6C4261724D656E75427574746F6E0E01000000000000310000000D57617463682057696E646F7773000000000000000000000000010000000100000000000000000000000100000003001380D88B00000000000031000000085761746368202631000000000000000000000000010000000100000000000000000000000100000000001380D98B00000000000031000000085761746368202632000000000000000000000000010000000100000000000000000000000100000000001380CE01000000000000FFFFFFFF0C576174636820416E63686F720100000000000000010000000000000001000000000000000000000001000000000013800F01000000000000320000000E4D656D6F72792057696E646F7773000000000000000000000000010000000100000000000000000000000100000004001380D28B00000000000032000000094D656D6F7279202631000000000000000000000000010000000100000000000000000000000100000000001380D38B00000000000032000000094D656D6F7279202632000000000000000000000000010000000100000000000000000000000100000000001380D48B00000000000032000000094D656D6F7279202633000000000000000000000000010000000100000000000000000000000100000000001380D58B00000000000032000000094D656D6F72792026340000000000000000000000000100000001000000000000000000000001000000000013801001000000000000330000000E53657269616C2057696E646F77730000000000000000000000000100000001000000000000000000000001000000040013809307000000000000330000000855415254202326310000000000000000000000000100000001000000000000000000000001000000000013809407000000000000330000000855415254202326320000000000000000000000000100000001000000000000000000000001000000000013809507000000000000330000000855415254202326330000000000000000000000000100000001000000000000000000000001000000000013809607000000000000330000001626446562756720287072696E746629205669657765720000000000000000000000000100000001000000000000000000000001000000000013803C010000000000007200000010416E616C797369732057696E646F7773000000000000000000000000010000000100000000000000000000000100000004001380658A000000000000340000000F264C6F67696320416E616C797A6572000000000000000000000000010000000100000000000000000000000100000000001380DC7F0000000000003E0000001526506572666F726D616E636520416E616C797A6572000000000000000000000000010000000100000000000000000000000100000000001380E788000000000000380000000E26436F646520436F766572616765000000000000000000000000010000000100000000000000000000000100000000001380CD01000000000000FFFFFFFF0F416E616C7973697320416E63686F7201000000000000000100000000000000010000000000000000000000010000000000138053010000000000003F0000000D54726163652057696E646F77730000000000000000000000000100000001000000000000000000000001000000010013805401000000000000FFFFFFFF115472616365204D656E7520416E63686F720100000000000000010000000000000001000000000000000000000001000000000013802901000000000000350000001553797374656D205669657765722057696E646F77730000000000000000000000000100000001000000000000000000000001000000010013804B01000000000000FFFFFFFF1453797374656D2056696577657220416E63686F720100000000000000010000000000000001000000000000000000000001000000000001800000000001000000FFFFFFFF00000000000000000000000000010000000100000013800189000000000000360000000F26546F6F6C626F782057696E646F7700000000000000000000000001000000010000000000000000000000010000000300138044C5000000000000FFFFFFFF0E5570646174652057696E646F77730100000000000000010000000000000001000000000000000000000001000000000013800000000000000400FFFFFFFF000000000000000000010000000000000001000000000000000000000001000000000013805B01000000000000FFFFFFFF12546F6F6C626F78204D656E75416E63686F72010000000000000001000000000000000100000000000000000000000100000000000000000005446562756764020000 - - - 898 - 1900FFFF01001100434D4643546F6F6C426172427574746F6ECC88000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF00000000000000000001000000000000000100000001801780000000000000FFFFFFFF00010000000000000001000000000000000100000001801D80000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF00000000000000000001000000000000000100000001801A80000000000000FFFFFFFF00010000000000000001000000000000000100000001801B80000000000000FFFFFFFF0001000000000000000100000000000000010000000180E57F000000000000FFFFFFFF00010000000000000001000000000000000100000001801C80000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF00000000000000000001000000000000000100000001800089000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF0000000000000000000100000000000000010000000180E48B000000000000FFFFFFFF0001000000000000000100000000000000010000000180F07F000000000000FFFFFFFF0001000000000000000100000000000000010000000180E888000000000000FFFFFFFF00010000000000000001000000000000000100000001803B01000000000000FFFFFFFF0001000000000000000100000000000000010000000180BB8A000000000000FFFFFFFF0001000000000000000100000000000000010000000180D88B000000000000FFFFFFFF0001000000000000000100000000000000010000000180D28B000000000000FFFFFFFF00010000000000000001000000000000000100000001809307000000000000FFFFFFFF0001000000000000000100000000000000010000000180658A000000000000FFFFFFFF0001000000000000000100000000000000010000000180C18A000000000000FFFFFFFF0001000000000000000100000000000000010000000180EE8B000000000000FFFFFFFF00010000000000000001000000000000000100000001800000000000000000FFFFFFFF00000000000000000001000000000000000100000001800189000000000000FFFFFFFF000100000000000000010000000000000001000000 - - - 898 - 1900FFFF01001100434D4643546F6F6C426172427574746F6ECC880000000000000000000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF000000000000000000000000000100000001000000018017800000000000000100000000000000000000000000000000010000000100000001801D800000000000000200000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF00000000000000000000000000010000000100000001801A800000000000000300000000000000000000000000000000010000000100000001801B80000000000000040000000000000000000000000000000001000000010000000180E57F0000000000000500000000000000000000000000000000010000000100000001801C800000000000000600000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF000000000000000000000000000100000001000000018000890000000000000700000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180E48B000000000000080000000000000000000000000000000001000000010000000180F07F000000000000090000000000000000000000000000000001000000010000000180E8880000000000000A00000000000000000000000000000000010000000100000001803B010000000000000B0000000000000000000000000000000001000000010000000180BB8A0000000000000C0000000000000000000000000000000001000000010000000180D88B0000000000000D0000000000000000000000000000000001000000010000000180D28B0000000000000E000000000000000000000000000000000100000001000000018093070000000000000F0000000000000000000000000000000001000000010000000180658A000000000000100000000000000000000000000000000001000000010000000180C18A000000000000110000000000000000000000000000000001000000010000000180EE8B0000000000001200000000000000000000000000000000010000000100000001800000000001000000FFFFFFFF0000000000000000000000000001000000010000000180018900000000000013000000000000000000000000000000000100000001000000 - - - - 0 - 2560 - 1440 - - - - - - 1 - 0 - - 100 - 26 - - ..\..\..\i2c_manager.c - 10 - 11 - 27 - 1 - - 0 - - - ..\..\..\main.c - 4 - 402 - 417 - 1 - - 0 - - - ..\..\..\..\..\..\modules\nrfx\drivers\src\nrfx_twi.c - 0 - 1 - 1 - 1 - - 0 - - - ..\..\..\..\..\..\components\ble\common\ble_conn_params.c - 0 - 1 - 1 - 1 - - 0 - - - ..\..\..\main.h - 22 - 16 - 57 - 1 - - 0 - - - ..\..\..\..\..\..\..\pc_firm\parser.c - 0 - 1165 - 1194 - 1 - - 0 - - - ..\..\..\..\..\..\components\ble\ble_services\ble_nus\ble_nus.c - 38 - 297 - 338 - 1 - - 0 - - - ..\..\..\..\..\..\components\libraries\timer\app_timer2.c - 0 - 574 - 597 - 1 - - 0 - - - ..\..\..\..\..\..\modules\nrfx\hal/nrf_gpio.h - 2 - 1 - 21 - 1 - - 0 - - - ..\..\..\..\..\..\components\libraries\log\nrf_log_default_backends.h - 69 - 27 - 68 - 1 - - 0 - - - ..\..\..\main_timer.c - 15 - 1 - 5 - 1 - - 0 - - - ..\..\..\measurements.c - 1 - 193 - 222 - 1 - - 0 - - - ..\..\..\ada2200_spi.c - 0 - 22 - 37 - 1 - - 0 - - - ..\..\..\..\..\..\integration\nrfx\legacy\nrf_drv_ppi.h - 0 - 58 - 74 - 1 - - 0 - - - ..\..\..\..\..\..\..\pc_firm\parser.h - 21 - 1 - 12 - 1 - - 0 - - - ..\..\..\pulse_gen.h - 0 - 1 - 1 - 1 - - 0 - - - ..\..\..\full_agc.c - 27 - 957 - 991 - 1 - - 0 - - - ..\..\..\..\ble_app_bladder_patch\measurements.h - 58 - 605 - 633 - 1 - - 0 - - - ..\..\..\..\dr_piezo\dr_piezo.c - 45 - 33 - 66 - 1 - - 0 - - - ..\..\..\..\..\..\components\libraries\delay\nrf_delay.h - 0 - 23 - 64 - 1 - - 0 - - - ..\..\..\..\dr_piezo\dr_piezo.h - 24 - 20 - 52 - 1 - - 0 - - - ..\..\..\..\..\..\components\libraries\util\app_error.h - 0 - 1 - 1 - 1 - - 0 - - - ..\config\sdk_config.h - 6 - 8366 - 8399 - 1 - - 0 - - - C:\Users\CharlesKWON\AppData\Local\Arm\Packs\NordicSemiconductor\nRF_DeviceFamilyPack\8.40.3\Device\Include\nrf52840.h - 8 - 2881 - 2902 - 1 - - 0 - - - ..\..\..\..\..\..\..\pc_firm\dr_util\dr_util.c - 30 - 1 - 15 - 1 - - 0 - - - ..\..\..\power_control.c - 6 - 63 - 65 - 1 - - 0 - - - ..\..\..\..\..\..\..\pc_firm\dr_adc121s051\dr_adc121s051.c - 2 - 746 - 780 - 1 - - 0 - - - - -
diff --git a/project/ble_peripheral/ble_app_bladder_patch/pca10056/s140/arm5_no_packs/ble_app_bladder_patch_s140.uvoptx b/project/ble_peripheral/ble_app_bladder_patch/pca10056/s140/arm5_no_packs/ble_app_bladder_patch_s140.uvoptx index c98294e..f16766c 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/pca10056/s140/arm5_no_packs/ble_app_bladder_patch_s140.uvoptx +++ b/project/ble_peripheral/ble_app_bladder_patch/pca10056/s140/arm5_no_packs/ble_app_bladder_patch_s140.uvoptx @@ -1508,7 +1508,7 @@ 1 33 1 - 0 + 1 0 0 ..\..\..\..\dr_piezo\dr_piezo.c diff --git a/project/ble_peripheral/ble_app_bladder_patch/power_control.c b/project/ble_peripheral/ble_app_bladder_patch/power_control.c index 8e1059a..716564f 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/power_control.c +++ b/project/ble_peripheral/ble_app_bladder_patch/power_control.c @@ -9,27 +9,16 @@ #include "main.h" #include "power_control.h" #include "nrf_delay.h" -#include "measurements.h" -#include "meas_pd_voltage_simple.h" -//#include "inv_imu_driver.h" -//#include "app_raw_main.h" -#include "mcp4725_i2c.h" -#include "ada2200_spi.h" -#include "ad5272_i2c.h" #include "nrf_log.h" #include "app_timer.h" #include "debug_print.h" #include "cat_interface.h" #include "i2c_manager.h" -#define IR_POWER_HOLD NRF_GPIO_PIN_MAP(0, 11) -//#define RCV_POWER_HOLD NRF_GPIO_PIN_MAP(1, 9) #define EEP_WP NRF_GPIO_PIN_MAP(0, 24) APP_TIMER_DEF(m_power_timer_id); -//#define POWER_LOOP_INTERVAL 30 - // 2025-12-08 change to #define POWER_LOOP_INTERVAL 30 -> 20 #define POWER_LOOP_INTERVAL 20 @@ -37,27 +26,14 @@ APP_TIMER_DEF(m_power_timer_id); static uint8_t p_order; -extern uint8_t simple_samples_in_buffer; extern volatile bool processing; bool lock_check = false; void power_gpio_init(void) { - nrf_gpio_cfg_output(IR_POWER_HOLD); - //nrf_gpio_cfg_output(RCV_POWER_HOLD); nrf_gpio_cfg_output(EEP_WP); } -void ir_power_control(on_off_cont_t ir_power_st) -{ - if(ir_power_st == OFF) { - nrf_gpio_pin_clear(IR_POWER_HOLD); - }else if(ir_power_st == ON){ - nrf_gpio_pin_set(IR_POWER_HOLD); - } -} - - void eeprom_control(on_off_cont_t eeprom_st) { if(eeprom_st == OFF) { @@ -67,43 +43,11 @@ void eeprom_control(on_off_cont_t eeprom_st) } } -//void rcv_power_control(on_off_cont_t rcv_power_st) -//{ -// if(rcv_power_st == OFF) { -// nrf_gpio_pin_clear(RCV_POWER_HOLD); -// }else if(rcv_power_st == ON){ -// nrf_gpio_pin_set(RCV_POWER_HOLD); -// } -//} - - int device_sleep_mode(void){ int rc = 0; - eeprom_control(OFF); - mcp4725_PowerDownMode(); - - nrf_delay_ms(2); - ada2200_stop(); - nrf_delay_ms(2); -// ada2200_uninit(); - - nrf_delay_ms(2); - - nrf_delay_ms(2); -// ad5272_i2c_uninit(); - - nrf_delay_ms(2); -// rcv_power_control(OFF); - nrf_delay_ms(2); - ir_power_control(OFF); + eeprom_control(OFF); nrf_delay_ms(2); DBG_PRINTF("Device_Sleep_Mode OK!\r\n"); - if(rc == 0) { -// DBG_PRINTF("Device_Sleep_Mode OK!\r\n"); - } else { - DBG_PRINTF("ERR!!! Device_Sleep_Mode Failed!\r\n"); - } - nrf_delay_ms(10); processing = false; return rc; @@ -116,140 +60,58 @@ int device_activated(void){ lock_check =true; power_timer_start(); eeprom_control(OFF); -// nrf_delay_ms(1); -// ir_power_control(ON); -// nrf_delay_ms(1); -// rcv_power_control(ON); - -// // nrf_delay_ms(5); -//// ada2200_start(); -// nrf_delay_ms(100); -// ada2200_init(); -// nrf_delay_ms(50); -// mcp4725_init(); -// nrf_delay_ms(50); -// ad5272_i2c_init(); -// nrf_delay_ms(50); -// mcp4725_powerOnWakeUp(); -// -// nrf_delay_ms(10); -// -// -// ad5272_normal_mode(); -// nrf_delay_ms(50); -// ada2200_start(); - - - - -// if( rc == 0) { -// DBG_PRINTF("Device_Activate_Mode OK!\r\n"); -// } else { -// DBG_PRINTF("ERR!!! Device_Activated Failed!\r\n"); -// } -// nrf_delay_ms(1000); return rc; } /** * @brief Power-up sequence state machine - * - * Executes hardware initialization in 9 steps (case 0-8) + * + * Executes hardware initialization steps * Called by app_timer at POWER_LOOP_INTERVAL (20ms) - * + * * Sequence: - * 0: LED off, IR power on + * 0: I2C init * 1: (reserved) - * 2: I2C init - * 3: (reserved) - * 4: DAC wake-up - * 5: LED-PD modulation set - * 6: ADA2200 lock-in amp start - * 7: PD channel select - * 8: Complete + * 2: Complete */ void power_loop(void *p_context) { UNUSED_PARAMETER(p_context); - + power_timer_stop(); - + #if DEBUG_MINIMAL_BOOT /* Minimal Boot: Skip sensor initialization */ DBG_PRINTF("[PWR] Minimal mode - skipping sensor init\r\n"); - p_order = 8; // Jump to complete + p_order = 2; // Jump to complete #else /* Full Boot: Execute power sequence */ switch (p_order) { - /* Step 0: Power On */ + /* Step 0: I2C Initialize */ case 0: - LED_ALLOFF(); - ir_power_control(ON); - nrf_delay_ms(20); - DBG_PRINTF("[PWR] Step %d: IR Power ON\r\n", p_order); - break; - - /* Step 1: Reserved */ - case 1: - DBG_PRINTF("[PWR] Step %d: (reserved)\r\n", p_order); - break; - - /* Step 2: I2C Initialize */ - case 2: sw_i2c_init_once(); nrf_delay_ms(10); DBG_PRINTF("[PWR] Step %d: I2C Init\r\n", p_order); break; - - /* Step 3: Reserved */ - case 3: + + /* Step 1: Reserved */ + case 1: DBG_PRINTF("[PWR] Step %d: (reserved)\r\n", p_order); break; - - /* Step 4: DAC Wake-up */ - case 4: - nrf_delay_ms(10); - mcp4725_powerOnWakeUp(); - nrf_delay_ms(20); - DBG_PRINTF("[PWR] Step %d: MCP4725 Wake-up\r\n", p_order); - break; - - /* Step 5: LED-PD Modulation Set */ - case 5: - led_pd_mod_set(1000); - nrf_delay_ms(10); - led_pd_mod_set(1000); - nrf_delay_ms(10); - DBG_PRINTF("[PWR] Step %d: LED-PD Mod Set (1000)\r\n", p_order); - break; - - /* Step 6: ADA2200 Lock-in Amplifier Start */ - case 6: - ada2200_start(); - nrf_delay_ms(100); - DBG_PRINTF("[PWR] Step %d: ADA2200 Start\r\n", p_order); - break; - - /* Step 7: PD Channel Select */ - case 7: - if (NRF_SUCCESS == pd_on(2)) { - DBG_PRINTF("[PWR] Step %d: PD Channel ON\r\n", p_order); - } - break; - - /* Step 8: Complete */ - case 8: + + /* Step 2: Complete */ + case 2: DBG_PRINTF("[PWR] Step %d: Sequence Complete\r\n", p_order); break; - + default: break; } #endif - + /* Advance to next step or finish */ - if (p_order < 8) { + if (p_order < 2) { p_order++; power_timer_start(); } else { @@ -258,21 +120,9 @@ void power_loop(void *p_context) } - - int device_reactivated(void){ - int rc = 0; - sw_i2c_init_once(); - mcp4725_PowerDownMode(); - DBG_PRINTF("LOCKIN!!\r\n"); - nrf_delay_ms(10); - ada2200_stop(); - - - nrf_delay_ms(10); - - ir_power_control(OFF); + sw_i2c_init_once(); nrf_delay_ms(10); lock_check = true; p_order = 0; @@ -293,13 +143,12 @@ void power_timer_stop(void) } -void power_timer_init(void) //active start +void power_timer_init(void) //active start { APP_ERROR_CHECK(app_timer_create(&m_power_timer_id, APP_TIMER_MODE_SINGLE_SHOT, power_loop)); - + // 2025-12-08 change to APP_TIMER_MODE_REPEATED mode //APP_ERROR_CHECK(app_timer_create(&m_power_timer_id, APP_TIMER_MODE_REPEATED, power_loop)); - - -} + +} diff --git a/project/ble_peripheral/ble_app_bladder_patch/power_control.h b/project/ble_peripheral/ble_app_bladder_patch/power_control.h index e368e37..bba0237 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/power_control.h +++ b/project/ble_peripheral/ble_app_bladder_patch/power_control.h @@ -12,9 +12,7 @@ #include "main.h" void power_gpio_init(void); -void ir_power_control(on_off_cont_t ir_power_st); -void rcv_power_control(on_off_cont_t rcv_power_st); -void eeprom_control(on_off_cont_t rcv_power_st); +void eeprom_control(on_off_cont_t eeprom_st); int device_sleep_mode(void); int device_activated(void); int device_reactivated(void); diff --git a/project/ble_peripheral/dr_piezo.rar b/project/ble_peripheral/dr_piezo.rar deleted file mode 100644 index b5c7789ddffcd8f5e78e1959d1cb9a2c5a0a5f4b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6026 zcmbW*MN}IMv@PJ^4nG#$og&34E}^(vp}4!#;FRL7!QG2XAyC{sxCDoy#jR-J^$qUq zj_z7#um@+n*Iu8Nxd$CN8UkPxXSov#0R;_!ihxjA_Z6V6sh7)xh=kQp0zmHli9ic$ zH(vgV1R%8XFm^(3Uzne;eVlq|<_KV!&O z=P%jFA<~#LQA9^xZi4BoZ_U9rF%7{}+2@SdJ;(T+Q5c^;`6z1^n9I+B(pI)Da%abq z@I#;PK#;_Va#i^QisH`}!?hM*tyX=`pPHP2_i)ly^d@=nb{aVWyW*j*`)`B!?_*RX%m?VKKVL8r`cecbme7CUNWhCUMTXR<`$BOMf*p zud}6YXt3Y-yyld5E&J4=D}@l18mCm*I4^(K!w&Q-e3fM{CXpL}#&_t|NjXn0eYxJ^ zUUZ(HAQZmT(WR`8j6HbM=G>!O{dEL``ftl@Ds6JkaMN?4C^m9_2)S^+y5PAzS`9qy7iO+d<-Fvk z*C5}=bKMtEJ>^HYO52}Z!QX(Hs3xDqhELmrT!^)na9{_HFX;!+rPM>zoxd!Kr#|oLIgHHB$gLU9eRdgK4LCekXQS=rC%Lxt) z&z*z$BFo}Me;Hn>rIqE6SUvH<<~4>U`x|%X>u@0v<`HWhlmNCFXo z_QqO<-}_qkl8$pxJu-5NvWiNVWEV?1S?uu=LXI@EA6VU2x6RRV;`E~89!;O=V@$H3 z2&MVeq@hDSWBr2`d<*LzSWmCcWil?n&$>7qNuK`Tex{z2y=M4;`z+efuK=bqt3v73eE3eirFXr1@@cKi01Os<;2lYp z&mDfDzUZ$5InxUR0ks%MOnHx={6E4PL=gi)w6J%3 zE7v4xmS)topG(>7{3U~dyTEjZd(cNk>y^uVRF(<1dVmQNa+ zQd>EydyAPnrW}*j#ZFc@aVRiR!mR~g%ZP1_GngUa=g_Eib0SsWZ=?>N`1X(a10^$x zye52C67>}@B)ZHNl17x%s&&6*6Ute}0<@8Qylm}n{P(0Eoi$m$oD<#SuH_ox!_v&t z_01i+;dZuhyW_MSLMZW#FJX+zRA2=GZrly2{Ac?~y6dYFu?W>LSdnNX^}%Ovf>Et9 z5UVjxlwvZ6ng|Z!=BJYkZ2$IK0ZL5fmY$xQ5YoRm&Z@iZ5a+49a@V`(o9FAmBc7n< zWu?Ava&tR?VCG{CVjy>=9%$5Pe7qn=Syv-KjF+=@z>QSWAAAmnxBb;|n}v@9ljX6> z0=vQK-eN){!Fo_Yel5T#fX4OLa9k`n6?u;H9GYy?2-$2w8g4-fCq7KZ$LF&&ok*^{ zFUfa0Z{b~4QYFB>n*wuD(&^vscQ4IC%54N)<}5&+&*#UoS*1}$e~NWRTC}P$`O$`J zSNF)5&tH{NwT02qrO*8fV#=`mXRG71E&LMx64<5}QXYpaZ+?iQ`_Z+qandJj!83lr=O zhD9`)ocQi3=X@Mq$XR=Lx{2bzr#Hb;)yaQ{V=;25e+z0{Rmd>O5+^#Tc5p()FMEX} zR#Tg3$01qa9}y?4qu+;(nkO2v2bm5UZ*sQna$ZOW?ACyN?5)UiQf;gJ6o{%d(7(b4 zuJha#&HoFyQ)Bg8tp2mF*>Hmvv>r)D=hSDjQx{&l**T&CJ#P_N{QB@pP?FI+rte@h zjTz*@>)gqV8%zE10;y^QfB(5~&zbK!FJ`1x{w&d3Vo6LVcW&lD?HuQKhEd?UXT?OW zUV9PbAp@&%oKlVjbG%!|nTqAinXiet=te2rPM|xJ` zDe@lqYcShZ_K*k9Oj4wFfr?&b*w|;?oodsmwCsm~?zmaL^)gnbRnN}`Ynq^f{2euw z)mGOn*n9#{7?}WDtAPC!qzB!M7#PrW(-!vbCK;{ZsHoUxK7EK}bg||I+1@eGvt<)2 z(DQHjbd%B3(zE!*k|d7hi+8^J1g4MqWrv`nM2fr~ zO%uo)40}9YOG?#tK19_26mMUgYV~ce_0;EmH0IYl^LeOHBDNkZ<&*cSlxgE@7RlC{S9zBxVP!CsV6(Y!>j6LDJ` z^DRm`M2fd4ZRm`y40(D_h39;ok^!)xSP2;{cVGaXdtSGkdtXg_uhtTcHU=@TiWV7V z;>MvWFe(kwjDszQ*UyjK&TG9a+%?H9r!9^jb_U%Syazs|>>XH_4XP&-PsYs^Y6bv(z>kky-EU*o z$kEkEbC}}ATTe$+cX4bEwK2hxcLv$4)LWy15u5i4b~UFDo-7MkfBa;=aeb~{Iz>Ey zc^?~^RmSFFul8bFP*t@ta*z?JSUk!{l08YIJ}qj_b_WY1A``Dcyr%WSClXl(WhMd{ ziKOX=myeFiWMarv|q;@LvRxC$9K7 z@aRSR((%{U?pAlGDUC0hM$*JP>g)Ol7D{I;_g86|ABbs@o{uFI%VZmYdKa>NrimGh z;a(0P#nK&5h~<#cGfRhGYINCUn;=P4(W6m^ZN`~re(nh{urANS3uiEj^g3mc$&-+2 zVK~=%mDJxvTl9EgVS|j%|1gDvR6s{y(vYef`C5}6H&L9g?B@qVNv|LsaZ2i@eSQWA zNs^LFKuFEFLvrCy8`Uj6x?hx95PT*Pa|XE4#aeosOCo1eMSakIK9b+#W1X>lEgxC+ax#3ld!>DdA0xlRLS zbVzRoyYJ19lbnsbTo^Vgz-bpUc$7$$r`i!UW@2+h9Zjjpm-6r!veewGEYA|OsZRkK z_3B4zA<#yhg9b$IWGIhc5|Me@NcrGQMJGA2-&RA%l?%#N`^Vm@ZTY3G`${T!3~R6F zDN$yGV{!kkU04$r7PF$mwiJp{JL8cCqBRsjN4eD0lf%my|ISQ&R;ERm z(}K3N?ggktb|+kzFLEhMt1_*ox_W(~4(ymR0an&yMdX4CofyOEdbx9a7%g9cWrLqu zT}>!n3pIZ9e`8Wtlx{WGaRv+pbkbGU{${AcSJ;qgZ6H@VIkR%>6y)U*)OT0y8#7|Q zlr=o3%wxbb;Y>c%DJEA7G6W@P%jhZ^_k>ivm9@zvsprm~IM2pKO5ifjV|^juwGH1z zUwF?1oKIH3oR~)||2=4xnIm^g_nCodsBe1ZQvqj{d5Y>YTuKSUW~`*5aA&%mj#BA6 z6Dyv$je8P2i$3#dB+V51*rqh<85XViwe{-mjM212Y89DgLnf5;lZ!Qv2W>AY?%=%H zPJAcMhpgO~Hlu*1G7}QAZM2GS`>n|95L!jK=p;`j ziqNrTF%QGqFKcP0H)0gd=GM4XcQ+E0NhuOk<{nY)%d>ss`AFDl&&^@qk8Cu>qf-X` z9I%k1(}wBh`sr{D%N!bDq47|QAF3^7oi`Njm1JSaK}=2(!`CmE*Ilx|G~G>jG_)6z z<-XEql&U|$o7l$~LPs*l7|8|?#p0w9+4B+JN573cgttg^d=F$THV{2@C_8Z9oQ==W zs!xkZL$nCtTIih!ex|D zq-4Htc1bK1W?SiDyIk#%%tWV!- zObx|M>CpyT2#-b_Q(}xceYpYqzDC!%ELC0wau1IRjB< z0`C&c1?8RC1jKy>)+Jakb<7*cW#h5pO_%2_l%Mf24sEPq)wZEOJ!!K}0O2PAMZ>C7 ztf>SHnq`&sXLYL08^&hp59ElAJ^vM2R(jk?vkaQvOT@w2QRfbZJo)hU-w*h{O?It- zA8Ehw`p1hBvhmxxlLXTOD1l7i$fe);z+Y9gEo@L4>Gqr-&TRWE%1q$RO*pLq<9%NBCL}Vm#5#)?7nOxS= z@TO2y@pxV#GMV+Xbw`-&{C8G6^CkaezoU z9iHF5f%Oz@w_*JqJb@9FwSAgA$^XxNFHeG0zK&zxvXp7Z`XMK|f{rl)ZDC7+;mcNt z8JOabf{_WNXOI*QB@D@t84=>GaL0S6SQ!;n(7}>ZAlgG%&5On41=k5K*7Yf}&ub7{%b z`-^$gxxKknweNZ1I{@{NMgU#q8hrXhiQp$RYTm7;5sZu>mLR3n%3l zaVN1WkZ+hp>F_3jUT_Hy8xdK&5RAmLanJiEK}C;O-^?{`CkVXwo>{W1Za1*8BCNil zW$+c8*=l7YMFF#GUoy2nM#T@Hj}xyS8A*MCD@LGGuPC1eQL0!@;v%}5_t{^1`EJR_ zPMDmkJsNaY-CuYmeva8@BxqI%K;I5<80T|XDge4K`HIdSij&h%VR_RzY=n-W{D9>9 zzYF}xiA~6YlFqeafSJn}Z)gZ8zVA(F7%`mCKGCsCIyQl_bTWNZpyPO7R)h&zVonWcs_vmXoa z6al*3+?;KopmCd>cQ!L{@k{ib1BqGm%AhPOp3d2JcGJU01KmldiA2y$;W`SJ;qM-E z|5SJ4*~iD(_#YTrPr*09S1+Yf2i2L&xmT`9;dQ(^R-2uSo_rS!!5w?%vV=OwO7nH| zTs~r*VV>=rv~LzQLZ(#&>*0>o3!jUSx0k{y;;o!>Q&|GsS+Al>r(LbgDO+p%mdr#n zvaj0adIpS%5n)d~-4xr+x<0O;YJei(oDHFlut9z5J@IqqqJp9K>Pj7d5B4ASl@nN5 z+n)T?GTe6CThGTkc7JlH z1PhAeM11cPFe5ddgDm{*kh6F1-u?p3Y0tgB5N}ubYpW`PoA8S(IF*wk;`YSQU}p5d zr&uYRJUchzyDCk$sQA@Z-m_1eI(*lwP=u(-?uDmPAOrp%3KO`D diff --git a/project/ble_peripheral/dr_piezo/dr_piezo.c b/project/ble_peripheral/dr_piezo/dr_piezo.c index 80f4d56..3b5f80a 100644 --- a/project/ble_peripheral/dr_piezo/dr_piezo.c +++ b/project/ble_peripheral/dr_piezo/dr_piezo.c @@ -145,19 +145,12 @@ void TIMER2_IRQHandler(void) void dr_piezo_power_on(void) { - /* 1. IR power ON (from power_control.c) */ - ir_power_control(ON); nrf_delay_ms(20); - /* 2. Configure power control pins */ - nrf_gpio_cfg_output(DR_PIEZO_PWR_SHDN); - nrf_gpio_cfg_output(DR_PIEZO_PWR_EN_10V); + nrf_gpio_cfg_output(DR_PIEZO_PWR_EN); + nrf_gpio_pin_set(DR_PIEZO_PWR_EN); - /* 3. Enable DC/DC converter (+/-20V) */ - nrf_gpio_pin_set(DR_PIEZO_PWR_SHDN); - nrf_gpio_pin_set(DR_PIEZO_PWR_EN_10V); - - /* 4. Wait for power stabilization */ + /* Wait for power stabilization */ nrf_delay_ms(10); m_power_enabled = true; @@ -169,8 +162,7 @@ void dr_piezo_power_off(void) { dr_piezo_disable(); - nrf_gpio_pin_clear(DR_PIEZO_PWR_SHDN); - nrf_gpio_pin_clear(DR_PIEZO_PWR_EN_10V); + nrf_gpio_pin_clear(DR_PIEZO_PWR_EN); m_power_enabled = false; @@ -470,16 +462,108 @@ void dr_piezo_set_frequency(uint32_t freq_hz) void dr_piezo_mux_init(void) { /* Configure MUX control pins as outputs */ - nrf_gpio_cfg_output(DR_PIEZO_MUX_SEL1); + /*nrf_gpio_cfg_output(DR_PIEZO_MUX_SEL1); nrf_gpio_cfg_output(DR_PIEZO_MUX_SEL2); - nrf_gpio_cfg_output(DR_PIEZO_MUX_SEL3); + nrf_gpio_cfg_output(DR_PIEZO_MUX_SEL3);*/ + + /*nrf_gpio_cfg_output(DR_PIEZO_EN_MUXA); + nrf_gpio_cfg_output(DR_PIEZO_EN_MUXB); + nrf_gpio_cfg_output(DR_PIEZO_MUX_SEL0); + nrf_gpio_cfg_output(DR_PIEZO_MUX_SEL1);*/ + + /* Configure pins as output with high drive strength */ + nrf_gpio_cfg( + DR_PIEZO_MUX_SEL0, + NRF_GPIO_PIN_DIR_OUTPUT, + NRF_GPIO_PIN_INPUT_DISCONNECT, + NRF_GPIO_PIN_NOPULL, + NRF_GPIO_PIN_H0H1, /* High drive */ + NRF_GPIO_PIN_NOSENSE + ); + nrf_gpio_cfg( + DR_PIEZO_MUX_SEL1, + NRF_GPIO_PIN_DIR_OUTPUT, + NRF_GPIO_PIN_INPUT_DISCONNECT, + NRF_GPIO_PIN_NOPULL, + NRF_GPIO_PIN_H0H1, /* High drive */ + NRF_GPIO_PIN_NOSENSE + ); + + /* GPIO PIN Setting jhChun 0129 */ + nrf_gpio_cfg( + DR_PIEZO_EN_MUXA, // PIN + NRF_GPIO_PIN_DIR_OUTPUT, // DIR : OUTPUT + NRF_GPIO_PIN_INPUT_DISCONNECT, // INPUT BUFFER X (DIR : OUTPUT) + NRF_GPIO_PIN_NOPULL, // PULL UP, PULL DOWN X + NRF_GPIO_PIN_H0H1, // HIGH DRIVE(STRONG OUTPUT) !! + NRF_GPIO_PIN_NOSENSE // INTERRUPT X + ); + + nrf_gpio_cfg( + DR_PIEZO_EN_MUXB, + NRF_GPIO_PIN_DIR_OUTPUT, + NRF_GPIO_PIN_INPUT_DISCONNECT, + NRF_GPIO_PIN_NOPULL, + NRF_GPIO_PIN_H0H1, + NRF_GPIO_PIN_NOSENSE + ); /* Set MUX selection: P1.13=HIGH, P1.12=LOW, P1.11=LOW */ - nrf_gpio_pin_set(DR_PIEZO_MUX_SEL1); /* P1.13 = HIGH */ - nrf_gpio_pin_clear(DR_PIEZO_MUX_SEL2); /* P1.12 = LOW */ - nrf_gpio_pin_clear(DR_PIEZO_MUX_SEL3); /* P1.11 = LOW */ + //nrf_gpio_pin_set(DR_PIEZO_MUX_SEL1); /* P1.13 = HIGH */ + //nrf_gpio_pin_clear(DR_PIEZO_MUX_SEL2); /* P1.12 = LOW */ + //nrf_gpio_pin_clear(DR_PIEZO_MUX_SEL3); /* P1.11 = LOW */ - DBG_PRINTF("[DR_PIEZO] MUX init: SEL1=HIGH, SEL2=LOW, SEL3=LOW\r\n"); + nrf_gpio_pin_clear(DR_PIEZO_EN_MUXA); /* P0.21 = LOW, Select Channel -> HIGH */ + nrf_gpio_pin_clear(DR_PIEZO_EN_MUXB); /* P0.23 = LOW, Select Channel -> HIGH */ + nrf_gpio_pin_clear(DR_PIEZO_MUX_SEL0); /* P1.10 = LOW */ + nrf_gpio_pin_clear(DR_PIEZO_MUX_SEL1); /* P0.28 = LOW */ + + DBG_PRINTF("[DR_PIEZO] MUX init done\r\n"); +} + + +void dr_piezo_select_channel(uint8_t channel) +{ + channel = channel & 0x07; /* Mask to 0-7 */ + + switch (channel) { + // EN_A EN_B SEL0 SEL1 + case 0: // A0: 1 0 0 0 + nrf_gpio_pin_clear(DR_PIEZO_EN_MUXB); nrf_gpio_pin_set(DR_PIEZO_EN_MUXA); + nrf_gpio_pin_clear(DR_PIEZO_MUX_SEL0); nrf_gpio_pin_clear(DR_PIEZO_MUX_SEL1); + break; + case 1: // A2: 1 0 1 0 + nrf_gpio_pin_clear(DR_PIEZO_EN_MUXB); nrf_gpio_pin_set(DR_PIEZO_EN_MUXA); + nrf_gpio_pin_set(DR_PIEZO_MUX_SEL0); nrf_gpio_pin_clear(DR_PIEZO_MUX_SEL1); + break; + case 2: // A1: 1 0 0 1 + nrf_gpio_pin_clear(DR_PIEZO_EN_MUXB); nrf_gpio_pin_set(DR_PIEZO_EN_MUXA); + nrf_gpio_pin_clear(DR_PIEZO_MUX_SEL0); nrf_gpio_pin_set(DR_PIEZO_MUX_SEL1); + break; + case 3: // A3: 1 0 1 1 + nrf_gpio_pin_clear(DR_PIEZO_EN_MUXB); nrf_gpio_pin_set(DR_PIEZO_EN_MUXA); + nrf_gpio_pin_set(DR_PIEZO_MUX_SEL0); nrf_gpio_pin_set(DR_PIEZO_MUX_SEL1); + break; + case 4: // B0: 0 1 1 1 + nrf_gpio_pin_clear(DR_PIEZO_EN_MUXA); nrf_gpio_pin_set(DR_PIEZO_EN_MUXB); + nrf_gpio_pin_set(DR_PIEZO_MUX_SEL0); nrf_gpio_pin_set(DR_PIEZO_MUX_SEL1); + break; + case 5: // B1: 0 1 1 0 + nrf_gpio_pin_clear(DR_PIEZO_EN_MUXA); nrf_gpio_pin_set(DR_PIEZO_EN_MUXB); + nrf_gpio_pin_clear(DR_PIEZO_MUX_SEL0); nrf_gpio_pin_set(DR_PIEZO_MUX_SEL1); + break; + case 6: // B2: 0 1 0 1 + nrf_gpio_pin_clear(DR_PIEZO_EN_MUXA); nrf_gpio_pin_set(DR_PIEZO_EN_MUXB); + nrf_gpio_pin_set(DR_PIEZO_MUX_SEL0); nrf_gpio_pin_clear(DR_PIEZO_MUX_SEL1); + break; + case 7: // B3: 0 1 0 0 + nrf_gpio_pin_clear(DR_PIEZO_EN_MUXA); nrf_gpio_pin_set(DR_PIEZO_EN_MUXB); + nrf_gpio_pin_clear(DR_PIEZO_MUX_SEL0); nrf_gpio_pin_clear(DR_PIEZO_MUX_SEL1); + break; + } + + /* Delay for MUX settling (analog path stabilization) */ + nrf_delay_us(DR_PIEZO_MUX_SETTLING_US); } void dr_piezo_test_pins(void) @@ -599,32 +683,33 @@ void dr_piezo_transmit(uint8_t cycles) * but it will also shorten their lifespan * - Charles KWON */ - -#define P_OUT_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_P_OUT)) /* P1.03 */ // Save your li -#define N_OUT_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_N_OUT)) /* P1.02 */ -#define PE_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_PE)) /* P1.05 */ -#define DMP_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_DMP)) /* P1.09 */ -/* Piezo channel select pins - MUST BE PRESERVED during burst */ -#define CH_SEL0_MASK (1UL << 11) /* P1.11 - Channel select LSB */ -#define CH_SEL1_MASK (1UL << 12) /* P1.12 - Channel select MSB */ -#define CH_SEL_MASK (CH_SEL0_MASK | CH_SEL1_MASK) +#define P_OUT_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_P_OUT)) /* P1.03 -> P1.07 */ +#define N_OUT_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_N_OUT)) /* P1.02 -> P1.06 */ +#define PE_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_PE)) /* P1.05 -> P0.25 */ +#define DMP_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_DMP)) /* P1.09 -> P1.00 */ /* Combined mask for all piezo control signals (excluding channel select) */ -#define PIEZO_CTRL_MASK (P_OUT_MASK | N_OUT_MASK | PE_MASK | DMP_MASK) +#define P1_CTRL_MASK (P_OUT_MASK | N_OUT_MASK | DMP_MASK) void dr_piezo_burst_sw(uint8_t cycles) { /* Clamp cycles to valid range (1-20) */ if (cycles < 1) cycles = 1; if (cycles > 20) cycles = 20; - + + /* RTT: snapshot then single print */ + uint32_t _d0 = NRF_P1->OUT; + /* Disable GPIOTE hardware control to prevent conflicts */ nrf_gpiote_task_disable(GPIOTE_CH_P_OUT); nrf_gpiote_task_disable(GPIOTE_CH_N_OUT); - + + uint32_t _d1 = NRF_P1->OUT; + /* Save ENTIRE P1 port state BEFORE any GPIO config */ uint32_t saved_p1_out = NRF_P1->OUT; + uint32_t saved_p0_out = NRF_P0->OUT; /* Configure all signal pins as outputs */ nrf_gpio_cfg_output(DR_PIEZO_PIN_PE); @@ -632,30 +717,26 @@ void dr_piezo_burst_sw(uint8_t cycles) nrf_gpio_cfg_output(DR_PIEZO_PIN_P_OUT); nrf_gpio_cfg_output(DR_PIEZO_PIN_N_OUT); + uint32_t _d2 = NRF_P1->OUT; + + DBG_PRINTF("[B]S0:%u%u%u\r\n", (_d0>>10)&1, (_d1>>10)&1, (_d2>>10)&1); + /* Restore P1 port state (nrf_gpio_cfg_output may clear bits) */ NRF_P1->OUT = saved_p1_out; - + NRF_P0->OUT = (NRF_P0->OUT & ~PE_MASK) | (saved_p0_out & PE_MASK); + /*-------------------------------------------------------------------------- * Pre-calculate all output states for fast switching * Each state represents a specific combination of pin levels * IMPORTANT: Channel select pins (P1.11, P1.12) are preserved in all states *------------------------------------------------------------------------*/ - uint32_t ch_sel_state = saved_p1_out & CH_SEL_MASK; - /* State 1: All control signals LOW (idle state) - preserve CH_SEL and other pins */ - uint32_t state_all_low = (saved_p1_out & ~PIEZO_CTRL_MASK); + // PE는 OUTSET/OUTCLR 사용 (다른 P0 핀 영향 방지) - /* State 2: Only PE HIGH (margin periods before pulses and after DMP) */ - uint32_t state_PE_only = (state_all_low | PE_MASK); - - /* State 3: PE=HIGH, P_OUT=HIGH, N_OUT=LOW (first half of each cycle) */ - uint32_t state_PE_P_high_N_low = (state_all_low | PE_MASK | P_OUT_MASK); - - /* State 4: PE=HIGH, P_OUT=LOW, N_OUT=HIGH (second half of each cycle) */ - uint32_t state_PE_P_low_N_high = (state_all_low | PE_MASK | N_OUT_MASK); - - /* State 5: PE=HIGH, DMP=HIGH, P_OUT=LOW, N_OUT=LOW (dump period) */ - uint32_t state_PE_DMP_high = (state_all_low | PE_MASK | DMP_MASK); + uint32_t p1_all_low = saved_p1_out & ~P1_CTRL_MASK; + uint32_t p1_P_high_N_low = p1_all_low | P_OUT_MASK; + uint32_t p1_P_low_N_high = p1_all_low | N_OUT_MASK; + uint32_t p1_DMP_high = p1_all_low | DMP_MASK; /*-------------------------------------------------------------------------- * Critical timing section - interrupts disabled @@ -663,34 +744,36 @@ void dr_piezo_burst_sw(uint8_t cycles) __disable_irq(); /* Initialize: Set all signals to LOW */ - NRF_P1->OUT = state_all_low; - + NRF_P0->OUTCLR = PE_MASK; // PE OFF (OUTCLR로 다른 핀 영향 없음) + NRF_P1->OUT = p1_all_low; + /* PE rises first with margin before pulses start */ - NRF_P1->OUT = state_PE_only; + NRF_P0->OUTSET = PE_MASK; // PE ON (OUTSET로 다른 핀 영향 없음) + + __NOP(); __NOP(); __NOP(); /* ~47ns margin */ + __NOP(); __NOP(); __NOP(); /* ~47ns margin */ + __NOP(); __NOP(); __NOP(); /* ~47ns margin */ + __NOP(); __NOP(); __NOP(); /* ~47ns margin */ + __NOP(); __NOP(); __NOP(); /* ~47ns margin */ + __NOP(); __NOP(); __NOP(); /* ~47ns margin */ + __NOP(); __NOP(); __NOP(); /* ~47ns margin */ + + __NOP(); __NOP(); __NOP(); /* ~47ns margin */ + __NOP(); __NOP(); __NOP(); /* ~47ns margin */ + __NOP(); __NOP(); __NOP(); /* ~47ns margin */ + __NOP(); __NOP(); __NOP(); /* ~47ns margin */ + __NOP(); __NOP(); __NOP(); /* ~47ns margin */ + __NOP(); __NOP(); __NOP(); /* ~47ns margin */ + __NOP(); __NOP(); __NOP(); /* ~47ns margin */ + + + __NOP(); __NOP(); __NOP(); /* ~47ns margin */ + __NOP(); __NOP(); __NOP(); /* ~47ns margin */ + __NOP(); __NOP(); __NOP(); /* ~47ns margin */ + __NOP(); __NOP(); __NOP(); /* ~47ns margin */ + __NOP(); __NOP(); __NOP(); /* ~47ns margin */ + __NOP(); __NOP(); __NOP(); /* ~47ns margin */ __NOP(); __NOP(); __NOP(); /* ~47ns margin */ - __NOP(); __NOP(); __NOP(); /* ~47ns margin */ - __NOP(); __NOP(); __NOP(); /* ~47ns margin */ - __NOP(); __NOP(); __NOP(); /* ~47ns margin */ - __NOP(); __NOP(); __NOP(); /* ~47ns margin */ - __NOP(); __NOP(); __NOP(); /* ~47ns margin */ - __NOP(); __NOP(); __NOP(); /* ~47ns margin */ - - __NOP(); __NOP(); __NOP(); /* ~47ns margin */ - __NOP(); __NOP(); __NOP(); /* ~47ns margin */ - __NOP(); __NOP(); __NOP(); /* ~47ns margin */ - __NOP(); __NOP(); __NOP(); /* ~47ns margin */ - __NOP(); __NOP(); __NOP(); /* ~47ns margin */ - __NOP(); __NOP(); __NOP(); /* ~47ns margin */ - __NOP(); __NOP(); __NOP(); /* ~47ns margin */ - - - __NOP(); __NOP(); __NOP(); /* ~47ns margin */ - __NOP(); __NOP(); __NOP(); /* ~47ns margin */ - __NOP(); __NOP(); __NOP(); /* ~47ns margin */ - __NOP(); __NOP(); __NOP(); /* ~47ns margin */ - __NOP(); __NOP(); __NOP(); /* ~47ns margin */ - __NOP(); __NOP(); __NOP(); /* ~47ns margin */ - __NOP(); __NOP(); __NOP(); /* ~47ns margin */ @@ -706,15 +789,17 @@ void dr_piezo_burst_sw(uint8_t cycles) for (uint8_t i = 0; i < cycles; i++) { /* First half-period: P_OUT=HIGH, N_OUT=LOW */ - NRF_P1->OUT = state_PE_P_high_N_low; - __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); + NRF_P1->OUT = p1_P_high_N_low; + __NOP(); __NOP(); __NOP(); __NOP(); __NOP();__NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); + /* Second half-period: P_OUT=LOW, N_OUT=HIGH */ - NRF_P1->OUT = state_PE_P_low_N_high; - __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); + NRF_P1->OUT = p1_P_low_N_high; __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); __NOP(); + } /*-------------------------------------------------------------------------- @@ -722,7 +807,8 @@ void dr_piezo_burst_sw(uint8_t cycles) * - Starts simultaneously with N_OUT falling edge * - Duration: ~500ns (32 NOPs) *------------------------------------------------------------------------*/ - NRF_P1->OUT = state_PE_DMP_high; + + NRF_P1->OUT = p1_DMP_high; __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); @@ -734,12 +820,11 @@ void dr_piezo_burst_sw(uint8_t cycles) __NOP(); __NOP(); __NOP(); __NOP(); /* DMP falls, PE remains HIGH for margin period */ - NRF_P1->OUT = state_PE_only; + NRF_P1->OUT = p1_all_low; __NOP(); __NOP(); __NOP(); /* ~47ns margin */ - - /* End of sequence: All signals return to LOW */ - NRF_P1->OUT = state_all_low; - + + NRF_P0->OUTCLR = PE_MASK; // PE OFF + __enable_irq(); } @@ -763,8 +848,9 @@ void dr_piezo_burst_sw_18mhz(uint8_t cycles) nrf_gpiote_task_disable(GPIOTE_CH_P_OUT); nrf_gpiote_task_disable(GPIOTE_CH_N_OUT); - /* Save ENTIRE P1 port state BEFORE any GPIO config */ + /* Save port states BEFORE any GPIO config */ uint32_t saved_p1_out = NRF_P1->OUT; + uint32_t saved_p0_out = NRF_P0->OUT; /* Configure all signal pins as outputs */ nrf_gpio_cfg_output(DR_PIEZO_PIN_PE); @@ -772,24 +858,26 @@ void dr_piezo_burst_sw_18mhz(uint8_t cycles) nrf_gpio_cfg_output(DR_PIEZO_PIN_P_OUT); nrf_gpio_cfg_output(DR_PIEZO_PIN_N_OUT); - /* Restore P1 port state (nrf_gpio_cfg_output may clear bits) */ + /* Restore port states (nrf_gpio_cfg_output may clear bits) */ NRF_P1->OUT = saved_p1_out; + NRF_P0->OUT = (NRF_P0->OUT & ~PE_MASK) | (saved_p0_out & PE_MASK); - /* Pre-calculate all output states for fast switching - * IMPORTANT: Channel select pins (P1.11, P1.12) are preserved in all states */ - uint32_t state_all_low = (saved_p1_out & ~PIEZO_CTRL_MASK); - uint32_t state_PE_only = (state_all_low | PE_MASK); - uint32_t state_PE_P_high_N_low = (state_all_low | PE_MASK | P_OUT_MASK); - uint32_t state_PE_P_low_N_high = (state_all_low | PE_MASK | N_OUT_MASK); - uint32_t state_PE_DMP_high = (state_all_low | PE_MASK | DMP_MASK); + //NRF_P0->OUT = saved_p0_out; + + /* Pre-calculate P1 output states (PE는 P0.25 - OUTSET/OUTCLR로 제어) */ + uint32_t p1_all_low = saved_p1_out & ~P1_CTRL_MASK; + uint32_t p1_P_high_N_low = p1_all_low | P_OUT_MASK; + uint32_t p1_P_low_N_high = p1_all_low | N_OUT_MASK; + uint32_t p1_DMP_high = p1_all_low | DMP_MASK; __disable_irq(); /* Initialize: Set all signals to LOW */ - NRF_P1->OUT = state_all_low; + NRF_P0->OUTCLR = PE_MASK; + NRF_P1->OUT = p1_all_low; /* PE rises first with margin before pulses start */ - NRF_P1->OUT = state_PE_only; + NRF_P0->OUTSET = PE_MASK; __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); @@ -826,20 +914,21 @@ void dr_piezo_burst_sw_18mhz(uint8_t cycles) for (uint8_t i = 0; i < cycles; i++) { /* First half-period: P_OUT=HIGH, N_OUT=LOW */ - NRF_P1->OUT = state_PE_P_high_N_low; + NRF_P1->OUT = p1_P_high_N_low; __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); - - /* Second half-period: P_OUT=LOW, N_OUT=HIGH */ - NRF_P1->OUT = state_PE_P_low_N_high; - __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); - __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); + + /* Second half-period: P_OUT=LOW, N_OUT=HIGH */ + NRF_P1->OUT = p1_P_low_N_high; + __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); + __NOP(); } /* DMP (Dump) pulse */ - NRF_P1->OUT = state_PE_DMP_high; + NRF_P1->OUT = p1_DMP_high; __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); @@ -851,11 +940,11 @@ void dr_piezo_burst_sw_18mhz(uint8_t cycles) __NOP(); __NOP(); __NOP(); __NOP(); /* DMP falls, PE remains HIGH for margin period */ - NRF_P1->OUT = state_PE_only; + NRF_P1->OUT = p1_all_low; __NOP(); __NOP(); __NOP(); - /* End of sequence: All signals return to LOW */ - NRF_P1->OUT = state_all_low; + /* End of sequence: PE OFF */ + NRF_P0->OUTCLR = PE_MASK; __enable_irq(); } @@ -881,8 +970,9 @@ void dr_piezo_burst_sw_20mhz(uint8_t cycles) nrf_gpiote_task_disable(GPIOTE_CH_P_OUT); nrf_gpiote_task_disable(GPIOTE_CH_N_OUT); - /* Save ENTIRE P1 port state BEFORE any GPIO config */ + /* Save port states BEFORE any GPIO config */ uint32_t saved_p1_out = NRF_P1->OUT; + uint32_t saved_p0_out = NRF_P0->OUT; /* Configure all signal pins as outputs */ nrf_gpio_cfg_output(DR_PIEZO_PIN_PE); @@ -890,24 +980,25 @@ void dr_piezo_burst_sw_20mhz(uint8_t cycles) nrf_gpio_cfg_output(DR_PIEZO_PIN_P_OUT); nrf_gpio_cfg_output(DR_PIEZO_PIN_N_OUT); - /* Restore P1 port state (nrf_gpio_cfg_output may clear bits) */ + /* Restore port states (nrf_gpio_cfg_output may clear bits) */ NRF_P1->OUT = saved_p1_out; + //NRF_P0->OUT = saved_p0_out; + NRF_P0->OUT = (NRF_P0->OUT & ~PE_MASK) | (saved_p0_out & PE_MASK); - /* Pre-calculate all output states for fast switching - * IMPORTANT: Channel select pins (P1.11, P1.12) are preserved in all states */ - uint32_t state_all_low = (saved_p1_out & ~PIEZO_CTRL_MASK); - uint32_t state_PE_only = (state_all_low | PE_MASK); - uint32_t state_PE_P_high_N_low = (state_all_low | PE_MASK | P_OUT_MASK); - uint32_t state_PE_P_low_N_high = (state_all_low | PE_MASK | N_OUT_MASK); - uint32_t state_PE_DMP_high = (state_all_low | PE_MASK | DMP_MASK); + /* Pre-calculate P1 output states (PE는 P0.25 - OUTSET/OUTCLR로 제어) */ + uint32_t p1_all_low = saved_p1_out & ~P1_CTRL_MASK; + uint32_t p1_P_high_N_low = p1_all_low | P_OUT_MASK; + uint32_t p1_P_low_N_high = p1_all_low | N_OUT_MASK; + uint32_t p1_DMP_high = p1_all_low | DMP_MASK; __disable_irq(); /* Initialize: Set all signals to LOW */ - NRF_P1->OUT = state_all_low; + NRF_P0->OUTCLR = PE_MASK; + NRF_P1->OUT = p1_all_low; /* PE rises first with margin before pulses start */ - NRF_P1->OUT = state_PE_only; + NRF_P0->OUTSET = PE_MASK; __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); @@ -944,20 +1035,21 @@ void dr_piezo_burst_sw_20mhz(uint8_t cycles) for (uint8_t i = 0; i < cycles; i++) { /* First half-period: P_OUT=HIGH, N_OUT=LOW */ - NRF_P1->OUT = state_PE_P_high_N_low; + NRF_P1->OUT = p1_P_high_N_low; __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); + __NOP(); /* Second half-period: P_OUT=LOW, N_OUT=HIGH */ - NRF_P1->OUT = state_PE_P_low_N_high; + NRF_P1->OUT = p1_P_low_N_high; __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); - __NOP(); + } /* DMP (Dump) pulse */ - NRF_P1->OUT = state_PE_DMP_high; + NRF_P1->OUT = p1_DMP_high; __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); @@ -969,11 +1061,257 @@ void dr_piezo_burst_sw_20mhz(uint8_t cycles) __NOP(); __NOP(); __NOP(); __NOP(); /* DMP falls, PE remains HIGH for margin period */ - NRF_P1->OUT = state_PE_only; + NRF_P1->OUT = p1_all_low; __NOP(); __NOP(); __NOP(); - /* End of sequence: All signals return to LOW */ - NRF_P1->OUT = state_all_low; + /* End of sequence: PE OFF */ + NRF_P0->OUTCLR = PE_MASK; + + __enable_irq(); +} + + + +/** + * @brief Software-based burst at 2.0 MHz + * @param cycles Number of cycles (1~20) + * + * Timing: + * 1.9 MHz: 500ns period, 250ns half-period + * At 64MHz CPU (1 NOP = 15.625ns): + * First half: 14 NOPs (~219ns) + register write (~30ns) = ~249ns + * Second half: 11 NOPs (~172ns) + loop overhead (~47ns) = ~219ns + * Total: ~468-500ns per cycle + */ +void dr_piezo_burst_sw_19mhz(uint8_t cycles) +{ + /* Clamp cycles to valid range (1-20) */ + if (cycles < 1) cycles = 1; + if (cycles > 20) cycles = 20; + + /* Disable GPIOTE hardware control to prevent conflicts */ + nrf_gpiote_task_disable(GPIOTE_CH_P_OUT); + nrf_gpiote_task_disable(GPIOTE_CH_N_OUT); + + /* Save port states BEFORE any GPIO config */ + uint32_t saved_p1_out = NRF_P1->OUT; + uint32_t saved_p0_out = NRF_P0->OUT; + + /* Configure all signal pins as outputs */ + nrf_gpio_cfg_output(DR_PIEZO_PIN_PE); + nrf_gpio_cfg_output(DR_PIEZO_PIN_DMP); + nrf_gpio_cfg_output(DR_PIEZO_PIN_P_OUT); + nrf_gpio_cfg_output(DR_PIEZO_PIN_N_OUT); + + /* Restore port states (nrf_gpio_cfg_output may clear bits) */ + NRF_P1->OUT = saved_p1_out; + //NRF_P0->OUT = saved_p0_out; + NRF_P0->OUT = (NRF_P0->OUT & ~PE_MASK) | (saved_p0_out & PE_MASK); + + /* Pre-calculate P1 output states (PE는 P0.25 - OUTSET/OUTCLR로 제어) */ + uint32_t p1_all_low = saved_p1_out & ~P1_CTRL_MASK; + uint32_t p1_P_high_N_low = p1_all_low | P_OUT_MASK; + uint32_t p1_P_low_N_high = p1_all_low | N_OUT_MASK; + uint32_t p1_DMP_high = p1_all_low | DMP_MASK; + + __disable_irq(); + + /* Initialize: Set all signals to LOW */ + NRF_P0->OUTCLR = PE_MASK; + NRF_P1->OUT = p1_all_low; + + /* PE rises first with margin before pulses start */ + NRF_P0->OUTSET = PE_MASK; + __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); + + __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); + + __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); + + /*-------------------------------------------------------------------------- + * Generate 2.0MHz pulse burst + * + * 2.0 MHz: 500ns period, 250ns half-period + * At 64MHz CPU (1 NOP = 15.625ns): + * First half: 14 NOPs (~219ns) + register write (~30ns) = ~249ns + * Second half: 11 NOPs (~172ns) + loop overhead (~47ns) = ~219ns + * Total: ~468-500ns per cycle + *------------------------------------------------------------------------*/ + for (uint8_t i = 0; i < cycles; i++) + { + /* First half-period: P_OUT=HIGH, N_OUT=LOW */ + NRF_P1->OUT = p1_P_high_N_low; + __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); __NOP(); + __NOP(); + + /* Second half-period: P_OUT=LOW, N_OUT=HIGH */ + NRF_P1->OUT = p1_P_low_N_high; + __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); __NOP(); + + } + + /* DMP (Dump) pulse */ + NRF_P1->OUT = p1_DMP_high; + + __NOP(); __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); __NOP(); + + /* DMP falls, PE remains HIGH for margin period */ + NRF_P1->OUT = p1_all_low; + __NOP(); __NOP(); __NOP(); + + /* End of sequence: PE OFF */ + NRF_P0->OUTCLR = PE_MASK; + + __enable_irq(); +} + + + + + +/** + * @brief Software-based burst at 2.2 MHz + * @param cycles Number of cycles (1~20) + * + * Timing: + * 2.2 MHz: 454ns period, 227ns half-period + * At 64MHz CPU (1 NOP = 15.625ns): + * First half: 13 NOPs (~203ns) + register write (~30ns) = ~233ns + * Second half: 11 NOPs (~172ns) + loop overhead (~47ns) = ~219ns + * Total: ~452ns per cycle (~2.21 MHz) + */ +void dr_piezo_burst_sw_22mhz(uint8_t cycles) +{ + /* Clamp cycles to valid range (1-20) */ + if (cycles < 1) cycles = 1; + if (cycles > 20) cycles = 20; + + /* Disable GPIOTE hardware control to prevent conflicts */ + nrf_gpiote_task_disable(GPIOTE_CH_P_OUT); + nrf_gpiote_task_disable(GPIOTE_CH_N_OUT); + + /* Save port states BEFORE any GPIO config */ + uint32_t saved_p1_out = NRF_P1->OUT; + uint32_t saved_p0_out = NRF_P0->OUT; + + /* Configure all signal pins as outputs */ + nrf_gpio_cfg_output(DR_PIEZO_PIN_PE); + nrf_gpio_cfg_output(DR_PIEZO_PIN_DMP); + nrf_gpio_cfg_output(DR_PIEZO_PIN_P_OUT); + nrf_gpio_cfg_output(DR_PIEZO_PIN_N_OUT); + + /* Restore port states (nrf_gpio_cfg_output may clear bits) */ + NRF_P1->OUT = saved_p1_out; + NRF_P0->OUT = (NRF_P0->OUT & ~PE_MASK) | (saved_p0_out & PE_MASK); + + /* Pre-calculate P1 output states */ + uint32_t p1_all_low = saved_p1_out & ~P1_CTRL_MASK; + uint32_t p1_P_high_N_low = p1_all_low | P_OUT_MASK; + uint32_t p1_P_low_N_high = p1_all_low | N_OUT_MASK; + uint32_t p1_DMP_high = p1_all_low | DMP_MASK; + + __disable_irq(); + + /* Initialize: Set all signals to LOW */ + NRF_P0->OUTCLR = PE_MASK; + NRF_P1->OUT = p1_all_low; + + /* PE rises first with margin before pulses start */ + NRF_P0->OUTSET = PE_MASK; + __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); + + __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); + + __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); + + /*-------------------------------------------------------------------------- + * Generate 2.2MHz pulse burst + * + * 2.2 MHz: 454ns period, 227ns half-period + * At 64MHz CPU (1 NOP = 15.625ns): + * First half: 11 NOPs - positive pulse width + * Second half: 10 NOPs - negative pulse width + * Total: 21 NOPs per cycle (~2.2 MHz) + *------------------------------------------------------------------------*/ + for (uint8_t i = 0; i < cycles; i++) + { + /* First half-period: P_OUT=HIGH, N_OUT=LOW (positive pulse width) */ + NRF_P1->OUT = p1_P_high_N_low; + __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); + __NOP(); + + /* Second half-period: P_OUT=LOW, N_OUT=HIGH (negative pulse width) */ + NRF_P1->OUT = p1_P_low_N_high; + __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); + } + + /* DMP (Dump) pulse */ + NRF_P1->OUT = p1_DMP_high; + + __NOP(); __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); __NOP(); + + /* DMP falls, PE remains HIGH for margin period */ + NRF_P1->OUT = p1_all_low; + __NOP(); __NOP(); __NOP(); + + /* End of sequence: PE OFF */ + NRF_P0->OUTCLR = PE_MASK; __enable_irq(); } @@ -999,8 +1337,9 @@ void dr_piezo_burst_sw_17mhz(uint8_t cycles) nrf_gpiote_task_disable(GPIOTE_CH_P_OUT); nrf_gpiote_task_disable(GPIOTE_CH_N_OUT); - /* Save ENTIRE P1 port state BEFORE any GPIO config */ + /* Save port states BEFORE any GPIO config */ uint32_t saved_p1_out = NRF_P1->OUT; + uint32_t saved_p0_out = NRF_P0->OUT; /* Configure all signal pins as outputs */ nrf_gpio_cfg_output(DR_PIEZO_PIN_PE); @@ -1008,24 +1347,24 @@ void dr_piezo_burst_sw_17mhz(uint8_t cycles) nrf_gpio_cfg_output(DR_PIEZO_PIN_P_OUT); nrf_gpio_cfg_output(DR_PIEZO_PIN_N_OUT); - /* Restore P1 port state (nrf_gpio_cfg_output may clear bits) */ + /* Restore port states (nrf_gpio_cfg_output may clear bits) */ NRF_P1->OUT = saved_p1_out; + NRF_P0->OUT = (NRF_P0->OUT & ~PE_MASK) | (saved_p0_out & PE_MASK); - /* Pre-calculate all output states for fast switching - * IMPORTANT: Channel select pins (P1.11, P1.12) are preserved in all states */ - uint32_t state_all_low = (saved_p1_out & ~PIEZO_CTRL_MASK); - uint32_t state_PE_only = (state_all_low | PE_MASK); - uint32_t state_PE_P_high_N_low = (state_all_low | PE_MASK | P_OUT_MASK); - uint32_t state_PE_P_low_N_high = (state_all_low | PE_MASK | N_OUT_MASK); - uint32_t state_PE_DMP_high = (state_all_low | PE_MASK | DMP_MASK); + /* Pre-calculate P1 output states (PE는 P0에서 OUTSET/OUTCLR로 제어) */ + uint32_t p1_all_low = saved_p1_out & ~P1_CTRL_MASK; + uint32_t p1_P_high_N_low = p1_all_low | P_OUT_MASK; + uint32_t p1_P_low_N_high = p1_all_low | N_OUT_MASK; + uint32_t p1_DMP_high = p1_all_low | DMP_MASK; __disable_irq(); /* Initialize: Set all signals to LOW */ - NRF_P1->OUT = state_all_low; + NRF_P0->OUTCLR = PE_MASK; // PE OFF + NRF_P1->OUT = p1_all_low; /* PE rises first with margin before pulses start */ - NRF_P1->OUT = state_PE_only; + NRF_P0->OUTSET = PE_MASK; // PE ON __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); @@ -1062,21 +1401,23 @@ void dr_piezo_burst_sw_17mhz(uint8_t cycles) for (uint8_t i = 0; i < cycles; i++) { /* First half-period: P_OUT=HIGH, N_OUT=LOW */ - NRF_P1->OUT = state_PE_P_high_N_low; + NRF_P1->OUT = p1_P_high_N_low; __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); + __NOP(); + /* Second half-period: P_OUT=LOW, N_OUT=HIGH */ - NRF_P1->OUT = state_PE_P_low_N_high; + NRF_P1->OUT = p1_P_low_N_high; __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); - __NOP(); __NOP(); __NOP(); __NOP(); + //__NOP(); __NOP(); __NOP(); __NOP(); } /* DMP (Dump) pulse */ - NRF_P1->OUT = state_PE_DMP_high; + NRF_P1->OUT = p1_DMP_high; __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); @@ -1088,11 +1429,11 @@ void dr_piezo_burst_sw_17mhz(uint8_t cycles) __NOP(); __NOP(); __NOP(); __NOP(); /* DMP falls, PE remains HIGH for margin period */ - NRF_P1->OUT = state_PE_only; + NRF_P1->OUT = p1_all_low; __NOP(); __NOP(); __NOP(); - /* End of sequence: All signals return to LOW */ - NRF_P1->OUT = state_all_low; + /* End of sequence: PE OFF */ + NRF_P0->OUTCLR = PE_MASK; // PE OFF __enable_irq(); } diff --git a/project/ble_peripheral/dr_piezo/dr_piezo.h b/project/ble_peripheral/dr_piezo/dr_piezo.h index 92b653b..aa7818d 100644 --- a/project/ble_peripheral/dr_piezo/dr_piezo.h +++ b/project/ble_peripheral/dr_piezo/dr_piezo.h @@ -27,23 +27,36 @@ /*============================================================================== * POWER CONTROL PINS (DC/DC Converter +/-20V) *============================================================================*/ -#define DR_PIEZO_PWR_SHDN NRF_GPIO_PIN_MAP(0, 21) /**< SHDN_VPP/VNN (LT3463) */ -#define DR_PIEZO_PWR_EN_10V NRF_GPIO_PIN_MAP(0, 22) /**< EN_+10V (MCP1804) */ +//#define DR_PIEZO_PWR_SHDN NRF_GPIO_PIN_MAP(0, 21) /**< SHDN_VPP/VNN (LT3463) */ // P0.21 : PZT_EN_MUXA jhChun 0128 +//#define DR_PIEZO_PWR_EN_10V NRF_GPIO_PIN_MAP(0, 22) /**< EN_+10V (MCP1804) */ // P0.22 : NIRS PIN + +#define DR_PIEZO_PWR_EN NRF_GPIO_PIN_MAP(1, 9) /** Power Enable jhChun 0128 */ /*============================================================================== * TX SIGNAL PINS (MOSFET Driver Control) *============================================================================*/ -#define DR_PIEZO_PIN_PE NRF_GPIO_PIN_MAP(1, 5) /**< Pulse Enable */ -#define DR_PIEZO_PIN_DMP NRF_GPIO_PIN_MAP(1, 9) /**< Dump control */ -#define DR_PIEZO_PIN_P_OUT NRF_GPIO_PIN_MAP(1, 3) /**< Positive output */ -#define DR_PIEZO_PIN_N_OUT NRF_GPIO_PIN_MAP(1, 2) /**< Negative output */ +//#define DR_PIEZO_PIN_PE NRF_GPIO_PIN_MAP(1, 5) /**< Pulse Enable */ +//#define DR_PIEZO_PIN_DMP NRF_GPIO_PIN_MAP(1, 9) /**< Dump control */ +//#define DR_PIEZO_PIN_P_OUT NRF_GPIO_PIN_MAP(1, 3) /**< Positive output */ +//#define DR_PIEZO_PIN_N_OUT NRF_GPIO_PIN_MAP(1, 2) /**< Negative output */ +#define DR_PIEZO_PIN_PE NRF_GPIO_PIN_MAP(0, 25) /**< Pulse Enable */ // P1.05 -> P0.25 +#define DR_PIEZO_PIN_DMP NRF_GPIO_PIN_MAP(1, 0) /**< Dump control */ // P1.9 -> P1.0 +#define DR_PIEZO_PIN_P_OUT NRF_GPIO_PIN_MAP(1, 7) /**< Positive output */ // P1.3 -> P1.7 +#define DR_PIEZO_PIN_N_OUT NRF_GPIO_PIN_MAP(1, 6) /**< Negative output */ // P1.2 -> P1.6 jhChun 0128 + /*============================================================================== * MUX CONTROL PINS (Echo Signal Path Selection) *============================================================================*/ -#define DR_PIEZO_MUX_SEL1 NRF_GPIO_PIN_MAP(1, 13) /**< MUX Select 1 (HIGH) */ -#define DR_PIEZO_MUX_SEL2 NRF_GPIO_PIN_MAP(1, 12) /**< MUX Select 2 (LOW) */ -#define DR_PIEZO_MUX_SEL3 NRF_GPIO_PIN_MAP(1, 11) /**< MUX Select 3 (LOW) */ +//#define DR_PIEZO_MUX_SEL1 NRF_GPIO_PIN_MAP(1, 13) /**< MUX Select 1 (HIGH) */ +//#define DR_PIEZO_MUX_SEL2 NRF_GPIO_PIN_MAP(1, 12) /**< MUX Select 2 (LOW) */ +//#define DR_PIEZO_MUX_SEL3 NRF_GPIO_PIN_MAP(1, 11) /**< MUX Select 3 (LOW) */ + +/* Piezo MUX pins (8ch) jhChun 0129 */ +#define DR_PIEZO_EN_MUXA NRF_GPIO_PIN_MAP(0, 21) /**< MUXA Enable */ +#define DR_PIEZO_EN_MUXB NRF_GPIO_PIN_MAP(0, 23) /**< MUXB Enable */ +#define DR_PIEZO_MUX_SEL0 NRF_GPIO_PIN_MAP(1, 10) /**< MUX Select 0 */ +#define DR_PIEZO_MUX_SEL1 NRF_GPIO_PIN_MAP(0, 28) /**< MUX Select 1 */ /*============================================================================== * CONFIGURATION @@ -57,6 +70,7 @@ #define DR_PIEZO_DEFAULT_CYCLES 5 /**< Default burst cycles (3~5) */ #define DR_PIEZO_MIN_CYCLES 3 #define DR_PIEZO_MAX_CYCLES 10 +#define DR_PIEZO_MUX_SETTLING_US 1300 /**< MUX settling delay (us) */ /*============================================================================== * POWER CONTROL FUNCTIONS @@ -126,10 +140,21 @@ void dr_piezo_test_pins(void); /** * @brief Initialize MUX control pins for echo signal path - * @note Sets P1.13=HIGH, P1.12=LOW, P1.11=LOW */ void dr_piezo_mux_init(void); +/** + * @brief Select piezo channel (0~7) via 8ch MUX + * @param channel Piezo channel number (0~7) + * + * Channel mapping (EN_MUXA, EN_MUXB, SEL0, SEL1): + * CH0=A0(1,0,0,0) CH1=A2(1,0,1,0) CH2=A1(1,0,0,1) CH3=A3(1,0,1,1) + * CH4=B0(0,1,1,1) CH5=B1(0,1,0,1) CH6=B2(0,1,1,0) CH7=B3(0,1,0,0) + * + * @note MUX settling time: 1.3ms delay after switching + */ +void dr_piezo_select_channel(uint8_t channel); + /*============================================================================== * SYSTEM FUNCTIONS (Power + TX combined) *============================================================================*/ @@ -171,6 +196,13 @@ void dr_piezo_burst_sw_18mhz(uint8_t cycles); */ void dr_piezo_burst_sw_20mhz(uint8_t cycles); +/** + * @brief Software-based burst at 2.2 MHz + * @param cycles Number of cycles (1~20) + * @note Fixed frequency: 2.2 MHz + */ +void dr_piezo_burst_sw_22mhz(uint8_t cycles); + /** * @brief Software-based burst at 1.7 MHz * @param cycles Number of cycles (1~20) @@ -178,5 +210,12 @@ void dr_piezo_burst_sw_20mhz(uint8_t cycles); */ void dr_piezo_burst_sw_17mhz(uint8_t cycles); +/** + * @brief Software-based burst at 1.9 MHz + * @param cycles Number of cycles (1~20) + * @note Fixed frequency: 1.9 MHz + */ +void dr_piezo_burst_sw_19mhz(uint8_t cycles); + #endif /* DR_PIEZO_H */ diff --git a/project/dfu/secure_bootloader/main.c b/project/dfu/secure_bootloader/main.c index b1545b2..36de500 100644 --- a/project/dfu/secure_bootloader/main.c +++ b/project/dfu/secure_bootloader/main.c @@ -3,7 +3,7 @@ * @author CandyPops Co. * @version V1.0.0 * @date 2022-09-05 - * @brief + * @brief ******************************************************************************/ #include @@ -21,7 +21,7 @@ #include "nrf_bootloader_info.h" #include "nrf_delay.h" -#define POWER_HOLD NRF_GPIO_PIN_MAP(0,8) // power hold port for medithings device +#define POWER_HOLD NRF_GPIO_PIN_MAP(0,8) // power hold port for medithings device static void on_error(void) { @@ -90,7 +90,7 @@ int main(void) nrf_gpio_cfg_output(POWER_HOLD); nrf_gpio_pin_set(POWER_HOLD); // The medithings device performs power hold when the power button is pressed for 3 seconds. // And then, The medithings device loads the bootloader. - + // Must happen before flash protection is applied, since it edits a protected page. nrf_bootloader_mbr_addrs_populate(); diff --git a/project/dfu/secure_bootloader/pca10056_s140_ble/arm5_no_packs/secure_bootloader_ble_s140_pca10056.uvoptx b/project/dfu/secure_bootloader/pca10056_s140_ble/arm5_no_packs/secure_bootloader_ble_s140_pca10056.uvoptx index 67cdafb..c585d50 100644 --- a/project/dfu/secure_bootloader/pca10056_s140_ble/arm5_no_packs/secure_bootloader_ble_s140_pca10056.uvoptx +++ b/project/dfu/secure_bootloader/pca10056_s140_ble/arm5_no_packs/secure_bootloader_ble_s140_pca10056.uvoptx @@ -73,7 +73,7 @@ 0 - 1 + 0 0 1 @@ -336,7 +336,7 @@ Application - 1 + 0 0 0 0 diff --git a/project/dfu/secure_bootloader/pca10056_s140_ble/config/sdk_config.h b/project/dfu/secure_bootloader/pca10056_s140_ble/config/sdk_config.h index f7fac8c..4874bb5 100644 --- a/project/dfu/secure_bootloader/pca10056_s140_ble/config/sdk_config.h +++ b/project/dfu/secure_bootloader/pca10056_s140_ble/config/sdk_config.h @@ -96,68 +96,15 @@ // DFU mode enter method //========================================================== -// NRF_BL_DFU_ENTER_METHOD_BUTTON - Enter DFU mode on button press. -//========================================================== + #ifndef NRF_BL_DFU_ENTER_METHOD_BUTTON -#define NRF_BL_DFU_ENTER_METHOD_BUTTON 1 +#define NRF_BL_DFU_ENTER_METHOD_BUTTON 0 #endif -// NRF_BL_DFU_ENTER_METHOD_BUTTON_PIN - Button for entering DFU mode. - -// <0=> 0 (P0.0) -// <1=> 1 (P0.1) -// <2=> 2 (P0.2) -// <3=> 3 (P0.3) -// <4=> 4 (P0.4) -// <5=> 5 (P0.5) -// <6=> 6 (P0.6) -// <7=> 7 (P0.7) -// <8=> 8 (P0.8) -// <9=> 9 (P0.9) -// <10=> 10 (P0.10) -// <11=> 11 (P0.11) -// <12=> 12 (P0.12) -// <13=> 13 (P0.13) -// <14=> 14 (P0.14) -// <15=> 15 (P0.15) -// <16=> 16 (P0.16) -// <17=> 17 (P0.17) -// <18=> 18 (P0.18) -// <19=> 19 (P0.19) -// <20=> 20 (P0.20) -// <21=> 21 (P0.21) -// <22=> 22 (P0.22) -// <23=> 23 (P0.23) -// <24=> 24 (P0.24) -// <25=> 25 (P0.25) -// <26=> 26 (P0.26) -// <27=> 27 (P0.27) -// <28=> 28 (P0.28) -// <29=> 29 (P0.29) -// <30=> 30 (P0.30) -// <31=> 31 (P0.31) -// <32=> 32 (P1.0) -// <33=> 33 (P1.1) -// <34=> 34 (P1.2) -// <35=> 35 (P1.3) -// <36=> 36 (P1.4) -// <37=> 37 (P1.5) -// <38=> 38 (P1.6) -// <39=> 39 (P1.7) -// <40=> 40 (P1.8) -// <41=> 41 (P1.9) -// <42=> 42 (P1.10) -// <43=> 43 (P1.11) -// <44=> 44 (P1.12) -// <45=> 45 (P1.13) -// <46=> 46 (P1.14) -// <47=> 47 (P1.15) #ifndef NRF_BL_DFU_ENTER_METHOD_BUTTON_PIN #define NRF_BL_DFU_ENTER_METHOD_BUTTON_PIN 25 #endif -// - // NRF_BL_DFU_ENTER_METHOD_PINRESET - Enter DFU mode on pin reset.