/******************************************************************************* * @file battery_saadc.c * @author CandyPops Co. * @version V1.0.0 * @date 2022-09-05 * @brief ******************************************************************************/ /******************************************************************************* * [모듈 개요] 배터리 전압 ADC 측정 모듈 * * nRF52840의 SAADC(Successive Approximation ADC)를 사용하여 배터리 전압을 측정: * - AIN2 채널, 10bit 해상도 * - 5초 주기 타이머(battery_loop)로 반복 측정 * - 저전압(3500mV 이하) 10회 연속 감지 시 자동 전원 OFF * - info4 모드(전체 센서 수집)에서는 info_batt에 저장 후 온도 측정으로 전환 * * 배터리 전압 변환 공식: * 전압(mV) = ADC값 x (600mV / 1023) x 6 x 1.42 (분압 저항 보정 계수) * * info4 모드 순서: 배터리 -> 온도(go_temp) -> IMU(motion_raw_data_enabled) ******************************************************************************/ #include "sdk_common.h" #include #include #include "nrf.h" #include "boards.h" #include "app_error.h" #include "nrf_drv_saadc.h" #include "nrf_drv_timer.h" #include "ble_nus.h" #include "nrf_log.h" #include "main.h" #include "app_timer.h" //#include "fstorage.h" #include "battery_saadc.h" #include "main_timer.h" #include "main.h" #include "debug_print.h" /* SAADC 내부 기준전압 600mV (부동소수점) */ #define BATTERY_REF_VOLTAGE_IN_MILLIVOLTS 600.0f /**< Reference voltage (in milli volts) used by ADC while doing conversion. */ /* 1/3 프리스케일링 보상 계수 (입력 전압을 1/3로 분압하므로 x3, 추가 x2 = 총 x6) (부동소수점) */ #define BATTERY_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.*/ /* 12비트 ADC 최대 디지털 값 (부동소수점) */ #define BATTERY_ADC_RES_12BITS 4095.0f /**< Maximum digital value for 12-bit ADC conversion. */ /**@brief Macro to convert the result of ADC conversion in millivolts. * * @param[in] ADC_VALUE ADC result. * * @retval Result converted to millivolts. */ #define BATTERY_RESULT_IN_MILLI_VOLTS(ADC_VALUE)\ ((((ADC_VALUE) * BATTERY_REF_VOLTAGE_IN_MILLIVOLTS) / BATTERY_ADC_RES_12BITS) * BATTERY_PRE_SCALING_COMPENSATION) /* 배터리 측정용 싱글 버퍼 (1회 측정 후 uninit하므로 더블 버퍼 불필요) */ static nrf_saadc_value_t adc_buf; //static nrf_saadc_value_t adc_bufs[2]; // 이전: 더블 버퍼 (연속 측정용) /* 배터리 모니터링 반복 타이머 정의 */ APP_TIMER_DEF(m_battery_loop_timer_id); /* 배터리 측정 주기: 5초 (밀리초 단위) */ #define BATTERY_LOOP_INTERVAL 5000 /* 저전압 체크 플래그 — battery_loop에서 true로 설정, 핸들러에서 소비 */ bool low_battery_check = false; /* SAADC 콜백 완료 플래그 — all_sensors()에서 배터리 측정 완료 대기용 */ volatile bool battery_saadc_done = false; /* info4: 전체 센서 데이터 수집 모드 플래그 */ extern bool info4; // main.c 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 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 * percentage and send it to peer. */ /** * @brief 배터리 전압 ADC 완료 콜백 * * SAADC 변환 완료 시 호출된다. * ADC 값을 실제 배터리 전압(mV)으로 변환하고, 동작 모드에 따라: * - 저전압 체크 모드: 3500mV 이하 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; float batt_lvl_in_milli_volt_0 = 0; /* 보정 전 전압 (부동소수점) */ float batt_lvl_in_milli_volt_1 = 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 측정(온도, 압력)과 하드웨어 공유 */ /* 1회 측정 후 해제이므로 buffer_convert(다음 버퍼 등록) 불필요 */ nrf_drv_saadc_channel_uninit(0); nrf_drv_saadc_uninit(); /* 콜백 완료 알림 (all_sensors 대기 해제용) */ if (info4) DBG_PRINTF("2"); battery_saadc_done = true; /* 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.42f; /* === 저전압 체크 모드 (battery_loop 타이머에서 설정) === */ if(low_battery_check == true) { low_battery_check = false; /* 배터리 전압이 LOW_BATTERY_VOLTAGE(3500mV) 이하인지 확인 */ 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 */ DBG_PRINTF("Save FDS parameters and then Power OFF\r\n"); 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, (int)batt_lvl_in_milli_volt_1); } } } /* === info4 모드: 전체 센서 수집(mbb) 중 배터리 값 저장 === */ else if (info4 == true) { info_batt = batt_lvl_in_milli_volt_1; } /* === 일반 모드: 단독 배터리 측정 요청(msn)에 대한 응답 전송 === */ else { if (cmd_type_t == CMD_UART) { DBG_PRINTF("Tn%d\r\n\r\n", (int)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); //data_tx_handler(ble_tx_buffer); } } } } /** * @brief SAADC를 배터리 전압 측정용으로 설정 * * AIN2 채널을 싱글엔드(SE) 모드, 1/3 프리스케일링으로 초기화한다. * 더블 버퍼(adc_bufs[0], [1])를 등록하여 연속 측정이 가능하도록 한다. * 콜백: battery_event_handler */ static void battery_configure(void) { /* SAADC 드라이버 초기화 (4x 오버샘플링으로 노이즈 저감) */ nrf_drv_saadc_config_t saadc_config = NRF_DRV_SAADC_DEFAULT_CONFIG; saadc_config.resolution = NRF_SAADC_RESOLUTION_12BIT; // 10 -> 12bit saadc_config.oversample = NRF_SAADC_OVERSAMPLE_4X; ret_code_t err_code = nrf_drv_saadc_init(&saadc_config, battery_event_handler); APP_ERROR_CHECK(err_code); /* AIN2 채널 설정: 싱글엔드 입력, 1/6 gain, burst + TACQ 20μs */ nrf_saadc_channel_config_t config = NRF_DRV_SAADC_DEFAULT_CHANNEL_CONFIG_SE(NRF_SAADC_INPUT_AIN2); config.burst = NRF_SAADC_BURST_ENABLED; config.acq_time = NRF_SAADC_ACQTIME_10US; err_code = nrf_drv_saadc_channel_init(0, &config); APP_ERROR_CHECK(err_code); /* 싱글 버퍼 등록 (1회 측정 후 uninit) */ err_code = nrf_drv_saadc_buffer_convert(&adc_buf, 1); APP_ERROR_CHECK(err_code); //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 배터리 전압 1회 측정 시작 * * SAADC를 배터리용으로 설정 후 샘플링을 트리거한다. * 결과는 battery_event_handler 콜백에서 비동기로 처리된다. */ void battery_level_meas(void) { ret_code_t err_code; battery_configure(); /* SAADC 배터리용 초기화 */ 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); /* 다른 센서 처리 중 또는 MBB 센서 수집 중이면 배터리 측정 스킵 (SAADC 충돌 방지) */ if (processing == true || info4 == true) { processing = false ; // add 20241218 //low_battery_check = true; return; } else { 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)); } /** @brief 배터리 모니터링 타이머 정지 */ void battery_timer_stop(void) { 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)); }