From 2c7f3063904abe4530d6ca5e5c9149f7c4013518 Mon Sep 17 00:00:00 2001 From: jhchun Date: Wed, 15 Apr 2026 15:53:46 +0900 Subject: [PATCH] =?UTF-8?q?=ED=94=84=EB=A1=9C=EC=A0=9D=ED=8A=B8=20?= =?UTF-8?q?=EC=A0=95=EB=A6=AC:=20=EC=95=B1=20=EB=82=B4=EB=B6=80=20?= =?UTF-8?q?=EA=B5=AC=EC=A1=B0=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EB=A0=88?= =?UTF-8?q?=EA=B1=B0=EC=8B=9C=20=ED=94=8C=EB=9E=98=EC=8B=9C=20=EC=8A=A4?= =?UTF-8?q?=ED=81=AC=EB=A6=BD=ED=8A=B8=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - command/system/measurement - .bat 파일 삭제(nrfprog 세대, nrfutil로 대체됨) --- pc_firm/.gitignore | 33 - .../ble_app_bladder_patch/command}/parser.c | 0 .../ble_app_bladder_patch/command}/parser.h | 48 +- .../command/util}/dr_util.c | 158 +- .../command/util}/dr_util.h | 36 +- .../__download_bootloader_rev3.bat | 0 .../{flash => download}/cp.bat | 0 .../{flash => download}/cpd.bat | 0 .../{flash => download}/cpd_eraseALL.bat | 0 .../{flash => download}/etc/__erase_all.bat | 0 .../{flash => download}/etc/__fw_protect.bat | 0 .../etc/__fw_unprotect.bat | 0 .../{flash => download}/etc/__reset.bat | 0 .../{flash => download}/generate_dfu_zip.bat | 0 .../{flash => download}/log.log | 0 .../{flash => download}/private.key | 0 .../flash/__download_bootloader.bat | 22 - .../flash/__download_bootloader_noRPB.bat | 21 - .../flash/__download_bootloader_noRPBYY.bat | 22 - .../flash/__download_nopro.bat | 20 - .../ble_app_bladder_patch/flash/sett.bat | 3 - .../measurement/adc121s051}/dr_adc121s051.c | 2590 +++++++------- .../measurement/adc121s051}/dr_adc121s051.h | 1032 +++--- .../measurement/piezo}/dr_piezo.c | 3166 ++++++++--------- .../measurement/piezo}/dr_piezo.h | 552 +-- .../ble_app_bladder_patch_s140.uvoptx | 80 +- .../ble_app_bladder_patch_s140.uvprojx | 40 +- .../system/security}/ble_quick_security.c | 350 +- .../system/security}/ble_quick_security.h | 112 +- ...ecure_bootloader_ble_s140_pca10056.uvprojx | 2 +- 30 files changed, 4083 insertions(+), 4204 deletions(-) delete mode 100644 pc_firm/.gitignore rename {pc_firm => project/ble_peripheral/ble_app_bladder_patch/command}/parser.c (100%) rename {pc_firm => project/ble_peripheral/ble_app_bladder_patch/command}/parser.h (95%) rename {pc_firm/dr_util => project/ble_peripheral/ble_app_bladder_patch/command/util}/dr_util.c (96%) rename {pc_firm/dr_util => project/ble_peripheral/ble_app_bladder_patch/command/util}/dr_util.h (97%) rename project/ble_peripheral/ble_app_bladder_patch/{flash => download}/__download_bootloader_rev3.bat (100%) rename project/ble_peripheral/ble_app_bladder_patch/{flash => download}/cp.bat (100%) rename project/ble_peripheral/ble_app_bladder_patch/{flash => download}/cpd.bat (100%) rename project/ble_peripheral/ble_app_bladder_patch/{flash => download}/cpd_eraseALL.bat (100%) rename project/ble_peripheral/ble_app_bladder_patch/{flash => download}/etc/__erase_all.bat (100%) rename project/ble_peripheral/ble_app_bladder_patch/{flash => download}/etc/__fw_protect.bat (100%) rename project/ble_peripheral/ble_app_bladder_patch/{flash => download}/etc/__fw_unprotect.bat (100%) rename project/ble_peripheral/ble_app_bladder_patch/{flash => download}/etc/__reset.bat (100%) rename project/ble_peripheral/ble_app_bladder_patch/{flash => download}/generate_dfu_zip.bat (100%) rename project/ble_peripheral/ble_app_bladder_patch/{flash => download}/log.log (100%) rename project/ble_peripheral/ble_app_bladder_patch/{flash => download}/private.key (100%) delete mode 100644 project/ble_peripheral/ble_app_bladder_patch/flash/__download_bootloader.bat delete mode 100644 project/ble_peripheral/ble_app_bladder_patch/flash/__download_bootloader_noRPB.bat delete mode 100644 project/ble_peripheral/ble_app_bladder_patch/flash/__download_bootloader_noRPBYY.bat delete mode 100644 project/ble_peripheral/ble_app_bladder_patch/flash/__download_nopro.bat delete mode 100644 project/ble_peripheral/ble_app_bladder_patch/flash/sett.bat rename {pc_firm/dr_adc121s051 => project/ble_peripheral/ble_app_bladder_patch/measurement/adc121s051}/dr_adc121s051.c (97%) rename {pc_firm/dr_adc121s051 => project/ble_peripheral/ble_app_bladder_patch/measurement/adc121s051}/dr_adc121s051.h (97%) rename project/ble_peripheral/{dr_piezo => ble_app_bladder_patch/measurement/piezo}/dr_piezo.c (97%) rename project/ble_peripheral/{dr_piezo => ble_app_bladder_patch/measurement/piezo}/dr_piezo.h (97%) rename {pc_firm/ble_security => project/ble_peripheral/ble_app_bladder_patch/system/security}/ble_quick_security.c (97%) rename {pc_firm/ble_security => project/ble_peripheral/ble_app_bladder_patch/system/security}/ble_quick_security.h (96%) diff --git a/pc_firm/.gitignore b/pc_firm/.gitignore deleted file mode 100644 index 6bfcfb9..0000000 --- a/pc_firm/.gitignore +++ /dev/null @@ -1,33 +0,0 @@ -# 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/parser.c b/project/ble_peripheral/ble_app_bladder_patch/command/parser.c similarity index 100% rename from pc_firm/parser.c rename to project/ble_peripheral/ble_app_bladder_patch/command/parser.c diff --git a/pc_firm/parser.h b/project/ble_peripheral/ble_app_bladder_patch/command/parser.h similarity index 95% rename from pc_firm/parser.h rename to project/ble_peripheral/ble_app_bladder_patch/command/parser.h index 565fe12..507e81d 100644 --- a/pc_firm/parser.h +++ b/project/ble_peripheral/ble_app_bladder_patch/command/parser.h @@ -1,24 +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 */ - +/* 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/dr_util/dr_util.c b/project/ble_peripheral/ble_app_bladder_patch/command/util/dr_util.c similarity index 96% rename from pc_firm/dr_util/dr_util.c rename to project/ble_peripheral/ble_app_bladder_patch/command/util/dr_util.c index c28fb8d..db94283 100644 --- a/pc_firm/dr_util/dr_util.c +++ b/project/ble_peripheral/ble_app_bladder_patch/command/util/dr_util.c @@ -1,79 +1,79 @@ -#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 uint8_t ble_bin_buffer[]; -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 */ -} +#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 uint8_t ble_bin_buffer[]; +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/project/ble_peripheral/ble_app_bladder_patch/command/util/dr_util.h similarity index 97% rename from pc_firm/dr_util/dr_util.h rename to project/ble_peripheral/ble_app_bladder_patch/command/util/dr_util.h index 4cf8078..5ebec64 100644 --- a/pc_firm/dr_util/dr_util.h +++ b/project/ble_peripheral/ble_app_bladder_patch/command/util/dr_util.h @@ -1,18 +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 */ - +#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/flash/__download_bootloader_rev3.bat b/project/ble_peripheral/ble_app_bladder_patch/download/__download_bootloader_rev3.bat similarity index 100% rename from project/ble_peripheral/ble_app_bladder_patch/flash/__download_bootloader_rev3.bat rename to project/ble_peripheral/ble_app_bladder_patch/download/__download_bootloader_rev3.bat diff --git a/project/ble_peripheral/ble_app_bladder_patch/flash/cp.bat b/project/ble_peripheral/ble_app_bladder_patch/download/cp.bat similarity index 100% rename from project/ble_peripheral/ble_app_bladder_patch/flash/cp.bat rename to project/ble_peripheral/ble_app_bladder_patch/download/cp.bat diff --git a/project/ble_peripheral/ble_app_bladder_patch/flash/cpd.bat b/project/ble_peripheral/ble_app_bladder_patch/download/cpd.bat similarity index 100% rename from project/ble_peripheral/ble_app_bladder_patch/flash/cpd.bat rename to project/ble_peripheral/ble_app_bladder_patch/download/cpd.bat diff --git a/project/ble_peripheral/ble_app_bladder_patch/flash/cpd_eraseALL.bat b/project/ble_peripheral/ble_app_bladder_patch/download/cpd_eraseALL.bat similarity index 100% rename from project/ble_peripheral/ble_app_bladder_patch/flash/cpd_eraseALL.bat rename to project/ble_peripheral/ble_app_bladder_patch/download/cpd_eraseALL.bat diff --git a/project/ble_peripheral/ble_app_bladder_patch/flash/etc/__erase_all.bat b/project/ble_peripheral/ble_app_bladder_patch/download/etc/__erase_all.bat similarity index 100% rename from project/ble_peripheral/ble_app_bladder_patch/flash/etc/__erase_all.bat rename to project/ble_peripheral/ble_app_bladder_patch/download/etc/__erase_all.bat diff --git a/project/ble_peripheral/ble_app_bladder_patch/flash/etc/__fw_protect.bat b/project/ble_peripheral/ble_app_bladder_patch/download/etc/__fw_protect.bat similarity index 100% rename from project/ble_peripheral/ble_app_bladder_patch/flash/etc/__fw_protect.bat rename to project/ble_peripheral/ble_app_bladder_patch/download/etc/__fw_protect.bat diff --git a/project/ble_peripheral/ble_app_bladder_patch/flash/etc/__fw_unprotect.bat b/project/ble_peripheral/ble_app_bladder_patch/download/etc/__fw_unprotect.bat similarity index 100% rename from project/ble_peripheral/ble_app_bladder_patch/flash/etc/__fw_unprotect.bat rename to project/ble_peripheral/ble_app_bladder_patch/download/etc/__fw_unprotect.bat diff --git a/project/ble_peripheral/ble_app_bladder_patch/flash/etc/__reset.bat b/project/ble_peripheral/ble_app_bladder_patch/download/etc/__reset.bat similarity index 100% rename from project/ble_peripheral/ble_app_bladder_patch/flash/etc/__reset.bat rename to project/ble_peripheral/ble_app_bladder_patch/download/etc/__reset.bat diff --git a/project/ble_peripheral/ble_app_bladder_patch/flash/generate_dfu_zip.bat b/project/ble_peripheral/ble_app_bladder_patch/download/generate_dfu_zip.bat similarity index 100% rename from project/ble_peripheral/ble_app_bladder_patch/flash/generate_dfu_zip.bat rename to project/ble_peripheral/ble_app_bladder_patch/download/generate_dfu_zip.bat diff --git a/project/ble_peripheral/ble_app_bladder_patch/flash/log.log b/project/ble_peripheral/ble_app_bladder_patch/download/log.log similarity index 100% rename from project/ble_peripheral/ble_app_bladder_patch/flash/log.log rename to project/ble_peripheral/ble_app_bladder_patch/download/log.log diff --git a/project/ble_peripheral/ble_app_bladder_patch/flash/private.key b/project/ble_peripheral/ble_app_bladder_patch/download/private.key similarity index 100% rename from project/ble_peripheral/ble_app_bladder_patch/flash/private.key rename to project/ble_peripheral/ble_app_bladder_patch/download/private.key diff --git a/project/ble_peripheral/ble_app_bladder_patch/flash/__download_bootloader.bat b/project/ble_peripheral/ble_app_bladder_patch/flash/__download_bootloader.bat deleted file mode 100644 index 1062ef1..0000000 --- a/project/ble_peripheral/ble_app_bladder_patch/flash/__download_bootloader.bat +++ /dev/null @@ -1,22 +0,0 @@ - -set CURDIR=%cd% - - - -copy ..\pca10056\s140\arm5_no_packs\_build\nrf52840_xxaa.hex medithings_bladder_patch_0001.hex -copy ..\..\..\dfu\secure_bootloader\pca10056_s140_ble\arm5_no_packs\_build\nrf52840_xxaa_s140.hex medithings_bladder_patch_bootloader.hex - -nrfutil settings generate --family NRF52840 --application medithings_bladder_patch_0001.hex --application-version 1 --bootloader-version 1 --bl-settings-version 2 medithings_bladder_patch_bootloader_setting.hex - -mergehex.exe --merge s140_nrf52_7.2.0_softdevice.hex medithings_bladder_patch_0001.hex --output medithings_bladder_patch_merged_1.hex -mergehex.exe --merge medithings_bladder_patch_bootloader.hex medithings_bladder_patch_bootloader_setting.hex --output medithings_bladder_patch_merged_2.hex -mergehex.exe --merge medithings_bladder_patch_merged_1.hex medithings_bladder_patch_merged_2.hex --output medithings_bladder_patch_dfu_merged_all.hex - -nrfjprog --family NRF52 --recover -nrfjprog --family NRF52 --eraseall -nrfjprog --family NRF52 --program medithings_bladder_patch_dfu_merged_all.hex -nrfjprog --family NRF52 --reset -nrfjprog --family NRF52 --rbp ALL - - -pause \ No newline at end of file diff --git a/project/ble_peripheral/ble_app_bladder_patch/flash/__download_bootloader_noRPB.bat b/project/ble_peripheral/ble_app_bladder_patch/flash/__download_bootloader_noRPB.bat deleted file mode 100644 index 2a43acb..0000000 --- a/project/ble_peripheral/ble_app_bladder_patch/flash/__download_bootloader_noRPB.bat +++ /dev/null @@ -1,21 +0,0 @@ - -set CURDIR=%cd% - - - -copy ..\pca10056\s140\arm5_no_packs\_build\nrf52840_xxaa.hex medithings_bladder_patch_0001.hex -copy ..\..\..\dfu\secure_bootloader\pca10056_s140_ble\arm5_no_packs\_build\nrf52840_xxaa_s140.hex medithings_bladder_patch_bootloader.hex - -nrfutil settings generate --family NRF52840 --application medithings_bladder_patch_0001.hex --application-version 1 --bootloader-version 1 --bl-settings-version 2 medithings_bladder_patch_bootloader_setting.hex - -mergehex.exe --merge s140_nrf52_7.2.0_softdevice.hex medithings_bladder_patch_0001.hex --output medithings_bladder_patch_merged_1.hex -mergehex.exe --merge medithings_bladder_patch_bootloader.hex medithings_bladder_patch_bootloader_setting.hex --output medithings_bladder_patch_merged_2.hex -mergehex.exe --merge medithings_bladder_patch_merged_1.hex medithings_bladder_patch_merged_2.hex --output medithings_bladder_patch_dfu_merged_all.hex - -nrfjprog --family NRF52 --recover -nrfjprog --family NRF52 --eraseall -nrfjprog --family NRF52 --program medithings_bladder_patch_dfu_merged_all.hex - - - -pause \ No newline at end of file diff --git a/project/ble_peripheral/ble_app_bladder_patch/flash/__download_bootloader_noRPBYY.bat b/project/ble_peripheral/ble_app_bladder_patch/flash/__download_bootloader_noRPBYY.bat deleted file mode 100644 index ade6814..0000000 --- a/project/ble_peripheral/ble_app_bladder_patch/flash/__download_bootloader_noRPBYY.bat +++ /dev/null @@ -1,22 +0,0 @@ - -set CURDIR=%cd% - - - -copy ..\pca10056\s140\arm5_no_packs\_build\nrf52840_xxaa.hex medithings_bladder_patch_0001.hex -copy ..\..\..\dfu\secure_bootloader\pca10056_s140_ble\arm5_no_packs\_build\nrf52840_xxaa_s140.hex medithings_bladder_patch_bootloader.hex - -nrfutil settings generate --family NRF52840 --application medithings_bladder_patch_0001.hex --application-version 1 --bootloader-version 1 --bl-settings-version 2 medithings_bladder_patch_bootloader_setting.hex - -mergehex.exe --merge s140_nrf52_7.2.0_softdevice.hex medithings_bladder_patch_0001.hex --output medithings_bladder_patch_merged_1.hex -mergehex.exe --merge medithings_bladder_patch_bootloader.hex medithings_bladder_patch_bootloader_setting.hex --output medithings_bladder_patch_merged_2.hex -mergehex.exe --merge medithings_bladder_patch_merged_1.hex medithings_bladder_patch_merged_2.hex --output medithings_bladder_patch_dfu_merged_all.hex - - -nrfjprog --family NRF52 --eraseall -nrfjprog --family NRF52 --program medithings_bladder_patch_dfu_merged_all.hex -nrfjprog --family NRF52 --reset -nrfjprog --family NRF52 --rbp ALL - - -pause \ No newline at end of file diff --git a/project/ble_peripheral/ble_app_bladder_patch/flash/__download_nopro.bat b/project/ble_peripheral/ble_app_bladder_patch/flash/__download_nopro.bat deleted file mode 100644 index 8db8de9..0000000 --- a/project/ble_peripheral/ble_app_bladder_patch/flash/__download_nopro.bat +++ /dev/null @@ -1,20 +0,0 @@ - -set CURDIR=%cd% - - - -copy ..\pca10056\s140\arm5_no_packs\_build\nrf52840_xxaa.hex medithings_bladder_patch_0001.hex -copy ..\..\..\dfu\secure_bootloader\pca10056_s140_ble\arm5_no_packs\_build\nrf52840_xxaa_s140.hex medithings_bladder_patch_bootloader.hex - -nrfutil settings generate --family NRF52840 --application medithings_bladder_patch_0001.hex --application-version 1 --bootloader-version 1 --bl-settings-version 2 medithings_bladder_patch_bootloader_setting.hex - -mergehex.exe --merge s140_nrf52_7.2.0_softdevice.hex medithings_bladder_patch_0001.hex --output medithings_bladder_patch_merged_1.hex -mergehex.exe --merge medithings_bladder_patch_bootloader.hex medithings_bladder_patch_bootloader_setting.hex --output medithings_bladder_patch_merged_2.hex -mergehex.exe --merge medithings_bladder_patch_merged_1.hex medithings_bladder_patch_merged_2.hex --output medithings_bladder_patch_dfu_merged_all.hex - -nrfjprog --family NRF52 --program medithings_bladder_patch_0001.hex -nrfjprog --family NRF52 --reset - - - -pause \ No newline at end of file diff --git a/project/ble_peripheral/ble_app_bladder_patch/flash/sett.bat b/project/ble_peripheral/ble_app_bladder_patch/flash/sett.bat deleted file mode 100644 index 1efbb1a..0000000 --- a/project/ble_peripheral/ble_app_bladder_patch/flash/sett.bat +++ /dev/null @@ -1,3 +0,0 @@ -set path=d:\nrfutil\bin;%path% -d: -cd D:\mt_project\vesiscan\project\ble_peripheral\ble_app_bladder_patch\hex \ No newline at end of file diff --git a/pc_firm/dr_adc121s051/dr_adc121s051.c b/project/ble_peripheral/ble_app_bladder_patch/measurement/adc121s051/dr_adc121s051.c similarity index 97% rename from pc_firm/dr_adc121s051/dr_adc121s051.c rename to project/ble_peripheral/ble_app_bladder_patch/measurement/adc121s051/dr_adc121s051.c index 571cf44..17836a2 100644 --- a/pc_firm/dr_adc121s051/dr_adc121s051.c +++ b/project/ble_peripheral/ble_app_bladder_patch/measurement/adc121s051/dr_adc121s051.c @@ -1,1295 +1,1295 @@ -/******************************************************************************* - * @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 Hardware SPI (nrfx_spim, SPIM2) implementation for ADC121S051. - * Replaces bit-bang SPI to eliminate __disable_irq() and prevent - * SoftDevice assertion failures during BLE connection events. - * 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 "nrfx_spim.h" -#include "nrf52840.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); -extern void dr_piezo_power_off(void); - -#include "parser.h" - -/*============================================================================== - * DEBUG CONFIGURATION - *============================================================================*/ -#define DEBUG_ADC -#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) - -/*============================================================================== - * PRIVATE VARIABLES - *============================================================================*/ - -static bool m_initialized = false; -static uint32_t m_vref_mv = DR_ADC_VREF_MV; - -/* Hardware SPI instance (SPIM3 - supports up to 32MHz, 16MHz for ADC121S051) */ -static nrfx_spim_t m_spim = NRFX_SPIM_INSTANCE(3); - -/* Echo capture buffer (module-level for external access) */ -static uint16_t m_echo_buffer[DR_ADC_ECHO_SAMPLES_MAX]; - -/*============================================================================== - * PRIVATE FUNCTIONS - *============================================================================*/ - -/** - * @brief Perform one ADC121S051 conversion via hardware SPI (SPIM2). - * @return 12-bit ADC value (0–4095) - * - * @details ADC121S051 serial frame (16 bits, MSB first): - * Bit 15-13: Leading zeros (Z2, Z1, Z0) - * Bit 12-1: Data bits D11-D0 - * Bit 0: Don't care - * Extract: (raw16 >> 1) & 0x0FFF - * - * SPI Mode 2 (CPOL=1, CPHA=0): - * - SCLK idle HIGH - * - Data valid on SCLK falling edge (sampled by SPIM on falling edge) - * - CS falling edge starts ADC conversion - */ -static uint16_t spim_read_raw(void) -{ - volatile uint8_t rx_buf[2] = {0, 0}; - NRF_SPIM_Type *spim = m_spim.p_reg; - - nrf_gpio_pin_clear(DR_ADC_PIN_CS); /* CS LOW */ - spim->RXD.PTR = (uint32_t)rx_buf; - spim->RXD.MAXCNT = 2; - spim->EVENTS_END = 0; - spim->TASKS_START = 1; - while (!spim->EVENTS_END) {} - nrf_gpio_pin_set(DR_ADC_PIN_CS); /* CS HIGH */ - - uint16_t raw16 = ((uint16_t)rx_buf[0] << 8) | rx_buf[1]; - return (raw16 >> 1) & 0x0FFF; -} - -/*============================================================================== - * PUBLIC FUNCTIONS - INITIALIZATION - *============================================================================*/ - -dr_adc_err_t dr_adc_init(void) -{ - nrfx_err_t err; - - ADC_LOG("ADC121S051 init (HW SPI, SPIM3)..."); - - nrfx_spim_config_t config = NRFX_SPIM_DEFAULT_CONFIG; - config.sck_pin = DR_ADC_PIN_SCLK; /* P0.14 */ - config.mosi_pin = NRFX_SPIM_PIN_NOT_USED;/* not used (read-only ADC) */ - config.miso_pin = DR_ADC_PIN_SDATA; /* P0.15 */ - config.ss_pin = DR_ADC_PIN_CS; /* P0.19 */ - config.frequency = NRF_SPIM_FREQ_16M; - config.mode = NRF_SPIM_MODE_3; /* CPOL=1 (idle HIGH), CPHA=1 (sample on rising - ADC presents on falling, stable on rising) */ - config.bit_order = NRF_SPIM_BIT_ORDER_MSB_FIRST; - config.ss_active_high = false; /* CS active LOW */ - - err = nrfx_spim_init(&m_spim, &config, NULL, NULL); - ADC_LOG("SPIM3 init result: 0x%08X (%s)", err, (err == NRFX_SUCCESS) ? "OK" : "FAIL"); - if (err != NRFX_SUCCESS) - { - return DR_ADC_ERR_NOT_INIT; - } - - /* Wait for ADC power stabilization */ - nrf_delay_us(10); - - /* Dummy read to wake up ADC and clear stale conversion data */ - (void)spim_read_raw(); - - m_initialized = true; - - ADC_LOG("ADC121S051 ready (VREF=%dmV, HW SPI)", m_vref_mv); - - return DR_ADC_OK; -} - -void dr_adc_uninit(void) -{ - if (!m_initialized) return; - - nrfx_spim_uninit(&m_spim); - - 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; - - *raw_value = spim_read_raw(); - - 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) -{ - volatile uint8_t rx_buf[2]; - uint32_t cs_mask = (1UL << DR_PIN_NUM(DR_ADC_PIN_CS)); - uint16_t raw16; - uint16_t i; - NRF_SPIM_Type *spim = m_spim.p_reg; - - 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; - - spim->RXD.MAXCNT = 2; - - for (i = 0; i < num_samples; i++) - { - NRF_P0->OUTCLR = cs_mask; - spim->RXD.PTR = (uint32_t)rx_buf; - spim->EVENTS_END = 0; - spim->TASKS_START = 1; - while (!spim->EVENTS_END) {} - NRF_P0->OUTSET = cs_mask; - - raw16 = ((uint16_t)rx_buf[0] << 8) | rx_buf[1]; - buffer[i] = (raw16 >> 1) & 0x0FFF; - } - - 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 dr_binary_tx_safe(uint8_t const *ble_bin_buff, uint16_t length); -extern void dr_sd_delay_ms(uint32_t ms); - -/* Platform interface (log function) */ -#include "parser.h" - -/*============================================================================== - * INTEGRATED BURST + CAPTURE + TRANSMIT - *============================================================================*/ - -/* External piezo burst function */ -extern void dr_piezo_burst_sw(uint8_t cycles); - -/* BLE packet constants */ -#define BLE_MTU_SIZE 244 /* ATT MTU 247 - 3 (ATT header) = 244 */ -#define BLE_REB_HEADER_LEN 6 /* "reb:" tag(4) + num_samples(2) */ -#define BLE_RED_HEADER_LEN 6 /* "red:" tag(4) + pkt_idx(2) */ -#define BLE_REB_DATA_LEN (BLE_MTU_SIZE - BLE_REB_HEADER_LEN) /* 238 bytes = 119 samples */ -#define BLE_RED_DATA_LEN (BLE_MTU_SIZE - BLE_RED_HEADER_LEN) /* 238 bytes = 119 samples */ -#define BLE_PACKET_DELAY_MS 100 /* Inter-packet delay - allow BLE TX buffer to drain */ - -/*============================================================================== - * 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) - * - * reb+red merged protocol: header와 데이터를 첫 패킷에 합침 - * - * Response format: - * Packet 1 (reb:): num_samples(2) + raw_data(up to 238 bytes = 119 samples) - * Packet 2~N (red:): pkt_idx(2) + raw_data(up to 238 bytes) — 119샘플 초과 시만 - * Final (raa:): status(2) — skip_raa=0 일 때만 - * - * With 100 samples: 200 bytes = reb:(6+200) = 206 bytes (단일 패킷) - */ -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 > 7) cycles = 5; /* Valid range: 3~7, default 5 */ - if (averaging == 0) averaging = 1; /* Minimum 1 */ - if (averaging > 1000) averaging = 1000; /* Maximum 1000 */ - if (piezo_ch >= MAA_NUM_CHANNELS) piezo_ch = 0; /* 채널 범위 검증 */ - - /* echo 분석은 PC에서 raw 데이터로 직접 수행 */ - - /* 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); - - /* MUX 전환 후 S/H 커패시터 안정화를 위한 dummy read */ - (void)spim_read_raw(); - - /*--- 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: Transmit via BLE - reb: + red: merged protocol ---*/ - /* Packet 1 (reb:): tag(4) + num_samples(2) + data(up to 238 bytes = 119 samples) */ - /* Packet 2~N (red:): tag(4) + pkt_idx(2) + data(up to 238 bytes) — only if > 119 samples */ - - uint16_t total_data_bytes = num_samples * 2; - uint16_t src_idx = 0; - - /* Packet 1: reb: header + data merged (Big-Endian) */ - ble_buffer[0] = 'r'; ble_buffer[1] = 'e'; ble_buffer[2] = 'b'; ble_buffer[3] = ':'; - ble_buffer[4] = (uint8_t)(num_samples >> 8); - ble_buffer[5] = (uint8_t)(num_samples & 0xFF); - - uint16_t dst_idx = BLE_REB_HEADER_LEN; - uint16_t first_chunk = (total_data_bytes > BLE_REB_DATA_LEN) ? BLE_REB_DATA_LEN : total_data_bytes; - while (src_idx < num_samples && (dst_idx - BLE_REB_HEADER_LEN) < first_chunk) { - uint16_t sample = m_echo_buffer[src_idx++] & 0x0FFF; - ble_buffer[dst_idx++] = (uint8_t)(sample >> 8); - ble_buffer[dst_idx++] = (uint8_t)(sample & 0xFF); - } - - dr_binary_tx_safe(ble_buffer, dst_idx / 2); - - /* Continuation packets (red:) — only if data didn't fit in reb: */ - uint16_t pkt = 0; - while (src_idx < num_samples) { - dr_sd_delay_ms(BLE_PACKET_DELAY_MS); - - ble_buffer[0] = 'r'; ble_buffer[1] = 'e'; ble_buffer[2] = 'd'; ble_buffer[3] = ':'; - ble_buffer[4] = (uint8_t)(pkt >> 8); - ble_buffer[5] = (uint8_t)(pkt & 0xFF); - - dst_idx = BLE_RED_HEADER_LEN; - while (src_idx < num_samples && (dst_idx - BLE_RED_HEADER_LEN) < BLE_RED_DATA_LEN) { - uint16_t sample = m_echo_buffer[src_idx++] & 0x0FFF; - ble_buffer[dst_idx++] = (uint8_t)(sample >> 8); - ble_buffer[dst_idx++] = (uint8_t)(sample & 0xFF); - } - - dr_binary_tx_safe(ble_buffer, dst_idx / 2); - pkt++; - } - - /* raa: completion — skip_raa: 0=send (mec), 1=skip (maa - caller sends final raa) */ - if (!skip_raa) { - dr_sd_delay_ms(BLE_PACKET_DELAY_MS); - 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); - } - - ADC_LOG("mec: reb+data merged, skip_raa=%u (%u samples)", skip_raa, num_samples); - - return DR_ADC_OK; -} - - -/*============================================================================== - * 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 > 7) 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); - - /* 채널 선택(MUX) : 1.3ms */ - dr_piezo_select_channel(piezo_ch); - - /* MUX 전환 후 S/H 커패시터 안정화를 위한 dummy read */ - (void)spim_read_raw(); - - /* 채널당 반복 측정 횟수만큼 */ - for (uint16_t avg_iter = 0; avg_iter < averaging; avg_iter++) - { - /* TX 펄스 출력(Burst) */ - switch (freq_option) { - case 0: - default: - dr_piezo_burst_sw_18mhz(cycles); - break; - case 1: - dr_piezo_burst_sw(cycles); /* 2.1MHz */ - break; - case 2: - dr_piezo_burst_sw_20mhz(cycles); - break; - case 3: - dr_piezo_burst_sw_17mhz(cycles); - break; - } - - /* TX 펄스 출력 후 ADC 시작 시까지 대기 시간 */ - if (delay_us > 0) - { - nrf_delay_us(delay_us); - } - - /* 측정 ADC 샘플 수만큼 캡처 */ - 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; - /* peak/baseline 분석은 PC에서 raw 데이터로 직접 수행 */ - - return DR_ADC_OK; -} - - -dr_adc_err_t dr_adc_transmit_channel(const dr_maa_channel_t *ch_data, - uint8_t *ble_buffer) -{ - if (ch_data == NULL || ble_buffer == NULL) { - return DR_ADC_ERR_INVALID_PARAM; - } - - uint16_t total_data_bytes = ch_data->num_samples * 2; - uint16_t src_idx = 0; - - /* Packet 1: reb: header + data merged (Big-Endian) */ - ble_buffer[0] = 'r'; ble_buffer[1] = 'e'; ble_buffer[2] = 'b'; ble_buffer[3] = ':'; - ble_buffer[4] = (uint8_t)(ch_data->num_samples >> 8); - ble_buffer[5] = (uint8_t)(ch_data->num_samples & 0xFF); - - uint16_t dst_idx = BLE_REB_HEADER_LEN; - uint16_t first_chunk = (total_data_bytes > BLE_REB_DATA_LEN) ? BLE_REB_DATA_LEN : total_data_bytes; - while (src_idx < ch_data->num_samples && (dst_idx - BLE_REB_HEADER_LEN) < first_chunk) { - uint16_t sample = ch_data->samples[src_idx++] & 0x0FFF; - ble_buffer[dst_idx++] = (uint8_t)(sample >> 8); - ble_buffer[dst_idx++] = (uint8_t)(sample & 0xFF); - } - - dr_binary_tx_safe(ble_buffer, dst_idx / 2); - - /* Continuation packets (red:) — only if data didn't fit in reb: */ - uint16_t pkt = 0; - while (src_idx < ch_data->num_samples) { - dr_sd_delay_ms(BLE_PACKET_DELAY_MS); - - ble_buffer[0] = 'r'; ble_buffer[1] = 'e'; ble_buffer[2] = 'd'; ble_buffer[3] = ':'; - ble_buffer[4] = (uint8_t)(pkt >> 8); - ble_buffer[5] = (uint8_t)(pkt & 0xFF); - - dst_idx = BLE_RED_HEADER_LEN; - while (src_idx < ch_data->num_samples && (dst_idx - BLE_RED_HEADER_LEN) < BLE_RED_DATA_LEN) { - uint16_t sample = ch_data->samples[src_idx++] & 0x0FFF; - ble_buffer[dst_idx++] = (uint8_t)(sample >> 8); - ble_buffer[dst_idx++] = (uint8_t)(sample & 0xFF); - } - - dr_binary_tx_safe(ble_buffer, dst_idx / 2); - pkt++; - } - - 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 (Big-Endian) */ - out_buffer[idx++] = (uint8_t)(samples[0] >> 8); - out_buffer[idx++] = (uint8_t)(samples[0] & 0xFF); - - /* 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] >> 8); - out_buffer[idx++] = (uint8_t)(samples[i] & 0xFF); - } - } - - *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)); - - /* Packet 1: rdb: header(8) + delta data merged (Big-Endian) */ - ble_buffer[0] = 'r'; ble_buffer[1] = 'd'; ble_buffer[2] = 'b'; ble_buffer[3] = ':'; - ble_buffer[4] = (uint8_t)(ch_data->num_samples >> 8); - ble_buffer[5] = (uint8_t)(ch_data->num_samples & 0xFF); - ble_buffer[6] = (uint8_t)(compressed_size >> 8); - ble_buffer[7] = (uint8_t)(compressed_size & 0xFF); - - #define BLE_RDB_HEADER_LEN 8 /* rdb: tag(4) + num_samples(2) + compressed_size(2) */ - uint16_t rdb_data_cap = BLE_MTU_SIZE - BLE_RDB_HEADER_LEN; /* 236 bytes */ - - uint16_t dst_idx = BLE_RDB_HEADER_LEN; - uint16_t src_idx = 0; - uint16_t first_chunk = (compressed_size > rdb_data_cap) ? rdb_data_cap : compressed_size; - while (src_idx < first_chunk) { - ble_buffer[dst_idx++] = delta_buffer[src_idx++]; - } - /* Pad to word boundary if needed */ - if (dst_idx & 1) ble_buffer[dst_idx++] = 0; - - dr_binary_tx_safe(ble_buffer, dst_idx / 2); - - /* Continuation packets (rdd:) — only if data didn't fit */ - uint16_t pkt = 0; - while (src_idx < compressed_size) { - dr_sd_delay_ms(BLE_PACKET_DELAY_MS); - - ble_buffer[0] = 'r'; ble_buffer[1] = 'd'; ble_buffer[2] = 'd'; ble_buffer[3] = ':'; - ble_buffer[4] = (uint8_t)(pkt >> 8); - ble_buffer[5] = (uint8_t)(pkt & 0xFF); - - dst_idx = BLE_RED_HEADER_LEN; - while (src_idx < compressed_size && (dst_idx - BLE_RED_HEADER_LEN) < BLE_RED_DATA_LEN) { - ble_buffer[dst_idx++] = delta_buffer[src_idx++]; - } - if (dst_idx & 1) ble_buffer[dst_idx++] = 0; - - dr_binary_tx_safe(ble_buffer, dst_idx / 2); - pkt++; - } - - 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 제거 — reb+red merged protocol) */ - -/** - * @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; - - dr_adc_err_t err = 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] - ); - - return err; -} - -/** - * @brief Send reb: header + data merged for current channel - * - * reb: tag(4) + num_samples(2) + data(up to 238 bytes) - * 100샘플(200B) 이하면 이 패킷 하나로 채널 전송 완료 - */ -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; - - /* reb: header + data merged (Big-Endian) */ - buf[0] = 'r'; buf[1] = 'e'; buf[2] = 'b'; buf[3] = ':'; - buf[4] = (uint8_t)(ch->num_samples >> 8); - buf[5] = (uint8_t)(ch->num_samples & 0xFF); - - uint16_t dst_idx = BLE_REB_HEADER_LEN; - uint16_t first_chunk = (total_data_bytes > BLE_REB_DATA_LEN) ? BLE_REB_DATA_LEN : total_data_bytes; - uint16_t src_idx = 0; - while (src_idx < ch->num_samples && (dst_idx - BLE_REB_HEADER_LEN) < first_chunk) { - uint16_t sample = ch->samples[src_idx++]; - buf[dst_idx++] = (uint8_t)(sample >> 8); - buf[dst_idx++] = (uint8_t)(sample & 0xFF); - } - - dr_binary_tx_safe(buf, dst_idx / 2); - dr_sd_delay_ms(5); /* dr_binary_tx_safe 내부 재시도(40ms)로 TX 완료 보장, 최소 딜레이만 */ - - g_maa_ctx.current_pkt = 0; - g_maa_ctx.data_offset = src_idx * 2; /* 이미 전송한 바이트 수 */ - - /* 데이터가 첫 패킷에 다 들어갔으면 바로 다음 채널로 */ - if (g_maa_ctx.data_offset >= total_data_bytes) { - g_maa_ctx.state = MAA_ASYNC_TX_DATA; /* send_data_packet()에서 false 반환 → 다음 채널 */ - } else { - g_maa_ctx.state = MAA_ASYNC_TX_DATA; - } -} - -/** - * @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_RED_DATA_LEN) ? BLE_RED_DATA_LEN : remaining; - - /* red: packet header (Big-Endian) */ - buf[0] = 'r'; buf[1] = 'e'; buf[2] = 'd'; buf[3] = ':'; - buf[4] = (uint8_t)(pkt_idx >> 8); - buf[5] = (uint8_t)(pkt_idx & 0xFF); - - /* 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 >> 8); - buf[dst_idx++] = (uint8_t)(sample & 0xFF); - } - - 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 >> 8); - buf[5] = (uint8_t)(status & 0xFF); - - dr_binary_tx_safe(buf, 3); - - //if (g_plat.log) g_plat.log("-------------------------------------------------------------------------------------\r\n"); - - /* 캡처 완료 → Piezo TX/RX 전원 OFF */ - dr_piezo_power_off(); - - g_maa_ctx.state = MAA_ASYNC_IDLE; - //ADC_LOG("maa_async: complete, status=0x%04X", status); - - /* 완료 콜백 호출 (mbb? 등에서 센서 측정 체인 트리거용) */ - if (g_maa_ctx.on_complete_cb) - { - void (*cb)(void) = g_maa_ctx.on_complete_cb; - g_maa_ctx.on_complete_cb = NULL; /* 1회성: 재호출 방지 */ - cb(); - } -} - -/*============================================================================== - * 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) -{ - dr_adc_err_t err; - uint8_t ch; - - 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 > 7) ? 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; - g_maa_ctx.on_complete_cb = NULL; /* 기본: 콜백 없음 (maa?) */ - - /* 전채널 캡처: BLE 전송 없이 6채널 전부 캡처 완료 후 TX 시작 */ - g_maa_ctx.state = MAA_ASYNC_CAPTURING; - - for (ch = 0; ch < MAA_NUM_CHANNELS; ch++) - { - err = maa_async_capture_channel(ch); - if (err != DR_ADC_OK) - { - if (g_plat.log) g_plat.log("[maa] maa_async_start: CH%u capture failed (%d)", ch, err); - maa_async_send_completion(0xFFF0 | ch); - return err; - } - } - - /* peak 로그와 raw 덤프 사이 구분 */ - //if (g_plat.log) g_plat.log("\r\n"); - - /* 캡처 완료 → Piezo TX/RX 전원 OFF (BLE 전송 중 불필요) */ - dr_piezo_power_off(); - - /* 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 - { - /* 이미 전채널 캡처됨, 바로 헤더 전송 */ - g_maa_ctx.state = MAA_ASYNC_CAPTURING; - 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); -} - -void maa_async_set_pre_capture_all(bool on) -{ - g_maa_ctx.pre_capture_all = on; -} - -maa_async_state_t maa_async_get_state(void) -{ - return g_maa_ctx.state; -} - -/*============================================================================== - * 비동기 측정 상태 IDLE로 초기화 - BLE 연결이 끊어지는 경우 먹통 현상 방지 - *============================================================================*/ -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; // 측정 상태 IDLE로 초기화 - g_maa_ctx.on_complete_cb = NULL; // abort 후 콜백 호출 방지 - dr_piezo_power_off(); // 전체 측정 중간에 끊기는 경우 Piezo TX/RX Sleep - } -} - -void maa_async_set_on_complete(void (*cb)(void)) -{ - g_maa_ctx.on_complete_cb = cb; -} - - +/******************************************************************************* + * @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 Hardware SPI (nrfx_spim, SPIM2) implementation for ADC121S051. + * Replaces bit-bang SPI to eliminate __disable_irq() and prevent + * SoftDevice assertion failures during BLE connection events. + * 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 "nrfx_spim.h" +#include "nrf52840.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); +extern void dr_piezo_power_off(void); + +#include "parser.h" + +/*============================================================================== + * DEBUG CONFIGURATION + *============================================================================*/ +#define DEBUG_ADC +#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) + +/*============================================================================== + * PRIVATE VARIABLES + *============================================================================*/ + +static bool m_initialized = false; +static uint32_t m_vref_mv = DR_ADC_VREF_MV; + +/* Hardware SPI instance (SPIM3 - supports up to 32MHz, 16MHz for ADC121S051) */ +static nrfx_spim_t m_spim = NRFX_SPIM_INSTANCE(3); + +/* Echo capture buffer (module-level for external access) */ +static uint16_t m_echo_buffer[DR_ADC_ECHO_SAMPLES_MAX]; + +/*============================================================================== + * PRIVATE FUNCTIONS + *============================================================================*/ + +/** + * @brief Perform one ADC121S051 conversion via hardware SPI (SPIM2). + * @return 12-bit ADC value (0–4095) + * + * @details ADC121S051 serial frame (16 bits, MSB first): + * Bit 15-13: Leading zeros (Z2, Z1, Z0) + * Bit 12-1: Data bits D11-D0 + * Bit 0: Don't care + * Extract: (raw16 >> 1) & 0x0FFF + * + * SPI Mode 2 (CPOL=1, CPHA=0): + * - SCLK idle HIGH + * - Data valid on SCLK falling edge (sampled by SPIM on falling edge) + * - CS falling edge starts ADC conversion + */ +static uint16_t spim_read_raw(void) +{ + volatile uint8_t rx_buf[2] = {0, 0}; + NRF_SPIM_Type *spim = m_spim.p_reg; + + nrf_gpio_pin_clear(DR_ADC_PIN_CS); /* CS LOW */ + spim->RXD.PTR = (uint32_t)rx_buf; + spim->RXD.MAXCNT = 2; + spim->EVENTS_END = 0; + spim->TASKS_START = 1; + while (!spim->EVENTS_END) {} + nrf_gpio_pin_set(DR_ADC_PIN_CS); /* CS HIGH */ + + uint16_t raw16 = ((uint16_t)rx_buf[0] << 8) | rx_buf[1]; + return (raw16 >> 1) & 0x0FFF; +} + +/*============================================================================== + * PUBLIC FUNCTIONS - INITIALIZATION + *============================================================================*/ + +dr_adc_err_t dr_adc_init(void) +{ + nrfx_err_t err; + + ADC_LOG("ADC121S051 init (HW SPI, SPIM3)..."); + + nrfx_spim_config_t config = NRFX_SPIM_DEFAULT_CONFIG; + config.sck_pin = DR_ADC_PIN_SCLK; /* P0.14 */ + config.mosi_pin = NRFX_SPIM_PIN_NOT_USED;/* not used (read-only ADC) */ + config.miso_pin = DR_ADC_PIN_SDATA; /* P0.15 */ + config.ss_pin = DR_ADC_PIN_CS; /* P0.19 */ + config.frequency = NRF_SPIM_FREQ_16M; + config.mode = NRF_SPIM_MODE_3; /* CPOL=1 (idle HIGH), CPHA=1 (sample on rising - ADC presents on falling, stable on rising) */ + config.bit_order = NRF_SPIM_BIT_ORDER_MSB_FIRST; + config.ss_active_high = false; /* CS active LOW */ + + err = nrfx_spim_init(&m_spim, &config, NULL, NULL); + ADC_LOG("SPIM3 init result: 0x%08X (%s)", err, (err == NRFX_SUCCESS) ? "OK" : "FAIL"); + if (err != NRFX_SUCCESS) + { + return DR_ADC_ERR_NOT_INIT; + } + + /* Wait for ADC power stabilization */ + nrf_delay_us(10); + + /* Dummy read to wake up ADC and clear stale conversion data */ + (void)spim_read_raw(); + + m_initialized = true; + + ADC_LOG("ADC121S051 ready (VREF=%dmV, HW SPI)", m_vref_mv); + + return DR_ADC_OK; +} + +void dr_adc_uninit(void) +{ + if (!m_initialized) return; + + nrfx_spim_uninit(&m_spim); + + 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; + + *raw_value = spim_read_raw(); + + 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) +{ + volatile uint8_t rx_buf[2]; + uint32_t cs_mask = (1UL << DR_PIN_NUM(DR_ADC_PIN_CS)); + uint16_t raw16; + uint16_t i; + NRF_SPIM_Type *spim = m_spim.p_reg; + + 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; + + spim->RXD.MAXCNT = 2; + + for (i = 0; i < num_samples; i++) + { + NRF_P0->OUTCLR = cs_mask; + spim->RXD.PTR = (uint32_t)rx_buf; + spim->EVENTS_END = 0; + spim->TASKS_START = 1; + while (!spim->EVENTS_END) {} + NRF_P0->OUTSET = cs_mask; + + raw16 = ((uint16_t)rx_buf[0] << 8) | rx_buf[1]; + buffer[i] = (raw16 >> 1) & 0x0FFF; + } + + 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 dr_binary_tx_safe(uint8_t const *ble_bin_buff, uint16_t length); +extern void dr_sd_delay_ms(uint32_t ms); + +/* Platform interface (log function) */ +#include "parser.h" + +/*============================================================================== + * INTEGRATED BURST + CAPTURE + TRANSMIT + *============================================================================*/ + +/* External piezo burst function */ +extern void dr_piezo_burst_sw(uint8_t cycles); + +/* BLE packet constants */ +#define BLE_MTU_SIZE 244 /* ATT MTU 247 - 3 (ATT header) = 244 */ +#define BLE_REB_HEADER_LEN 6 /* "reb:" tag(4) + num_samples(2) */ +#define BLE_RED_HEADER_LEN 6 /* "red:" tag(4) + pkt_idx(2) */ +#define BLE_REB_DATA_LEN (BLE_MTU_SIZE - BLE_REB_HEADER_LEN) /* 238 bytes = 119 samples */ +#define BLE_RED_DATA_LEN (BLE_MTU_SIZE - BLE_RED_HEADER_LEN) /* 238 bytes = 119 samples */ +#define BLE_PACKET_DELAY_MS 100 /* Inter-packet delay - allow BLE TX buffer to drain */ + +/*============================================================================== + * 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) + * + * reb+red merged protocol: header와 데이터를 첫 패킷에 합침 + * + * Response format: + * Packet 1 (reb:): num_samples(2) + raw_data(up to 238 bytes = 119 samples) + * Packet 2~N (red:): pkt_idx(2) + raw_data(up to 238 bytes) — 119샘플 초과 시만 + * Final (raa:): status(2) — skip_raa=0 일 때만 + * + * With 100 samples: 200 bytes = reb:(6+200) = 206 bytes (단일 패킷) + */ +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 > 7) cycles = 5; /* Valid range: 3~7, default 5 */ + if (averaging == 0) averaging = 1; /* Minimum 1 */ + if (averaging > 1000) averaging = 1000; /* Maximum 1000 */ + if (piezo_ch >= MAA_NUM_CHANNELS) piezo_ch = 0; /* 채널 범위 검증 */ + + /* echo 분석은 PC에서 raw 데이터로 직접 수행 */ + + /* 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); + + /* MUX 전환 후 S/H 커패시터 안정화를 위한 dummy read */ + (void)spim_read_raw(); + + /*--- 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: Transmit via BLE - reb: + red: merged protocol ---*/ + /* Packet 1 (reb:): tag(4) + num_samples(2) + data(up to 238 bytes = 119 samples) */ + /* Packet 2~N (red:): tag(4) + pkt_idx(2) + data(up to 238 bytes) — only if > 119 samples */ + + uint16_t total_data_bytes = num_samples * 2; + uint16_t src_idx = 0; + + /* Packet 1: reb: header + data merged (Big-Endian) */ + ble_buffer[0] = 'r'; ble_buffer[1] = 'e'; ble_buffer[2] = 'b'; ble_buffer[3] = ':'; + ble_buffer[4] = (uint8_t)(num_samples >> 8); + ble_buffer[5] = (uint8_t)(num_samples & 0xFF); + + uint16_t dst_idx = BLE_REB_HEADER_LEN; + uint16_t first_chunk = (total_data_bytes > BLE_REB_DATA_LEN) ? BLE_REB_DATA_LEN : total_data_bytes; + while (src_idx < num_samples && (dst_idx - BLE_REB_HEADER_LEN) < first_chunk) { + uint16_t sample = m_echo_buffer[src_idx++] & 0x0FFF; + ble_buffer[dst_idx++] = (uint8_t)(sample >> 8); + ble_buffer[dst_idx++] = (uint8_t)(sample & 0xFF); + } + + dr_binary_tx_safe(ble_buffer, dst_idx / 2); + + /* Continuation packets (red:) — only if data didn't fit in reb: */ + uint16_t pkt = 0; + while (src_idx < num_samples) { + dr_sd_delay_ms(BLE_PACKET_DELAY_MS); + + ble_buffer[0] = 'r'; ble_buffer[1] = 'e'; ble_buffer[2] = 'd'; ble_buffer[3] = ':'; + ble_buffer[4] = (uint8_t)(pkt >> 8); + ble_buffer[5] = (uint8_t)(pkt & 0xFF); + + dst_idx = BLE_RED_HEADER_LEN; + while (src_idx < num_samples && (dst_idx - BLE_RED_HEADER_LEN) < BLE_RED_DATA_LEN) { + uint16_t sample = m_echo_buffer[src_idx++] & 0x0FFF; + ble_buffer[dst_idx++] = (uint8_t)(sample >> 8); + ble_buffer[dst_idx++] = (uint8_t)(sample & 0xFF); + } + + dr_binary_tx_safe(ble_buffer, dst_idx / 2); + pkt++; + } + + /* raa: completion — skip_raa: 0=send (mec), 1=skip (maa - caller sends final raa) */ + if (!skip_raa) { + dr_sd_delay_ms(BLE_PACKET_DELAY_MS); + 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); + } + + ADC_LOG("mec: reb+data merged, skip_raa=%u (%u samples)", skip_raa, num_samples); + + return DR_ADC_OK; +} + + +/*============================================================================== + * 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 > 7) 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); + + /* 채널 선택(MUX) : 1.3ms */ + dr_piezo_select_channel(piezo_ch); + + /* MUX 전환 후 S/H 커패시터 안정화를 위한 dummy read */ + (void)spim_read_raw(); + + /* 채널당 반복 측정 횟수만큼 */ + for (uint16_t avg_iter = 0; avg_iter < averaging; avg_iter++) + { + /* TX 펄스 출력(Burst) */ + switch (freq_option) { + case 0: + default: + dr_piezo_burst_sw_18mhz(cycles); + break; + case 1: + dr_piezo_burst_sw(cycles); /* 2.1MHz */ + break; + case 2: + dr_piezo_burst_sw_20mhz(cycles); + break; + case 3: + dr_piezo_burst_sw_17mhz(cycles); + break; + } + + /* TX 펄스 출력 후 ADC 시작 시까지 대기 시간 */ + if (delay_us > 0) + { + nrf_delay_us(delay_us); + } + + /* 측정 ADC 샘플 수만큼 캡처 */ + 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; + /* peak/baseline 분석은 PC에서 raw 데이터로 직접 수행 */ + + return DR_ADC_OK; +} + + +dr_adc_err_t dr_adc_transmit_channel(const dr_maa_channel_t *ch_data, + uint8_t *ble_buffer) +{ + if (ch_data == NULL || ble_buffer == NULL) { + return DR_ADC_ERR_INVALID_PARAM; + } + + uint16_t total_data_bytes = ch_data->num_samples * 2; + uint16_t src_idx = 0; + + /* Packet 1: reb: header + data merged (Big-Endian) */ + ble_buffer[0] = 'r'; ble_buffer[1] = 'e'; ble_buffer[2] = 'b'; ble_buffer[3] = ':'; + ble_buffer[4] = (uint8_t)(ch_data->num_samples >> 8); + ble_buffer[5] = (uint8_t)(ch_data->num_samples & 0xFF); + + uint16_t dst_idx = BLE_REB_HEADER_LEN; + uint16_t first_chunk = (total_data_bytes > BLE_REB_DATA_LEN) ? BLE_REB_DATA_LEN : total_data_bytes; + while (src_idx < ch_data->num_samples && (dst_idx - BLE_REB_HEADER_LEN) < first_chunk) { + uint16_t sample = ch_data->samples[src_idx++] & 0x0FFF; + ble_buffer[dst_idx++] = (uint8_t)(sample >> 8); + ble_buffer[dst_idx++] = (uint8_t)(sample & 0xFF); + } + + dr_binary_tx_safe(ble_buffer, dst_idx / 2); + + /* Continuation packets (red:) — only if data didn't fit in reb: */ + uint16_t pkt = 0; + while (src_idx < ch_data->num_samples) { + dr_sd_delay_ms(BLE_PACKET_DELAY_MS); + + ble_buffer[0] = 'r'; ble_buffer[1] = 'e'; ble_buffer[2] = 'd'; ble_buffer[3] = ':'; + ble_buffer[4] = (uint8_t)(pkt >> 8); + ble_buffer[5] = (uint8_t)(pkt & 0xFF); + + dst_idx = BLE_RED_HEADER_LEN; + while (src_idx < ch_data->num_samples && (dst_idx - BLE_RED_HEADER_LEN) < BLE_RED_DATA_LEN) { + uint16_t sample = ch_data->samples[src_idx++] & 0x0FFF; + ble_buffer[dst_idx++] = (uint8_t)(sample >> 8); + ble_buffer[dst_idx++] = (uint8_t)(sample & 0xFF); + } + + dr_binary_tx_safe(ble_buffer, dst_idx / 2); + pkt++; + } + + 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 (Big-Endian) */ + out_buffer[idx++] = (uint8_t)(samples[0] >> 8); + out_buffer[idx++] = (uint8_t)(samples[0] & 0xFF); + + /* 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] >> 8); + out_buffer[idx++] = (uint8_t)(samples[i] & 0xFF); + } + } + + *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)); + + /* Packet 1: rdb: header(8) + delta data merged (Big-Endian) */ + ble_buffer[0] = 'r'; ble_buffer[1] = 'd'; ble_buffer[2] = 'b'; ble_buffer[3] = ':'; + ble_buffer[4] = (uint8_t)(ch_data->num_samples >> 8); + ble_buffer[5] = (uint8_t)(ch_data->num_samples & 0xFF); + ble_buffer[6] = (uint8_t)(compressed_size >> 8); + ble_buffer[7] = (uint8_t)(compressed_size & 0xFF); + + #define BLE_RDB_HEADER_LEN 8 /* rdb: tag(4) + num_samples(2) + compressed_size(2) */ + uint16_t rdb_data_cap = BLE_MTU_SIZE - BLE_RDB_HEADER_LEN; /* 236 bytes */ + + uint16_t dst_idx = BLE_RDB_HEADER_LEN; + uint16_t src_idx = 0; + uint16_t first_chunk = (compressed_size > rdb_data_cap) ? rdb_data_cap : compressed_size; + while (src_idx < first_chunk) { + ble_buffer[dst_idx++] = delta_buffer[src_idx++]; + } + /* Pad to word boundary if needed */ + if (dst_idx & 1) ble_buffer[dst_idx++] = 0; + + dr_binary_tx_safe(ble_buffer, dst_idx / 2); + + /* Continuation packets (rdd:) — only if data didn't fit */ + uint16_t pkt = 0; + while (src_idx < compressed_size) { + dr_sd_delay_ms(BLE_PACKET_DELAY_MS); + + ble_buffer[0] = 'r'; ble_buffer[1] = 'd'; ble_buffer[2] = 'd'; ble_buffer[3] = ':'; + ble_buffer[4] = (uint8_t)(pkt >> 8); + ble_buffer[5] = (uint8_t)(pkt & 0xFF); + + dst_idx = BLE_RED_HEADER_LEN; + while (src_idx < compressed_size && (dst_idx - BLE_RED_HEADER_LEN) < BLE_RED_DATA_LEN) { + ble_buffer[dst_idx++] = delta_buffer[src_idx++]; + } + if (dst_idx & 1) ble_buffer[dst_idx++] = 0; + + dr_binary_tx_safe(ble_buffer, dst_idx / 2); + pkt++; + } + + 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 제거 — reb+red merged protocol) */ + +/** + * @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; + + dr_adc_err_t err = 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] + ); + + return err; +} + +/** + * @brief Send reb: header + data merged for current channel + * + * reb: tag(4) + num_samples(2) + data(up to 238 bytes) + * 100샘플(200B) 이하면 이 패킷 하나로 채널 전송 완료 + */ +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; + + /* reb: header + data merged (Big-Endian) */ + buf[0] = 'r'; buf[1] = 'e'; buf[2] = 'b'; buf[3] = ':'; + buf[4] = (uint8_t)(ch->num_samples >> 8); + buf[5] = (uint8_t)(ch->num_samples & 0xFF); + + uint16_t dst_idx = BLE_REB_HEADER_LEN; + uint16_t first_chunk = (total_data_bytes > BLE_REB_DATA_LEN) ? BLE_REB_DATA_LEN : total_data_bytes; + uint16_t src_idx = 0; + while (src_idx < ch->num_samples && (dst_idx - BLE_REB_HEADER_LEN) < first_chunk) { + uint16_t sample = ch->samples[src_idx++]; + buf[dst_idx++] = (uint8_t)(sample >> 8); + buf[dst_idx++] = (uint8_t)(sample & 0xFF); + } + + dr_binary_tx_safe(buf, dst_idx / 2); + dr_sd_delay_ms(5); /* dr_binary_tx_safe 내부 재시도(40ms)로 TX 완료 보장, 최소 딜레이만 */ + + g_maa_ctx.current_pkt = 0; + g_maa_ctx.data_offset = src_idx * 2; /* 이미 전송한 바이트 수 */ + + /* 데이터가 첫 패킷에 다 들어갔으면 바로 다음 채널로 */ + if (g_maa_ctx.data_offset >= total_data_bytes) { + g_maa_ctx.state = MAA_ASYNC_TX_DATA; /* send_data_packet()에서 false 반환 → 다음 채널 */ + } else { + g_maa_ctx.state = MAA_ASYNC_TX_DATA; + } +} + +/** + * @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_RED_DATA_LEN) ? BLE_RED_DATA_LEN : remaining; + + /* red: packet header (Big-Endian) */ + buf[0] = 'r'; buf[1] = 'e'; buf[2] = 'd'; buf[3] = ':'; + buf[4] = (uint8_t)(pkt_idx >> 8); + buf[5] = (uint8_t)(pkt_idx & 0xFF); + + /* 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 >> 8); + buf[dst_idx++] = (uint8_t)(sample & 0xFF); + } + + 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 >> 8); + buf[5] = (uint8_t)(status & 0xFF); + + dr_binary_tx_safe(buf, 3); + + //if (g_plat.log) g_plat.log("-------------------------------------------------------------------------------------\r\n"); + + /* 캡처 완료 → Piezo TX/RX 전원 OFF */ + dr_piezo_power_off(); + + g_maa_ctx.state = MAA_ASYNC_IDLE; + //ADC_LOG("maa_async: complete, status=0x%04X", status); + + /* 완료 콜백 호출 (mbb? 등에서 센서 측정 체인 트리거용) */ + if (g_maa_ctx.on_complete_cb) + { + void (*cb)(void) = g_maa_ctx.on_complete_cb; + g_maa_ctx.on_complete_cb = NULL; /* 1회성: 재호출 방지 */ + cb(); + } +} + +/*============================================================================== + * 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) +{ + dr_adc_err_t err; + uint8_t ch; + + 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 > 7) ? 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; + g_maa_ctx.on_complete_cb = NULL; /* 기본: 콜백 없음 (maa?) */ + + /* 전채널 캡처: BLE 전송 없이 6채널 전부 캡처 완료 후 TX 시작 */ + g_maa_ctx.state = MAA_ASYNC_CAPTURING; + + for (ch = 0; ch < MAA_NUM_CHANNELS; ch++) + { + err = maa_async_capture_channel(ch); + if (err != DR_ADC_OK) + { + if (g_plat.log) g_plat.log("[maa] maa_async_start: CH%u capture failed (%d)", ch, err); + maa_async_send_completion(0xFFF0 | ch); + return err; + } + } + + /* peak 로그와 raw 덤프 사이 구분 */ + //if (g_plat.log) g_plat.log("\r\n"); + + /* 캡처 완료 → Piezo TX/RX 전원 OFF (BLE 전송 중 불필요) */ + dr_piezo_power_off(); + + /* 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 + { + /* 이미 전채널 캡처됨, 바로 헤더 전송 */ + g_maa_ctx.state = MAA_ASYNC_CAPTURING; + 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); +} + +void maa_async_set_pre_capture_all(bool on) +{ + g_maa_ctx.pre_capture_all = on; +} + +maa_async_state_t maa_async_get_state(void) +{ + return g_maa_ctx.state; +} + +/*============================================================================== + * 비동기 측정 상태 IDLE로 초기화 - BLE 연결이 끊어지는 경우 먹통 현상 방지 + *============================================================================*/ +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; // 측정 상태 IDLE로 초기화 + g_maa_ctx.on_complete_cb = NULL; // abort 후 콜백 호출 방지 + dr_piezo_power_off(); // 전체 측정 중간에 끊기는 경우 Piezo TX/RX Sleep + } +} + +void maa_async_set_on_complete(void (*cb)(void)) +{ + g_maa_ctx.on_complete_cb = cb; +} + + diff --git a/pc_firm/dr_adc121s051/dr_adc121s051.h b/project/ble_peripheral/ble_app_bladder_patch/measurement/adc121s051/dr_adc121s051.h similarity index 97% rename from pc_firm/dr_adc121s051/dr_adc121s051.h rename to project/ble_peripheral/ble_app_bladder_patch/measurement/adc121s051/dr_adc121s051.h index 163ef83..e75ba8a 100644 --- a/pc_firm/dr_adc121s051/dr_adc121s051.h +++ b/project/ble_peripheral/ble_app_bladder_patch/measurement/adc121s051/dr_adc121s051.h @@ -1,516 +1,516 @@ -/******************************************************************************* - * @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 119 /**< 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~7) - * @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+data merged), red: (continuation, >119 samples only) - * @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 6 /* 4 -> 8 -> 6 jhChun 26.03.17*/ -#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 */ -} 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 merged protocol. - * reb: tag(4) + num_samples(2) + data(up to 238B). red: only if > 119 samples. - * - * @param ch_data Pointer to captured channel data - * @param ble_buffer Working buffer for BLE packets (>= 244 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 merged protocol (delta variant of reb+red merged) - * - * @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 all CH -> TX reb:(header+data) -> TX red: (if needed) - * BLE_NUS_EVT_TX_RDY -> maa_async_on_tx_ready() -> 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+data merged */ - 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 */ - bool pre_capture_all; /**< true: 전채널 캡처 완료 후 일괄 전송 (mbb용) */ - void (*on_complete_cb)(void); /**< 비동기 캡처 완료 후 호출될 콜백 (NULL이면 미사용) */ -} 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~7) - * @param averaging Averaging count (1~1000) - * @param ble_buffer Working buffer (>= 244 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); - -/** - * @brief 자동 전원 플래그 설정 (완료 후 자동 power off) - */ -void maa_async_set_auto_power(bool on); -void maa_async_set_pre_capture_all(bool on); - - -/** - * @brief 비동기 캡처 완료 콜백 설정 - * raa: 전송 + 전원 OFF 이후 호출된다. NULL이면 콜백 없음. - */ -void maa_async_set_on_complete(void (*cb)(void)); - -#endif /* DR_ADC121S051_H */ - +/******************************************************************************* + * @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 119 /**< 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~7) + * @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+data merged), red: (continuation, >119 samples only) + * @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 6 /* 4 -> 8 -> 6 jhChun 26.03.17*/ +#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 */ +} 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 merged protocol. + * reb: tag(4) + num_samples(2) + data(up to 238B). red: only if > 119 samples. + * + * @param ch_data Pointer to captured channel data + * @param ble_buffer Working buffer for BLE packets (>= 244 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 merged protocol (delta variant of reb+red merged) + * + * @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 all CH -> TX reb:(header+data) -> TX red: (if needed) + * BLE_NUS_EVT_TX_RDY -> maa_async_on_tx_ready() -> 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+data merged */ + 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 */ + bool pre_capture_all; /**< true: 전채널 캡처 완료 후 일괄 전송 (mbb용) */ + void (*on_complete_cb)(void); /**< 비동기 캡처 완료 후 호출될 콜백 (NULL이면 미사용) */ +} 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~7) + * @param averaging Averaging count (1~1000) + * @param ble_buffer Working buffer (>= 244 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); + +/** + * @brief 자동 전원 플래그 설정 (완료 후 자동 power off) + */ +void maa_async_set_auto_power(bool on); +void maa_async_set_pre_capture_all(bool on); + + +/** + * @brief 비동기 캡처 완료 콜백 설정 + * raa: 전송 + 전원 OFF 이후 호출된다. NULL이면 콜백 없음. + */ +void maa_async_set_on_complete(void (*cb)(void)); + +#endif /* DR_ADC121S051_H */ + diff --git a/project/ble_peripheral/dr_piezo/dr_piezo.c b/project/ble_peripheral/ble_app_bladder_patch/measurement/piezo/dr_piezo.c similarity index 97% rename from project/ble_peripheral/dr_piezo/dr_piezo.c rename to project/ble_peripheral/ble_app_bladder_patch/measurement/piezo/dr_piezo.c index 7ba78dc..c20a8b1 100644 --- a/project/ble_peripheral/dr_piezo/dr_piezo.c +++ b/project/ble_peripheral/ble_app_bladder_patch/measurement/piezo/dr_piezo.c @@ -1,1583 +1,1583 @@ -/******************************************************************************* - * @file dr_piezo.c - * @brief Piezo Transducer Driver (2MHz Signal Generator) - * @author Charles KWON - * @date 2025-12-09 - * - * @details Uses Timer2 + GPIOTE + PPI for CPU-free 2MHz waveform generation - * - * Timing Diagram (???): - * - * |<----------- PE HIGH ----------->| - * PE ___/?????????????????????????????????\___ - * P_OUT ___/?\_/?\_/?\_/?\_/?\________________\___ - * N_OUT ___\_/?\_/?\_/?\_/?\_/________________\___ - * DMP _________________________/?????\_________ - * |<-- 3~5 cycles -->| || - * - * P_OUT? N_OUT? ?? ?? ?? (???) - * - * 2MHz = 500ns period = 250ns half-period - * Timer @ 16MHz: 1 tick = 62.5ns - * Half-period = 250ns = 4 ticks - * Full-period = 500ns = 8 ticks - ******************************************************************************/ - -/******************************************************************************* - * [한국어 설명] 피에조 초음파 트랜스듀서 드라이버 - * - * === 개요 === - * 방광 측정용 2MHz 초음파 송신 신호를 생성하는 드라이버. - * nRF52840의 하드웨어 주변장치(Timer2 + GPIOTE + PPI)를 활용하여 - * CPU 개입 없이 정밀한 2MHz 파형을 자동으로 생성한다. - * - * === 초음파 TX 시퀀스 === - * 1단계: PE(Pulse Enable) = HIGH -> MOSFET 드라이버 활성화 - * 2단계: P_OUT/N_OUT 교번 펄스 생성 (2MHz, 3~7 사이클) - * - P_OUT과 N_OUT은 역상(반대 위상)으로 동작 - * - 피에조 소자 양단에 +/-20V 교번 전압 인가 - * 3단계: DMP(Dump) = HIGH -> 펄스 완료 후 피에조에 남은 잔류 에너지 방전 - * 4단계: DMP = LOW -> 방전 완료 - * 5단계: PE = LOW -> MOSFET 드라이버 비활성화, 유휴 상태 복귀 - * - * === 하드웨어 아키텍처 === - * [Timer2] --- CC[0](반주기=4틱) ---> [PPI CH8,9] ---> [GPIOTE CH4,5] -> P_OUT/N_OUT 토글 - * |-- CC[1](전체주기=8틱) --> [PPI CH10,11] --> [GPIOTE CH4,5] -> P_OUT/N_OUT 토글 - * |-- CC[2](전체주기=8틱) --> 타이머 자동 클리어 + 인터럽트(잔여 사이클 카운트) - * - * - Timer2: 16MHz 클럭, 16비트 모드 - * - CC[0] = 4틱(250ns) -> 반주기 시점에서 P_OUT/N_OUT 토글 - * - CC[1] = 8틱(500ns) -> 전체주기 시점에서 P_OUT/N_OUT 토글 - * - CC[2] = 8틱(500ns) -> 인터럽트 발생 + 타이머 자동 클리어(SHORT) - * - * - GPIOTE CH4: P_OUT 핀 토글 모드 - * - GPIOTE CH5: N_OUT 핀 토글 모드 - * - * - PPI CH8: CC[0] 이벤트 -> P_OUT 토글 태스크 - * - PPI CH9: CC[0] 이벤트 -> N_OUT 토글 태스크 - * - PPI CH10: CC[1] 이벤트 -> P_OUT 토글 태스크 - * - PPI CH11: CC[1] 이벤트 -> N_OUT 토글 태스크 - * - * === 전원 === - * DR_PIEZO_PWR_EN(P1.9) -> DC/DC 컨버터 활성화 -> +/-20V 고전압 생성 - * - * === 소프트웨어 버스트 모드 (dr_piezo_burst_sw 계열) === - * Timer/PPI 대신 CPU에서 직접 GPIO를 제어하는 방식. - * 인터럽트를 비활성화(__disable_irq)하고 NOP 명령어로 정밀 타이밍 생성. - * 포트 레지스터(NRF_P1->OUT)에 직접 접근하여 여러 핀을 동시에 제어. - * 주파수별(1.7/1.8/1.9/2.0/2.1/2.2 MHz) NOP 개수가 다름. - * - * NOP 타이밍 계산법: - * CPU 클럭 64MHz -> 1 NOP = 15.625ns - * 목표 반주기(ns) = 1,000,000 / (목표주파수MHz * 2) - * 필요 NOP 수 = (목표 반주기 - 레지스터 쓰기 시간(~30ns)) / 15.625 - * 루프 오버헤드(~47ns = 3 NOP)는 두 번째 반주기에서 차감 - ******************************************************************************/ - -/* 헤더 포함 */ -#include "dr_piezo.h" -#include "nrf_gpio.h" /* GPIO 제어 (핀 설정, 출력) */ -#include "nrf_timer.h" /* 타이머 주변장치 제어 */ -#include "nrf_gpiote.h" /* GPIOTE (GPIO Tasks and Events) 제어 */ -#include "nrf_ppi.h" /* PPI (Programmable Peripheral Interconnect) 제어 */ -#include "nrf_delay.h" /* 지연(딜레이) 함수 */ -#include "power_control.h" /* 전원 관리 */ -#include "app_util_platform.h" /* 인터럽트 우선순위 등 플랫폼 유틸리티 */ - -/* 조건부 디버그 출력: FEATURE_PRINTF 정의 시 SEGGER RTT로 출력 */ -#ifdef FEATURE_PRINTF -#include "debug_print.h" -#else -#define DBG_PRINTF(...) -#endif - -/*============================================================================== - * 하드웨어 리소스 할당 - * - Timer2: 2MHz 파형 생성의 시간 기준 (16MHz 클럭) - * - GPIOTE CH4/5: P_OUT/N_OUT 핀의 하드웨어 토글 - * - PPI CH8~11: 타이머 비교 이벤트 -> GPIOTE 토글 태스크 자동 연결 - *============================================================================*/ -#define PIEZO_TIMER NRF_TIMER2 /* 사용할 타이머 인스턴스 */ -#define PIEZO_TIMER_IRQn TIMER2_IRQn /* 타이머2 인터럽트 번호 */ -#define PIEZO_TIMER_IRQ_PRIORITY 6 /* 인터럽트 우선순위 (6 = 중간) */ - -/* GPIOTE 채널 할당 - 핀 토글 제어용 */ -#define GPIOTE_CH_P_OUT 4 /* P_OUT(양극 출력) 토글용 GPIOTE 채널 */ -#define GPIOTE_CH_N_OUT 5 /* N_OUT(음극 출력) 토글용 GPIOTE 채널 */ - -/* PPI 채널 할당 - 타이머 이벤트 -> GPIOTE 태스크 연결 */ -#define PPI_CH_P_OUT_TOGGLE_0 8 /* CC[0](반주기) -> P_OUT 토글 */ -#define PPI_CH_N_OUT_TOGGLE_0 9 /* CC[0](반주기) -> N_OUT 토글 */ -#define PPI_CH_P_OUT_TOGGLE_1 10 /* CC[1](전체주기) -> P_OUT 토글 */ -#define PPI_CH_N_OUT_TOGGLE_1 11 /* CC[1](전체주기) -> N_OUT 토글 */ - -/*============================================================================== - * 타이밍 상수 - * Timer2 클럭: 16MHz -> 1틱 = 62.5ns - * 2MHz 신호: 주기 500ns(8틱), 반주기 250ns(4틱) - *============================================================================*/ -#define TIMER_FREQ_MHZ 16 /* 타이머 클럭 주파수 (MHz) */ -#define TICK_NS (1000 / TIMER_FREQ_MHZ) /* 1틱 = 62.5ns */ - -/* 2MHz 기준 타이밍: 주기 = 500ns, 반주기 = 250ns */ -#define PERIOD_TICKS_2MHZ 8 /* 전체 주기: 500ns / 62.5ns = 8틱 */ -#define HALF_PERIOD_TICKS 4 /* 반주기: 250ns / 62.5ns = 4틱 */ - -/*============================================================================== - * 피에조 동작 주파수 설정 - *============================================================================*/ -/* - * 목표 피에조 주파수: 2.1 MHz (하드웨어 버스트 모드용) - * - * 소프트웨어 버스트 모드의 타이밍은 각 주파수별 함수에 NOP 개수로 하드코딩됨. - * 안정적인 파형 생성을 위해 컴파일 타임에 고정. - * - * NOP 기반 타이밍 계산 (CPU 64MHz, 1 NOP = 15.625ns): - * 1.7 MHz: 반주기 294ns -> 첫 반주기 18 NOP, 둘째 반주기 10 NOP + 루프 오버헤드 - * 1.8 MHz: 반주기 278ns -> 첫 반주기 17 NOP, 둘째 반주기 11 NOP + 루프 오버헤드 - * 1.9 MHz: 반주기 263ns -> 첫 반주기 15 NOP, 둘째 반주기 9 NOP + 루프 오버헤드 - * 2.0 MHz: 반주기 250ns -> 첫 반주기 15 NOP, 둘째 반주기 10 NOP + 루프 오버헤드 - * 2.1 MHz: 반주기 238ns -> 첫 반주기 14 NOP, 둘째 반주기 9 NOP + 루프 오버헤드 - * 2.2 MHz: 반주기 227ns -> 첫 반주기 13 NOP, 둘째 반주기 8 NOP + 루프 오버헤드 - * - * 주파수를 변경하려면 dr_piezo_burst_sw_XXmhz() 함수의 NOP 수를 수정할 것. - */ -#define PIEZO_FREQ_MHZ 2.1f /* 기본 동작 주파수 (MHz) */ - -/*============================================================================== - * 정적 변수 - *============================================================================*/ -static volatile bool m_tx_active = false; /* TX 송신 중 플래그 (인터럽트에서 변경) */ -static volatile uint8_t m_remaining_cycles = 0; /* 남은 펄스 사이클 수 (인터럽트에서 감소) */ -static uint32_t m_period_ticks = PERIOD_TICKS_2MHZ; /* 현재 주기 (타이머 틱 단위) */ -static bool m_power_enabled = false; /* DC/DC 컨버터 전원 상태 */ -static bool m_initialized = false; /* 드라이버 초기화 완료 여부 */ - -/*============================================================================== - * 타이머2 인터럽트 핸들러 - * 매 주기(CC[2])마다 호출되어 잔여 사이클을 감소시킨다. - * 잔여 사이클이 0이 되면: - * 1) 타이머 정지 및 클리어 - * 2) GPIOTE 비활성화 (P_OUT/N_OUT 토글 중단) - * 3) GPIO를 출력 모드로 재설정 후 LOW로 초기화 - * 4) DMP 펄스 발생 (피에조 잔류 에너지 방전) - * 5) PE = LOW (MOSFET 드라이버 비활성화) - *============================================================================*/ -void TIMER2_IRQHandler(void) -{ - if (nrf_timer_event_check(PIEZO_TIMER, NRF_TIMER_EVENT_COMPARE2)) - { - nrf_timer_event_clear(PIEZO_TIMER, NRF_TIMER_EVENT_COMPARE2); - - if (m_remaining_cycles > 0) - { - m_remaining_cycles--; - } - - if (m_remaining_cycles == 0) - { - /* Step 1: Stop timer */ - nrf_timer_task_trigger(PIEZO_TIMER, NRF_TIMER_TASK_STOP); - nrf_timer_task_trigger(PIEZO_TIMER, NRF_TIMER_TASK_CLEAR); - - /* Step 2: Disable GPIOTE (GPIO ??? ??) */ - nrf_gpiote_task_disable(GPIOTE_CH_P_OUT); - nrf_gpiote_task_disable(GPIOTE_CH_N_OUT); - - /* Step 3: GPIO? ?? ? idle ?? */ - nrf_gpio_cfg_output(DR_PIEZO_PIN_P_OUT); - nrf_gpio_cfg_output(DR_PIEZO_PIN_N_OUT); - nrf_gpio_pin_clear(DR_PIEZO_PIN_P_OUT); - nrf_gpio_pin_clear(DR_PIEZO_PIN_N_OUT); - - /* Step 4: DMP pulse */ - nrf_gpio_pin_set(DR_PIEZO_PIN_DMP); - __NOP(); __NOP(); __NOP(); __NOP(); - __NOP(); __NOP(); __NOP(); __NOP(); - __NOP(); __NOP(); __NOP(); __NOP(); - __NOP(); __NOP(); __NOP(); __NOP(); - nrf_gpio_pin_clear(DR_PIEZO_PIN_DMP); - - /* Step 5: PE = LOW */ - nrf_gpio_pin_clear(DR_PIEZO_PIN_PE); - - m_tx_active = false; - } - } -} - -/*============================================================================== - * 전원 제어 함수 - * DC/DC 컨버터(±20V)를 ON/OFF하여 피에조 구동 전압을 제어한다. - * 전원 안정화에 약 10ms 필요. - *============================================================================*/ - -/* 피에조 전원 ON: DC/DC 컨버터 활성화 → ±20V 생성 */ -void dr_piezo_power_on(void) -{ - //nrf_delay_ms(20); - - nrf_gpio_cfg_output(DR_PIEZO_PWR_EN); - nrf_gpio_pin_set(DR_PIEZO_PWR_EN); - - /* Wait for power stabilization */ - nrf_delay_ms(3); - - m_power_enabled = true; - - //DBG_PRINTF("[PIEZO] TX/RX Active: +/-20V ready\r\n"); -} - -/* 피에조 전원 OFF: TX 비활성화 → MUX 비활성화 → DC/DC 컨버터 차단 */ -void dr_piezo_power_off(void) -{ - dr_piezo_disable(); - - /* MUX enable 핀 클리어: select_channel() 이후 HIGH로 남은 핀 해제 */ - nrf_gpio_pin_clear(DR_PIEZO_EN_MUXA); - nrf_gpio_pin_clear(DR_PIEZO_EN_MUXB); - nrf_gpio_pin_clear(DR_PIEZO_MUX_SEL0); - nrf_gpio_pin_clear(DR_PIEZO_MUX_SEL1); - - nrf_gpio_pin_clear(DR_PIEZO_PWR_EN); - - m_power_enabled = false; - - //DBG_PRINTF("[PIEZO] Power OFF\r\n"); -} - -/* 피에조 전원 상태 확인 */ -bool dr_piezo_is_power_on(void) -{ - return m_power_enabled; -} - -/*============================================================================== - * 내부(private) 초기화 함수 - *============================================================================*/ - -/* GPIO 초기화: 모든 신호 핀을 출력 모드로 설정하고 LOW(유휴)로 초기화 */ -static void dr_piezo_gpio_init(void) -{ - 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); - - /* All LOW = idle */ - nrf_gpio_pin_clear(DR_PIEZO_PIN_PE); - nrf_gpio_pin_clear(DR_PIEZO_PIN_DMP); - nrf_gpio_pin_clear(DR_PIEZO_PIN_P_OUT); - nrf_gpio_pin_clear(DR_PIEZO_PIN_N_OUT); - - /* Initialize MUX control pins */ - dr_piezo_mux_init(); -} - -/* GPIOTE 초기화: P_OUT/N_OUT을 토글 모드로 설정 (PPI 연결 대상) */ -static void dr_piezo_gpiote_init(void) -{ - /* P_OUT: Toggle mode, initial LOW */ - nrf_gpiote_task_configure( - GPIOTE_CH_P_OUT, - DR_PIEZO_PIN_P_OUT, - NRF_GPIOTE_POLARITY_TOGGLE, - NRF_GPIOTE_INITIAL_VALUE_LOW - ); - nrf_gpiote_task_enable(GPIOTE_CH_P_OUT); - - /* N_OUT: Toggle mode, initial LOW */ - nrf_gpiote_task_configure( - GPIOTE_CH_N_OUT, - DR_PIEZO_PIN_N_OUT, - NRF_GPIOTE_POLARITY_TOGGLE, - NRF_GPIOTE_INITIAL_VALUE_LOW - ); - nrf_gpiote_task_enable(GPIOTE_CH_N_OUT); -} - -/* - * 타이머 초기화: 16MHz 클럭, 16비트 모드 - * CC[0]=반주기(4틱): 반주기 시점 토글 이벤트 - * CC[1]=전체주기(8틱): 전체주기 시점 토글 이벤트 - * CC[2]=전체주기(8틱): 인터럽트 발생 + 타이머 자동 클리어(SHORT) - */ -static void dr_piezo_timer_init(void) -{ - nrf_timer_task_trigger(PIEZO_TIMER, NRF_TIMER_TASK_STOP); - nrf_timer_task_trigger(PIEZO_TIMER, NRF_TIMER_TASK_CLEAR); - - nrf_timer_mode_set(PIEZO_TIMER, NRF_TIMER_MODE_TIMER); - nrf_timer_bit_width_set(PIEZO_TIMER, NRF_TIMER_BIT_WIDTH_16); - nrf_timer_frequency_set(PIEZO_TIMER, NRF_TIMER_FREQ_16MHz); - - /* - * ??? ??? ?? ???: - * - CC[0] = 4 (half period): P_OUT ??, N_OUT ?? - * - CC[1] = 8 (full period): P_OUT ??, N_OUT ?? - * - CC[2] = 8 (full period): ??? ??? + CLEAR - * - * ?? ??: P=HIGH, N=LOW - * t=4: ? ? ?? -> P=LOW, N=HIGH - * t=8: ? ? ?? -> P=HIGH, N=LOW (+ CLEAR) - * t=12: ? ? ?? -> P=LOW, N=HIGH - * ... - */ - - /* CC[0]: ??? ?? */ - nrf_timer_cc_write(PIEZO_TIMER, NRF_TIMER_CC_CHANNEL0, HALF_PERIOD_TICKS); // 4 - - /* CC[1]: ? ?? ?? */ - nrf_timer_cc_write(PIEZO_TIMER, NRF_TIMER_CC_CHANNEL1, m_period_ticks); // 8 - - /* CC[2]: ? ?? - ??? ???? */ - nrf_timer_cc_write(PIEZO_TIMER, NRF_TIMER_CC_CHANNEL2, m_period_ticks); // 8 - - /* CC[2]?? ?? CLEAR */ - nrf_timer_shorts_enable(PIEZO_TIMER, NRF_TIMER_SHORT_COMPARE2_CLEAR_MASK); - - /* CC[2] ???? (??? ???) */ - nrf_timer_int_enable(PIEZO_TIMER, NRF_TIMER_INT_COMPARE2_MASK); - - NVIC_SetPriority(PIEZO_TIMER_IRQn, PIEZO_TIMER_IRQ_PRIORITY); - NVIC_EnableIRQ(PIEZO_TIMER_IRQn); -} - -/* - * PPI 초기화: 타이머 비교 이벤트 → GPIOTE 토글 태스크 연결 (4채널) - * CC[0] 이벤트(반주기) → P_OUT 토글 + N_OUT 토글 - * CC[1] 이벤트(전체주기) → P_OUT 토글 + N_OUT 토글 - * 이로써 P_OUT과 N_OUT은 항상 역상으로 동작함. - */ -static void dr_piezo_ppi_init(void) -{ - /* - * ??? ??: - * CC[0] (t=4): P_OUT ??, N_OUT ?? - * CC[1] (t=8): P_OUT ??, N_OUT ?? - */ - - /* CC[0] -> P_OUT toggle */ - nrf_ppi_channel_endpoint_setup( - (nrf_ppi_channel_t)PPI_CH_P_OUT_TOGGLE_0, - (uint32_t)nrf_timer_event_address_get(PIEZO_TIMER, NRF_TIMER_EVENT_COMPARE0), - (uint32_t)&NRF_GPIOTE->TASKS_OUT[GPIOTE_CH_P_OUT] - ); - nrf_ppi_channel_enable((nrf_ppi_channel_t)PPI_CH_P_OUT_TOGGLE_0); - - /* CC[0] -> N_OUT toggle */ - nrf_ppi_channel_endpoint_setup( - (nrf_ppi_channel_t)PPI_CH_N_OUT_TOGGLE_0, - (uint32_t)nrf_timer_event_address_get(PIEZO_TIMER, NRF_TIMER_EVENT_COMPARE0), - (uint32_t)&NRF_GPIOTE->TASKS_OUT[GPIOTE_CH_N_OUT] - ); - nrf_ppi_channel_enable((nrf_ppi_channel_t)PPI_CH_N_OUT_TOGGLE_0); - - /* CC[1] -> P_OUT toggle */ - nrf_ppi_channel_endpoint_setup( - (nrf_ppi_channel_t)PPI_CH_P_OUT_TOGGLE_1, - (uint32_t)nrf_timer_event_address_get(PIEZO_TIMER, NRF_TIMER_EVENT_COMPARE1), - (uint32_t)&NRF_GPIOTE->TASKS_OUT[GPIOTE_CH_P_OUT] - ); - nrf_ppi_channel_enable((nrf_ppi_channel_t)PPI_CH_P_OUT_TOGGLE_1); - - /* CC[1] -> N_OUT toggle */ - nrf_ppi_channel_endpoint_setup( - (nrf_ppi_channel_t)PPI_CH_N_OUT_TOGGLE_1, - (uint32_t)nrf_timer_event_address_get(PIEZO_TIMER, NRF_TIMER_EVENT_COMPARE1), - (uint32_t)&NRF_GPIOTE->TASKS_OUT[GPIOTE_CH_N_OUT] - ); - nrf_ppi_channel_enable((nrf_ppi_channel_t)PPI_CH_N_OUT_TOGGLE_1); -} - -/*============================================================================== - * TX 드라이버 공개 함수 - *============================================================================*/ - -/* TX 드라이버 초기화: GPIO → GPIOTE → Timer → PPI 순서로 설정 */ -void dr_piezo_init(void) -{ - dr_piezo_gpio_init(); - dr_piezo_gpiote_init(); - dr_piezo_timer_init(); - dr_piezo_ppi_init(); - - m_tx_active = false; - m_remaining_cycles = 0; - m_initialized = true; -} - -/* TX 드라이버 해제: 타이머 정지, PPI/GPIOTE 비활성화, 모든 핀 LOW */ -void dr_piezo_uninit(void) -{ - nrf_timer_task_trigger(PIEZO_TIMER, NRF_TIMER_TASK_STOP); - nrf_timer_int_disable(PIEZO_TIMER, NRF_TIMER_INT_COMPARE2_MASK); - NVIC_DisableIRQ(PIEZO_TIMER_IRQn); - - nrf_ppi_channel_disable((nrf_ppi_channel_t)PPI_CH_P_OUT_TOGGLE_0); - nrf_ppi_channel_disable((nrf_ppi_channel_t)PPI_CH_N_OUT_TOGGLE_0); - nrf_ppi_channel_disable((nrf_ppi_channel_t)PPI_CH_P_OUT_TOGGLE_1); - nrf_ppi_channel_disable((nrf_ppi_channel_t)PPI_CH_N_OUT_TOGGLE_1); - - nrf_gpiote_task_disable(GPIOTE_CH_P_OUT); - nrf_gpiote_task_disable(GPIOTE_CH_N_OUT); - - nrf_gpio_pin_clear(DR_PIEZO_PIN_PE); - nrf_gpio_pin_clear(DR_PIEZO_PIN_DMP); - nrf_gpio_pin_clear(DR_PIEZO_PIN_P_OUT); - nrf_gpio_pin_clear(DR_PIEZO_PIN_N_OUT); - - m_tx_active = false; - m_initialized = false; -} - -/* - * 하드웨어 기반 버스트 송신 (Timer + PPI + GPIOTE 사용) - * 1) GPIOTE를 재설정: P_OUT=HIGH 시작, N_OUT=LOW 시작 (역상) - * 2) PE = HIGH → MOSFET 드라이버 활성화 - * 3) 타이머 시작 → PPI가 자동으로 P_OUT/N_OUT 토글 - * 4) 인터럽트 핸들러에서 잔여 사이클 관리 및 종료 처리 - */ -void dr_piezo_burst(uint8_t cycles) -{ - if (m_tx_active) - { - DBG_PRINTF("[DR_PIEZO] TX busy!\r\n"); - return; - } - - if (!m_initialized) - { - DBG_PRINTF("[DR_PIEZO] ERROR: Not initialized!\r\n"); - return; - } - - if (cycles < DR_PIEZO_MIN_CYCLES) cycles = DR_PIEZO_MIN_CYCLES; - if (cycles > DR_PIEZO_MAX_CYCLES) cycles = DR_PIEZO_MAX_CYCLES; - - DBG_PRINTF("[DR_PIEZO] Burst: %d cycles\r\n", cycles); - - m_remaining_cycles = cycles; - m_tx_active = true; - - /* GPIOTE ??? */ - nrf_gpiote_task_disable(GPIOTE_CH_P_OUT); - nrf_gpiote_task_disable(GPIOTE_CH_N_OUT); - - nrf_gpiote_task_configure( - GPIOTE_CH_P_OUT, - DR_PIEZO_PIN_P_OUT, - NRF_GPIOTE_POLARITY_TOGGLE, - NRF_GPIOTE_INITIAL_VALUE_HIGH /* P_OUT starts HIGH */ - ); - nrf_gpiote_task_enable(GPIOTE_CH_P_OUT); - - nrf_gpiote_task_configure( - GPIOTE_CH_N_OUT, - DR_PIEZO_PIN_N_OUT, - NRF_GPIOTE_POLARITY_TOGGLE, - NRF_GPIOTE_INITIAL_VALUE_LOW /* N_OUT starts LOW */ - ); - nrf_gpiote_task_enable(GPIOTE_CH_N_OUT); - - /* DMP = LOW */ - nrf_gpio_pin_clear(DR_PIEZO_PIN_DMP); - - /* Timer clear */ - nrf_timer_task_trigger(PIEZO_TIMER, NRF_TIMER_TASK_CLEAR); - - /* Clear pending events */ - nrf_timer_event_clear(PIEZO_TIMER, NRF_TIMER_EVENT_COMPARE0); - nrf_timer_event_clear(PIEZO_TIMER, NRF_TIMER_EVENT_COMPARE1); - nrf_timer_event_clear(PIEZO_TIMER, NRF_TIMER_EVENT_COMPARE2); - - /* PE = HIGH (??? ??) */ - nrf_gpio_pin_set(DR_PIEZO_PIN_PE); - - /* ??? ?? ? ??? ?? */ - __NOP(); __NOP(); __NOP(); __NOP(); - - /* Timer START -> PPI? ???? P_OUT/N_OUT ?? */ - nrf_timer_task_trigger(PIEZO_TIMER, NRF_TIMER_TASK_START); -} - -/* 기본 사이클 수(5)로 버스트 송신 */ -void dr_piezo_pulse(void) -{ - dr_piezo_burst(DR_PIEZO_DEFAULT_CYCLES); -} - -/* TX 출력 활성화: 수동으로 초기 상태 설정 (PE=HIGH, P_OUT=HIGH, N_OUT=LOW) */ -void dr_piezo_enable(void) -{ - nrf_gpio_pin_set(DR_PIEZO_PIN_P_OUT); - nrf_gpio_pin_clear(DR_PIEZO_PIN_N_OUT); - nrf_gpio_pin_clear(DR_PIEZO_PIN_DMP); - nrf_gpio_pin_set(DR_PIEZO_PIN_PE); - DBG_PRINTF("[DR_PIEZO] TX enabled\r\n"); -} - -/* TX 출력 비활성화: 모든 신호 핀 LOW로 복귀 (유휴 상태) */ -void dr_piezo_disable(void) -{ - nrf_gpio_pin_clear(DR_PIEZO_PIN_DMP); - nrf_gpio_pin_clear(DR_PIEZO_PIN_PE); - nrf_gpio_pin_clear(DR_PIEZO_PIN_P_OUT); - nrf_gpio_pin_clear(DR_PIEZO_PIN_N_OUT); -} - -/* TX 송신 중 여부 확인 (인터럽트 핸들러에서 false로 전환) */ -bool dr_piezo_is_busy(void) -{ - return m_tx_active; -} - -/* 동작 주파수 변경 (100kHz~4MHz): 타이머 CC 레지스터 재설정 */ -void dr_piezo_set_frequency(uint32_t freq_hz) -{ - if (freq_hz < 100000 || freq_hz > 4000000) - { - DBG_PRINTF("[DR_PIEZO] Invalid freq: %d Hz\r\n", freq_hz); - return; - } - - uint32_t period_ns = 1000000000UL / freq_hz; - m_period_ticks = period_ns * TIMER_FREQ_MHZ / 1000; - - nrf_timer_cc_write(PIEZO_TIMER, NRF_TIMER_CC_CHANNEL0, m_period_ticks / 2); - nrf_timer_cc_write(PIEZO_TIMER, NRF_TIMER_CC_CHANNEL1, m_period_ticks); - nrf_timer_cc_write(PIEZO_TIMER, NRF_TIMER_CC_CHANNEL2, m_period_ticks); - - DBG_PRINTF("[DR_PIEZO] Freq: %d Hz (period=%d ticks)\r\n", freq_hz, m_period_ticks); -} - -/*============================================================================== - * MUX 제어 함수 - * 8채널 아날로그 MUX로 피에조 에코 신호 경로를 선택한다. - * MUXA: CH0~CH3 담당, MUXB: CH4~CH7 담당 - * SEL0, SEL1: MUX 내부 채널 주소 선택 - * High Drive(H0H1) 모드: MUX IC의 빠른 스위칭을 위해 강한 출력 구동력 사용 - *============================================================================*/ - -/* MUX 제어 핀 초기화: 4개 핀 모두 High Drive 출력으로 설정, 기본값 LOW */ -void dr_piezo_mux_init(void) -{ - /* 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 - ); - - 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 */ -} - - -/* - * 피에조 채널 선택 (0~7) - * 채널 매핑 (EN_MUXA, EN_MUXB, SEL0, SEL1): - * CH0 = MUXA 입력0 (1,0,0,0) CH4 = MUXB 입력0 (0,1,1,1) - * CH1 = MUXA 입력2 (1,0,1,0) CH5 = MUXB 입력1 (0,1,0,1) - * CH2 = MUXA 입력1 (1,0,0,1) CH6 = MUXB 입력2 (0,1,1,0) - * CH3 = MUXA 입력3 (1,0,1,1) CH7 = MUXB 입력3 (0,1,0,0) - * 채널 전환 후 MUX 안정화 대기 시간(1.3ms) 필요. - */ -void dr_piezo_select_channel(uint8_t channel) -{ - channel = channel & 0x07; /* 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; - } - - /* 채널 변경 시 MUX 안정화 시간 필요 (> 1.2ms) */ - nrf_delay_us(DR_PIEZO_MUX_SETTLING_US); -} - -/* 핀 테스트: 각 신호 핀을 순서대로 HIGH/LOW 토글 (오실로스코프 확인용) */ -void dr_piezo_test_pins(void) -{ - DBG_PRINTF("[DR_PIEZO] Pin test...\r\n"); - - nrf_gpiote_task_disable(GPIOTE_CH_P_OUT); - nrf_gpiote_task_disable(GPIOTE_CH_N_OUT); - - 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); - - DBG_PRINTF(" PE (P1.05)...\r\n"); - nrf_gpio_pin_set(DR_PIEZO_PIN_PE); - nrf_delay_ms(100); - nrf_gpio_pin_clear(DR_PIEZO_PIN_PE); - nrf_delay_ms(100); - - DBG_PRINTF(" DMP (P1.09)...\r\n"); - nrf_gpio_pin_set(DR_PIEZO_PIN_DMP); - nrf_delay_ms(100); - nrf_gpio_pin_clear(DR_PIEZO_PIN_DMP); - nrf_delay_ms(100); - - DBG_PRINTF(" P_OUT (P1.03)...\r\n"); - nrf_gpio_pin_set(DR_PIEZO_PIN_P_OUT); - nrf_delay_ms(100); - nrf_gpio_pin_clear(DR_PIEZO_PIN_P_OUT); - nrf_delay_ms(100); - - DBG_PRINTF(" N_OUT (P1.02)...\r\n"); - nrf_gpio_pin_set(DR_PIEZO_PIN_N_OUT); - nrf_delay_ms(100); - nrf_gpio_pin_clear(DR_PIEZO_PIN_N_OUT); - nrf_delay_ms(100); - - DBG_PRINTF("[DR_PIEZO] Pin test done\r\n"); -} - -/*============================================================================== - * 시스템 함수 (전원 + TX 드라이버 통합 제어) - *============================================================================*/ - -/* 시스템 전체 초기화: TX 드라이버 초기화 → 전원 ON (부팅 시 1회만 호출) */ -void dr_piezo_system_init(void) -{ - dr_piezo_init(); /* GPIO/GPIOTE/Timer/PPI 먼저 안전 상태로 설정 */ - dr_piezo_power_on(); /* 그 다음 DC/DC 전원 인가 */ -} - -/* 시스템 전체 종료: TX 드라이버 해제 → 전원 OFF */ -void dr_piezo_system_uninit(void) -{ - dr_piezo_uninit(); - dr_piezo_power_off(); -} - -/* 전원 확인 후 버스트 송신 (블로킹: 송신 완료까지 대기) */ -void dr_piezo_transmit(uint8_t cycles) -{ - if (!m_power_enabled) - { - DBG_PRINTF("[DR_PIEZO] ERROR: Power not enabled!\r\n"); - return; - } - - dr_piezo_burst(cycles); - - while (dr_piezo_is_busy()) { } -} - -/*============================================================================== - * 소프트웨어 기반 버스트 모드 - * 2025-12-11 Charles KWON - *============================================================================== - * - * Timer/PPI/GPIOTE 하드웨어 대신 CPU에서 직접 GPIO 레지스터를 조작하여 - * 초음파 펄스를 생성하는 방식. 인터럽트를 비활성화하여 정확한 타이밍 보장. - * - * === 타이밍 다이어그램 === - * - * |<-마진->|<----- 펄스들 ----->|<-- DMP -->|<-마진->| - * - * PE ___/--------------------------------------------------\___ - * P_OUT ___________/-\_/-\_/-\_/-\_/-\____________________\_______ - * N_OUT ___________\_/-\_/-\_/-\_/-\_/____________________\_______ - * DMP __________________________________/----------\____________ - * - * === 신호 설명 === - * - PE (Pulse Enable): 전체 시퀀스를 감싸는 활성화 신호 (전후 마진 포함) - * - P_OUT: 양극 출력, N_OUT과 역상으로 2MHz 토글 - * - N_OUT: 음극 출력, P_OUT과 역상으로 2MHz 토글 - * - DMP (Dump): 펄스 완료 후 피에조 잔류 에너지 방전 - * - * === 동작 원리 === - * 1) __disable_irq()로 인터럽트 차단 → 타이밍 흔들림 방지 - * 2) PE ON (P0.25 OUTSET 레지스터 사용 → 다른 P0 핀 영향 없음) - * 3) NOP 마진 후 for 루프로 P_OUT/N_OUT 교번 출력 - * 4) DMP 펄스 (32 NOP ≒ 500ns) - * 5) PE OFF 후 __enable_irq()로 인터럽트 복원 - * - * === 포트 레지스터 직접 접근 === - * NRF_P1->OUT 레지스터에 미리 계산된 비트 마스크를 직접 기록. - * 이 방식으로 P_OUT, N_OUT, DMP를 동시에 제어하면서도 - * 채널 선택 핀(MUX SEL)은 보존한다 (P1_CTRL_MASK로 제어 핀만 변경). - * PE는 P0 포트에 있으므로 OUTSET/OUTCLR 레지스터로 별도 제어. - *============================================================================*/ -/* 핀 번호에서 포트 내 비트 위치 추출 (하위 5비트 = 0~31) */ -#define PIN_NUM(pin) ((pin) & 0x1F) - - -/* P1 포트 핀의 비트 마스크 - dr_piezo.h의 핀 정의에서 자동 생성 - * - * 경고: 핀 번호를 절대 하드코딩하지 말 것! - * 하드코딩은 개발자의 시간을 일시적으로 절약해줄 수 있지만, - * 동시에 개발자의 수명을 단축시킬 것이다. - * - Charles KWON - * - * 각 마스크는 해당 핀의 포트 레지스터 내 비트 위치를 나타낸다. - * NRF_P1->OUT에 직접 쓸 때 사용되며, 여러 핀을 동시에 제어 가능. -*/ - -#define P_OUT_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_P_OUT)) /* P1.07 양극 출력 */ -#define N_OUT_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_N_OUT)) /* P1.06 음극 출력 */ -#define PE_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_PE)) /* P0.25 펄스 활성화 */ -#define DMP_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_DMP)) /* P1.00 방전 제어 */ - -/* P1 포트에서 피에조 제어에 사용하는 핀들의 결합 마스크 (채널 선택 핀 제외) */ -#define P1_CTRL_MASK (P_OUT_MASK | N_OUT_MASK | DMP_MASK) - -/* - * 소프트웨어 버스트 - 기본 주파수 2.1MHz - * - * NOP 타이밍 계산: - * 2.1MHz → 주기 476ns, 반주기 238ns - * CPU 64MHz → 1 NOP = 15.625ns - * 첫 반주기: 14 NOP(≒219ns) + 레지스터 쓰기(≒30ns) = ≒249ns - * 둘째 반주기: 9 NOP(≒141ns) + 루프 오버헤드(≒47ns) + 레지스터 쓰기(≒30ns) = ≒218ns - * 합계: ≒436~476ns (≒2.1MHz) - */ -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); - 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); - - 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 - *------------------------------------------------------------------------*/ - - // PE는 OUTSET/OUTCLR 사용 (다른 P0 핀 영향 방지) - - 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 - *------------------------------------------------------------------------*/ - __disable_irq(); - - /* Initialize: Set all signals to LOW */ - NRF_P0->OUTCLR = PE_MASK; // PE OFF (OUTCLR로 다른 핀 영향 없음) - NRF_P1->OUT = p1_all_low; - - /* PE rises first with margin before pulses start */ - 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 */ - - - - /*-------------------------------------------------------------------------- - * Generate 2.1MHz pulse burst (hardcoded NOP timing for stability) - * - * 2.1 MHz: 476ns period, 238ns half-period - * At 64MHz CPU (1 NOP = 15.625ns): - * First half: 13 NOPs (~203ns) + register write (~30ns) = ~233ns - * Second half: 10 NOPs (~156ns) + loop overhead (~47ns) = ~203ns - * Total: ~436-476ns 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(); - - - /* 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 - * - Starts simultaneously with N_OUT falling edge - * - Duration: ~500ns (32 NOPs) - *------------------------------------------------------------------------*/ - - 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(); /* ~47ns margin */ - - NRF_P0->OUTCLR = PE_MASK; // PE OFF - - __enable_irq(); -} - -/** - * @brief Software-based burst at 1.8 MHz - * @param cycles Number of cycles (1~20) - * - * Timing: - * 1.8 MHz: 556ns period, 278ns half-period - * At 64MHz CPU (1 NOP = 15.625ns): - * First half: 15 NOPs (~234ns) - * Second half: 12 NOPs (~188ns) + loop overhead - */ -/* - * 소프트웨어 버스트 - 1.8MHz - * NOP 타이밍: 반주기 278ns - * 첫 반주기: 17 NOP(≒266ns) + 레지스터 쓰기(≒30ns) = ≒296ns - * 둘째 반주기: 11 NOP(≒172ns) + 루프 오버헤드(≒47ns) + 레지스터 쓰기(≒30ns) = ≒249ns - * 합계: ≒499~556ns (≒1.8MHz) - */ -void dr_piezo_burst_sw_18mhz(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); - - //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_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 1.8MHz pulse burst - * - * 1.8 MHz: 556ns period, 278ns half-period - * At 64MHz CPU (1 NOP = 15.625ns): - * First half: 15 NOPs (~234ns) + register write (~30ns) = ~264ns - * Second half: 12 NOPs (~188ns) + loop overhead (~47ns) = ~235ns - * Total: ~499-556ns 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(); - __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 = 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.0 MHz - * @param cycles Number of cycles (1~20) - * - * Timing: - * 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 - */ -/* - * 소프트웨어 버스트 - 2.0MHz - * NOP 타이밍: 반주기 250ns - * 첫 반주기: 15 NOP(≒234ns) + 레지스터 쓰기(≒30ns) = ≒264ns - * 둘째 반주기: 10 NOP(≒156ns) + 루프 오버헤드(≒47ns) + 레지스터 쓰기(≒30ns) = ≒233ns - * 합계: ≒468~500ns (≒2.0MHz) - */ -void dr_piezo_burst_sw_20mhz(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(); __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.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 - */ -/* - * 소프트웨어 버스트 - 1.9MHz - * NOP 타이밍: 반주기 263ns - * 첫 반주기: 15 NOP(≒234ns) + 레지스터 쓰기(≒30ns) = ≒264ns - * 둘째 반주기: 9 NOP(≒141ns) + 루프 오버헤드(≒47ns) + 레지스터 쓰기(≒30ns) = ≒218ns - * 합계: ≒468~500ns (≒1.9MHz) - */ -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) - */ -/* - * 소프트웨어 버스트 - 2.2MHz - * NOP 타이밍: 반주기 227ns - * 첫 반주기: 13 NOP(≒203ns) + 레지스터 쓰기(≒30ns) = ≒233ns - * 둘째 반주기: 8 NOP(≒125ns) + 루프 오버헤드(≒47ns) + 레지스터 쓰기(≒30ns) = ≒202ns - * 합계: ≒435~454ns (≒2.2MHz) - */ -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(); -} - -/** - * @brief Software-based burst at 1.7 MHz - * @param cycles Number of cycles (1~20) - * - * Timing: - * 1.7 MHz: 588ns period, 294ns half-period - * At 64MHz CPU (1 NOP = 15.625ns): - * First half: 17 NOPs (~266ns) + register write (~30ns) = ~296ns - * Second half: 14 NOPs (~219ns) + loop overhead (~47ns) = ~266ns - * Total: ~532-588ns per cycle - */ -/* - * 소프트웨어 버스트 - 1.7MHz - * NOP 타이밍: 반주기 294ns - * 첫 반주기: 18 NOP(≒281ns) + 레지스터 쓰기(≒30ns) = ≒311ns - * 둘째 반주기: 10 NOP(≒156ns) + 루프 오버헤드(≒47ns) + 레지스터 쓰기(≒30ns) = ≒233ns - * 합계: ≒532~588ns (≒1.7MHz) - */ -void dr_piezo_burst_sw_17mhz(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 (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_P0->OUTCLR = PE_MASK; // PE OFF - NRF_P1->OUT = p1_all_low; - - /* PE rises first with margin before pulses start */ - NRF_P0->OUTSET = PE_MASK; // PE ON - __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 1.7MHz pulse burst - * - * 1.7 MHz: 588ns period, 294ns half-period - * At 64MHz CPU (1 NOP = 15.625ns): - * First half: 17 NOPs (~266ns) + register write (~30ns) = ~296ns - * Second half: 14 NOPs (~219ns) + loop overhead (~47ns) = ~266ns - * Total: ~532-588ns 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(); - __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(); __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; // PE OFF - - __enable_irq(); -} - +/******************************************************************************* + * @file dr_piezo.c + * @brief Piezo Transducer Driver (2MHz Signal Generator) + * @author Charles KWON + * @date 2025-12-09 + * + * @details Uses Timer2 + GPIOTE + PPI for CPU-free 2MHz waveform generation + * + * Timing Diagram (???): + * + * |<----------- PE HIGH ----------->| + * PE ___/?????????????????????????????????\___ + * P_OUT ___/?\_/?\_/?\_/?\_/?\________________\___ + * N_OUT ___\_/?\_/?\_/?\_/?\_/________________\___ + * DMP _________________________/?????\_________ + * |<-- 3~5 cycles -->| || + * + * P_OUT? N_OUT? ?? ?? ?? (???) + * + * 2MHz = 500ns period = 250ns half-period + * Timer @ 16MHz: 1 tick = 62.5ns + * Half-period = 250ns = 4 ticks + * Full-period = 500ns = 8 ticks + ******************************************************************************/ + +/******************************************************************************* + * [한국어 설명] 피에조 초음파 트랜스듀서 드라이버 + * + * === 개요 === + * 방광 측정용 2MHz 초음파 송신 신호를 생성하는 드라이버. + * nRF52840의 하드웨어 주변장치(Timer2 + GPIOTE + PPI)를 활용하여 + * CPU 개입 없이 정밀한 2MHz 파형을 자동으로 생성한다. + * + * === 초음파 TX 시퀀스 === + * 1단계: PE(Pulse Enable) = HIGH -> MOSFET 드라이버 활성화 + * 2단계: P_OUT/N_OUT 교번 펄스 생성 (2MHz, 3~7 사이클) + * - P_OUT과 N_OUT은 역상(반대 위상)으로 동작 + * - 피에조 소자 양단에 +/-20V 교번 전압 인가 + * 3단계: DMP(Dump) = HIGH -> 펄스 완료 후 피에조에 남은 잔류 에너지 방전 + * 4단계: DMP = LOW -> 방전 완료 + * 5단계: PE = LOW -> MOSFET 드라이버 비활성화, 유휴 상태 복귀 + * + * === 하드웨어 아키텍처 === + * [Timer2] --- CC[0](반주기=4틱) ---> [PPI CH8,9] ---> [GPIOTE CH4,5] -> P_OUT/N_OUT 토글 + * |-- CC[1](전체주기=8틱) --> [PPI CH10,11] --> [GPIOTE CH4,5] -> P_OUT/N_OUT 토글 + * |-- CC[2](전체주기=8틱) --> 타이머 자동 클리어 + 인터럽트(잔여 사이클 카운트) + * + * - Timer2: 16MHz 클럭, 16비트 모드 + * - CC[0] = 4틱(250ns) -> 반주기 시점에서 P_OUT/N_OUT 토글 + * - CC[1] = 8틱(500ns) -> 전체주기 시점에서 P_OUT/N_OUT 토글 + * - CC[2] = 8틱(500ns) -> 인터럽트 발생 + 타이머 자동 클리어(SHORT) + * + * - GPIOTE CH4: P_OUT 핀 토글 모드 + * - GPIOTE CH5: N_OUT 핀 토글 모드 + * + * - PPI CH8: CC[0] 이벤트 -> P_OUT 토글 태스크 + * - PPI CH9: CC[0] 이벤트 -> N_OUT 토글 태스크 + * - PPI CH10: CC[1] 이벤트 -> P_OUT 토글 태스크 + * - PPI CH11: CC[1] 이벤트 -> N_OUT 토글 태스크 + * + * === 전원 === + * DR_PIEZO_PWR_EN(P1.9) -> DC/DC 컨버터 활성화 -> +/-20V 고전압 생성 + * + * === 소프트웨어 버스트 모드 (dr_piezo_burst_sw 계열) === + * Timer/PPI 대신 CPU에서 직접 GPIO를 제어하는 방식. + * 인터럽트를 비활성화(__disable_irq)하고 NOP 명령어로 정밀 타이밍 생성. + * 포트 레지스터(NRF_P1->OUT)에 직접 접근하여 여러 핀을 동시에 제어. + * 주파수별(1.7/1.8/1.9/2.0/2.1/2.2 MHz) NOP 개수가 다름. + * + * NOP 타이밍 계산법: + * CPU 클럭 64MHz -> 1 NOP = 15.625ns + * 목표 반주기(ns) = 1,000,000 / (목표주파수MHz * 2) + * 필요 NOP 수 = (목표 반주기 - 레지스터 쓰기 시간(~30ns)) / 15.625 + * 루프 오버헤드(~47ns = 3 NOP)는 두 번째 반주기에서 차감 + ******************************************************************************/ + +/* 헤더 포함 */ +#include "dr_piezo.h" +#include "nrf_gpio.h" /* GPIO 제어 (핀 설정, 출력) */ +#include "nrf_timer.h" /* 타이머 주변장치 제어 */ +#include "nrf_gpiote.h" /* GPIOTE (GPIO Tasks and Events) 제어 */ +#include "nrf_ppi.h" /* PPI (Programmable Peripheral Interconnect) 제어 */ +#include "nrf_delay.h" /* 지연(딜레이) 함수 */ +#include "power_control.h" /* 전원 관리 */ +#include "app_util_platform.h" /* 인터럽트 우선순위 등 플랫폼 유틸리티 */ + +/* 조건부 디버그 출력: FEATURE_PRINTF 정의 시 SEGGER RTT로 출력 */ +#ifdef FEATURE_PRINTF +#include "debug_print.h" +#else +#define DBG_PRINTF(...) +#endif + +/*============================================================================== + * 하드웨어 리소스 할당 + * - Timer2: 2MHz 파형 생성의 시간 기준 (16MHz 클럭) + * - GPIOTE CH4/5: P_OUT/N_OUT 핀의 하드웨어 토글 + * - PPI CH8~11: 타이머 비교 이벤트 -> GPIOTE 토글 태스크 자동 연결 + *============================================================================*/ +#define PIEZO_TIMER NRF_TIMER2 /* 사용할 타이머 인스턴스 */ +#define PIEZO_TIMER_IRQn TIMER2_IRQn /* 타이머2 인터럽트 번호 */ +#define PIEZO_TIMER_IRQ_PRIORITY 6 /* 인터럽트 우선순위 (6 = 중간) */ + +/* GPIOTE 채널 할당 - 핀 토글 제어용 */ +#define GPIOTE_CH_P_OUT 4 /* P_OUT(양극 출력) 토글용 GPIOTE 채널 */ +#define GPIOTE_CH_N_OUT 5 /* N_OUT(음극 출력) 토글용 GPIOTE 채널 */ + +/* PPI 채널 할당 - 타이머 이벤트 -> GPIOTE 태스크 연결 */ +#define PPI_CH_P_OUT_TOGGLE_0 8 /* CC[0](반주기) -> P_OUT 토글 */ +#define PPI_CH_N_OUT_TOGGLE_0 9 /* CC[0](반주기) -> N_OUT 토글 */ +#define PPI_CH_P_OUT_TOGGLE_1 10 /* CC[1](전체주기) -> P_OUT 토글 */ +#define PPI_CH_N_OUT_TOGGLE_1 11 /* CC[1](전체주기) -> N_OUT 토글 */ + +/*============================================================================== + * 타이밍 상수 + * Timer2 클럭: 16MHz -> 1틱 = 62.5ns + * 2MHz 신호: 주기 500ns(8틱), 반주기 250ns(4틱) + *============================================================================*/ +#define TIMER_FREQ_MHZ 16 /* 타이머 클럭 주파수 (MHz) */ +#define TICK_NS (1000 / TIMER_FREQ_MHZ) /* 1틱 = 62.5ns */ + +/* 2MHz 기준 타이밍: 주기 = 500ns, 반주기 = 250ns */ +#define PERIOD_TICKS_2MHZ 8 /* 전체 주기: 500ns / 62.5ns = 8틱 */ +#define HALF_PERIOD_TICKS 4 /* 반주기: 250ns / 62.5ns = 4틱 */ + +/*============================================================================== + * 피에조 동작 주파수 설정 + *============================================================================*/ +/* + * 목표 피에조 주파수: 2.1 MHz (하드웨어 버스트 모드용) + * + * 소프트웨어 버스트 모드의 타이밍은 각 주파수별 함수에 NOP 개수로 하드코딩됨. + * 안정적인 파형 생성을 위해 컴파일 타임에 고정. + * + * NOP 기반 타이밍 계산 (CPU 64MHz, 1 NOP = 15.625ns): + * 1.7 MHz: 반주기 294ns -> 첫 반주기 18 NOP, 둘째 반주기 10 NOP + 루프 오버헤드 + * 1.8 MHz: 반주기 278ns -> 첫 반주기 17 NOP, 둘째 반주기 11 NOP + 루프 오버헤드 + * 1.9 MHz: 반주기 263ns -> 첫 반주기 15 NOP, 둘째 반주기 9 NOP + 루프 오버헤드 + * 2.0 MHz: 반주기 250ns -> 첫 반주기 15 NOP, 둘째 반주기 10 NOP + 루프 오버헤드 + * 2.1 MHz: 반주기 238ns -> 첫 반주기 14 NOP, 둘째 반주기 9 NOP + 루프 오버헤드 + * 2.2 MHz: 반주기 227ns -> 첫 반주기 13 NOP, 둘째 반주기 8 NOP + 루프 오버헤드 + * + * 주파수를 변경하려면 dr_piezo_burst_sw_XXmhz() 함수의 NOP 수를 수정할 것. + */ +#define PIEZO_FREQ_MHZ 2.1f /* 기본 동작 주파수 (MHz) */ + +/*============================================================================== + * 정적 변수 + *============================================================================*/ +static volatile bool m_tx_active = false; /* TX 송신 중 플래그 (인터럽트에서 변경) */ +static volatile uint8_t m_remaining_cycles = 0; /* 남은 펄스 사이클 수 (인터럽트에서 감소) */ +static uint32_t m_period_ticks = PERIOD_TICKS_2MHZ; /* 현재 주기 (타이머 틱 단위) */ +static bool m_power_enabled = false; /* DC/DC 컨버터 전원 상태 */ +static bool m_initialized = false; /* 드라이버 초기화 완료 여부 */ + +/*============================================================================== + * 타이머2 인터럽트 핸들러 + * 매 주기(CC[2])마다 호출되어 잔여 사이클을 감소시킨다. + * 잔여 사이클이 0이 되면: + * 1) 타이머 정지 및 클리어 + * 2) GPIOTE 비활성화 (P_OUT/N_OUT 토글 중단) + * 3) GPIO를 출력 모드로 재설정 후 LOW로 초기화 + * 4) DMP 펄스 발생 (피에조 잔류 에너지 방전) + * 5) PE = LOW (MOSFET 드라이버 비활성화) + *============================================================================*/ +void TIMER2_IRQHandler(void) +{ + if (nrf_timer_event_check(PIEZO_TIMER, NRF_TIMER_EVENT_COMPARE2)) + { + nrf_timer_event_clear(PIEZO_TIMER, NRF_TIMER_EVENT_COMPARE2); + + if (m_remaining_cycles > 0) + { + m_remaining_cycles--; + } + + if (m_remaining_cycles == 0) + { + /* Step 1: Stop timer */ + nrf_timer_task_trigger(PIEZO_TIMER, NRF_TIMER_TASK_STOP); + nrf_timer_task_trigger(PIEZO_TIMER, NRF_TIMER_TASK_CLEAR); + + /* Step 2: Disable GPIOTE (GPIO ??? ??) */ + nrf_gpiote_task_disable(GPIOTE_CH_P_OUT); + nrf_gpiote_task_disable(GPIOTE_CH_N_OUT); + + /* Step 3: GPIO? ?? ? idle ?? */ + nrf_gpio_cfg_output(DR_PIEZO_PIN_P_OUT); + nrf_gpio_cfg_output(DR_PIEZO_PIN_N_OUT); + nrf_gpio_pin_clear(DR_PIEZO_PIN_P_OUT); + nrf_gpio_pin_clear(DR_PIEZO_PIN_N_OUT); + + /* Step 4: DMP pulse */ + nrf_gpio_pin_set(DR_PIEZO_PIN_DMP); + __NOP(); __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); __NOP(); + __NOP(); __NOP(); __NOP(); __NOP(); + nrf_gpio_pin_clear(DR_PIEZO_PIN_DMP); + + /* Step 5: PE = LOW */ + nrf_gpio_pin_clear(DR_PIEZO_PIN_PE); + + m_tx_active = false; + } + } +} + +/*============================================================================== + * 전원 제어 함수 + * DC/DC 컨버터(±20V)를 ON/OFF하여 피에조 구동 전압을 제어한다. + * 전원 안정화에 약 10ms 필요. + *============================================================================*/ + +/* 피에조 전원 ON: DC/DC 컨버터 활성화 → ±20V 생성 */ +void dr_piezo_power_on(void) +{ + //nrf_delay_ms(20); + + nrf_gpio_cfg_output(DR_PIEZO_PWR_EN); + nrf_gpio_pin_set(DR_PIEZO_PWR_EN); + + /* Wait for power stabilization */ + nrf_delay_ms(3); + + m_power_enabled = true; + + //DBG_PRINTF("[PIEZO] TX/RX Active: +/-20V ready\r\n"); +} + +/* 피에조 전원 OFF: TX 비활성화 → MUX 비활성화 → DC/DC 컨버터 차단 */ +void dr_piezo_power_off(void) +{ + dr_piezo_disable(); + + /* MUX enable 핀 클리어: select_channel() 이후 HIGH로 남은 핀 해제 */ + nrf_gpio_pin_clear(DR_PIEZO_EN_MUXA); + nrf_gpio_pin_clear(DR_PIEZO_EN_MUXB); + nrf_gpio_pin_clear(DR_PIEZO_MUX_SEL0); + nrf_gpio_pin_clear(DR_PIEZO_MUX_SEL1); + + nrf_gpio_pin_clear(DR_PIEZO_PWR_EN); + + m_power_enabled = false; + + //DBG_PRINTF("[PIEZO] Power OFF\r\n"); +} + +/* 피에조 전원 상태 확인 */ +bool dr_piezo_is_power_on(void) +{ + return m_power_enabled; +} + +/*============================================================================== + * 내부(private) 초기화 함수 + *============================================================================*/ + +/* GPIO 초기화: 모든 신호 핀을 출력 모드로 설정하고 LOW(유휴)로 초기화 */ +static void dr_piezo_gpio_init(void) +{ + 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); + + /* All LOW = idle */ + nrf_gpio_pin_clear(DR_PIEZO_PIN_PE); + nrf_gpio_pin_clear(DR_PIEZO_PIN_DMP); + nrf_gpio_pin_clear(DR_PIEZO_PIN_P_OUT); + nrf_gpio_pin_clear(DR_PIEZO_PIN_N_OUT); + + /* Initialize MUX control pins */ + dr_piezo_mux_init(); +} + +/* GPIOTE 초기화: P_OUT/N_OUT을 토글 모드로 설정 (PPI 연결 대상) */ +static void dr_piezo_gpiote_init(void) +{ + /* P_OUT: Toggle mode, initial LOW */ + nrf_gpiote_task_configure( + GPIOTE_CH_P_OUT, + DR_PIEZO_PIN_P_OUT, + NRF_GPIOTE_POLARITY_TOGGLE, + NRF_GPIOTE_INITIAL_VALUE_LOW + ); + nrf_gpiote_task_enable(GPIOTE_CH_P_OUT); + + /* N_OUT: Toggle mode, initial LOW */ + nrf_gpiote_task_configure( + GPIOTE_CH_N_OUT, + DR_PIEZO_PIN_N_OUT, + NRF_GPIOTE_POLARITY_TOGGLE, + NRF_GPIOTE_INITIAL_VALUE_LOW + ); + nrf_gpiote_task_enable(GPIOTE_CH_N_OUT); +} + +/* + * 타이머 초기화: 16MHz 클럭, 16비트 모드 + * CC[0]=반주기(4틱): 반주기 시점 토글 이벤트 + * CC[1]=전체주기(8틱): 전체주기 시점 토글 이벤트 + * CC[2]=전체주기(8틱): 인터럽트 발생 + 타이머 자동 클리어(SHORT) + */ +static void dr_piezo_timer_init(void) +{ + nrf_timer_task_trigger(PIEZO_TIMER, NRF_TIMER_TASK_STOP); + nrf_timer_task_trigger(PIEZO_TIMER, NRF_TIMER_TASK_CLEAR); + + nrf_timer_mode_set(PIEZO_TIMER, NRF_TIMER_MODE_TIMER); + nrf_timer_bit_width_set(PIEZO_TIMER, NRF_TIMER_BIT_WIDTH_16); + nrf_timer_frequency_set(PIEZO_TIMER, NRF_TIMER_FREQ_16MHz); + + /* + * ??? ??? ?? ???: + * - CC[0] = 4 (half period): P_OUT ??, N_OUT ?? + * - CC[1] = 8 (full period): P_OUT ??, N_OUT ?? + * - CC[2] = 8 (full period): ??? ??? + CLEAR + * + * ?? ??: P=HIGH, N=LOW + * t=4: ? ? ?? -> P=LOW, N=HIGH + * t=8: ? ? ?? -> P=HIGH, N=LOW (+ CLEAR) + * t=12: ? ? ?? -> P=LOW, N=HIGH + * ... + */ + + /* CC[0]: ??? ?? */ + nrf_timer_cc_write(PIEZO_TIMER, NRF_TIMER_CC_CHANNEL0, HALF_PERIOD_TICKS); // 4 + + /* CC[1]: ? ?? ?? */ + nrf_timer_cc_write(PIEZO_TIMER, NRF_TIMER_CC_CHANNEL1, m_period_ticks); // 8 + + /* CC[2]: ? ?? - ??? ???? */ + nrf_timer_cc_write(PIEZO_TIMER, NRF_TIMER_CC_CHANNEL2, m_period_ticks); // 8 + + /* CC[2]?? ?? CLEAR */ + nrf_timer_shorts_enable(PIEZO_TIMER, NRF_TIMER_SHORT_COMPARE2_CLEAR_MASK); + + /* CC[2] ???? (??? ???) */ + nrf_timer_int_enable(PIEZO_TIMER, NRF_TIMER_INT_COMPARE2_MASK); + + NVIC_SetPriority(PIEZO_TIMER_IRQn, PIEZO_TIMER_IRQ_PRIORITY); + NVIC_EnableIRQ(PIEZO_TIMER_IRQn); +} + +/* + * PPI 초기화: 타이머 비교 이벤트 → GPIOTE 토글 태스크 연결 (4채널) + * CC[0] 이벤트(반주기) → P_OUT 토글 + N_OUT 토글 + * CC[1] 이벤트(전체주기) → P_OUT 토글 + N_OUT 토글 + * 이로써 P_OUT과 N_OUT은 항상 역상으로 동작함. + */ +static void dr_piezo_ppi_init(void) +{ + /* + * ??? ??: + * CC[0] (t=4): P_OUT ??, N_OUT ?? + * CC[1] (t=8): P_OUT ??, N_OUT ?? + */ + + /* CC[0] -> P_OUT toggle */ + nrf_ppi_channel_endpoint_setup( + (nrf_ppi_channel_t)PPI_CH_P_OUT_TOGGLE_0, + (uint32_t)nrf_timer_event_address_get(PIEZO_TIMER, NRF_TIMER_EVENT_COMPARE0), + (uint32_t)&NRF_GPIOTE->TASKS_OUT[GPIOTE_CH_P_OUT] + ); + nrf_ppi_channel_enable((nrf_ppi_channel_t)PPI_CH_P_OUT_TOGGLE_0); + + /* CC[0] -> N_OUT toggle */ + nrf_ppi_channel_endpoint_setup( + (nrf_ppi_channel_t)PPI_CH_N_OUT_TOGGLE_0, + (uint32_t)nrf_timer_event_address_get(PIEZO_TIMER, NRF_TIMER_EVENT_COMPARE0), + (uint32_t)&NRF_GPIOTE->TASKS_OUT[GPIOTE_CH_N_OUT] + ); + nrf_ppi_channel_enable((nrf_ppi_channel_t)PPI_CH_N_OUT_TOGGLE_0); + + /* CC[1] -> P_OUT toggle */ + nrf_ppi_channel_endpoint_setup( + (nrf_ppi_channel_t)PPI_CH_P_OUT_TOGGLE_1, + (uint32_t)nrf_timer_event_address_get(PIEZO_TIMER, NRF_TIMER_EVENT_COMPARE1), + (uint32_t)&NRF_GPIOTE->TASKS_OUT[GPIOTE_CH_P_OUT] + ); + nrf_ppi_channel_enable((nrf_ppi_channel_t)PPI_CH_P_OUT_TOGGLE_1); + + /* CC[1] -> N_OUT toggle */ + nrf_ppi_channel_endpoint_setup( + (nrf_ppi_channel_t)PPI_CH_N_OUT_TOGGLE_1, + (uint32_t)nrf_timer_event_address_get(PIEZO_TIMER, NRF_TIMER_EVENT_COMPARE1), + (uint32_t)&NRF_GPIOTE->TASKS_OUT[GPIOTE_CH_N_OUT] + ); + nrf_ppi_channel_enable((nrf_ppi_channel_t)PPI_CH_N_OUT_TOGGLE_1); +} + +/*============================================================================== + * TX 드라이버 공개 함수 + *============================================================================*/ + +/* TX 드라이버 초기화: GPIO → GPIOTE → Timer → PPI 순서로 설정 */ +void dr_piezo_init(void) +{ + dr_piezo_gpio_init(); + dr_piezo_gpiote_init(); + dr_piezo_timer_init(); + dr_piezo_ppi_init(); + + m_tx_active = false; + m_remaining_cycles = 0; + m_initialized = true; +} + +/* TX 드라이버 해제: 타이머 정지, PPI/GPIOTE 비활성화, 모든 핀 LOW */ +void dr_piezo_uninit(void) +{ + nrf_timer_task_trigger(PIEZO_TIMER, NRF_TIMER_TASK_STOP); + nrf_timer_int_disable(PIEZO_TIMER, NRF_TIMER_INT_COMPARE2_MASK); + NVIC_DisableIRQ(PIEZO_TIMER_IRQn); + + nrf_ppi_channel_disable((nrf_ppi_channel_t)PPI_CH_P_OUT_TOGGLE_0); + nrf_ppi_channel_disable((nrf_ppi_channel_t)PPI_CH_N_OUT_TOGGLE_0); + nrf_ppi_channel_disable((nrf_ppi_channel_t)PPI_CH_P_OUT_TOGGLE_1); + nrf_ppi_channel_disable((nrf_ppi_channel_t)PPI_CH_N_OUT_TOGGLE_1); + + nrf_gpiote_task_disable(GPIOTE_CH_P_OUT); + nrf_gpiote_task_disable(GPIOTE_CH_N_OUT); + + nrf_gpio_pin_clear(DR_PIEZO_PIN_PE); + nrf_gpio_pin_clear(DR_PIEZO_PIN_DMP); + nrf_gpio_pin_clear(DR_PIEZO_PIN_P_OUT); + nrf_gpio_pin_clear(DR_PIEZO_PIN_N_OUT); + + m_tx_active = false; + m_initialized = false; +} + +/* + * 하드웨어 기반 버스트 송신 (Timer + PPI + GPIOTE 사용) + * 1) GPIOTE를 재설정: P_OUT=HIGH 시작, N_OUT=LOW 시작 (역상) + * 2) PE = HIGH → MOSFET 드라이버 활성화 + * 3) 타이머 시작 → PPI가 자동으로 P_OUT/N_OUT 토글 + * 4) 인터럽트 핸들러에서 잔여 사이클 관리 및 종료 처리 + */ +void dr_piezo_burst(uint8_t cycles) +{ + if (m_tx_active) + { + DBG_PRINTF("[DR_PIEZO] TX busy!\r\n"); + return; + } + + if (!m_initialized) + { + DBG_PRINTF("[DR_PIEZO] ERROR: Not initialized!\r\n"); + return; + } + + if (cycles < DR_PIEZO_MIN_CYCLES) cycles = DR_PIEZO_MIN_CYCLES; + if (cycles > DR_PIEZO_MAX_CYCLES) cycles = DR_PIEZO_MAX_CYCLES; + + DBG_PRINTF("[DR_PIEZO] Burst: %d cycles\r\n", cycles); + + m_remaining_cycles = cycles; + m_tx_active = true; + + /* GPIOTE ??? */ + nrf_gpiote_task_disable(GPIOTE_CH_P_OUT); + nrf_gpiote_task_disable(GPIOTE_CH_N_OUT); + + nrf_gpiote_task_configure( + GPIOTE_CH_P_OUT, + DR_PIEZO_PIN_P_OUT, + NRF_GPIOTE_POLARITY_TOGGLE, + NRF_GPIOTE_INITIAL_VALUE_HIGH /* P_OUT starts HIGH */ + ); + nrf_gpiote_task_enable(GPIOTE_CH_P_OUT); + + nrf_gpiote_task_configure( + GPIOTE_CH_N_OUT, + DR_PIEZO_PIN_N_OUT, + NRF_GPIOTE_POLARITY_TOGGLE, + NRF_GPIOTE_INITIAL_VALUE_LOW /* N_OUT starts LOW */ + ); + nrf_gpiote_task_enable(GPIOTE_CH_N_OUT); + + /* DMP = LOW */ + nrf_gpio_pin_clear(DR_PIEZO_PIN_DMP); + + /* Timer clear */ + nrf_timer_task_trigger(PIEZO_TIMER, NRF_TIMER_TASK_CLEAR); + + /* Clear pending events */ + nrf_timer_event_clear(PIEZO_TIMER, NRF_TIMER_EVENT_COMPARE0); + nrf_timer_event_clear(PIEZO_TIMER, NRF_TIMER_EVENT_COMPARE1); + nrf_timer_event_clear(PIEZO_TIMER, NRF_TIMER_EVENT_COMPARE2); + + /* PE = HIGH (??? ??) */ + nrf_gpio_pin_set(DR_PIEZO_PIN_PE); + + /* ??? ?? ? ??? ?? */ + __NOP(); __NOP(); __NOP(); __NOP(); + + /* Timer START -> PPI? ???? P_OUT/N_OUT ?? */ + nrf_timer_task_trigger(PIEZO_TIMER, NRF_TIMER_TASK_START); +} + +/* 기본 사이클 수(5)로 버스트 송신 */ +void dr_piezo_pulse(void) +{ + dr_piezo_burst(DR_PIEZO_DEFAULT_CYCLES); +} + +/* TX 출력 활성화: 수동으로 초기 상태 설정 (PE=HIGH, P_OUT=HIGH, N_OUT=LOW) */ +void dr_piezo_enable(void) +{ + nrf_gpio_pin_set(DR_PIEZO_PIN_P_OUT); + nrf_gpio_pin_clear(DR_PIEZO_PIN_N_OUT); + nrf_gpio_pin_clear(DR_PIEZO_PIN_DMP); + nrf_gpio_pin_set(DR_PIEZO_PIN_PE); + DBG_PRINTF("[DR_PIEZO] TX enabled\r\n"); +} + +/* TX 출력 비활성화: 모든 신호 핀 LOW로 복귀 (유휴 상태) */ +void dr_piezo_disable(void) +{ + nrf_gpio_pin_clear(DR_PIEZO_PIN_DMP); + nrf_gpio_pin_clear(DR_PIEZO_PIN_PE); + nrf_gpio_pin_clear(DR_PIEZO_PIN_P_OUT); + nrf_gpio_pin_clear(DR_PIEZO_PIN_N_OUT); +} + +/* TX 송신 중 여부 확인 (인터럽트 핸들러에서 false로 전환) */ +bool dr_piezo_is_busy(void) +{ + return m_tx_active; +} + +/* 동작 주파수 변경 (100kHz~4MHz): 타이머 CC 레지스터 재설정 */ +void dr_piezo_set_frequency(uint32_t freq_hz) +{ + if (freq_hz < 100000 || freq_hz > 4000000) + { + DBG_PRINTF("[DR_PIEZO] Invalid freq: %d Hz\r\n", freq_hz); + return; + } + + uint32_t period_ns = 1000000000UL / freq_hz; + m_period_ticks = period_ns * TIMER_FREQ_MHZ / 1000; + + nrf_timer_cc_write(PIEZO_TIMER, NRF_TIMER_CC_CHANNEL0, m_period_ticks / 2); + nrf_timer_cc_write(PIEZO_TIMER, NRF_TIMER_CC_CHANNEL1, m_period_ticks); + nrf_timer_cc_write(PIEZO_TIMER, NRF_TIMER_CC_CHANNEL2, m_period_ticks); + + DBG_PRINTF("[DR_PIEZO] Freq: %d Hz (period=%d ticks)\r\n", freq_hz, m_period_ticks); +} + +/*============================================================================== + * MUX 제어 함수 + * 8채널 아날로그 MUX로 피에조 에코 신호 경로를 선택한다. + * MUXA: CH0~CH3 담당, MUXB: CH4~CH7 담당 + * SEL0, SEL1: MUX 내부 채널 주소 선택 + * High Drive(H0H1) 모드: MUX IC의 빠른 스위칭을 위해 강한 출력 구동력 사용 + *============================================================================*/ + +/* MUX 제어 핀 초기화: 4개 핀 모두 High Drive 출력으로 설정, 기본값 LOW */ +void dr_piezo_mux_init(void) +{ + /* 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 + ); + + 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 */ +} + + +/* + * 피에조 채널 선택 (0~7) + * 채널 매핑 (EN_MUXA, EN_MUXB, SEL0, SEL1): + * CH0 = MUXA 입력0 (1,0,0,0) CH4 = MUXB 입력0 (0,1,1,1) + * CH1 = MUXA 입력2 (1,0,1,0) CH5 = MUXB 입력1 (0,1,0,1) + * CH2 = MUXA 입력1 (1,0,0,1) CH6 = MUXB 입력2 (0,1,1,0) + * CH3 = MUXA 입력3 (1,0,1,1) CH7 = MUXB 입력3 (0,1,0,0) + * 채널 전환 후 MUX 안정화 대기 시간(1.3ms) 필요. + */ +void dr_piezo_select_channel(uint8_t channel) +{ + channel = channel & 0x07; /* 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; + } + + /* 채널 변경 시 MUX 안정화 시간 필요 (> 1.2ms) */ + nrf_delay_us(DR_PIEZO_MUX_SETTLING_US); +} + +/* 핀 테스트: 각 신호 핀을 순서대로 HIGH/LOW 토글 (오실로스코프 확인용) */ +void dr_piezo_test_pins(void) +{ + DBG_PRINTF("[DR_PIEZO] Pin test...\r\n"); + + nrf_gpiote_task_disable(GPIOTE_CH_P_OUT); + nrf_gpiote_task_disable(GPIOTE_CH_N_OUT); + + 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); + + DBG_PRINTF(" PE (P1.05)...\r\n"); + nrf_gpio_pin_set(DR_PIEZO_PIN_PE); + nrf_delay_ms(100); + nrf_gpio_pin_clear(DR_PIEZO_PIN_PE); + nrf_delay_ms(100); + + DBG_PRINTF(" DMP (P1.09)...\r\n"); + nrf_gpio_pin_set(DR_PIEZO_PIN_DMP); + nrf_delay_ms(100); + nrf_gpio_pin_clear(DR_PIEZO_PIN_DMP); + nrf_delay_ms(100); + + DBG_PRINTF(" P_OUT (P1.03)...\r\n"); + nrf_gpio_pin_set(DR_PIEZO_PIN_P_OUT); + nrf_delay_ms(100); + nrf_gpio_pin_clear(DR_PIEZO_PIN_P_OUT); + nrf_delay_ms(100); + + DBG_PRINTF(" N_OUT (P1.02)...\r\n"); + nrf_gpio_pin_set(DR_PIEZO_PIN_N_OUT); + nrf_delay_ms(100); + nrf_gpio_pin_clear(DR_PIEZO_PIN_N_OUT); + nrf_delay_ms(100); + + DBG_PRINTF("[DR_PIEZO] Pin test done\r\n"); +} + +/*============================================================================== + * 시스템 함수 (전원 + TX 드라이버 통합 제어) + *============================================================================*/ + +/* 시스템 전체 초기화: TX 드라이버 초기화 → 전원 ON (부팅 시 1회만 호출) */ +void dr_piezo_system_init(void) +{ + dr_piezo_init(); /* GPIO/GPIOTE/Timer/PPI 먼저 안전 상태로 설정 */ + dr_piezo_power_on(); /* 그 다음 DC/DC 전원 인가 */ +} + +/* 시스템 전체 종료: TX 드라이버 해제 → 전원 OFF */ +void dr_piezo_system_uninit(void) +{ + dr_piezo_uninit(); + dr_piezo_power_off(); +} + +/* 전원 확인 후 버스트 송신 (블로킹: 송신 완료까지 대기) */ +void dr_piezo_transmit(uint8_t cycles) +{ + if (!m_power_enabled) + { + DBG_PRINTF("[DR_PIEZO] ERROR: Power not enabled!\r\n"); + return; + } + + dr_piezo_burst(cycles); + + while (dr_piezo_is_busy()) { } +} + +/*============================================================================== + * 소프트웨어 기반 버스트 모드 + * 2025-12-11 Charles KWON + *============================================================================== + * + * Timer/PPI/GPIOTE 하드웨어 대신 CPU에서 직접 GPIO 레지스터를 조작하여 + * 초음파 펄스를 생성하는 방식. 인터럽트를 비활성화하여 정확한 타이밍 보장. + * + * === 타이밍 다이어그램 === + * + * |<-마진->|<----- 펄스들 ----->|<-- DMP -->|<-마진->| + * + * PE ___/--------------------------------------------------\___ + * P_OUT ___________/-\_/-\_/-\_/-\_/-\____________________\_______ + * N_OUT ___________\_/-\_/-\_/-\_/-\_/____________________\_______ + * DMP __________________________________/----------\____________ + * + * === 신호 설명 === + * - PE (Pulse Enable): 전체 시퀀스를 감싸는 활성화 신호 (전후 마진 포함) + * - P_OUT: 양극 출력, N_OUT과 역상으로 2MHz 토글 + * - N_OUT: 음극 출력, P_OUT과 역상으로 2MHz 토글 + * - DMP (Dump): 펄스 완료 후 피에조 잔류 에너지 방전 + * + * === 동작 원리 === + * 1) __disable_irq()로 인터럽트 차단 → 타이밍 흔들림 방지 + * 2) PE ON (P0.25 OUTSET 레지스터 사용 → 다른 P0 핀 영향 없음) + * 3) NOP 마진 후 for 루프로 P_OUT/N_OUT 교번 출력 + * 4) DMP 펄스 (32 NOP ≒ 500ns) + * 5) PE OFF 후 __enable_irq()로 인터럽트 복원 + * + * === 포트 레지스터 직접 접근 === + * NRF_P1->OUT 레지스터에 미리 계산된 비트 마스크를 직접 기록. + * 이 방식으로 P_OUT, N_OUT, DMP를 동시에 제어하면서도 + * 채널 선택 핀(MUX SEL)은 보존한다 (P1_CTRL_MASK로 제어 핀만 변경). + * PE는 P0 포트에 있으므로 OUTSET/OUTCLR 레지스터로 별도 제어. + *============================================================================*/ +/* 핀 번호에서 포트 내 비트 위치 추출 (하위 5비트 = 0~31) */ +#define PIN_NUM(pin) ((pin) & 0x1F) + + +/* P1 포트 핀의 비트 마스크 - dr_piezo.h의 핀 정의에서 자동 생성 + * + * 경고: 핀 번호를 절대 하드코딩하지 말 것! + * 하드코딩은 개발자의 시간을 일시적으로 절약해줄 수 있지만, + * 동시에 개발자의 수명을 단축시킬 것이다. + * - Charles KWON + * + * 각 마스크는 해당 핀의 포트 레지스터 내 비트 위치를 나타낸다. + * NRF_P1->OUT에 직접 쓸 때 사용되며, 여러 핀을 동시에 제어 가능. +*/ + +#define P_OUT_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_P_OUT)) /* P1.07 양극 출력 */ +#define N_OUT_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_N_OUT)) /* P1.06 음극 출력 */ +#define PE_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_PE)) /* P0.25 펄스 활성화 */ +#define DMP_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_DMP)) /* P1.00 방전 제어 */ + +/* P1 포트에서 피에조 제어에 사용하는 핀들의 결합 마스크 (채널 선택 핀 제외) */ +#define P1_CTRL_MASK (P_OUT_MASK | N_OUT_MASK | DMP_MASK) + +/* + * 소프트웨어 버스트 - 기본 주파수 2.1MHz + * + * NOP 타이밍 계산: + * 2.1MHz → 주기 476ns, 반주기 238ns + * CPU 64MHz → 1 NOP = 15.625ns + * 첫 반주기: 14 NOP(≒219ns) + 레지스터 쓰기(≒30ns) = ≒249ns + * 둘째 반주기: 9 NOP(≒141ns) + 루프 오버헤드(≒47ns) + 레지스터 쓰기(≒30ns) = ≒218ns + * 합계: ≒436~476ns (≒2.1MHz) + */ +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); + 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); + + 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 + *------------------------------------------------------------------------*/ + + // PE는 OUTSET/OUTCLR 사용 (다른 P0 핀 영향 방지) + + 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 + *------------------------------------------------------------------------*/ + __disable_irq(); + + /* Initialize: Set all signals to LOW */ + NRF_P0->OUTCLR = PE_MASK; // PE OFF (OUTCLR로 다른 핀 영향 없음) + NRF_P1->OUT = p1_all_low; + + /* PE rises first with margin before pulses start */ + 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 */ + + + + /*-------------------------------------------------------------------------- + * Generate 2.1MHz pulse burst (hardcoded NOP timing for stability) + * + * 2.1 MHz: 476ns period, 238ns half-period + * At 64MHz CPU (1 NOP = 15.625ns): + * First half: 13 NOPs (~203ns) + register write (~30ns) = ~233ns + * Second half: 10 NOPs (~156ns) + loop overhead (~47ns) = ~203ns + * Total: ~436-476ns 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(); + + + /* 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 + * - Starts simultaneously with N_OUT falling edge + * - Duration: ~500ns (32 NOPs) + *------------------------------------------------------------------------*/ + + 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(); /* ~47ns margin */ + + NRF_P0->OUTCLR = PE_MASK; // PE OFF + + __enable_irq(); +} + +/** + * @brief Software-based burst at 1.8 MHz + * @param cycles Number of cycles (1~20) + * + * Timing: + * 1.8 MHz: 556ns period, 278ns half-period + * At 64MHz CPU (1 NOP = 15.625ns): + * First half: 15 NOPs (~234ns) + * Second half: 12 NOPs (~188ns) + loop overhead + */ +/* + * 소프트웨어 버스트 - 1.8MHz + * NOP 타이밍: 반주기 278ns + * 첫 반주기: 17 NOP(≒266ns) + 레지스터 쓰기(≒30ns) = ≒296ns + * 둘째 반주기: 11 NOP(≒172ns) + 루프 오버헤드(≒47ns) + 레지스터 쓰기(≒30ns) = ≒249ns + * 합계: ≒499~556ns (≒1.8MHz) + */ +void dr_piezo_burst_sw_18mhz(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); + + //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_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 1.8MHz pulse burst + * + * 1.8 MHz: 556ns period, 278ns half-period + * At 64MHz CPU (1 NOP = 15.625ns): + * First half: 15 NOPs (~234ns) + register write (~30ns) = ~264ns + * Second half: 12 NOPs (~188ns) + loop overhead (~47ns) = ~235ns + * Total: ~499-556ns 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(); + __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 = 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.0 MHz + * @param cycles Number of cycles (1~20) + * + * Timing: + * 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 + */ +/* + * 소프트웨어 버스트 - 2.0MHz + * NOP 타이밍: 반주기 250ns + * 첫 반주기: 15 NOP(≒234ns) + 레지스터 쓰기(≒30ns) = ≒264ns + * 둘째 반주기: 10 NOP(≒156ns) + 루프 오버헤드(≒47ns) + 레지스터 쓰기(≒30ns) = ≒233ns + * 합계: ≒468~500ns (≒2.0MHz) + */ +void dr_piezo_burst_sw_20mhz(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(); __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.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 + */ +/* + * 소프트웨어 버스트 - 1.9MHz + * NOP 타이밍: 반주기 263ns + * 첫 반주기: 15 NOP(≒234ns) + 레지스터 쓰기(≒30ns) = ≒264ns + * 둘째 반주기: 9 NOP(≒141ns) + 루프 오버헤드(≒47ns) + 레지스터 쓰기(≒30ns) = ≒218ns + * 합계: ≒468~500ns (≒1.9MHz) + */ +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) + */ +/* + * 소프트웨어 버스트 - 2.2MHz + * NOP 타이밍: 반주기 227ns + * 첫 반주기: 13 NOP(≒203ns) + 레지스터 쓰기(≒30ns) = ≒233ns + * 둘째 반주기: 8 NOP(≒125ns) + 루프 오버헤드(≒47ns) + 레지스터 쓰기(≒30ns) = ≒202ns + * 합계: ≒435~454ns (≒2.2MHz) + */ +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(); +} + +/** + * @brief Software-based burst at 1.7 MHz + * @param cycles Number of cycles (1~20) + * + * Timing: + * 1.7 MHz: 588ns period, 294ns half-period + * At 64MHz CPU (1 NOP = 15.625ns): + * First half: 17 NOPs (~266ns) + register write (~30ns) = ~296ns + * Second half: 14 NOPs (~219ns) + loop overhead (~47ns) = ~266ns + * Total: ~532-588ns per cycle + */ +/* + * 소프트웨어 버스트 - 1.7MHz + * NOP 타이밍: 반주기 294ns + * 첫 반주기: 18 NOP(≒281ns) + 레지스터 쓰기(≒30ns) = ≒311ns + * 둘째 반주기: 10 NOP(≒156ns) + 루프 오버헤드(≒47ns) + 레지스터 쓰기(≒30ns) = ≒233ns + * 합계: ≒532~588ns (≒1.7MHz) + */ +void dr_piezo_burst_sw_17mhz(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 (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_P0->OUTCLR = PE_MASK; // PE OFF + NRF_P1->OUT = p1_all_low; + + /* PE rises first with margin before pulses start */ + NRF_P0->OUTSET = PE_MASK; // PE ON + __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 1.7MHz pulse burst + * + * 1.7 MHz: 588ns period, 294ns half-period + * At 64MHz CPU (1 NOP = 15.625ns): + * First half: 17 NOPs (~266ns) + register write (~30ns) = ~296ns + * Second half: 14 NOPs (~219ns) + loop overhead (~47ns) = ~266ns + * Total: ~532-588ns 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(); + __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(); __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; // PE OFF + + __enable_irq(); +} + diff --git a/project/ble_peripheral/dr_piezo/dr_piezo.h b/project/ble_peripheral/ble_app_bladder_patch/measurement/piezo/dr_piezo.h similarity index 97% rename from project/ble_peripheral/dr_piezo/dr_piezo.h rename to project/ble_peripheral/ble_app_bladder_patch/measurement/piezo/dr_piezo.h index bac7157..229369d 100644 --- a/project/ble_peripheral/dr_piezo/dr_piezo.h +++ b/project/ble_peripheral/ble_app_bladder_patch/measurement/piezo/dr_piezo.h @@ -1,276 +1,276 @@ -/******************************************************************************* - * @file dr_piezo.h - * @brief Piezo Transducer Driver (2MHz Signal Generator) - * @author Charles KWON - * @date 2025-12-09 - * - * @note Hardware: nRF52840 + MD1822K6-G MOSFET Driver + TC7920K6-G MOSFET - * Output: +/-20V at 2MHz, 3~5 cycles - * - * @details Timing Sequence: - * 1. PE = HIGH (enable) - * 2. P_OUT/N_OUT = 2MHz pulses (3~5 cycles) - * 3. DMP = HIGH (dump) - * 4. DMP = LOW - * 5. PE = LOW (disable) - * - * All signals (P_OUT, N_OUT, DMP) operate within PE HIGH period. - ******************************************************************************/ - -/******************************************************************************* - * [한국어 설명] 피에조 초음파 트랜스듀서 드라이버 헤더 - * - * === 개요 === - * 방광 측정용 초음파 송신기의 핀 할당, 설정값, 함수 선언을 정의. - * nRF52840 + MD1822K6-G(MOSFET 드라이버) + TC7920K6-G(MOSFET) 하드웨어 구성. - * 출력: +/-20V, 2MHz, 3~7 사이클 버스트. - * - * === 핀 할당 === - * 전원 제어: - * - DR_PIEZO_PWR_EN (P1.9): DC/DC 컨버터 활성화 -> +/-20V 고전압 생성 - * - * TX 신호 핀 (MOSFET 드라이버 제어): - * - PE (P0.25): Pulse Enable - 전체 시퀀스 활성화/비활성화 - * - DMP (P1.0): Dump - 펄스 후 피에조 잔류 에너지 방전 - * - P_OUT (P1.7): Positive Output - 피에조 양극 구동 - * - N_OUT (P1.6): Negative Output - 피에조 음극 구동 (P_OUT과 역상) - * - * MUX 제어 핀 (8채널 에코 신호 경로 선택): - * - EN_MUXA (P0.21): MUXA 활성화 (CH0~CH3 담당) - * - EN_MUXB (P0.23): MUXB 활성화 (CH4~CH7 담당) - * - SEL0 (P1.10): MUX 내부 채널 주소 비트 0 - * - SEL1 (P0.28): MUX 내부 채널 주소 비트 1 - * - * === MUX 채널 매핑 (8채널) === - * CH0 = MUXA 입력0: EN_A=1, EN_B=0, SEL0=0, SEL1=0 - * CH1 = MUXA 입력2: EN_A=1, EN_B=0, SEL0=1, SEL1=0 - * CH2 = MUXA 입력1: EN_A=1, EN_B=0, SEL0=0, SEL1=1 - * CH3 = MUXA 입력3: EN_A=1, EN_B=0, SEL0=1, SEL1=1 - * CH4 = MUXB 입력0: EN_A=0, EN_B=1, SEL0=1, SEL1=1 - * CH5 = MUXB 입력1: EN_A=0, EN_B=1, SEL0=0, SEL1=1 - * CH6 = MUXB 입력2: EN_A=0, EN_B=1, SEL0=1, SEL1=0 - * CH7 = MUXB 입력3: EN_A=0, EN_B=1, SEL0=0, SEL1=0 - * - * === 두 가지 버스트 모드 === - * 1) 하드웨어 버스트 (dr_piezo_burst): Timer2 + PPI + GPIOTE 사용, CPU 비의존적 - * 2) 소프트웨어 버스트 (dr_piezo_burst_sw_XXmhz): CPU NOP 기반 정밀 타이밍 - * - 주파수별 전용 함수: 1.7/1.8/1.9/2.0/2.1/2.2 MHz - ******************************************************************************/ - -#ifndef DR_PIEZO_H -#define DR_PIEZO_H - -#include -#include -#include "nrf_gpio.h" - -/*============================================================================== - * 전원 제어 핀 (DC/DC 컨버터 +/-20V) - * DR_PIEZO_PWR_EN: HIGH로 설정 시 DC/DC 컨버터가 +/-20V 고전압 생성 - *============================================================================*/ -#define DR_PIEZO_PWR_EN NRF_GPIO_PIN_MAP(1, 9) /** Power Enable jhChun 0128 */ - -/*============================================================================== - * TX 신호 핀 (MOSFET 드라이버 제어) - * PE: Pulse Enable - 전체 TX 시퀀스 활성화/비활성화 - * DMP: Dump - 펄스 후 피에조 잔류 에너지 방전용 - * P_OUT: Positive Output - 피에조 양극 구동 (N_OUT과 역상) - * N_OUT: Negative Output - 피에조 음극 구동 (P_OUT과 역상) - * 주의: 이전 핀 할당(주석 처리)에서 새 보드 레이아웃으로 변경됨 (jhChun 0128) - *============================================================================*/ -#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 제어 핀 (에코 신호 경로 선택) - * 8채널 아날로그 MUX로 피에조 센서 채널을 선택한다. - * MUXA(CH0~CH3)와 MUXB(CH4~CH7) 두 개의 4채널 MUX 사용. - * EN_MUXA/EN_MUXB: 각 MUX 활성화 (동시에 하나만 HIGH) - * SEL0/SEL1: MUX 내부 4채널 중 하나를 선택하는 주소 비트 - *============================================================================*/ -/* 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 */ - -/*============================================================================== - * 설정값 - * DR_PIEZO_FREQ_HZ: 목표 주파수 (실제 동작 주파수는 dr_piezo.c에서 결정) - * DR_PIEZO_DEFAULT_CYCLES: 기본 버스트 사이클 수 (5) - * DR_PIEZO_MIN/MAX_CYCLES: 허용 사이클 범위 (3~7) - * DR_PIEZO_MUX_SETTLING_US: MUX 채널 전환 후 아날로그 경로 안정화 대기 시간 - *============================================================================*/ -/** - * @note Actual operating frequency is defined in dr_piezo.c as PIEZO_FREQ_MHZ. - * Change PIEZO_FREQ_MHZ in dr_piezo.c to adjust the burst frequency. - * Current setting: 2.1 MHz - */ -#define DR_PIEZO_FREQ_HZ 2100000 /**< Target frequency (set PIEZO_FREQ_MHZ in .c) */ -#define DR_PIEZO_DEFAULT_CYCLES 5 /**< Default burst cycles (3~5) */ -#define DR_PIEZO_MIN_CYCLES 3 -#define DR_PIEZO_MAX_CYCLES 7 -#define DR_PIEZO_MUX_SETTLING_US 1300 /**< MUX settling delay (us) */ - -/*============================================================================== - * 전원 제어 함수 - * DC/DC 컨버터(+/-20V)를 ON/OFF하여 피에조 구동 고전압을 제어한다. - *============================================================================*/ - -/** - * @brief Power ON piezo system (+/-20V DC/DC converter) - */ -void dr_piezo_power_on(void); - -/** - * @brief Power OFF piezo system - */ -void dr_piezo_power_off(void); - -/** - * @brief 피에조 전원 상태 확인 - * @return true: 전원 ON, false: 전원 OFF - */ -bool dr_piezo_is_power_on(void); - -/*============================================================================== - * TX 드라이버 함수 - * 초음파 송신 관련: 초기화, 버스트 송신, 활성화/비활성화, 주파수 설정 - *============================================================================*/ - -/** - * @brief Initialize piezo TX driver (Timer + PPI + GPIOTE) - */ -void dr_piezo_init(void); - -/** - * @brief Uninitialize piezo TX driver - */ -void dr_piezo_uninit(void); - -/** - * @brief Transmit a burst of 2MHz pulses - * @param cycles Number of cycles to transmit (3~10) - */ -void dr_piezo_burst(uint8_t cycles); - -/** - * @brief Transmit default burst (5 cycles) - */ -void dr_piezo_pulse(void); - -/** - * @brief Enable TX output (prepare for transmission) - */ -void dr_piezo_enable(void); - -/** - * @brief Disable TX output (return to idle state) - */ -void dr_piezo_disable(void); - -/** - * @brief Check if TX is currently active - * @return true if transmitting - */ -bool dr_piezo_is_busy(void); - -/** - * @brief Set TX frequency (for testing) - * @param freq_hz Frequency in Hz (100kHz ~ 4MHz) - */ -void dr_piezo_set_frequency(uint32_t freq_hz); - -/** - * @brief Test all pins manually (for debugging with oscilloscope) - */ -void dr_piezo_test_pins(void); - -/** - * @brief Initialize MUX control pins for echo signal path - */ -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); - -/*============================================================================== - * 시스템 함수 (전원 + TX 통합 제어) - * 전원 ON/OFF와 TX 드라이버 초기화/해제를 한 번에 수행하는 편의 함수. - * 소프트웨어 버스트(burst_sw) 계열: CPU NOP 기반 정밀 타이밍. - * - Timer/PPI 없이 CPU에서 직접 GPIO를 제어 - * - 인터럽트 비활성화 상태에서 동작하여 타이밍 정확도 보장 - * - 주파수별 전용 함수 제공 (NOP 개수가 다름) - *============================================================================*/ - -/** - * @brief Full system initialization (power + TX driver) - */ -void dr_piezo_system_init(void); - -/** - * @brief Full system shutdown - */ -void dr_piezo_system_uninit(void); - -/** - * @brief Transmit with power check - * @param cycles Number of cycles - */ -void dr_piezo_transmit(uint8_t cycles); - -/** - * @brief Software-based burst (CPU-controlled, no Timer/PPI) - * @param cycles Number of cycles (1~20) - * @note Default frequency: 2.1 MHz - */ -void dr_piezo_burst_sw(uint8_t cycles); - -/** - * @brief Software-based burst at 1.8 MHz - * @param cycles Number of cycles (1~20) - * @note Fixed frequency: 1.8 MHz - */ -void dr_piezo_burst_sw_18mhz(uint8_t cycles); - -/** - * @brief Software-based burst at 2.0 MHz - * @param cycles Number of cycles (1~20) - * @note Fixed frequency: 2.0 MHz - */ -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) - * @note Fixed frequency: 1.7 MHz - */ -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 */ - +/******************************************************************************* + * @file dr_piezo.h + * @brief Piezo Transducer Driver (2MHz Signal Generator) + * @author Charles KWON + * @date 2025-12-09 + * + * @note Hardware: nRF52840 + MD1822K6-G MOSFET Driver + TC7920K6-G MOSFET + * Output: +/-20V at 2MHz, 3~5 cycles + * + * @details Timing Sequence: + * 1. PE = HIGH (enable) + * 2. P_OUT/N_OUT = 2MHz pulses (3~5 cycles) + * 3. DMP = HIGH (dump) + * 4. DMP = LOW + * 5. PE = LOW (disable) + * + * All signals (P_OUT, N_OUT, DMP) operate within PE HIGH period. + ******************************************************************************/ + +/******************************************************************************* + * [한국어 설명] 피에조 초음파 트랜스듀서 드라이버 헤더 + * + * === 개요 === + * 방광 측정용 초음파 송신기의 핀 할당, 설정값, 함수 선언을 정의. + * nRF52840 + MD1822K6-G(MOSFET 드라이버) + TC7920K6-G(MOSFET) 하드웨어 구성. + * 출력: +/-20V, 2MHz, 3~7 사이클 버스트. + * + * === 핀 할당 === + * 전원 제어: + * - DR_PIEZO_PWR_EN (P1.9): DC/DC 컨버터 활성화 -> +/-20V 고전압 생성 + * + * TX 신호 핀 (MOSFET 드라이버 제어): + * - PE (P0.25): Pulse Enable - 전체 시퀀스 활성화/비활성화 + * - DMP (P1.0): Dump - 펄스 후 피에조 잔류 에너지 방전 + * - P_OUT (P1.7): Positive Output - 피에조 양극 구동 + * - N_OUT (P1.6): Negative Output - 피에조 음극 구동 (P_OUT과 역상) + * + * MUX 제어 핀 (8채널 에코 신호 경로 선택): + * - EN_MUXA (P0.21): MUXA 활성화 (CH0~CH3 담당) + * - EN_MUXB (P0.23): MUXB 활성화 (CH4~CH7 담당) + * - SEL0 (P1.10): MUX 내부 채널 주소 비트 0 + * - SEL1 (P0.28): MUX 내부 채널 주소 비트 1 + * + * === MUX 채널 매핑 (8채널) === + * CH0 = MUXA 입력0: EN_A=1, EN_B=0, SEL0=0, SEL1=0 + * CH1 = MUXA 입력2: EN_A=1, EN_B=0, SEL0=1, SEL1=0 + * CH2 = MUXA 입력1: EN_A=1, EN_B=0, SEL0=0, SEL1=1 + * CH3 = MUXA 입력3: EN_A=1, EN_B=0, SEL0=1, SEL1=1 + * CH4 = MUXB 입력0: EN_A=0, EN_B=1, SEL0=1, SEL1=1 + * CH5 = MUXB 입력1: EN_A=0, EN_B=1, SEL0=0, SEL1=1 + * CH6 = MUXB 입력2: EN_A=0, EN_B=1, SEL0=1, SEL1=0 + * CH7 = MUXB 입력3: EN_A=0, EN_B=1, SEL0=0, SEL1=0 + * + * === 두 가지 버스트 모드 === + * 1) 하드웨어 버스트 (dr_piezo_burst): Timer2 + PPI + GPIOTE 사용, CPU 비의존적 + * 2) 소프트웨어 버스트 (dr_piezo_burst_sw_XXmhz): CPU NOP 기반 정밀 타이밍 + * - 주파수별 전용 함수: 1.7/1.8/1.9/2.0/2.1/2.2 MHz + ******************************************************************************/ + +#ifndef DR_PIEZO_H +#define DR_PIEZO_H + +#include +#include +#include "nrf_gpio.h" + +/*============================================================================== + * 전원 제어 핀 (DC/DC 컨버터 +/-20V) + * DR_PIEZO_PWR_EN: HIGH로 설정 시 DC/DC 컨버터가 +/-20V 고전압 생성 + *============================================================================*/ +#define DR_PIEZO_PWR_EN NRF_GPIO_PIN_MAP(1, 9) /** Power Enable jhChun 0128 */ + +/*============================================================================== + * TX 신호 핀 (MOSFET 드라이버 제어) + * PE: Pulse Enable - 전체 TX 시퀀스 활성화/비활성화 + * DMP: Dump - 펄스 후 피에조 잔류 에너지 방전용 + * P_OUT: Positive Output - 피에조 양극 구동 (N_OUT과 역상) + * N_OUT: Negative Output - 피에조 음극 구동 (P_OUT과 역상) + * 주의: 이전 핀 할당(주석 처리)에서 새 보드 레이아웃으로 변경됨 (jhChun 0128) + *============================================================================*/ +#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 제어 핀 (에코 신호 경로 선택) + * 8채널 아날로그 MUX로 피에조 센서 채널을 선택한다. + * MUXA(CH0~CH3)와 MUXB(CH4~CH7) 두 개의 4채널 MUX 사용. + * EN_MUXA/EN_MUXB: 각 MUX 활성화 (동시에 하나만 HIGH) + * SEL0/SEL1: MUX 내부 4채널 중 하나를 선택하는 주소 비트 + *============================================================================*/ +/* 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 */ + +/*============================================================================== + * 설정값 + * DR_PIEZO_FREQ_HZ: 목표 주파수 (실제 동작 주파수는 dr_piezo.c에서 결정) + * DR_PIEZO_DEFAULT_CYCLES: 기본 버스트 사이클 수 (5) + * DR_PIEZO_MIN/MAX_CYCLES: 허용 사이클 범위 (3~7) + * DR_PIEZO_MUX_SETTLING_US: MUX 채널 전환 후 아날로그 경로 안정화 대기 시간 + *============================================================================*/ +/** + * @note Actual operating frequency is defined in dr_piezo.c as PIEZO_FREQ_MHZ. + * Change PIEZO_FREQ_MHZ in dr_piezo.c to adjust the burst frequency. + * Current setting: 2.1 MHz + */ +#define DR_PIEZO_FREQ_HZ 2100000 /**< Target frequency (set PIEZO_FREQ_MHZ in .c) */ +#define DR_PIEZO_DEFAULT_CYCLES 5 /**< Default burst cycles (3~5) */ +#define DR_PIEZO_MIN_CYCLES 3 +#define DR_PIEZO_MAX_CYCLES 7 +#define DR_PIEZO_MUX_SETTLING_US 1300 /**< MUX settling delay (us) */ + +/*============================================================================== + * 전원 제어 함수 + * DC/DC 컨버터(+/-20V)를 ON/OFF하여 피에조 구동 고전압을 제어한다. + *============================================================================*/ + +/** + * @brief Power ON piezo system (+/-20V DC/DC converter) + */ +void dr_piezo_power_on(void); + +/** + * @brief Power OFF piezo system + */ +void dr_piezo_power_off(void); + +/** + * @brief 피에조 전원 상태 확인 + * @return true: 전원 ON, false: 전원 OFF + */ +bool dr_piezo_is_power_on(void); + +/*============================================================================== + * TX 드라이버 함수 + * 초음파 송신 관련: 초기화, 버스트 송신, 활성화/비활성화, 주파수 설정 + *============================================================================*/ + +/** + * @brief Initialize piezo TX driver (Timer + PPI + GPIOTE) + */ +void dr_piezo_init(void); + +/** + * @brief Uninitialize piezo TX driver + */ +void dr_piezo_uninit(void); + +/** + * @brief Transmit a burst of 2MHz pulses + * @param cycles Number of cycles to transmit (3~10) + */ +void dr_piezo_burst(uint8_t cycles); + +/** + * @brief Transmit default burst (5 cycles) + */ +void dr_piezo_pulse(void); + +/** + * @brief Enable TX output (prepare for transmission) + */ +void dr_piezo_enable(void); + +/** + * @brief Disable TX output (return to idle state) + */ +void dr_piezo_disable(void); + +/** + * @brief Check if TX is currently active + * @return true if transmitting + */ +bool dr_piezo_is_busy(void); + +/** + * @brief Set TX frequency (for testing) + * @param freq_hz Frequency in Hz (100kHz ~ 4MHz) + */ +void dr_piezo_set_frequency(uint32_t freq_hz); + +/** + * @brief Test all pins manually (for debugging with oscilloscope) + */ +void dr_piezo_test_pins(void); + +/** + * @brief Initialize MUX control pins for echo signal path + */ +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); + +/*============================================================================== + * 시스템 함수 (전원 + TX 통합 제어) + * 전원 ON/OFF와 TX 드라이버 초기화/해제를 한 번에 수행하는 편의 함수. + * 소프트웨어 버스트(burst_sw) 계열: CPU NOP 기반 정밀 타이밍. + * - Timer/PPI 없이 CPU에서 직접 GPIO를 제어 + * - 인터럽트 비활성화 상태에서 동작하여 타이밍 정확도 보장 + * - 주파수별 전용 함수 제공 (NOP 개수가 다름) + *============================================================================*/ + +/** + * @brief Full system initialization (power + TX driver) + */ +void dr_piezo_system_init(void); + +/** + * @brief Full system shutdown + */ +void dr_piezo_system_uninit(void); + +/** + * @brief Transmit with power check + * @param cycles Number of cycles + */ +void dr_piezo_transmit(uint8_t cycles); + +/** + * @brief Software-based burst (CPU-controlled, no Timer/PPI) + * @param cycles Number of cycles (1~20) + * @note Default frequency: 2.1 MHz + */ +void dr_piezo_burst_sw(uint8_t cycles); + +/** + * @brief Software-based burst at 1.8 MHz + * @param cycles Number of cycles (1~20) + * @note Fixed frequency: 1.8 MHz + */ +void dr_piezo_burst_sw_18mhz(uint8_t cycles); + +/** + * @brief Software-based burst at 2.0 MHz + * @param cycles Number of cycles (1~20) + * @note Fixed frequency: 2.0 MHz + */ +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) + * @note Fixed frequency: 1.7 MHz + */ +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/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 7b2092e..75b8859 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 @@ -161,7 +161,7 @@ 0 0 0 - ..\..\..\..\..\..\pc_firm\dr_adc121s051\dr_adc121s051.c + C:\jhChun\VesiScan-Basic\pc_firm\dr_adc121s051\dr_adc121s051.c @@ -177,7 +177,7 @@ 0 0 0 - ..\..\..\..\..\..\pc_firm\parser.c + C:\jhChun\VesiScan-Basic\pc_firm\parser.c @@ -193,7 +193,7 @@ 0 0 0 - ..\..\..\..\..\..\pc_firm\parser.c + C:\jhChun\VesiScan-Basic\pc_firm\parser.c @@ -625,7 +625,7 @@ 0 0 0 - ..\..\..\..\..\..\pc_firm\parser.c + C:\jhChun\VesiScan-Basic\pc_firm\parser.c @@ -641,7 +641,7 @@ 0 0 0 - ..\..\..\..\..\..\pc_firm\parser.c + C:\jhChun\VesiScan-Basic\pc_firm\parser.c @@ -657,7 +657,7 @@ 0 0 0 - ..\..\..\..\..\..\pc_firm\parser.c + C:\jhChun\VesiScan-Basic\pc_firm\parser.c @@ -673,7 +673,7 @@ 0 0 0 - ..\..\..\..\..\..\pc_firm\parser.c + C:\jhChun\VesiScan-Basic\pc_firm\parser.c @@ -689,7 +689,7 @@ 0 0 0 - ..\..\..\..\..\..\pc_firm\parser.c + C:\jhChun\VesiScan-Basic\pc_firm\parser.c @@ -705,7 +705,7 @@ 0 0 0 - ..\..\..\..\..\..\pc_firm\parser.c + C:\jhChun\VesiScan-Basic\pc_firm\parser.c @@ -801,7 +801,7 @@ 0 0 0 - ..\..\..\..\..\..\pc_firm\parser.c + C:\jhChun\VesiScan-Basic\pc_firm\parser.c @@ -817,7 +817,7 @@ 0 0 0 - ..\..\..\..\..\..\pc_firm\parser.c + C:\jhChun\VesiScan-Basic\pc_firm\parser.c @@ -833,7 +833,7 @@ 0 0 0 - ..\..\..\..\..\..\pc_firm\parser.c + C:\jhChun\VesiScan-Basic\pc_firm\parser.c @@ -849,7 +849,7 @@ 0 0 0 - ..\..\..\..\..\..\pc_firm\parser.c + C:\jhChun\VesiScan-Basic\pc_firm\parser.c @@ -865,7 +865,7 @@ 0 0 0 - ..\..\..\..\..\..\pc_firm\parser.c + C:\jhChun\VesiScan-Basic\pc_firm\parser.c @@ -881,7 +881,7 @@ 0 0 0 - ..\..\..\..\..\..\pc_firm\parser.c + C:\jhChun\VesiScan-Basic\pc_firm\parser.c @@ -897,7 +897,7 @@ 0 0 0 - ..\..\..\..\..\..\pc_firm\dr_adc121s051\dr_adc121s051.c + C:\jhChun\VesiScan-Basic\pc_firm\dr_adc121s051\dr_adc121s051.c @@ -913,7 +913,7 @@ 0 0 0 - ..\..\..\..\..\..\pc_firm\dr_adc121s051\dr_adc121s051.c + C:\jhChun\VesiScan-Basic\pc_firm\dr_adc121s051\dr_adc121s051.c @@ -1195,7 +1195,7 @@ 0 0 0 - ..\..\..\sensors\battery\battery_saadc.c + ..\..\..\measurement\battery\battery_saadc.c battery_saadc.c 0 0 @@ -1207,7 +1207,7 @@ 0 0 0 - ..\..\..\io\power\power_control.c + ..\..\..\system\power\power_control.c power_control.c 0 0 @@ -1219,7 +1219,7 @@ 0 0 0 - ..\..\..\sensors\temperature\tmp235_q1.c + ..\..\..\measurement\temperature\tmp235_q1.c tmp235_q1.c 0 0 @@ -1267,7 +1267,7 @@ 0 0 0 - ..\..\..\..\..\..\pc_firm\parser.h + ..\..\..\command\parser.h parser.h 0 0 @@ -1279,7 +1279,7 @@ 0 0 0 - ..\..\..\..\..\..\pc_firm\parser.c + ..\..\..\command\parser.c parser.c 0 0 @@ -1291,7 +1291,7 @@ 0 0 0 - ..\..\..\..\..\..\pc_firm\ble_security\ble_quick_security.c + ..\..\..\system\security\ble_quick_security.c ble_quick_security.c 0 0 @@ -1303,7 +1303,7 @@ 0 0 0 - ..\..\..\..\..\..\pc_firm\ble_security\ble_quick_security.h + ..\..\..\system\security\ble_quick_security.h ble_quick_security.h 0 0 @@ -1315,7 +1315,7 @@ 0 0 0 - ..\..\..\..\dr_piezo\dr_piezo.c + ..\..\..\measurement\piezo\dr_piezo.c dr_piezo.c 0 0 @@ -1327,7 +1327,7 @@ 0 0 0 - ..\..\..\..\dr_piezo\dr_piezo.h + ..\..\..\measurement\piezo\dr_piezo.h dr_piezo.h 0 0 @@ -1339,7 +1339,7 @@ 0 0 0 - ..\..\..\..\..\..\pc_firm\dr_util\dr_util.c + ..\..\..\command\util\dr_util.c dr_util.c 0 0 @@ -1351,7 +1351,7 @@ 0 0 0 - ..\..\..\..\..\..\pc_firm\dr_util\dr_util.h + ..\..\..\command\util\dr_util.h dr_util.h 0 0 @@ -1363,7 +1363,7 @@ 0 0 0 - ..\..\..\..\..\..\pc_firm\dr_adc121s051\dr_adc121s051.c + ..\..\..\measurement\adc121s051\dr_adc121s051.c dr_adc121s051.c 0 0 @@ -1375,7 +1375,7 @@ 0 0 0 - ..\..\..\io\led\led_control.c + ..\..\..\system\led\led_control.c led_control.c 0 0 @@ -3111,7 +3111,7 @@ 0 0 0 - ..\..\..\sensors\imu\system_interface.c + ..\..\..\measurement\imu\system_interface.c system_interface.c 0 0 @@ -3123,7 +3123,7 @@ 0 0 0 - ..\..\..\sensors\imu\Invn\EmbUtils\DataConverter.c + ..\..\..\measurement\imu\Invn\EmbUtils\DataConverter.c DataConverter.c 0 0 @@ -3135,7 +3135,7 @@ 0 0 0 - ..\..\..\sensors\imu\Invn\EmbUtils\ErrorHelper.c + ..\..\..\measurement\imu\Invn\EmbUtils\ErrorHelper.c ErrorHelper.c 0 0 @@ -3147,7 +3147,7 @@ 0 0 0 - ..\..\..\sensors\imu\Invn\EmbUtils\InvBasicMath.c + ..\..\..\measurement\imu\Invn\EmbUtils\InvBasicMath.c InvBasicMath.c 0 0 @@ -3159,7 +3159,7 @@ 0 0 0 - ..\..\..\sensors\imu\Invn\imu\inv_imu_apex.c + ..\..\..\measurement\imu\Invn\imu\inv_imu_apex.c inv_imu_apex.c 0 0 @@ -3171,7 +3171,7 @@ 0 0 0 - ..\..\..\sensors\imu\Invn\imu\inv_imu_driver.c + ..\..\..\measurement\imu\Invn\imu\inv_imu_driver.c inv_imu_driver.c 0 0 @@ -3183,7 +3183,7 @@ 0 0 0 - ..\..\..\sensors\imu\Invn\imu\inv_imu_selftest.c + ..\..\..\measurement\imu\Invn\imu\inv_imu_selftest.c inv_imu_selftest.c 0 0 @@ -3195,7 +3195,7 @@ 0 0 0 - ..\..\..\sensors\imu\Invn\imu\inv_imu_transport.c + ..\..\..\measurement\imu\Invn\imu\inv_imu_transport.c inv_imu_transport.c 0 0 @@ -3207,7 +3207,7 @@ 0 0 0 - ..\..\..\sensors\imu\app_raw\app_raw.c + ..\..\..\measurement\imu\app_raw\app_raw.c app_raw.c 0 0 @@ -3219,7 +3219,7 @@ 0 0 0 - ..\..\..\sensors\imu\app_raw\app_raw_main.c + ..\..\..\measurement\imu\app_raw\app_raw_main.c app_raw_main.c 0 0 diff --git a/project/ble_peripheral/ble_app_bladder_patch/pca10056/s140/arm5_no_packs/ble_app_bladder_patch_s140.uvprojx b/project/ble_peripheral/ble_app_bladder_patch/pca10056/s140/arm5_no_packs/ble_app_bladder_patch_s140.uvprojx index 3e5d082..002f6dc 100644 --- a/project/ble_peripheral/ble_app_bladder_patch/pca10056/s140/arm5_no_packs/ble_app_bladder_patch_s140.uvprojx +++ b/project/ble_peripheral/ble_app_bladder_patch/pca10056/s140/arm5_no_packs/ble_app_bladder_patch_s140.uvprojx @@ -133,7 +133,7 @@ 0 0 1 - 1 + 0 4100 1 @@ -339,7 +339,7 @@ --reduce_paths --diag_suppress 188 APP_TIMER_V2 APP_TIMER_V2_RTC1_ENABLED BOARD_PCA10056 FLOAT_ABI_HARD NRF52840_XXAA NRF_SD_BLE_API_VERSION=7 S140 SOFTDEVICE_PRESENT __HEAP_SIZE=8192 __STACK_SIZE=8192 NRF_DFU_SVCI_ENABLED NRF_DFU_TRANSPORT_BLE=1 ICM42670P CONFIG_NFCT_PINS_AS_GPIOS MBEDTLS_CONFIG_FILE=<nrf_crypto_mbedtls_config.h> NRF_APP_VERSION=0x00000001 NRF_APP_VERSION_ADDR=0x1D000 NRF_CRYPTO_MAX_INSTANCE_COUN=1 - ..\..\..\config;..\..\..\..\..\..\components;..\..\..\..\..\..\components\ble\ble_advertising;..\..\..\..\..\..\components\ble\ble_dtm;..\..\..\..\..\..\components\ble\ble_link_ctx_manager;..\..\..\..\..\..\components\ble\ble_racp;..\..\..\..\..\..\components\ble\ble_services\ble_ancs_c;..\..\..\..\..\..\components\ble\ble_services\ble_ans_c;..\..\..\..\..\..\components\ble\ble_services\ble_bas;..\..\..\..\..\..\components\ble\ble_services\ble_bas_c;..\..\..\..\..\..\components\ble\ble_services\ble_cscs;..\..\..\..\..\..\components\ble\ble_services\ble_cts_c;..\..\..\..\..\..\components\ble\ble_services\ble_dfu;..\..\..\..\..\..\components\ble\ble_services\ble_dis;..\..\..\..\..\..\components\ble\ble_services\ble_gls;..\..\..\..\..\..\components\ble\ble_services\ble_hids;..\..\..\..\..\..\components\ble\ble_services\ble_hrs;..\..\..\..\..\..\components\ble\ble_services\ble_hrs_c;..\..\..\..\..\..\components\ble\ble_services\ble_hts;..\..\..\..\..\..\components\ble\ble_services\ble_ias;..\..\..\..\..\..\components\ble\ble_services\ble_ias_c;..\..\..\..\..\..\components\ble\ble_services\ble_lbs;..\..\..\..\..\..\components\ble\ble_services\ble_lbs_c;..\..\..\..\..\..\components\ble\ble_services\ble_lls;..\..\..\..\..\..\components\ble\ble_services\ble_nus;..\..\..\..\..\..\components\ble\ble_services\ble_nus_c;..\..\..\..\..\..\components\ble\ble_services\ble_rscs;..\..\..\..\..\..\components\ble\ble_services\ble_rscs_c;..\..\..\..\..\..\components\ble\ble_services\ble_tps;..\..\..\..\..\..\components\ble\common;..\..\..\..\..\..\components\ble\nrf_ble_gatt;..\..\..\..\..\..\components\ble\nrf_ble_qwr;..\..\..\..\..\..\components\ble\peer_manager;..\..\..\..\..\..\components\boards;..\..\..\..\..\..\components\libraries\atomic;..\..\..\..\..\..\components\libraries\atomic_fifo;..\..\..\..\..\..\components\libraries\atomic_flags;..\..\..\..\..\..\components\libraries\balloc;..\..\..\..\..\..\components\libraries\bootloader\ble_dfu;..\..\..\..\..\..\components\libraries\bsp;..\..\..\..\..\..\components\libraries\button;..\..\..\..\..\..\components\libraries\cli;..\..\..\..\..\..\components\libraries\crc16;..\..\..\..\..\..\components\libraries\crc32;..\..\..\..\..\..\components\libraries\crypto;..\..\..\..\..\..\components\libraries\csense;..\..\..\..\..\..\components\libraries\csense_drv;..\..\..\..\..\..\components\libraries\delay;..\..\..\..\..\..\components\libraries\ecc;..\..\..\..\..\..\components\libraries\experimental_section_vars;..\..\..\..\..\..\components\libraries\experimental_task_manager;..\..\..\..\..\..\components\libraries\fds;..\..\..\..\..\..\components\libraries\fifo;..\..\..\..\..\..\components\libraries\fstorage;..\..\..\..\..\..\components\libraries\gfx;..\..\..\..\..\..\components\libraries\gpiote;..\..\..\..\..\..\components\libraries\hardfault;..\..\..\..\..\..\components\libraries\hci;..\..\..\..\..\..\components\libraries\led_softblink;..\..\..\..\..\..\components\libraries\log;..\..\..\..\..\..\components\libraries\log\src;..\..\..\..\..\..\components\libraries\low_power_pwm;..\..\..\..\..\..\components\libraries\mem_manager;..\..\..\..\..\..\components\libraries\memobj;..\..\..\..\..\..\components\libraries\mpu;..\..\..\..\..\..\components\libraries\mutex;..\..\..\..\..\..\components\libraries\pwm;..\..\..\..\..\..\components\libraries\pwr_mgmt;..\..\..\..\..\..\components\libraries\queue;..\..\..\..\..\..\components\libraries\ringbuf;..\..\..\..\..\..\components\libraries\scheduler;..\..\..\..\..\..\components\libraries\sdcard;..\..\..\..\..\..\components\libraries\slip;..\..\..\..\..\..\components\libraries\sortlist;..\..\..\..\..\..\components\libraries\spi_mngr;..\..\..\..\..\..\components\libraries\stack_guard;..\..\..\..\..\..\components\libraries\strerror;..\..\..\..\..\..\components\libraries\svc;..\..\..\..\..\..\components\libraries\timer;..\..\..\..\..\..\components\libraries\twi_mngr;..\..\..\..\..\..\components\libraries\twi_sensor;..\..\..\..\..\..\components\libraries\uart;..\..\..\..\..\..\components\libraries\usbd;..\..\..\..\..\..\components\libraries\usbd\class\audio;..\..\..\..\..\..\components\libraries\usbd\class\cdc;..\..\..\..\..\..\components\libraries\usbd\class\cdc\acm;..\..\..\..\..\..\components\libraries\usbd\class\hid;..\..\..\..\..\..\components\libraries\usbd\class\hid\generic;..\..\..\..\..\..\components\libraries\usbd\class\hid\kbd;..\..\..\..\..\..\components\libraries\usbd\class\hid\mouse;..\..\..\..\..\..\components\libraries\usbd\class\msc;..\..\..\..\..\..\components\libraries\util;..\..\..\..\..\..\components\nfc\ndef\conn_hand_parser;..\..\..\..\..\..\components\nfc\ndef\conn_hand_parser\ac_rec_parser;..\..\..\..\..\..\components\nfc\ndef\conn_hand_parser\ble_oob_advdata_parser;..\..\..\..\..\..\components\nfc\ndef\conn_hand_parser\le_oob_rec_parser;..\..\..\..\..\..\components\nfc\ndef\connection_handover\ac_rec;..\..\..\..\..\..\components\nfc\ndef\connection_handover\ble_oob_advdata;..\..\..\..\..\..\components\nfc\ndef\connection_handover\ble_pair_lib;..\..\..\..\..\..\components\nfc\ndef\connection_handover\ble_pair_msg;..\..\..\..\..\..\components\nfc\ndef\connection_handover\common;..\..\..\..\..\..\components\nfc\ndef\connection_handover\ep_oob_rec;..\..\..\..\..\..\components\nfc\ndef\connection_handover\hs_rec;..\..\..\..\..\..\components\nfc\ndef\connection_handover\le_oob_rec;..\..\..\..\..\..\components\nfc\ndef\generic\message;..\..\..\..\..\..\components\nfc\ndef\generic\record;..\..\..\..\..\..\components\nfc\ndef\launchapp;..\..\..\..\..\..\components\nfc\ndef\parser\message;..\..\..\..\..\..\components\nfc\ndef\parser\record;..\..\..\..\..\..\components\nfc\ndef\text;..\..\..\..\..\..\components\nfc\ndef\uri;..\..\..\..\..\..\components\nfc\platform;..\..\..\..\..\..\components\nfc\t2t_lib;..\..\..\..\..\..\components\nfc\t2t_parser;..\..\..\..\..\..\components\nfc\t4t_lib;..\..\..\..\..\..\components\nfc\t4t_parser\apdu;..\..\..\..\..\..\components\nfc\t4t_parser\cc_file;..\..\..\..\..\..\components\nfc\t4t_parser\hl_detection_procedure;..\..\..\..\..\..\components\nfc\t4t_parser\tlv;..\..\..\..\..\..\components\softdevice\common;..\..\..\..\..\..\components\softdevice\s140\headers;..\..\..\..\..\..\components\softdevice\s140\headers\nrf52;..\..\..\..\..\..\external\fprintf;..\..\..\..\..\..\external\segger_rtt;..\..\..\..\..\..\external\utf_converter;..\..\..\..\..\..\integration\nrfx;..\..\..\..\..\..\integration\nrfx\legacy;..\..\..\..\..\..\modules\nrfx;..\..\..\..\..\..\modules\nrfx\drivers\include;..\..\..\..\..\..\modules\nrfx\hal;..\config;..\..\..\..\ble_app_bladder_patch;..\..\..\..\..\..\components\libraries\bootloader\dfu;..\..\..\..\..\..\components\libraries\bootloader;..\..\..\..\..\..\modules\nrfx\drivers\src;..\..\..\measurement\imu;..\..\..\measurement\imu\Invn;..\..\..\measurement\imu\Invn\EmbUtils;..\..\..\measurement\imu\Invn\imu;..\..\..\measurement\imu\Invn\lib_agm;..\..\..\measurement\imu\Invn\lib_agm\invn\common;..\..\..\measurement\imu\Invn\lib_agm\test_vector;..\..\..\..\..\..\integration\nrfx\legacy;..\..\..\measurement\imu\app_raw;..\..\..\..\..\..\components\libraries\crypto\backend\cc310;..\..\..\..\..\..\components\libraries\crypto\backend\cc310_bl;..\..\..\..\..\..\components\libraries\crypto\backend\cifra;..\..\..\..\..\..\components\libraries\crypto\backend\mbedtls;..\..\..\..\..\..\components\libraries\crypto\backend\micro_ecc;..\..\..\..\..\..\components\libraries\crypto\backend\nrf_hw;..\..\..\..\..\..\components\libraries\crypto\backend\nrf_sw;..\..\..\..\..\..\components\libraries\crypto\backend\oberon;..\..\..\..\..\..\components\libraries\crypto\backend\optiga;..\..\..\..\..\..\external\nrf_cc310\include;..\..\..\..\..\..\external\mbedtls\include;..\..\..\..\..\..\external\nrf_oberon;..\..\..\..\..\..\external\nrf_oberon\include;..\..\..\..\..\..\external\nrf_tls\mbedtls\nrf_crypto\config;..\..\..\..\..\..\components\libraries\stack_info;..\..\..\..\..\..\pc_firm\ble_security;..\..\..\..\..\..\pc_firm;..\..\..\..\dr_piezo;..\..\..\..\..\..\pc_firm\dr_util;..\..\..\..\..\..\pc_firm\dr_adc121s051;..\..\..\measurement\battery;..\..\..\measurement\temperature;..\..\..\system\led;..\..\..\system\power;..\..\..\hal\i2c;..\..\..\hal\fds;..\..\..\system + ..\..\..\config;..\..\..\..\..\..\components;..\..\..\..\..\..\components\ble\ble_advertising;..\..\..\..\..\..\components\ble\ble_dtm;..\..\..\..\..\..\components\ble\ble_link_ctx_manager;..\..\..\..\..\..\components\ble\ble_racp;..\..\..\..\..\..\components\ble\ble_services\ble_ancs_c;..\..\..\..\..\..\components\ble\ble_services\ble_ans_c;..\..\..\..\..\..\components\ble\ble_services\ble_bas;..\..\..\..\..\..\components\ble\ble_services\ble_bas_c;..\..\..\..\..\..\components\ble\ble_services\ble_cscs;..\..\..\..\..\..\components\ble\ble_services\ble_cts_c;..\..\..\..\..\..\components\ble\ble_services\ble_dfu;..\..\..\..\..\..\components\ble\ble_services\ble_dis;..\..\..\..\..\..\components\ble\ble_services\ble_gls;..\..\..\..\..\..\components\ble\ble_services\ble_hids;..\..\..\..\..\..\components\ble\ble_services\ble_hrs;..\..\..\..\..\..\components\ble\ble_services\ble_hrs_c;..\..\..\..\..\..\components\ble\ble_services\ble_hts;..\..\..\..\..\..\components\ble\ble_services\ble_ias;..\..\..\..\..\..\components\ble\ble_services\ble_ias_c;..\..\..\..\..\..\components\ble\ble_services\ble_lbs;..\..\..\..\..\..\components\ble\ble_services\ble_lbs_c;..\..\..\..\..\..\components\ble\ble_services\ble_lls;..\..\..\..\..\..\components\ble\ble_services\ble_nus;..\..\..\..\..\..\components\ble\ble_services\ble_nus_c;..\..\..\..\..\..\components\ble\ble_services\ble_rscs;..\..\..\..\..\..\components\ble\ble_services\ble_rscs_c;..\..\..\..\..\..\components\ble\ble_services\ble_tps;..\..\..\..\..\..\components\ble\common;..\..\..\..\..\..\components\ble\nrf_ble_gatt;..\..\..\..\..\..\components\ble\nrf_ble_qwr;..\..\..\..\..\..\components\ble\peer_manager;..\..\..\..\..\..\components\boards;..\..\..\..\..\..\components\libraries\atomic;..\..\..\..\..\..\components\libraries\atomic_fifo;..\..\..\..\..\..\components\libraries\atomic_flags;..\..\..\..\..\..\components\libraries\balloc;..\..\..\..\..\..\components\libraries\bootloader\ble_dfu;..\..\..\..\..\..\components\libraries\bsp;..\..\..\..\..\..\components\libraries\button;..\..\..\..\..\..\components\libraries\cli;..\..\..\..\..\..\components\libraries\crc16;..\..\..\..\..\..\components\libraries\crc32;..\..\..\..\..\..\components\libraries\crypto;..\..\..\..\..\..\components\libraries\csense;..\..\..\..\..\..\components\libraries\csense_drv;..\..\..\..\..\..\components\libraries\delay;..\..\..\..\..\..\components\libraries\ecc;..\..\..\..\..\..\components\libraries\experimental_section_vars;..\..\..\..\..\..\components\libraries\experimental_task_manager;..\..\..\..\..\..\components\libraries\fds;..\..\..\..\..\..\components\libraries\fifo;..\..\..\..\..\..\components\libraries\fstorage;..\..\..\..\..\..\components\libraries\gfx;..\..\..\..\..\..\components\libraries\gpiote;..\..\..\..\..\..\components\libraries\hardfault;..\..\..\..\..\..\components\libraries\hci;..\..\..\..\..\..\components\libraries\led_softblink;..\..\..\..\..\..\components\libraries\log;..\..\..\..\..\..\components\libraries\log\src;..\..\..\..\..\..\components\libraries\low_power_pwm;..\..\..\..\..\..\components\libraries\mem_manager;..\..\..\..\..\..\components\libraries\memobj;..\..\..\..\..\..\components\libraries\mpu;..\..\..\..\..\..\components\libraries\mutex;..\..\..\..\..\..\components\libraries\pwm;..\..\..\..\..\..\components\libraries\pwr_mgmt;..\..\..\..\..\..\components\libraries\queue;..\..\..\..\..\..\components\libraries\ringbuf;..\..\..\..\..\..\components\libraries\scheduler;..\..\..\..\..\..\components\libraries\sdcard;..\..\..\..\..\..\components\libraries\slip;..\..\..\..\..\..\components\libraries\sortlist;..\..\..\..\..\..\components\libraries\spi_mngr;..\..\..\..\..\..\components\libraries\stack_guard;..\..\..\..\..\..\components\libraries\strerror;..\..\..\..\..\..\components\libraries\svc;..\..\..\..\..\..\components\libraries\timer;..\..\..\..\..\..\components\libraries\twi_mngr;..\..\..\..\..\..\components\libraries\twi_sensor;..\..\..\..\..\..\components\libraries\uart;..\..\..\..\..\..\components\libraries\usbd;..\..\..\..\..\..\components\libraries\usbd\class\audio;..\..\..\..\..\..\components\libraries\usbd\class\cdc;..\..\..\..\..\..\components\libraries\usbd\class\cdc\acm;..\..\..\..\..\..\components\libraries\usbd\class\hid;..\..\..\..\..\..\components\libraries\usbd\class\hid\generic;..\..\..\..\..\..\components\libraries\usbd\class\hid\kbd;..\..\..\..\..\..\components\libraries\usbd\class\hid\mouse;..\..\..\..\..\..\components\libraries\usbd\class\msc;..\..\..\..\..\..\components\libraries\util;..\..\..\..\..\..\components\nfc\ndef\conn_hand_parser;..\..\..\..\..\..\components\nfc\ndef\conn_hand_parser\ac_rec_parser;..\..\..\..\..\..\components\nfc\ndef\conn_hand_parser\ble_oob_advdata_parser;..\..\..\..\..\..\components\nfc\ndef\conn_hand_parser\le_oob_rec_parser;..\..\..\..\..\..\components\nfc\ndef\connection_handover\ac_rec;..\..\..\..\..\..\components\nfc\ndef\connection_handover\ble_oob_advdata;..\..\..\..\..\..\components\nfc\ndef\connection_handover\ble_pair_lib;..\..\..\..\..\..\components\nfc\ndef\connection_handover\ble_pair_msg;..\..\..\..\..\..\components\nfc\ndef\connection_handover\common;..\..\..\..\..\..\components\nfc\ndef\connection_handover\ep_oob_rec;..\..\..\..\..\..\components\nfc\ndef\connection_handover\hs_rec;..\..\..\..\..\..\components\nfc\ndef\connection_handover\le_oob_rec;..\..\..\..\..\..\components\nfc\ndef\generic\message;..\..\..\..\..\..\components\nfc\ndef\generic\record;..\..\..\..\..\..\components\nfc\ndef\launchapp;..\..\..\..\..\..\components\nfc\ndef\parser\message;..\..\..\..\..\..\components\nfc\ndef\parser\record;..\..\..\..\..\..\components\nfc\ndef\text;..\..\..\..\..\..\components\nfc\ndef\uri;..\..\..\..\..\..\components\nfc\platform;..\..\..\..\..\..\components\nfc\t2t_lib;..\..\..\..\..\..\components\nfc\t2t_parser;..\..\..\..\..\..\components\nfc\t4t_lib;..\..\..\..\..\..\components\nfc\t4t_parser\apdu;..\..\..\..\..\..\components\nfc\t4t_parser\cc_file;..\..\..\..\..\..\components\nfc\t4t_parser\hl_detection_procedure;..\..\..\..\..\..\components\nfc\t4t_parser\tlv;..\..\..\..\..\..\components\softdevice\common;..\..\..\..\..\..\components\softdevice\s140\headers;..\..\..\..\..\..\components\softdevice\s140\headers\nrf52;..\..\..\..\..\..\external\fprintf;..\..\..\..\..\..\external\segger_rtt;..\..\..\..\..\..\external\utf_converter;..\..\..\..\..\..\integration\nrfx;..\..\..\..\..\..\integration\nrfx\legacy;..\..\..\..\..\..\modules\nrfx;..\..\..\..\..\..\modules\nrfx\drivers\include;..\..\..\..\..\..\modules\nrfx\hal;..\config;..\..\..\..\ble_app_bladder_patch;..\..\..\..\..\..\components\libraries\bootloader\dfu;..\..\..\..\..\..\components\libraries\bootloader;..\..\..\..\..\..\modules\nrfx\drivers\src;..\..\..\measurement\imu;..\..\..\measurement\imu\Invn;..\..\..\measurement\imu\Invn\EmbUtils;..\..\..\measurement\imu\Invn\imu;..\..\..\measurement\imu\Invn\lib_agm;..\..\..\measurement\imu\Invn\lib_agm\invn\common;..\..\..\measurement\imu\Invn\lib_agm\test_vector;..\..\..\..\..\..\integration\nrfx\legacy;..\..\..\measurement\imu\app_raw;..\..\..\..\..\..\components\libraries\crypto\backend\cc310;..\..\..\..\..\..\components\libraries\crypto\backend\cc310_bl;..\..\..\..\..\..\components\libraries\crypto\backend\cifra;..\..\..\..\..\..\components\libraries\crypto\backend\mbedtls;..\..\..\..\..\..\components\libraries\crypto\backend\micro_ecc;..\..\..\..\..\..\components\libraries\crypto\backend\nrf_hw;..\..\..\..\..\..\components\libraries\crypto\backend\nrf_sw;..\..\..\..\..\..\components\libraries\crypto\backend\oberon;..\..\..\..\..\..\components\libraries\crypto\backend\optiga;..\..\..\..\..\..\external\nrf_cc310\include;..\..\..\..\..\..\external\mbedtls\include;..\..\..\..\..\..\external\nrf_oberon;..\..\..\..\..\..\external\nrf_oberon\include;..\..\..\..\..\..\external\nrf_tls\mbedtls\nrf_crypto\config;..\..\..\..\..\..\components\libraries\stack_info;..\..\..\system\security;..\..\..\command;..\..\..\measurement\piezo;..\..\..\command\util;..\..\..\measurement\adc121s051;..\..\..\measurement\battery;..\..\..\measurement\temperature;..\..\..\system\led;..\..\..\system\power;..\..\..\hal\i2c;..\..\..\hal\fds;..\..\..\system @@ -431,47 +431,47 @@ parser.h 5 - ..\..\..\..\..\..\pc_firm\parser.h + ..\..\..\command\parser.h parser.c 1 - ..\..\..\..\..\..\pc_firm\parser.c + ..\..\..\command\parser.c ble_quick_security.c 1 - ..\..\..\..\..\..\pc_firm\ble_security\ble_quick_security.c + ..\..\..\system\security\ble_quick_security.c ble_quick_security.h 5 - ..\..\..\..\..\..\pc_firm\ble_security\ble_quick_security.h + ..\..\..\system\security\ble_quick_security.h dr_piezo.c 1 - ..\..\..\..\dr_piezo\dr_piezo.c + ..\..\..\measurement\piezo\dr_piezo.c dr_piezo.h 5 - ..\..\..\..\dr_piezo\dr_piezo.h + ..\..\..\measurement\piezo\dr_piezo.h dr_util.c 1 - ..\..\..\..\..\..\pc_firm\dr_util\dr_util.c + ..\..\..\command\util\dr_util.c dr_util.h 5 - ..\..\..\..\..\..\pc_firm\dr_util\dr_util.h + ..\..\..\command\util\dr_util.h dr_adc121s051.c 1 - ..\..\..\..\..\..\pc_firm\dr_adc121s051\dr_adc121s051.c + ..\..\..\measurement\adc121s051\dr_adc121s051.c led_control.c @@ -4619,47 +4619,47 @@ parser.h 5 - ..\..\..\..\..\..\pc_firm\parser.h + ..\..\..\command\parser.h parser.c 1 - ..\..\..\..\..\..\pc_firm\parser.c + ..\..\..\command\parser.c ble_quick_security.c 1 - ..\..\..\..\..\..\pc_firm\ble_security\ble_quick_security.c + ..\..\..\system\security\ble_quick_security.c ble_quick_security.h 5 - ..\..\..\..\..\..\pc_firm\ble_security\ble_quick_security.h + ..\..\..\system\security\ble_quick_security.h dr_piezo.c 1 - ..\..\..\..\dr_piezo\dr_piezo.c + ..\..\..\measurement\piezo\dr_piezo.c dr_piezo.h 5 - ..\..\..\..\dr_piezo\dr_piezo.h + ..\..\..\measurement\piezo\dr_piezo.h dr_util.c 1 - ..\..\..\..\..\..\pc_firm\dr_util\dr_util.c + ..\..\..\command\util\dr_util.c dr_util.h 5 - ..\..\..\..\..\..\pc_firm\dr_util\dr_util.h + ..\..\..\command\util\dr_util.h dr_adc121s051.c 1 - ..\..\..\..\..\..\pc_firm\dr_adc121s051\dr_adc121s051.c + ..\..\..\measurement\adc121s051\dr_adc121s051.c led_control.c diff --git a/pc_firm/ble_security/ble_quick_security.c b/project/ble_peripheral/ble_app_bladder_patch/system/security/ble_quick_security.c similarity index 97% rename from pc_firm/ble_security/ble_quick_security.c rename to project/ble_peripheral/ble_app_bladder_patch/system/security/ble_quick_security.c index 7be591c..37b6bb7 100644 --- a/pc_firm/ble_security/ble_quick_security.c +++ b/project/ble_peripheral/ble_app_bladder_patch/system/security/ble_quick_security.c @@ -1,175 +1,175 @@ -/** - * @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; - - // DEV 모드: 보안 실패 이벤트는 SDK 핸들러에 전달하지 않음 (disconnect 방지) - if (m_state.dev_mode && p_evt->evt_id == PM_EVT_CONN_SEC_FAILED) { - DBG_PRINTF("Security failed: error=%d\r\n", - p_evt->params.conn_sec_failed.error); - DBG_PRINTF("DEV: Ignoring sec failure, keeping connection\r\n"); - return; - } - - // Call standard handlers (required) - pm_handler_on_pm_evt(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); - - if (m_state.dev_mode) { - // DEV 모드: 보안 실패 무시 — 연결 유지 - DBG_PRINTF("DEV: Ignoring sec failure, keeping connection\r\n"); - break; - } - - if (p_evt->params.conn_sec_failed.error == PM_CONN_SEC_ERROR_PIN_OR_KEY_MISSING) { - // Key missing: 재페어링 시도, 실패 시 disconnect로 폴백 - err_code = pm_conn_secure(p_evt->conn_handle, true); - if (err_code != NRF_ERROR_INVALID_STATE && - err_code != NRF_ERROR_BUSY && - err_code != BLE_ERROR_INVALID_CONN_HANDLE) { - APP_ERROR_CHECK(err_code); - } - if (err_code != NRF_SUCCESS) { - // 재페어링 불가 → disconnect - pm_handler_disconnect_on_sec_failure(p_evt); - } - } else { - // 기타 보안 실패 → bond 삭제 후 재페어링 시도 - pm_peer_id_t peer_id; - if (pm_peer_id_get(p_evt->conn_handle, &peer_id) == NRF_SUCCESS - && peer_id != PM_PEER_ID_INVALID) { - pm_peer_delete(peer_id); - } - err_code = pm_conn_secure(p_evt->conn_handle, true); - if (err_code != NRF_SUCCESS) { - pm_handler_disconnect_on_sec_failure(p_evt); - } - } - break; - - case PM_EVT_CONN_SEC_CONFIG_REQ: - { - pm_conn_sec_config_t config = { - .allow_repairing = true - }; - 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; - } -} +/** + * @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; + + // DEV 모드: 보안 실패 이벤트는 SDK 핸들러에 전달하지 않음 (disconnect 방지) + if (m_state.dev_mode && p_evt->evt_id == PM_EVT_CONN_SEC_FAILED) { + DBG_PRINTF("Security failed: error=%d\r\n", + p_evt->params.conn_sec_failed.error); + DBG_PRINTF("DEV: Ignoring sec failure, keeping connection\r\n"); + return; + } + + // Call standard handlers (required) + pm_handler_on_pm_evt(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); + + if (m_state.dev_mode) { + // DEV 모드: 보안 실패 무시 — 연결 유지 + DBG_PRINTF("DEV: Ignoring sec failure, keeping connection\r\n"); + break; + } + + if (p_evt->params.conn_sec_failed.error == PM_CONN_SEC_ERROR_PIN_OR_KEY_MISSING) { + // Key missing: 재페어링 시도, 실패 시 disconnect로 폴백 + err_code = pm_conn_secure(p_evt->conn_handle, true); + if (err_code != NRF_ERROR_INVALID_STATE && + err_code != NRF_ERROR_BUSY && + err_code != BLE_ERROR_INVALID_CONN_HANDLE) { + APP_ERROR_CHECK(err_code); + } + if (err_code != NRF_SUCCESS) { + // 재페어링 불가 → disconnect + pm_handler_disconnect_on_sec_failure(p_evt); + } + } else { + // 기타 보안 실패 → bond 삭제 후 재페어링 시도 + pm_peer_id_t peer_id; + if (pm_peer_id_get(p_evt->conn_handle, &peer_id) == NRF_SUCCESS + && peer_id != PM_PEER_ID_INVALID) { + pm_peer_delete(peer_id); + } + err_code = pm_conn_secure(p_evt->conn_handle, true); + if (err_code != NRF_SUCCESS) { + pm_handler_disconnect_on_sec_failure(p_evt); + } + } + break; + + case PM_EVT_CONN_SEC_CONFIG_REQ: + { + pm_conn_sec_config_t config = { + .allow_repairing = true + }; + 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/project/ble_peripheral/ble_app_bladder_patch/system/security/ble_quick_security.h similarity index 96% rename from pc_firm/ble_security/ble_quick_security.h rename to project/ble_peripheral/ble_app_bladder_patch/system/security/ble_quick_security.h index fdc107a..2eae990 100644 --- a/pc_firm/ble_security/ble_quick_security.h +++ b/project/ble_peripheral/ble_app_bladder_patch/system/security/ble_quick_security.h @@ -1,56 +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 + /** + * @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/project/dfu/secure_bootloader/pca10056_s140_ble/arm5_no_packs/secure_bootloader_ble_s140_pca10056.uvprojx b/project/dfu/secure_bootloader/pca10056_s140_ble/arm5_no_packs/secure_bootloader_ble_s140_pca10056.uvprojx index ef61dc3..f1e8ec1 100644 --- a/project/dfu/secure_bootloader/pca10056_s140_ble/arm5_no_packs/secure_bootloader_ble_s140_pca10056.uvprojx +++ b/project/dfu/secure_bootloader/pca10056_s140_ble/arm5_no_packs/secure_bootloader_ble_s140_pca10056.uvprojx @@ -133,7 +133,7 @@ 0 0 1 - 1 + 0 4099 1