코드 정리

- 주석 영문으로 변경
- Allman 스타일로 통일
This commit is contained in:
2026-04-16 12:01:51 +09:00
parent c98d9ae14e
commit 2861cb9815
35 changed files with 2406 additions and 2557 deletions

View File

@@ -6,16 +6,16 @@
*
* @details Uses Timer2 + GPIOTE + PPI for CPU-free 2MHz waveform generation
*
* Timing Diagram (???):
* Timing Diagram (TX sequence):
*
* |<----------- PE HIGH ----------->|
* PE ___/?????????????????????????????????\___
* P_OUT ___/?\_/?\_/?\_/?\_/?\________________\___
* N_OUT ___\_/?\_/?\_/?\_/?\_/________________\___
* DMP _________________________/?????\_________
* PE ___/-------------------------------------\___
* P_OUT ___/-\_/-\_/-\_/-\_/-\________________\___
* N_OUT ___\_/-\_/-\_/-\_/-\_/________________\___
* DMP _________________________/-----\_________
* |<-- 3~5 cycles -->| |<DMP>|
*
* P_OUT? N_OUT? ?? ?? ?? (???)
* P_OUT and N_OUT toggle in anti-phase (complementary)
*
* 2MHz = 500ns period = 250ns half-period
* Timer @ 16MHz: 1 tick = 62.5ns
@@ -23,68 +23,47 @@
* Full-period = 500ns = 8 ticks
******************************************************************************/
/*******************************************************************************
* [한국어 설명] 피에조 초음파 트랜스듀서 드라이버
/*==============================================================================
* Module overview — Piezo ultrasound transducer driver
*
* === 개요 ===
* 방광 측정용 2MHz 초음파 송신 신호를 생성하는 드라이버.
* nRF52840의 하드웨어 주변장치(Timer2 + GPIOTE + PPI)를 활용하여
* CPU 개입 없이 정밀한 2MHz 파형을 자동으로 생성한다.
* Generates 2 MHz ultrasound TX signals for bladder measurement using
* nRF52840 hardware peripherals (Timer2 + GPIOTE + PPI) for CPU-free
* precise waveform generation.
*
* === 초음파 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 드라이버 비활성화, 유휴 상태 복귀
* TX sequence:
* 1. PE = HIGH -> enable MOSFET driver
* 2. P_OUT / N_OUT alternating pulses (2 MHz, 3..7 cycles)
* P_OUT and N_OUT are anti-phase, applying +/-20V across the piezo
* 3. DMP = HIGH -> dump residual piezo energy
* 4. DMP = LOW -> discharge complete
* 5. PE = LOW -> return to idle
*
* === 하드웨어 아키텍처 ===
* [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틱) --> 타이머 자동 클리어 + 인터럽트(잔여 사이클 카운트)
* HW architecture:
* Timer2 CC[0] (half-period = 4 ticks) -> PPI CH8,9 -> GPIOTE CH4,5 toggle
* Timer2 CC[1] (full-period = 8 ticks) -> PPI CH10,11 -> GPIOTE CH4,5 toggle
* Timer2 CC[2] (full-period = 8 ticks) -> auto-clear + IRQ (cycle counter)
*
* - Timer2: 16MHz 클럭, 16비트 모드
* - CC[0] = 4틱(250ns) -> 반주기 시점에서 P_OUT/N_OUT 토글
* - CC[1] = 8틱(500ns) -> 전체주기 시점에서 P_OUT/N_OUT 토글
* - CC[2] = 8틱(500ns) -> 인터럽트 발생 + 타이머 자동 클리어(SHORT)
* Power: DR_PIEZO_PWR_EN (P1.9) -> DC/DC -> +/-20V
*
* - 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)는 두 번째 반주기에서 차감
******************************************************************************/
* SW burst mode (dr_piezo_burst_sw_* family):
* Direct GPIO via port register (NRF_P1->OUT), interrupts disabled,
* NOP-based timing. Per-frequency NOP count differs.
* NOP timing: CPU 64 MHz -> 1 NOP = 15.625 ns
* half_period_ns = 1 000 000 / (freq_MHz * 2)
* NOP_count = (half_period - register_write ~30 ns) / 15.625
* loop overhead (~47 ns = 3 NOP) subtracted from second half-period
*============================================================================*/
/* 헤더 포함 */
#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" /* 인터럽트 우선순위 등 플랫폼 유틸리티 */
#include "nrf_gpio.h"
#include "nrf_timer.h"
#include "nrf_gpiote.h"
#include "nrf_ppi.h"
#include "nrf_delay.h"
#include "power_control.h"
#include "app_util_platform.h"
/* 조건부 디버그 출력: FEATURE_PRINTF 정의 시 SEGGER RTT로 출력 */
/* Debug output: enabled when FEATURE_PRINTF is defined */
#ifdef FEATURE_PRINTF
#include "debug_print.h"
#else
@@ -92,76 +71,72 @@
#endif
/*==============================================================================
* 하드웨어 리소스 할당
* - Timer2: 2MHz 파형 생성의 시간 기준 (16MHz 클럭)
* - GPIOTE CH4/5: P_OUT/N_OUT 핀의 하드웨어 토글
* - PPI CH8~11: 타이머 비교 이벤트 -> GPIOTE 토글 태스크 자동 연결
* Hardware resource allocation
* Timer2: 16 MHz clock, time base for 2 MHz waveform
* GPIOTE CH4/5: P_OUT / N_OUT hardware toggle
* PPI CH8..11: timer compare events -> GPIOTE toggle tasks
*============================================================================*/
#define PIEZO_TIMER NRF_TIMER2 /* 사용할 타이머 인스턴스 */
#define PIEZO_TIMER_IRQn TIMER2_IRQn /* 타이머2 인터럽트 번호 */
#define PIEZO_TIMER_IRQ_PRIORITY 6 /* 인터럽트 우선순위 (6 = 중간) */
#define PIEZO_TIMER NRF_TIMER2
#define PIEZO_TIMER_IRQn TIMER2_IRQn
#define PIEZO_TIMER_IRQ_PRIORITY 6
/* GPIOTE 채널 할당 - 핀 토글 제어용 */
#define GPIOTE_CH_P_OUT 4 /* P_OUT(양극 출력) 토글용 GPIOTE 채널 */
#define GPIOTE_CH_N_OUT 5 /* N_OUT(음극 출력) 토글용 GPIOTE 채널 */
#define GPIOTE_CH_P_OUT 4 /* P_OUT toggle */
#define GPIOTE_CH_N_OUT 5 /* N_OUT toggle */
/* 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 토글 */
#define PPI_CH_P_OUT_TOGGLE_0 8 /* CC[0] (half-period) -> P_OUT */
#define PPI_CH_N_OUT_TOGGLE_0 9 /* CC[0] (half-period) -> N_OUT */
#define PPI_CH_P_OUT_TOGGLE_1 10 /* CC[1] (full-period) -> P_OUT */
#define PPI_CH_N_OUT_TOGGLE_1 11 /* CC[1] (full-period) -> N_OUT */
/*==============================================================================
* 타이밍 상수
* Timer2 클럭: 16MHz -> 1틱 = 62.5ns
* 2MHz 신호: 주기 500ns(8틱), 반주기 250ns(4)
* Timing constants
* Timer2 @ 16 MHz: 1 tick = 62.5 ns
* 2 MHz signal: period 500 ns (8 ticks), half-period 250 ns (4 ticks)
*============================================================================*/
#define TIMER_FREQ_MHZ 16 /* 타이머 클럭 주파수 (MHz) */
#define TICK_NS (1000 / TIMER_FREQ_MHZ) /* 1틱 = 62.5ns */
#define TIMER_FREQ_MHZ 16
#define TICK_NS (1000 / TIMER_FREQ_MHZ) /* 62.5 ns */
/* 2MHz 기준 타이밍: 주기 = 500ns, 반주기 = 250ns */
#define PERIOD_TICKS_2MHZ 8 /* 전체 주기: 500ns / 62.5ns = 8 */
#define HALF_PERIOD_TICKS 4 /* 반주기: 250ns / 62.5ns = 4 */
/* 2 MHz reference timing: period = 500 ns, half-period = 250 ns */
#define PERIOD_TICKS_2MHZ 8 /* full period: 500 ns / 62.5 ns = 8 ticks */
#define HALF_PERIOD_TICKS 4 /* half period: 250 ns / 62.5 ns = 4 ticks */
/*==============================================================================
* 피에조 동작 주파수 설정
*============================================================================*/
/*
* 목표 피에조 주파수: 2.1 MHz (하드웨어 버스트 모드용)
* Piezo operating frequency
*
* 소프트웨어 버스트 모드의 타이밍은 각 주파수별 함수에 NOP 개수로 하드코딩됨.
* 안정적인 파형 생성을 위해 컴파일 타임에 고정.
* Target: 2.1 MHz (HW burst mode).
* SW burst timing is hard-coded per-frequency via NOP count.
*
* 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 + 루프 오버헤드
* NOP timing (CPU 64 MHz, 1 NOP = 15.625 ns):
* 1.7 MHz: half 294 ns -> 1st half 18 NOP, 2nd half 10 NOP + loop overhead
* 1.8 MHz: half 278 ns -> 1st half 17 NOP, 2nd half 11 NOP + loop overhead
* 1.9 MHz: half 263 ns -> 1st half 15 NOP, 2nd half 9 NOP + loop overhead
* 2.0 MHz: half 250 ns -> 1st half 15 NOP, 2nd half 10 NOP + loop overhead
* 2.1 MHz: half 238 ns -> 1st half 14 NOP, 2nd half 9 NOP + loop overhead
* 2.2 MHz: half 227 ns -> 1st half 13 NOP, 2nd half 8 NOP + loop overhead
*
* 주파수를 변경하려면 dr_piezo_burst_sw_XXmhz() 함수의 NOP 수를 수정할 것.
*/
#define PIEZO_FREQ_MHZ 2.1f /* 기본 동작 주파수 (MHz) */
/*==============================================================================
* 정적 변수
* To change frequency, modify NOP count in dr_piezo_burst_sw_XXmhz().
*============================================================================*/
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; /* 드라이버 초기화 완료 여부 */
#define PIEZO_FREQ_MHZ 2.1f /* default operating frequency (MHz) */
/*==============================================================================
* 타이머2 인터럽트 핸들러
* 매 주기(CC[2])마다 호출되어 잔여 사이클을 감소시킨다.
* 잔여 사이클이 0이 되면:
* 1) 타이머 정지 및 클리어
* 2) GPIOTE 비활성화 (P_OUT/N_OUT 토글 중단)
* 3) GPIO를 출력 모드로 재설정 후 LOW로 초기화
* 4) DMP 펄스 발생 (피에조 잔류 에너지 방전)
* 5) PE = LOW (MOSFET 드라이버 비활성화)
* Static variables
*============================================================================*/
static volatile bool m_tx_active = false; /* TX in progress (modified in IRQ) */
static volatile uint8_t m_remaining_cycles = 0; /* remaining pulse cycles (decremented in IRQ) */
static uint32_t m_period_ticks = PERIOD_TICKS_2MHZ; /* current period (timer ticks) */
static bool m_power_enabled = false; /* DC/DC converter state */
static bool m_initialized = false; /* driver initialised flag */
/*==============================================================================
* Timer2 IRQ handler
*
* Called every period (CC[2]) to decrement the remaining cycle count.
* When zero:
* 1) Stop and clear timer
* 2) Disable GPIOTE (stop P_OUT/N_OUT toggling)
* 3) Reconfigure GPIOs as push-pull output, drive LOW
* 4) Generate DMP pulse (discharge residual piezo energy)
* 5) PE = LOW (disable MOSFET driver)
*============================================================================*/
void TIMER2_IRQHandler(void)
{
@@ -180,11 +155,11 @@ void TIMER2_IRQHandler(void)
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 ??? ??) */
/* Step 2: Disable GPIOTE (return GPIO to SW control) */
nrf_gpiote_task_disable(GPIOTE_CH_P_OUT);
nrf_gpiote_task_disable(GPIOTE_CH_N_OUT);
/* Step 3: GPIO? ?? ? idle ?? */
/* Step 3: Reconfigure GPIOs and set idle state */
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);
@@ -207,12 +182,10 @@ void TIMER2_IRQHandler(void)
}
/*==============================================================================
* 전원 제어 함수
* DC/DC 컨버터(±20V)를 ON/OFF하여 피에조 구동 전압을 제어한다.
* 전원 안정화에 약 10ms 필요.
* Power control
* Switches the DC/DC converter (+/-20V) for piezo driving voltage.
* ~10 ms needed for power stabilisation.
*============================================================================*/
/* 피에조 전원 ON: DC/DC 컨버터 활성화 → ±20V 생성 */
void dr_piezo_power_on(void)
{
//nrf_delay_ms(20);
@@ -228,12 +201,12 @@ void dr_piezo_power_on(void)
//DBG_PRINTF("[PIEZO] TX/RX Active: +/-20V ready\r\n");
}
/* 피에조 전원 OFF: TX 비활성화 → MUX 비활성화 → DC/DC 컨버터 차단 */
/* Power OFF: disable TX -> disable MUX -> shut down DC/DC */
void dr_piezo_power_off(void)
{
dr_piezo_disable();
/* MUX enable 핀 클리어: select_channel() 이후 HIGH로 남은 핀 해제 */
/* clear MUX enable pins left HIGH by select_channel() */
nrf_gpio_pin_clear(DR_PIEZO_EN_MUXA);
nrf_gpio_pin_clear(DR_PIEZO_EN_MUXB);
nrf_gpio_pin_clear(DR_PIEZO_MUX_SEL0);
@@ -246,17 +219,17 @@ void dr_piezo_power_off(void)
//DBG_PRINTF("[PIEZO] Power OFF\r\n");
}
/* 피에조 전원 상태 확인 */
/* Check piezo power state */
bool dr_piezo_is_power_on(void)
{
return m_power_enabled;
}
/*==============================================================================
* 내부(private) 초기화 함수
* Internal (private) initialisation
*============================================================================*/
/* GPIO 초기화: 모든 신호 핀을 출력 모드로 설정하고 LOW(유휴)로 초기화 */
/* Configure all signal pins as push-pull output, drive LOW (idle) */
static void dr_piezo_gpio_init(void)
{
nrf_gpio_cfg_output(DR_PIEZO_PIN_PE);
@@ -274,7 +247,7 @@ static void dr_piezo_gpio_init(void)
dr_piezo_mux_init();
}
/* GPIOTE 초기화: P_OUT/N_OUT을 토글 모드로 설정 (PPI 연결 대상) */
/* GPIOTE init: configure P_OUT/N_OUT in toggle mode (PPI targets) */
static void dr_piezo_gpiote_init(void)
{
/* P_OUT: Toggle mode, initial LOW */
@@ -297,10 +270,10 @@ static void dr_piezo_gpiote_init(void)
}
/*
* 타이머 초기화: 16MHz 클럭, 16비트 모드
* CC[0]=반주기(4틱): 반주기 시점 토글 이벤트
* CC[1]=전체주기(8틱): 전체주기 시점 토글 이벤트
* CC[2]=전체주기(8틱): 인터럽트 발생 + 타이머 자동 클리어(SHORT)
* Timer init: 16 MHz clock, 16-bit mode
* CC[0] = half-period (4 ticks): toggle event at half-period
* CC[1] = full-period (8 ticks): toggle event at full-period
* CC[2] = full-period (8 ticks): IRQ + auto-clear (SHORT)
*/
static void dr_piezo_timer_init(void)
{
@@ -312,31 +285,31 @@ static void dr_piezo_timer_init(void)
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
* Compare channel assignment:
* - CC[0] = 4 (half period): toggle P_OUT, toggle N_OUT
* - CC[1] = 8 (full period): toggle P_OUT, toggle N_OUT
* - CC[2] = 8 (full period): cycle count IRQ + CLEAR
*
* Initial state: P=HIGH, N=LOW
* t=4: 1st toggle -> P=LOW, N=HIGH
* t=8: 2nd toggle -> P=HIGH, N=LOW (+ CLEAR)
* t=12: 1st toggle -> P=LOW, N=HIGH
* ...
*/
/* CC[0]: ??? ?? */
/* CC[0]: half-period toggle */
nrf_timer_cc_write(PIEZO_TIMER, NRF_TIMER_CC_CHANNEL0, HALF_PERIOD_TICKS); // 4
/* CC[1]: ? ?? ?? */
/* CC[1]: full-period toggle */
nrf_timer_cc_write(PIEZO_TIMER, NRF_TIMER_CC_CHANNEL1, m_period_ticks); // 8
/* CC[2]: ? ?? - ??? ???? */
/* CC[2]: full-period - cycle count IRQ source */
nrf_timer_cc_write(PIEZO_TIMER, NRF_TIMER_CC_CHANNEL2, m_period_ticks); // 8
/* CC[2]?? ?? CLEAR */
/* Auto-CLEAR on CC[2] match */
nrf_timer_shorts_enable(PIEZO_TIMER, NRF_TIMER_SHORT_COMPARE2_CLEAR_MASK);
/* CC[2] ???? (??? ???) */
/* CC[2] interrupt enable (cycle counter) */
nrf_timer_int_enable(PIEZO_TIMER, NRF_TIMER_INT_COMPARE2_MASK);
NVIC_SetPriority(PIEZO_TIMER_IRQn, PIEZO_TIMER_IRQ_PRIORITY);
@@ -344,17 +317,17 @@ static void dr_piezo_timer_init(void)
}
/*
* PPI 초기화: 타이머 비교 이벤트 → GPIOTE 토글 태스크 연결 (4채널)
* CC[0] 이벤트(반주기) → P_OUT 토글 + N_OUT 토글
* CC[1] 이벤트(전체주기) → P_OUT 토글 + N_OUT 토글
* 이로써 P_OUT N_OUT은 항상 역상으로 동작함.
* PPI init: connect timer compare events to GPIOTE toggle tasks (4 channels)
* CC[0] event (half-period) -> P_OUT toggle + N_OUT toggle
* CC[1] event (full-period) -> P_OUT toggle + N_OUT toggle
* This ensures P_OUT and N_OUT always operate in anti-phase.
*/
static void dr_piezo_ppi_init(void)
{
/*
* ??? ??:
* CC[0] (t=4): P_OUT ??, N_OUT ??
* CC[1] (t=8): P_OUT ??, N_OUT ??
* Connection map:
* CC[0] (t=4): P_OUT toggle, N_OUT toggle
* CC[1] (t=8): P_OUT toggle, N_OUT toggle
*/
/* CC[0] -> P_OUT toggle */
@@ -391,10 +364,10 @@ static void dr_piezo_ppi_init(void)
}
/*==============================================================================
* TX 드라이버 공개 함수
* TX driver public functions
*============================================================================*/
/* TX 드라이버 초기화: GPIO GPIOTE Timer PPI 순서로 설정 */
/* TX driver init: configure GPIO -> GPIOTE -> Timer -> PPI in order */
void dr_piezo_init(void)
{
dr_piezo_gpio_init();
@@ -407,7 +380,7 @@ void dr_piezo_init(void)
m_initialized = true;
}
/* TX 드라이버 해제: 타이머 정지, PPI/GPIOTE 비활성화, 모든 핀 LOW */
/* TX driver deinit: stop timer, disable PPI/GPIOTE, all pins LOW */
void dr_piezo_uninit(void)
{
nrf_timer_task_trigger(PIEZO_TIMER, NRF_TIMER_TASK_STOP);
@@ -432,11 +405,11 @@ void dr_piezo_uninit(void)
}
/*
* 하드웨어 기반 버스트 송신 (Timer + PPI + GPIOTE 사용)
* 1) GPIOTE를 재설정: P_OUT=HIGH 시작, N_OUT=LOW 시작 (역상)
* 2) PE = HIGH → MOSFET 드라이버 활성화
* 3) 타이머 시작 → PPI가 자동으로 P_OUT/N_OUT 토글
* 4) 인터럽트 핸들러에서 잔여 사이클 관리 및 종료 처리
* Hardware-based burst transmit (using Timer + PPI + GPIOTE)
* 1) Reconfigure GPIOTE: P_OUT starts HIGH, N_OUT starts LOW (anti-phase)
* 2) PE = HIGH -> enable MOSFET driver
* 3) Start timer -> PPI automatically toggles P_OUT/N_OUT
* 4) IRQ handler manages remaining cycles and shutdown sequence
*/
void dr_piezo_burst(uint8_t cycles)
{
@@ -460,7 +433,7 @@ void dr_piezo_burst(uint8_t cycles)
m_remaining_cycles = cycles;
m_tx_active = true;
/* GPIOTE ??? */
/* Reconfigure GPIOTE */
nrf_gpiote_task_disable(GPIOTE_CH_P_OUT);
nrf_gpiote_task_disable(GPIOTE_CH_N_OUT);
@@ -491,23 +464,23 @@ void dr_piezo_burst(uint8_t cycles)
nrf_timer_event_clear(PIEZO_TIMER, NRF_TIMER_EVENT_COMPARE1);
nrf_timer_event_clear(PIEZO_TIMER, NRF_TIMER_EVENT_COMPARE2);
/* PE = HIGH (??? ??) */
/* PE = HIGH (enable driver) */
nrf_gpio_pin_set(DR_PIEZO_PIN_PE);
/* ??? ?? ? ??? ?? */
/* Settling delay before starting timer */
__NOP(); __NOP(); __NOP(); __NOP();
/* Timer START -> PPI? ???? P_OUT/N_OUT ?? */
/* Timer START -> PPI automatically toggles P_OUT/N_OUT */
nrf_timer_task_trigger(PIEZO_TIMER, NRF_TIMER_TASK_START);
}
/* 기본 사이클 수(5)로 버스트 송신 */
/* Burst transmit with default cycle count (5) */
void dr_piezo_pulse(void)
{
dr_piezo_burst(DR_PIEZO_DEFAULT_CYCLES);
}
/* TX 출력 활성화: 수동으로 초기 상태 설정 (PE=HIGH, P_OUT=HIGH, N_OUT=LOW) */
/* Enable TX output: manually set initial state (PE=HIGH, P_OUT=HIGH, N_OUT=LOW) */
void dr_piezo_enable(void)
{
nrf_gpio_pin_set(DR_PIEZO_PIN_P_OUT);
@@ -517,7 +490,7 @@ void dr_piezo_enable(void)
DBG_PRINTF("[DR_PIEZO] TX enabled\r\n");
}
/* TX 출력 비활성화: 모든 신호 핀 LOW로 복귀 (유휴 상태) */
/* Disable TX output: all signal pins return to LOW (idle) */
void dr_piezo_disable(void)
{
nrf_gpio_pin_clear(DR_PIEZO_PIN_DMP);
@@ -526,13 +499,13 @@ void dr_piezo_disable(void)
nrf_gpio_pin_clear(DR_PIEZO_PIN_N_OUT);
}
/* TX 송신 중 여부 확인 (인터럽트 핸들러에서 false로 전환) */
/* Check if TX is in progress (cleared to false in IRQ handler) */
bool dr_piezo_is_busy(void)
{
return m_tx_active;
}
/* 동작 주파수 변경 (100kHz~4MHz): 타이머 CC 레지스터 재설정 */
/* Change operating frequency (100kHz~4MHz): reconfigure timer CC registers */
void dr_piezo_set_frequency(uint32_t freq_hz)
{
if (freq_hz < 100000 || freq_hz > 4000000)
@@ -552,14 +525,14 @@ void dr_piezo_set_frequency(uint32_t freq_hz)
}
/*==============================================================================
* MUX 제어 함수
* 8채널 아날로그 MUX로 피에조 에코 신호 경로를 선택한다.
* MUXA: CH0~CH3 담당, MUXB: CH4~CH7 담당
* SEL0, SEL1: MUX 내부 채널 주소 선택
* High Drive(H0H1) 모드: MUX IC의 빠른 스위칭을 위해 강한 출력 구동력 사용
* MUX control functions
* 8-channel analog MUX selects piezo echo signal path.
* MUXA: CH0~CH3, MUXB: CH4~CH7
* SEL0, SEL1: MUX internal channel address selection
* High Drive (H0H1) mode: strong output drive for fast MUX IC switching
*============================================================================*/
/* MUX 제어 핀 초기화: 4개 핀 모두 High Drive 출력으로 설정, 기본값 LOW */
/* MUX control pin init: all 4 pins as High Drive output, default LOW */
void dr_piezo_mux_init(void)
{
/* Configure pins as output with high drive strength */
@@ -607,17 +580,17 @@ void dr_piezo_mux_init(void)
/*
* 피에조 채널 선택 (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) 필요.
* Select piezo channel (0~7)
* Channel mapping (EN_MUXA, EN_MUXB, SEL0, SEL1):
* CH0 = MUXA input0 (1,0,0,0) CH4 = MUXB input0 (0,1,1,1)
* CH1 = MUXA input2 (1,0,1,0) CH5 = MUXB input1 (0,1,0,1)
* CH2 = MUXA input1 (1,0,0,1) CH6 = MUXB input2 (0,1,1,0)
* CH3 = MUXA input3 (1,0,1,1) CH7 = MUXB input3 (0,1,0,0)
* MUX settling time (~1.3ms) required after channel switch.
*/
void dr_piezo_select_channel(uint8_t channel)
{
channel = channel & 0x07; /* 0~7 범위로 마스킹 */
channel = channel & 0x07; /* Mask to 0~7 range */
switch (channel) {
// EN_A EN_B SEL0 SEL1
@@ -655,11 +628,11 @@ void dr_piezo_select_channel(uint8_t channel)
break;
}
/* 채널 변경 시 MUX 안정화 시간 필요 (> 1.2ms) */
/* MUX settling time required after channel change (> 1.2ms) */
nrf_delay_us(DR_PIEZO_MUX_SETTLING_US);
}
/* 핀 테스트: 각 신호 핀을 순서대로 HIGH/LOW 토글 (오실로스코프 확인용) */
/* Pin test: toggle each signal pin HIGH/LOW in sequence (for oscilloscope verification) */
void dr_piezo_test_pins(void)
{
DBG_PRINTF("[DR_PIEZO] Pin test...\r\n");
@@ -700,24 +673,24 @@ void dr_piezo_test_pins(void)
}
/*==============================================================================
* 시스템 함수 (전원 + TX 드라이버 통합 제어)
* System functions (power + TX driver unified control)
*============================================================================*/
/* 시스템 전체 초기화: TX 드라이버 초기화 → 전원 ON (부팅 시 1회만 호출) */
/* Full system init: TX driver init -> power ON (call once at boot) */
void dr_piezo_system_init(void)
{
dr_piezo_init(); /* GPIO/GPIOTE/Timer/PPI 먼저 안전 상태로 설정 */
dr_piezo_power_on(); /* 그 다음 DC/DC 전원 인가 */
dr_piezo_init(); /* Set GPIO/GPIOTE/Timer/PPI to safe state first */
dr_piezo_power_on(); /* Then apply DC/DC power */
}
/* 시스템 전체 종료: TX 드라이버 해제 → 전원 OFF */
/* Full system shutdown: deinit TX driver -> power OFF */
void dr_piezo_system_uninit(void)
{
dr_piezo_uninit();
dr_piezo_power_off();
}
/* 전원 확인 후 버스트 송신 (블로킹: 송신 완료까지 대기) */
/* Burst transmit with power check (blocking: waits until TX complete) */
void dr_piezo_transmit(uint8_t cycles)
{
if (!m_power_enabled)
@@ -732,73 +705,74 @@ void dr_piezo_transmit(uint8_t cycles)
}
/*==============================================================================
* 소프트웨어 기반 버스트 모드
* Software-based burst mode
* 2025-12-11 Charles KWON
*==============================================================================
*
* Timer/PPI/GPIOTE 하드웨어 대신 CPU에서 직접 GPIO 레지스터를 조작하여
* 초음파 펄스를 생성하는 방식. 인터럽트를 비활성화하여 정확한 타이밍 보장.
* Generates ultrasound pulses by directly manipulating GPIO registers from
* the CPU, instead of using Timer/PPI/GPIOTE hardware. Interrupts are
* disabled to guarantee precise timing.
*
* === 타이밍 다이어그램 ===
* === Timing diagram ===
*
* |<-마진->|<----- 펄스들 ----->|<-- DMP -->|<-마진->|
* |<-margin->|<---- pulses ----->|<-- DMP -->|<-margin->|
*
* PE ___/--------------------------------------------------\___
* P_OUT ___________/-\_/-\_/-\_/-\_/-\____________________\_______
* N_OUT ___________\_/-\_/-\_/-\_/-\_/____________________\_______
* DMP __________________________________/----------\____________
*
* === 신호 설명 ===
* - PE (Pulse Enable): 전체 시퀀스를 감싸는 활성화 신호 (전후 마진 포함)
* - P_OUT: 양극 출력, N_OUT과 역상으로 2MHz 토글
* - N_OUT: 음극 출력, P_OUT과 역상으로 2MHz 토글
* - DMP (Dump): 펄스 완료 후 피에조 잔류 에너지 방전
* === Signal description ===
* - PE (Pulse Enable): activation signal wrapping the entire sequence (with margins)
* - P_OUT: positive output, toggles at 2MHz in anti-phase with N_OUT
* - N_OUT: negative output, toggles at 2MHz in anti-phase with P_OUT
* - DMP (Dump): discharges residual piezo energy after pulse completion
*
* === 동작 원리 ===
* 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()로 인터럽트 복원
* === Operating principle ===
* 1) __disable_irq() blocks interrupts -> prevents timing jitter
* 2) PE ON (via P0.25 OUTSET register -> no effect on other P0 pins)
* 3) NOP margin, then for-loop alternates P_OUT/N_OUT
* 4) DMP pulse (32 NOP ~ 500ns)
* 5) PE OFF, then __enable_irq() restores interrupts
*
* === 포트 레지스터 직접 접근 ===
* NRF_P1->OUT 레지스터에 미리 계산된 비트 마스크를 직접 기록.
* 이 방식으로 P_OUT, N_OUT, DMP를 동시에 제어하면서도
* 채널 선택 핀(MUX SEL)은 보존한다 (P1_CTRL_MASK로 제어 핀만 변경).
* PE는 P0 포트에 있으므로 OUTSET/OUTCLR 레지스터로 별도 제어.
* === Direct port register access ===
* Pre-calculated bitmasks are written directly to NRF_P1->OUT register.
* This allows simultaneous control of P_OUT, N_OUT, DMP while preserving
* channel select pins (MUX SEL) via P1_CTRL_MASK (only control pins change).
* PE is on P0 port, so it is controlled separately via OUTSET/OUTCLR registers.
*============================================================================*/
/* 핀 번호에서 포트 내 비트 위치 추출 (하위 5비트 = 0~31) */
/* Extract bit position within port from pin number (lower 5 bits = 0~31) */
#define PIN_NUM(pin) ((pin) & 0x1F)
/* P1 포트 핀의 비트 마스크 - dr_piezo.h의 핀 정의에서 자동 생성
/* P1 port pin bitmasks - auto-generated from pin definitions in dr_piezo.h
*
* 경고: 핀 번호를 절대 하드코딩하지 말 것!
* 하드코딩은 개발자의 시간을 일시적으로 절약해줄 수 있지만,
* 동시에 개발자의 수명을 단축시킬 것이다.
* WARNING: Never hardcode pin numbers!
* Hardcoding may save a developer's time temporarily,
* but it will also shorten that developer's lifespan.
* - Charles KWON
*
* 각 마스크는 해당 핀의 포트 레지스터 내 비트 위치를 나타낸다.
* NRF_P1->OUT에 직접 쓸 때 사용되며, 여러 핀을 동시에 제어 가능.
* Each mask represents the bit position within the port register.
* Used for direct writes to NRF_P1->OUT, enabling simultaneous multi-pin control.
*/
#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 방전 제어 */
#define P_OUT_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_P_OUT)) /* P1.07 positive output */
#define N_OUT_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_N_OUT)) /* P1.06 negative output */
#define PE_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_PE)) /* P0.25 pulse enable */
#define DMP_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_DMP)) /* P1.00 dump control */
/* P1 포트에서 피에조 제어에 사용하는 핀들의 결합 마스크 (채널 선택 핀 제외) */
/* Combined mask of piezo control pins on P1 port (excludes channel select pins) */
#define P1_CTRL_MASK (P_OUT_MASK | N_OUT_MASK | DMP_MASK)
/*
* 소프트웨어 버스트 - 기본 주파수 2.1MHz
* Software burst - default frequency 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)
* NOP timing calculation:
* 2.1MHz -> period 476ns, half-period 238ns
* CPU 64MHz -> 1 NOP = 15.625ns
* 1st half: 14 NOP (~219ns) + register write (~30ns) = ~249ns
* 2nd half: 9 NOP (~141ns) + loop overhead (~47ns) + register write (~30ns) = ~218ns
* Total: ~436-476ns (~2.1MHz)
*/
void dr_piezo_burst_sw(uint8_t cycles)
{
@@ -839,7 +813,7 @@ void dr_piezo_burst_sw(uint8_t cycles)
* IMPORTANT: Channel select pins (P1.11, P1.12) are preserved in all states
*------------------------------------------------------------------------*/
// PE OUTSET/OUTCLR 사용 (다른 P0 핀 영향 방지)
// PE uses OUTSET/OUTCLR (prevents affecting other P0 pins)
uint32_t p1_all_low = saved_p1_out & ~P1_CTRL_MASK;
uint32_t p1_P_high_N_low = p1_all_low | P_OUT_MASK;
@@ -852,11 +826,11 @@ void dr_piezo_burst_sw(uint8_t cycles)
__disable_irq();
/* Initialize: Set all signals to LOW */
NRF_P0->OUTCLR = PE_MASK; // PE OFF (OUTCLR로 다른 핀 영향 없음)
NRF_P0->OUTCLR = PE_MASK; // PE OFF (OUTCLR does not affect other pins)
NRF_P1->OUT = p1_all_low;
/* PE rises first with margin before pulses start */
NRF_P0->OUTSET = PE_MASK; // PE ON (OUTSET로 다른 핀 영향 없음)
NRF_P0->OUTSET = PE_MASK; // PE ON (OUTSET does not affect other pins)
__NOP(); __NOP(); __NOP(); /* ~47ns margin */
__NOP(); __NOP(); __NOP(); /* ~47ns margin */
@@ -947,11 +921,11 @@ void dr_piezo_burst_sw(uint8_t cycles)
* 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)
* Software burst - 1.8MHz
* NOP timing: half-period 278ns
* 1st half: 17 NOP (~266ns) + register write (~30ns) = ~296ns
* 2nd half: 11 NOP (~172ns) + loop overhead (~47ns) + register write (~30ns) = ~249ns
* Total: ~499-556ns (~1.8MHz)
*/
void dr_piezo_burst_sw_18mhz(uint8_t cycles)
{
@@ -979,7 +953,7 @@ void dr_piezo_burst_sw_18mhz(uint8_t cycles)
//NRF_P0->OUT = saved_p0_out;
/* Pre-calculate P1 output states (PE P0.25 - OUTSET/OUTCLR로 제어) */
/* Pre-calculate P1 output states (PE on P0.25 - controlled via 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;
@@ -1076,11 +1050,11 @@ void dr_piezo_burst_sw_18mhz(uint8_t cycles)
* Total: ~468-500ns per cycle
*/
/*
* 소프트웨어 버스트 - 2.0MHz
* NOP 타이밍: 반주기 250ns
* 첫 반주기: 15 NOP(≒234ns) + 레지스터 쓰기(≒30ns) = 264ns
* 둘째 반주기: 10 NOP(≒156ns) + 루프 오버헤드(≒47ns) + 레지스터 쓰기(≒30ns) = 233ns
* 합계: 468~500ns (2.0MHz)
* Software burst - 2.0MHz
* NOP timing: half-period 250ns
* 1st half: 15 NOP (~234ns) + register write (~30ns) = ~264ns
* 2nd half: 10 NOP (~156ns) + loop overhead (~47ns) + register write (~30ns) = ~233ns
* Total: ~468-500ns (~2.0MHz)
*/
void dr_piezo_burst_sw_20mhz(uint8_t cycles)
{
@@ -1107,7 +1081,7 @@ void dr_piezo_burst_sw_20mhz(uint8_t cycles)
//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로 제어) */
/* Pre-calculate P1 output states (PE on P0.25 - controlled via 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;
@@ -1206,11 +1180,11 @@ void dr_piezo_burst_sw_20mhz(uint8_t cycles)
* Total: ~468-500ns per cycle
*/
/*
* 소프트웨어 버스트 - 1.9MHz
* NOP 타이밍: 반주기 263ns
* 첫 반주기: 15 NOP(≒234ns) + 레지스터 쓰기(≒30ns) = 264ns
* 둘째 반주기: 9 NOP(≒141ns) + 루프 오버헤드(≒47ns) + 레지스터 쓰기(≒30ns) = 218ns
* 합계: 468~500ns (1.9MHz)
* Software burst - 1.9MHz
* NOP timing: half-period 263ns
* 1st half: 15 NOP (~234ns) + register write (~30ns) = ~264ns
* 2nd half: 9 NOP (~141ns) + loop overhead (~47ns) + register write (~30ns) = ~218ns
* Total: ~468-500ns (~1.9MHz)
*/
void dr_piezo_burst_sw_19mhz(uint8_t cycles)
{
@@ -1237,7 +1211,7 @@ void dr_piezo_burst_sw_19mhz(uint8_t cycles)
//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로 제어) */
/* Pre-calculate P1 output states (PE on P0.25 - controlled via 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;
@@ -1338,11 +1312,11 @@ void dr_piezo_burst_sw_19mhz(uint8_t cycles)
* 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)
* Software burst - 2.2MHz
* NOP timing: half-period 227ns
* 1st half: 13 NOP (~203ns) + register write (~30ns) = ~233ns
* 2nd half: 8 NOP (~125ns) + loop overhead (~47ns) + register write (~30ns) = ~202ns
* Total: ~435-454ns (~2.2MHz)
*/
void dr_piezo_burst_sw_22mhz(uint8_t cycles)
{
@@ -1464,11 +1438,11 @@ void dr_piezo_burst_sw_22mhz(uint8_t cycles)
* Total: ~532-588ns per cycle
*/
/*
* 소프트웨어 버스트 - 1.7MHz
* NOP 타이밍: 반주기 294ns
* 첫 반주기: 18 NOP(≒281ns) + 레지스터 쓰기(≒30ns) = 311ns
* 둘째 반주기: 10 NOP(≒156ns) + 루프 오버헤드(≒47ns) + 레지스터 쓰기(≒30ns) = 233ns
* 합계: 532~588ns (1.7MHz)
* Software burst - 1.7MHz
* NOP timing: half-period 294ns
* 1st half: 18 NOP (~281ns) + register write (~30ns) = ~311ns
* 2nd half: 10 NOP (~156ns) + loop overhead (~47ns) + register write (~30ns) = ~233ns
* Total: ~532-588ns (~1.7MHz)
*/
void dr_piezo_burst_sw_17mhz(uint8_t cycles)
{
@@ -1494,7 +1468,7 @@ void dr_piezo_burst_sw_17mhz(uint8_t cycles)
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로 제어) */
/* Pre-calculate P1 output states (PE on P0 - controlled via 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;

View File

@@ -1,61 +1,30 @@
/*******************************************************************************
* @file dr_piezo.h
* @brief Piezo Transducer Driver (2MHz Signal Generator)
* @author Charles KWON
* @date 2025-12-09
/*==============================================================================
* dr_piezo.h - Piezo Transducer Driver (2 MHz Signal Generator)
*
* @note Hardware: nRF52840 + MD1822K6-G MOSFET Driver + TC7920K6-G MOSFET
* Output: +/-20V at 2MHz, 3~5 cycles
* Hardware: nRF52840 + MD1822K6-G MOSFET Driver + TC7920K6-G MOSFET
* Output: +/-20V at 2 MHz, 3..7 cycles burst
*
* @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)
* Timing Sequence:
* 1. PE = HIGH (enable)
* 2. P_OUT/N_OUT = 2 MHz pulses (3..7 cycles)
* 3. DMP = HIGH (dump residual energy)
* 4. DMP = LOW
* 5. PE = LOW (disable)
*
* All signals (P_OUT, N_OUT, DMP) operate within PE HIGH period.
******************************************************************************/
/*******************************************************************************
* [한국어 설명] 피에조 초음파 트랜스듀서 드라이버 헤더
* Pin assignment:
* Power: DR_PIEZO_PWR_EN (P1.9) — DC/DC +/-20V enable
* TX: PE (P0.25), DMP (P1.0), P_OUT (P1.7), N_OUT (P1.6)
* MUX: EN_MUXA (P0.21), EN_MUXB (P0.23), SEL0 (P1.10), SEL1 (P0.28)
*
* === 개요 ===
* 방광 측정용 초음파 송신기의 핀 할당, 설정값, 함수 선언을 정의.
* nRF52840 + MD1822K6-G(MOSFET 드라이버) + TC7920K6-G(MOSFET) 하드웨어 구성.
* 출력: +/-20V, 2MHz, 3~7 사이클 버스트.
* MUX channel mapping (8ch):
* 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)
*
* === 핀 할당 ===
* 전원 제어:
* - 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
******************************************************************************/
* Two burst modes:
* 1) HW burst (dr_piezo_burst): Timer2 + PPI + GPIOTE, CPU-independent
* 2) SW burst (dr_piezo_burst_sw_XXmhz): CPU NOP-based precise timing
* Per-frequency functions: 1.7 / 1.8 / 1.9 / 2.0 / 2.1 / 2.2 MHz
*============================================================================*/
#ifndef DR_PIEZO_H
#define DR_PIEZO_H
@@ -65,212 +34,92 @@
#include "nrf_gpio.h"
/*==============================================================================
* 전원 제어 핀 (DC/DC 컨버터 +/-20V)
* DR_PIEZO_PWR_EN: HIGH로 설정 시 DC/DC 컨버터가 +/-20V 고전압 생성
* Power control pin (+/-20V DC/DC converter)
*============================================================================*/
#define DR_PIEZO_PWR_EN NRF_GPIO_PIN_MAP(1, 9) /** Power Enable jhChun 0128 */
#define DR_PIEZO_PWR_EN NRF_GPIO_PIN_MAP(1, 9)
/*==============================================================================
* TX 신호 핀 (MOSFET 드라이버 제어)
* PE: Pulse Enable - 전체 TX 시퀀스 활성화/비활성화
* DMP: Dump - 펄스 후 피에조 잔류 에너지 방전용
* P_OUT: Positive Output - 피에조 양극 구동 (N_OUT과 역상)
* N_OUT: Negative Output - 피에조 음극 구동 (P_OUT과 역상)
* 주의: 이전 핀 할당(주석 처리)에서 새 보드 레이아웃으로 변경됨 (jhChun 0128)
* TX signal pins (MOSFET driver)
* PE: Pulse Enable — activates the entire TX sequence
* DMP: Dump — discharges residual piezo energy after burst
* P_OUT: Positive output — drives piezo positive terminal
* N_OUT: Negative output — drives piezo negative terminal (inverted P_OUT)
*============================================================================*/
#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
#define DR_PIEZO_PIN_PE NRF_GPIO_PIN_MAP(0, 25) /**< Pulse Enable */
#define DR_PIEZO_PIN_DMP NRF_GPIO_PIN_MAP(1, 0) /**< Dump control */
#define DR_PIEZO_PIN_P_OUT NRF_GPIO_PIN_MAP(1, 7) /**< Positive output */
#define DR_PIEZO_PIN_N_OUT NRF_GPIO_PIN_MAP(1, 6) /**< Negative output */
/*==============================================================================
* MUX 제어 핀 (에코 신호 경로 선택)
* 8채널 아날로그 MUX로 피에조 센서 채널을 선택한다.
* MUXA(CH0~CH3)와 MUXB(CH4~CH7) 두 개의 4채널 MUX 사용.
* EN_MUXA/EN_MUXB: 각 MUX 활성화 (동시에 하나만 HIGH)
* SEL0/SEL1: MUX 내부 4채널 중 하나를 선택하는 주소 비트
* MUX control pins (echo signal path selection, 8 channels)
* MUXA handles CH0..CH3, MUXB handles CH4..CH7.
* Only one MUX is enabled at a time.
*============================================================================*/
/* 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 채널 전환 후 아날로그 경로 안정화 대기 시간
* Configuration
*============================================================================*/
/**
* @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_DEFAULT_CYCLES 5 /**< Default burst cycles */
#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하여 피에조 구동 고전압을 제어한다.
* Power control
*============================================================================*/
/**
* @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
*/
/** @return true if power is ON */
bool dr_piezo_is_power_on(void);
/*==============================================================================
* TX 드라이버 함수
* 초음파 송신 관련: 초기화, 버스트 송신, 활성화/비활성화, 주파수 설정
* TX driver
*============================================================================*/
/**
* @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
* @brief Select piezo channel (0..7) via 8ch MUX
* @note MUX settling time: ~1.3 ms delay after switching
*/
void dr_piezo_select_channel(uint8_t channel);
/*==============================================================================
* 시스템 함수 (전원 + TX 통합 제어)
* 전원 ON/OFF와 TX 드라이버 초기화/해제를 한 번에 수행하는 편의 함수.
* 소프트웨어 버스트(burst_sw) 계열: CPU NOP 기반 정밀 타이밍.
* - Timer/PPI 없이 CPU에서 직접 GPIO를 제어
* - 인터럽트 비활성화 상태에서 동작하여 타이밍 정확도 보장
* - 주파수별 전용 함수 제공 (NOP 개수가 다름)
* System functions (power + TX combined)
*============================================================================*/
/**
* @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);
/*==============================================================================
* Software burst CPU NOP-based precise timing, no Timer/PPI
*
* Interrupts are disabled during burst for timing accuracy.
* Per-frequency functions (NOP count varies):
*============================================================================*/
/**
* @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);
void dr_piezo_burst_sw(uint8_t cycles); /**< 2.1 MHz (default) */
void dr_piezo_burst_sw_18mhz(uint8_t cycles); /**< 1.8 MHz */
void dr_piezo_burst_sw_20mhz(uint8_t cycles); /**< 2.0 MHz */
void dr_piezo_burst_sw_22mhz(uint8_t cycles); /**< 2.2 MHz */
void dr_piezo_burst_sw_17mhz(uint8_t cycles); /**< 1.7 MHz */
void dr_piezo_burst_sw_19mhz(uint8_t cycles); /**< 1.9 MHz */
#endif /* DR_PIEZO_H */