/******************************************************************************* * @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 */