레거시 cmd_parse.c 제거 및 dr_cmd_parser 직접 호출 구조로 전환

- main.c에서 received_command_process() 대신 dr_cmd_parser() 직접 호출
- cmd_parse.c 전역변수/함수(SERIAL_NO, HW_NO, param_error 등) main.c로 이동
- g_plat 초기화를 main.c 초기화 구간으로 이동, log를 RTT 출력으로 연결
- 미사용 명령 삭제: mta, mtr, mst, mxz, myz, mpn, mdc
- cmd_parse.c/h 삭제, 참조하던 5개 파일 include 정리

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
jhChun
2026-03-17 15:45:33 +09:00
parent e7f3c5b02b
commit 36c17d79c6
10 changed files with 2596 additions and 2994 deletions

View File

@@ -3,7 +3,33 @@
* @author CandyPops Co.
* @version V1.0.0
* @date 2022-09-05
* @brief
* @brief
******************************************************************************/
/*******************************************************************************
* [모듈 개요] ICM42670P IMU 드라이버 상위 레이어
*
* ICM42670P IMU 센서의 초기화, 설정, 데이터 읽기를 담당하는 애플리케이션
* 레이어 모듈이다. InvenSense 드라이버 API를 래핑하여 사용한다.
*
* 주요 기능:
* 1) setup_imu_device() - IMU 초기화 및 WHOAMI 확인 (0x67 = ICM42670P)
* 2) configure_imu_device() - 센서 파라미터 설정
* - 가속도계: ±4g FSR, 100Hz(저전력) 또는 800Hz(저잡음)
* - 자이로: ±2000dps FSR, 100Hz 또는 800Hz
* - FIFO 비활성화 (레지스터 직접 읽기 모드)
* 3) get_imu_data() - FIFO 또는 레지스터에서 센서 데이터 읽기
* 4) imu_callback() - 센서 데이터 수신 콜백
* - 마운팅 매트릭스 적용 (보드 방향 보정)
* - info4 모드: info_imu[6]에 데이터 저장
* - BLE 모드: "rsp:" 태그로 6축 데이터 BLE 전송
* - UART 모드: 텍스트 형식으로 시리얼 출력
* 5) imu_read_direct() - 드라이버 API를 우회한 직접 I2C 레지스터 읽기
* - 센서 설정 → 전원 ON → 80ms 대기 → 12바이트 읽기 → 슬립
*
* 마운팅 매트릭스:
* Q30 고정소수점 형식의 3x3 회전 매트릭스로, 보드에 장착된 센서의
* 물리적 방향을 소프트웨어 좌표계에 맞춰 보정한다.
******************************************************************************/
#include "sdk_config.h"
@@ -17,15 +43,15 @@
#include "app_util_platform.h"
#include "main.h"
#include <cmd_parse.h>
/* 2026-03-17: cmd_parse.h 삭제 — main.h는 이미 포함됨 */
#include "debug_print.h"
#include "nrf_delay.h"
/*
* Print raw data or scaled data
* 0 : print raw accel, gyro and temp data
* 1 : print scaled accel, gyro and temp data in g, dps and degree Celsius
* 데이터 출력 형식 선택
* 0 : 원시 데이터 (raw accel, gyro, temp) 출력
* 1 : 스케일링된 데이터 (g, dps, 섭씨) 출력
*/
#define SCALED_DATA_G_DPS 0
@@ -34,12 +60,20 @@
* Static and extern variables
* -------------------------------------------------------------------------------------- */
/* Just a handy variable to handle the IMU object */
/* IMU 드라이버 객체 — 드라이버 API 호출 시 항상 이 구조체를 전달 */
static struct inv_imu_device icm_driver;
/* BLE 전송용 바이너리 버퍼 */
uint8_t imu_bin_buffer[BLE_NUS_MAX_DATA_LEN];
/*
* ICM mounting matrix
* Coefficients are coded as Q30 integer
* ICM42670P 마운팅 매트릭스 (Q30 고정소수점)
*
* 센서가 보드에 장착된 물리적 방향에 따라 좌표 변환을 수행한다.
* Q30 형식: 1.0 = (1 << 30) = 0x40000000
*
* SM_REVB_DB (개발보드): X→-Y, Y→X 변환 (90도 회전)
* 기본 (SmartMotion): 단위 행렬 (변환 없음)
*/
#if (SM_BOARD_REV == SM_REVB_DB) /* when DB or EVB are used */
static int32_t icm_mounting_matrix[9] = { 0, -(1<<30), 0,
@@ -50,13 +84,14 @@ static int32_t icm_mounting_matrix[9] = {(1<<30), 0, 0,
0, (1<<30), 0,
0, 0, (1<<30)};
#endif
bool custom_add_data;
extern bool motion_raw_data_enabled;
extern char ble_tx_buffer[BLE_NUS_MAX_DATA_LEN];
extern which_cmd_t cmd_type_t;
uint16_t ssp_data[6]={0,};
extern bool info4; //cmd_parse
volatile uint16_t info_imu[6];
bool custom_add_data; /* 커스텀 데이터 추가 플래그 (BLE 전송 제어) */
extern bool motion_raw_data_enabled; /* 외부에서 원시 데이터 읽기를 요청하는 플래그 */
extern char ble_tx_buffer[BLE_NUS_MAX_DATA_LEN]; /* BLE 텍스트 전송 버퍼 */
extern which_cmd_t cmd_type_t; /* 현재 명령 소스 (BLE 또는 UART) */
uint16_t ssp_data[6]={0,}; /* BLE 전송용 6축 데이터 배열 (accel XYZ + gyro XYZ) */
extern bool info4; /* info4 모드 플래그 (cmd_parse에서 설정) */
volatile uint16_t info_imu[6]; /* info4 모드에서 IMU 데이터를 저장하는 전역 배열 */
/* --------------------------------------------------------------------------------------
* static function declaration
@@ -67,25 +102,37 @@ static void apply_mounting_matrix(const int32_t matrix[9], int32_t raw[3]);
* Functions definition
* -------------------------------------------------------------------------------------- */
/*
* setup_imu_device()
* IMU 디바이스 초기화 및 식별 확인
*
* 처리 흐름:
* 1) inv_imu_init()으로 드라이버 초기화 (시리얼 인터페이스 + 콜백 등록)
* 2) WHOAMI 레지스터 읽기로 디바이스 확인
* 3) WHOAMI 값이 ICM_WHOAMI(0x67)와 일치하는지 검증
*
* 반환값: 0=성공, 음수=에러
*/
int setup_imu_device(struct inv_imu_serif *icm_serif)
{
int rc = 0;
uint8_t who_am_i;
/* Init device */
/* IMU 드라이버 초기화 — 시리얼 인터페이스 연결 및 콜백 함수 등록 */
rc = inv_imu_init(&icm_driver, icm_serif, imu_callback);
if (rc != INV_ERROR_SUCCESS) {
printf("!!! ERROR : Failed to initialize IMU!\r\n");
return rc;
}
/* Check WHOAMI */
/* WHOAMI 레지스터 읽기 — 디바이스 존재 및 통신 확인 */
rc = inv_imu_get_who_am_i(&icm_driver, &who_am_i);
if (rc != INV_ERROR_SUCCESS) {
printf("!!! ERROR : Failed to read whoami!\r\n");
return rc;
}
/* WHOAMI 값 검증 — ICM42670P의 경우 0x67이어야 함 */
if (who_am_i != ICM_WHOAMI) {
printf("!!! ERROR : Bad WHOAMI value! Read 0x%02x, expected 0x%02x\r\n", who_am_i, ICM_WHOAMI);
return INV_ERROR;
@@ -94,55 +141,89 @@ int setup_imu_device(struct inv_imu_serif *icm_serif)
return rc;
}
/*
* configure_imu_device()
* IMU 센서 동작 파라미터를 설정한다.
*
* 설정 항목:
* - FIFO: 비활성화 (USE_FIFO=0일 때, 레지스터 직접 읽기 모드)
* - 가속도계 FSR: ±4g (USE_HIGH_RES_MODE=0일 때)
* - 자이로 FSR: ±2000dps
* - ODR(출력 데이터율):
* - 저잡음 모드(USE_LOW_NOISE_MODE=1): 800Hz
* - 저전력 모드(USE_LOW_NOISE_MODE=0): 100Hz
* - 자이로는 항상 저잡음 모드로 동작
* - FIFO 미사용 시 자이로 스타트업 시간만큼 대기
*
* 반환값: 0=성공, 음수=에러
*/
int configure_imu_device(void)
{
int rc = 0;
/* FIFO 비활성화 — 레지스터에서 직접 데이터를 읽는다 */
if (!USE_FIFO)
rc |= inv_imu_configure_fifo(&icm_driver, INV_IMU_FIFO_DISABLED);
if (USE_HIGH_RES_MODE) {
/* 고해상도 FIFO 모드: 20비트 데이터, FSR은 16g/2000dps로 고정됨 */
rc |= inv_imu_enable_high_resolution_fifo(&icm_driver);
} else {
/* 표준 모드: 가속도계 ±4g, 자이로 ±2000dps FSR 설정 */
rc |= inv_imu_set_accel_fsr(&icm_driver, ACCEL_CONFIG0_FS_SEL_4g);
rc |= inv_imu_set_gyro_fsr(&icm_driver, GYRO_CONFIG0_FS_SEL_2000dps);
}
if (USE_LOW_NOISE_MODE) {
/* 저잡음 모드: 800Hz ODR, 가속도계 저잡음 모드 활성화 */
rc |= inv_imu_set_accel_frequency(&icm_driver, ACCEL_CONFIG0_ODR_800_HZ);
rc |= inv_imu_set_gyro_frequency(&icm_driver, GYRO_CONFIG0_ODR_800_HZ);
rc |= inv_imu_enable_accel_low_noise_mode(&icm_driver);
} else {
/* 저전력 모드: 100Hz ODR, 가속도계 저전력 모드 활성화 */
rc |= inv_imu_set_accel_frequency(&icm_driver, ACCEL_CONFIG0_ODR_100_HZ);
rc |= inv_imu_set_gyro_frequency(&icm_driver, GYRO_CONFIG0_ODR_100_HZ);
rc |= inv_imu_enable_accel_low_power_mode(&icm_driver);
}
/* 자이로는 모드에 관계없이 항상 저잡음 모드로 동작 */
rc |= inv_imu_enable_gyro_low_noise_mode(&icm_driver);
/* FIFO 미사용 시 자이로 스타트업 시간만큼 대기 (첫 유효 데이터까지의 지연) */
if (!USE_FIFO)
inv_imu_sleep_us(GYR_STARTUP_TIME_US);
return rc;
}
/*
* get_imu_data()
* IMU에서 센서 데이터를 읽는다.
* USE_FIFO 설정에 따라 FIFO 또는 레지스터에서 데이터를 가져온다.
* 읽은 데이터는 imu_callback()을 통해 처리된다.
*/
int get_imu_data(void)
{
#if USE_FIFO
return inv_imu_get_data_from_fifo(&icm_driver);
#else
return inv_imu_get_data_from_registers(&icm_driver);
#endif
}
#if SCALED_DATA_G_DPS
/*
* get_accel_and_gyr_fsr()
* 현재 설정된 가속도계와 자이로의 FSR(Full Scale Range) 값을 가져온다.
* 스케일링된 데이터(g, dps) 변환에 사용된다.
*/
static void get_accel_and_gyr_fsr(int16_t * accel_fsr_g, int16_t * gyro_fsr_dps)
{
ACCEL_CONFIG0_FS_SEL_t accel_fsr_bitfield;
GYRO_CONFIG0_FS_SEL_t gyro_fsr_bitfield;
inv_imu_get_accel_fsr(&icm_driver, &accel_fsr_bitfield);
switch(accel_fsr_bitfield) {
case ACCEL_CONFIG0_FS_SEL_2g: *accel_fsr_g = 2;
@@ -155,7 +236,7 @@ static void get_accel_and_gyr_fsr(int16_t * accel_fsr_g, int16_t * gyro_fsr_dps)
break;
default: *accel_fsr_g = -1;
}
inv_imu_get_gyro_fsr(&icm_driver, &gyro_fsr_bitfield);
switch(gyro_fsr_bitfield) {
case GYRO_CONFIG0_FS_SEL_250dps: *gyro_fsr_dps = 250;
@@ -172,10 +253,24 @@ static void get_accel_and_gyr_fsr(int16_t * accel_fsr_g, int16_t * gyro_fsr_dps)
#endif
/*
* imu_callback()
* IMU 드라이버가 새 센서 데이터를 읽을 때마다 호출되는 콜백 함수.
*
* 처리 흐름:
* 1) 이벤트에서 가속도/자이로 원시 데이터 추출
* - FIFO 모드: 타임스탬프 롤오버 처리, 고해상도(20비트) 지원
* - 레지스터 모드: 16비트 데이터 직접 사용
* 2) 마운팅 매트릭스 적용 (보드 장착 방향 보정)
* 3) 데이터 출력 (모드에 따라 분기):
* - info4 모드: info_imu[6] 전역 배열에 저장 (외부에서 폴링)
* - UART 모드: "Tp" 접두사로 6축 데이터 텍스트 출력
* - BLE 모드: "rsp:" 태그로 바이너리 패킷 전송 + UART 텍스트 동시 출력
*/
void imu_callback(inv_imu_sensor_event_t *event)
{
int32_t accel[3], gyro[3];
#if SCALED_DATA_G_DPS
float accel_g[3];
float gyro_dps[3];
@@ -183,40 +278,43 @@ void imu_callback(inv_imu_sensor_event_t *event)
int16_t accel_fsr_g, gyro_fsr_dps;
#endif
#if USE_FIFO
#if USE_FIFO
static uint64_t last_fifo_timestamp = 0;
static uint32_t rollover_num = 0;
// Handle rollover
/* FIFO 타임스탬프 롤오버 처리 (16비트 → 64비트 확장) */
if (last_fifo_timestamp > event->timestamp_fsync)
rollover_num++;
last_fifo_timestamp = event->timestamp_fsync;
// Compute timestamp in us
/* 타임스탬프를 마이크로초 단위로 변환 (Q24 해상도 적용) */
timestamp = event->timestamp_fsync + rollover_num * UINT16_MAX;
timestamp *= inv_imu_get_fifo_timestamp_resolution_us_q24(&icm_driver);
timestamp /= (1UL << 24);
if (icm_driver.fifo_highres_enabled) {
/* 고해상도 모드: 16비트 데이터를 4비트 좌측 시프트 + 하위 4비트 추가 → 20비트 */
accel[0] = (((int32_t)event->accel[0] << 4)) | event->accel_high_res[0];
accel[1] = (((int32_t)event->accel[1] << 4)) | event->accel_high_res[1];
accel[2] = (((int32_t)event->accel[2] << 4)) | event->accel_high_res[2];
gyro[0] = (((int32_t)event->gyro[0] << 4)) | event->gyro_high_res[0];
gyro[1] = (((int32_t)event->gyro[1] << 4)) | event->gyro_high_res[1];
gyro[2] = (((int32_t)event->gyro[2] << 4)) | event->gyro_high_res[2];
} else {
/* 표준 해상도: 16비트 데이터 그대로 사용 */
accel[0] = event->accel[0];
accel[1] = event->accel[1];
accel[2] = event->accel[2];
gyro[0] = event->gyro[0];
gyro[1] = event->gyro[1];
gyro[2] = event->gyro[2];
}
#else
/* 레지스터 직접 읽기 모드: 16비트 원시 데이터 추출 */
accel[0] = event->accel[0];
accel[1] = event->accel[1];
accel[2] = event->accel[2];
@@ -225,18 +323,20 @@ void imu_callback(inv_imu_sensor_event_t *event)
gyro[1] = event->gyro[1];
gyro[2] = event->gyro[2];
// Force sensor_mask so it gets displayed below
/* 레지스터 모드에서는 센서 마스크를 강제 설정하여 아래 출력 로직이 동작하도록 함 */
event->sensor_mask |= (1 << INV_SENSOR_TEMPERATURE);
event->sensor_mask |= (1 << INV_SENSOR_ACCEL);
event->sensor_mask |= (1 << INV_SENSOR_GYRO);
#endif
/* 마운팅 매트릭스 적용 — 센서의 물리적 장착 방향을 소프트웨어 좌표계로 보정 */
apply_mounting_matrix(icm_mounting_matrix, accel);
apply_mounting_matrix(icm_mounting_matrix, gyro);
#if SCALED_DATA_G_DPS
/*
* Convert raw data into scaled data in g and dps
* 원시 데이터를 물리 단위(g, dps)로 변환
* 변환 공식: 물리값 = 원시값 * FSR / INT16_MAX
*/
get_accel_and_gyr_fsr(&accel_fsr_g, &gyro_fsr_dps);
accel_g[0] = (float)(accel[0] * accel_fsr_g) / INT16_MAX;
@@ -245,31 +345,37 @@ void imu_callback(inv_imu_sensor_event_t *event)
gyro_dps[0] = (float)(gyro[0] * gyro_fsr_dps) / INT16_MAX;
gyro_dps[1] = (float)(gyro[1] * gyro_fsr_dps) / INT16_MAX;
gyro_dps[2] = (float)(gyro[2] * gyro_fsr_dps) / INT16_MAX;
/* 온도 변환: 고해상도/레지스터 모드는 /128, FIFO 표준 모드는 /2 */
if (USE_HIGH_RES_MODE || !USE_FIFO)
temp_degc = 25 + ((float)event->temperature / 128);
else
else
temp_degc = 25 + ((float)event->temperature / 2);
/*
* Output scaled data on UART link
* 스케일링된 데이터를 UART로 출력
*/
if (event->sensor_mask & (1 << INV_SENSOR_ACCEL) && event->sensor_mask & (1 << INV_SENSOR_GYRO))
printf("%u: %.3f, \t%.3f, \t%.3f, \t%.3f, \t%.3f, \t%.3f, \t%.3f\r\n",
printf("%u: %.3f, \t%.3f, \t%.3f, \t%.3f, \t%.3f, \t%.3f, \t%.3f\r\n",
(uint32_t)timestamp,
accel_g[0], accel_g[1], accel_g[2],
accel_g[0], accel_g[1], accel_g[2],
temp_degc,
gyro_dps[0], gyro_dps[1], gyro_dps[2]);
#else
/*
* Output raw data on UART link
* 원시 데이터 출력 — 명령 소스(info4/UART/BLE)에 따라 분기
*/
if (event->sensor_mask & (1 << INV_SENSOR_ACCEL) && event->sensor_mask & (1 << INV_SENSOR_GYRO) || motion_raw_data_enabled)
{
motion_raw_data_enabled = false;
if (info4 == true)
{
/*
* info4 모드: 전역 배열 info_imu[6]에 데이터를 저장한다.
* 외부 모듈에서 이 배열을 폴링하여 데이터를 사용한다.
*/
info_imu[0] = (uint16_t)accel[0];
info_imu[1] = (uint16_t)accel[1];
info_imu[2] = (uint16_t) accel[2];
@@ -277,17 +383,23 @@ void imu_callback(inv_imu_sensor_event_t *event)
info_imu[4] = (uint16_t) gyro[1];
info_imu[5] = (uint16_t)gyro[2];
}
else if(cmd_type_t == CMD_UART) {
/* UART 모드: "Tp" 접두사로 6축 데이터를 텍스트 형식으로 출력 */
printf("Tp%d,%d,%d,%d,%d,%d\r\n\r\n", accel[0], accel[1], accel[2], gyro[0], gyro[1], gyro[2]);
} else if(cmd_type_t == CMD_BLE) {
//sprintf(ble_tx_buffer, "Tp%d,%d,%d,%d,%d,%d\r\n\r\n", accel[0], accel[1], accel[2], gyro[0], gyro[1], gyro[2]);
ssp_data[0] = (uint16_t)accel[0];
ssp_data[1] = (uint16_t)accel[1];
ssp_data[2] = (uint16_t)accel[2];
ssp_data[3] = (uint16_t)gyro[0];
ssp_data[4] = (uint16_t)gyro[1];
ssp_data[5] = (uint16_t)gyro[2];
/*
* BLE 모드: 6축 데이터를 바이너리 패킷으로 BLE 전송
* ssp_data[0~2] = 가속도 XYZ, ssp_data[3~5] = 자이로 XYZ
* format_data()로 "rsp:" 태그 + 12바이트 데이터를 패킷화
* dr_binary_tx_safe()로 8바이트 BLE 전송
*/
ssp_data[0] = (uint16_t)accel[0];
ssp_data[1] = (uint16_t)accel[1];
ssp_data[2] = (uint16_t)accel[2];
ssp_data[3] = (uint16_t)gyro[0];
ssp_data[4] = (uint16_t)gyro[1];
ssp_data[5] = (uint16_t)gyro[2];
format_data(imu_bin_buffer, "rsp:", ssp_data,12);
printf("Tp%d,%d,%d,%d,%d,%d\r\n\r\n", accel[0], accel[1], accel[2], gyro[0], gyro[1], gyro[2]);
dr_binary_tx_safe(imu_bin_buffer,8);
@@ -307,6 +419,16 @@ void imu_callback(inv_imu_sensor_event_t *event)
* Static functions definition
* -------------------------------------------------------------------------------------- */
/*
* apply_mounting_matrix()
* Q30 고정소수점 회전 매트릭스를 3축 벡터에 적용한다.
*
* 계산 방식:
* result[i] = matrix[i*3+0]*raw[0] + matrix[i*3+1]*raw[1] + matrix[i*3+2]*raw[2]
* 결과를 30비트 우측 시프트하여 Q30 → 정수 변환
*
* 이를 통해 센서의 물리적 장착 방향에 관계없이 일관된 좌표계를 사용할 수 있다.
*/
static void apply_mounting_matrix(const int32_t matrix[9], int32_t raw[3])
{
unsigned i;
@@ -317,12 +439,32 @@ static void apply_mounting_matrix(const int32_t matrix[9], int32_t raw[3])
data_q30[i] += ((int64_t)matrix[3*i+1] * raw[1]);
data_q30[i] += ((int64_t)matrix[3*i+2] * raw[2]);
}
/* Q30 → 정수 변환: 30비트 우측 시프트 */
raw[0] = (int32_t)(data_q30[0]>>30);
raw[1] = (int32_t)(data_q30[1]>>30);
raw[2] = (int32_t)(data_q30[2]>>30);
}
/*
* imu_read_direct()
* 드라이버 API를 우회하여 직접 I2C 레지스터를 읽는 함수.
* DRDY(데이터 준비) 인터럽트를 기다리지 않고 즉시 데이터를 읽는다.
*
* 처리 흐름:
* 1) TWI 초기화 확인 (최초 1회만)
* 2) 자이로 설정: ±2000dps, 100Hz ODR (GYRO_CONFIG0 = 0x09)
* 3) 가속도 설정: ±4g, 100Hz ODR (ACCEL_CONFIG0 = 0x29)
* 4) 전원 ON: 가속도+자이로 저잡음 모드 (PWR_MGMT0 = 0x0F)
* 5) 80ms 대기 (자이로 스타트업: 최소 45ms + 여유)
* 6) ACCEL_DATA_X1(0x0B)부터 12바이트 연속 읽기 (accel 6 + gyro 6)
* 7) 빅엔디안 → int16_t 변환
* 8) 마운팅 매트릭스 적용
* 9) "rsp:" 태그로 BLE 전송
* 10) IMU 슬립 모드로 전환 (전력 절감)
*
* 반환값: 0=성공, -1=TX 실패, -2=RX 실패
*/
/* Raw I2C read from ICM42670P — bypasses driver API entirely */
#include "system_interface.h"
#include "nrfx_twi.h"
@@ -330,12 +472,12 @@ static void apply_mounting_matrix(const int32_t matrix[9], int32_t raw[3])
extern const nrfx_twi_t m_twi_icm42670;
#define IMU_I2C_ADDR 0x68
#define REG_ACCEL_X1 0x0B /* ACCEL_DATA_X1 */
#define REG_ACCEL_X1 0x0B /* ACCEL_DATA_X1 — 가속도 X축 상위 바이트 레지스터 */
/* Direct IMU register read — raw I2C, no DRDY, sends rsp: via BLE */
int imu_read_direct(void)
{
uint8_t raw[12]; /* 6 accel + 6 gyro */
uint8_t raw[12]; /* 가속도 6바이트 + 자이로 6바이트 */
int32_t accel[3], gyro[3];
uint8_t reg;
uint32_t ret;
@@ -344,46 +486,50 @@ int imu_read_direct(void)
DBG_PRINTF("[IMU] enter\r\n");
/* Ensure ICM42670P TWI is initialized (once only) */
/* TWI(I2C) 초기화 — 최초 1회만 수행 (재초기화로 클린 상태 보장) */
if (!twi_ready) {
inv_i2c_master_uninitialize();
inv_i2c_master_initialize();
twi_ready = true;
}
/* Configure gyro: ±2000dps, 100Hz ODR — GYRO_CONFIG0(0x20) = 0x09 */
/* 자이로 설정: GYRO_CONFIG0(0x20) = 0x09 → ±2000dps FSR, 100Hz ODR */
{
uint8_t gyro_cfg[2] = { 0x20, 0x09 };
icm42670_twi_tx(IMU_I2C_ADDR, gyro_cfg, 2, false);
}
/* Configure accel: ±4g, 100Hz ODR — ACCEL_CONFIG0(0x21) = 0x29 */
/* 가속도 설정: ACCEL_CONFIG0(0x21) = 0x29 → ±4g FSR, 100Hz ODR */
{
uint8_t accel_cfg[2] = { 0x21, 0x29 };
icm42670_twi_tx(IMU_I2C_ADDR, accel_cfg, 2, false);
}
/* Enable accel (low-noise) + gyro (low-noise): PWR_MGMT0 = 0x0F */
/* 전원 ON: PWR_MGMT0(0x1F) = 0x0F → 가속도(저잡음) + 자이로(저잡음) 활성화 */
{
uint8_t pwr_cmd[2] = { 0x1F, 0x0F }; /* reg=0x1F, val=0x0F */
icm42670_twi_tx(IMU_I2C_ADDR, pwr_cmd, 2, false);
nrf_delay_ms(80); /* gyro needs min 45ms + margin for first valid sample */
nrf_delay_ms(80); /* 자이로 스타트업: 최소 45ms + 안전 마진 */
}
/* Read 12 bytes starting from ACCEL_DATA_X1 (0x0B..0x16) */
/* ACCEL_DATA_X1(0x0B)부터 12바이트 연속 읽기 (0x0B~0x16) */
reg = REG_ACCEL_X1;
ret = icm42670_twi_tx(IMU_I2C_ADDR, &reg, 1, true);
ret = icm42670_twi_tx(IMU_I2C_ADDR, &reg, 1, true); /* 레지스터 주소 전송 (STOP 없음) */
if (ret) {
DBG_PRINTF("[IMU] tx FAIL %u\r\n", ret);
return -1;
}
ret = icm42670_twi_rx(IMU_I2C_ADDR, raw, 12);
ret = icm42670_twi_rx(IMU_I2C_ADDR, raw, 12); /* 12바이트 데이터 수신 */
if (ret) {
DBG_PRINTF("[IMU] rx FAIL %u\r\n", ret);
return -2;
}
/* Big-Endian register layout: [0..5]=accel, [6..11]=gyro */
/*
* 빅엔디안 레지스터 레이아웃을 int16_t로 변환
* raw[0..5] = 가속도 X,Y,Z (각 2바이트, MSB first)
* raw[6..11] = 자이로 X,Y,Z (각 2바이트, MSB first)
*/
accel[0] = (int16_t)((raw[0] << 8) | raw[1]);
accel[1] = (int16_t)((raw[2] << 8) | raw[3]);
accel[2] = (int16_t)((raw[4] << 8) | raw[5]);
@@ -391,12 +537,14 @@ int imu_read_direct(void)
gyro[1] = (int16_t)((raw[8] << 8) | raw[9]);
gyro[2] = (int16_t)((raw[10] << 8) | raw[11]);
/* 마운팅 매트릭스 적용 — 보드 장착 방향 보정 */
apply_mounting_matrix(icm_mounting_matrix, accel);
apply_mounting_matrix(icm_mounting_matrix, gyro);
DBG_PRINTF("[IMU] A:%d,%d,%d G:%d,%d,%d\r\n",
accel[0], accel[1], accel[2], gyro[0], gyro[1], gyro[2]);
/* BLE 전송용 데이터 패킹: "rsp:" 태그 + 6축 데이터(12바이트) */
ssp_data[0] = (uint16_t)accel[0];
ssp_data[1] = (uint16_t)accel[1];
ssp_data[2] = (uint16_t)accel[2];
@@ -407,7 +555,7 @@ int imu_read_direct(void)
format_data(imu_bin_buffer, "rsp:", ssp_data, 12);
dr_binary_tx_safe(imu_bin_buffer, 8);
/* Put IMU back to sleep: accel OFF + gyro OFF */
/* IMU 슬립 모드: PWR_MGMT0 = 0x00 → 가속도/자이로 모두 OFF (전력 절감) */
{
uint8_t pwr_off[2] = { 0x1F, 0x00 }; /* reg=PWR_MGMT0, val=0x00 */
icm42670_twi_tx(IMU_I2C_ADDR, pwr_off, 2, false);

View File

@@ -3,7 +3,28 @@
* @author CandyPops Co.
* @version V1.0.0
* @date 2022-09-05
* @brief
* @brief
******************************************************************************/
/*******************************************************************************
* [모듈 개요] ICM42670P 메인 초기화 및 폴링 루프
*
* ICM42670P IMU 센서의 전체 초기화 시퀀스와 메인 루프를 담당한다.
*
* 초기화 흐름 (icm42670_init):
* 1) setup_mcu() - I2C 시리얼 인터페이스 구조체 설정 및 TWI 초기화
* 2) setup_imu_device() - IMU 드라이버 초기화 + WHOAMI 확인
* 3) configure_imu_device() - 센서 파라미터 설정 (FSR, ODR, 전원 모드)
* 4) inv_gpio_sensor_irq_init() - INT1(P1.13) GPIO 인터럽트 설정
*
* 메인 루프 (icm42670_main):
* - INT1 인터럽트 발생 시 irq_from_device 플래그가 세팅됨
* - 메인 루프에서 플래그를 확인하고, 세팅되어 있으면 센서 데이터를 읽음
* - 인터럽트는 하강 에지(HITOLO)에서 발생 (INT1 핀 풀업 설정)
*
* 보조 함수:
* - inv_imu_sleep_us() - nrf_delay_us 래퍼 (IMU 드라이버가 사용)
* - inv_imu_get_time_us() - RTC1 카운터로 타임스탬프 제공
******************************************************************************/
#include "sdk_config.h"
#include "app_raw.h"
@@ -22,7 +43,7 @@
#include "nrf_delay.h"
#include "app_util_platform.h"
#include <cmd_parse.h>
#include "main.h" /* 2026-03-17: cmd_parse.h 삭제 → main.h */
#include "i2c_manager.h"
/* --------------------------------------------------------------------------------------
* Global variables
@@ -32,7 +53,12 @@
* Static variables
* -------------------------------------------------------------------------------------- */
/* Flag set from IMU device irq handler */
/*
* IMU 인터럽트 플래그
* INT1 핀의 하강 에지 인터럽트 발생 시 1로 세팅된다.
* 메인 루프에서 이 플래그를 확인 후 데이터를 읽고 0으로 클리어한다.
* volatile: ISR에서 변경되므로 컴파일러 최적화 방지
*/
static volatile int irq_from_device;
@@ -55,41 +81,66 @@ static int setup_mcu(struct inv_imu_serif *icm_serif);
* @return NULL
*
*/
/*
* inv_gpio_sensor_interrupt_handler()
* INT1 핀 인터럽트 핸들러 (ISR).
* 센서가 새 데이터를 준비했을 때 호출되며, 플래그만 세팅하고 즉시 반환한다.
* 실제 데이터 처리는 메인 루프(icm42670_main)에서 수행한다.
*/
static void inv_gpio_sensor_interrupt_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action)
{
irq_from_device = 1;
}
}
/*
* inv_gpio_sensor_irq_init()
* INT1(P1.13) GPIO 인터럽트를 초기화한다.
*
* 설정:
* - 트리거: 하강 에지 (HITOLO) — 센서가 INT를 Low로 끌어내릴 때
* - 풀업 저항: 내부 풀업 활성화
* - 핸들러: inv_gpio_sensor_interrupt_handler
* - GPIOTE 모듈이 미초기화 상태이면 먼저 초기화
*/
void inv_gpio_sensor_irq_init(void)
{
ret_code_t err_code;
/* Initialize int pin */
/* GPIOTE 모듈 초기화 (이미 초기화되어 있으면 건너뜀) */
if (!nrfx_gpiote_is_init())
{
err_code = nrfx_gpiote_init();
APP_ERROR_CHECK(err_code);
}
/* 하강 에지 인터럽트 설정: High→Low 전환 시 트리거, 내부 풀업 사용 */
nrfx_gpiote_in_config_t in_config = NRFX_GPIOTE_CONFIG_IN_SENSE_HITOLO(true);
in_config.pull = NRF_GPIO_PIN_PULLUP;
/* INT1 핀에 인터럽트 핸들러 등록 */
err_code = nrfx_gpiote_in_init(ICM42670_INT1_PIN, &in_config, inv_gpio_sensor_interrupt_handler);
APP_ERROR_CHECK(err_code);
/* 인터럽트 이벤트 활성화 */
nrfx_gpiote_in_event_enable(ICM42670_INT1_PIN, true);
}
/*
* inv_gpio_sensor_irq_uninit()
* INT1 GPIO 인터럽트를 비활성화하고 해제한다.
* 센서 비활성화 시 또는 재초기화 전에 호출된다.
*/
void inv_gpio_sensor_irq_uninit(void)
{
/* 인터럽트 이벤트 비활성화 */
nrfx_gpiote_in_event_disable(ICM42670_INT1_PIN);
/* INT1 핀 인터럽트 설정 해제 */
nrfx_gpiote_in_uninit(ICM42670_INT1_PIN);
/* Initialize int pin */
/* GPIOTE 모듈 해제 (초기화된 경우에만) */
if (nrfx_gpiote_is_init())
{
nrfx_gpiote_uninit();
@@ -100,6 +151,19 @@ void inv_gpio_sensor_irq_uninit(void)
/* --------------------------------------------------------------------------------------
* Main
* -------------------------------------------------------------------------------------- */
/*
* icm42670_init()
* ICM42670P 전체 초기화 시퀀스를 수행한다.
*
* 초기화 순서:
* 1) setup_mcu() - I2C 인터페이스 구조체 설정 및 TWI 하드웨어 초기화
* 2) setup_imu_device() - IMU 드라이버 초기화, WHOAMI(0x67) 확인
* 3) configure_imu_device() - FSR, ODR, 전원 모드 설정
* 4) inv_gpio_sensor_irq_init() - INT1 인터럽트 활성화 (데이터 준비 알림)
*
* 반환값: 0=성공, -1=초기화 실패
*/
int icm42670_init(void)
{
int rc = 0;
@@ -113,29 +177,45 @@ int icm42670_init(void)
printf("!!!error during initialization\r\n");
return -1;
}
/* 초기화 성공 후 INT1 인터럽트 활성화 — 이후 데이터 준비 시 ISR이 호출됨 */
inv_gpio_sensor_irq_init();
return rc;
}
/*
* icm42670_main()
* ICM42670P 메인 폴링 루프.
* 메인 애플리케이션 루프에서 주기적으로 호출되어야 한다.
*
* 동작:
* 1) I2C 하드웨어가 초기화되었는지 확인 (hw_i2c_init_once)
* 2) irq_from_device 플래그 확인 (ISR에서 세팅됨)
* 3) 플래그가 세팅되어 있으면 센서 데이터 읽기 (get_imu_data)
* 4) 데이터 읽기 완료 후 플래그 클리어
*
* 참고: 인터럽트 기반 폴링 방식으로, ISR에서는 플래그만 세팅하고
* 실제 I2C 통신은 메인 컨텍스트에서 수행한다.
*/
void icm42670_main(void)
{
int rc = 0;
hw_i2c_init_once();
/* Poll device for data */
/* 인터럽트 발생 여부 확인 후 데이터 읽기 */
if (irq_from_device) {
rc = get_imu_data();
if(rc < 0) {
printf("error while getting data\r\n");
}
/* 플래그 클리어 — 다음 인터럽트까지 대기 */
irq_from_device = 0;
}
}
@@ -144,25 +224,29 @@ void icm42670_main(void)
* -------------------------------------------------------------------------------------- */
/*
* This function initializes MCU on which this software is running.
* It configures:
* - a UART link used to print some messages
* - interrupt priority group and GPIO so that MCU can receive interrupts from IMU
* - a microsecond timer requested by IMU driver to compute some delay
* - a microsecond timer used to get some timestamps
* - a serial link to communicate from MCU to IMU
* setup_mcu()
* MCU 측 시리얼 인터페이스를 설정한다.
*
* inv_imu_serif 구조체에 다음을 등록:
* - read_reg / write_reg : I2C 읽기/쓰기 콜백 함수 (system_interface.c에서 구현)
* - max_read / max_write : 최대 전송 크기 (32KB)
* - serif_type : 통신 타입 (UI_I2C)
*
* 설정 후 inv_io_hal_init()을 호출하여 실제 TWI 하드웨어를 초기화한다.
*/
static int setup_mcu(struct inv_imu_serif *icm_serif)
{
int rc = 0;
/* Initialize serial interface between MCU and IMU */
icm_serif->context = 0; /* no need */
icm_serif->read_reg = inv_io_hal_read_reg;
icm_serif->write_reg = inv_io_hal_write_reg;
icm_serif->max_read = 1024*32; /* maximum number of bytes allowed per serial read */
icm_serif->max_write = 1024*32; /* maximum number of bytes allowed per serial write */
icm_serif->serif_type = SERIF_TYPE;
/* IMU 드라이버용 시리얼 인터페이스 구조체 설정 */
icm_serif->context = 0; /* 컨텍스트 미사용 */
icm_serif->read_reg = inv_io_hal_read_reg; /* 레지스터 읽기 콜백 */
icm_serif->write_reg = inv_io_hal_write_reg; /* 레지스터 쓰기 콜백 */
icm_serif->max_read = 1024*32; /* 1회 읽기 최대 바이트 수 */
icm_serif->max_write = 1024*32; /* 1회 쓰기 최대 바이트 수 */
icm_serif->serif_type = SERIF_TYPE; /* UI_I2C (app_raw.h에서 정의) */
/* TWI 하드웨어 초기화 */
rc |= inv_io_hal_init(icm_serif);
return rc;
@@ -173,16 +257,28 @@ static int setup_mcu(struct inv_imu_serif *icm_serif)
* Extern functions definition
* -------------------------------------------------------------------------------------- */
/* Sleep implementation */
/*
* inv_imu_sleep_us()
* IMU 드라이버가 사용하는 마이크로초 단위 슬립 함수.
* nrf_delay_us()를 래핑하여 플랫폼 독립적 인터페이스를 제공한다.
* 예: 자이로 스타트업 대기(GYR_STARTUP_TIME_US) 시 사용
*/
void inv_imu_sleep_us(uint32_t us)
{
nrf_delay_us(us);
}
/* Get time implementation */
/*
* inv_imu_get_time_us()
* IMU 드라이버가 사용하는 타임스탬프 함수.
* nRF52840의 RTC1 카운터 값을 반환한다.
*
* 주의: RTC1은 32.768kHz로 동작하므로, 반환값의 단위는 엄밀히
* 마이크로초가 아닌 RTC 틱(약 30.5us/tick)이다.
* 드라이버 내부에서 상대적 시간 비교 용도로 사용된다.
*/
uint64_t inv_imu_get_time_us(void)
{
return NRF_RTC1->COUNTER;
}