#ifndef MOTOR_CURRENT_H #define MOTOR_CURRENT_H #include #include /* * motor_current — ADC-based motor current monitoring and overload protection * for Issue #584. * * Hardware: * ADC3 IN13 (PC3, ADC_CURR_PIN) is already sampled by battery_adc.c via * DMA2_Stream0 circular. This module reads battery_adc_get_current_ma() * each tick rather than running a second ADC, since total discharge current * on this single-motor balance bot equals motor current plus ~30 mA overhead. * * Behaviour: * MC_NORMAL : current_ma < MOTOR_CURR_SOFT_MA — full output * MC_SOFT_LIMIT : current_ma in [SOFT_MA, HARD_MA) — linear PWM reduction * MC_COOLDOWN : hard cutoff latched after HARD_MA sustained for * MOTOR_CURR_OVERLOAD_MS (2 s) — zero output for * MOTOR_CURR_COOLDOWN_MS (10 s), then MC_NORMAL * * Soft limit formula (MC_SOFT_LIMIT): * scale = (HARD_MA - current_ma) / (HARD_MA - SOFT_MA) [0..1] * limited_cmd = (int16_t)(cmd * scale) * * Fault event: * On each hard-cutoff trip, s_fault_count is incremented (saturates at 255) * and motor_current_fault_pending() returns true for one main-loop tick so * the caller can append a fault log entry. * * Main-loop integration (pseudo-code): * * void main_loop_tick(uint32_t now_ms) { * battery_adc_tick(now_ms); * motor_current_tick(now_ms); * * if (motor_current_fault_pending()) * fault_log_append(FAULT_MOTOR_OVERCURRENT); * * int16_t cmd = balance_pid_output(); * cmd = motor_current_apply_limit(cmd); * motor_driver_update(&g_motor, cmd, steer, now_ms); * * motor_current_send_tlm(now_ms); // rate-limited to MOTOR_CURR_TLM_HZ * } */ /* ---- Thresholds ---- */ #define MOTOR_CURR_HARD_MA 5000u /* 5 A — hard cutoff level */ #define MOTOR_CURR_SOFT_MA 4000u /* 4 A — soft-limit onset (80% of hard) */ #define MOTOR_CURR_OVERLOAD_MS 2000u /* sustained over HARD_MA before fault */ #define MOTOR_CURR_COOLDOWN_MS 10000u /* zero-output recovery period (ms) */ #define MOTOR_CURR_TLM_HZ 5u /* JLINK_TLM_MOTOR_CURRENT publish rate */ /* ---- State enum ---- */ typedef enum { MC_NORMAL = 0, MC_SOFT_LIMIT = 1, MC_COOLDOWN = 2, } MotorCurrentState; /* ---- API ---- */ /* * motor_current_init() — reset all state. * Call once during system init, after battery_adc_init(). */ void motor_current_init(void); /* * motor_current_tick(now_ms) — evaluate ADC reading, update state machine. * Call from main loop after battery_adc_tick(), at any rate ≥ 10 Hz. * Non-blocking (<1 µs). */ void motor_current_tick(uint32_t now_ms); /* * motor_current_apply_limit(cmd) — scale motor command by current-limit factor. * MC_NORMAL: returns cmd unchanged. * MC_SOFT_LIMIT: returns cmd scaled down linearly. * MC_COOLDOWN: returns 0. * Call after motor_current_tick() each loop iteration. */ int16_t motor_current_apply_limit(int16_t cmd); /* * motor_current_is_faulted() — true while in MC_COOLDOWN (output zeroed). */ bool motor_current_is_faulted(void); /* * motor_current_state() — current state machine state. */ MotorCurrentState motor_current_state(void); /* * motor_current_ma() — most recent ADC reading used by the state machine (mA). */ int32_t motor_current_ma(void); /* * motor_current_fault_count() — lifetime hard-cutoff trip counter (0..255). */ uint8_t motor_current_fault_count(void); /* * motor_current_fault_pending() — true for exactly one tick after a hard * cutoff trip fires. Main loop should append a fault log entry and then the * flag clears automatically on the next call. */ bool motor_current_fault_pending(void); /* * motor_current_send_tlm(now_ms) — transmit JLINK_TLM_MOTOR_CURRENT (0x86) * frame to Jetson. Rate-limited to MOTOR_CURR_TLM_HZ; safe to call every tick. */ void motor_current_send_tlm(uint32_t now_ms); #endif /* MOTOR_CURRENT_H */