maa samples 140->100 변경, mec/maa 수신 시 Piezo 자동 Active/Sleep

- MAA_NUM_SAMPLES 140에서 100으로 변경 (DR_ADC_ECHO_SAMPLES_MAX 제한)
- 단일(mec) 및 모든 채널(maa) 에코 캡처 명령 수신 시 Piezo 자동 Active, 응답 송신 후 Sleep
- Cmd_mpa 중복 호출 정리 (dr_piezo_system_init 내부에 power_on 포함)
- 채널별 디버그 로그 추가
- 코드 리뷰 주석 정리 및 기타 파일 업데이트

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
jhChun
2026-03-17 18:03:04 +09:00
parent 39b4ffe66a
commit 96a46a26dd
29 changed files with 1474 additions and 501 deletions
@@ -3,19 +3,39 @@
* @author CandyPops Co.
* @version V1.0.0
* @date 2022-09-05
* @brief
* @brief
******************************************************************************/
/*******************************************************************************
* [헤더 개요] 배터리 전압 및 압력센서 SAADC 측정 인터페이스
*
* nRF52840 SAADC를 이용한 배터리 전압 측정과 압력센서 2채널 측정의
* 외부 호출용 API를 선언한다.
*
* 주요 API:
* - battery_level_meas() : 배터리 전압 1회 측정 (AIN2)
* - pressure_all_level_meas() : 압력센서 2채널 1회 측정 (AIN7, AIN4)
* - battery_timer_init/start/stop() : 5초 주기 배터리 모니터링 타이머 제어
*
* LOW_BATTERY_VOLTAGE(3100mV) 이하가 10회 연속 감지되면 자동 전원 OFF
******************************************************************************/
#ifndef _BATTERY_SAADC_H_
#define _BATTERY_SAADC_H_
/* 저전압 판정 임계값 (mV) — 이 값 이하가 10회 연속이면 자동 전원 OFF */
#define LOW_BATTERY_VOLTAGE 3100 /* Low Battery 임계값 */
/** @brief 배터리 전압 1회 측정 시작 (비동기, 결과는 콜백에서 처리) */
void battery_level_meas(void);
/** @brief 압력센서 2채널(AIN7, AIN4) 1회 측정 시작 (비동기) */
void pressure_all_level_meas(void);
/** @brief 배터리 모니터링 5초 반복 타이머 시작 */
void battery_timer_start(void);
/** @brief 배터리 모니터링 타이머 정지 */
void battery_timer_stop(void);
/** @brief 배터리 모니터링 타이머 초기화 (앱 시작 시 1회 호출) */
void battery_timer_init(void);
#endif //_BATTERY_SAADC_H_
@@ -1,13 +1,32 @@
// file: debug_print.h
/*******************************************************************************
* [한국어 설명] 디버그 출력 매크로 (조건부 컴파일)
*
* SEGGER RTT(Real Time Transfer)를 이용한 디버그 출력 매크로.
* J-Link 디버거를 통해 실시간으로 로그를 PC에 전송한다.
* UART를 사용하지 않으므로 시스템 타이밍에 미치는 영향이 적다.
*
* === 조건부 컴파일 ===
* ENABLE_PRINTF = 1: DBG_PRINTF가 SEGGER_RTT_printf(채널0)로 치환됨
* -> 실제 로그 출력 (디버깅 시 사용)
* ENABLE_PRINTF = 0: DBG_PRINTF가 빈 매크로로 치환됨
* -> 코드에서 완전히 제거 (릴리스 빌드 시 사용)
*
* === 사용법 ===
* DBG_PRINTF("값: %d\r\n", value); // printf와 동일한 포맷 문자열
* 출력은 SEGGER RTT Viewer 또는 J-Link RTT Client에서 확인.
******************************************************************************/
#ifndef DEBUG_PRINT_H
#define DEBUG_PRINT_H
#define ENABLE_PRINTF 1 // Set to 0 to disable globally
#define ENABLE_PRINTF 1 /* 1=디버그 출력 활성화, 0=전역 비활성화 */
#if ENABLE_PRINTF
#include "SEGGER_RTT.h"
/* SEGGER RTT 채널 0으로 포맷 문자열 출력 */
#define DBG_PRINTF(...) SEGGER_RTT_printf(0, __VA_ARGS__)
#else
/* 빈 매크로: 컴파일러가 호출 코드를 완전히 제거 */
#define DBG_PRINTF(...) // Do nothing
#endif
@@ -2,6 +2,36 @@
TEST medi50 Dec 23
******************************************************************************/
/**
* @file fstorage.c
* @brief FDS(Flash Data Storage) 기반 설정 저장 모듈
*
* 외부 EEPROM을 대체하여 nRF52840 내장 플래시에 장치 설정을 저장/로드한다.
*
* [레코드 관리]
* - CONFIG_FILE = 0x8010, CONFIG_REC_KEY = 0x7010 으로 단일 레코드를 관리한다.
*
* [config_data_t 구조체 필드]
* - magic(4B) : 포맷 확인용 매직 넘버 (MAGIC = 0x20231226)
* - hw_no(12B) : 하드웨어 번호 (BLE 명령으로 설정)
* - serial_no(12B) : 시리얼 번호 (기본값: "VB026030000")
* - passkey(6B) : BLE 페어링용 정적 패스키
* - bond_delete(1B) : 본딩 데이터 삭제 플래그
* - reset_status(1B) : 리셋 상태 값
* - pd_adc_cnt : 포토다이오드 ADC 측정 횟수
* - pd_delay_us : 포토다이오드 측정 간 지연 시간(us)
* - life_cycle : 장치 사용 횟수
*
* [매직 넘버 검증]
* - 플래시에서 로드한 데이터의 magic 값이 0x20231226과 일치하는지 확인하여
* 유효한 설정인지 판별한다. 불일치 시 기본값으로 초기화한다.
*
* [FDS 이벤트 후처리]
* - FDS 쓰기/업데이트 완료 이벤트 수신 후, 대기 중인 후처리를 수행한다:
* 전원 OFF (go_device_power_off), 슬립 진입 (go_sleep_mode_enter),
* 시스템 리셋 (go_NVIC_SystemReset)
*/
#include "sdk_config.h"
#include <string.h>
@@ -27,23 +57,29 @@
#include "debug_print.h"
/* File ID and Key used for the configuration record. */
/* FDS 레코드 식별자: 파일 ID와 레코드 키로 단일 설정 레코드를 관리 */
#define CONFIG_FILE (0x8010)
#define CONFIG_REC_KEY (0x7010)
/* 매직 넘버: 플래시에 저장된 데이터가 유효한 설정인지 판별하는 데 사용 */
#define CONFIG_MAGIC_NUMBER_VALUE (0x20231226)
/* 전역 설정 데이터 구조체 인스턴스 */
config_data_t m_config;
extern bool go_device_power_off;
extern bool go_sleep_mode_enter;
extern bool go_NVIC_SystemReset;
/* FDS 쓰기 완료 후 수행할 후처리 플래그 (main.c에서 선언) */
extern bool go_device_power_off; /* 전원 OFF 요청 */
extern bool go_sleep_mode_enter; /* 슬립 모드 진입 요청 */
extern bool go_NVIC_SystemReset; /* 시스템 리셋 요청 */
/* Flag to check fds initialization. */
/* FDS 초기화 완료 여부 플래그 (fds_evt_handler에서 true로 설정) */
static bool volatile m_fds_initialized;
/* FDS 쓰기 진행 중 플래그: true이면 쓰기 완료 대기 중 */
bool fds_flag_write = false;
/* A record containing dummy configuration data. */
/* FDS에 기록할 레코드 템플릿 (m_config 데이터를 가리킴) */
static fds_record_t const m_dummy_record =
{
.file_id = CONFIG_FILE,
@@ -54,9 +90,16 @@ static fds_record_t const m_dummy_record =
};
int8_t reset_status_dflt = 99;
uint8_t static_passkey_dflt[6] = "123456";
/* 기본 설정값 상수 */
int8_t reset_status_dflt = 99; /* 리셋 상태 기본값 */
uint8_t static_passkey_dflt[6] = "123456"; /* BLE 패스키 기본값 */
/**
* @brief 기본 설정값 초기화
*
* m_config 구조체의 각 필드를 공장 초기값으로 설정한다.
* 플래시에 유효한 설정이 없거나 매직 넘버가 불일치할 때 호출된다.
*/
void fds_default_value_set(void)
{
/* HW Number - empty (set via BLE command) */
@@ -84,8 +127,19 @@ void fds_default_value_set(void)
}
/* 마지막 FDS 이벤트 ID 저장 (디버깅용) */
static volatile uint8_t fds_last_evt = 0xFF;
/**
* @brief FDS 이벤트 콜백 핸들러
*
* FDS 내부에서 비동기 작업이 완료될 때 호출된다.
* - FDS_EVT_INIT : FDS 초기화 완료 → m_fds_initialized 플래그 설정
* - FDS_EVT_WRITE : 새 레코드 쓰기 완료 → fds_flag_write 해제
* - FDS_EVT_UPDATE : 레코드 업데이트 완료 → fds_flag_write 해제 후
* 대기 중인 전원 OFF / 슬립 진입 / 시스템 리셋 수행
* - FDS_EVT_DEL_RECORD / FDS_EVT_DEL_FILE / FDS_EVT_GC : 현재 미사용
*/
static void fds_evt_handler( fds_evt_t const *p_evt )
{
fds_last_evt = p_evt->id;
@@ -140,7 +194,13 @@ static void fds_evt_handler( fds_evt_t const *p_evt )
}
/**@brief Wait for fds to initialize. */
/**
* @brief FDS 초기화 완료 대기
*
* m_fds_initialized 플래그가 true가 될 때까지 대기한다.
* 최대 3초(3000ms) 타임아웃이 설정되어 있으며,
* 타임아웃 시 에러 로그를 출력하고 반환한다.
*/
static void wait_for_fds_ready( void )
{
uint32_t timeout = 0;
@@ -157,6 +217,20 @@ static void wait_for_fds_ready( void )
}
/**
* @brief FDS에서 설정 로드
*
* 플래시에서 CONFIG_FILE/CONFIG_REC_KEY 레코드를 검색하여 m_config에 로드한다.
*
* 동작 흐름:
* 1. fds_record_find()로 레코드 검색 (실패 시 최대 10회 재시도, 100ms 간격)
* 2. 레코드 발견 시:
* - fds_record_open()으로 열기 (CRC 에러 시 삭제 후 기본값으로 재생성)
* - 데이터를 m_config로 복사
* - 매직 넘버 불일치 시 기존 레코드 삭제 → 기본값 설정 → 재기록
* 3. 레코드 미발견 시:
* - 기본값으로 새 레코드 생성 후 다시 로드
*/
void config_load( void )
{
ret_code_t rc;
@@ -264,6 +338,21 @@ void config_load( void )
}
/**
* @brief 현재 설정을 FDS에 저장
*
* m_config의 내용을 플래시에 기록한다.
*
* 동작 흐름:
* 1. 이전 FDS 쓰기 작업이 진행 중이면 최대 3초 대기
* 2. 매직 넘버가 올바르지 않으면 보정
* 3. 기존 레코드가 있으면 fds_record_update()로 갱신
* - 플래시 공간 부족 시 GC(가비지 컬렉션) 수행 후 재시도
* 4. 기존 레코드가 없으면 fds_record_write()로 새로 생성
*
* 참고: 쓰기 완료는 fds_evt_handler()에서 비동기로 처리되며,
* 완료 후 전원 OFF/슬립/리셋 등의 후처리가 수행될 수 있다.
*/
void config_save( void )
{
ret_code_t rc;
@@ -334,11 +423,25 @@ void config_save( void )
}
/**
* @brief config_load()의 래퍼 함수
*
* 외부 모듈에서 설정 로드를 요청할 때 사용한다.
*/
void fs_set_value(void)
{
config_load();
}
/**
* @brief FDS 초기화
*
* 부팅 시 호출되어 FDS 모듈을 초기화한다.
* 1. fds_register()로 이벤트 핸들러 등록
* 2. fds_init()로 FDS 초기화 시작
* 3. wait_for_fds_ready()로 초기화 완료 대기 (최대 3초)
* 4. fds_stat()로 플래시 상태 확인
*/
void fs_storage_init(void)
{
ret_code_t rc;
@@ -3,7 +3,29 @@
* @author CandyPops Co.
* @version V1.0.0
* @date 2022-09-05
* @brief
* @brief FDS(Flash Data Storage) 기반 설정 저장 모듈 인터페이스
*******************************************************************************
*
* [헤더 개요]
* nRF52840 내장 플래시에 디바이스 설정을 저장/로드하는 FDS 모듈의 공용 API.
* 외부 EEPROM을 대체하며, SoftDevice와 공존하여 플래시를 안전하게 관리한다.
*
* [config_data_t 구조체] (45바이트, 패킹됨)
* magic_number(4B): 포맷 확인용 (0x20231226)
* hw_no(12B): 하드웨어 번호
* serial_no(12B): 시리얼 번호 (BLE 디바이스 이름으로도 사용)
* static_passkey(6B): BLE 페어링 패스키 (숫자 6자리)
* bond_data_delete(1B): 본딩 삭제 플래그
* reset_status(1B): 리셋 상태 코드
* pd_adc_cnt(1B): ADC 샘플링 횟수
* pd_delay_us(2B): PD 안정화 딜레이 (마이크로초)
* life_cycle(4B): 디바이스 사용 횟수
*
* [주요 API]
* fs_storage_init(): FDS 초기화 (부트 시 1회)
* config_load(): FDS에서 설정 로드 (없으면 기본값 생성)
* config_save(): 현재 설정을 FDS에 저장
*
******************************************************************************/
#ifndef IHP_FSTORAGE_H_
@@ -1,6 +1,28 @@
/*******************************************************************************
* @file i2c_manager.c
* @brief Reliable HW↔SW I2C Switching Logic (with Mode Set Logging)
*******************************************************************************
*
* [모듈 개요]
* I2C 버스의 HW(하드웨어 TWI) / SW(소프트웨어 비트뱅) 모드 전환을 관리하는 모듈.
*
* - HW I2C: nRF52840 내장 TWI 하드웨어 주변장치를 사용 (ICM42670P IMU 센서 통신용, 400kHz)
* - SW I2C: GPIO 비트뱅 방식의 소프트웨어 I2C (현재 사용하지 않는 레거시 코드)
*
* [핀 설정]
* SCL = P1.14 (ICM42670_I2C_SCL_PIN)
* SDA = P1.15 (ICM42670_I2C_SDA_PIN)
*
* [주요 함수]
* hw_i2c_init_once() : SW→HW 전환 또는 HW 초기화 (이미 HW 모드면 중복 초기화 방지)
* sw_i2c_init_once() : HW→SW 전환 (TWI 해제 후 SW 모드 진입, 레거시)
* i2c_reset_state() : 모든 I2C 모드 플래그를 초기화 (HW/SW 모두 false)
*
* [동작 원리]
* HW_I2C_FRQ, SW_I2C_FRQ 두 개의 bool 플래그로 현재 모드를 추적하며,
* 한 번에 하나의 모드만 활성화되도록 상호 배제(mutex) 방식으로 관리한다.
* 모드 전환 시 기존 모드의 리소스를 먼저 해제한 후 새 모드를 초기화한다.
*
******************************************************************************/
#include "i2c_manager.h"
@@ -12,40 +34,52 @@
#include "boards.h"
#include "system_interface.h"
bool HW_I2C_FRQ = true;
bool SW_I2C_FRQ = false;
/* 현재 I2C 모드 상태 플래그 (true = 해당 모드 활성화) */
bool HW_I2C_FRQ = true; /* HW TWI 모드 활성 여부 (기본값: true, 초기 상태는 HW) */
bool SW_I2C_FRQ = false; /* SW 비트뱅 모드 활성 여부 */
/* TWI 인스턴스 번호 (nRF52840은 TWI0, TWI1 두 개 지원) */
#define TWI_INSTANCE 0
/* TWI (I2C) 하드웨어 인스턴스 : IMU 드라이버에서 사용 - jhChun 26.03.16 */
const nrfx_twi_t m_twi = NRFX_TWI_INSTANCE(TWI_INSTANCE);
/* TWI (I2C) 해제 - jhChun 26.03.16 */
/* TWI 하드웨어를 비활성화하고 초기화 해제하여 GPIO 핀을 반환한다 */
static void twi_uninitialize(void){
nrfx_twi_disable(&m_twi);
nrfx_twi_uninit(&m_twi);
nrfx_twi_disable(&m_twi); /* TWI 주변장치 비활성화 */
nrfx_twi_uninit(&m_twi); /* TWI 초기화 해제 (핀 리소스 반환) */
}
/* TWI (I2C) 하드웨어 초기화 (SCL/SDA핀, 400kHz) - jhChun 26.03.16 */
/* SCL, SDA 핀을 설정하고 400kHz Fast Mode로 TWI를 초기화 및 활성화한다 */
static void twi_initialize(void){
ret_code_t err_code;
/* TWI 설정 구조체: 핀 번호, 클럭 속도, 인터럽트 우선순위 지정 */
const nrfx_twi_config_t twi_config = {
.scl = ICM42670_I2C_SCL_PIN,
.sda = ICM42670_I2C_SDA_PIN,
.frequency = NRF_TWI_FREQ_400K,
.interrupt_priority = APP_IRQ_PRIORITY_HIGH,
.scl = ICM42670_I2C_SCL_PIN, /* SCL 핀 (P1.14) */
.sda = ICM42670_I2C_SDA_PIN, /* SDA 핀 (P1.15) */
.frequency = NRF_TWI_FREQ_400K, /* 400kHz Fast Mode */
.interrupt_priority = APP_IRQ_PRIORITY_HIGH, /* 높은 인터럽트 우선순위 */
};
/* TWI 초기화 (이벤트 핸들러 NULL = 블로킹 모드) */
err_code = nrfx_twi_init(&m_twi, &twi_config, NULL, NULL);
APP_ERROR_CHECK(err_code);
nrfx_twi_enable(&m_twi);
nrfx_twi_enable(&m_twi); /* TWI 주변장치 활성화 → I2C 통신 가능 */
}
/* -------------------------------------------------------------------------- */
/* HW (TWI) 초기화 */
/* -------------------------------------------------------------------------- */
/*
* HW I2C 모드로 전환하거나 초기화하는 함수.
* - SW 모드가 활성화되어 있으면 SW 플래그를 해제하고 HW로 전환
* - 이미 HW 모드이면 중복 초기화를 방지하여 바로 리턴
* - 최초 HW 초기화 시 twi_initialize()를 호출하여 TWI 하드웨어 설정
*/
void hw_i2c_init_once(void)
{
// SW 모드일 경우 강제 해제 후 HW 전환
@@ -55,9 +89,10 @@ void hw_i2c_init_once(void)
// SW 리소스 해제 (필요 시 추가)
SW_I2C_FRQ = false;
nrf_delay_ms(2);
nrf_delay_ms(2); /* 모드 전환 안정화 대기 (2ms) */
}
/* 이미 HW 모드가 활성화되어 있으면 중복 초기화 방지를 위해 즉시 리턴 */
// 이미 HW면 스킵
if (HW_I2C_FRQ)
{
@@ -65,10 +100,12 @@ void hw_i2c_init_once(void)
return;
}
/* HW TWI 하드웨어 초기화 수행 (SCL/SDA 핀 설정, 400kHz, 활성화) */
// 실제 HW 초기화
twi_initialize();
nrf_delay_ms(2);
nrf_delay_ms(2); /* 초기화 후 안정화 대기 (2ms) */
/* 모드 플래그 갱신: HW 활성, SW 비활성 */
HW_I2C_FRQ = true;
SW_I2C_FRQ = false;
@@ -78,6 +115,12 @@ void hw_i2c_init_once(void)
/* -------------------------------------------------------------------------- */
/* SW (Port Bang-Bang) 초기화 */
/* -------------------------------------------------------------------------- */
/*
* SW I2C(비트뱅) 모드로 전환하는 함수. (현재 레거시, 사용하지 않음)
* - HW 모드가 활성화되어 있으면 TWI를 해제하고 SW로 전환
* - 이미 SW 모드이면 중복 초기화를 방지하여 바로 리턴
* - power_control.c의 power_loop()에서 Step 0에서 호출됨
*/
void sw_i2c_init_once(void)
{
// HW 모드일 경우 강제 해제 후 SW 전환
@@ -85,9 +128,9 @@ void sw_i2c_init_once(void)
{
//DBG_PRINTF("[I2C]HW→SW\r\n");
nrfx_twi_disable(&m_twi);
nrfx_twi_uninit(&m_twi);
nrf_delay_ms(2);
nrfx_twi_disable(&m_twi); /* TWI 비활성화 */
nrfx_twi_uninit(&m_twi); /* TWI 초기화 해제 */
nrf_delay_ms(2); /* 모드 전환 안정화 대기 (2ms) */
HW_I2C_FRQ = false;
}
@@ -99,10 +142,12 @@ void sw_i2c_init_once(void)
return;
}
/* TWI 라인 완전 해제 후 SW 비트뱅 모드 진입 */
// 실제 SW 초기화
twi_uninitialize(); // TWI 라인 해제
nrf_delay_ms(1);
nrf_delay_ms(1); /* 해제 후 안정화 대기 (1ms) */
/* 모드 플래그 갱신: SW 활성, HW 비활성 */
SW_I2C_FRQ = true;
HW_I2C_FRQ = false;
@@ -112,9 +157,14 @@ void sw_i2c_init_once(void)
/* -------------------------------------------------------------------------- */
/* 전체 리셋 */
/* -------------------------------------------------------------------------- */
/*
* 모든 I2C 모드 플래그를 초기화하는 함수.
* HW/SW 모두 비활성 상태로 만들어, 다음 init 호출 시 강제로 재초기화되도록 한다.
* 주로 시스템 리셋이나 에러 복구 시 사용.
*/
void i2c_reset_state(void)
{
HW_I2C_FRQ = false;
SW_I2C_FRQ = false;
HW_I2C_FRQ = false; /* HW 모드 플래그 초기화 */
SW_I2C_FRQ = false; /* SW 모드 플래그 초기화 */
DBG_PRINTF("Flags reset\r\n");
}
@@ -1,6 +1,16 @@
/*******************************************************************************
* @file i2c_manager.h
* @brief Common header for HW/SW I2C mutex control
*******************************************************************************
*
* [헤더 개요]
* I2C 버스 HW/SW 모드 전환 관리자의 공용 인터페이스 헤더.
*
* - HW_I2C_FRQ: HW TWI 모드 활성 여부 (true = HW I2C 사용 중)
* - SW_I2C_FRQ: SW 비트뱅 모드 활성 여부 (true = SW I2C 사용 중)
*
* 두 플래그는 상호 배제적으로 동작하며, 동시에 true가 되지 않도록 관리된다.
*
******************************************************************************/
#ifndef __I2C_MANAGER_H__
#define __I2C_MANAGER_H__
@@ -8,11 +18,33 @@
#include <stdbool.h>
#include "app_error.h"
extern bool HW_I2C_FRQ;
extern bool SW_I2C_FRQ;
/* I2C 모드 상태 플래그 (외부 참조용) */
extern bool HW_I2C_FRQ; /* HW TWI 모드 활성 여부 */
extern bool SW_I2C_FRQ; /* SW 비트뱅 모드 활성 여부 */
/**
* @brief HW I2C(TWI) 모드 초기화 (중복 초기화 방지)
*
* SW 모드가 활성화되어 있으면 해제 후 HW로 전환한다.
* 이미 HW 모드이면 아무 동작 없이 리턴한다.
* ICM42670P IMU 센서 통신 전에 호출하여 HW I2C를 준비한다.
*/
void hw_i2c_init_once(void);
/**
* @brief SW I2C(비트뱅) 모드 초기화 (레거시, 현재 미사용)
*
* HW 모드가 활성화되어 있으면 TWI를 해제한 후 SW로 전환한다.
* 이미 SW 모드이면 아무 동작 없이 리턴한다.
*/
void sw_i2c_init_once(void);
/**
* @brief I2C 모드 플래그 전체 초기화
*
* HW_I2C_FRQ, SW_I2C_FRQ를 모두 false로 리셋한다.
* 다음 init 호출 시 강제로 재초기화가 수행된다.
*/
void i2c_reset_state(void);
#endif
@@ -3,7 +3,20 @@
* @author CandyPops Co.
* @version V1.0.0
* @date 2022-09-05
* @brief
* @brief
******************************************************************************/
/*******************************************************************************
* [헤더 개요] ICM42670P IMU 드라이버 상위 레이어 선언
*
* IMU 센서의 초기화, 설정, 데이터 읽기를 위한 함수 프로토타입과
* 동작 모드 설정 매크로를 정의한다.
*
* 주요 설정 매크로:
* SERIF_TYPE - 통신 인터페이스 (UI_I2C)
* USE_LOW_NOISE_MODE - 1:저잡음(800Hz), 0:저전력(100Hz)
* USE_HIGH_RES_MODE - 1:20비트 고해상도, 0:16비트 표준
* USE_FIFO - 1:FIFO 사용, 0:레지스터 직접 읽기
******************************************************************************/
#ifndef _APP_RAW_H_
@@ -16,64 +29,71 @@
#include "inv_imu_driver.h"
/*** Example configuration ***/
/*** 설정 매크로 ***/
/*
* Select communication link between SmartMotion and IMU
/*
* MCU와 IMU 간 통신 인터페이스 선택
* UI_I2C: I2C 통신 사용 (기본)
*/
#define SERIF_TYPE UI_I2C
/*
* Set power mode flag
* Set this flag to run example in low-noise mode.
* Reset this flag to run example in low-power mode.
* Note: low-noise mode is not available with sensor data frequencies less than 12.5Hz.
* 전원 모드 설정
* 1: 저잡음 모드 — 800Hz ODR, 높은 정밀도, 높은 전력 소모
* 0: 저전력 모드 — 100Hz ODR, 낮은 전력 소모
* 주의: 12.5Hz 미만 ODR에서는 저잡음 모드 사용 불가
*/
#define USE_LOW_NOISE_MODE 1
/*
* Select Fifo resolution Mode (default is low resolution mode)
* Low resolution mode: 16 bits data format
* High resolution mode: 20 bits data format
* Warning: Enabling High Res mode will force FSR to 16g and 2000dps
* FIFO 해상도 모드 선택
* 0: 저해상도 — 16비트 데이터 (기본)
* 1: 고해상도 — 20비트 데이터 (FSR이 16g/2000dps로 강제 고정됨)
*/
#define USE_HIGH_RES_MODE 0
/*
* Select to use FIFO or to read data from registers
* 데이터 읽기 방식 선택
* 0: 레지스터 직접 읽기 (현재 사용 중)
* 1: FIFO에서 읽기
*/
#define USE_FIFO 0
/**
* \brief This function is in charge of reseting and initializing IMU device. It should
* be successfully executed before any access to IMU device.
* \brief IMU 디바이스를 리셋하고 초기화한다. WHOAMI 확인 포함.
* 다른 IMU 접근 함수 호출 전에 반드시 성공적으로 실행되어야 한다.
*
* \return 0 on success, negative value on error.
* \return 0=성공, 음수=에러
*/
int setup_imu_device(struct inv_imu_serif *icm_serif);
/**
* \brief This function configures the device in order to output gyro and accelerometer.
* \return 0 on success, negative value on error.
* \brief 자이로 및 가속도계 출력을 위한 디바이스 설정을 수행한다.
* FSR, ODR, 전원 모드, FIFO 설정 등을 적용한다.
* \return 0=성공, 음수=에러
*/
int configure_imu_device(void);
/**
* \brief This function extracts data from the IMU FIFO.
* \return 0 on success, negative value on error.
* \brief FIFO 또는 레지스터에서 IMU 데이터를 추출한다.
* 내부적으로 imu_callback()이 호출되어 데이터를 처리한다.
* \return 0=성공, 음수=에러
*/
int get_imu_data(void);
/**
* \brief This function is the custom handling packet function.
* \param[in] event structure containing sensor data from one packet
* \brief 센서 데이터 수신 콜백. 마운팅 매트릭스 적용 후
* info4/BLE/UART 모드에 따라 데이터를 출력한다.
* \param[in] event 하나의 센서 데이터 패킷을 담은 구조체
*/
void imu_callback(inv_imu_sensor_event_t *event);
/**
* \brief Direct IMU register read — bypasses DRDY, sends rsp: via BLE.
* \return 0 on success, negative value on error.
* \brief 드라이버 API를 우회한 직접 I2C 레지스터 읽기.
* DRDY 인터럽트 없이 즉시 센서 데이터를 읽어 BLE로 전송한다.
* 읽기 후 IMU를 슬립 모드로 전환하여 전력을 절감한다.
* \return 0=성공, 음수=에러
*/
int imu_read_direct(void);
@@ -3,16 +3,29 @@
* @author CandyPops Co.
* @version V1.0.0
* @date 2022-09-05
* @brief
* @brief
******************************************************************************/
/*******************************************************************************
* [헤더 개요] ICM42670P 메인 초기화/폴링 루프 선언
*
* ICM42670P IMU 센서의 전체 초기화 및 메인 루프 함수를 선언한다.
* - icm42670_init() : 전체 초기화 (MCU설정 → IMU초기화 → 센서설정 → 인터럽트 활성화)
* - icm42670_main() : 메인 폴링 루프 (INT1 인터럽트 확인 → 데이터 읽기)
* - icm42670_uninit() : 해제 (프로토타입만 선언, 구현은 별도)
******************************************************************************/
#ifndef _APP_RAW_MAIN_H_
#define _APP_RAW_MAIN_H_
#include "sdk_config.h"
/* ICM42670P 전체 초기화 — MCU I2C 설정 → IMU 드라이버 초기화 → 센서 설정 → 인터럽트 활성화 */
int icm42670_init(void);
/* ICM42670P 메인 폴링 루프 — INT1 인터럽트 플래그 확인 후 센서 데이터 읽기 */
void icm42670_main(void);
/* ICM42670P 해제 (프로토타입 선언) */
int icm42670_uninit(void);
#endif /* !_APP_RAW_MAIN_H_ */
@@ -3,7 +3,29 @@
* @author CandyPops Co.
* @version V1.0.0
* @date 2022-09-05
* @brief
* @brief
******************************************************************************/
/*******************************************************************************
* [모듈 개요] ICM42670P IMU 센서 I2C 통신 인터페이스
*
* nRF52840의 TWI(I2C) 하드웨어를 사용하여 ICM42670P IMU 센서와 통신하는
* 저수준 인터페이스 모듈이다.
*
* - I2C 슬레이브 주소: 0x68 (ICM42670P 기본 주소)
* - I2C 핀 설정: SCL=P1.14, SDA=P1.15 (system_interface.h에서 정의)
* - TWI 인스턴스: NRFX_TWI_INSTANCE(0) 사용
* - 통신 속도: 100kHz (NRF_TWI_FREQ_100K)
*
* 주요 함수 흐름:
* inv_io_hal_init() → I2C 또는 SPI 초기화 (현재 I2C만 구현)
* inv_io_hal_read_reg() → 레지스터 읽기 (TX로 주소 전송 → RX로 데이터 수신)
* inv_io_hal_write_reg() → 레지스터 쓰기 (주소+데이터를 한번에 TX)
*
* 에러 처리: 모든 I2C 읽기/쓰기에서 실패 시 1회 재시도 후 에러 출력
*
* 참고: SPI4 인터페이스 코드 경로도 존재하지만 현재 미구현 상태이며,
* 실제 운용에서는 I2C(UI_I2C)만 사용한다.
******************************************************************************/
/* board driver */
@@ -21,18 +43,31 @@
#include "system_interface.h"
#include "nrf_delay.h"
/* I2C number and slave address for INV device */
/* ICM42670P I2C 슬레이브 주소 및 직렬 쓰기 최대 바이트 수 */
#define ICM_I2C_ADDR 0x68
#define INV_MAX_SERIAL_WRITE 16
/* TWI instance. */
/* TWI(I2C) 인스턴스 생성 — system_interface.h의 ICM42670_I2C_INSTANCE(0)을 사용 */
const nrfx_twi_t m_twi_icm42670 = NRFX_TWI_INSTANCE(ICM42670_I2C_INSTANCE);
/*
* inv_i2c_master_uninitialize()
* I2C 버스를 비활성화하고 TWI 인스턴스를 해제한다.
* 슬립 모드 진입 전 또는 재초기화 전에 호출된다.
*/
void inv_i2c_master_uninitialize(void){
nrfx_twi_disable(&m_twi_icm42670);
nrfx_twi_uninit(&m_twi_icm42670);
}
/*
* inv_i2c_master_initialize()
* nRF52840 TWI 하드웨어를 초기화하고 활성화한다.
* - SCL: P1.14, SDA: P1.15
* - 속도: 100kHz
* - 인터럽트 우선순위: 최고 (APP_IRQ_PRIORITY_HIGH)
* - 이벤트 핸들러 없음 (블로킹 모드로 동작)
*/
void inv_i2c_master_initialize(void){
ret_code_t err_code;
@@ -43,50 +78,75 @@ void inv_i2c_master_initialize(void){
.interrupt_priority = APP_IRQ_PRIORITY_HIGH,
};
/* TWI 드라이버 초기화 (이벤트 핸들러=NULL → 블로킹 모드) */
err_code = nrfx_twi_init(&m_twi_icm42670, &twi_icm42670_config, NULL, NULL);
APP_ERROR_CHECK(err_code);
/* TWI 하드웨어 활성화 — 이후 tx/rx 가능 */
nrfx_twi_enable(&m_twi_icm42670);
}
/*
* icm42670_twi_tx()
* I2C 전송 래퍼 함수. nrfx_twi_tx를 호출하여 데이터를 송신한다.
* no_stop=true이면 STOP 컨디션을 보내지 않음 (Repeated START를 위해 사용)
*/
uint32_t icm42670_twi_tx( uint8_t device_id,
uint8_t const * p_data,
uint8_t length,
bool no_stop)
{
ret_code_t ret;
ret = nrfx_twi_tx(&m_twi_icm42670, device_id, p_data, length, no_stop);
ret_code_t ret;
ret = nrfx_twi_tx(&m_twi_icm42670, device_id, p_data, length, no_stop);
return ret;
}
/*
* icm42670_twi_rx()
* I2C 수신 래퍼 함수. nrfx_twi_rx를 호출하여 데이터를 수신한다.
*/
uint32_t icm42670_twi_rx( uint8_t device_id,
uint8_t * p_data,
uint8_t length)
{
ret_code_t ret;
ret_code_t ret;
ret = nrfx_twi_rx(&m_twi_icm42670, device_id, p_data, length);
return ret;
}
/*
* inv_i2c_master_read_register()
* ICM42670P의 특정 레지스터에서 데이터를 읽는다.
*
* 동작 순서:
* 1) TX: 레지스터 주소 1바이트 전송 (no_stop=true → Repeated START 준비)
* 2) RX: 지정된 길이만큼 데이터 수신
*
* 에러 처리: TX, RX 각각 실패 시 1회 재시도한다.
*/
static unsigned long inv_i2c_master_read_register(unsigned char Address, unsigned char RegisterAddr, unsigned short RegisterLen, unsigned char *RegisterValue){
//ret_code_t ret;
uint32_t ret;
uint8_t addr8 = (uint8_t)RegisterAddr;
/* 1단계: 읽을 레지스터 주소를 전송 (STOP 없이 → Repeated START 사용) */
ret = icm42670_twi_tx(Address, &addr8, 1, true);
if(ret != NRF_SUCCESS) {
/* 실패 시 1회 재시도 */
ret = icm42670_twi_tx(Address, &addr8, 1, true);
if(ret != NRF_SUCCESS) {
printf("ERR! i2c read-1\r\n");
}
}
ret = icm42670_twi_rx(Address, RegisterValue, RegisterLen);
/* 2단계: 해당 레지스터에서 데이터 수신 */
ret = icm42670_twi_rx(Address, RegisterValue, RegisterLen);
if(ret != NRF_SUCCESS) {
ret = icm42670_twi_rx(Address, RegisterValue, RegisterLen);
/* 실패 시 1회 재시도 */
ret = icm42670_twi_rx(Address, RegisterValue, RegisterLen);
if(ret != NRF_SUCCESS) {
printf("ERR! i2c read-2\r\n");
}
@@ -95,14 +155,28 @@ static unsigned long inv_i2c_master_read_register(unsigned char Address, unsigne
return ret;
}
/*
* inv_i2c_master_write_register()
* ICM42670P의 특정 레지스터에 데이터를 쓴다.
*
* 동작 순서:
* 1) 버퍼[0]에 레지스터 주소, 버퍼[1~N]에 쓸 데이터를 배치
* 2) TX: 주소+데이터를 한번에 전송 (no_stop=false → STOP 컨디션 포함)
*
* 에러 처리: 실패 시 1회 재시도한다.
*/
static unsigned long inv_i2c_master_write_register(unsigned char Address, unsigned char RegisterAddr, unsigned short RegisterLen, const unsigned char *RegisterValue){
uint32_t ret;
uint8_t buffer[1 + INV_MAX_SERIAL_WRITE]; /* Addr + data */
uint8_t buffer[1 + INV_MAX_SERIAL_WRITE]; /* 레지스터 주소(1) + 데이터(최대 16바이트) */
/* 버퍼 구성: [레지스터 주소][데이터 바이트들] */
buffer[0] = (uint8_t)RegisterAddr;
memcpy(buffer+1, RegisterValue, RegisterLen);
/* 주소+데이터를 한번에 전송 */
ret = icm42670_twi_tx(Address, buffer, RegisterLen+1, false);
if(ret != NRF_SUCCESS) {
/* 실패 시 1회 재시도 */
ret = icm42670_twi_tx(Address, buffer, RegisterLen+1, false);
if(ret != NRF_SUCCESS) {
printf("ERR! i2c write\r\n");
@@ -114,7 +188,12 @@ static unsigned long inv_i2c_master_write_register(unsigned char Address, unsign
/*
* inv_io_hal_init()
* IMU 드라이버가 사용하는 시리얼 인터페이스(I2C 또는 SPI)를 초기화한다.
* serif->serif_type에 따라 분기하며, 현재는 I2C만 구현되어 있다.
* 반환값: 0=성공, -1=지원하지 않는 인터페이스 타입
*/
int inv_io_hal_init(struct inv_imu_serif *serif)
{
@@ -122,13 +201,14 @@ int inv_io_hal_init(struct inv_imu_serif *serif)
switch (serif->serif_type) {
case UI_SPI4:
{
/* SPI4 초기화 — 현재 미구현 (I2C만 사용) */
break;
}
case UI_I2C:
inv_i2c_master_initialize();
break;
default:
return -1;
}
@@ -137,12 +217,17 @@ int inv_io_hal_init(struct inv_imu_serif *serif)
}
/*
* inv_io_hal_read_reg()
* IMU 드라이버 콜백: 지정된 레지스터에서 데이터를 읽는다.
* 시리얼 타입에 따라 I2C 또는 SPI 읽기를 수행한다.
*/
int inv_io_hal_read_reg(struct inv_imu_serif *serif, uint8_t reg, uint8_t * rbuffer, uint32_t rlen)
{
switch (serif->serif_type) {
case UI_SPI4:
return 0;
case UI_I2C:
return inv_i2c_master_read_register(ICM_I2C_ADDR, reg, rlen, rbuffer);
@@ -151,12 +236,17 @@ int inv_io_hal_read_reg(struct inv_imu_serif *serif, uint8_t reg, uint8_t * rbuf
}
}
/*
* inv_io_hal_write_reg()
* IMU 드라이버 콜백: 지정된 레지스터에 데이터를 쓴다.
* 시리얼 타입에 따라 I2C 또는 SPI 쓰기를 수행한다.
*/
int inv_io_hal_write_reg(struct inv_imu_serif *serif, uint8_t reg, const uint8_t * wbuffer, uint32_t wlen)
{
switch (serif->serif_type) {
case UI_SPI4:
return 0;
case UI_I2C:
return inv_i2c_master_write_register(ICM_I2C_ADDR, reg, wlen, wbuffer);
@@ -164,20 +254,30 @@ int inv_io_hal_write_reg(struct inv_imu_serif *serif, uint8_t reg, const uint8_t
return -1;
}
}
/*
* cat_read()
* 범용 I2C 읽기 함수 (디버그/레거시용).
* 8바이트를 읽어 첫 번째 바이트를 반환하고, 읽은 데이터를 콘솔에 출력한다.
* 참고: 현재 실제 운용에서는 사용되지 않으며, 디버그 목적으로 남아 있다.
*/
uint8_t cat_read(uint8_t device_id, uint8_t address, uint8_t *data)
{
uint8_t read_data = 0;
char adata[8];
ret_code_t err_code;
//address = 1|(address<<1);
address = (address & 0xFF);
/* 레지스터 주소 전송 (STOP 없이, Repeated START 준비) */
err_code = nrfx_twi_tx(&m_twi_icm42670, device_id, &address, 1, true);
if (err_code != NRF_SUCCESS) {
// Handle error
// return;
}
/* 8바이트 데이터 수신 */
err_code = nrfx_twi_rx(&m_twi_icm42670, device_id, data, 8);
if (err_code != NRF_SUCCESS) {
// Handle error
@@ -187,41 +287,48 @@ uint8_t cat_read(uint8_t device_id, uint8_t address, uint8_t *data)
memcpy(adata,data,8);
printf("Data %s . \r\n", adata);
return read_data;
}
/*
* cat_write()
* 범용 I2C 쓰기 함수 (디버그/레거시용).
* 주소 1바이트 + 데이터 1바이트를 전송한다.
* 참고: buffer에 6바이트를 복사하지만, 실제 전송은 2바이트만 수행한다.
*/
void cat_write(uint8_t device_id, uint8_t address, uint8_t *data){
uint8_t buffer[7]={0x00,0x00,0x00,0x00,0x00,0x00,0x00};
address = (address & 0xFF);
buffer[0] = (address);
//buffer[1] =(data & 0xFF);
memcpy(buffer+1,data,6);
ret_code_t err_code;
//err_code = nrf_drv_twi_tx(&m_twi_ir, device_id, 0x00, 1, false);
/* 주소(1바이트) + 데이터(1바이트) = 2바이트 전송 */
err_code = nrfx_twi_tx(&m_twi_icm42670, device_id, buffer, 2, false);
// err_code = nrf_drv_twi_tx(&m_twi_ir, device_id, buffer, 2, false);
// nrfx_twi_rx(&m_twi_icm42670, device_id, p_data, length);
// nrfx_twi_tx(&m_twi_icm42670, device_id, p_data, length, no_stop);
// nrfx_twi_tx(&m_twi_icm42670, device_id, p_data, length, no_stop);
printf("Data %x %x %x %x. \r\n", buffer[0], buffer[1], buffer[2], buffer[3]);
//err_code = nrf_drv_twi_tx(&m_twi_ir, device_id, buffer, 6, false);
if (err_code != NRF_SUCCESS) {
printf("TWI Error.");
}
}
@@ -3,7 +3,22 @@
* @author CandyPops Co.
* @version V1.0.0
* @date 2022-09-05
* @brief
* @brief
******************************************************************************/
/*******************************************************************************
* [헤더 개요] ICM42670P I2C 통신 인터페이스 선언
*
* nRF52840 TWI 하드웨어를 통해 ICM42670P IMU 센서와 통신하기 위한
* 핀 정의, 함수 프로토타입을 선언한다.
*
* 핀 배치:
* - I2C SCL : P1.14
* - I2C SDA : P1.15
* - INT1 : P1.13 (데이터 준비 인터럽트)
* - INT2 : P0.26 (보조 인터럽트, 현재 미사용)
*
* TWI 인스턴스: 0번 사용
******************************************************************************/
#ifndef _SYSTEM_INTERFACE_H_
@@ -17,25 +32,42 @@
#endif
#define ICM42670_I2C_INSTANCE 0 /**< I2C instance index. */
#define ICM42670_I2C_SDA_PIN NRF_GPIO_PIN_MAP(1,15)
#define ICM42670_I2C_SCL_PIN NRF_GPIO_PIN_MAP(1,14)
#define ICM42670_INT1_PIN NRF_GPIO_PIN_MAP(1,13)
#define ICM42670_INT2_PIN NRF_GPIO_PIN_MAP(0,26)
#define ICM42670_I2C_INSTANCE 0 /**< I2C(TWI) 인스턴스 인덱스 */
#define ICM42670_I2C_SDA_PIN NRF_GPIO_PIN_MAP(1,15) /**< SDA 핀: P1.15 */
#define ICM42670_I2C_SCL_PIN NRF_GPIO_PIN_MAP(1,14) /**< SCL 핀: P1.14 */
#define ICM42670_INT1_PIN NRF_GPIO_PIN_MAP(1,13) /**< INT1 핀: P1.13 (데이터 준비 인터럽트) */
#define ICM42670_INT2_PIN NRF_GPIO_PIN_MAP(0,26) /**< INT2 핀: P0.26 (보조, 현재 미사용) */
/* I2C 전송 래퍼 — no_stop=true이면 Repeated START를 위해 STOP 컨디션 생략 */
uint32_t icm42670_twi_tx( uint8_t device_id,
uint8_t const * p_data,
uint8_t length,
bool no_stop);
/* I2C 수신 래퍼 */
uint32_t icm42670_twi_rx( uint8_t device_id,
uint8_t * p_data,
uint8_t length);
/* 범용 I2C 읽기 (디버그/레거시용) — 8바이트를 읽어 첫 바이트 반환 */
uint8_t cat_read (uint8_t device_id, uint8_t address, uint8_t *data);
void cat_write (uint8_t device_id, uint8_t address, uint8_t *data);
/* 범용 I2C 쓰기 (디버그/레거시용) — 주소+데이터 2바이트 전송 */
void cat_write (uint8_t device_id, uint8_t address, uint8_t *data);
/* I2C 하드웨어 해제 (슬립 또는 재초기화 전 호출) */
void inv_i2c_master_uninitialize(void);
/* I2C 하드웨어 초기화 (100kHz, 블로킹 모드) */
void inv_i2c_master_initialize(void);
/* IMU 드라이버용 시리얼 인터페이스 초기화 (I2C/SPI 분기) */
int inv_io_hal_init(struct inv_imu_serif *serif);
/* IMU 드라이버 콜백: 레지스터 읽기 */
int inv_io_hal_read_reg(struct inv_imu_serif *serif, uint8_t reg, uint8_t * rbuffer, uint32_t rlen);
/* IMU 드라이버 콜백: 레지스터 쓰기 */
int inv_io_hal_write_reg(struct inv_imu_serif *serif, uint8_t reg, const uint8_t * wbuffer, uint32_t wlen);
#endif /* !_SYSTEM_INTERFACE_H_ */
@@ -608,11 +608,15 @@ static void nrf_qwr_error_handler(uint32_t nrf_error)
extern bool maa_async_on_tx_ready(void);
extern bool maa_async_is_busy(void);
/* 2026-03-17: BLE 콜백에서 블로킹 방지 — 명령을 버퍼에 저장 후 메인 루프에서 처리 */
static volatile uint8_t pending_cmd_buf[BLE_NUS_MAX_DATA_LEN];
static volatile uint8_t pending_cmd_len = 0;
/**
* @brief NUS(Nordic UART Service) 데이터 수신 핸들러
*
* BLE를 통해 스마트폰 앱에서 데이터를 수신했을 때 호출됨
* - RX_DATA 이벤트: 수신된 명령을 received_command_process()로 전달
* - RX_DATA 이벤트: 수신 데이터를 버퍼에 복사 (메인 루프에서 dr_cmd_parser 호출)
* - TX_RDY 이벤트: BLE TX 버퍼에 공간이 생겼을 때 비동기 MAA 전송 계속
*/
static void nus_data_handler(ble_nus_evt_t * p_evt)
@@ -622,7 +626,12 @@ static void nus_data_handler(ble_nus_evt_t * p_evt)
cmd_type_t = CMD_BLE;
ble_got_new_data = true;
DBG_PRINTF("[NUS] RX len=%d\r\n", p_evt->params.rx_data.length);
dr_cmd_parser(p_evt->params.rx_data.p_data, p_evt->params.rx_data.length);
/* 콜백에서는 복사만 하고, 메인 루프에서 처리 */
if (p_evt->params.rx_data.length <= BLE_NUS_MAX_DATA_LEN) {
memcpy((void *)pending_cmd_buf, p_evt->params.rx_data.p_data, p_evt->params.rx_data.length);
pending_cmd_len = p_evt->params.rx_data.length;
}
}
else if (p_evt->type == BLE_NUS_EVT_TX_RDY)
{
@@ -1771,6 +1780,13 @@ int main(void)
// MAIN LOOP
for (;;)
{
/* BLE 콜백에서 수신된 명령이 있으면 여기서 처리 (블로킹 방지) */
if (pending_cmd_len > 0) {
uint8_t len = pending_cmd_len;
pending_cmd_len = 0;
dr_cmd_parser((const uint8_t *)pending_cmd_buf, len);
}
idle_state_handle();
}
}
@@ -3,15 +3,35 @@
* @author CandyPops Co.
* @version V1.0.0
* @date 2022-09-05
* @brief
* @brief
*******************************************************************************
*
* [헤더 개요]
* 메인 이벤트 루프 타이머의 공용 인터페이스 헤더.
*
* 10ms(일반) 또는 80ms(디테일) 간격의 싱글샷 타이머를 사용하여
* main_loop() 콜백에서 센서 데이터 수집 및 시스템 제어를 수행한다.
*
* [주요 함수]
* main_timer_start() : 타이머 시작 (싱글샷, 수동 재시작 필요)
* main_timer_stop() : 타이머 정지
* main_timer_init() : 타이머 초기화 (앱 시작 시 1회 호출)
*
******************************************************************************/
#ifndef TIMER_ROUTINE_H__
#define TIMER_ROUTINE_H__
/** @brief 메인 루프 타이머 시작 (싱글샷, MAIN_LOOP_INTERVAL 후 main_loop 호출) */
void main_timer_start(void);
/** @brief 지정된 간격(ms)으로 메인 루프 타이머 시작 */
void main_timer_start_ms(uint32_t interval_ms);
/** @brief 메인 루프 타이머 정지 */
void main_timer_stop(void);
/** @brief 메인 루프 타이머 초기화 (앱 시작 시 1회, 싱글샷 모드로 생성) */
void main_timer_init(void);
#endif //TIMER_ROUTINE_H__
@@ -418,16 +418,6 @@
<FileType>1</FileType>
<FilePath>..\..\..\fstorage.c</FilePath>
</File>
<File>
<FileName>cmd_parse.c</FileName>
<FileType>1</FileType>
<FilePath>..\..\..\cmd_parse.c</FilePath>
</File>
<File>
<FileName>cmd_parse.h</FileName>
<FileType>5</FileType>
<FilePath>..\..\..\cmd_parse.h</FilePath>
</File>
<File>
<FileName>debug_print.h</FileName>
<FileType>5</FileType>
@@ -4631,16 +4621,6 @@
<FileType>1</FileType>
<FilePath>..\..\..\fstorage.c</FilePath>
</File>
<File>
<FileName>cmd_parse.c</FileName>
<FileType>1</FileType>
<FilePath>..\..\..\cmd_parse.c</FilePath>
</File>
<File>
<FileName>cmd_parse.h</FileName>
<FileType>5</FileType>
<FilePath>..\..\..\cmd_parse.h</FilePath>
</File>
<File>
<FileName>debug_print.h</FileName>
<FileType>5</FileType>
@@ -11473,7 +11473,7 @@
// <i> or this value is actually used. It depends on which one is bigger.
#ifndef SEGGER_RTT_CONFIG_BUFFER_SIZE_UP
#define SEGGER_RTT_CONFIG_BUFFER_SIZE_UP 512
#define SEGGER_RTT_CONFIG_BUFFER_SIZE_UP 1024 // 512 -> 1024 jhChun 26.03.17
#endif
// <o> SEGGER_RTT_CONFIG_MAX_NUM_UP_BUFFERS - Maximum number of upstream buffers.
@@ -1,6 +1,33 @@
/*******************************************************************************
TEST medi50 Dec 23
//=========power_control.c====================
*******************************************************************************
*
* [ ]
* 퀀 ( / / ).
*
* [ ]
* device_activated()
* p_order=0 , power_loop
* power_loop() 20ms Step 012
* - Step 0: I2C (sw_i2c_init_once)
* - Step 1: ( )
* - Step 2: 퀀
*
* [ ]
* device_sleep_mode()
* EEPROM OFF processing
*
* []
* device_reactivated()
* I2C 퀀
*
* [EEPROM ]
* EEP_WP = P0.24 (, FDS를 )
*
* []
* app_timer, 20ms power_loop
*
******************************************************************************/
#include <stdbool.h>
#include <stdint.h>
@@ -15,50 +42,89 @@
#include "app_timer.h"
#include "debug_print.h"
#include "i2c_manager.h"
/* EEPROM 쓰기 보호 핀 (P0.24) - 레거시, 현재는 FDS 사용 */
#define EEP_WP NRF_GPIO_PIN_MAP(0, 24)
/* 전원 시퀀스용 싱글샷 타이머 인스턴스 */
APP_TIMER_DEF(m_power_timer_id);
/* 전원 시퀀스 상태머신의 타이머 간격 (20ms) */
// 2025-12-08 change to #define POWER_LOOP_INTERVAL 30 -> 20
#define POWER_LOOP_INTERVAL 20
/* 전원 시퀀스 현재 단계 (0: I2C 초기화, 1: 예약, 2: 완료) */
static uint8_t p_order;
/* 데이터 처리 중 플래그 (외부 모듈에서 선언) */
extern volatile bool processing;
/* 전원 시퀀스 잠금 플래그 (true = 전원 시퀀스 진행 중) */
bool lock_check = false;
/**
* @brief GPIO
*
* EEP_WP .
*/
void power_gpio_init(void)
{
nrf_gpio_cfg_output(EEP_WP);
}
/**
* @brief EEPROM (ON/OFF)
*
* @param eeprom_st ON: EEP_WP HIGH ( ), OFF: LOW ( )
* , FDS(Flash Data Storage) .
*/
void eeprom_control(on_off_cont_t eeprom_st)
{
if(eeprom_st == OFF) {
nrf_gpio_pin_clear(EEP_WP);
nrf_gpio_pin_clear(EEP_WP); /* LOW: EEPROM 쓰기 보호 활성화 */
}else if(eeprom_st == ON){
nrf_gpio_pin_set(EEP_WP);
nrf_gpio_pin_set(EEP_WP); /* HIGH: EEPROM 쓰기 허용 */
}
}
/**
* @brief
*
* EEPROM을 OFF하고, (processing)
* .
*
* @return 0 ( )
*/
int device_sleep_mode(void){
int rc = 0;
eeprom_control(OFF);
eeprom_control(OFF); /* EEPROM 쓰기 보호 활성화 */
nrf_delay_ms(2);
DBG_PRINTF("Device_Sleep_Mode OK!\r\n");
nrf_delay_ms(10);
processing = false;
processing = false; /* 데이터 처리 플래그 해제 → 센서 처리 중단 */
return rc;
}
/**
* @brief ( 퀀 )
*
* 퀀 0 ,
* power_loop() .
* EEPROM은 OFF .
*
* @return 0 ( )
*/
int device_activated(void){
int rc = 0;
p_order = 0;
lock_check =true;
power_timer_start();
eeprom_control(OFF);
p_order = 0; /* 상태머신 시작 단계 (Step 0: I2C 초기화) */
lock_check =true; /* 전원 시퀀스 진행 중 잠금 */
power_timer_start(); /* 20ms 후 power_loop() 첫 호출 */
eeprom_control(OFF); /* EEPROM OFF 상태로 시작 */
return rc;
}
@@ -74,32 +140,49 @@ int device_activated(void){
* 1: (reserved)
* 2: Complete
*/
/*
* 퀀 (20ms ).
*
* [ ]
* 1) ()
* 2) p_order에
* 3) p_order < 2
* 4) p_order == 2 퀀
*
* [DEBUG_MINIMAL_BOOT ]
* (Step 2)
*/
void power_loop(void *p_context)
{
UNUSED_PARAMETER(p_context);
power_timer_stop();
power_timer_stop(); /* 현재 타이머 정지 (싱글샷 → 수동 재시작 필요) */
#if DEBUG_MINIMAL_BOOT
/* Minimal Boot: Skip sensor initialization */
/* 미니멀 부트 모드: 센서 초기화 생략하고 바로 완료 */
DBG_PRINTF("[PWR] Minimal mode - skipping sensor init\r\n");
p_order = 2; // Jump to complete
#else
/* Full Boot: Execute power sequence */
/* 풀 부트 모드: 전원 시퀀스 단계별 실행 */
switch (p_order) {
/* Step 0: I2C Initialize */
/* 단계 0: I2C 버스를 SW 모드로 초기화 (레거시 흐름) */
case 0:
sw_i2c_init_once();
nrf_delay_ms(10);
sw_i2c_init_once(); /* SW I2C 초기화 (TWI 해제 → 비트뱅 모드) */
nrf_delay_ms(10); /* I2C 버스 안정화 대기 */
DBG_PRINTF("[PWR] Step %d: I2C Init\r\n", p_order);
break;
/* Step 1: Reserved */
/* 단계 1: 예약 (추후 센서 초기화 등 추가 가능) */
case 1:
DBG_PRINTF("[PWR] Step %d: (reserved)\r\n", p_order);
break;
/* Step 2: Complete */
/* 단계 2: 전원 시퀀스 완료 → 디바이스 사용 준비 완료 */
case 2:
DBG_PRINTF("[PWR] Step %d: Sequence Complete\r\n", p_order);
break;
@@ -110,31 +193,49 @@ void power_loop(void *p_context)
#endif
/* Advance to next step or finish */
/* 다음 단계로 진행하거나, 시퀀스 완료 처리 */
if (p_order < 2) {
p_order++;
power_timer_start();
p_order++; /* 다음 단계로 이동 */
power_timer_start(); /* 20ms 후 다음 단계 실행 */
} else {
/* 전원 시퀀스 전체 완료 */
DBG_PRINTF("[PWR] Device Activated OK!\r\n");
}
}
/**
* @brief ( )
*
* I2C를 , 퀀 .
* .
*
* @return 0 ( )
*/
int device_reactivated(void){
int rc = 0;
sw_i2c_init_once();
nrf_delay_ms(10);
lock_check = true;
p_order = 0;
power_timer_start();
sw_i2c_init_once(); /* I2C 버스 재초기화 */
nrf_delay_ms(10); /* 안정화 대기 */
lock_check = true; /* 전원 시퀀스 진행 중 잠금 */
p_order = 0; /* 상태머신을 Step 0부터 재시작 */
power_timer_start(); /* 20ms 후 power_loop() 시작 */
return rc;
}
/**
* @brief 퀀
*
* POWER_LOOP_INTERVAL(20ms) power_loop() .
*/
void power_timer_start(void)
{
APP_ERROR_CHECK(app_timer_start(m_power_timer_id, APP_TIMER_TICKS(POWER_LOOP_INTERVAL), NULL));
}
/**
* @brief 퀀
*/
void power_timer_stop(void)
{
APP_ERROR_CHECK(app_timer_stop(m_power_timer_id));
@@ -142,6 +243,12 @@ void power_timer_stop(void)
}
/**
* @brief 퀀 ( 1 )
*
* , power_loop() .
* power_timer_start() .
*/
void power_timer_init(void) //active start
{
APP_ERROR_CHECK(app_timer_create(&m_power_timer_id, APP_TIMER_MODE_SINGLE_SHOT, power_loop));
@@ -3,7 +3,22 @@
* @author CandyPops Co.
* @version V1.0.0
* @date 2022-09-05
* @brief
* @brief
*******************************************************************************
*
* [ ]
* 퀀 .
*
* [ ]
* power_gpio_init() : GPIO (EEP_WP )
* eeprom_control() : EEPROM ON/OFF ()
* device_activated() : power_loop
* device_sleep_mode() : (EEPROM OFF, )
* device_reactivated() : I2C 퀀
* power_loop() : 20ms 퀀
* power_timer_start/stop : 퀀
* power_timer_init() : 퀀 ( 1)
*
******************************************************************************/
#ifndef _POWER_CONTROL_H_
@@ -11,14 +26,31 @@
#include "main.h"
/** @brief 전원 관련 GPIO 핀 초기화 (EEP_WP 출력 설정) */
void power_gpio_init(void);
/** @brief EEPROM 쓰기 보호 제어 (ON=허용, OFF=보호) - 레거시 */
void eeprom_control(on_off_cont_t eeprom_st);
/** @brief 디바이스 슬립 모드 진입 (EEPROM OFF, processing 해제) */
int device_sleep_mode(void);
/** @brief 디바이스 전원 켜기 (전원 시퀀스 상태머신 시작) */
int device_activated(void);
/** @brief 디바이스 재활성화 (슬립 복귀 시 I2C 재초기화 후 시퀀스 재시작) */
int device_reactivated(void);
/** @brief 전원 시퀀스 상태머신 (20ms 타이머 콜백, Step 0→1→2) */
void power_loop(void * p_context); /* For x ms */
/** @brief 전원 시퀀스 타이머 시작 (20ms 싱글샷) */
void power_timer_start(void);
/** @brief 전원 시퀀스 타이머 정지 */
void power_timer_stop(void);;
/** @brief 전원 시퀀스 타이머 초기화 (앱 시작 시 1회 호출) */
void power_timer_init(void);
#endif //_POWER_CONTROL_H_
@@ -6,6 +6,26 @@
* @date 2025-10-13
******************************************************************************/
/*******************************************************************************
* [ ] PWM
*
* === ===
* SKIP_PWM PWM .
* init/start/stop Stub( ) ,
* PWM .
* .
*
* === (SKIP_PWM ) ===
* nrfx_pwm + app_timer를 3(P28/P29/P30) PWM .
* PWM 0, 16MHz , TOP=8, .
* 6 퀀 .
* app_timer(10ms) .
*
* === ===
* dr_piezo
* PWM .
******************************************************************************/
#define SKIP_PWM // ? ?? ???? PWM ?? ???
#include "pulse_gen.h"
@@ -15,57 +35,72 @@
#ifdef SKIP_PWM
/* ========================================================================== */
/* PWM ?? ???? Stub ?? */
/* PWM 비활성화 모드: Stub 구현 (로그만 출력, 실제 동작 없음) */
/* ========================================================================== */
/* Stub 초기화: 아무 동작 없이 성공 반환 */
ret_code_t pulse_gen_init(void)
{
DBG_PRINTF("[PWM] skipped init\r\n");
return NRF_SUCCESS;
}
/* Stub 시작: 아무 동작 없음 */
void pulse_gen_start(void)
{
DBG_PRINTF("[PWM] skipped start\r\n");
}
/* Stub 정지: 아무 동작 없음 */
void pulse_gen_stop(void)
{
DBG_PRINTF("[PWM] skipped stop\r\n");
}
#else /* ==================================================================== */
/* ?? PWM ?? ?? (SKIP_PWM ?? ?? ? ???) */
/* 실제 PWM 구현 코드 (SKIP_PWM 미정의 시에만 컴파일됨) */
/* ========================================================================== */
#include "nrfx_pwm.h"
/* --- Configuration --- */
#define PULSE_PIN_1 28
#define PULSE_PIN_2 29
#define PULSE_PIN_3 30
#define PULSE_DELAY_MS 10
/* --- 설정값 --- */
#define PULSE_PIN_1 28 /* PWM 출력 핀 1 (P0.28) */
#define PULSE_PIN_2 29 /* PWM 출력 핀 2 (P0.29) */
#define PULSE_PIN_3 30 /* PWM 출력 핀 3 (P0.30) */
#define PULSE_DELAY_MS 10 /* 반복 재생 간격 (ms) */
#define PWM_INSTANCE_ID 0
#define PWM_TOP_VALUE 8
#define SEQUENCE_LENGTH 6
#define PWM_INSTANCE_ID 0 /* PWM 하드웨어 인스턴스 번호 */
#define PWM_TOP_VALUE 8 /* PWM 카운터 최대값 (듀티 사이클 분해능) */
#define SEQUENCE_LENGTH 6 /* PWM 시퀀스 단계 수 */
static nrfx_pwm_t m_pwm = NRFX_PWM_INSTANCE(PWM_INSTANCE_ID);
APP_TIMER_DEF(m_pulse_delay_timer_id);
static volatile bool m_pulses_running = false;
static nrfx_pwm_t m_pwm = NRFX_PWM_INSTANCE(PWM_INSTANCE_ID); /* PWM 인스턴스 */
APP_TIMER_DEF(m_pulse_delay_timer_id); /* 반복 재생용 앱 타이머 */
static volatile bool m_pulses_running = false; /* 펄스 생성 동작 중 플래그 */
/* PWM 시퀀스 데이터: 6단계에 걸쳐 3채널의 듀티 사이클을 개별 설정 */
static nrf_pwm_values_individual_t m_pulse_seq_values[SEQUENCE_LENGTH];
static nrf_pwm_sequence_t const m_pulse_sequence =
{
.values.p_individual = m_pulse_seq_values,
.length = NRF_PWM_VALUES_LENGTH(m_pulse_seq_values),
.repeats = 0,
.end_delay = 0
.repeats = 0, /* 각 단계 반복 없음 */
.end_delay = 0 /* 시퀀스 종료 후 추가 지연 없음 */
};
/* -------------------------------------------------------------------------- */
/* Local Handlers */
/* 내부 핸들러 함수 */
/* -------------------------------------------------------------------------- */
/*
* PWM 퀀
* 6 CH0/CH1/CH2의 :
* 0: CH0=100%, CH1=0, CH2=0
* 1: CH0=0, CH1=100%, CH2=0
* 2: CH0=100%, CH1=0, CH2=0
* 3: CH0=0, CH1=100%, CH2=0
* 4: CH0=0, CH1=0, CH2=0 ( OFF)
* 5: CH0=0, CH1=0, CH2=100%
*/
static void prepare_pulse_sequence(void)
{
m_pulse_seq_values[0].channel_0 = PWM_TOP_VALUE; m_pulse_seq_values[0].channel_1 = 0; m_pulse_seq_values[0].channel_2 = 0;
@@ -76,6 +111,7 @@ static void prepare_pulse_sequence(void)
m_pulse_seq_values[5].channel_0 = 0; m_pulse_seq_values[5].channel_1 = 0; m_pulse_seq_values[5].channel_2 = PWM_TOP_VALUE;
}
/* 타이머 타임아웃 핸들러: m_pulses_running이면 PWM 시퀀스 1회 재생 후 타이머 재시작 */
static void pulse_delay_timeout_handler(void * p_context)
{
if (m_pulses_running)
@@ -86,8 +122,14 @@ static void pulse_delay_timeout_handler(void * p_context)
}
/* -------------------------------------------------------------------------- */
/* Public Functions */
/* 공개 함수 */
/* -------------------------------------------------------------------------- */
/*
* PWM : PWM +
* PWM: 16MHz , UP , TOP=8,
* : (SINGLE_SHOT) , 10ms 퀀
*/
ret_code_t pulse_gen_init(void)
{
ret_code_t err_code;
@@ -121,18 +163,20 @@ ret_code_t pulse_gen_init(void)
return err_code;
}
/* 펄스 생성 시작: 플래그 설정 후 타임아웃 핸들러를 직접 호출하여 즉시 첫 재생 */
void pulse_gen_start(void)
{
if (m_pulses_running) return;
if (m_pulses_running) return; /* 이미 동작 중이면 무시 */
m_pulses_running = true;
pulse_delay_timeout_handler(NULL);
pulse_delay_timeout_handler(NULL); /* 첫 번째 재생 즉시 시작 */
}
/* 펄스 생성 정지: 플래그 해제 + 타이머 정지 + PWM 즉시 정지 */
void pulse_gen_stop(void)
{
m_pulses_running = false;
app_timer_stop(m_pulse_delay_timer_id);
nrfx_pwm_stop(&m_pwm, true);
nrfx_pwm_stop(&m_pwm, true); /* true = 즉시 정지 (현재 시퀀스 중단) */
}
#endif /* SKIP_PWM */
@@ -1,3 +1,15 @@
/*******************************************************************************
* [ ] PWM
*
* PWM .
* SKIP_PWM , Stub .
* nrfx_pwm + app_timer로 3(P28/P29/P30) PWM .
*
* : dr_piezo
* PWM .
* .
******************************************************************************/
#ifndef PULSE_GEN_H__
#define PULSE_GEN_H__
@@ -6,20 +18,23 @@
#include "sdk_errors.h"
/**
* @brief Initializes the PWM peripheral and app_timer for pulse generation.
* @brief PWM app_timer를 .
* (SKIP_PWM NRF_SUCCESS )
*
* @return NRF_SUCCESS on successful initialization, otherwise an error code.
*/
ret_code_t pulse_gen_init(void);
/**
* @brief Starts the repeating pulse generation.
* Generates a burst of pulses, waits a few ms, and repeats.
* @brief .
* , ms .
* (SKIP_PWM )
*/
void pulse_gen_start(void);
/**
* @brief Stops the repeating pulse generation.
* @brief .
* (SKIP_PWM )
*/
void pulse_gen_stop(void);
@@ -97,6 +97,7 @@ void tmp235_voltage_handler(nrf_drv_saadc_evt_t const * p_event) /* TMP325 Vout
/* ADC 변환 결과 읽기 */
adc_result = p_event->data.done.p_buffer[0];
DBG_PRINTF("[TMP] adc=%d\r\n", adc_result);
/* SAADC 해제 — 배터리/압력센서 측정과 하드웨어 공유 */
nrf_drv_saadc_uninit();
@@ -3,13 +3,28 @@
* @author CandyPops Co.
* @version V1.0.0
* @date 2022-09-05
* @brief
* @brief
******************************************************************************/
/*******************************************************************************
* [ ] TMP235-Q1
*
* TMP235-Q1의 SAADC(AIN3)
* (°C) API를 .
*
* API:
* - tmp235_init() : SAADC + ( )
* - tmp235_voltage_level_meas() : 1 ( )
*
* : Vout(mV) Ta(°C) = (Vout - 500) / 10.0 (0~100°C )
******************************************************************************/
#ifndef _TMP235_Q1_H_
#define _TMP235_Q1_H_
/** @brief TMP235 SAADC 초기화 및 측정 시작 (AIN3 채널) */
void tmp235_init(void);
/** @brief 온도 1회 측정 외부 호출 함수 (내부적으로 tmp235_init 호출) */
void tmp235_voltage_level_meas(void);
#endif /* !_TMP235_Q1_H_ */
+266 -94
View File
@@ -3,35 +3,88 @@
* @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 -->| |<DMP>|
*
*
* 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
******************************************************************************/
#include "dr_piezo.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"
/*******************************************************************************
* [ ]
*
* === ===
* 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
@@ -39,62 +92,76 @@
#endif
/*==============================================================================
* HARDWARE RESOURCE ALLOCATION
*
* - 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
#define PIEZO_TIMER_IRQ_PRIORITY 6
#define PIEZO_TIMER NRF_TIMER2 /* 사용할 타이머 인스턴스 */
#define PIEZO_TIMER_IRQn TIMER2_IRQn /* 타이머2 인터럽트 번호 */
#define PIEZO_TIMER_IRQ_PRIORITY 6 /* 인터럽트 우선순위 (6 = 중간) */
/* GPIOTE channels */
#define GPIOTE_CH_P_OUT 4
#define GPIOTE_CH_N_OUT 5
/* GPIOTE 채널 할당 - 핀 토글 제어용 */
#define GPIOTE_CH_P_OUT 4 /* P_OUT(양극 출력) 토글용 GPIOTE 채널 */
#define GPIOTE_CH_N_OUT 5 /* N_OUT(음극 출력) 토글용 GPIOTE 채널 */
/* PPI channels */
#define PPI_CH_P_OUT_TOGGLE_0 8
#define PPI_CH_N_OUT_TOGGLE_0 9
#define PPI_CH_P_OUT_TOGGLE_1 10
#define PPI_CH_N_OUT_TOGGLE_1 11
/* 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 토글 */
/*==============================================================================
* TIMING CONSTANTS
*
* Timer2 : 16MHz -> 1 = 62.5ns
* 2MHz : 500ns(8), 250ns(4)
*============================================================================*/
#define TIMER_FREQ_MHZ 16
#define TICK_NS (1000 / TIMER_FREQ_MHZ) /* 62.5ns */
#define TIMER_FREQ_MHZ 16 /* 타이머 클럭 주파수 (MHz) */
#define TICK_NS (1000 / TIMER_FREQ_MHZ) /* 1틱 = 62.5ns */
/* 2MHz: period = 500ns, half = 250ns */
#define PERIOD_TICKS_2MHZ 8 /* 500ns / 62.5ns = 8 */
#define HALF_PERIOD_TICKS 4 /* 250ns / 62.5ns = 4 */
/* 2MHz 기준 타이밍: 주기 = 500ns, 반주기 = 250ns */
#define PERIOD_TICKS_2MHZ 8 /* 전체 주기: 500ns / 62.5ns = 8 */
#define HALF_PERIOD_TICKS 4 /* 반주기: 250ns / 62.5ns = 4 */
/*==============================================================================
* PIEZO OPERATING FREQUENCY CONFIGURATION
*
*============================================================================*/
/*
* Target piezo frequency: 1.8 MHz
* : 2.1 MHz ( )
*
* Timing (hardcoded in dr_piezo_burst_sw for stability):
* 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
* NOP .
* .
*
* To change frequency, modify the NOP counts in dr_piezo_burst_sw():
* 2.0 MHz: First=14, Second=11
* 2.1 MHz: First=13, Second=10
* 1.5 MHz: First=18, Second=15
* 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
#define PIEZO_FREQ_MHZ 2.1f /* 기본 동작 주파수 (MHz) */
/*==============================================================================
* STATIC VARIABLES
*
*============================================================================*/
static volatile bool m_tx_active = false;
static volatile uint8_t m_remaining_cycles = 0;
static uint32_t m_period_ticks = PERIOD_TICKS_2MHZ;
static bool m_power_enabled = false;
static bool m_initialized = false;
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; /* 드라이버 초기화 완료 여부 */
/*==============================================================================
* INTERRUPT HANDLER
* 2
* (CC[2]) .
* 0 :
* 1)
* 2) GPIOTE (P_OUT/N_OUT )
* 3) GPIO를 LOW로
* 4) DMP ( )
* 5) PE = LOW (MOSFET )
*============================================================================*/
void TIMER2_IRQHandler(void)
{
@@ -140,9 +207,12 @@ void TIMER2_IRQHandler(void)
}
/*==============================================================================
* POWER CONTROL FUNCTIONS
*
* DC/DC (±20V) ON/OFF하여 .
* 10ms .
*============================================================================*/
/* 피에조 전원 ON: DC/DC 컨버터 활성화 → ±20V 생성 */
void dr_piezo_power_on(void)
{
nrf_delay_ms(20);
@@ -158,6 +228,7 @@ void dr_piezo_power_on(void)
DBG_PRINTF("[DR_PIEZO] Power ON: +/-20V ready\r\n");
}
/* 피에조 전원 OFF: TX 비활성화 후 DC/DC 컨버터 차단 */
void dr_piezo_power_off(void)
{
dr_piezo_disable();
@@ -169,10 +240,17 @@ void dr_piezo_power_off(void)
DBG_PRINTF("[DR_PIEZO] Power OFF\r\n");
}
/* 피에조 전원 상태 확인 */
bool dr_piezo_is_power_on(void)
{
return m_power_enabled;
}
/*==============================================================================
* PRIVATE FUNCTIONS
* (private)
*============================================================================*/
/* GPIO 초기화: 모든 신호 핀을 출력 모드로 설정하고 LOW(유휴)로 초기화 */
static void dr_piezo_gpio_init(void)
{
nrf_gpio_cfg_output(DR_PIEZO_PIN_PE);
@@ -192,6 +270,7 @@ static void dr_piezo_gpio_init(void)
DBG_PRINTF("[DR_PIEZO] GPIO init done\r\n");
}
/* GPIOTE 초기화: P_OUT/N_OUT을 토글 모드로 설정 (PPI 연결 대상) */
static void dr_piezo_gpiote_init(void)
{
/* P_OUT: Toggle mode, initial LOW */
@@ -215,6 +294,12 @@ static void dr_piezo_gpiote_init(void)
DBG_PRINTF("[DR_PIEZO] GPIOTE init done\r\n");
}
/*
* : 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);
@@ -258,6 +343,12 @@ static void dr_piezo_timer_init(void)
DBG_PRINTF("[DR_PIEZO] Timer init done (period=%d ticks)\r\n", m_period_ticks);
}
/*
* 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)
{
/*
@@ -302,9 +393,10 @@ static void dr_piezo_ppi_init(void)
}
/*==============================================================================
* TX DRIVER FUNCTIONS
* TX
*============================================================================*/
/* TX 드라이버 초기화: GPIO → GPIOTE → Timer → PPI 순서로 설정 */
void dr_piezo_init(void)
{
DBG_PRINTF("[DR_PIEZO] Initializing TX driver...\r\n");
@@ -321,6 +413,7 @@ void dr_piezo_init(void)
DBG_PRINTF("[DR_PIEZO] TX driver ready (2MHz)\r\n");
}
/* TX 드라이버 해제: 타이머 정지, PPI/GPIOTE 비활성화, 모든 핀 LOW */
void dr_piezo_uninit(void)
{
nrf_timer_task_trigger(PIEZO_TIMER, NRF_TIMER_TASK_STOP);
@@ -346,6 +439,13 @@ void dr_piezo_uninit(void)
DBG_PRINTF("[DR_PIEZO] TX driver uninitialized\r\n");
}
/*
* (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)
@@ -409,11 +509,13 @@ void dr_piezo_burst(uint8_t cycles)
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);
@@ -423,6 +525,7 @@ void dr_piezo_enable(void)
DBG_PRINTF("[DR_PIEZO] TX enabled\r\n");
}
/* TX 출력 비활성화: 모든 신호 핀 LOW로 복귀 (유휴 상태) */
void dr_piezo_disable(void)
{
nrf_gpio_pin_clear(DR_PIEZO_PIN_DMP);
@@ -432,11 +535,13 @@ void dr_piezo_disable(void)
DBG_PRINTF("[DR_PIEZO] TX disabled\r\n");
}
/* 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)
@@ -456,9 +561,14 @@ void dr_piezo_set_frequency(uint32_t freq_hz)
}
/*==============================================================================
* MUX CONTROL FUNCTION
* 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 MUX control pins as outputs */
@@ -522,9 +632,18 @@ 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) .
*/
void dr_piezo_select_channel(uint8_t channel)
{
channel = channel & 0x07; /* Mask to 0-7 */
channel = channel & 0x07; /* 0~7 범위로 마스킹 */
switch (channel) {
// EN_A EN_B SEL0 SEL1
@@ -566,6 +685,7 @@ void dr_piezo_select_channel(uint8_t channel)
nrf_delay_us(DR_PIEZO_MUX_SETTLING_US);
}
/* 핀 테스트: 각 신호 핀을 순서대로 HIGH/LOW 토글 (오실로스코프 확인용) */
void dr_piezo_test_pins(void)
{
DBG_PRINTF("[DR_PIEZO] Pin test...\r\n");
@@ -606,9 +726,10 @@ void dr_piezo_test_pins(void)
}
/*==============================================================================
* SYSTEM FUNCTIONS
* ( + TX )
*============================================================================*/
/* 시스템 전체 초기화: 전원 ON → TX 드라이버 초기화 */
void dr_piezo_system_init(void)
{
DBG_PRINTF("[DR_PIEZO] System init...\r\n");
@@ -617,6 +738,7 @@ void dr_piezo_system_init(void)
DBG_PRINTF("[DR_PIEZO] System ready\r\n");
}
/* 시스템 전체 종료: TX 드라이버 해제 → 전원 OFF */
void dr_piezo_system_uninit(void)
{
dr_piezo_uninit();
@@ -624,6 +746,7 @@ void dr_piezo_system_uninit(void)
DBG_PRINTF("[DR_PIEZO] System shutdown\r\n");
}
/* 전원 확인 후 버스트 송신 (블로킹: 송신 완료까지 대기) */
void dr_piezo_transmit(uint8_t cycles)
{
if (!m_power_enabled)
@@ -638,60 +761,74 @@ void dr_piezo_transmit(uint8_t cycles)
}
/*==============================================================================
* SOFTWARE-BASED BURST
*
* 2025-12-11 Charles KWON
*==============================================================================
*
* Timing Diagram:
*
* |<-margin->|<----- pulses ----->|<-- DMP -->|<-margin->|
*
*
* Timer/PPI/GPIOTE CPU에서 GPIO
* . .
*
* === ===
*
* |<-->|<----- ----->|<-- DMP -->|<-->|
*
* PE ___/--------------------------------------------------\___
* P_OUT ___________/-\_/-\_/-\_/-\_/-\____________________\_______
* N_OUT ___________\_/-\_/-\_/-\_/-\_/____________________\_______
* DMP __________________________________/----------\____________
*
* Signal Description:
* - PE (Pulse Enable): Wraps entire sequence with margin before and after
* - P_OUT: Positive output, starts LOW, toggles at 2MHz, opposite phase to N_OUT
* - N_OUT: Negative output, starts LOW, toggles at 2MHz, opposite phase to P_OUT
* - DMP (Dump): Activates after pulses complete, simultaneous with N_OUT falling edge
*
* Timing Specifications:
* - Frequency: 2MHz (500ns period, 250ns half-period)
* - CPU Clock: 64MHz (1 NOP = 15.625ns)
* - Duty Cycle: 50:50
* - DMP Pulse Width: ~500ns
* - PE Margin: ~3 NOPs before pulses and after DMP
*
* Pin Mapping (Port P1):
* - P1.02: N_OUT (Negative output)
* - P1.03: P_OUT (Positive output)
* - P1.05: PE (Pulse Enable)
* - P1.09: DMP (Dump control)
*
* Note: Direct register access (NRF_P1->OUT) is used for simultaneous
* multi-pin control to ensure precise timing and phase alignment.
*
* === ===
* - 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)
/* Bit masks for P1 port pins - derived from dr_piezo.h definitions
*
* WARNING: Never hardcode pin numbers!
* Hardcoding may save a developer's time momentarily,
* but it will also shorten their lifespan
/* P1 포트 핀의 비트 마스크 - dr_piezo.h의 핀 정의에서 자동 생성
*
* : !
* ,
* .
* - Charles KWON
*
* .
* NRF_P1->OUT에 , .
*/
#define P_OUT_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_P_OUT)) /* P1.03 -> P1.07 */
#define N_OUT_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_N_OUT)) /* P1.02 -> P1.06 */
#define PE_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_PE)) /* P1.05 -> P0.25 */
#define DMP_MASK (1UL << PIN_NUM(DR_PIEZO_PIN_DMP)) /* P1.09 -> P1.00 */
#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 방전 제어 */
/* Combined mask for all piezo control signals (excluding channel select) */
/* 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) */
@@ -838,6 +975,13 @@ void dr_piezo_burst_sw(uint8_t cycles)
* 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) */
@@ -960,6 +1104,13 @@ void dr_piezo_burst_sw_18mhz(uint8_t cycles)
* 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) */
@@ -1083,6 +1234,13 @@ void dr_piezo_burst_sw_20mhz(uint8_t cycles)
* 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) */
@@ -1208,6 +1366,13 @@ void dr_piezo_burst_sw_19mhz(uint8_t cycles)
* 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) */
@@ -1327,6 +1492,13 @@ void dr_piezo_burst_sw_22mhz(uint8_t cycles)
* 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) */
+78 -11
View File
@@ -3,20 +3,60 @@
* @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
*
* 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
@@ -25,7 +65,8 @@
#include "nrf_gpio.h"
/*==============================================================================
* POWER CONTROL PINS (DC/DC Converter +/-20V)
* (DC/DC +/-20V)
* DR_PIEZO_PWR_EN: HIGH로 DC/DC +/-20V
*============================================================================*/
//#define DR_PIEZO_PWR_SHDN NRF_GPIO_PIN_MAP(0, 21) /**< SHDN_VPP/VNN (LT3463) */ // P0.21 : PZT_EN_MUXA jhChun 0128
//#define DR_PIEZO_PWR_EN_10V NRF_GPIO_PIN_MAP(0, 22) /**< EN_+10V (MCP1804) */ // P0.22 : NIRS PIN
@@ -33,7 +74,12 @@
#define DR_PIEZO_PWR_EN NRF_GPIO_PIN_MAP(1, 9) /** Power Enable jhChun 0128 */
/*==============================================================================
* TX SIGNAL PINS (MOSFET Driver Control)
* 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(1, 5) /**< Pulse Enable */
//#define DR_PIEZO_PIN_DMP NRF_GPIO_PIN_MAP(1, 9) /**< Dump control */
@@ -46,7 +92,11 @@
#define DR_PIEZO_PIN_N_OUT NRF_GPIO_PIN_MAP(1, 6) /**< Negative output */ // P1.2 -> P1.6 jhChun 0128
/*==============================================================================
* MUX CONTROL PINS (Echo Signal Path Selection)
* MUX ( )
* 8 MUX로 .
* MUXA(CH0~CH3) MUXB(CH4~CH7) 4 MUX .
* EN_MUXA/EN_MUXB: MUX ( HIGH)
* SEL0/SEL1: MUX 4
*============================================================================*/
//#define DR_PIEZO_MUX_SEL1 NRF_GPIO_PIN_MAP(1, 13) /**< MUX Select 1 (HIGH) */
//#define DR_PIEZO_MUX_SEL2 NRF_GPIO_PIN_MAP(1, 12) /**< MUX Select 2 (LOW) */
@@ -59,7 +109,11 @@
#define DR_PIEZO_MUX_SEL1 NRF_GPIO_PIN_MAP(0, 28) /**< MUX Select 1 */
/*==============================================================================
* CONFIGURATION
*
* 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.
@@ -73,7 +127,8 @@
#define DR_PIEZO_MUX_SETTLING_US 1300 /**< MUX settling delay (us) */
/*==============================================================================
* POWER CONTROL FUNCTIONS
*
* DC/DC (+/-20V) ON/OFF하여 .
*============================================================================*/
/**
@@ -86,8 +141,15 @@ void dr_piezo_power_on(void);
*/
void dr_piezo_power_off(void);
/**
* @brief
* @return true: ON, false: OFF
*/
bool dr_piezo_is_power_on(void);
/*==============================================================================
* TX DRIVER FUNCTIONS
* TX
* : , , /,
*============================================================================*/
/**
@@ -156,7 +218,12 @@ void dr_piezo_mux_init(void);
void dr_piezo_select_channel(uint8_t channel);
/*==============================================================================
* SYSTEM FUNCTIONS (Power + TX combined)
* ( + TX )
* ON/OFF와 TX / .
* (burst_sw) : CPU NOP .
* - Timer/PPI CPU에서 GPIO를
* -
* - (NOP )
*============================================================================*/
/**