레거시 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:
@@ -3,11 +3,31 @@
|
||||
* @author CandyPops Co.
|
||||
* @version V1.0.0
|
||||
* @date 2022-09-05
|
||||
* @brief
|
||||
* @brief
|
||||
******************************************************************************/
|
||||
|
||||
/*******************************************************************************
|
||||
* [모듈 개요] 배터리 전압 및 압력센서 ADC 측정 모듈
|
||||
*
|
||||
* nRF52840의 SAADC(Successive Approximation ADC)를 사용하여 다음을 수행한다:
|
||||
* 1) 배터리 전압 측정 (AIN2 채널, 1/3 프리스케일링)
|
||||
* - 5초 주기 타이머(battery_loop)로 반복 측정
|
||||
* - 저전압(3100mV 이하) 10회 연속 감지 시 자동 전원 OFF
|
||||
* - info4 모드(전체 센서 수집)에서는 info_batt에 저장 후 온도 측정으로 전환
|
||||
* 2) 압력센서 2채널 측정 (AIN7=P1, AIN4=P2)
|
||||
* - 12bit ADC 값을 mV로 변환하여 BLE 또는 UART로 전송
|
||||
*
|
||||
* 배터리 전압 변환 공식:
|
||||
* 전압(mV) = ADC값 x (600mV / 1023) x 6 x 1.42 (분압 저항 보정 계수)
|
||||
*
|
||||
* 압력센서 전압 변환 공식:
|
||||
* 전압(mV) = ADC값 x 0.805(uV/LSB) / 1000, 범위 0~3500mV 클램핑
|
||||
*
|
||||
* info4 모드 순서: 배터리 -> 온도(go_temp) -> IMU(motion_raw_data_enabled)
|
||||
******************************************************************************/
|
||||
|
||||
#include "sdk_common.h"
|
||||
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "nrf.h"
|
||||
@@ -22,15 +42,18 @@
|
||||
//#include "fstorage.h"
|
||||
#include "battery_saadc.h"
|
||||
#include "main_timer.h"
|
||||
#include <cmd_parse.h>
|
||||
#include "main.h" /* 2026-03-17: cmd_parse.h 삭제 → 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)
|
||||
#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.
|
||||
@@ -40,29 +63,41 @@
|
||||
#define BATTERY_RESULT_IN_MILLI_VOLTS(ADC_VALUE)\
|
||||
((((ADC_VALUE) * BATTERY_REF_VOLTAGE_IN_MILLIVOLTS) / BATTERY_ADC_RES_10BITS) * BATTERY_PRE_SCALING_COMPENSATION)
|
||||
|
||||
/* 배터리 측정용 더블 버퍼 (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
|
||||
|
||||
// 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];
|
||||
/* true가 되면 main_timer에서 전원 OFF 시퀀스 실행 */
|
||||
extern bool go_device_power_off;
|
||||
/* 다른 작업(IMU 등) 처리 중이면 true — 배터리 측정 스킵용 */
|
||||
extern volatile bool processing;
|
||||
|
||||
/* 현재 명령 소스: CMD_UART 또는 CMD_BLE */
|
||||
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
|
||||
|
||||
@@ -74,15 +109,27 @@ extern uint8_t ble_bin_buffer[BLE_NUS_MAX_DATA_LEN] ;
|
||||
* @details This function will fetch the conversion result from the ADC, convert the value into
|
||||
* percentage and send it to peer.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief 압력센서 ADC 원시값을 밀리볼트(mV)로 변환
|
||||
*
|
||||
* 변환 공식: mV = raw_adc x 805(uV/LSB) / 1000
|
||||
* 결과를 0~3500mV 범위로 클램핑하여 이상치 방지
|
||||
*
|
||||
* @param raw_adc SAADC에서 읽은 원시 ADC 값 (12bit)
|
||||
* @return 변환된 전압값 (mV), 0~3500 범위
|
||||
*/
|
||||
static uint16_t convert_adc_to_mV(int16_t raw_adc)
|
||||
{
|
||||
/* 음수 ADC 값은 0으로 처리 (노이즈 등으로 발생 가능) */
|
||||
if (raw_adc < 0)
|
||||
raw_adc = 0;
|
||||
|
||||
int32_t mv = (int32_t)raw_adc * MV_PER_ADC_STEP; // ?: 805 uV
|
||||
/* 805 uV/LSB 스케일링: raw x 805 = uV, /1000 = mV */
|
||||
int32_t mv = (int32_t)raw_adc * MV_PER_ADC_STEP; // 단위: 805 uV
|
||||
mv /= 1000;
|
||||
|
||||
// ===== (3) 0~3500mV ??? ???? ?? ?? =====
|
||||
/* 0~3500mV 범위로 클램핑하여 유효 범위 보장 */
|
||||
if (mv < 0)
|
||||
mv = 0;
|
||||
|
||||
@@ -92,49 +139,61 @@ extern uint8_t ble_bin_buffer[BLE_NUS_MAX_DATA_LEN] ;
|
||||
|
||||
return (uint16_t)mv;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 압력센서 2채널 ADC 완료 콜백
|
||||
*
|
||||
* SAADC 변환 완료 시 호출된다.
|
||||
* AIN7(P1)과 AIN4(P2) 두 채널의 ADC 값을 mV로 변환한 뒤,
|
||||
* info4 모드이면 글로벌 변수에 저장하고, 아니면 BLE/UART로 즉시 전송한다.
|
||||
* 변환 후 SAADC를 해제하여 다른 ADC 측정(배터리, 온도)과 공유한다.
|
||||
*/
|
||||
void pressure_all_event_handler(nrf_drv_saadc_evt_t const * p_event)
|
||||
{
|
||||
if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
|
||||
{
|
||||
int16_t p1_adc = p_event->data.done.p_buffer[0]; // AIN7
|
||||
int16_t p2_adc = p_event->data.done.p_buffer[1]; // AIN4
|
||||
/* 버퍼에서 2채널 ADC 원시값 추출 */
|
||||
int16_t p1_adc = p_event->data.done.p_buffer[0]; // AIN7 (압력센서1)
|
||||
int16_t p2_adc = p_event->data.done.p_buffer[1]; // AIN4 (압력센서2)
|
||||
|
||||
|
||||
|
||||
/* ADC 원시값 → 밀리볼트 변환 */
|
||||
uint16_t p1_mV = convert_adc_to_mV(p1_adc);
|
||||
uint16_t p2_mV = convert_adc_to_mV(p2_adc);
|
||||
|
||||
// PD Full mode(info4=true)When info_p1/info_p2 to Update
|
||||
/* info4(전체 센서 수집) 모드일 때 글로벌 변수에 저장 */
|
||||
if(info4 == true)
|
||||
{
|
||||
info_p1 = p1_mV;
|
||||
info_p2 = p2_mV;
|
||||
}
|
||||
// Re-buffer
|
||||
/* 다음 변환을 위해 버퍼 재등록 */
|
||||
APP_ERROR_CHECK(nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, 2));
|
||||
|
||||
// uninit
|
||||
/* SAADC 해제 — 배터리/온도 측정과 하드웨어를 공유하므로 사용 후 반드시 해제 */
|
||||
nrf_drv_saadc_uninit();
|
||||
nrf_drv_saadc_channel_uninit(0);
|
||||
nrf_drv_saadc_channel_uninit(1);
|
||||
|
||||
// UART send
|
||||
|
||||
/* 결과 전송: 명령 소스에 따라 UART 또는 BLE로 전송 */
|
||||
if(cmd_type_t == CMD_UART)
|
||||
{
|
||||
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);
|
||||
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);
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -142,34 +201,54 @@ void pressure_all_event_handler(nrf_drv_saadc_evt_t const * p_event)
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief 배터리 전압 ADC 완료 콜백
|
||||
*
|
||||
* SAADC 변환 완료 시 호출된다.
|
||||
* ADC 값을 실제 배터리 전압(mV)으로 변환하고, 동작 모드에 따라:
|
||||
* - 저전압 체크 모드: 3100mV 이하 10회 연속이면 자동 전원 OFF
|
||||
* - info4 모드: info_batt에 저장 후 온도 측정(go_temp)으로 전환
|
||||
* - 일반 모드: BLE 또는 UART로 즉시 전송
|
||||
*
|
||||
* 전압 변환: ADC값 x (600/1023) x 6 = 기본 전압, x 1.42 = 분압 보정 후 실제 전압
|
||||
*/
|
||||
void battery_event_handler( nrf_drv_saadc_evt_t const * p_event )
|
||||
{
|
||||
|
||||
|
||||
/* 저전압 연속 감지 카운터 (static으로 호출 간 유지) */
|
||||
static uint8_t low_battery_cnt = 0;
|
||||
|
||||
|
||||
if (p_event->type == NRF_DRV_SAADC_EVT_DONE)
|
||||
{
|
||||
nrf_saadc_value_t register_val = 0;
|
||||
uint16_t batt_lvl_in_milli_volt_0 = 0;
|
||||
uint16_t batt_lvl_in_milli_volt_1 = 0;
|
||||
uint16_t batt_lvl_in_milli_volt_0 = 0; /* 보정 전 전압 */
|
||||
uint16_t batt_lvl_in_milli_volt_1 = 0; /* 분압 보정 후 최종 전압 */
|
||||
uint32_t err_code = 0;
|
||||
|
||||
|
||||
/* ADC 변환 결과 읽기 */
|
||||
register_val = p_event->data.done.p_buffer[0];
|
||||
|
||||
/* 다음 변환을 위해 버퍼 재등록 */
|
||||
err_code = nrf_drv_saadc_buffer_convert(p_event->data.done.p_buffer, 1);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
/* SAADC 해제 — 다른 ADC 측정(온도, 압력)과 하드웨어 공유 */
|
||||
nrf_drv_saadc_uninit();
|
||||
nrf_drv_saadc_channel_uninit(0);
|
||||
|
||||
/* 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) {
|
||||
|
||||
low_battery_check = false;
|
||||
|
||||
/* 배터리 전압이 LOW_BATTERY_VOLTAGE(3100mV) 이하인지 확인 */
|
||||
if(batt_lvl_in_milli_volt_1 <= LOW_BATTERY_VOLTAGE) {
|
||||
/* 10회 연속 저전압 감지 시 전원 OFF 시퀀스 시작 */
|
||||
if(low_battery_cnt >= 10) {
|
||||
low_battery_cnt = 0;
|
||||
/*go to power off and fds save */
|
||||
@@ -177,25 +256,28 @@ void battery_event_handler( nrf_drv_saadc_evt_t const * p_event )
|
||||
go_device_power_off = true;
|
||||
main_timer_start();
|
||||
}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){
|
||||
|
||||
|
||||
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) {
|
||||
DBG_PRINTF("Tn%d\r\n\r\n", batt_lvl_in_milli_volt_1);
|
||||
} 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);
|
||||
@@ -203,45 +285,65 @@ void battery_event_handler( nrf_drv_saadc_evt_t const * p_event )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* info4 모드: 배터리 측정 완료 → 다음 단계(온도 측정)로 전환 */
|
||||
if (info4 == true){
|
||||
go_batt =false;
|
||||
go_temp = true;
|
||||
main_timer_start();
|
||||
go_batt =false; /* 배터리 측정 완료 표시 */
|
||||
go_temp = true; /* 온도 측정 시작 플래그 설정 */
|
||||
main_timer_start(); /* 메인 타이머 시작 → 온도 측정 트리거 */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**@brief Function for configuring ADC to do battery level conversion.
|
||||
/**
|
||||
* @brief SAADC를 배터리 전압 측정용으로 설정
|
||||
*
|
||||
* AIN2 채널을 싱글엔드(SE) 모드, 1/3 프리스케일링으로 초기화한다.
|
||||
* 더블 버퍼(adc_bufs[0], [1])를 등록하여 연속 측정이 가능하도록 한다.
|
||||
* 콜백: battery_event_handler
|
||||
*/
|
||||
static void battery_configure(void)
|
||||
{
|
||||
/* SAADC 드라이버 초기화 (기본 설정 + 배터리 이벤트 핸들러 등록) */
|
||||
ret_code_t err_code = nrf_drv_saadc_init(NULL, battery_event_handler);
|
||||
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);
|
||||
err_code = nrf_drv_saadc_channel_init(0, &config);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
/* 더블 버퍼 등록 — SAADC가 교대로 사용하여 데이터 손실 방지 */
|
||||
err_code = nrf_drv_saadc_buffer_convert(&adc_bufs[0], 1);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
err_code = nrf_drv_saadc_buffer_convert(&adc_bufs[1], 1);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief SAADC를 압력센서 2채널 측정용으로 설정
|
||||
*
|
||||
* AIN7(압력센서1)과 AIN4(압력센서2)를 싱글엔드 모드로 초기화한다.
|
||||
* 이미 초기화된 상태(NRF_ERROR_INVALID_STATE)는 무시하여 안전하게 처리한다.
|
||||
* 콜백: pressure_all_event_handler
|
||||
*/
|
||||
void pressure_all_configure(void)
|
||||
{
|
||||
ret_code_t err_code;
|
||||
|
||||
/* SAADC 드라이버 초기화 (이미 초기화된 경우 무시) */
|
||||
err_code = nrf_drv_saadc_init(NULL, pressure_all_event_handler);
|
||||
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);
|
||||
/* 채널 1: AIN4 (압력센서2, P2) */
|
||||
nrf_saadc_channel_config_t ch1_cfg =
|
||||
NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN4);
|
||||
|
||||
@@ -257,6 +359,7 @@ void pressure_all_configure(void)
|
||||
return;
|
||||
}
|
||||
|
||||
/* 2채널 ADC 버퍼 등록 ([0]=P1, [1]=P2) */
|
||||
err_code = nrf_drv_saadc_buffer_convert(pressure_adc_buf, 2);
|
||||
if (err_code != NRF_SUCCESS) {
|
||||
DBG_PRINTF("SAADC buf conv err=%d\r\n", err_code);
|
||||
@@ -264,53 +367,75 @@ void pressure_all_configure(void)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 배터리 전압 1회 측정 시작
|
||||
*
|
||||
* SAADC를 배터리용으로 설정 후 샘플링을 트리거한다.
|
||||
* 결과는 battery_event_handler 콜백에서 비동기로 처리된다.
|
||||
*/
|
||||
void battery_level_meas(void)
|
||||
{
|
||||
ret_code_t err_code;
|
||||
|
||||
battery_configure();
|
||||
err_code = nrf_drv_saadc_sample();
|
||||
|
||||
battery_configure(); /* SAADC 배터리용 초기화 */
|
||||
err_code = nrf_drv_saadc_sample(); /* ADC 샘플링 트리거 (비동기) */
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 압력센서 2채널 1회 측정 시작
|
||||
*
|
||||
* SAADC를 압력센서 2채널용으로 설정 후 샘플링을 트리거한다.
|
||||
* 결과는 pressure_all_event_handler 콜백에서 비동기로 처리된다.
|
||||
*/
|
||||
void pressure_all_level_meas(void) //add cj add 25/11/19
|
||||
{
|
||||
ret_code_t err_code;
|
||||
|
||||
pressure_all_configure(); // 2
|
||||
pressure_all_configure(); /* SAADC 압력센서 2채널 초기화 */
|
||||
|
||||
err_code = nrf_drv_saadc_sample();
|
||||
err_code = nrf_drv_saadc_sample(); /* ADC 샘플링 트리거 (비동기) */
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 배터리 모니터링 타이머 콜백 (5초 주기)
|
||||
*
|
||||
* 다른 작업(IMU 등) 처리 중이면 측정을 건너뛴다.
|
||||
* 그렇지 않으면 저전압 체크 플래그를 설정하고 배터리 측정을 시작한다.
|
||||
*/
|
||||
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();
|
||||
low_battery_check = true; /* 저전압 감지 모드로 측정 */
|
||||
battery_level_meas(); /* 배터리 ADC 1회 측정 시작 */
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief 배터리 모니터링 타이머 시작 (5초 반복) */
|
||||
void battery_timer_start(void)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_start(m_battery_loop_timer_id, APP_TIMER_TICKS(BATTERY_LOOP_INTERVAL), NULL));
|
||||
APP_ERROR_CHECK(app_timer_start(m_battery_loop_timer_id, APP_TIMER_TICKS(BATTERY_LOOP_INTERVAL), NULL));
|
||||
}
|
||||
|
||||
/** @brief 배터리 모니터링 타이머 정지 */
|
||||
void battery_timer_stop(void)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_stop(m_battery_loop_timer_id));
|
||||
APP_ERROR_CHECK(app_timer_stop(m_battery_loop_timer_id));
|
||||
}
|
||||
|
||||
/** @brief 배터리 모니터링 타이머 초기화 (반복 모드, 콜백: battery_loop) */
|
||||
void battery_timer_init(void)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_create(&m_battery_loop_timer_id, APP_TIMER_MODE_REPEATED, battery_loop));
|
||||
APP_ERROR_CHECK(app_timer_create(&m_battery_loop_timer_id, APP_TIMER_MODE_REPEATED, battery_loop));
|
||||
}
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,74 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* @file cmd_parse.h
|
||||
* @brief
|
||||
******************************************************************************/
|
||||
#ifndef _CMD_PARSE_H_
|
||||
#define _CMD_PARSE_H_
|
||||
#include "sdk_config.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "nordic_common.h"
|
||||
#include "nrf.h"
|
||||
#include "ble_hci.h"
|
||||
#include "ble_advdata.h"
|
||||
#include "ble_advertising.h"
|
||||
#include "ble_conn_params.h"
|
||||
#include "nrf_sdh.h"
|
||||
#include "nrf_sdh_soc.h"
|
||||
#include "nrf_sdh_ble.h"
|
||||
#include "nrf_ble_gatt.h"
|
||||
#include "nrf_ble_qwr.h"
|
||||
#include "app_timer.h"
|
||||
#include "ble_nus.h"
|
||||
#include "app_uart.h"
|
||||
#include "app_util_platform.h"
|
||||
#include "bsp_btn_ble.h"
|
||||
#include "nrf_pwr_mgmt.h"
|
||||
#include "nrf_delay.h"
|
||||
#include "math.h"
|
||||
#include "crc16.h" //add 25.04.23
|
||||
#include "nrf_ble_lesc.h"
|
||||
#include "nrf_crypto.h"
|
||||
#include "nrf_pwr_mgmt.h"
|
||||
#include <ctype.h>
|
||||
|
||||
#include "system_interface.h"
|
||||
#include "main.h"
|
||||
#include "app_raw_main.h" //0117
|
||||
#include "main_timer.h"
|
||||
#include "power_control.h"
|
||||
#include "tmp235_q1.h"
|
||||
//#include "fstorage.h"
|
||||
#include "battery_saadc.h"
|
||||
typedef struct {
|
||||
char tag[5]; // Null-terminated 4-char command
|
||||
uint16_t value0; // Data value
|
||||
uint16_t value1; // Data value
|
||||
uint16_t value2; // Data value
|
||||
uint16_t value3; // Data value
|
||||
uint16_t value4; // Data value
|
||||
char value_ascii[13];
|
||||
uint8_t values[24]; // data vlue
|
||||
// uint16_t crc; // CRC value
|
||||
} ParsedCmd;
|
||||
uint32_t serial_to_passkey_hash(const char *input);
|
||||
void received_command_process(uint8_t const *data_array, which_cmd_t cmd_t,uint8_t length);
|
||||
bool length_error(const char *cmd , uint8_t target_length, uint8_t length);
|
||||
bool parse_cmd(const uint8_t *buffer, ParsedCmd *cmd_out,uint8_t length);
|
||||
bool activate_error(const char *cmd , bool device_status);
|
||||
void param_error(const char *cmd );
|
||||
void quest_error(const char *cmd );
|
||||
bool is_valid_serial_no(const char *serial);
|
||||
|
||||
|
||||
bool is_valid_passkey(const char *passkey);
|
||||
|
||||
bool crc16_check(uint8_t const * p_data, uint32_t data_len, uint16_t expected_crc);
|
||||
bool crc16_check_packet(uint8_t const * packet, uint32_t packet_len);
|
||||
#endif /* */
|
||||
|
||||
@@ -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, ®, 1, true);
|
||||
ret = icm42670_twi_tx(IMU_I2C_ADDR, ®, 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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,74 +3,164 @@
|
||||
* @author CandyPops Co.
|
||||
* @version V1.0.0
|
||||
* @date 2022-09-05
|
||||
* @brief
|
||||
* @brief VesiScan BASIC 메인 헤더 파일
|
||||
*
|
||||
* [시스템 개요]
|
||||
* VesiScan BASIC은 nRF52840 기반 BLE 방광 모니터링 패치 디바이스이다.
|
||||
* 본 헤더는 시스템 전역에서 사용하는 열거형, 함수 선언, 전역 변수를 정의한다.
|
||||
*
|
||||
* [통신 방식]
|
||||
* - BLE NUS (Nordic UART Service): 스마트폰 앱과 바이너리 프로토콜 통신
|
||||
* - 물리 UART (1Mbps): 디버그 및 공장 테스트용
|
||||
*
|
||||
* [데이터 전송 흐름]
|
||||
* 1. 앱/UART에서 명령 수신 → received_command_process()
|
||||
* 2. 센서 데이터 수집 (배터리, 온도, IMU, 압력)
|
||||
* 3. format_data() 계열 함수로 바이너리 패킷 생성
|
||||
* 4. dr_binary_tx_safe()로 CRC16 추가 후 BLE 전송
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef MAIN_H__
|
||||
#define MAIN_H__
|
||||
|
||||
/*==============================================================================
|
||||
* DATA LENGTH CONSTANTS
|
||||
* 데이터 길이 상수
|
||||
*============================================================================*/
|
||||
#define SERIAL_NO_LENGTH 12
|
||||
#define HW_NO_LENGTH 12
|
||||
#define PASSKEY_LENGTH 6
|
||||
#define SERIAL_NO_LENGTH 12 /* 시리얼 번호 길이 (예: "VB026030000") */
|
||||
#define HW_NO_LENGTH 12 /* 하드웨어 번호(버전) 길이 */
|
||||
#define PASSKEY_LENGTH 6 /* BLE 페어링 패스키 길이 (숫자 6자리) */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
#include "boards.h"
|
||||
|
||||
typedef enum
|
||||
/*==============================================================================
|
||||
* 열거형 정의
|
||||
*============================================================================*/
|
||||
|
||||
/* 디바이스 ON/OFF 제어용 열거형 (EEPROM, 전원 등) */
|
||||
typedef enum
|
||||
{
|
||||
OFF = 0,
|
||||
ON = 1
|
||||
OFF = 0, /* 꺼짐 */
|
||||
ON = 1 /* 켜짐 */
|
||||
}on_off_cont_t;
|
||||
|
||||
typedef enum
|
||||
/* 명령 수신 경로 구분 (BLE 또는 UART) */
|
||||
typedef enum
|
||||
{
|
||||
CMD_BLE = 0,
|
||||
CMD_UART = 1
|
||||
CMD_BLE = 0, /* BLE NUS를 통해 수신된 명령 */
|
||||
CMD_UART = 1 /* 물리 UART를 통해 수신된 명령 */
|
||||
}which_cmd_t;
|
||||
|
||||
/* 챔버 자동 테스트 모드 (FEATURE_CHAMBER_AUTO_TEST 활성 시) */
|
||||
#if FEATURE_CHAMBER_AUTO_TEST
|
||||
typedef enum
|
||||
typedef enum
|
||||
{
|
||||
SIMPLE_AUTO_MODE = 0,
|
||||
HALF_AUTO_MODE = 1,
|
||||
FULL_AUTO_MODE = 2,
|
||||
NONE_AUTO_MODE = 3
|
||||
SIMPLE_AUTO_MODE = 0, /* 간단 자동 모드 */
|
||||
HALF_AUTO_MODE = 1, /* 반자동 모드 */
|
||||
FULL_AUTO_MODE = 2, /* 전체 자동 모드 */
|
||||
NONE_AUTO_MODE = 3 /* 자동 모드 없음 */
|
||||
}auto_meas_mode_t;
|
||||
#endif
|
||||
typedef enum
|
||||
|
||||
/* BLE 연결 상태 */
|
||||
typedef enum
|
||||
{
|
||||
BLE_DISCONNECTED_ST = 0,
|
||||
BLE_CONNECTED_ST = 1
|
||||
BLE_DISCONNECTED_ST = 0, /* BLE 미연결 */
|
||||
BLE_CONNECTED_ST = 1 /* BLE 연결됨 */
|
||||
}ble_status_t;
|
||||
|
||||
/*==============================================================================
|
||||
* 함수 선언
|
||||
*============================================================================*/
|
||||
|
||||
#if FEATURE_SECURE_CONNECTION
|
||||
/* BLE 광고 시작 (erase_bonds=true이면 본딩 정보 삭제 후 시작) */
|
||||
static void advertising_start(bool erase_bonds);
|
||||
#endif
|
||||
|
||||
/* 슬립 모드 진입: LED 표시 후 POWER_OFF_DELAY(3초) 후 전원 차단 */
|
||||
void sleep_mode_enter(void);
|
||||
|
||||
/* 전원 OFF 타이머 콜백: POWER_OFF_DELAY 경과 후 실제 전원 차단 */
|
||||
static void t_power_off_timeout_handler(void * p_context);
|
||||
|
||||
/* 디바이스 전원 OFF: LED 표시 후 타이머로 지연 전원 차단 */
|
||||
void device_power_off(void);
|
||||
|
||||
/* 전원 제어 핸들러: POWER_HOLD 핀으로 물리적 전원 ON/OFF */
|
||||
static void power_control_handler(on_off_cont_t device_power_st);
|
||||
|
||||
/* 전원 버튼 상태머신 (타이머 콜백, 5ms 간격):
|
||||
* - 짧은 눌림(<1.5초): 전원 OFF
|
||||
* - 중간 눌림(1.5초~10초): 부팅 시퀀스 시작
|
||||
* - 긴 눌림(>10초): 공장 초기화 (패스키 리셋 + 전원 OFF) */
|
||||
static void main_s(void * p_context);
|
||||
|
||||
/* Peer Manager 타이머 콜백: reset_status==5이면 BLE 연결 강제 해제 */
|
||||
static void PM_s(void * p_context);
|
||||
|
||||
//static void main_re(void * p_context);
|
||||
|
||||
/* 메인 루틴 핸들러 (레거시, 현재 미사용) */
|
||||
static void main_routine_handler(void * p_context);
|
||||
void received_command_process(uint8_t const *data_array, which_cmd_t cmd_t,uint8_t length);
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
* 데이터 전송 함수
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
/* ASCII 텍스트 BLE 전송 ('\r'까지 전송, CRC16 자동 추가) */
|
||||
void data_tx_handler(char const *p_data_to_send);
|
||||
|
||||
/* 바이너리 데이터 BLE 안전 전송 (CRC16 자동 추가, 재시도 로직 포함)
|
||||
* @param ble_bin_buff 전송할 바이너리 버퍼
|
||||
* @param length 데이터 길이 (uint16_t 워드 단위, 실제 바이트 = length × 2) */
|
||||
void dr_binary_tx_safe(uint8_t const *ble_bin_buff, uint16_t length);
|
||||
|
||||
/* SoftDevice 호환 딜레이 (nrf_delay_ms 래퍼) */
|
||||
void dr_sd_delay_ms(uint32_t ms);
|
||||
|
||||
/*------------------------------------------------------------------------------
|
||||
* 바이너리 패킷 포맷 함수
|
||||
* 패킷 구조: [4바이트 태그][데이터][2바이트 CRC16]
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
/* 단일 uint16_t 값 포맷: [tag 4B][value 2B] */
|
||||
void single_format_data(uint8_t *buffer, const char *tag, const uint16_t value) ;
|
||||
|
||||
/* uint16_t 배열 포맷: [tag 4B][data0 2B][data1 2B]... */
|
||||
void format_data(uint8_t *buffer, const char *tag, const uint16_t *data_array, size_t length);
|
||||
|
||||
/* uint8_t 바이트 배열 포맷: [tag 4B][byte0][byte1]... */
|
||||
void format_data_byte(uint8_t *buffer, const char *tag, const uint8_t *data_array, size_t length);
|
||||
void ascii_format_data(uint8_t *buffer, const char *tag, const char *data_ascii, size_t length);
|
||||
|
||||
extern volatile bool data_tx_in_progress;
|
||||
extern volatile bool ble_connection_st;
|
||||
extern volatile bool processing;
|
||||
/* ASCII 문자열 포맷: [tag 4B][char0][char1]... */
|
||||
void ascii_format_data(uint8_t *buffer, const char *tag, const char *data_ascii, size_t length);
|
||||
|
||||
/*==============================================================================
|
||||
* 전역 변수 (extern)
|
||||
*============================================================================*/
|
||||
extern volatile bool data_tx_in_progress; /* BLE TX 전송 진행 중 플래그 */
|
||||
extern volatile bool ble_connection_st; /* BLE 연결 상태 (0=미연결, 1=연결) */
|
||||
extern volatile bool processing; /* 센서 데이터 처리 중 플래그 (중복 명령 방지) */
|
||||
|
||||
/* 2026-03-17: cmd_parse.c에서 main.c로 이동한 전역변수 */
|
||||
extern char SERIAL_NO[SERIAL_NO_LENGTH]; /* 시리얼 번호 */
|
||||
extern char HW_NO[HW_NO_LENGTH]; /* 하드웨어 번호 */
|
||||
extern char m_static_passkey[PASSKEY_LENGTH]; /* BLE 정적 패스키 */
|
||||
extern bool bond_data_delete; /* 본딩 데이터 삭제 요청 플래그 */
|
||||
extern uint32_t m_life_cycle; /* 디바이스 수명 사이클 카운터 */
|
||||
extern uint8_t resetCount; /* 통신 타임아웃 카운터 */
|
||||
extern bool info4; /* 추가 정보 포함 측정 플래그 */
|
||||
extern uint8_t m_reset_status; /* 리셋 상태 코드 */
|
||||
|
||||
extern uint8_t ble_bin_buffer[]; /* BLE 바이너리 응답 버퍼 */
|
||||
|
||||
/* 에러 응답 전송 */
|
||||
void param_error(const char *cmd);
|
||||
|
||||
#endif //MAIN_H__
|
||||
|
||||
|
||||
@@ -1,5 +1,32 @@
|
||||
/*******************************************************************************
|
||||
TEST medi50 Dec 23
|
||||
*******************************************************************************
|
||||
*
|
||||
* [모듈 개요]
|
||||
* 메인 이벤트 루프 타이머 모듈 (10ms 간격, 싱글샷 모드).
|
||||
*
|
||||
* 센서 데이터 수집 및 시스템 제어 이벤트를 플래그 기반으로 디스패치한다.
|
||||
* app_timer 싱글샷 모드를 사용하므로, 이벤트 처리 완료 후
|
||||
* 필요 시 main_timer_start()로 수동 재시작해야 한다.
|
||||
*
|
||||
* [이벤트 플래그 및 처리 순서]
|
||||
* motion_raw_data_enabled → IMU 데이터 읽기 (icm42670_main 호출)
|
||||
* - motion_data_once == true: 단발성 읽기 (HW I2C 초기화 후 1회)
|
||||
* - motion_data_once == false: 연속 읽기 (BLE 전송 대기 중이 아닐 때)
|
||||
* go_batt → 배터리 전압 측정 (battery_level_meas)
|
||||
* go_temp → 온도 측정 (tmp235_voltage_level_meas)
|
||||
* go_device_power_off → 디바이스 전원 OFF (device_power_off)
|
||||
* go_sleep_mode_enter → 슬립 모드 진입 (sleep_mode_enter)
|
||||
* go_NVIC_SystemReset → NVIC 시스템 리셋
|
||||
*
|
||||
* [info4 모드 측정 순서]
|
||||
* IMU 연속 읽기 → go_batt(배터리) → go_temp(온도) → motion_data_once(IMU 단발)
|
||||
* 온도 측정 완료 시 motion_data_once=true로 설정하여 다시 IMU로 돌아간다.
|
||||
*
|
||||
* [타이머 설정]
|
||||
* - 일반 모드: 10ms 간격 (MAIN_LOOP_INTERVAL)
|
||||
* - FEATURE_DETAIL_VALUE_FULL 모드: 80ms 간격 (디테일 프린트아웃용)
|
||||
*
|
||||
******************************************************************************/
|
||||
|
||||
#include "sdk_common.h"
|
||||
@@ -29,12 +56,15 @@
|
||||
#include "tmp235_q1.h"
|
||||
//#include "fstorage.h"
|
||||
#include "power_control.h"
|
||||
#include <cmd_parse.h>
|
||||
#include "main.h" /* 2026-03-17: cmd_parse.h 삭제 → main.h */
|
||||
#include "debug_print.h"
|
||||
#include "i2c_manager.h" //add cj
|
||||
|
||||
/* 메인 루프 싱글샷 타이머 인스턴스 */
|
||||
APP_TIMER_DEF(m_main_loop_timer_id);
|
||||
|
||||
#if FEATURE_DETAIL_VALUE_FULL
|
||||
/* 디테일 프린트아웃 모드: 80ms 간격으로 메인 루프 실행 */
|
||||
#define MAIN_LOOP_INTERVAL 80 /* 디테일 프린트아웃이 있을경우 Full_Mode Main Prosessing 수행하는 타이머 */
|
||||
|
||||
//extern bool pd_adc_full_a_start;
|
||||
@@ -45,16 +75,37 @@ APP_TIMER_DEF(m_main_loop_timer_id);
|
||||
|
||||
extern which_cmd_t cmd_type_t;
|
||||
#else
|
||||
/* 일반 모드: 10ms 간격으로 메인 루프 실행 */
|
||||
#define MAIN_LOOP_INTERVAL 10
|
||||
#endif
|
||||
bool go_batt= false;
|
||||
bool go_temp= false;
|
||||
bool go_device_power_off = false;
|
||||
bool go_sleep_mode_enter = false;
|
||||
bool go_NVIC_SystemReset = false;
|
||||
bool motion_raw_data_enabled = false;
|
||||
bool ble_got_new_data = false;
|
||||
bool motion_data_once = false;
|
||||
|
||||
/* ========================================================================== */
|
||||
/* 이벤트 플래그 (외부 모듈에서 설정, main_loop에서 처리) */
|
||||
/* ========================================================================== */
|
||||
bool go_batt= false; /* 배터리 측정 요청 플래그 */
|
||||
bool go_temp= false; /* 온도 측정 요청 플래그 */
|
||||
bool go_device_power_off = false; /* 디바이스 전원 OFF 요청 플래그 */
|
||||
bool go_sleep_mode_enter = false; /* 슬립 모드 진입 요청 플래그 */
|
||||
bool go_NVIC_SystemReset = false; /* 시스템 리셋 요청 플래그 */
|
||||
bool motion_raw_data_enabled = false; /* IMU 모션 데이터 읽기 활성화 플래그 */
|
||||
bool ble_got_new_data = false; /* BLE로 새 데이터 전송 완료 여부 */
|
||||
bool motion_data_once = false; /* IMU 단발성 읽기 모드 (true: 1회만 읽기) */
|
||||
|
||||
/**
|
||||
* @brief 메인 이벤트 루프 (싱글샷 타이머 콜백)
|
||||
*
|
||||
* 플래그 기반 이벤트 디스패처로, 설정된 플래그에 따라 해당 처리를 수행한다.
|
||||
* 싱글샷 타이머이므로 연속 실행이 필요한 경우 처리 내부에서 main_timer_start()를
|
||||
* 다시 호출하여 타이머를 재시작해야 한다.
|
||||
*
|
||||
* [처리 우선순위] (코드 순서대로 검사)
|
||||
* 1. IMU 모션 데이터 (motion_raw_data_enabled)
|
||||
* 2. 배터리 측정 (go_batt)
|
||||
* 3. 온도 측정 (go_temp)
|
||||
* 4. 전원 OFF (go_device_power_off)
|
||||
* 5. 슬립 모드 (go_sleep_mode_enter)
|
||||
* 6. 시스템 리셋 (go_NVIC_SystemReset)
|
||||
*/
|
||||
void main_loop(void * p_context) /* For x ms */
|
||||
{
|
||||
UNUSED_PARAMETER(p_context);
|
||||
@@ -87,27 +138,38 @@ void main_loop(void * p_context) /* For x ms */
|
||||
// }
|
||||
#endif
|
||||
|
||||
// For Motion Data Sampling
|
||||
// For Motion Data Sampling
|
||||
|
||||
/* ---- IMU 모션 데이터 읽기 ---- */
|
||||
/*
|
||||
* motion_raw_data_enabled가 true이면 IMU(ICM42670P) 데이터를 읽는다.
|
||||
*
|
||||
* motion_data_once == true:
|
||||
* 단발성 읽기 모드. HW I2C를 초기화한 후 icm42670_main()을 1회 호출.
|
||||
* info4 모드에서 배터리/온도 측정 후 다시 IMU로 돌아올 때 사용.
|
||||
*
|
||||
* motion_data_once == false:
|
||||
* 연속 읽기 모드. BLE 전송 대기 중(ble_got_new_data==false)이면
|
||||
* icm42670_main()을 호출하고 10ms 후 타이머를 재시작하여 반복 실행.
|
||||
*/
|
||||
if(motion_raw_data_enabled == true) {
|
||||
main_timer_stop();
|
||||
main_timer_stop(); /* 타이머 정지 (재진입 방지) */
|
||||
if(motion_data_once == true)
|
||||
{
|
||||
|
||||
hw_i2c_init_once();
|
||||
icm42670_main();
|
||||
/* 단발성 모드: HW I2C 초기화 후 IMU 데이터 1회 읽기 */
|
||||
hw_i2c_init_once(); /* HW TWI 모드로 전환 (400kHz) */
|
||||
icm42670_main(); /* IMU 데이터 읽기 및 처리 */
|
||||
}
|
||||
else{
|
||||
|
||||
/* 연속 모드: BLE 전송 대기 중이 아니면 반복 읽기 */
|
||||
if(ble_got_new_data==false){
|
||||
//for(uint16_t i=0 ; i<60 ;i++)
|
||||
//for(uint16_t i=0 ; i<60 ;i++)
|
||||
//{
|
||||
DBG_PRINTF("IMU \r\n");
|
||||
|
||||
icm42670_main();
|
||||
nrf_delay_ms(10);
|
||||
motion_raw_data_enabled = true;
|
||||
main_timer_start();
|
||||
|
||||
icm42670_main(); /* IMU 데이터 읽기 */
|
||||
motion_raw_data_enabled = true; /* 플래그 유지 (연속 읽기) */
|
||||
main_timer_start_ms(1000); /* 1초 후 다음 IMU 읽기 */
|
||||
}
|
||||
// else if(ble_got_new_data==true){
|
||||
// motion_data_once = true;
|
||||
@@ -116,73 +178,113 @@ void main_loop(void * p_context) /* For x ms */
|
||||
}
|
||||
|
||||
|
||||
/* ---- 배터리 전압 측정 ---- */
|
||||
/*
|
||||
* go_batt 플래그가 true이면 배터리 레벨을 측정한다.
|
||||
* info4 모드에서 IMU 연속 읽기 이후 호출되는 단계.
|
||||
* 측정 완료 후 타이머가 정지된 상태로 유지된다.
|
||||
*/
|
||||
if(go_batt == true) {
|
||||
DBG_PRINTF("IMU BATT\r\n");
|
||||
main_timer_stop();
|
||||
go_batt = false;
|
||||
main_timer_stop(); /* 타이머 정지 */
|
||||
go_batt = false; /* 플래그 소비 (1회 실행) */
|
||||
// go_temp = true;
|
||||
battery_level_meas();
|
||||
battery_level_meas(); /* SAADC를 이용한 배터리 전압 측정 */
|
||||
// nrf_delay_ms(20);
|
||||
// m48_adc_start_init();
|
||||
// main_timer_start();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* ---- 온도 측정 ---- */
|
||||
/*
|
||||
* go_temp 플래그가 true이면 TMP235-Q1 센서로 온도를 측정한다.
|
||||
* info4 모드에서 배터리 측정 이후 호출되는 단계.
|
||||
* 측정 완료 후 motion_data_once=true로 설정하여
|
||||
* 다음 IMU 읽기는 단발성 모드(HW I2C 재초기화)로 전환된다.
|
||||
*/
|
||||
if(go_temp == true) {
|
||||
DBG_PRINTF("IMU Temp\r\n");
|
||||
main_timer_stop();
|
||||
main_timer_stop(); /* 타이머 정지 */
|
||||
// go_batt = false;
|
||||
go_temp = false;
|
||||
motion_data_once = true;
|
||||
tmp235_voltage_level_meas();
|
||||
go_temp = false; /* 플래그 소비 (1회 실행) */
|
||||
motion_data_once = true; /* 다음 IMU 읽기를 단발성 모드로 전환 */
|
||||
tmp235_voltage_level_meas(); /* TMP235-Q1 온도 센서 전압 측정 */
|
||||
// motion_raw_data_enabled = true;
|
||||
// main_timer_start();
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* For System Control */
|
||||
|
||||
|
||||
/* ---- 시스템 제어 이벤트 처리 ---- */
|
||||
|
||||
|
||||
/* 디바이스 전원 OFF 처리 */
|
||||
if(go_device_power_off == true){
|
||||
main_timer_stop();
|
||||
main_timer_stop(); /* 타이머 정지 */
|
||||
|
||||
DBG_PRINTF("Off main_timer\r\n");
|
||||
device_power_off();
|
||||
device_power_off(); /* 디바이스 전원 OFF 실행 */
|
||||
}
|
||||
|
||||
/* 슬립 모드 진입 처리 */
|
||||
if(go_sleep_mode_enter == true){
|
||||
main_timer_stop();
|
||||
main_timer_stop(); /* 타이머 정지 */
|
||||
|
||||
DBG_PRINTF("sleep main timer\r\n");
|
||||
|
||||
|
||||
sleep_mode_enter();
|
||||
|
||||
|
||||
sleep_mode_enter(); /* 슬립 모드 진입 실행 */
|
||||
}
|
||||
|
||||
/* NVIC 시스템 리셋 처리 */
|
||||
if(go_NVIC_SystemReset == true) {
|
||||
|
||||
|
||||
|
||||
main_timer_stop();
|
||||
|
||||
NVIC_SystemReset();
|
||||
|
||||
|
||||
|
||||
main_timer_stop(); /* 타이머 정지 */
|
||||
|
||||
NVIC_SystemReset(); /* ARM Cortex-M4 시스템 리셋 */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief 메인 루프 타이머 시작
|
||||
*
|
||||
* 싱글샷 모드로 MAIN_LOOP_INTERVAL(10ms 또는 80ms) 후 main_loop()를 호출한다.
|
||||
*/
|
||||
void main_timer_start(void)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_start(m_main_loop_timer_id, APP_TIMER_TICKS(MAIN_LOOP_INTERVAL), NULL));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 지정된 간격(ms)으로 메인 루프 타이머 시작
|
||||
*
|
||||
* IMU 연속 스트리밍 등 기본 간격과 다른 주기가 필요할 때 사용.
|
||||
*/
|
||||
void main_timer_start_ms(uint32_t interval_ms)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_start(m_main_loop_timer_id, APP_TIMER_TICKS(interval_ms), NULL));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 메인 루프 타이머 정지
|
||||
*/
|
||||
void main_timer_stop(void)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_stop(m_main_loop_timer_id));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 메인 루프 타이머 초기화 (앱 시작 시 1회 호출)
|
||||
*
|
||||
* 싱글샷 모드 타이머를 생성하고, 콜백으로 main_loop()를 등록한다.
|
||||
* 싱글샷이므로 매 호출마다 main_timer_start()로 수동 재시작해야 한다.
|
||||
*/
|
||||
void main_timer_init(void)
|
||||
{
|
||||
APP_ERROR_CHECK(app_timer_create(&m_main_loop_timer_id, APP_TIMER_MODE_SINGLE_SHOT, main_loop));
|
||||
|
||||
@@ -3,11 +3,29 @@
|
||||
* @author CandyPops Co.
|
||||
* @version V1.0.0
|
||||
* @date 2022-09-05
|
||||
* @brief
|
||||
* @brief
|
||||
******************************************************************************/
|
||||
|
||||
/*******************************************************************************
|
||||
* [모듈 개요] TMP235-Q1 아날로그 온도센서 드라이버
|
||||
*
|
||||
* TMP235-Q1은 온도에 비례하는 아날로그 전압(Vout)을 출력하는 센서이다.
|
||||
* nRF52840 SAADC의 AIN3 채널로 Vout을 읽고, mV로 변환한 뒤
|
||||
* 온도(°C)로 계산한다.
|
||||
*
|
||||
* 온도 계산 공식 (구간별 선형 보간):
|
||||
* - Vout <= 1500mV (0~100°C): Ta = (Vout - 500) / 10.0
|
||||
* - Vout <= 1750mV (100~125°C): Ta = (Vout - 1500) / 10.1 + 100
|
||||
* - Vout <= 2000mV (125~150°C): Ta = (Vout - 1752.5) / 10.6 + 125
|
||||
* - Vout > 2000mV: 오류 (150°C 초과)
|
||||
*
|
||||
* info4 모드(전체 센서 수집) 동작 순서:
|
||||
* 배터리(go_batt) → 온도(go_temp) → IMU(motion_raw_data_enabled)
|
||||
* 온도 측정 완료 시 go_temp=false, motion_raw_data_enabled=true 로 전환
|
||||
******************************************************************************/
|
||||
|
||||
#include "sdk_common.h"
|
||||
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
@@ -19,11 +37,14 @@
|
||||
#include "ble_nus.h"
|
||||
#include "tmp235_q1.h"
|
||||
#include "main.h"
|
||||
#include <cmd_parse.h>
|
||||
/* 2026-03-17: cmd_parse.h 삭제 — main.h는 이미 포함됨 */
|
||||
#include "main_timer.h"
|
||||
#include "debug_print.h"
|
||||
/* SAADC 내부 기준전압 600mV (부동소수점) */
|
||||
#define TMP235_REF_VOLTAGE_IN_MILLIVOLTS 600.0f /**< Reference voltage (in milli volts) used by ADC while doing conversion. */
|
||||
/* 1/3 프리스케일링 보상 계수 x6 (부동소수점) */
|
||||
#define TMP235_PRE_SCALING_COMPENSATION 6.0f /**< 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 최대값 1024 (부동소수점, 분해능 기준) */
|
||||
#define TMP235_ADC_RES_10BITS 1024.0f /**< Maximum digital value for 10-bit ADC conversion. */
|
||||
|
||||
/**@brief Macro to convert the result of ADC conversion in millivolts.
|
||||
@@ -32,102 +53,151 @@
|
||||
*
|
||||
* @retval Result converted to millivolts.
|
||||
*/
|
||||
/* ADC 원시값 → TMP235 출력전압(mV) 변환 매크로: ADC x (600/1024) x 6 */
|
||||
#define TMP235_VOUT_IN_MILLI_VOLTS(ADC_VALUE)\
|
||||
((((ADC_VALUE) * TMP235_REF_VOLTAGE_IN_MILLIVOLTS) / TMP235_ADC_RES_10BITS) * TMP235_PRE_SCALING_COMPENSATION)
|
||||
|
||||
/* SAADC 변환 결과 저장 버퍼 (1채널) */
|
||||
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 go_temp; //cmd_parse
|
||||
/* info4 모드에서 온도값을 임시 저장 (°C x 100, 정수 표현) */
|
||||
volatile uint16_t info_temp; //48_C
|
||||
extern bool motion_raw_data_enabled;
|
||||
extern bool motion_raw_data_enabled;
|
||||
|
||||
/**@brief Function for handling the ADC interrupt.
|
||||
*
|
||||
* @details This function will fetch the conversion result from the ADC, convert the value into
|
||||
* percentage and send it to peer.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief TMP235 온도센서 ADC 완료 콜백
|
||||
*
|
||||
* SAADC 변환 완료 시 호출된다.
|
||||
* ADC값 → Vout(mV) → 온도(°C) 순으로 변환하고, 동작 모드에 따라:
|
||||
* - info4 모드: info_temp에 저장 (°C x 100 정수), 이후 IMU 측정으로 전환
|
||||
* - 일반 모드: BLE("rso:" 바이너리) 또는 UART로 온도값 전송
|
||||
*/
|
||||
void tmp235_voltage_handler(nrf_drv_saadc_evt_t const * p_event) /* TMP325 Vout reading */
|
||||
{
|
||||
|
||||
float led_temp;
|
||||
uint16_t led_temp_16;
|
||||
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;
|
||||
float tmp235_voltage_in_milli_volts = 0;
|
||||
|
||||
/* ADC 변환 결과 읽기 */
|
||||
adc_result = p_event->data.done.p_buffer[0];
|
||||
|
||||
/* SAADC 해제 — 배터리/압력센서 측정과 하드웨어 공유 */
|
||||
nrf_drv_saadc_uninit();
|
||||
nrf_drv_saadc_channel_uninit(0);
|
||||
|
||||
/* ADC값 → TMP235 출력전압(mV) 변환 */
|
||||
tmp235_voltage_in_milli_volts = TMP235_VOUT_IN_MILLI_VOLTS(adc_result);
|
||||
|
||||
/*
|
||||
* Vout → 온도(°C) 변환 (구간별 선형 보간)
|
||||
* TMP235 데이터시트 기반:
|
||||
* 0~100°C 구간: 기울기 10.0 mV/°C, 오프셋 500mV
|
||||
* 100~125°C 구간: 기울기 10.1 mV/°C
|
||||
* 125~150°C 구간: 기울기 10.6 mV/°C
|
||||
*/
|
||||
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) {
|
||||
/* 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) {
|
||||
/* 125~150°C: 기울기가 10.6으로 더 증가 */
|
||||
led_temp = (tmp235_voltage_in_milli_volts - 1752.5f) / 10.6f + 125.0f;
|
||||
}else {
|
||||
/* 150°C 초과 — 센서 측정 범위 벗어남 */
|
||||
DBG_PRINTF("ERR!!! Temprature is over 150c\r\n");
|
||||
}
|
||||
|
||||
/* info4 모드: 온도값을 정수(°C x 100)로 저장 (예: 36.50°C → 3650) */
|
||||
if (info4 == true){
|
||||
|
||||
|
||||
info_temp =(uint16_t)(led_temp*100);
|
||||
|
||||
|
||||
}
|
||||
/* UART 모드: 소수점 2자리까지 텍스트로 출력 */
|
||||
else if(cmd_type_t == CMD_UART) {
|
||||
DBG_PRINTF("To%.2f\r\n\r\n",led_temp);
|
||||
} else if(cmd_type_t == CMD_BLE) {
|
||||
/* 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);
|
||||
|
||||
|
||||
dr_binary_tx_safe(ble_bin_buffer,3);
|
||||
|
||||
|
||||
// sprintf(ble_tx_buffer, "To%.2f\r\n",led_temp);
|
||||
// data_tx_handler(ble_tx_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (info4 == true){
|
||||
|
||||
go_temp = false;
|
||||
|
||||
|
||||
motion_raw_data_enabled = true;
|
||||
main_timer_start();
|
||||
/* info4 모드: 온도 측정 완료 → 다음 단계(IMU 측정)로 전환 */
|
||||
if (info4 == true){
|
||||
|
||||
go_temp = false; /* 온도 측정 완료 표시 */
|
||||
|
||||
|
||||
motion_raw_data_enabled = true; /* IMU 데이터 수집 시작 플래그 */
|
||||
main_timer_start(); /* 메인 타이머 시작 → IMU 측정 트리거 */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief TMP235 온도센서 SAADC 초기화 및 측정 시작
|
||||
*
|
||||
* AIN3 채널을 싱글엔드 모드로 설정하고 즉시 샘플링을 트리거한다.
|
||||
* 결과는 tmp235_voltage_handler 콜백에서 비동기로 처리된다.
|
||||
*/
|
||||
void tmp235_init(void)
|
||||
{
|
||||
/* SAADC 드라이버 초기화 (기본 설정 + 온도센서 이벤트 핸들러 등록) */
|
||||
ret_code_t err_code = nrf_drv_saadc_init(NULL, tmp235_voltage_handler);
|
||||
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 */
|
||||
err_code = nrf_drv_saadc_channel_init(0, &config);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
/* ADC 버퍼 등록 (1채널, 1샘플) */
|
||||
err_code = nrf_drv_saadc_buffer_convert(&adc_buf, 1);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
|
||||
/* 즉시 ADC 샘플링 시작 (비동기) */
|
||||
err_code = nrf_drv_saadc_sample();
|
||||
APP_ERROR_CHECK(err_code);
|
||||
APP_ERROR_CHECK(err_code);
|
||||
}
|
||||
|
||||
|
||||
/* Ta = (Vout – Voffs ) / Tc + Tinfl */
|
||||
/**
|
||||
* @brief 온도 측정 외부 호출 함수
|
||||
*
|
||||
* tmp235_init()을 호출하여 SAADC 초기화 + 측정을 일괄 수행한다.
|
||||
* 내부적으로 init 시 바로 샘플링이 시작되므로 별도 sample 호출 불필요.
|
||||
*/
|
||||
void tmp235_voltage_level_meas(void)
|
||||
{
|
||||
tmp235_init();
|
||||
//tmp235_uninit();
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user