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

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

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
jhChun
2026-03-17 18:03:04 +09:00
parent 39b4ffe66a
commit 96a46a26dd
29 changed files with 1474 additions and 501 deletions

View File

@@ -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);

View File

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

View File

@@ -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.");
}
}

View File

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