73 lines
3.3 KiB
C++
73 lines
3.3 KiB
C++
#pragma once
|
|
|
|
#include <string>
|
|
|
|
namespace fgc {
|
|
|
|
// Axis speed as a physical quantity, and its conversion to the TMC5160's VMAX.
|
|
//
|
|
// VMAX is a register value in internal units, and what it MEANS depends on the
|
|
// drivetrain: 30000 is 21.8 deg/s on the Delta fleet and 6.0 deg/s on the IOTA
|
|
// tower, because their gear ratios differ by 3.6x. Copying the number between
|
|
// towers therefore produces a silently different machine, which is exactly what
|
|
// happened - both carry the same literal.
|
|
//
|
|
// Declaring degrees per second instead makes the intent portable and checkable:
|
|
// it is the quantity that determines image smear and panorama period, and it can
|
|
// be reasoned about without knowing a gear ratio.
|
|
//
|
|
// The conversion needs only counts_per_deg, which the host already derives per
|
|
// tower from the homed travel. That works because the firmware keeps XACTUAL
|
|
// (step space) and XENC (encoder space) on the SAME scale - see
|
|
// firmware/docs/hardware-notes.md, "Encoders track 1:1 but must be aligned to
|
|
// XACTUAL". If that ever stops being true, this conversion breaks and the step
|
|
// scale becomes a separate calibration.
|
|
|
|
// The TMC5160 ramp generator: v[counts/s] = VMAX * f_CLK / 2^24.
|
|
//
|
|
// 12 MHz is the internal oscillator's nominal frequency. Confirmed on the rig
|
|
// rather than taken on faith: the predicted 0.7153 counts/s per VMAX unit is
|
|
// within 2% of the 0.73 measured across three homing runs on tower 121.
|
|
constexpr double kTmcClockHz = 12'000'000.0;
|
|
constexpr double kTmcVmaxDiv = 16'777'216.0; // 2^24
|
|
// VMAX is a 23-bit field.
|
|
constexpr long kTmcVmaxMax = 0x7FFFFF;
|
|
|
|
// The fleet default, in the unit that means the same thing on every tower.
|
|
constexpr double kDefaultScanSpeedDegPerSec = 8.0;
|
|
|
|
// Below this, counts_per_deg is not a drivetrain - it is an uncalibrated axis
|
|
// still carrying AxisMap's default of 1.0.
|
|
//
|
|
// The floor is physical, not arbitrary: the AS5047P in ABI mode emits 4000 steps
|
|
// per revolution (COMPONENTS.md section 7), so even an axis driven with NO gear
|
|
// reduction reads 11.1 counts/deg. Anything under that cannot have come from a
|
|
// real encoder on this rig. Converting a speed through such a scale yields a
|
|
// VMAX near zero - an axis that looks configured and never finishes a sweep -
|
|
// so it is refused rather than commanded.
|
|
constexpr double kMinPlausibleCountsPerDeg = 10.0;
|
|
|
|
struct AxisSpeedPlan {
|
|
bool ok = false;
|
|
long vmax = 0;
|
|
double requested_deg_s = 0.0;
|
|
// What `vmax` actually delivers. VMAX is an integer, so this differs from the
|
|
// request by the quantisation - negligible at production speeds, but it is
|
|
// what the startup log should report, because it is what the axis does.
|
|
double actual_deg_s = 0.0;
|
|
bool clamped = false;
|
|
std::string error; // why, when !ok
|
|
};
|
|
|
|
// `counts_per_deg` may be negative to flip the direction sense (see Geometry.h);
|
|
// only its magnitude matters to a speed, so the sign is ignored rather than
|
|
// producing a negative VMAX.
|
|
AxisSpeedPlan planAxisSpeed(double deg_per_s, double counts_per_deg,
|
|
double clock_hz = kTmcClockHz);
|
|
|
|
// The inverse, for reporting what a firmware default or an existing literal
|
|
// actually amounts to on this drivetrain.
|
|
double vmaxToDegPerSec(long vmax, double counts_per_deg, double clock_hz = kTmcClockHz);
|
|
|
|
} // namespace fgc
|