This repository has been archived on 2025-02-01. You can view files and clone it, but cannot push or open issues or pull requests.
reprapfirmware-dc42/Move.h
David Crocker 21d688ece0 Various improvements
Tidied up axis homing tracking.
When checking endstops, wait for move to complete before accepting
further moves, otherwise subsequent moves use the wrong coordinates.
Temperatures and Z probes are now monitored continuously using a tick
interrupt to kick off ADC conversions. ADC is now run in 12-bit mode.
Thermistor readings are passed through averaging filters. Thermistors
are monitored for overheat conditions and bad readings in the tick ISR
and the appropriate heater is turned off (useful because the main loop
sometimes gets suspended while trying to do USB communication).
Use watchdog timer to monitor the tick interrupt - needs patch to
Arduino Due core library. Add facility to test watchdog timer (M111
S1001). Added an error status word to record that errors have occurred
(e.g. over-temperature).
M111 command changes so that S0 turns debug off, S1 turns debug on, S2
reports free memory - also now reports the type of the last restart and
the error status word. Fixed problem whereby M111 debug reports were not
sent to the web interface.
Implemented M999 command, which resets the Duet.
Removed an unused variable.
Changed some more "char*" to "const char*".
Changed extruder PID parameters and added more explanation for them.
2014-03-02 23:26:14 +00:00

600 lines
14 KiB
C++

/****************************************************************************************************
RepRapFirmware - Move
This is all the code to deal with movement and kinematics.
-----------------------------------------------------------------------------------------------------
Version 0.1
18 November 2012
Adrian Bowyer
RepRap Professional Ltd
http://reprappro.com
Licence: GPL
****************************************************************************************************/
#ifndef MOVE_H
#define MOVE_H
#define DDA_RING_LENGTH 5
#define LOOK_AHEAD_RING_LENGTH 20
#define LOOK_AHEAD 7
enum MovementProfile
{
moving = 0, // Ordinary trapezoidal-velocity-profile movement
noFlat = 1, // Triangular profile movement
change = 2 // To make this movement, the initial and/or final velocities must change
};
// The possible states of a movement in the look-ahead ring as the look-ahead is
// being done.
enum MovementState
{
unprocessed = 0,
vCosineSet = 1,
upPass = 2,
complete = 4,
released = 8
};
enum MovementType
{
noMove = 0,
xyMove = 1,
zMove = 2,
eMove = 4
};
enum PointCoordinateSet
{
unset = 0,
xSet = 1,
ySet = 2,
zSet = 4
};
class LookAhead
{
public:
friend class Move;
friend class DDA;
protected:
LookAhead(Move* m, Platform* p, LookAhead* n);
void Init(const long ep[], float feedRate, float vv, EndstopMode ce, int8_t mt);
LookAhead* Next();
LookAhead* Previous();
const long* MachineEndPoints() const;
float MachineToEndPoint(int8_t drive);
static float MachineToEndPoint(int8_t drive, long coord);
static long EndPointToMachine(int8_t drive, float coord);
int8_t GetMovementType() const;
float FeedRate() const;
float V() const;
void SetV(float vv);
void SetFeedRate(float f);
int8_t Processed() const;
void SetProcessed(MovementState ms);
void SetDriveCoordinateAndZeroEndSpeed(float a, int8_t drive);
EndstopMode CheckEndStops() const;
void Release();
private:
Move* move;
Platform* platform;
LookAhead* next;
LookAhead* previous;
long endPoint[DRIVES+1]; // Should never use the +1, but safety first
int8_t movementType;
float Cosine();
EndstopMode checkEndStops;
float cosine;
float v; // The feedrate we can actually do
float feedRate; // The requested feedrate
float instantDv;
volatile int8_t processed;
};
class DDA
{
public:
friend class Move;
friend class LookAhead;
protected:
DDA(Move* m, Platform* p, DDA* n);
MovementProfile Init(LookAhead* lookAhead, float& u, float& v);
void Start(bool noTest);
void Step();
bool Active() const;
DDA* Next();
float InstantDv() const;
private:
MovementProfile AccelerationCalculation(float& u, float& v, MovementProfile result);
void SetXYAcceleration();
void SetEAcceleration(float eDistance);
Move* move;
Platform* platform;
DDA* next;
LookAhead* myLookAheadEntry;
long counter[DRIVES];
long delta[DRIVES];
bool directions[DRIVES];
long totalSteps;
long stepCount;
EndstopMode checkEndStops;
float timeStep;
float velocity;
long stopAStep;
long startDStep;
float distance;
float acceleration;
float instantDv;
volatile bool active;
};
class Move
{
public:
Move(Platform* p, GCodes* g);
void Init();
void Spin();
void Exit();
bool GetCurrentState(float m[]); // takes account of all the rings and delays
void LiveCoordinates(float m[]); // Just gives the last point at the end of the last DDA
void Interrupt();
void InterruptTime();
bool AllMovesAreFinished();
void ResumeMoving();
void DoLookAhead();
void HitLowStop(int8_t drive, LookAhead* la, DDA* hitDDA);
void NearLowStop(int8_t drive, LookAhead* la, DDA* hitDDA);
void HitHighStop(int8_t drive, LookAhead* la, DDA* hitDDA);
void SetPositions(float move[]);
void SetLiveCoordinates(float coords[]);
void SetXBedProbePoint(int index, float x);
void SetYBedProbePoint(int index, float y);
void SetZBedProbePoint(int index, float z);
float xBedProbePoint(int index) const;
float yBedProbePoint(int index) const;
float zBedProbePoint(int index) const;
int NumberOfProbePoints() const;
int NumberOfXYProbePoints() const;
bool AllProbeCoordinatesSet(int index) const;
bool XYProbeCoordinatesSet(int index) const;
void SetZProbing(bool probing);
void SetProbedBedEquation();
float SecondDegreeTransformZ(float x, float y) const;
float GetLastProbedZ() const;
void SetAxisCompensation(int8_t axis, float tangent);
void SetIdentityTransform();
void Transform(float move[]);
void InverseTransform(float move[]);
void Diagnostics();
float ComputeCurrentCoordinate(int8_t drive, LookAhead* la, DDA* runningDDA);
void SetStepHypotenuse();
friend class DDA;
private:
bool DDARingAdd(LookAhead* lookAhead);
DDA* DDARingGet();
bool DDARingEmpty() const;
bool NoLiveMovement() const;
bool DDARingFull() const;
bool GetDDARingLock();
void ReleaseDDARingLock();
bool LookAheadRingEmpty() const;
bool LookAheadRingFull() const;
bool LookAheadRingAdd(const long ep[], float feedRate, float vv, EndstopMode ce, int8_t movementType);
LookAhead* LookAheadRingGet();
int8_t GetMovementType(const long sp[], const long ep[]) const;
float liveCoordinates[DRIVES + 1];
Platform* platform;
GCodes* gCodes;
DDA* dda;
DDA* ddaRingAddPointer;
DDA* ddaRingGetPointer;
volatile bool ddaRingLocked;
LookAhead* lookAheadRingAddPointer;
LookAhead* lookAheadRingGetPointer;
LookAhead* lastMove;
DDA* lookAheadDDA;
int lookAheadRingCount;
float lastTime;
bool addNoMoreMoves;
bool active;
float currentFeedrate;
float nextMove[DRIVES + 1]; // Extra is for feedrate
float stepDistances[(1<<AXES)]; // Index bits: lsb -> dx, dy, dz <- msb
float extruderStepDistances[(1<<(DRIVES-AXES))]; // NB - limits us to 5 extruders
long nextMachineEndPoints[DRIVES+1];
float xBedProbePoints[NUMBER_OF_PROBE_POINTS];
float yBedProbePoints[NUMBER_OF_PROBE_POINTS];
float zBedProbePoints[NUMBER_OF_PROBE_POINTS];
uint8_t probePointSet[NUMBER_OF_PROBE_POINTS];
float aX, aY, aC; // Bed plane explicit equation z' = z + aX*x + aY*y + aC
float tanXY, tanYZ, tanXZ; // 90 degrees + angle gives angle between axes
float xRectangle, yRectangle;
float lastZHit;
bool zProbing;
bool secondDegreeCompensation;
float longWait;
};
//********************************************************************************************************
inline LookAhead* LookAhead::Next()
{
return next;
}
inline LookAhead* LookAhead::Previous()
{
return previous;
}
inline void LookAhead::SetV(float vv)
{
v = vv;
}
inline float LookAhead::V() const
{
return v;
}
inline float LookAhead::MachineToEndPoint(int8_t drive)
{
if(drive >= DRIVES)
platform->Message(HOST_MESSAGE, "MachineToEndPoint() called for feedrate!\n");
return ((float)(endPoint[drive]))/platform->DriveStepsPerUnit(drive);
}
inline float LookAhead::FeedRate() const
{
return feedRate;
}
inline void LookAhead::SetFeedRate(float f)
{
feedRate = f;
}
inline int8_t LookAhead::Processed() const
{
return processed;
}
inline void LookAhead::SetProcessed(MovementState ms)
{
if(ms == unprocessed)
processed = unprocessed;
else
processed |= ms;
}
inline void LookAhead::Release()
{
processed = released;
}
inline EndstopMode LookAhead::CheckEndStops() const
{
return checkEndStops;
}
inline void LookAhead::SetDriveCoordinateAndZeroEndSpeed(float a, int8_t drive)
{
endPoint[drive] = EndPointToMachine(drive, a);
cosine = 2.0;
v = 0.0;
}
inline const long* LookAhead::MachineEndPoints() const
{
return endPoint;
}
inline int8_t LookAhead::GetMovementType() const
{
return movementType;
}
//******************************************************************************************************
inline bool DDA::Active() const
{
return active;
}
inline DDA* DDA::Next()
{
return next;
}
inline float DDA::InstantDv() const
{
return instantDv;
}
//***************************************************************************************
inline bool Move::DDARingEmpty() const
{
return ddaRingGetPointer == ddaRingAddPointer;
}
inline bool Move::NoLiveMovement() const
{
if(dda != NULL)
return false;
return DDARingEmpty();
}
// Leave a gap of 2 as the last Get result may still be being processed
inline bool Move::DDARingFull() const
{
return ddaRingAddPointer->Next()->Next() == ddaRingGetPointer;
}
inline bool Move::LookAheadRingEmpty() const
{
return lookAheadRingCount == 0;
}
// Leave a gap of 2 as the last Get result may still be being processed
inline bool Move::LookAheadRingFull() const
{
if(!(lookAheadRingAddPointer->Processed() & released))
return true;
return lookAheadRingAddPointer->Next()->Next() == lookAheadRingGetPointer; // probably not needed; just return the bool in the if above
}
inline bool Move::GetDDARingLock()
{
if(ddaRingLocked)
return false;
ddaRingLocked = true;
return true;
}
inline void Move::ReleaseDDARingLock()
{
ddaRingLocked = false;
}
inline void Move::LiveCoordinates(float m[])
{
for(int8_t drive = 0; drive <= DRIVES; drive++)
m[drive] = liveCoordinates[drive];
InverseTransform(m);
}
// These are the actual numbers that we want to be the coordinates, so
// don't transform them.
inline void Move::SetLiveCoordinates(float coords[])
{
for(int8_t drive = 0; drive <= DRIVES; drive++)
liveCoordinates[drive] = coords[drive];
}
// To wait until all the current moves in the buffers are
// complete, call this function repeatedly and wait for it to
// return true. Then do whatever you wanted to do after all
// current moves have finished. THEN CALL THE ResumeMoving() FUNCTION
// OTHERWISE NOTHING MORE WILL EVER HAPPEN.
inline bool Move::AllMovesAreFinished()
{
addNoMoreMoves = true;
return LookAheadRingEmpty() && NoLiveMovement();
}
inline void Move::ResumeMoving()
{
addNoMoreMoves = false;
}
inline void Move::SetXBedProbePoint(int index, float x)
{
if(index < 0 || index >= NUMBER_OF_PROBE_POINTS)
{
platform->Message(HOST_MESSAGE, "Z probe point X index out of range.\n");
return;
}
xBedProbePoints[index] = x;
probePointSet[index] |= xSet;
}
inline void Move::SetYBedProbePoint(int index, float y)
{
if(index < 0 || index >= NUMBER_OF_PROBE_POINTS)
{
platform->Message(HOST_MESSAGE, "Z probe point Y index out of range.\n");
return;
}
yBedProbePoints[index] = y;
probePointSet[index] |= ySet;
}
inline void Move::SetZBedProbePoint(int index, float z)
{
if(index < 0 || index >= NUMBER_OF_PROBE_POINTS)
{
platform->Message(HOST_MESSAGE, "Z probe point Z index out of range.\n");
return;
}
zBedProbePoints[index] = z;
probePointSet[index] |= zSet;
}
inline float Move::xBedProbePoint(int index) const
{
return xBedProbePoints[index];
}
inline float Move::yBedProbePoint(int index) const
{
return yBedProbePoints[index];
}
inline float Move::zBedProbePoint(int index) const
{
return zBedProbePoints[index];
}
inline void Move::SetZProbing(bool probing)
{
zProbing = probing;
}
inline float Move::GetLastProbedZ() const
{
return lastZHit;
}
inline bool Move::AllProbeCoordinatesSet(int index) const
{
return probePointSet[index] == (xSet | ySet | zSet);
}
inline bool Move::XYProbeCoordinatesSet(int index) const
{
return (probePointSet[index] & xSet) && (probePointSet[index] & ySet);
}
inline int Move::NumberOfProbePoints() const
{
if(AllProbeCoordinatesSet(0) && AllProbeCoordinatesSet(1) && AllProbeCoordinatesSet(2))
{
if(AllProbeCoordinatesSet(3))
return 4;
else
return 3;
}
return 0;
}
inline int Move::NumberOfXYProbePoints() const
{
if(XYProbeCoordinatesSet(0) && XYProbeCoordinatesSet(1) && XYProbeCoordinatesSet(2))
{
if(XYProbeCoordinatesSet(3))
return 4;
else
return 3;
}
return 0;
}
/*
* Transform to a ruled-surface quadratic. The corner points for interpolation are indexed:
*
* ^ [1] [2]
* |
* Y
* |
* | [0] [3]
* -----X---->
*
* The values of x and y are transformed to put them in the interval [0, 1].
*/
inline float Move::SecondDegreeTransformZ(float x, float y) const
{
x = (x - xBedProbePoints[0])*xRectangle;
y = (y - yBedProbePoints[0])*yRectangle;
return (1.0 - x)*(1.0 - y)*zBedProbePoints[0] + x*(1.0 - y)*zBedProbePoints[3] + (1.0 - x)*y*zBedProbePoints[1] + x*y*zBedProbePoints[2];
}
inline void Move::HitLowStop(int8_t drive, LookAhead* la, DDA* hitDDA)
{
float hitPoint = 0.0;
if(drive == Z_AXIS)
{
if(zProbing)
{
// Executing G32, so record the Z position at which we hit the end stop
if (gCodes->GetAxisIsHomed(drive))
{
// Z-axis has already been homed, so just record the height of the bed at this point
lastZHit = ComputeCurrentCoordinate(drive, la, hitDDA);
la->SetDriveCoordinateAndZeroEndSpeed(lastZHit, drive);
lastZHit = lastZHit - platform->ZProbeStopHeight();
}
else
{
// Z axis has not yet been homed, so treat this probe as a homing command
la->SetDriveCoordinateAndZeroEndSpeed(platform->ZProbeStopHeight(), drive);
gCodes->SetAxisIsHomed(drive);
lastZHit = 0.0;
}
return;
} else
{
// Executing G30, so set the current Z height to the value at which the end stop is triggered
// Transform it first so that the height is correct in user coordinates
float xyzPoint[DRIVES + 1];
LiveCoordinates(xyzPoint);
xyzPoint[Z_AXIS] = lastZHit = platform->ZProbeStopHeight();
Transform(xyzPoint);
hitPoint = xyzPoint[Z_AXIS];
}
}
la->SetDriveCoordinateAndZeroEndSpeed(hitPoint, drive);
gCodes->SetAxisIsHomed(drive);
}
inline void Move::HitHighStop(int8_t drive, LookAhead* la, DDA* hitDDA)
{
la->SetDriveCoordinateAndZeroEndSpeed(platform->AxisLength(drive), drive);
gCodes->SetAxisIsHomed(drive);
}
inline void Move::NearLowStop(int8_t drive, LookAhead* la, DDA* hitDDA)
{
la->SetDriveCoordinateAndZeroEndSpeed(1.0, drive); // say we are at 1mm
}
inline float Move::ComputeCurrentCoordinate(int8_t drive, LookAhead* la, DDA* runningDDA)
{
float previous = la->Previous()->MachineToEndPoint(drive);
if(runningDDA->totalSteps <= 0)
return previous;
return previous + (la->MachineToEndPoint(drive) - previous)*(float)runningDDA->stepCount/(float)runningDDA->totalSteps;
}
#endif