astra/nema17-stepper-motor-controller-spring-clamp-rp2040

A 4-layer RP2040 controller board that negotiates USB‑C PD motor power and drives a NEMA17 stepper via a DRV8825, with current sensing, thermal shutdown, RGB status LED, buzzer alarm, optional AS5600 encoder, protection/filtering, and test points.

Version
1.4.0
License
unset
Stars
0

firmware/current_monitor.py

"""Signed winding-current telemetry for INA241A1 + 0.1 ohm inline shunts.

Gain is 10 V/V, so sensitivity is 1 V/A. REF1=3V3, REF2=GND.
These readings are short-window signed averages, not RMS or stall detection.
ADC reference is nominally 3.3 V; calibrate against a meter on real hardware.
"""

ADC_FULL_SCALE = 65535
ADC_REFERENCE_V = 3.3
SENSITIVITY_V_PER_A = 10 * 0.1


def current_amperes(raw, zero=ADC_FULL_SCALE / 2, reference_v=ADC_REFERENCE_V):
    if not 0 <= raw <= ADC_FULL_SCALE:
        raise ValueError("ADC value out of range")
    return (raw - zero) * reference_v / ADC_FULL_SCALE / SENSITIVITY_V_PER_A


class CurrentMonitor:
    def __init__(self, adc_a, adc_b):
        self.adcs = (adc_a, adc_b)
        self.zero = [ADC_FULL_SCALE / 2, ADC_FULL_SCALE / 2]
        self.amperes = (None, None)
        self.clipped = False

    def sample(self, count=32):
        if count < 1 or count > 256:
            raise ValueError("invalid sample count")
        totals = [0, 0]
        clipped = False
        for _ in range(count):
            for i, adc in enumerate(self.adcs):
                raw = adc.read_u16()
                # Flag lost measurement range; don't present saturation as valid current.
                clipped = clipped or raw < 1024 or raw > ADC_FULL_SCALE - 1024
                totals[i] += raw
        self.clipped = clipped
        self.amperes = tuple(current_amperes(totals[i] / count, self.zero[i]) for i in range(2))
        return self.amperes