/*
 * battery_adc.h — DMA-based battery voltage/current ADC driver (Issue #533)
 *
 * Hardware:
 *   ADC3 channel IN11 (PC1) — Vbat through 10kΩ/1kΩ divider (11:1 ratio)
 *   ADC3 channel IN13 (PC3) — Ibat via shunt amplifier (ADC_IBAT_SCALE=115)
 *   DMA2 Stream0 Channel2   — ADC3 → memory circular (8-word buffer)
 *   USART1 (jlink)          — telemetry to Jetson via JLINK_TLM_BATTERY (0x82)
 *
 * HOW IT WORKS:
 * 1. ADC3 runs in continuous scan mode, alternating IN11 (Vbat) and IN13 (Ibat)
 *    at APB2/8 clock (≈ 13.5 MHz ADC clock on STM32F7 @ 216 MHz).
 *    480-cycle sampling per channel → ~35 µs per scan pair, ~28 kHz scan rate.
 *
 * 2. DMA2_Stream0 (circular) fills an 8-word buffer: 4 Vbat samples followed
 *    by 4 Ibat samples per DMA half-complete cycle.  Interleaved layout:
 *      [vbat0, ibat0, vbat1, ibat1, vbat2, ibat2, vbat3, ibat3]
 *
 * 3. battery_adc_tick() (call from main loop, 10–100 Hz) averages the 4 Vbat
 *    and 4 Ibat raw values (4× hardware oversampling), then feeds a 1st-order
 *    IIR low-pass filter:
 *      filtered += (raw - filtered) >> BATTERY_ADC_LPF_SHIFT
 *    With LPF_SHIFT=3 (α = 1/8) and 100 Hz tick rate, cutoff ≈ 4 Hz.
 *
 * 4. Calibration scales and offsets the filtered output:
 *      vbat_mv  = filtered_raw * (VBAT_AREF_MV * VBAT_SCALE_NUM) / 4096
 *               + cal.vbat_offset_mv
 *      ibat_ma  = filtered_raw * ADC_IBAT_SCALE_MA_PER_COUNT / 1000
 *               + cal.ibat_offset_ma
 *    User calibration adjusts cal.vbat_offset_mv to null out divider tolerance.
 *
 * 5. battery_adc_publish() sends JLINK_TLM_BATTERY (0x82) to Jetson at 1 Hz.
 *
 * 6. battery_adc_check_pm() monitors for low voltage.  If Vbat drops below
 *    BATTERY_ADC_LOW_MV for BATTERY_ADC_LOW_HOLD_MS, calls
 *    power_mgmt_notify_battery(vbat_mv) which requests sleep (Issue #467).
 *
 * Interrupt safety:
 *   s_dma_buf is written by DMA hardware; battery_adc_tick() reads it with a
 *   brief __disable_irq() snapshot to prevent torn reads of the 16-bit words.
 *   All other state is private to the main-loop call path.
 */

#ifndef BATTERY_ADC_H
#define BATTERY_ADC_H

#include <stdint.h>
#include <stdbool.h>

/* ---- Low-pass filter ---- */
/* IIR shift: α = 1/8 → cutoff ≈ 4 Hz at 100 Hz tick rate */
#define BATTERY_ADC_LPF_SHIFT       3u

/* ---- Low-voltage thresholds (mV) ---- */
/* 3S LiPo: 9.0 V cell floor ×3 = 9900 mV full, 9000 mV absolute minimum */
#define BATTERY_ADC_LOW_MV          10200u   /* ≈ 15% SoC — warn / throttle     */
#define BATTERY_ADC_CRITICAL_MV      9600u   /* ≈  5% SoC — request sleep (#467) */
#define BATTERY_ADC_LOW_HOLD_MS      5000u   /* must stay below this long to act  */

/* 4S LiPo equivalents (auto-detected when Vbat ≥ 13 V at boot) */
#define BATTERY_ADC_LOW_MV_4S       13600u
#define BATTERY_ADC_CRITICAL_MV_4S  12800u

/* ---- Telemetry rate ---- */
#define BATTERY_ADC_PUBLISH_HZ      1u       /* JLINK_TLM_BATTERY TX rate         */

/* ---- Calibration struct ---- */
typedef struct {
    int16_t  vbat_offset_mv;   /* additive offset after scale (mV, ±500 clamp)  */
    int16_t  ibat_offset_ma;   /* additive offset for current (mA, ±200 clamp)  */
    uint16_t vbat_scale_num;   /* divider numerator  override; 0 = use VBAT_SCALE_NUM */
    uint16_t vbat_scale_den;   /* divider denominator override; 0 = use 1           */
} battery_adc_cal_t;

/* ---- API ---- */

/*
 * battery_adc_init() — configure ADC3 continuous-scan + DMA2_Stream0.
 * Must be called after __HAL_RCC_ADC3_CLK_ENABLE / GPIO clock enables.
 * Call once during system init, before battery_adc_tick().
 */
void battery_adc_init(void);

/*
 * battery_adc_tick(now_ms) — average DMA buffer, apply IIR LPF, update state.
 * Call from main loop at 10–100 Hz.  Non-blocking (<5 µs).
 */
void battery_adc_tick(uint32_t now_ms);

/*
 * battery_adc_get_voltage_mv() — calibrated, LPF-filtered Vbat in mV.
 * Returns 0 if ADC not initialised.
 */
uint32_t battery_adc_get_voltage_mv(void);

/*
 * battery_adc_get_current_ma() — calibrated, LPF-filtered Ibat in mA.
 * Positive = discharging (load current).  Returns 0 if not initialised.
 */
int32_t battery_adc_get_current_ma(void);

/*
 * battery_adc_get_raw_voltage_mv() — unfiltered last-tick average (mV).
 * Useful for calibration; use filtered version for control logic.
 */
uint32_t battery_adc_get_raw_voltage_mv(void);

/*
 * battery_adc_calibrate(cal) — store calibration constants.
 * Applies immediately to subsequent battery_adc_tick() calls.
 * Pass NULL to reset to defaults (0 offset, default scale).
 */
void battery_adc_calibrate(const battery_adc_cal_t *cal);

/*
 * battery_adc_get_calibration(out_cal) — read back current calibration.
 */
void battery_adc_get_calibration(battery_adc_cal_t *out_cal);

/*
 * battery_adc_publish(now_ms) — send JLINK_TLM_BATTERY (0x82) frame.
 * Rate-limited to BATTERY_ADC_PUBLISH_HZ; safe to call every main loop tick.
 */
void battery_adc_publish(uint32_t now_ms);

/*
 * battery_adc_check_pm(now_ms) — evaluate low-voltage thresholds.
 * Calls power_mgmt_notify_battery() on sustained critical voltage.
 * Call from main loop after battery_adc_tick().
 */
void battery_adc_check_pm(uint32_t now_ms);

/*
 * battery_adc_is_low() — true if Vbat below BATTERY_ADC_LOW_MV (warn level).
 */
bool battery_adc_is_low(void);

/*
 * battery_adc_is_critical() — true if Vbat below BATTERY_ADC_CRITICAL_MV.
 */
bool battery_adc_is_critical(void);

#endif /* BATTERY_ADC_H */
