Version 1.17dev8

Fixed bugs related to grid bed compensation and M571 in 1.17dev7 version
Software reset code storage/retrieval now works on Duet WiFi
Removed max average printing acceleration parameter
M571 now accepts F parameter to set the PWM frequency
M106 now shows if fan is disabled
This commit is contained in:
David Crocker 2016-12-09 14:01:40 +00:00
parent 9799ffce73
commit a7ea87d1e3
15 changed files with 360 additions and 278 deletions

View file

@ -28,11 +28,11 @@ Licence: GPL
// Firmware name is now defined in the Pins file
#ifndef VERSION
# define VERSION "1.17dev7"
# define VERSION "1.17dev8"
#endif
#ifndef DATE
# define DATE "2016-12-07"
# define DATE "2016-12-09"
#endif
#define AUTHORS "reprappro, dc42, zpl, t3p3, dnewman"
@ -118,7 +118,7 @@ const unsigned int FirstRtdChannel = 200; // Temperature sensor channels 200..
const unsigned int SlowHeaterPwmFreq = 10; // slow PWM frequency for bed and chamber heaters, compatible with DC/AC SSRs
const unsigned int NormalHeaterPwmFreq = 250; // normal PWM frequency used for hot ends
const unsigned int DefaultFanPwmFreq = 250; // increase to 25kHz using M106 command to meet Intel 4-wire PWM fan specification
const unsigned int DefaultPinWritePwmFreq = 500; // default PWM frequency for M42 pin writes
const unsigned int DefaultPinWritePwmFreq = 500; // default PWM frequency for M42 pin writes and extrusion ancilliary PWM
// Default Z probe values

View file

@ -132,4 +132,14 @@ void Fan::Check()
}
}
void Fan::Disable()
{
if (pin != NoPin)
{
inverted = false;
SetHardwarePwm(0.0);
}
pin = NoPin;
}
// End

View file

@ -48,7 +48,7 @@ public:
void SetTriggerTemperature(float t) { triggerTemperature = t; }
void SetHeatersMonitored(uint16_t h);
void Check();
void Disable() { pin = NoPin; }
void Disable();
};
#endif /* SRC_FAN_H_ */

View file

@ -148,7 +148,7 @@ void GCodes::Reset()
simulationMode = 0;
simulationTime = 0.0;
isPaused = false;
filePos = moveBuffer.filePos = noFilePosition;
moveBuffer.filePos = noFilePosition;
lastEndstopStates = platform->GetAllEndstopStates();
firmwareUpdateModuleMap = 0;
@ -699,11 +699,6 @@ void GCodes::DoFilePrint(GCodeBuffer& gb, StringRef& reply)
char b;
if (fd.Read(b))
{
if (gb.StartingNewCode() && &gb == fileGCode && gb.MachineState().previous == nullptr)
{
filePos = fd.GetPosition() - 1;
//debugPrintf("Set file pos %u\n", filePos);
}
if (gb.Put(b))
{
gb.SetFinished(ActOnCode(gb, reply));
@ -732,6 +727,7 @@ void GCodes::DoFilePrint(GCodeBuffer& gb, StringRef& reply)
if (gb.MachineState().previous == nullptr)
{
// Finished printing SD card file
UnlockAll(gb);
reprap.GetPrintMonitor()->StoppedPrint();
if (platform->Emulating() == marlin)
{
@ -947,7 +943,7 @@ void GCodes::Pop(GCodeBuffer& gb)
// Move expects all axis movements to be absolute, and all extruder drive moves to be relative. This function serves that.
// 'moveType' is the S parameter in the G0 or G1 command, or -1 if we are doing G92.
// For regular (type 0) moves, we apply limits and do X axis mapping.
// Returns the number of segments if we have a legal move (or 1 if we are doing G92), or zero if this gcode should be discarded
// Returns the number of segments if we have a legal move, 1 if we are doing G92, or zero if this gcode should be discarded
unsigned int GCodes::LoadMoveBufferFromGCode(GCodeBuffer& gb, int moveType)
{
// Zero every extruder drive as some drives may not be changed
@ -1214,7 +1210,7 @@ int GCodes::SetUpMove(GCodeBuffer& gb, StringRef& reply)
}
}
// Load the last position and feed rate into moveBuffer
// Load the last position into moveBuffer
#if SUPPORT_ROLAND
if (reprap.GetRoland()->Active())
{
@ -1227,24 +1223,25 @@ int GCodes::SetUpMove(GCodeBuffer& gb, StringRef& reply)
}
// Load the move buffer with either the absolute movement required or the relative movement required
float oldCoords[MAX_AXES];
memcpy(oldCoords, moveBuffer.coords, sizeof(oldCoords));
memcpy(moveBuffer.initialCoords, moveBuffer.coords, numAxes * sizeof(moveBuffer.initialCoords[0]));
segmentsLeft = LoadMoveBufferFromGCode(gb, moveBuffer.moveType);
if (segmentsLeft != 0)
{
// Flag whether we should use pressure advance, if there is any extrusion in this move.
// We assume it is a normal printing move needing pressure advance if there is forward extrusion and XY movement.
// The movement code will only apply pressure advance if there is forward extrusion, so we only need to check for XY movement here.
// We assume it is a normal printing move needing pressure advance if there is forward extrusion and XYU.. movement.
// The movement code will only apply pressure advance if there is forward extrusion, so we only need to check for XYU.. movement here.
moveBuffer.usePressureAdvance = false;
for (size_t axis = 0; axis < numAxes; ++axis)
{
if (axis != Z_AXIS && moveBuffer.coords[axis] != oldCoords[axis])
if (axis != Z_AXIS && moveBuffer.coords[axis] != moveBuffer.initialCoords[axis])
{
moveBuffer.usePressureAdvance = true;
break;
}
}
moveBuffer.filePos = (&gb == fileGCode) ? filePos : noFilePosition;
moveBuffer.filePos = (&gb == fileGCode) ? gb.MachineState().fileState.GetPosition() : noFilePosition;
moveBuffer.canPauseAfter = (moveBuffer.endStopsToCheck == 0);
//debugPrintf("Queue move pos %u\n", moveFilePos);
}
return (moveBuffer.moveType != 0 || moveBuffer.endStopsToCheck != 0) ? 2 : 1;
@ -1266,7 +1263,9 @@ bool GCodes::ReadMove(RawMove& m)
}
else
{
// This move needs to be divided into 2 or more segments
// This move needs to be divided into 2 or more segments. We can only pause after the final segment.
m.canPauseAfter = false;
// Do the axes
for (size_t drive = 0; drive < numAxes; ++drive)
{
@ -1375,52 +1374,65 @@ bool GCodes::DoCannedCycleMove(GCodeBuffer& gb, EndstopChecks ce)
return false;
}
// This handles G92
// This handles G92. Return true if completed, false if it needs to be called again.
bool GCodes::SetPositions(GCodeBuffer& gb)
{
// Don't pause the machine if only extruder drives are being reset (DC, 2015-09-06).
// This avoids blobs and seams when the gcode uses absolute E coordinates and periodically includes G92 E0.
bool includingAxes = false;
for (size_t drive = 0; drive < numAxes; ++drive)
if (gb.Seen('R') && gb.GetIValue() == 1)
{
if (gb.Seen(axisLetters[drive]))
// Babystepping command. All coordinates except Z are ignored.
if (gb.Seen('Z'))
{
includingAxes = true;
break;
const float babystepAmount = gb.GetFValue() * distanceScale;
if (fabs(babystepAmount) <= 1.0) // limit babystepping to 1mm
{
reprap.GetMove()->Babystep(babystepAmount);
}
}
}
if (includingAxes)
else
{
if (!LockMovementAndWaitForStandstill(gb))
// Don't pause the machine if only extruder drives are being reset (DC, 2015-09-06).
// This avoids blobs and seams when the gcode uses absolute E coordinates and periodically includes G92 E0.
bool includingAxes = false;
for (size_t drive = 0; drive < numAxes; ++drive)
{
if (gb.Seen(axisLetters[drive]))
{
includingAxes = true;
break;
}
}
if (includingAxes)
{
if (!LockMovementAndWaitForStandstill(gb)) // lock movement and get current coordinates
{
return false;
}
}
else if (segmentsLeft != 0) // wait for previous move to be taken so that GetCurrentUserPosition returns the correct value
{
return false;
}
}
else if (segmentsLeft != 0) // wait for previous move to be taken so that GetCurrentUserPosition returns the correct value
{
return false;
}
reprap.GetMove()->GetCurrentUserPosition(moveBuffer.coords, 0, reprap.GetCurrentXAxes()); // make sure move buffer is up to date
bool ok = LoadMoveBufferFromGCode(gb, -1);
if (ok && includingAxes)
{
#if SUPPORT_ROLAND
if (reprap.GetRoland()->Active())
const bool ok = LoadMoveBufferFromGCode(gb, -1);
if (ok && includingAxes)
{
for(size_t axis = 0; axis < AXES; axis++)
#if SUPPORT_ROLAND
if (reprap.GetRoland()->Active())
{
if (!reprap.GetRoland()->ProcessG92(moveBuffer[axis], axis))
for(size_t axis = 0; axis < AXES; axis++)
{
return false;
if (!reprap.GetRoland()->ProcessG92(moveBuffer[axis], axis))
{
return false;
}
}
}
}
#endif
SetPositions(moveBuffer.coords);
SetPositions(moveBuffer.coords);
}
}
return true;
}
@ -2974,7 +2986,7 @@ void GCodes::StartToolChange(GCodeBuffer& gb, bool inM109)
}
// Retract or un-retract filament, returning true if movement has been queued, false if this needs to be called again
bool GCodes::RetractFilament(bool retract)
bool GCodes::RetractFilament(GCodeBuffer& gb, bool retract)
{
if (retractLength != 0.0 || retractHop != 0.0 || (!retract && retractExtra != 0.0))
{
@ -3008,7 +3020,8 @@ bool GCodes::RetractFilament(bool retract)
moveBuffer.isFirmwareRetraction = true;
moveBuffer.usePressureAdvance = false;
moveBuffer.filePos = filePos;
moveBuffer.filePos = (&gb == fileGCode) ? gb.MachineState().fileState.GetPosition() : noFilePosition;
moveBuffer.canPauseAfter = !retract; // don't pause after a retraction because that could cause too much retraction
moveBuffer.xAxes = reprap.GetCurrentXAxes();
segmentsLeft = 1;
}

View file

@ -65,12 +65,13 @@ public:
float coords[DRIVES]; // new positions for the axes, amount of movement for the extruders
float initialCoords[MAX_AXES]; // the initial positions of the axes
float feedRate; // feed rate of this move
FilePosition filePos; // offset in the file being printed that this move was read from
FilePosition filePos; // offset in the file being printed at the end of reading this move
uint32_t xAxes; // axes that X is mapped to
EndstopChecks endStopsToCheck; // endstops to check
uint8_t moveType; // the S parameter from the G0 or G1 command, 0 for a normal move
bool isFirmwareRetraction; // true if this is a firmware retraction/un-retraction move
bool usePressureAdvance; // true if we want to us extruder pressure advance, if there is any extrusion
bool canPauseAfter; // true if we can pause just after this move and successfully restart
};
GCodes(Platform* p, Webserver* w);
@ -197,7 +198,7 @@ private:
void SetAllAxesNotHomed(); // Flag all axes as not homed
void SetPositions(float positionNow[DRIVES]); // Set the current position to be this
const char *TranslateEndStopResult(EndStopHit es); // Translate end stop result to text
bool RetractFilament(bool retract); // Retract or un-retract filaments
bool RetractFilament(GCodeBuffer& gb, bool retract); // Retract or un-retract filaments
bool ChangeMicrostepping(size_t drive, int microsteps, int mode) const; // Change microstepping on the specified drive
void ListTriggers(StringRef reply, TriggerMask mask); // Append a list of trigger endstops to a message
void CheckTriggers(); // Check for and execute triggers
@ -280,7 +281,6 @@ private:
float simulationTime; // Accumulated simulation time
uint8_t simulationMode; // 0 = not simulating, 1 = simulating, >1 are simulation modes for debugging
FilePosition filePos; // The position we got up to in the file being printed
// Firmware retraction settings
float retractLength, retractExtra; // retraction length and extra length to un-retract

View file

@ -133,7 +133,7 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, StringRef& reply)
{
return false;
}
result = RetractFilament(true);
result = RetractFilament(gb, true);
}
break;
@ -142,7 +142,7 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, StringRef& reply)
{
return false;
}
result = RetractFilament(false);
result = RetractFilament(gb, false);
break;
case 20: // Inches (which century are we living in, here?)
@ -913,121 +913,127 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
}
else
{
bool seen = false;
Fan& fan = platform->GetFan(fanNum);
if (gb.Seen('I')) // Invert cooling
if (!fan.IsEnabled())
{
const int invert = gb.GetIValue();
if (invert < 0)
reply.printf("Fan number %d is disabled", fanNum);
}
else
{
bool seen = false;
if (gb.Seen('I')) // Invert cooling
{
fan.Disable();
}
else
{
fan.SetInverted(invert > 0);
}
seen = true;
}
if (gb.Seen('F')) // Set PWM frequency
{
fan.SetPwmFrequency(gb.GetFValue());
seen = true;
}
if (gb.Seen('T')) // Set thermostatic trigger temperature
{
seen = true;
fan.SetTriggerTemperature(gb.GetFValue());
}
if (gb.Seen('B')) // Set blip time
{
seen = true;
fan.SetBlipTime(gb.GetFValue());
}
if (gb.Seen('L')) // Set minimum speed
{
seen = true;
fan.SetMinValue(gb.GetFValue());
}
if (gb.Seen('H')) // Set thermostatically-controller heaters
{
seen = true;
long heaters[HEATERS];
size_t numH = HEATERS;
gb.GetLongArray(heaters, numH);
// Note that M106 H-1 disables thermostatic mode. The following code implements that automatically.
uint16_t hh = 0;
for (size_t h = 0; h < numH; ++h)
{
const int hnum = heaters[h];
if (hnum >= 0 && hnum < HEATERS)
const int invert = gb.GetIValue();
if (invert < 0)
{
hh |= (1u << (unsigned int)hnum);
fan.Disable();
}
else
{
fan.SetInverted(invert > 0);
}
seen = true;
}
if (gb.Seen('F')) // Set PWM frequency
{
fan.SetPwmFrequency(gb.GetFValue());
seen = true;
}
if (gb.Seen('T')) // Set thermostatic trigger temperature
{
seen = true;
fan.SetTriggerTemperature(gb.GetFValue());
}
if (gb.Seen('B')) // Set blip time
{
seen = true;
fan.SetBlipTime(gb.GetFValue());
}
if (gb.Seen('L')) // Set minimum speed
{
seen = true;
fan.SetMinValue(gb.GetFValue());
}
if (gb.Seen('H')) // Set thermostatically-controller heaters
{
seen = true;
long heaters[HEATERS];
size_t numH = HEATERS;
gb.GetLongArray(heaters, numH);
// Note that M106 H-1 disables thermostatic mode. The following code implements that automatically.
uint16_t hh = 0;
for (size_t h = 0; h < numH; ++h)
{
const int hnum = heaters[h];
if (hnum >= 0 && hnum < HEATERS)
{
hh |= (1u << (unsigned int)hnum);
}
}
if (hh != 0)
{
platform->SetFanValue(fanNum, 1.0); // default the fan speed to full for safety
}
fan.SetHeatersMonitored(hh);
}
if (gb.Seen('S')) // Set new fan value - process this after processing 'H' or it may not be acted on
{
const float f = constrain<float>(gb.GetFValue(), 0.0, 255.0);
if (seen || seenFanNum)
{
platform->SetFanValue(fanNum, f);
}
else
{
// We are processing an M106 S### command with no other recognised parameters and we have a tool selected.
// Apply the fan speed setting to the fans in the fan mapping for the current tool.
lastDefaultFanSpeed = f;
SetMappedFanSpeed();
}
}
if (hh != 0)
else if (gb.Seen('R'))
{
platform->SetFanValue(fanNum, 1.0); // default the fan speed to full for safety
}
fan.SetHeatersMonitored(hh);
}
if (gb.Seen('S')) // Set new fan value - process this after processing 'H' or it may not be acted on
{
const float f = constrain<float>(gb.GetFValue(), 0.0, 255.0);
if (seen || seenFanNum)
{
platform->SetFanValue(fanNum, f);
}
else
{
// We are processing an M106 S### command with no other recognised parameters and we have a tool selected.
// Apply the fan speed setting to the fans in the fan mapping for the current tool.
lastDefaultFanSpeed = f;
SetMappedFanSpeed();
}
}
else if (gb.Seen('R'))
{
const int i = gb.GetIValue();
switch(i)
{
case 0:
case 1:
// Restore fan speed to value when print was paused
platform->SetFanValue(fanNum, pausedFanValues[fanNum]);
break;
case 2:
// Set the speeds of mapped fans to the last known value. Fan number is ignored.
SetMappedFanSpeed();
break;
default:
break;
}
}
else if (!seen)
{
reply.printf("Fan%i frequency: %dHz, speed: %d%%, min: %d%%, blip: %.2f, inverted: %s",
fanNum,
(int)(fan.GetPwmFrequency()),
(int)(fan.GetValue() * 100.0),
(int)(fan.GetMinValue() * 100.0),
fan.GetBlipTime(),
(fan.GetInverted()) ? "yes" : "no");
uint16_t hh = fan.GetHeatersMonitored();
if (hh != 0)
{
reply.catf(", trigger: %dC, heaters:", (int)fan.GetTriggerTemperature());
for (unsigned int i = 0; i < HEATERS; ++i)
const int i = gb.GetIValue();
switch(i)
{
if ((hh & (1u << i)) != 0)
case 0:
case 1:
// Restore fan speed to value when print was paused
platform->SetFanValue(fanNum, pausedFanValues[fanNum]);
break;
case 2:
// Set the speeds of mapped fans to the last known value. Fan number is ignored.
SetMappedFanSpeed();
break;
default:
break;
}
}
else if (!seen)
{
reply.printf("Fan%i frequency: %dHz, speed: %d%%, min: %d%%, blip: %.2f, inverted: %s",
fanNum,
(int)(fan.GetPwmFrequency()),
(int)(fan.GetValue() * 100.0),
(int)(fan.GetMinValue() * 100.0),
fan.GetBlipTime(),
(fan.GetInverted()) ? "yes" : "no");
uint16_t hh = fan.GetHeatersMonitored();
if (hh != 0)
{
reply.catf(", trigger: %dC, heaters:", (int)fan.GetTriggerTemperature());
for (unsigned int i = 0; i < HEATERS; ++i)
{
reply.catf(" %u", i);
if ((hh & (1u << i)) != 0)
{
reply.catf(" %u", i);
}
}
}
}
@ -1491,13 +1497,6 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
}
}
if (gb.Seen('P'))
{
// Set max average printing acceleration
platform->SetMaxAverageAcceleration(gb.GetFValue() * distanceScale);
seen = true;
}
if (!seen)
{
reply.printf("Accelerations: ");
@ -1512,7 +1511,6 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
reply.catf("%c%.1f", sep, platform->Acceleration(extruder + numAxes) / distanceScale);
sep = ':';
}
reply.catf(", avg. printing: %.1f", platform->GetMaxAverageAcceleration());
}
}
break;
@ -2591,6 +2589,10 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
}
seen = true;
}
if (gb.Seen('F'))
{
platform->SetExtrusionAncilliaryPwmFrequency(gb.GetFValue());
}
if (gb.Seen('S'))
{
platform->SetExtrusionAncilliaryPwmValue(gb.GetFValue());
@ -2598,8 +2600,10 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply)
}
if (!seen)
{
reply.printf("Extrusion ancillary PWM %.3f on pin %u",
platform->GetExtrusionAncilliaryPwmValue(), platform->GetExtrusionAncilliaryPwmPin());
reply.printf("Extrusion ancillary PWM %.3f at %.1fHz on pin %u",
platform->GetExtrusionAncilliaryPwmValue(),
platform->GetExtrusionAncilliaryPwmFrequency(),
platform->GetExtrusionAncilliaryPwmPin());
}
}
break;

View file

@ -240,6 +240,7 @@ bool DDA::Init(const GCodes::RawMove &nextMove, bool doMotorMapping)
// 3. Store some values
endStopsToCheck = nextMove.endStopsToCheck;
canPauseAfter = nextMove.canPauseAfter;
filePos = nextMove.filePos;
usePressureAdvance = nextMove.usePressureAdvance;
hadLookaheadUnderrun = false;
@ -564,15 +565,14 @@ void DDA::RecalculateMove()
topSpeed = requestedSpeed;
}
canPause = (endStopsToCheck == 0);
if (canPause && endSpeed != 0.0)
if (canPauseAfter && endSpeed != 0.0)
{
const Platform * const p = reprap.GetPlatform();
for (size_t drive = 0; drive < DRIVES; ++drive)
{
if (ddm[drive].state == DMState::moving && endSpeed * fabsf(directionVector[drive]) > p->ActualInstantDv(drive))
{
canPause = false;
canPauseAfter = false;
break;
}
}
@ -724,44 +724,6 @@ void DDA::Prepare()
float decelStartTime = accelStopTime + (params.decelStartDistance - accelDistance)/topSpeed;
float totalTime = decelStartTime + (topSpeed - endSpeed)/acceleration;
// Enforce the maximum average acceleration
if (isPrintingMove && topSpeed > startSpeed && topSpeed > endSpeed)
{
const float maxAverageAcceleration = reprap.GetPlatform()->GetMaxAverageAcceleration();
if (2 * topSpeed - startSpeed - endSpeed > totalTime * maxAverageAcceleration)
{
// Reduce the top speed to comply with the maximum average acceleration
const float a2 = fsquare(acceleration);
const float am2 = fsquare(maxAverageAcceleration);
const float aam = acceleration * maxAverageAcceleration;
const float discriminant = (a2 + (2 * aam) - am2) * (fsquare(startSpeed) + fsquare(endSpeed))
+ (2 * a2 + 2 * am2 - 4 * aam) * startSpeed * endSpeed
+ (8 * a2 * maxAverageAcceleration - 4 * acceleration * am2) * totalDistance;
const float oldTopSpeed = topSpeed;
if (discriminant < 0.0)
{
topSpeed = max<float>(startSpeed, endSpeed);
}
else
{
const float temp = (sqrtf(discriminant) + (acceleration - maxAverageAcceleration) * (startSpeed + endSpeed))
/(4 * acceleration - 2 * maxAverageAcceleration);
topSpeed = max<float>(max<float>(temp, startSpeed), endSpeed);
}
//DEBUG
debugPrintf("ots %f nts %f ss %f es %f\n", oldTopSpeed, topSpeed, startSpeed, endSpeed);
// Recalculate parameters
accelDistance = (fsquare(topSpeed) - fsquare(startSpeed))/(2 * acceleration);
decelDistance = (fsquare(topSpeed) - fsquare(endSpeed))/(2 * acceleration);
params.decelStartDistance = totalDistance - decelDistance;
accelStopTime = (topSpeed - startSpeed)/acceleration;
decelStartTime = accelStopTime + (params.decelStartDistance - accelDistance)/topSpeed;
totalTime = decelStartTime + (topSpeed - endSpeed)/acceleration;
}
}
clocksNeeded = (uint32_t)(totalTime * stepClockRate);
params.startSpeedTimesCdivA = (uint32_t)((startSpeed * stepClockRate)/acceleration);

View file

@ -47,7 +47,7 @@ public:
void Prepare(); // Calculate all the values and freeze this DDA
float CalcTime() const; // Calculate the time needed for this move (used for simulation)
bool HasStepError() const;
bool CanPause() const { return canPause; }
bool CanPauseAfter() const { return canPauseAfter; }
bool IsPrintingMove() const { return isPrintingMove; } // Return true if this involves both XY movement and extrusion
DDAState GetState() const { return state; }
@ -117,7 +117,7 @@ private:
volatile DDAState state; // what state this DDA is in
uint8_t endCoordinatesValid : 1; // True if endCoordinates can be relied on
uint8_t isDeltaMovement : 1; // True if this is a delta printer movement
uint8_t canPause : 1; // True if we can pause at the end of this move
uint8_t canPauseAfter : 1; // True if we can pause at the end of this move
uint8_t goingSlow : 1; // True if we have reduced speed during homing
uint8_t isPrintingMove : 1; // True if this move includes XY movement and extrusion
uint8_t usePressureAdvance : 1; // True if pressure advance should be applied to any forward extrusion

View file

@ -46,6 +46,7 @@ void Move::Init()
currentDda = nullptr;
addNoMoreMoves = false;
babysteppingLeft = 0.0;
stepErrors = 0;
numLookaheadUnderruns = numPrepareUnderruns = 0;
@ -311,11 +312,13 @@ FilePosition Move::PausePrint(float positions[DRIVES], float& pausedFeedRate, ui
const DDA *savedDdaRingAddPointer = ddaRingAddPointer;
cpu_irq_disable();
DDA *dda = currentDda;
FilePosition fPos = noFilePosition;
if (dda != nullptr)
{
// A move is being executed. See if we can safely pause at the end of it.
if (dda->CanPause())
if (dda->CanPauseAfter())
{
fPos = dda->GetFilePosition();
ddaRingAddPointer = dda->GetNext();
}
else
@ -325,8 +328,9 @@ FilePosition Move::PausePrint(float positions[DRIVES], float& pausedFeedRate, ui
dda = ddaRingGetPointer;
while (dda != ddaRingAddPointer)
{
if (dda->CanPause())
if (dda->CanPauseAfter())
{
fPos = dda->GetFilePosition();
ddaRingAddPointer = dda->GetNext();
if (ddaRingAddPointer->GetState() == DDA::frozen)
{
@ -347,8 +351,6 @@ FilePosition Move::PausePrint(float positions[DRIVES], float& pausedFeedRate, ui
cpu_irq_enable();
FilePosition fPos = noFilePosition;
if (ddaRingAddPointer != savedDdaRingAddPointer)
{
const size_t numAxes = reprap.GetGCodes()->GetNumAxes();
@ -372,10 +374,6 @@ FilePosition Move::PausePrint(float positions[DRIVES], float& pausedFeedRate, ui
{
positions[drive] += dda->GetEndCoordinate(drive, true); // update the amount of extrusion we are going to skip
}
if (fPos == noFilePosition)
{
fPos = dda->GetFilePosition();
}
(void)dda->Free();
dda = dda->GetNext();
}
@ -389,6 +387,13 @@ FilePosition Move::PausePrint(float positions[DRIVES], float& pausedFeedRate, ui
return fPos;
}
// Request babystepping
void Move::Babystep(float zMovement)
{
babysteppingLeft += zMovement;
// TODO use this value somewhere
}
uint32_t maxReps = 0;
#if 0
@ -402,7 +407,7 @@ void Move::Diagnostics(MessageType mtype)
Platform * const p = reprap.GetPlatform();
p->Message(mtype, "=== Move ===\n");
p->MessageF(mtype, "MaxReps: %u, StepErrors: %u, MaxWait: %ums, Underruns: %u, %u\n",
maxReps, stepErrors, longestGcodeWaitInterval, numLookaheadUnderruns, numPrepareUnderruns);
maxReps, stepErrors, longestGcodeWaitInterval, numLookaheadUnderruns, numPrepareUnderruns);
maxReps = 0;
numLookaheadUnderruns = numPrepareUnderruns = 0;
longestGcodeWaitInterval = 0;

View file

@ -117,6 +117,7 @@ public:
void UseHeightMap(bool b) { useGridHeights = b; } // Start or stop using the height map
bool UsingHeightMap() const { return useGridHeights; } // Are we doing grid bed compensation?
void Babystep(float zMovement); // Request babystepping
private:
@ -192,6 +193,8 @@ private:
int coreXYMode; // 0 = Cartesian, 1 = CoreXY, 2 = CoreXZ, 3 = CoreYZ
float axisFactors[MAX_AXES]; // How much further the motors need to move for each axis movement, on a CoreXY/CoreXZ/CoreYZ machine
unsigned int stepErrors; // count of step errors, for diagnostics
float babysteppingLeft; // the amount of Z babystepping left to do
};
//******************************************************************************************************

View file

@ -227,8 +227,6 @@ void Platform::Init()
stepperDacVoltageOffset = STEPPER_DAC_VOLTAGE_OFFSET;
#endif
maxAverageAcceleration = 10000.0; // high enough to have no effect until it is changed
// Z PROBE
zProbePin = Z_PROBE_PIN;
zProbeAdcChannel = PinToAdcChannel(zProbePin);
@ -326,12 +324,17 @@ void Platform::Init()
TMC2660::Init(ENABLE_PINS, numTMC2660Drivers);
#endif
// Allow extrusion ancilliary PWM to use FAN0 even if FAN0 has not been disabled, for backwards compatibility
extrusionAncilliaryPwmValue = 0.0;
extrusionAncilliaryPwmLogicalPin = -1;
extrusionAncilliaryPwmFirmwarePin = NoPin;
extrusionAncilliaryPwmInvert = false;
SetExtrusionAncilliaryPwmPin(Fan0LogicalPin);
extrusionAncilliaryPwmFrequency = DefaultPinWritePwmFreq;
extrusionAncilliaryPwmLogicalPin = Fan0LogicalPin;
extrusionAncilliaryPwmFirmwarePin = COOLING_FAN_PINS[0];
extrusionAncilliaryPwmInvert =
#if defined(DUET_NG) || defined(__RADDS__)
false;
#else
(board == BoardType::Duet_06 || board == BoardType::Duet_07);
#endif
ARRAY_INIT(tempSensePins, TEMP_SENSE_PINS);
ARRAY_INIT(heatOnPins, HEAT_ON_PINS);
ARRAY_INIT(spiTempSenseCsPins, SpiTempSensorCsPins);
@ -1243,14 +1246,41 @@ void Platform::SoftwareReset(uint16_t reason)
reason |= (uint16_t)reprap.GetSpinningModule();
// Record the reason for the software reset
SoftwareResetData temp;
temp.magic = SoftwareResetData::magicValue;
temp.version = SoftwareResetData::versionValue;
temp.resetReason = reason;
GetStackUsage(NULL, NULL, &temp.neverUsedRam);
// First find a free slot (wear levelling)
size_t slot = SoftwareResetData::numberOfSlots;
SoftwareResetData srdBuf[SoftwareResetData::numberOfSlots];
// Save diagnostics data to Flash and reset the software
DueFlashStorage::write(SoftwareResetData::nvAddress, &temp, sizeof(SoftwareResetData));
#ifdef DUET_NG
if (flash_read_user_signature(reinterpret_cast<uint32_t*>(srdBuf), sizeof(srdBuf)/sizeof(uint32_t)) == FLASH_RC_OK)
#else
DueFlashStorage::read(SoftwareResetData::nvAddress, srdBuf, sizeof(srdBuf));
#endif
{
while (slot != 0 && srdBuf[slot - 1].isVacant())
{
--slot;
}
}
if (slot == SoftwareResetData::numberOfSlots)
{
// No free slots, so erase the area
#ifdef DUET_NG
flash_erase_user_signature();
#endif
memset(srdBuf, 0xFF, sizeof(srdBuf));
slot = 0;
}
srdBuf[slot].magic = SoftwareResetData::magicValue;
srdBuf[slot].resetReason = reason;
GetStackUsage(NULL, NULL, &srdBuf[slot].neverUsedRam);
// Save diagnostics data to Flash
#ifdef DUET_NG
flash_write_user_signature(srdBuf, sizeof(srdBuf)/sizeof(uint32_t));
#else
DueFlashStorage::write(SoftwareResetData::nvAddress, srdBuf, sizeof(srdBuf));
#endif
}
Reset();
for(;;) {}
@ -1365,7 +1395,7 @@ void Platform::Diagnostics(MessageType mtype)
MessageF(mtype, "Program static ram used: %d\n", &_end - ramstart);
MessageF(mtype, "Dynamic ram used: %d\n", mi.uordblks);
MessageF(mtype, "Recycled dynamic ram: %d\n", mi.fordblks);
size_t currentStack, maxStack, neverUsed;
uint32_t currentStack, maxStack, neverUsed;
GetStackUsage(&currentStack, &maxStack, &neverUsed);
MessageF(mtype, "Current stack ram used: %d\n", currentStack);
MessageF(mtype, "Maximum stack ram used: %d\n", maxStack);
@ -1373,20 +1403,40 @@ void Platform::Diagnostics(MessageType mtype)
// Show the up time and reason for the last reset
const uint32_t now = (uint32_t)Time(); // get up time in seconds
const char* resetReasons[8] = { "power up", "backup", "watchdog", "software", "external", "?", "?", "?" };
const char* resetReasons[8] = { "power up", "backup", "watchdog", "software", "reset button", "?", "?", "?" };
MessageF(mtype, "Last reset %02d:%02d:%02d ago, cause: %s\n",
(unsigned int)(now/3600), (unsigned int)((now % 3600)/60), (unsigned int)(now % 60),
resetReasons[(REG_RSTC_SR & RSTC_SR_RSTTYP_Msk) >> RSTC_SR_RSTTYP_Pos]);
// Show the reset code stored at the last software reset
Message(mtype, "Last software reset code & available RAM: ");
{
SoftwareResetData temp;
temp.magic = 0;
DueFlashStorage::read(SoftwareResetData::nvAddress, &temp, sizeof(SoftwareResetData));
if (temp.magic == SoftwareResetData::magicValue && temp.version == SoftwareResetData::versionValue)
SoftwareResetData srdBuf[SoftwareResetData::numberOfSlots];
memset(srdBuf, 0, sizeof(srdBuf));
int slot = -1;
#ifdef DUET_NG
if (flash_read_user_signature(reinterpret_cast<uint32_t*>(srdBuf), sizeof(srdBuf)/sizeof(uint32_t)) == FLASH_RC_OK)
#else
DueFlashStorage::read(SoftwareResetData::nvAddress, srdBuf, sizeof(srdBuf));
#endif
{
MessageF(mtype, "Last software reset code & available RAM: 0x%04x, %u\n", temp.resetReason, temp.neverUsedRam);
MessageF(mtype, "Spinning module during software reset: %s\n", moduleName[temp.resetReason & 0x0F]);
// Find the last slot written
slot = SoftwareResetData::numberOfSlots - 1;
while (slot >= 0 && srdBuf[slot].magic == 0xFFFF)
{
--slot;
}
}
if (slot >= 0 && srdBuf[slot].magic == SoftwareResetData::magicValue)
{
MessageF(mtype, "0x%04x, %u (slot %d)\n", srdBuf[slot].resetReason, srdBuf[slot].neverUsedRam, slot);
MessageF(mtype, "Spinning module during software reset: %s\n", moduleName[srdBuf[slot].resetReason & 0x0F]);
}
else
{
Message(mtype, "not available\n");
}
}
@ -1447,13 +1497,18 @@ void Platform::Diagnostics(MessageType mtype)
#endif
// Show current RTC time
Message(mtype, "Current date and time: ");
struct tm timeInfo;
if (gmtime_r(&realTime, &timeInfo) != nullptr)
{
MessageF(mtype, "Current date and time: %04u-%02u-%02u %02u:%02u:%02u\n",
MessageF(mtype, "%04u-%02u-%02u %02u:%02u:%02u\n",
timeInfo.tm_year + 1900, timeInfo.tm_mon + 1, timeInfo.tm_mday,
timeInfo.tm_hour, timeInfo.tm_min, timeInfo.tm_sec);
}
else
{
Message(mtype, "clock not set\n");
}
// Debug
//MessageF(mtype, "TC_FMR = %08x, PWM_FPE = %08x, PWM_FSR = %08x\n", TC2->TC_FMR, PWM->PWM_FPE, PWM->PWM_FSR);
@ -1503,24 +1558,24 @@ void Platform::DiagnosticTest(int d)
}
// Return the stack usage and amount of memory that has never been used, in bytes
void Platform::GetStackUsage(size_t* currentStack, size_t* maxStack, size_t* neverUsed) const
void Platform::GetStackUsage(uint32_t* currentStack, uint32_t* maxStack, uint32_t* neverUsed) const
{
const char *ramend = (const char *)
#ifdef DUET_NG
0x20020000; // 0x20000000 + 128Kb
const char * const ramend = (const char *)
#if SAM4E
IRAM_ADDR + IRAM_SIZE;
#else
0x20088000; // 0x20070000 + 96Kb
IRAM0_ADDR + IRAM_SIZE;
#endif
register const char * stack_ptr asm ("sp");
const char *heapend = sbrk(0);
const char* stack_lwm = heapend;
const char * const heapend = sbrk(0);
const char * stack_lwm = heapend;
while (stack_lwm < stack_ptr && *stack_lwm == memPattern)
{
++stack_lwm;
}
if (currentStack) { *currentStack = ramend - stack_ptr; }
if (maxStack) { *maxStack = ramend - stack_lwm; }
if (neverUsed) { *neverUsed = stack_lwm - heapend; }
if (currentStack != nullptr) { *currentStack = ramend - stack_ptr; }
if (maxStack != nullptr) { *maxStack = ramend - stack_lwm; }
if (neverUsed != nullptr) { *neverUsed = stack_lwm - heapend; }
}
void Platform::ClassReport(float &lastTime)
@ -2541,11 +2596,11 @@ void Platform::SetBoardType(BoardType bt)
#else
// Determine whether this is a Duet 0.6 or a Duet 0.8.5 board.
// If it is a 0.85 board then DAC0 (AKA digital pin 67) is connected to ground via a diode and a 2.15K resistor.
// So we enable the pullup (value 150K-150K) on pin 67 and read it, expecting a LOW on a 0.8.5 board and a HIGH on a 0.6 board.
// So we enable the pullup (value 100K-150K) on pin 67 and read it, expecting a LOW on a 0.8.5 board and a HIGH on a 0.6 board.
// This may fail if anyone connects a load to the DAC0 pin on a Duet 0.6, hence we implement board selection in M115 as well.
pinMode(Dac0DigitalPin, INPUT_PULLUP);
board = (digitalRead(Dac0DigitalPin)) ? BoardType::Duet_06 : BoardType::Duet_085;
pinMode(Dac0DigitalPin, INPUT); // turn pullup off
pinMode(Dac0DigitalPin, INPUT); // turn pullup off
#endif
}
else
@ -2628,6 +2683,9 @@ bool Platform::GetFirmwarePin(int logicalPin, PinAccess access, Pin& firmwarePin
)
{
firmwarePin = COOLING_FAN_PINS[logicalPin - Fan0LogicalPin];
#if !defined(DUET_NG) && !defined(__RADDS__)
invert = (board == BoardType::Duet_06 || board == BoardType::Duet_07);
#endif
}
}
else if (logicalPin >= EndstopXLogicalPin && logicalPin < EndstopXLogicalPin + (int)ARRAY_SIZE(endStopPins)) // pins 40-49 correspond to endstop pins

View file

@ -509,10 +509,6 @@ public:
float Acceleration(size_t drive) const;
const float* Accelerations() const;
void SetAcceleration(size_t drive, float value);
const float GetMaxAverageAcceleration() const
{ return maxAverageAcceleration; }
void SetMaxAverageAcceleration(float f)
{ maxAverageAcceleration = f; }
float MaxFeedrate(size_t drive) const;
const float* MaxFeedrates() const;
void SetMaxFeedrate(size_t drive, float value);
@ -567,6 +563,8 @@ public:
void SetExtrusionAncilliaryPwmValue(float v);
float GetExtrusionAncilliaryPwmValue() const;
void SetExtrusionAncilliaryPwmFrequency(float f);
float GetExtrusionAncilliaryPwmFrequency() const;
bool SetExtrusionAncilliaryPwmPin(int logicalPin);
int GetExtrusionAncilliaryPwmPin() const { return extrusionAncilliaryPwmLogicalPin; }
void ExtrudeOn();
@ -681,31 +679,41 @@ private:
static float AdcReadingToPowerVoltage(uint16_t reading);
#endif
// These are the structures used to hold out non-volatile data.
// The SAM3X doesn't have EEPROM so we save the data to flash. This unfortunately means that it gets cleared
// These are the structures used to hold our non-volatile data.
// The SAM3X and SAM4E don't have EEPROM so we save the data to flash. This unfortunately means that it gets cleared
// every time we reprogram the firmware via bossa, but it can be retained when firmware updates are performed
// via the web interface. That's why it's a good idea to implement versioning here - increase these values
// whenever the fields of the following structs have changed.
//
// The SAM4E has a large page erase size (8K). For this reason we store the software reset data in the 512-byte user signature area
// instead, which doesn't get cleared when the Erase button is pressed. The SoftareResetData struct must have at least one 32-bit
// field to guarantee that values of this type will be 32-bit aligned. It must have no virtual members because it is read/written
// directly form/to flash memory.
struct SoftwareResetData
{
static const uint16_t magicValue = 0x7C5F; // value we use to recognise that all the flash data has been written
static const uint16_t versionValue = 1; // increment this whenever this struct changes
static const uint16_t versionValue = 2; // increment this whenever this struct changes
static const uint16_t magicValue = 0x7D00 | versionValue; // value we use to recognise that all the flash data has been written
static const uint32_t nvAddress = 0; // must be 4-byte aligned
static const size_t numberOfSlots = 8; // number of storage slots used to implement wear levelling
uint16_t magic;
uint16_t version;
uint16_t magic; // the magic number, including the version
uint16_t resetReason; // this records why we did a software reset, for diagnostic purposes
uint16_t dummy; // padding to align the next field (should happen automatically I think)
size_t neverUsedRam; // the amount of never used RAM at the last abnormal software reset
uint32_t neverUsedRam; // the amount of never used RAM at the last abnormal software reset
bool isVacant() const // return true if this struct can be written without erasing it first
{
return magic == 0xFFFF && resetReason == 0xFFFF && neverUsedRam == 0xFFFFFFFF;
}
};
static_assert(SoftwareResetData::numberOfSlots * sizeof(SoftwareResetData) <= 512, "Can't fit software reset data in SAM4E user signature area");
// The following struct is due to be replaced by the config-override.g file.
struct FlashData
{
static const uint16_t magicValue = 0xE6C4; // value we use to recognise that the flash data has been written
static const uint16_t versionValue = 5; // increment this whenever this struct changes
static const uint32_t nvAddress = (SoftwareResetData::nvAddress + sizeof(SoftwareResetData) + 3) & (~3);
static const uint32_t nvAddress = SoftwareResetData::nvAddress + (SoftwareResetData::numberOfSlots * sizeof(SoftwareResetData));
uint16_t magic;
uint16_t version;
@ -742,7 +750,7 @@ private:
uint32_t errorCodeBits;
void InitialiseInterrupts();
void GetStackUsage(size_t* currentStack, size_t* maxStack, size_t* neverUsed) const;
void GetStackUsage(uint32_t* currentStack, uint32_t* maxStack, uint32_t* neverUsed) const;
// DRIVES
@ -770,7 +778,6 @@ private:
uint32_t slowDriverStepPulseClocks; // minimum high and low step pulse widths, in processor clocks
uint32_t slowDrivers; // bitmap of driver port bits that need extended step pulse timing
float idleCurrentFactor;
float maxAverageAcceleration;
#if defined(DUET_NG)
size_t numTMC2660Drivers; // the number of TMC2660 drivers we have, the remaining are simple enable/step/dir drivers
@ -793,6 +800,7 @@ private:
volatile ThermistorAveragingFilter thermistorFilters[HEATERS]; // bed and extruder thermistor readings
float extrusionAncilliaryPwmValue;
float extrusionAncilliaryPwmFrequency;
int extrusionAncilliaryPwmLogicalPin;
Pin extrusionAncilliaryPwmFirmwarePin;
bool extrusionAncilliaryPwmInvert;
@ -1125,6 +1133,16 @@ inline float Platform::GetExtrusionAncilliaryPwmValue() const
return extrusionAncilliaryPwmValue;
}
inline void Platform::SetExtrusionAncilliaryPwmFrequency(float f)
{
extrusionAncilliaryPwmFrequency = f;
}
inline float Platform::GetExtrusionAncilliaryPwmFrequency() const
{
return extrusionAncilliaryPwmFrequency;
}
// For the Duet we use the fan output for this
// DC 2015-03-21: To allow users to control the cooling fan via gcodes generated by slic3r etc.,
// only turn the fan on/off if the extruder ancilliary PWM has been set nonzero.
@ -1133,7 +1151,8 @@ inline void Platform::ExtrudeOn()
{
if (extrusionAncilliaryPwmValue > 0.0)
{
WriteAnalog(extrusionAncilliaryPwmFirmwarePin, extrusionAncilliaryPwmValue, DefaultPinWritePwmFreq);
WriteAnalog(extrusionAncilliaryPwmFirmwarePin,
(extrusionAncilliaryPwmInvert) ? 1.0 - extrusionAncilliaryPwmValue : extrusionAncilliaryPwmValue, extrusionAncilliaryPwmFrequency);
}
}
@ -1144,7 +1163,8 @@ inline void Platform::ExtrudeOff()
{
if (extrusionAncilliaryPwmValue > 0.0)
{
WriteAnalog(extrusionAncilliaryPwmFirmwarePin, 0.0, DefaultPinWritePwmFreq);
WriteAnalog(extrusionAncilliaryPwmFirmwarePin,
(extrusionAncilliaryPwmInvert) ? 1.0 : 0.0, extrusionAncilliaryPwmFrequency);
}
}

View file

@ -306,15 +306,22 @@ void Tool::SetVariables(const float* standby, const float* active)
else
{
const float temperatureLimit = reprap.GetHeat()->GetTemperatureLimit(heaters[heater]);
const Tool * const currentTool = reprap.GetCurrentTool();
if (active[heater] < temperatureLimit)
{
activeTemperatures[heater] = active[heater];
reprap.GetHeat()->SetActiveTemperature(heaters[heater], activeTemperatures[heater]);
if (currentTool == nullptr || currentTool == this)
{
reprap.GetHeat()->SetActiveTemperature(heaters[heater], activeTemperatures[heater]);
}
}
if (standby[heater] < temperatureLimit)
{
standbyTemperatures[heater] = standby[heater];
reprap.GetHeat()->SetStandbyTemperature(heaters[heater], standbyTemperatures[heater]);
if (currentTool == nullptr || currentTool == this)
{
reprap.GetHeat()->SetStandbyTemperature(heaters[heater], standbyTemperatures[heater]);
}
}
}
}