saltylab-firmware/include/power_mgmt.h
sl-controls cc0ffd1999 feat: Battery voltage ADC driver with DMA sampling (Issue #533)
STM32F7 ADC driver for battery voltage/current monitoring using
DMA-based continuous sampling, IIR low-pass filter, voltage divider
calibration, and USART telemetry to Jetson. Integrates with power
management for low-battery sleep (Issue #467).

Implementation:
- include/battery_adc.h: New driver header with calibration struct and
  public API (init, tick, get_voltage_mv, get_current_ma, calibrate,
  publish, check_pm, is_low, is_critical)
- src/battery_adc.c: ADC3 continuous-scan DMA (DMA2_Stream0/Ch2), 4x
  hardware oversampling of both Vbat (PC1/IN11) and Ibat (PC3/IN13),
  IIR LPF (alpha=1/8, cutoff ~4 Hz at 100 Hz tick rate), calibration
  with ±500 mV offset clamp, 3S/4S auto-detection, 1 Hz USART publish
- include/jlink.h + src/jlink.c: Add JLINK_TLM_BATTERY (0x82) telemetry
  type and jlink_tlm_battery_t (10-byte packed struct), implement
  jlink_send_battery_telemetry() using CRC16-XModem framing
- include/power_mgmt.h + src/power_mgmt.c: Add
  power_mgmt_notify_battery() — triggers STOP-mode sleep when Vbat
  sustains critical level (Issue #467)
- test/test_battery_adc.c: 27 unit tests (27/27 passing): voltage
  conversion, calibration offset/scale, IIR LPF convergence, SoC
  estimation (3S/4S), low/critical flags, PM notification timing,
  calibration reset, publish rate-limiting

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-07 10:01:02 -05:00

104 lines
3.6 KiB
C

#ifndef POWER_MGMT_H
#define POWER_MGMT_H
#include <stdint.h>
#include <stdbool.h>
/*
* power_mgmt — STM32F7 STOP-mode sleep/wake manager (Issue #178).
*
* State machine:
* PM_ACTIVE ──(idle ≥ PM_IDLE_TIMEOUT_MS or sleep cmd)──► PM_SLEEP_PENDING
* PM_SLEEP_PENDING ──(fade complete, ≥ PM_FADE_MS)──► PM_SLEEPING (WFI)
* PM_SLEEPING ──(EXTI wake)──► PM_WAKING ──(clocks restored)──► PM_ACTIVE
*
* Any call to power_mgmt_activity() during SLEEP_PENDING or SLEEPING
* immediately transitions back toward PM_ACTIVE.
*
* Wake sources (EXTI, falling edge on UART idle-high RX pin or IMU INT):
* EXTI1 PA1 UART4_RX — CRSF/ELRS start bit
* EXTI7 PB7 USART1_RX — JLink start bit
* EXTI4 PC4 MPU6000 INT — IMU motion (handler owned by mpu6000.c)
*
* Peripheral gating on sleep entry (clock disable, state preserved):
* Disabled: SPI3/I2S3 (audio amp), SPI2 (OSD), USART6, UART5 (debug)
* Active: SPI1 (IMU), UART4 (CRSF), USART1 (JLink), I2C1 (baro/mag)
*
* Sleep LED (LED1, active-low PC15):
* PM_SLEEP_PENDING: triangle-wave pulse, period PM_LED_PERIOD_MS
* All other states: 0 (caller uses normal LED logic)
*
* IWDG:
* Fed immediately before WFI. STOP wakeup <10 ms typical — well within
* WATCHDOG_TIMEOUT_MS (50 ms).
*
* Safety interlock:
* Caller MUST NOT call power_mgmt_tick() while armed; call
* power_mgmt_activity() instead to keep the idle timer reset.
*
* JLink integration:
* JLINK_CMD_SLEEP (0x09) → power_mgmt_request_sleep()
* Any valid JLink frame → power_mgmt_activity() (handled in main loop)
*/
typedef enum {
PM_ACTIVE = 0, /* Normal, all peripherals running */
PM_SLEEP_PENDING = 1, /* Idle timeout reached; LED fade-out in progress */
PM_SLEEPING = 2, /* In STOP mode (WFI); execution blocked in tick() */
PM_WAKING = 3, /* Transitional; clocks/peripherals being restored */
} PowerState;
/* ---- API ---- */
/*
* power_mgmt_init() — configure wake EXTI lines (EXTI1, EXTI7).
* Call after crsf_init() and jlink_init().
*/
void power_mgmt_init(void);
/*
* power_mgmt_activity() — record cmd_vel event (CRSF frame, JLink frame).
* Resets idle timer; aborts any pending/active sleep.
*/
void power_mgmt_activity(void);
/*
* power_mgmt_request_sleep() — force sleep regardless of idle timer
* (called on JLINK_CMD_SLEEP). Next tick() enters PM_SLEEP_PENDING.
*/
void power_mgmt_request_sleep(void);
/*
* power_mgmt_tick(now_ms) — drive state machine. May block in WFI during
* STOP mode. Returns state after this tick.
* MUST NOT be called while balance_state == BALANCE_ARMED.
*/
PowerState power_mgmt_tick(uint32_t now_ms);
/* power_mgmt_state() — non-blocking read of current state. */
PowerState power_mgmt_state(void);
/*
* power_mgmt_led_brightness() — 0-255 brightness for sleep-pending pulse.
* Returns 0 when not in PM_SLEEP_PENDING; caller uses normal LED logic.
*/
uint8_t power_mgmt_led_brightness(void);
/*
* power_mgmt_current_ma() — estimated total current draw (mA) based on
* gating state; populated in JLINK_TLM_POWER telemetry.
*/
uint16_t power_mgmt_current_ma(void);
/* power_mgmt_idle_ms() — ms elapsed since last power_mgmt_activity() call. */
uint32_t power_mgmt_idle_ms(void);
/*
* power_mgmt_notify_battery(vbat_mv) — Issue #467 integration.
* Called by battery_adc_check_pm() after BATTERY_ADC_LOW_HOLD_MS of sustained
* critical voltage. Forces PM_SLEEP_PENDING to protect the LiPo.
*/
void power_mgmt_notify_battery(uint32_t vbat_mv);
#endif /* POWER_MGMT_H */