- Piezo 6ch 측정 + 센서(배터리, IMU, 온도) 측정: mbb 명령어 추가

- Flash Memory Piezo 측정 파라미터 추가

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
jhChun
2026-03-18 13:54:06 +09:00
parent 96a46a26dd
commit 3ecd81c252
12 changed files with 507 additions and 306 deletions

View File

@@ -7,9 +7,9 @@
******************************************************************************/
/*******************************************************************************
* [모듈 개요] 배터리 전압 및 압력센서 ADC 측정 모듈
* [모듈 개요] 배터리 전압 및 압력센서 ADC 측정 모듈 ---> 압력 센서 미탑재로 압력 센서 부분 삭제 예정
*
* nRF52840의 SAADC(Successive Approximation ADC)를 사용하여 다음을 수행한다:
* nRF52840의 SAADC(Successive Approximation ADC)를 사용하여 다음을 수행:
* 1) 배터리 전압 측정 (AIN2 채널, 1/3 프리스케일링)
* - 5초 주기 타이머(battery_loop)로 반복 측정
* - 저전압(3100mV 이하) 10회 연속 감지 시 자동 전원 OFF
@@ -42,18 +42,22 @@
//#include "fstorage.h"
#include "battery_saadc.h"
#include "main_timer.h"
#include "main.h" /* 2026-03-17: cmd_parse.h 삭제 → main.h */
#include "main.h"
#include "debug_print.h"
/* SAADC 내부 기준전압 600mV */
#define BATTERY_REF_VOLTAGE_IN_MILLIVOLTS 600 /**< Reference voltage (in milli volts) used by ADC while doing conversion. */
/* 1/3 프리스케일링 보상 계수 (입력 전압을 1/3로 분압하므로 x3, 추가 x2 = 총 x6) */
#define BATTERY_PRE_SCALING_COMPENSATION 6 /**< The ADC is configured to use VDD with 1/3 prescaling as input. And hence the result of conversion is to be multiplied by 3 to get the actual value of the battery voltage.*/
/* 10비트 ADC 최대 디지털 값 */
#define BATTERY_ADC_RES_10BITS 1023 /**< Maximum digital value for 10-bit ADC conversion. */
//#define PRESSURE_RESULT_IN_MILLI_VOLTS(adc) ((adc * 3600) / 1023)
#define PRESSURE_OFFSET_DEFAULT 0 // 압력 offset. 캘리브레이션 시 사용 가능
#define MV_PER_ADC_STEP 805 // 약 0.805mV per 1 LSB (nRF 12bit + scaling)
/**@brief Macro to convert the result of ADC conversion in millivolts.
*
* @param[in] ADC_VALUE ADC result.
@@ -65,29 +69,33 @@
/* 배터리 측정용 더블 버퍼 (SAADC가 비동기로 교대 사용) */
static nrf_saadc_value_t adc_bufs[2];
/* 압력센서 2채널 ADC 버퍼 [0]=AIN7(P1), [1]=AIN4(P2) */
static int16_t pressure_adc_buf[2]; //cj add 25/11/19
static uint16_t convert_adc_to_mV(int16_t raw_adc); //cj add 25/11/19
/* 배터리 모니터링 반복 타이머 정의 */
APP_TIMER_DEF(m_battery_loop_timer_id);
/* 배터리 측정 주기: 5초 (밀리초 단위) */
#define BATTERY_LOOP_INTERVAL 5000
/* 저전압 체크 플래그 — battery_loop에서 true로 설정, 핸들러에서 소비 */
bool low_battery_check = false;
/* info4: 전체 센서 데이터 수집 모드 플래그 (cmd_parse에서 설정) */
extern bool info4; //cmd_parse
extern bool info4; // main.c
// cj add edit 25/11/24
/* info4 모드에서 압력센서 측정값을 임시 저장하는 변수 (mV 단위) */
volatile uint16_t info_p1;
volatile uint16_t info_p2;
extern char ble_tx_buffer[BLE_NUS_MAX_DATA_LEN];
extern char ble_tx_buffer[BLE_NUS_MAX_DATA_LEN];
/* true가 되면 main_timer에서 전원 OFF 시퀀스 실행 */
extern bool go_device_power_off;
/* 다른 작업(IMU 등) 처리 중이면 true — 배터리 측정 스킵용 */
extern volatile bool processing;
@@ -95,15 +103,17 @@ extern volatile bool processing;
extern which_cmd_t cmd_type_t;
extern uint8_t ble_bin_buffer[BLE_NUS_MAX_DATA_LEN] ;
/* info4 모드에서 배터리 전압을 임시 저장 (mV 단위) */
volatile uint16_t info_batt; //48_c
/* info4 순차 측정 제어 플래그: go_batt→go_temp→motion */
extern bool go_temp; //
extern bool go_batt; //cmd_parse
extern bool motion_raw_data_enabled ;
extern bool ble_got_new_data;
extern bool motion_data_once ;
/* info4 순차 측정 제어 플래그: go_batt→ go_temp → motion */
extern bool go_temp; //
extern bool go_batt; //cmd_parse
extern bool motion_raw_data_enabled ;
extern bool ble_got_new_data;
extern bool motion_data_once ;
/**@brief Function for handling the ADC interrupt.
*
* @details This function will fetch the conversion result from the ADC, convert the value into
@@ -123,7 +133,9 @@ extern uint8_t ble_bin_buffer[BLE_NUS_MAX_DATA_LEN] ;
{
/* 음수 ADC 값은 0으로 처리 (노이즈 등으로 발생 가능) */
if (raw_adc < 0)
raw_adc = 0;
{
raw_adc = 0;
}
/* 805 uV/LSB 스케일링: raw x 805 = uV, /1000 = mV */
int32_t mv = (int32_t)raw_adc * MV_PER_ADC_STEP; // 단위: 805 uV
@@ -131,12 +143,15 @@ extern uint8_t ble_bin_buffer[BLE_NUS_MAX_DATA_LEN] ;
/* 0~3500mV 범위로 클램핑하여 유효 범위 보장 */
if (mv < 0)
mv = 0;
{
mv = 0;
}
if (mv > 3500)
mv = 3500;
{
mv = 3500;
}
return (uint16_t)mv;
}
@@ -181,26 +196,22 @@ void pressure_all_event_handler(nrf_drv_saadc_evt_t const * p_event)
DBG_PRINTF("P1:%d P2:%d\r\n", p1_mV, p2_mV);
}
/* BLE 모드이고 info4가 아닌 경우(단독 압력 측정) → BLE 바이너리 전송 */
else if(cmd_type_t == CMD_BLE && info4 == false)
{
DBG_PRINTF("P1:%d P2:%d\r\n", p1_mV, p2_mV);
// uint16_t len = sprintf((char*)ble_bin_buffer,
// "rpn:%04x,%04x", p1_mV, p2_mV);
/* 2채널 압력값을 "rpn:" 헤더와 함께 바이너리 포맷으로 BLE 전송 */
uint16_t result_data[2];
result_data[0] = p1_mV;
result_data[1] = p2_mV;
format_data(ble_bin_buffer, "rpn:", result_data,2);
dr_binary_tx_safe(ble_bin_buffer,4);
else if(cmd_type_t == CMD_BLE && info4 == false)
{
DBG_PRINTF("P1:%d P2:%d\r\n", p1_mV, p2_mV);
// uint16_t len = sprintf((char*)ble_bin_buffer,
// "rpn:%04x,%04x", p1_mV, p2_mV);
/* 2채널 압력값을 "rpn:" 헤더와 함께 바이너리 포맷으로 BLE 전송 */
uint16_t result_data[2];
result_data[0] = p1_mV;
result_data[1] = p2_mV;
format_data(ble_bin_buffer, "rpn:", result_data,2);
dr_binary_tx_safe(ble_bin_buffer,4);
}
}
}
}
/**
* @brief 배터리 전압 ADC 완료 콜백
*
@@ -214,7 +225,6 @@ void pressure_all_event_handler(nrf_drv_saadc_evt_t const * p_event)
*/
void battery_event_handler( nrf_drv_saadc_evt_t const * p_event )
{
/* 저전압 연속 감지 카운터 (static으로 호출 간 유지) */
static uint8_t low_battery_cnt = 0;
@@ -238,60 +248,60 @@ void battery_event_handler( nrf_drv_saadc_evt_t const * p_event )
/* ADC값 → mV 변환 (매크로: ADC x 600/1023 x 6) */
batt_lvl_in_milli_volt_0 = BATTERY_RESULT_IN_MILLI_VOLTS(register_val);
/* 분압 저항 보정 계수 1.42 적용 → 실제 배터리 전압 */
batt_lvl_in_milli_volt_1 = (batt_lvl_in_milli_volt_0) *1.42;
/* === 저전압 체크 모드 (battery_loop 타이머에서 설정) === */
if(low_battery_check == true) {
if(low_battery_check == true)
{
low_battery_check = false;
/* 배터리 전압이 LOW_BATTERY_VOLTAGE(3100mV) 이하인지 확인 */
if(batt_lvl_in_milli_volt_1 <= LOW_BATTERY_VOLTAGE) {
if(batt_lvl_in_milli_volt_1 <= LOW_BATTERY_VOLTAGE)
{
/* 10회 연속 저전압 감지 시 전원 OFF 시퀀스 시작 */
if(low_battery_cnt >= 10) {
if(low_battery_cnt >= 10)
{
low_battery_cnt = 0;
/*go to power off and fds save */
DBG_PRINTF("Save FDS parameters and then Power OFF\r\n");
go_device_power_off = true;
main_timer_start();
}else{
}
else
{
/* 아직 10회 미만 — 카운터 증가 후 경고 출력 */
low_battery_cnt++;
DBG_PRINTF("WARNING!!! low_battery cnt = %d, Batt = %d(mV)\r\n", low_battery_cnt, batt_lvl_in_milli_volt_1);
}
}
}
/* === info4 모드: 전체 센서 수집 중 배터리 값 저장 === */
else if (info4 == true){
/* === info4 모드: 전체 센서 수집 중 배터리 값 저장 === */
else if (info4 == true)
{
info_batt = batt_lvl_in_milli_volt_1;
DBG_PRINTF("INFOTn%d\r\n\r\n", batt_lvl_in_milli_volt_1);
}
/* === 일반 모드: 단독 배터리 측정 요청에 대한 응답 전송 === */
else {
if(cmd_type_t == CMD_UART) {
else
{
if (cmd_type_t == CMD_UART)
{
DBG_PRINTF("Tn%d\r\n\r\n", batt_lvl_in_milli_volt_1);
} else if(cmd_type_t == CMD_BLE) {
}
else if (cmd_type_t == CMD_BLE)
{
/* "rsn:" 헤더와 함께 배터리 전압을 바이너리로 BLE 전송 */
single_format_data(ble_bin_buffer, "rsn:", batt_lvl_in_milli_volt_1);
dr_binary_tx_safe(ble_bin_buffer,3);
dr_binary_tx_safe(ble_bin_buffer,3);
//data_tx_handler(ble_tx_buffer);
}
}
}
/* info4 모드: 배터리 측정 완료 → 다음 단계(온도 측정)로 전환 */
if (info4 == true){
go_batt =false; /* 배터리 측정 완료 표시 */
go_temp = true; /* 온도 측정 시작 플래그 설정 */
main_timer_start(); /* 메인 타이머 시작 → 온도 측정 트리거 */
}
}
@@ -309,8 +319,7 @@ static void battery_configure(void)
APP_ERROR_CHECK(err_code);
/* AIN2 채널 설정: 싱글엔드 입력, 1/3 프리스케일링 (기본값) */
nrf_saadc_channel_config_t config =
NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN2);
nrf_saadc_channel_config_t config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN2);
err_code = nrf_drv_saadc_channel_init(0, &config);
APP_ERROR_CHECK(err_code);
@@ -335,33 +344,40 @@ void pressure_all_configure(void)
/* SAADC 드라이버 초기화 (이미 초기화된 경우 무시) */
err_code = nrf_drv_saadc_init(NULL, pressure_all_event_handler);
if (err_code != NRF_SUCCESS && err_code != NRF_ERROR_INVALID_STATE) {
if (err_code != NRF_SUCCESS && err_code != NRF_ERROR_INVALID_STATE)
{
DBG_PRINTF("SAADC init err=%d\r\n", err_code);
return;
}
/* 채널 0: AIN7 (압력센서1, P1) */
nrf_saadc_channel_config_t ch0_cfg =
NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN7);
nrf_saadc_channel_config_t ch0_cfg = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN7);
/* 채널 1: AIN4 (압력센서2, P2) */
nrf_saadc_channel_config_t ch1_cfg =
NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN4);
nrf_saadc_channel_config_t ch1_cfg = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN4);
err_code = nrf_drv_saadc_channel_init(0, &ch0_cfg);
if (err_code != NRF_SUCCESS && err_code != NRF_ERROR_INVALID_STATE) {
if (err_code != NRF_SUCCESS && err_code != NRF_ERROR_INVALID_STATE)
{
DBG_PRINTF("SAADC ch0 init err=%d\r\n", err_code);
return;
}
err_code = nrf_drv_saadc_channel_init(1, &ch1_cfg);
if (err_code != NRF_SUCCESS && err_code != NRF_ERROR_INVALID_STATE) {
if (err_code != NRF_SUCCESS && err_code != NRF_ERROR_INVALID_STATE)
{
DBG_PRINTF("SAADC ch1 init err=%d\r\n", err_code);
return;
}
/* 2채널 ADC 버퍼 등록 ([0]=P1, [1]=P2) */
err_code = nrf_drv_saadc_buffer_convert(pressure_adc_buf, 2);
if (err_code != NRF_SUCCESS) {
if (err_code != NRF_SUCCESS)
{
DBG_PRINTF("SAADC buf conv err=%d\r\n", err_code);
return;
}
@@ -377,11 +393,9 @@ void battery_level_meas(void)
{
ret_code_t err_code;
battery_configure(); /* SAADC 배터리용 초기화 */
battery_configure(); /* SAADC 배터리용 초기화 */
err_code = nrf_drv_saadc_sample(); /* ADC 샘플링 트리거 (비동기) */
APP_ERROR_CHECK(err_code);
}
/**
@@ -409,15 +423,18 @@ void pressure_all_level_meas(void) //add cj add 25/11/19
void battery_loop(void * p_context) /* For 1sec */
{
UNUSED_PARAMETER(p_context);
/* 다른 센서 처리 중이면 배터리 측정 스킵 (충돌 방지) */
if(processing==true)
{
processing = false ; // add 20241218
//low_battery_check = true;
return;}
else{
low_battery_check = true; /* 저전압 감지 모드로 측정 */
battery_level_meas(); /* 배터리 ADC 1회 측정 시작 */
return;
}
else
{
low_battery_check = true; /* 저전압 감지 모드로 측정 */
battery_level_meas(); /* 배터리 ADC 1회 측정 시작 */
}
}

View File

@@ -63,7 +63,7 @@
#define CONFIG_REC_KEY (0x7010)
/* 매직 넘버: 플래시에 저장된 데이터가 유효한 설정인지 판별하는 데 사용 */
#define CONFIG_MAGIC_NUMBER_VALUE (0x20231226)
#define CONFIG_MAGIC_NUMBER_VALUE (0x20260318)
/* 전역 설정 데이터 구조체 인스턴스 */
config_data_t m_config;
@@ -118,12 +118,15 @@ void fds_default_value_set(void)
/* Reset status */
m_config.reset_status = reset_status_dflt;
/* Measurement parameters */
m_config.pd_adc_cnt = 8;
m_config.pd_delay_us = 8000;
/* Device usage count */
m_config.life_cycle = 0;
/* 피에조 캡처 파라미터 기본값 */
m_config.piezo_freq_option = 1; /* 2.1MHz */
m_config.piezo_delay_us = 10; /* 버스트 후 10us */
m_config.piezo_num_samples = 100; /* 100샘플 */
m_config.piezo_cycles = 7; /* 7사이클 */
m_config.piezo_averaging = 5; /* 5회 평균화 */
}
@@ -209,7 +212,8 @@ static void wait_for_fds_ready( void )
nrf_pwr_mgmt_run();
nrf_delay_ms(1);
timeout++;
if (timeout > 3000) { /* 3 second timeout */
if (timeout > 3000) /* 3 second timeout */
{
DBG_PRINTF("[FDS] TIMEOUT!\r\n");
break;
}
@@ -245,7 +249,8 @@ void config_load( void )
DBG_PRINTF("[FDS] find rc=%u\r\n", rc);
/* FDS may not be fully ready yet - retry before writing defaults */
if (rc != NRF_SUCCESS && cfg_retry < 10) {
if (rc != NRF_SUCCESS && cfg_retry < 10)
{
cfg_retry++;
DBG_PRINTF("[FDS] retry %u/10\r\n", cfg_retry);
nrf_delay_ms(100);
@@ -259,7 +264,8 @@ void config_load( void )
/* Open the record and read its contents. */
rc = fds_record_open(&desc, &config);
if (rc != NRF_SUCCESS) {
if (rc != NRF_SUCCESS)
{
/* CRC error or corrupt record - delete and use defaults */
DBG_PRINTF("[FDS] open ERR=%u, deleting\r\n", rc);
(void)fds_record_delete(&desc);
@@ -275,8 +281,7 @@ void config_load( void )
rc = fds_record_close(&desc);
APP_ERROR_CHECK(rc);
DBG_PRINTF("[FDS] magic=0x%08X (expect 0x%08X)\r\n",
m_config.magic_number, CONFIG_MAGIC_NUMBER_VALUE);
DBG_PRINTF("[FDS] magic=0x%08X (expect 0x%08X)\r\n", m_config.magic_number, CONFIG_MAGIC_NUMBER_VALUE);
if( m_config.magic_number != (uint32_t)CONFIG_MAGIC_NUMBER_VALUE )
{ // first init
@@ -315,14 +320,18 @@ void config_load( void )
fds_flag_write = true;
rc = fds_record_write(&desc, &m_dummy_record);
if (rc != NRF_SUCCESS) {
if (rc != NRF_SUCCESS)
{
DBG_PRINTF("[FDS] Write ERR=%u\r\n", rc);
fds_flag_write = false;
}
while( fds_flag_write )
{
nrf_pwr_mgmt_run();
}
if( (rc != NRF_SUCCESS) && (rc == FDS_ERR_NO_SPACE_IN_FLASH) )
{
rc = fds_gc();
@@ -332,6 +341,7 @@ void config_load( void )
{
APP_ERROR_CHECK(rc);
}
NRF_LOG_FLUSH();
goto cfg_load_start;
}
@@ -362,15 +372,19 @@ void config_save( void )
DBG_PRINTF("[CFG_SAVE] start\r\n");
/* Wait for any previous FDS operation to complete */
if (fds_flag_write) {
if (fds_flag_write)
{
uint32_t wait_cnt = 0;
DBG_PRINTF("[CFG_SAVE] waiting for prev FDS op...\r\n");
while (fds_flag_write && wait_cnt < 3000) {
while (fds_flag_write && wait_cnt < 3000)
{
nrf_pwr_mgmt_run();
nrf_delay_ms(1);
wait_cnt++;
}
if (fds_flag_write) {
if (fds_flag_write)
{
DBG_PRINTF("[CFG_SAVE] TIMEOUT! forcing flag clear\r\n");
fds_flag_write = false;
}
@@ -391,6 +405,7 @@ void config_save( void )
fds_flag_write = true;
rc = fds_record_update(&desc, &m_dummy_record);
DBG_PRINTF("[CFG_SAVE] update rc=%u\r\n", rc);
if( rc == FDS_ERR_NO_SPACE_IN_FLASH )
{
fds_flag_write = false;
@@ -400,6 +415,7 @@ void config_save( void )
rc = fds_record_update(&desc, &m_dummy_record);
DBG_PRINTF("[CFG_SAVE] retry rc=%u\r\n", rc);
}
if( rc != NRF_SUCCESS )
{
DBG_PRINTF("[CFG_SAVE] FAIL rc=%u\r\n", rc);
@@ -412,6 +428,7 @@ void config_save( void )
fds_flag_write = true;
rc = fds_record_write(&desc, &m_dummy_record);
DBG_PRINTF("[CFG_SAVE] write rc=%u\r\n", rc);
if( rc != NRF_SUCCESS )
{
DBG_PRINTF("[CFG_SAVE] FAIL rc=%u\r\n", rc);

View File

@@ -40,15 +40,20 @@
typedef struct
{
uint32_t magic_number; /* 4B - 0x20231226 */
char hw_no[12]; /* 12B - HW Number */
char hw_no[12]; /* 12B - HW Version */
char serial_no[12]; /* 12B - Serial Number */
uint8_t static_passkey[6]; /* 6B - BLE Passkey */
uint8_t bond_data_delete; /* 1B - Bond delete flag */
int8_t reset_status; /* 1B - Reset status */
uint8_t pd_adc_cnt; /* 1B - ADC sample count */
uint16_t pd_delay_us; /* 2B - PD delay (us) */
uint32_t life_cycle; /* 4B - Device usage count (sxz/syz command) */
} config_data_t; /* Total: 45 bytes - FDS에 저장하는 디바이스 설정 */
uint32_t life_cycle; /* 4B - Device usage count */
/* Piezo 측정 파라미터 - 8B */
uint8_t piezo_freq_option; /* 1B - Frequency : 송신 펄스 주파수 (0=1.8M, 1=2.1M, 2=2.0M, 3=1.7M) */
uint8_t piezo_cycles; /* 1B - Burst Cycle : 송신 펄스 사이클 수 (3~7) */
uint16_t piezo_averaging; /* 2B - 채널당 반복 측정 횟수 */
uint16_t piezo_delay_us; /* 2B - 송신 펄스 출력 후 ADC 시작 시까지 대기시간(us) */
uint16_t piezo_num_samples; /* 2B - 측정 ADC 샘플 개수(80~140) */
} config_data_t; /* Total: 48 bytes - FDS에 저장하는 디바이스 설정 */
extern config_data_t m_config;

View File

@@ -9,8 +9,8 @@
/*******************************************************************************
* [모듈 개요] ICM42670P IMU 드라이버 상위 레이어
*
* ICM42670P IMU 센서의 초기화, 설정, 데이터 읽기를 담당하는 애플리케이션
* 레이어 모듈이다. InvenSense 드라이버 API를 래핑하여 사용한다.
* ICM42670P IMU 센서의 초기화, 설정, 데이터 읽기를 담당하는 애플리케이션 레이어 모듈
* InvenSense 드라이버 API를 래핑하여 사용
*
* 주요 기능:
* 1) setup_imu_device() - IMU 초기화 및 WHOAMI 확인 (0x67 = ICM42670P)
@@ -28,8 +28,7 @@
* - 센서 설정 → 전원 ON → 80ms 대기 → 12바이트 읽기 → 슬립
*
* 마운팅 매트릭스:
* Q30 고정소수점 형식의 3x3 회전 매트릭스로, 보드에 장착된 센서의
* 물리적 방향을 소프트웨어 좌표계에 맞춰 보정한다.
* Q30 고정소수점 형식의 3x3 회전 매트릭스로, 보드에 장착된 센서의물리적 방향을 소프트웨어 좌표계에 맞춰 보정
******************************************************************************/
#include "sdk_config.h"
@@ -43,7 +42,6 @@
#include "app_util_platform.h"
#include "main.h"
/* 2026-03-17: cmd_parse.h 삭제 — main.h는 이미 포함됨 */
#include "debug_print.h"
#include "nrf_delay.h"
@@ -60,7 +58,7 @@
* Static and extern variables
* -------------------------------------------------------------------------------------- */
/* IMU 드라이버 객체 — 드라이버 API 호출 시 항상 이 구조체 전달 */
/* IMU 드라이버 객체 — 드라이버 API 호출 시 항상 이 구조체 전달 */
static struct inv_imu_device icm_driver;
/* BLE 전송용 바이너리 버퍼 */
@@ -69,11 +67,11 @@ static struct inv_imu_device icm_driver;
/*
* ICM42670P 마운팅 매트릭스 (Q30 고정소수점)
*
* 센서가 보드에 장착된 물리적 방향에 따라 좌표 변환을 수행한다.
* 센서가 보드에 장착된 물리적 방향에 따라 좌표 변환
* Q30 형식: 1.0 = (1 << 30) = 0x40000000
*
* SM_REVB_DB (개발보드): X→-Y, Y→X 변환 (90도 회전)
* 기본 (SmartMotion): 단위 행렬 (변환 없음)
* 기본 (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,
@@ -85,13 +83,13 @@ static int32_t icm_mounting_matrix[9] = {(1<<30), 0, 0,
0, 0, (1<<30)};
#endif
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 데이터를 저장하는 전역 배열 */
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
@@ -143,7 +141,7 @@ int setup_imu_device(struct inv_imu_serif *icm_serif)
/*
* configure_imu_device()
* IMU 센서 동작 파라미터 설정한다.
* IMU 센서 동작 파라미터 설정
*
* 설정 항목:
* - FIFO: 비활성화 (USE_FIFO=0일 때, 레지스터 직접 읽기 모드)
@@ -161,7 +159,7 @@ int configure_imu_device(void)
{
int rc = 0;
/* FIFO 비활성화 — 레지스터에서 직접 데이터를 읽는다 */
/* FIFO 비활성화 — 레지스터에서 직접 데이터를 읽 */
if (!USE_FIFO)
rc |= inv_imu_configure_fifo(&icm_driver, INV_IMU_FIFO_DISABLED);
@@ -199,9 +197,9 @@ int configure_imu_device(void)
/*
* get_imu_data()
* IMU에서 센서 데이터는다.
* USE_FIFO 설정에 따라 FIFO 또는 레지스터에서 데이터를 가져온다.
* 읽은 데이터는 imu_callback()을 통해 처리된다.
* IMU에서 센서 데이터 읽
* USE_FIFO 설정에 따라 FIFO 또는 레지스터에서 데이터를 가져
* 읽은 데이터는 imu_callback()을 통해 처리
*/
int get_imu_data(void)
{
@@ -255,7 +253,7 @@ static void get_accel_and_gyr_fsr(int16_t * accel_fsr_g, int16_t * gyro_fsr_dps)
/*
* imu_callback()
* IMU 드라이버가 새 센서 데이터를 읽을 때마다 호출되는 콜백 함수.
* IMU 드라이버가 새 센서 데이터를 읽을 때마다 호출되는 콜백 함수
*
* 처리 흐름:
* 1) 이벤트에서 가속도/자이로 원시 데이터 추출
@@ -370,45 +368,45 @@ void imu_callback(inv_imu_sensor_event_t *event)
{
motion_raw_data_enabled = false;
/* info4 모드: 전역 배열 info_imu[6]에 데이터 저장, 외부 모듈에서 이 배열을 폴링하여 데이터 사용 */
if (info4 == true)
{
/*
* info4 모드: 전역 배열 info_imu[6]에 데이터를 저장한다.
* 외부 모듈에서 이 배열을 폴링하여 데이터를 사용한다.
*/
info_imu[0] = (uint16_t)accel[0];
info_imu[0] = (uint16_t)accel[0];
info_imu[1] = (uint16_t)accel[1];
info_imu[2] = (uint16_t) accel[2];
info_imu[2] = (uint16_t)accel[2];
info_imu[3] = (uint16_t)gyro[0];
info_imu[4] = (uint16_t) gyro[1];
info_imu[5] = (uint16_t)gyro[2];
info_imu[4] = (uint16_t)gyro[1];
info_imu[5] = (uint16_t)gyro[2];
}
/* UART 모드: "Tp" 접두사로 6축 데이터를 텍스트 형식으로 출력 */
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) {
/*
* 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);
}
/*
* BLE 모드: 6축 데이터를 바이너리 패킷으로 BLE 전송
* ssp_data[0~2] = 가속도 XYZ, ssp_data[3~5] = 자이로 XYZ
* format_data()로 "rsp:" 태그 + 12바이트 데이터를 패킷화
* dr_binary_tx_safe()로 8바이트 BLE 전송
*/
else if(cmd_type_t == CMD_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);
if(custom_add_data==true)
{
if(custom_add_data==true) {
custom_add_data = false;
}
else{
//data_tx_handler(ble_tx_buffer);
else {
//data_tx_handler(ble_tx_buffer);
}
}
}
@@ -544,7 +542,7 @@ int imu_read_direct(void)
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];
@@ -552,8 +550,19 @@ int imu_read_direct(void)
ssp_data[4] = (uint16_t)gyro[1];
ssp_data[5] = (uint16_t)gyro[2];
format_data(imu_bin_buffer, "rsp:", ssp_data, 12);
dr_binary_tx_safe(imu_bin_buffer, 8);
if (info4 == true) {
/* info4 모드: 전역 배열에 저장 (mbb?에서 rbb: 패킷으로 일괄 전송) */
info_imu[0] = ssp_data[0];
info_imu[1] = ssp_data[1];
info_imu[2] = ssp_data[2];
info_imu[3] = ssp_data[3];
info_imu[4] = ssp_data[4];
info_imu[5] = ssp_data[5];
} else {
/* 일반 모드: "rsp:" 태그로 BLE 즉시 전송 */
format_data(imu_bin_buffer, "rsp:", ssp_data, 12);
dr_binary_tx_safe(imu_bin_buffer, 8);
}
/* IMU 슬립 모드: PWR_MGMT0 = 0x00 → 가속도/자이로 모두 OFF (전력 절감) */
{

View File

@@ -278,7 +278,7 @@ char HW_NO[HW_NO_LENGTH]; /* 하드웨어 번호 (FDS
bool bond_data_delete; /* 본딩 데이터 삭제 요청 플래그 */
uint32_t m_life_cycle; /* 디바이스 수명 사이클 카운터 */
uint8_t resetCount = 0; /* 통신 타임아웃 카운터 (리셋 감지용) */
bool info4; /* 추가 정보(배터리/온도/IMU) 포함 측정 플래그 */
bool info4; /* 센서 측정 정보(배터리/IMU/온도) 포함 측정 플래그 */
uint8_t m_reset_status; /* 리셋 상태 코드 (1=정상, 2=SW리셋, 5=보안리셋, 10=본딩완료) */
/*==============================================================================
@@ -318,10 +318,13 @@ static void power_hold_init(void)
*/
static void power_control_handler(on_off_cont_t device_power_st)
{
if (device_power_st == OFF) {
if (device_power_st == OFF)
{
nrf_gpio_pin_clear(POWER_HOLD); /* P0.8 LOW → 전원 래치 해제 → 전원 차단 */
DBG_PRINTF("[PWR] OFF\r\n");
} else if (device_power_st == ON) {
}
else if (device_power_st == ON)
{
nrf_gpio_pin_set(POWER_HOLD); /* P0.8 HIGH → 전원 유지 */
DBG_PRINTF("[PWR] ON\r\n");
}
@@ -404,7 +407,8 @@ static void load_flash_config(void)
m_need_save_defaults = false;
/* 하드웨어 번호 — 비어있으면 기본값 채움 */
if (m_config.hw_no[0] == 0 || m_config.hw_no[0] == (char)0xFF) {
if (m_config.hw_no[0] == 0 || m_config.hw_no[0] == (char)0xFF)
{
memset(m_config.hw_no, 0, HW_NO_LENGTH);
memcpy(m_config.hw_no, HARDWARE_VERSION, strlen(HARDWARE_VERSION));
DBG_PRINTF("[CFG] HW empty, set default: %s\r\n", HARDWARE_VERSION);
@@ -412,7 +416,8 @@ static void load_flash_config(void)
}
/* 시리얼 번호 — 비어있으면 기본값 채움 */
if (m_config.serial_no[0] == 0 || m_config.serial_no[0] == (char)0xFF) {
if (m_config.serial_no[0] == 0 || m_config.serial_no[0] == (char)0xFF)
{
memset(m_config.serial_no, 0, SERIAL_NO_LENGTH);
memcpy(m_config.serial_no, FIRMWARE_SERIAL_NO, strlen(FIRMWARE_SERIAL_NO));
DBG_PRINTF("[CFG] S/N empty, set default: %s\r\n", FIRMWARE_SERIAL_NO);
@@ -472,7 +477,8 @@ static void PM_s(void * p_context)
UNUSED_PARAMETER(p_context);
APP_ERROR_CHECK(app_timer_stop(m_PM_timer_id));
if (m_reset_status == 5) {
if (m_reset_status == 5)
{
DBG_PRINTF("[PM] Kill\r\n");
sd_ble_gap_disconnect(m_conn_handle, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION);
}
@@ -628,7 +634,8 @@ static void nus_data_handler(ble_nus_evt_t * p_evt)
DBG_PRINTF("[NUS] RX len=%d\r\n", p_evt->params.rx_data.length);
/* 콜백에서는 복사만 하고, 메인 루프에서 처리 */
if (p_evt->params.rx_data.length <= BLE_NUS_MAX_DATA_LEN) {
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;
}

View File

@@ -215,16 +215,11 @@ void main_loop(void * p_context) /* For x ms */
}
/* For System Control */
/* ---- 시스템 제어 이벤트 처리 ---- */
/* 디바이스 전원 OFF 처리 */
if(go_device_power_off == true){
main_timer_stop(); /* 타이머 정지 */
DBG_PRINTF("Off main_timer\r\n");
device_power_off(); /* 디바이스 전원 OFF 실행 */
}
@@ -232,21 +227,14 @@ void main_loop(void * p_context) /* For x ms */
/* 슬립 모드 진입 처리 */
if(go_sleep_mode_enter == true){
main_timer_stop(); /* 타이머 정지 */
DBG_PRINTF("sleep main timer\r\n");
sleep_mode_enter(); /* 슬립 모드 진입 실행 */
}
/* NVIC 시스템 리셋 처리 */
if(go_NVIC_SystemReset == true) {
main_timer_stop(); /* 타이머 정지 */
NVIC_SystemReset(); /* ARM Cortex-M4 시스템 리셋 */
NVIC_SystemReset(); /* ARM Cortex-M4 시스템 리셋 */
}
}
@@ -254,7 +242,7 @@ void main_loop(void * p_context) /* For x ms */
/**
* @brief 메인 루프 타이머 시작
*
* 싱글샷 모드로 MAIN_LOOP_INTERVAL(10ms 또는 80ms) 후 main_loop()를 호출한다.
* 싱글샷 모드로 MAIN_LOOP_INTERVAL(10ms 또는 80ms) 후 main_loop()를 호출
*/
void main_timer_start(void)
{
@@ -264,7 +252,7 @@ void main_timer_start(void)
/**
* @brief 지정된 간격(ms)으로 메인 루프 타이머 시작
*
* IMU 연속 스트리밍 등 기본 간격과 다른 주기가 필요할 때 사용.
* IMU 연속 스트리밍 등 기본 간격과 다른 주기가 필요할 때 사용
*/
void main_timer_start_ms(uint32_t interval_ms)
{
@@ -282,8 +270,8 @@ void main_timer_stop(void)
/**
* @brief 메인 루프 타이머 초기화 (앱 시작 시 1회 호출)
*
* 싱글샷 모드 타이머를 생성하고, 콜백으로 main_loop()를 등록한다.
* 싱글샷이므로 매 호출마다 main_timer_start()로 수동 재시작해야 한다.
* 싱글샷 모드 타이머를 생성하고, 콜백으로 main_loop()를 등록
* 싱글샷이므로 매 호출마다 main_timer_start()로 수동 재시작해야
*/
void main_timer_init(void)
{

View File

@@ -7,11 +7,11 @@
******************************************************************************/
/*******************************************************************************
* [한국어 설명] PWM 펄스 생성기
* PWM 펄스 생성기
*
* === 현재 상태 ===
* SKIP_PWM 매크로가 정의되어 있어 PWM 기능이 비활성화됨.
* init/start/stop 함수는 모두 Stub(빈 껍데기) 구현으로,
* init/start/stop 함수는 모두 Stub으로,
* 로그만 출력하고 실제 PWM 동작은 하지 않는다.
* 빌드 호환성을 위해 함수 인터페이스만 유지.
*

View File

@@ -9,9 +9,8 @@
/*******************************************************************************
* [모듈 개요] TMP235-Q1 아날로그 온도센서 드라이버
*
* TMP235-Q1은 온도에 비례하는 아날로그 전압(Vout)을 출력하는 센서이다.
* nRF52840 SAADC의 AIN3 채널로 Vout을 읽고, mV로 변환한 뒤
* 온도(°C)로 계산한다.
* TMP235-Q1은 온도에 비례하는 아날로그 전압(Vout)을 출력하는 센서
* nRF52840 SAADC의 AIN3 채널로 Vout을 읽고, mV로 변환한 뒤 온도(°C)로 계산
*
* 온도 계산 공식 (구간별 선형 보간):
* - Vout <= 1500mV (0~100°C): Ta = (Vout - 500) / 10.0
@@ -61,13 +60,17 @@
static nrf_saadc_value_t adc_buf;
extern char ble_tx_buffer[BLE_NUS_MAX_DATA_LEN];
extern uint8_t ble_bin_buffer[BLE_NUS_MAX_DATA_LEN] ;
/* 현재 명령 소스: CMD_UART 또는 CMD_BLE */
extern which_cmd_t cmd_type_t;
/* info4: 전체 센서 데이터 수집 모드 플래그 */
extern bool info4; //cmd_parse
extern bool info4; // main.c
/* 온도 측정 순서 제어 플래그 */
extern bool go_temp; //cmd_parse
/* info4 모드에서 온도값을 임시 저장 (°C x 100, 정수 표현) */
extern bool go_temp; // main_timer.c
/* info4 모드에서 온도값 임시 저장 (°C x 100, 정수 표현) */
volatile uint16_t info_temp; //48_C
extern bool motion_raw_data_enabled;
@@ -87,9 +90,9 @@ extern bool motion_raw_data_enabled;
*/
void tmp235_voltage_handler(nrf_drv_saadc_evt_t const * p_event) /* TMP325 Vout reading */
{
float led_temp; /* 계산된 온도 (°C, 부동소수점) */
uint16_t led_temp_16; /* BLE 전송용 온도 (°C x 100, 정수) */
if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
{
nrf_saadc_value_t adc_result;
@@ -113,50 +116,50 @@ void tmp235_voltage_handler(nrf_drv_saadc_evt_t const * p_event) /* TMP325 Vout
* 100~125°C 구간: 기울기 10.1 mV/°C
* 125~150°C 구간: 기울기 10.6 mV/°C
*/
if(tmp235_voltage_in_milli_volts <= 1500) {
if(tmp235_voltage_in_milli_volts <= 1500)
{
/* 0~100°C: Ta = (Vout - 500mV) / 10.0 mV/°C */
led_temp = (tmp235_voltage_in_milli_volts - 500.0f) / 10.0f + 0.0f;
}else if(tmp235_voltage_in_milli_volts <= 1750) {
}
else if(tmp235_voltage_in_milli_volts <= 1750)
{
/* 100~125°C: 기울기가 10.1로 약간 증가 */
led_temp = (tmp235_voltage_in_milli_volts - 1500.0f) / 10.1f + 100.0f;
}else if(tmp235_voltage_in_milli_volts <= 2000) {
}
else if(tmp235_voltage_in_milli_volts <= 2000)
{
/* 125~150°C: 기울기가 10.6으로 더 증가 */
led_temp = (tmp235_voltage_in_milli_volts - 1752.5f) / 10.6f + 125.0f;
}else {
}
else
{
/* 150°C 초과 — 센서 측정 범위 벗어남 */
DBG_PRINTF("ERR!!! Temprature is over 150c\r\n");
}
/* info4 모드: 온도값을 정수(°C x 100)로 저장 (예: 36.50°C → 3650) */
if (info4 == true){
if (info4 == true)
{
info_temp =(uint16_t)(led_temp*100);
}
/* UART 모드: 소수점 2자리까지 텍스트로 출력 */
else if(cmd_type_t == CMD_UART) {
else if(cmd_type_t == CMD_UART)
{
DBG_PRINTF("To%.2f\r\n\r\n",led_temp);
}
/* BLE 모드: °C x 100 정수를 "rso:" 헤더로 바이너리 전송 */
} else if(cmd_type_t == CMD_BLE) {
led_temp_16 = (uint16_t)(led_temp*100);
single_format_data(ble_bin_buffer, "rso:", led_temp_16);
else if(cmd_type_t == CMD_BLE)
{
led_temp_16 = (uint16_t)(led_temp*100);
single_format_data(ble_bin_buffer, "rso:", led_temp_16);
dr_binary_tx_safe(ble_bin_buffer,3);
dr_binary_tx_safe(ble_bin_buffer,3);
// sprintf(ble_tx_buffer, "To%.2f\r\n",led_temp);
// data_tx_handler(ble_tx_buffer);
}
}
/* info4 모드: 온도 측정 완료 → 다음 단계(IMU 측정)로 전환 */
if (info4 == true){
go_temp = false; /* 온도 측정 완료 표시 */
motion_raw_data_enabled = true; /* IMU 데이터 수집 시작 플래그 */
main_timer_start(); /* 메인 타이머 시작 → IMU 측정 트리거 */
}
}
@@ -173,8 +176,7 @@ void tmp235_init(void)
APP_ERROR_CHECK(err_code);
/* AIN3 채널 설정: TMP235-Q1 Vout 핀 (싱글엔드 입력) */
nrf_saadc_channel_config_t config =
NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN3); /* TMP235_Q1 Voltage Output Measurement */
nrf_saadc_channel_config_t config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN3); /* TMP235_Q1 Voltage Output Measurement */
err_code = nrf_drv_saadc_channel_init(0, &config);
APP_ERROR_CHECK(err_code);
@@ -197,8 +199,7 @@ void tmp235_init(void)
*/
void tmp235_voltage_level_meas(void)
{
tmp235_init();
tmp235_init(); // init 함수에 있는 걸 그냥 여기 넣어도
//tmp235_uninit();
}