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 36c17d79c6
commit a0c96c6677
29 changed files with 1474 additions and 501 deletions
+74 -4
View File
@@ -165,6 +165,75 @@ cat_interface.c 삭제(EEPROM/AES 코드 전체 삭제). TWI 인스턴스(m_twi)
| c_addr[6] | main.c:187 | 삭제 | **미완료** |
| led_pd_dac_v[LED_NUM] | 참조 다수 | 삭제 | 확인 필요 |
### 2.3.1 cmd_parse.c → parser.c 통합 현황
현재 구조: main.c → `received_command_process()` [cmd_parse.c] → `dr_cmd_parser()` [parser.c] → 실패 시 레거시 if-else 체인 [cmd_parse.c]
#### parser.c 명령어 테이블 (g_cmd_table)
| 태그 | 핸들러 | enabled | 기능 | 비고 |
|------|--------|---------|------|------|
| `cmd?` | Cmd_cmd | true | 핀 테스트 (sudo) | parser.c 신규 |
| `mpa?` | Cmd_mpa | true | Piezo 활성화 | parser.c 신규 |
| `mpb?` | Cmd_mpb | true | Piezo 비활성화 | parser.c 신규 |
| `mpc?` | Cmd_mpc | true | Piezo Cycles 제어 | parser.c 신규 |
| `mdc?` | Cmd_mdc | true | Piezo burst + Echo (12-bit) | parser.c 신규 |
| `mec?` | Cmd_mec | true | Piezo burst + Echo (16-bit) | parser.c 신규 |
| `maa?` | Cmd_maa | true | 8채널 전체 캡처 | parser.c 신규 |
| `msp?` | Cmd_msp | true | IMU 6축 raw data | parser.c 신규 |
| `mwh?` | Cmd_mwh | true | HW번호 쓰기 (FDS) | shz? 대체 |
| `mws?` | Cmd_mws | true | 시리얼번호 쓰기 (FDS) | ssz? 대체 |
| `mrh?` | Cmd_mrh | true | HW번호 읽기 (FDS) | siz? 대체 |
| `mrs?` | Cmd_mrs | true | 시리얼번호 읽기 (FDS) | srz? 대체 |
| `mpz?` | Cmd_mpz | true | 패스키 쓰기 (FDS) | **신규 추가** (spz? 대체) |
| `mqz?` | Cmd_mqz | true | 패스키 읽기 | **신규 추가** (sqz? 대체) |
| `mxz?` | Cmd_mxz | true | life_cycle 쓰기 (FDS) | **신규 추가** (sxz? 대체) |
| `myz?` | Cmd_myz | true | life_cycle 읽기 | **신규 추가** (syz? 대체) |
| `mta?` | Cmd_mta | true | 디바이스 상태 | sta? 리네이밍 |
| `sta?` | Cmd_sta | true | 디바이스 활성화/슬립 | 레거시 호환 |
| `str?` | Cmd_str | false | 디바이스 상태 읽기 | 미완성 (TODO) |
| `mcj?` | Cmd_mcj | true | PD-ADC 풀 측정 | parser.c 신규 |
| `scj?` | Cmd_mcj | true | mcj 호환 | 레거시 호환 |
| `sej?` | Cmd_sej | true | PD-ADC 측정 | 레거시 호환 |
| `ssj?` | Cmd_ssj | false | PD-ADC 측정 | 비활성 |
| `msn?` | Cmd_msn | true | 배터리 측정 | ssn? 리네이밍 |
| `ssn?` | Cmd_msn | true | 배터리 측정 | 레거시 호환 |
| `spn?` | Cmd_spn | false | 압력 센서 측정 | 비활성 |
| `sso?` | Cmd_sso | false | 온도 센서 측정 | 비활성 |
| `ssp?` | Cmd_ssp | true | 모션 센서 raw data | 레거시 호환 |
| `ssq?` | Cmd_ssq | false | 전원 OFF | 비활성 |
| `ssr?` | Cmd_ssr | false | 본딩 삭제+리셋 | 비활성 |
| `sss?` | Cmd_sss | false | 디바이스 리셋 | 비활성 |
| `sst?` | Cmd_sst | false | Ready 응답 | 비활성 |
| `mfv?` | Cmd_mfv | true | 펌웨어 버전 읽기 | ssv? 대체 |
#### cmd_parse.c 레거시 명령어 (parser.c에 없는 것)
| 태그 | 상태 | 기능 | 비고 |
|------|------|------|------|
| `spz?` | 활성 | 패스키 쓰기 | → parser.c `mpz?`로 대체 완료 |
| `sqz?` | 활성 | 패스키 읽기 | → parser.c `mqz?`로 대체 완료 |
| `sxz?` | 활성 | life_cycle 쓰기 | → parser.c `mxz?`로 대체 완료 |
| `syz?` | 활성 | life_cycle 읽기 | → parser.c `myz?`로 대체 완료 |
| `sez?` | 활성 (stub) | AGC gain — HW 제거됨 | param_error만 응답 |
| `sfz?` | 활성 (stub) | AGC gain — HW 제거됨 | param_error만 응답 |
| `ssv?` | `if(0&&)` 비활성 | 펌웨어 버전 | → parser.c `mfv?`로 대체됨 |
| `ssz?` | `if(0&&)` 비활성 | 시리얼번호 쓰기 | → parser.c `mws?`로 대체됨 |
| `srz?` | `if(0&&)` 비활성 | 시리얼번호 읽기 | → parser.c `mrs?`로 대체됨 |
| `siz?` | `if(0&&)` 비활성 | HW번호 읽기 | → parser.c `mrh?`로 대체됨 |
| `shz?` | `if(0&&)` 비활성 | HW번호 쓰기 | → parser.c `mwh?`로 대체됨 |
#### 통합 남은 작업
| # | 작업 | 현재 상태 |
|---|------|----------|
| 1 | parser.c에 누락 명령어 추가 (mpz/mqz/mxz/myz) | **완료** |
| 2 | received_command_process() 래퍼를 main.c로 이동 | **미완료** |
| 3 | 전역 변수 (SERIAL_NO, m_static_passkey 등) main.c로 이동 | **미완료** |
| 4 | cmd_parse.h include 정리 (7개 파일) | **미완료** |
| 5 | cmd_parse.c / cmd_parse.h 삭제 | **미완료** |
| 6 | Keil 프로젝트 파일 업데이트 | **미완료** |
### 2.4 주석 처리된 코드
| 위치 | 내용 | 판정 | 현재 상태 |
@@ -448,10 +517,11 @@ BLE_GAP_EVT_CONNECTED/DISCONNECTED 핸들러에 IMU 타이머 start/stop 로직
| 5 | 미사용 HW 드라이버 삭제 (1.2절) | **완료** |
| 6 | cat_interface.c EEPROM 기능 삭제 + TWI를 i2c_manager.c로 통합 (1.5절) | **완료** |
| 7 | cmd_parse.c EEPROM 호출 -> FDS 전환 (1.5.4절) | **완료** |
| 8 | 미사용 전역변수 + HW_NO 삭제 (2.2-2.3절) | **미완료** |
| 9 | DEBUG_MINIMAL_BOOT 조건 제거 (3절) | **미완료** |
| 10 | Keil uvprojx 소스 참조 제거 (4절) | 확인 필요 |
| 11 | 주석 처리된 코드 삭제 (2.4절) | **미완료** |
| 8 | cmd_parse.c → parser.c 통합 (2.3.1절) | **진행 중** (명령어 이전 완료, 파일 삭제 미완료) |
| 9 | 미사용 전역변수 + HW_NO 삭제 (2.2-2.3절) | **미완료** |
| 10 | DEBUG_MINIMAL_BOOT 조건 제거 (3절) | **미완료** |
| 11 | Keil uvprojx 소스 참조 제거 (4절) | 확인 필요 |
| 12 | 주석 처리된 코드 삭제 (2.4절) | **미완료** |
### 2차 정리 (저전력 최적화)
Binary file not shown.
Binary file not shown.
+18 -1
View File
@@ -49,6 +49,9 @@ extern void dr_piezo_burst_sw_20mhz(uint8_t cycles);
extern void dr_piezo_burst_sw_17mhz(uint8_t cycles);
extern void dr_piezo_burst_sw_22mhz(uint8_t cycles);
extern void dr_piezo_burst_sw_19mhz(uint8_t cycles);
extern void dr_piezo_power_off(void);
#include "parser.h"
/*==============================================================================
* DEBUG CONFIGURATION
@@ -791,7 +794,7 @@ dr_adc_err_t dr_adc_burst_capture_transmit(uint8_t freq_option, uint16_t delay_u
if (cycles < 3 || cycles > 9) cycles = 5; /* Valid range: 3~9, default 5 */
if (averaging == 0) averaging = 1; /* Minimum 1 */
if (averaging > 1000) averaging = 1000; /* Maximum 1000 */
if (piezo_ch > 7) piezo_ch = 0; /* Validate piezo channel: 0~7 jhChun 26.01.29 */
if (piezo_ch >= MAA_NUM_CHANNELS) piezo_ch = 0; /* 채널 범위 검증 */
dr_adc_echo_t echo;
echo.peak_raw = 0;
@@ -1325,6 +1328,8 @@ static dr_adc_err_t maa_async_capture_channel(uint8_t ch)
{
if (ch > (MAA_NUM_CHANNELS - 1)) return DR_ADC_ERR_INVALID_PARAM;
if (g_plat.log) g_plat.log("[maa] capturing CH%u\r\n", ch);
return dr_adc_capture_channel_only(
g_maa_ctx.freq_option,
g_maa_ctx.delay_us,
@@ -1434,6 +1439,13 @@ static void maa_async_send_completion(uint16_t status)
dr_binary_tx_safe(buf, 3);
/* 자동으로 켰으면 완료 후 전원 OFF */
if (g_maa_ctx.auto_powered) {
if (g_plat.log) g_plat.log("[Cmd_maa] TX/RX Active -> Sleep\r\n");
dr_piezo_power_off();
g_maa_ctx.auto_powered = false;
}
g_maa_ctx.state = MAA_ASYNC_IDLE;
ADC_LOG("maa_async: complete, status=0x%04X", status);
}
@@ -1551,3 +1563,8 @@ void maa_async_abort(void)
}
}
void maa_async_set_auto_power(bool on)
{
g_maa_ctx.auto_powered = on;
}
+7 -1
View File
@@ -336,7 +336,7 @@ void dr_piezo_select_channel(uint8_t channel);
* @brief 8-channel echo buffer for maa? command
* Memory: 140 samples × 2 bytes × 8 channels = 2,240 bytes
*/
#define MAA_NUM_CHANNELS 8 /* 4 -> 8 jhChun 26.02.12*/
#define MAA_NUM_CHANNELS 6 /* 4 -> 8 -> 6 jhChun 26.03.17*/
#define MAA_SAMPLES_MAX 200
/**
@@ -454,6 +454,7 @@ typedef struct {
dr_maa_channel_t channels[MAA_NUM_CHANNELS]; /**< Captured data for each channel */
uint16_t total_packets; /**< Total packets for current channel */
uint16_t data_packets; /**< Data packets for current channel */
bool auto_powered; /**< true: 자동 전원 ON → 완료 후 OFF */
} maa_async_ctx_t;
/**
@@ -501,5 +502,10 @@ maa_async_state_t maa_async_get_state(void);
*/
void maa_async_abort(void);
/**
* @brief 자동 전원 플래그 설정 (완료 후 자동 power off)
*/
void maa_async_set_auto_power(bool on);
#endif /* DR_ADC121S051_H */
+51 -34
View File
@@ -111,6 +111,7 @@ extern config_data_t m_config; /* 전체 설정 구조체 (FDS 저장 대
/* 피에조 초음파 트랜스듀서 제어 함수 */
extern void dr_piezo_power_on( void ); /* 피에조 회로 전원 ON (TX/RX 보드) */
extern void dr_piezo_power_off( void ); /* 피에조 회로 전원 OFF */
extern bool dr_piezo_is_power_on( void ); /* 피에조 전원 상태 확인 */
extern void dr_piezo_burst_sw(uint8_t cycles); /* 2.1MHz 버스트 펄스 발생 (기본 주파수) */
extern void dr_piezo_burst_sw_18mhz(uint8_t cycles); /* 1.8MHz 버스트 펄스 발생 */
extern void dr_piezo_burst_sw_20mhz(uint8_t cycles); /* 2.0MHz 버스트 펄스 발생 */
@@ -436,7 +437,7 @@ static CmdEntry g_cmd_table[] = {
/* D. 각종 센서 측정 */
{ "msn?", true, Cmd_msn },
{ "mso?", false, Cmd_mso },
{ "mso?", true, Cmd_mso },
{ "msp?", true, Cmd_msp },
{ "msi?", true, Cmd_msi },
@@ -511,7 +512,7 @@ static int dr_cmd_dispatch(const ParsedCmd *cmd)
int dr_cmd_parser(const uint8_t *buf, uint8_t len)
{
ParsedCmd cmd;
g_plat.log("parser!!!!!!!!");
g_plat.log("parser!!!!!!!!\r\n");
if (g_plat.log) g_plat.log("[PARSER] in len=%u crc=%u\r\n", len, g_plat.crc_check);
@@ -614,9 +615,7 @@ static int Cmd_msq(const ParsedCmd *cmd)
{
uint16_t val = 0;
dr_get_u16(cmd, 0, &val);
if (g_plat.log && g_log_enable) {
g_plat.log("[Cmd_msq] Power off\r\n");
}
if (g_plat.log) g_plat.log("[Cmd_msq] Power off val=%u\r\n", val);
single_format_data(ble_bin_buffer, "rsq:", val);
dr_binary_tx_safe(ble_bin_buffer, 2);
go_device_power_off = true; /* 메인 루프에서 전원 OFF 실행 예약 */
@@ -637,9 +636,7 @@ static int Cmd_msr(const ParsedCmd *cmd)
{
uint16_t val = 0;
dr_get_u16(cmd, 0, &val);
if (g_plat.log && g_log_enable) {
g_plat.log("[Cmd_msr] Bond delete + reset\r\n");
}
if (g_plat.log) g_plat.log("[Cmd_msr] Bond delete + reset val=%u\r\n", val);
single_format_data(ble_bin_buffer, "rsr:", val);
dr_binary_tx_safe(ble_bin_buffer, 2);
@@ -665,9 +662,7 @@ static int Cmd_mss(const ParsedCmd *cmd)
{
uint16_t val = 0;
dr_get_u16(cmd, 0, &val);
if (g_plat.log && g_log_enable) {
g_plat.log("[Cmd_mss] Device reset\r\n");
}
if (g_plat.log) g_plat.log("[Cmd_mss] Device reset val=%u\r\n", val);
single_format_data(ble_bin_buffer, "rss:", val);
dr_binary_tx_safe(ble_bin_buffer, 2);
@@ -712,8 +707,7 @@ static int Cmd_mpa(const ParsedCmd *cmd)
g_plat.log("[Cmd_mpa] Piezo Activation\n");
}
dr_piezo_power_on(); /* 피에조 보드 전원 ON (LDO 활성화) */
dr_piezo_system_init(); /* 피에조 시스템 초기화 (DAC/MUX/ADC 설정) */
dr_piezo_system_init(); /* 내부에서 power_on + init 수행 */
if (g_plat.tx_bin) {
single_format_data(ble_bin_buffer, "rpa:", 1);
@@ -777,8 +771,8 @@ static int Cmd_mpc(const ParsedCmd *cmd)
(void)dr_get_u16(cmd, 1, &freq_option); /* word 1: 주파수 옵션 */
(void)dr_get_u16(cmd, 2, &piezo_ch); /* word 2: 피에조 채널 */
/* 채널 범위 검증: 0~7 유효, 초과 시 0으로 리셋 */
if (piezo_ch > 7) piezo_ch = 0;
/* 채널 범위 검증: 초과 시 0으로 리셋 */
if (piezo_ch >= MAA_NUM_CHANNELS) piezo_ch = 0;
if (g_plat.log && g_log_enable) {
const char *freq_str = (freq_option == 0) ? "1.8MHz" :
@@ -851,6 +845,14 @@ static int Cmd_mec(const ParsedCmd *cmd)
uint16_t averaging = 1; /* 기본 1 (평균화 없음), 최대 1000 */
uint16_t piezo_ch = 0; /* 기본 채널 0 (유효: 0~7) */
/* 피에조 전원이 꺼져 있으면 자동으로 켜기 */
bool auto_powered = false;
if (!dr_piezo_is_power_on()) {
if (g_plat.log) g_plat.log("[Cmd_mec] TX/RX Sleep -> Active\r\n");
dr_piezo_system_init();
auto_powered = true;
}
/* 6개 파라미터 순서대로 추출 (데이터 부족 시 기본값 유지) */
(void)dr_get_u16(cmd, 0, &freq_option); /* word 0: 주파수 옵션 */
(void)dr_get_u16(cmd, 1, &delay_us); /* word 1: 버스트→ADC 딜레이 */
@@ -863,8 +865,8 @@ static int Cmd_mec(const ParsedCmd *cmd)
if (averaging == 0) averaging = 1;
if (averaging > 1000) averaging = 1000;
/* 피에조 채널 범위 검증: 0~7 */
if (piezo_ch > 7) piezo_ch = 0;
/* 피에조 채널 범위 검증 */
if (piezo_ch >= MAA_NUM_CHANNELS) piezo_ch = 0;
if (g_plat.log && g_log_enable) {
const char *freq_str = (freq_option == 0) ? "1.8MHz" :
@@ -877,18 +879,10 @@ static int Cmd_mec(const ParsedCmd *cmd)
freq_option, freq_str, delay_us, num_samples, cycles, averaging, piezo_ch);
}
/* 통합 버스트+캡처+전송 함수 호출
* 내부 동작 순서:
* 1. ADC 전원 ON
* 2. MUX로 피에조 채널 선택 (0~7)
* 3. 주파수별 버스트 펄스 발생 (freq_option에 따라)
* 4. delay_us 후 ADC 캡처 (averaging 횟수만큼 반복)
* 5. 캡처된 샘플 평균화 (노이즈 저감)
* 6. BLE 멀티패킷 전송 (패킷 간 타이밍 자동 관리)
*/
/* 통합 버스트+캡처+전송 함수 호출 */
dr_adc_err_t err = dr_adc_burst_capture_transmit(
(uint8_t)freq_option, delay_us, num_samples, (uint8_t)cycles,
(uint16_t)averaging, (uint8_t)piezo_ch, ble_bin_buffer, 0); /* 0=raa 태그로 전송 */
(uint16_t)averaging, (uint8_t)piezo_ch, ble_bin_buffer, 0);
/* 에러 발생 시 에러 코드 + 요청 샘플 수 응답 */
if (err != DR_ADC_OK) {
@@ -899,6 +893,12 @@ static int Cmd_mec(const ParsedCmd *cmd)
g_plat.log("[Cmd_mec] result=%d\r\n", err);
}
/* 자동으로 켰으면 완료 후 전원 OFF */
if (auto_powered) {
if (g_plat.log) g_plat.log("[Cmd_mec] TX/RX Active -> Sleep\r\n");
dr_piezo_power_off();
}
return 1;
}
@@ -981,7 +981,7 @@ static int Cmd_cmd(const ParsedCmd *cmd)
*/
#define MAA_FREQ_OPTION 1 /* 기본 2.1MHz */
#define MAA_DELAY_US 10 /* 버스트 후 10us 딜레이 */
#define MAA_NUM_SAMPLES 140 /* 140샘플 (약 25cm 거리) */
#define MAA_NUM_SAMPLES 100 /* 140샘플 (약 25cm 거리) */
#define MAA_CYCLES 7 /* 7사이클 버스트 */
#define MAA_AVERAGING 5 /* 5회 평균화 */
@@ -1005,11 +1005,22 @@ static int Cmd_maa(const ParsedCmd *cmd)
return 1;
}
/* 피에조 전원이 꺼져 있으면 자동으로 켜기 */
bool auto_powered = false;
bool pwr = dr_piezo_is_power_on();
if (g_plat.log) g_plat.log("[Cmd_maa] piezo_power=%u\r\n", pwr);
if (!pwr) {
if (g_plat.log) g_plat.log("[Cmd_maa] TX/RX Sleep -> Active\r\n");
dr_piezo_system_init();
auto_powered = true;
}
/*=======================================================================
* 비동기 4채널 캡처 시작
* 비동기 6채널 캡처 시작
* - maa_async_start(): CH0 캡처 실행 + 첫 헤더 패킷 전송
* - 이후 패킷은 BLE_NUS_EVT_TX_RDY 콜백에서 자동 전송
* - 블로킹 딜레이 없음 → SoftDevice 이벤트 정상 처리 가능
* - auto_powered=true면 완료 후 자동 전원 OFF
*=======================================================================*/
err = maa_async_start(
(uint8_t)MAA_FREQ_OPTION,
@@ -1021,12 +1032,19 @@ static int Cmd_maa(const ParsedCmd *cmd)
);
if (err != DR_ADC_OK) {
/* 시작 실패 → maa_async_start 내부에서 에러 응답 이미 전송됨 */
/* 시작 실패 → 에러 응답 전송 (앱 타임아웃 방지) */
if (g_plat.log) g_plat.log("[Cmd_maa] start failed err=%d\r\n", err);
single_format_data(ble_bin_buffer, "raa:", (uint16_t)(0xFF00 | err));
dr_binary_tx_safe(ble_bin_buffer, 3);
if (auto_powered) dr_piezo_power_off();
return 1;
}
/* 자동 전원 플래그 설정 → 비동기 완료 시 자동 OFF */
maa_async_set_auto_power(auto_powered);
/* 즉시 반환 → 비동기 전송 진행 중 */
/* 전체 완료 시 상태 머신이 "raa:" 응답 자동 전송 */
/* 전체 완료 시 상태 머신이 "raa:" 응답 + 자동 power off 처리 */
return 1;
}
@@ -1119,10 +1137,7 @@ static int Cmd_mrs(const ParsedCmd *cmd)
/*==============================================================================
* 패스키 / 수명 카운터 (FDS)
*
* 패스키: BLE 페어링 시 사용하는 6자리 숫자 (예: "123456")
* 수명 카운터: 디바이스 사용 횟수 추적 (uint32, 상위16+하위16으로 분할 전송)
*============================================================================*/
/* mpz? - BLE 패스키 FDS에 쓰기
@@ -1154,6 +1169,8 @@ static int Cmd_mqz(const ParsedCmd *cmd)
(void)cmd;
memcpy(m_static_passkey, m_config.static_passkey, 6);
if (g_plat.log) g_plat.log("[mqz] Passkey read: %.6s\r\n", m_static_passkey);
ascii_format_data(ble_bin_buffer, "rqz:", m_static_passkey, 6);
dr_binary_tx_safe(ble_bin_buffer, 5);
return 1;
@@ -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 HWSW 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() : SWHW HW ( HW )
* sw_i2c_init_once() : HWSW (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 )
*============================================================================*/
/**