배터리/온도 보호 판정 정책 변경

1. 정렬모드    - 판정하지 않음2. 연속 측정 모드    - 측정 응답에 포함되는 배터리/IMU 온도 값으로 저전압/고온 카운트 및 판정    - 배터리 3500mV 이하 또는 온도 40℃ 이상 30회 연속인 경우 약 5분 후 전원 OFF    - FW 내부 60초 주기 모니터는 skip    - 측정에 실패하는 경우 저전압/고온 카운트는 유지3. 정렬모드도 연속 측정 모드도 아닌 경우    - FW 내부 60초 주기 모니터로 저전압/고온 카운트 및 판정    - 배터리 3500mV 이하 또는 온도 40℃ 이상 5회 연속인 경우 약 5분 후 전원 OFF
This commit is contained in:
2026-06-29 16:50:21 +09:00
parent ad38741b46
commit 749e1d4e0e
3 changed files with 249 additions and 66 deletions
+175 -43
View File
@@ -19,6 +19,7 @@
#include <zephyr/drivers/adc.h>
#include <zephyr/devicetree.h>
#include <string.h>
#include <limits.h>
#include "battery_adc.h"
#include "debug_print.h"
@@ -43,11 +44,37 @@ volatile uint16_t info_batt = 0;
static struct k_timer battery_timer;
static struct k_work battery_work;
static uint8_t low_battery_cnt = 0;
static uint8_t high_temperature_cnt = 0;
#define BATTERY_MONITOR_LIMIT_COUNT 5U
#define HIGH_TEMPERATURE_CDEG 4000 /* 40.00 C */
/* 60초 내부 모니터 전용 카운터
* - 앱 측정 명령(mbb?)이 없는 일반 상태에서만 사용
* - mbb? 성공 시 연속 측정 판정으로 넘어가므로 0으로 초기화
*/
static uint8_t periodic_low_battery_cnt;
static uint8_t periodic_high_temperature_cnt;
/* 연속 측정(mbb?) 응답값 전용 카운터
* - mbb? 응답에 포함된 배터리/IMU 온도 값으로 판정
* - 측정 실패 시 호출되지 않으므로 기존 카운트 유지
*/
static uint8_t continuous_low_battery_cnt;
static uint8_t continuous_high_temperature_cnt;
/* 정렬 상태 보호 판정 차단 플래그
* - mls?로 ALIGN_SEARCHING/ALIGN_COMPLETE 진입 시 true
* - 정렬 중에는 배터리/온도 보호 판정 자체를 진행하지 않음
*/
static bool alignment_mode_active;
/* 최근 mbb? 성공 이후 내부 60초 모니터를 잠시 막는 기준 시각
* - 연속 측정 중에는 mbb? 응답값으로 보호 판정
* - 이 값보다 현재 시간이 작으면 60초 내부 모니터는 skip
*/
static int64_t continuous_monitor_active_until_ms;
#define BATTERY_MONITOR_LIMIT_COUNT 5U
#define BATTERY_CONTINUOUS_MONITOR_LIMIT_COUNT 30U
#define HIGH_TEMPERATURE_CDEG 4000 /* 40.00 C */
#define CONTINUOUS_MONITOR_ACTIVE_MS 10000
#define BATTERY_MONITOR_INTERVAL_MS 60000 /* 60초 주기 */
@@ -67,6 +94,77 @@ static int adc_raw_to_mv(int16_t raw)
return (int)mv;
}
static bool continuous_monitor_is_active(void)
{
/* 마지막 mbb? 성공 이후 CONTINUOUS_MONITOR_ACTIVE_MS 안쪽이면 연속 측정 중으로 간주 */
return (continuous_monitor_active_until_ms > 0) &&
(k_uptime_get() < continuous_monitor_active_until_ms);
}
static void record_battery_value(int batt_mv, uint8_t *cnt,
uint8_t limit, const char *mode)
{
/* mode별 카운터만 전달받아 공통 판정 로직 사용
* - periodic: 60초 내부 모니터, limit 5
* - continuous: mbb? 응답값 기반, limit 30
*/
if (batt_mv <= LOW_BATTERY_VOLTAGE)
{
if (*cnt < UINT8_MAX)
{
(*cnt)++;
}
DBG_PRINTF("[BATT] LOW! mode=%s cnt=%u/%u mv=%d\r\n",
mode, *cnt, limit, batt_mv);
if (*cnt >= limit)
{
*cnt = 0;
DBG_ERR("[BATT] %ux low (%s) -> Power OFF\r\n", limit, mode);
sleep_mode_enter_reason("battery-low");
}
}
else
{
*cnt = 0;
}
}
static void record_temperature_value(int16_t temp_cdeg, uint8_t *cnt,
uint8_t limit, const char *mode)
{
/* IMU 온도는 centi-degree 단위
* - 4000 = 40.00 C
* - 정상 온도 수신 시 기준 이하이면 해당 mode 카운터 초기화
*/
if (temp_cdeg > HIGH_TEMPERATURE_CDEG)
{
if (*cnt < UINT8_MAX)
{
(*cnt)++;
}
DBG_PRINTF("[TEMP] HIGH! mode=%s cnt=%u/%u temp=%d.%02d C\r\n",
mode,
*cnt,
limit,
temp_cdeg / 100,
(temp_cdeg < 0 ? -temp_cdeg : temp_cdeg) % 100);
if (*cnt >= limit)
{
*cnt = 0;
DBG_ERR("[TEMP] %ux high (%s) -> Power OFF\r\n", limit, mode);
sleep_mode_enter_reason("temperature-high");
}
}
else
{
*cnt = 0;
}
}
/*==============================================================================
* Public functions
*============================================================================*/
@@ -134,16 +232,72 @@ int battery_read_mv(void)
return mv;
}
void battery_protection_set_alignment_mode(bool enabled)
{
/* 정렬 상태 진입/해제는 mls? 명령에서 전달
* - enabled true: 보호 판정 중지 + 기존 누적값 초기화
* - enabled false: 다음 조건부터 다시 판정 가능
*/
alignment_mode_active = enabled;
if (enabled)
{
periodic_low_battery_cnt = 0;
periodic_high_temperature_cnt = 0;
continuous_low_battery_cnt = 0;
continuous_high_temperature_cnt = 0;
}
}
void battery_protection_record_continuous(int batt_mv, int16_t temp_cdeg)
{
/* mbb? 측정 성공 시에만 호출
* - 실패한 측정은 호출되지 않아서 continuous 카운트 유지
* - 정렬 중에는 응답값이 있어도 보호 판정 제외
*/
if (alignment_mode_active)
{
return;
}
/* mbb?가 들어온 직후에는 내부 60초 모니터 대신 응답값 기반 판정 사용
* - periodic 카운터는 섞이지 않게 초기화
* - continuous 카운터는 아래 record_* 함수에서 값에 따라 증가/초기화
*/
continuous_monitor_active_until_ms = k_uptime_get() + CONTINUOUS_MONITOR_ACTIVE_MS;
periodic_low_battery_cnt = 0;
periodic_high_temperature_cnt = 0;
record_battery_value(batt_mv,
&continuous_low_battery_cnt,
BATTERY_CONTINUOUS_MONITOR_LIMIT_COUNT,
"continuous");
record_temperature_value(temp_cdeg,
&continuous_high_temperature_cnt,
BATTERY_CONTINUOUS_MONITOR_LIMIT_COUNT,
"continuous");
}
/*==============================================================================
* 주기 모니터링 타이머
*============================================================================*/
/** @brief k_work 핸들러 스레드 컨텍스트에서 ADC 읽기 (adc_read는 블로킹이라 ISR 불가) */
/* k_work 핸들러: 스레드 컨텍스트에서 ADC 읽기 (adc_read는 블로킹이라 ISR 불가) */
static void battery_work_handler(struct k_work *work)
{
ARG_UNUSED(work);
/* 보호 판정 제외 구간
* - 정렬 중: 정책상 보호 판정 미진행
* - 최근 mbb? 성공 직후: mbb? 응답값 기반 continuous 판정이 담당
*/
if (alignment_mode_active || continuous_monitor_is_active())
{
return;
}
/* 측정 처리 중이거나 IMU FIFO 사용 중(정렬모드)이면 I2C 접근 충돌 방지 */
if (processing || imu_fifo_is_active())
{
return;
@@ -156,24 +310,14 @@ static void battery_work_handler(struct k_work *work)
return;
}
/* 배터리 저전압 */
if (batt <= LOW_BATTERY_VOLTAGE)
{
low_battery_cnt++;
DBG_PRINTF("[BATT] LOW! cnt=%d mv=%d\r\n", low_battery_cnt, batt);
if (low_battery_cnt >= BATTERY_MONITOR_LIMIT_COUNT)
{
low_battery_cnt = 0;
DBG_ERR("[BATT] %ux low -> Power OFF\r\n", BATTERY_MONITOR_LIMIT_COUNT);
sleep_mode_enter_reason("battery-low");
return;
}
}
else
{
low_battery_cnt = 0;
}
/* 일반 상태의 60초 내부 모니터 판정
* - 저전압이면 periodic_low_battery_cnt 증가
* - 정상 전압이면 periodic_low_battery_cnt 초기화
*/
record_battery_value(batt,
&periodic_low_battery_cnt,
BATTERY_MONITOR_LIMIT_COUNT,
"periodic");
int16_t temp_cdeg = 0;
int temp_ret = imu_read_temperature_cdeg(&temp_cdeg);
@@ -184,26 +328,14 @@ static void battery_work_handler(struct k_work *work)
return;
}
if (temp_cdeg > HIGH_TEMPERATURE_CDEG)
{
high_temperature_cnt++;
DBG_PRINTF("[TEMP] HIGH! cnt=%d temp=%d.%02d C\r\n",
high_temperature_cnt,
temp_cdeg / 100,
(temp_cdeg < 0 ? -temp_cdeg : temp_cdeg) % 100);
if (high_temperature_cnt >= BATTERY_MONITOR_LIMIT_COUNT)
{
high_temperature_cnt = 0;
DBG_ERR("[TEMP] %ux high -> Power OFF\r\n", BATTERY_MONITOR_LIMIT_COUNT);
sleep_mode_enter_reason("temperature-high");
return;
}
}
else
{
high_temperature_cnt = 0;
}
/* 일반 상태의 IMU 온도 판정
* - 고온이면 periodic_high_temperature_cnt 증가
* - 정상 온도이면 periodic_high_temperature_cnt 초기화
*/
record_temperature_value(temp_cdeg,
&periodic_high_temperature_cnt,
BATTERY_MONITOR_LIMIT_COUNT,
"periodic");
}
/* 타이머 ISR → k_work 예약 */