#pragma once // // TmcCurrent — milliamps to TMC5160 register fields. // // The driver expresses coil current as two dimensionless knobs: GLOBALSCALER // (0x0B) scales the whole bridge, and IRUN (a field of IHOLD_IRUN, 0x10) scales // within that. What a given pair actually delivers depends on the sense resistor // soldered to the board, which is why this lives on the host: hardware.ini knows // the resistor, the firmware does not. // // I_rms = (GLOBALSCALER/256) * ((IRUN+1)/32) * (V_FS / (R_SENSE + 0.02)) / sqrt(2) // // V_FS is 0.325 V for the DRVCONF the firmware writes, and the 0.02 ohm is the // TMC5160's internal contribution in series with the external resistor. // #include #include namespace fgc { // RMS phase current in milliamps for a given register pair. Returns 0 for a // non-positive sense resistor rather than dividing by something meaningless. double tmcRmsCurrentMa(int global_scaler, int irun, double r_sense_ohm, double v_fs = 0.325); struct TmcCurrentPlan { bool ok = false; int global_scaler = 0; // 32..255. Never 0: on the TMC that means 256, i.e. // FULL SCALE, so a defaulted or truncated zero would // silently mean maximum current. int run_irun = 0; // 0..31, normal motion int homing_irun = 0; // 0..31, endstop search only // What the chosen registers actually deliver — always recomputed, never the // requested value echoed back, so the log cannot flatter the result. double run_ma = 0.0; double homing_ma = 0.0; std::vector warnings; // caller logs each at WARN }; // One GLOBALSCALER serves both currents: it is chosen so the RUN current lands at // IRUN = 31 (microstep resolution is best at the top of the IRUN range, and that // is where the axis spends its working life), and the homing IRUN is then derived // at that same scaler. The firmware therefore switches modes with a single // register write, and never re-scales a moving motor. TmcCurrentPlan planTmcCurrent(double r_sense_ohm, double run_ma, double homing_ma, double v_fs = 0.325); } // namespace fgc