전원 + BLE + 배터리

This commit is contained in:
jhChun
2026-04-10 17:57:09 +09:00
parent 750f2d139e
commit 8dcf4adf31
22 changed files with 1791 additions and 175 deletions
+196
View File
@@ -0,0 +1,196 @@
/*******************************************************************************
* @file battery_adc.c
* @brief Battery voltage ADC measurement (Zephyr devicetree 기반)
*
* ADC 채널 설정은 디바이스트리 overlay에서 관리:
* - AIN2 (P0.04), Single-ended
* - 12-bit 해상도, 1/6 gain, 4X oversample, 40us acq time
*
* 변환 공식: mV = (ADC × 600 / 4095) × 6 × 1.42
* → 프로젝트 고유 분압비(1.42x) 때문에 adc_raw_to_millivolts_dt() 사용 불가
*
* 동작 모드:
* - 단독 측정 (msn? 커맨드) → battery_read_mv() 호출
* - 주기 모니터링 (60초) → 저전압 10회 연속 시 자동 전원 OFF
* - info4 모드 (mbb?) → info_batt에 저장
******************************************************************************/
#include <zephyr/kernel.h>
#include <zephyr/drivers/adc.h>
#include <zephyr/devicetree.h>
#include <string.h>
#include "battery_adc.h"
#include "debug_print.h"
#include "main.h"
/*==============================================================================
* ADC 디바이스트리 설정
*============================================================================*/
#define ZEPHYR_USER_NODE DT_PATH(zephyr_user)
/* 디바이스트리에서 ADC 채널 스펙 가져오기 (gain, ref, acq time, input, resolution, oversampling) */
static const struct adc_dt_spec battery_adc = ADC_DT_SPEC_GET(ZEPHYR_USER_NODE);
static int16_t adc_buffer;
/*==============================================================================
* Module variables
*============================================================================*/
volatile bool battery_saadc_done = false;
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;
#define BATTERY_MONITOR_INTERVAL_MS 60000 /* 60초 주기 */
/*==============================================================================
* 전압 변환
*============================================================================*/
/** @brief ADC raw → mV 변환
* 공식: (raw × 600 / 4095) × 6 × 1.42
* 정수 연산: raw × 600 × 6 × 142 / (4095 × 100) */
static int adc_raw_to_mv(int16_t raw)
{
if (raw < 0)
{
raw = 0;
}
/* 오버플로 방지: 600 × 6 × 142 = 511,200 → raw 최대 4095 → 4095 × 511200 = ~2B → int32 OK */
int32_t mv = ((int32_t)raw * 600 * 6 * 142) / (4095 * 100);
return (int)mv;
}
/*==============================================================================
* Public functions
*============================================================================*/
void battery_adc_init(void)
{
/* ADC 디바이스 준비 상태 확인 */
if (!adc_is_ready_dt(&battery_adc))
{
DBG_PRINTF("[BATT] ADC device not ready\r\n");
return;
}
/* 디바이스트리 설정으로 채널 구성 */
int err = adc_channel_setup_dt(&battery_adc);
if (err)
{
DBG_PRINTF("[BATT] Channel setup failed (err %d)\r\n", err);
return;
}
DBG_PRINTF("[BATT] ADC init OK (DT-based, ch=%d, res=%d, os=%d)\r\n",
battery_adc.channel_id,
battery_adc.resolution,
battery_adc.oversampling);
}
int battery_read_mv(void)
{
/* 매번 채널 재설정 (다른 센서와 SAADC 공유 시 필요) */
int err = adc_channel_setup_dt(&battery_adc);
if (err)
{
DBG_PRINTF("[BATT] Channel setup failed (err %d)\r\n", err);
return -1;
}
/* 디바이스트리에서 시퀀스 초기화 (channels, resolution, oversampling) */
struct adc_sequence seq = {0};
err = adc_sequence_init_dt(&battery_adc, &seq);
if (err)
{
DBG_PRINTF("[BATT] Sequence init failed (err %d)\r\n", err);
return -1;
}
seq.buffer = &adc_buffer;
seq.buffer_size = sizeof(adc_buffer);
/* ADC 읽기 */
err = adc_read_dt(&battery_adc, &seq);
if (err)
{
DBG_PRINTF("[BATT] Read failed (err %d)\r\n", err);
return -1;
}
int16_t raw = adc_buffer;
int mv = adc_raw_to_mv(raw);
//DBG_PRINTF("[BATT] raw=%d mv=%d\r\n", raw, mv);
battery_saadc_done = true;
return mv;
}
/*==============================================================================
* 주기 모니터링 타이머
*============================================================================*/
/** @brief k_work 핸들러 — 스레드 컨텍스트에서 ADC 읽기 (adc_read는 블로킹이라 ISR 불가) */
static void battery_work_handler(struct k_work *work)
{
ARG_UNUSED(work);
if (processing)
{
return;
}
int batt = battery_read_mv();
if (batt < 0)
{
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 >= 10)
{
low_battery_cnt = 0;
DBG_PRINTF("[BATT] 10x low -> Power OFF\r\n");
sleep_mode_enter();
}
}
else
{
low_battery_cnt = 0;
}
}
/* 타이머 ISR → k_work 예약 */
static void battery_timer_handler(struct k_timer *timer)
{
ARG_UNUSED(timer);
k_work_submit(&battery_work);
}
void battery_timer_init(void)
{
k_work_init(&battery_work, battery_work_handler);
k_timer_init(&battery_timer, battery_timer_handler, NULL);
}
void battery_timer_start(void)
{
k_timer_start(&battery_timer, K_MSEC(BATTERY_MONITOR_INTERVAL_MS), K_MSEC(BATTERY_MONITOR_INTERVAL_MS));
}
void battery_timer_stop(void)
{
k_timer_stop(&battery_timer);
}