Version 1.09s beta 4

Enhancements:
- Implemented M143 and M350
- Wait until movement finished when processing M906
- Allow for additional axes in M906 reporting code
- Added support for external drivers module
- Aux2 device support is now conditional
- Added separate error code for temperature above safety limit
Bug fixes:
- Fixed spurious error report when processing corrupt input line
- When there is a temperature error, return the correct error code
- Update the overheat ADC value when changing thermistor parameters
- Fixed occasional divide by zero problem in PrintMonitor that led to
AJAX errors
- Cold extrusion prevention only checks the active tool, to allow the
same extruder and heater to be configured in multiple tools
- If extrusion is prevented because of a temperature fault, display a
message instead of silently preventing extrusion
This commit is contained in:
David Crocker 2016-03-09 14:34:01 +00:00
parent a5722accc7
commit bac9eb516e
15 changed files with 498 additions and 252 deletions

View file

@ -26,17 +26,20 @@ Licence: GPL
#define NAME "RepRapFirmware"
#ifndef VERSION
#define VERSION "1.09r-dc42"
#define VERSION "1.09s-dc42-beta4"
#endif
#ifndef DATE
#define DATE "2016-01-16"
#define DATE "2016-03-08"
#endif
#define AUTHORS "reprappro, dc42, zpl, t3p3, dnewman"
#define FLASH_SAVE_ENABLED (1)
//#define EXTERNAL_DRIVERS (1)
//#define FIRST_EXTERNAL_DRIVE (4)
// Other firmware that we might switch to be compatible with.
enum Compatibility
@ -81,9 +84,9 @@ const float HOT_ENOUGH_TO_EXTRUDE = 160.0; // Celsius
const float HOT_ENOUGH_TO_RETRACT = 90.0; // Celsius
const float TIME_TO_HOT = 150.0; // Seconds
const uint8_t MAX_BAD_TEMPERATURE_COUNT = 6; // Number of bad temperature samples before a heater fault is reported
const uint8_t MAX_BAD_TEMPERATURE_COUNT = 4; // Number of bad temperature samples permitted before a heater fault is reported
const float BAD_LOW_TEMPERATURE = -10.0; // Celsius
const float BAD_HIGH_TEMPERATURE = 300.0; // Celsius
const float DEFAULT_TEMPERATURE_LIMIT = 300.0; // Celsius
const float HOT_END_FAN_TEMPERATURE = 45.0; // Temperature at which a thermostatic hot end fan comes on
const float BAD_ERROR_TEMPERATURE = 2000.0; // must exceed BAD_HIGH_TEMPERATURE

21
ExternalDrivers.h Normal file
View file

@ -0,0 +1,21 @@
/*
* ExternalDrivers.h
*
* Created on: 23 Jan 2016
* Author: David
*/
#ifndef EXTERNALDRIVERS_H_
#define EXTERNALDRIVERS_H_
namespace ExternalDrivers
{
void Init();
void SetCurrent(size_t drive, float current);
void EnableDrive(size_t drive, bool en);
uint32_t GetStatus(size_t drive);
bool SetMicrostepping(size_t drive, int microsteps, int mode);
unsigned int GetMicrostepping(size_t drive, bool& interpolation);
};
#endif /* EXTERNALDRIVERS_H_ */

View file

@ -65,12 +65,14 @@ bool GCodeBuffer::Put(char c)
// Deal with line numbers and checksums
if (Seen('*'))
{
int csSent = GetIValue();
int csHere = CheckSum();
Seen('N');
const int csSent = GetIValue();
const int csHere = CheckSum();
if (csSent != csHere)
{
if (Seen('N'))
{
snprintf(gcodeBuffer, GCODE_LENGTH, "M998 P%d", GetIValue());
}
Init();
return true;
}

View file

@ -3550,8 +3550,19 @@ bool GCodes::HandleMcode(GCodeBuffer* gb, StringRef& reply)
}
break;
case 143: // Set temperature limit
if (gb->Seen('S'))
{
platform->SetTemperatureLimit(gb->GetFValue());
}
else
{
reply.printf("Temperature limit is %.1fC", platform->GetTemperatureLimit());
}
break;
case 144: // Set bed to standby
#if BED_HEATER != -1
#if BED_HEATER >= 0
reprap.GetHeat()->Standby(BED_HEATER);
#else
reply.copy("Hot bed is not present!");
@ -3559,14 +3570,7 @@ bool GCodes::HandleMcode(GCodeBuffer* gb, StringRef& reply)
#endif
break;
// case 160: //number of mixing filament drives TODO: With tools defined, is this needed?
// if(gb->Seen('S'))
// {
// platform->SetMixingDrives(gb->GetIValue());
// }
// break;
case 190: // Deprecated...
case 190: // Set bed temperature and wait
if (!AllMovesAreFinishedAndMoveBufferIsLoaded()) // tell Move not to wait for more moves
{
return false;
@ -3818,9 +3822,75 @@ bool GCodes::HandleMcode(GCodeBuffer* gb, StringRef& reply)
SetHeaterParameters(gb, reply);
break;
case 350: // Set/report microstepping
if (!AllMovesAreFinishedAndMoveBufferIsLoaded())
{
return false;
}
{
// interp is current an int not a bool, because we use special values of interp to set the chopper control register
int interp = 0;
if (gb->Seen('I'))
{
interp = gb->GetIValue();
}
bool seen = false;
for (size_t axis = 0; axis < AXES; axis++)
{
if (gb->Seen(axisLetters[axis]))
{
seen = true;
int microsteps = gb->GetIValue();
if (!platform->SetMicrostepping(axis, microsteps, interp))
{
platform->MessageF(GENERIC_MESSAGE, "Drive %c does not support %dx microstepping%s\n",
axisLetters[axis], microsteps, (interp) ? " with interpolation" : "");
}
}
}
if (gb->Seen(extrudeLetter))
{
seen = true;
long eVals[DRIVES - AXES];
size_t eCount = DRIVES - AXES;
gb->GetLongArray(eVals, eCount);
for (size_t e = 0; e < eCount; e++)
{
if (!platform->SetMicrostepping(AXES + e, (int)eVals[e], interp))
{
platform->MessageF(GENERIC_MESSAGE, "Drive E%u does not support %dx microstepping%s\n",
e, (int)eVals[e], (interp) ? " with interpolation" : "");
}
}
}
if (!seen)
{
reply.copy("Microstepping - ");
for (size_t axis = 0; axis < AXES; ++axis)
{
bool interp;
int microsteps = platform->GetMicrostepping(axis, interp);
reply.catf("%c:%d%s, ", axisLetters[axis], microsteps, (interp) ? "(on)" : "");
}
reply.cat("E");
for (size_t drive = AXES; drive < DRIVES; drive++)
{
bool interp;
int microsteps = platform->GetMicrostepping(drive, interp);
reply.catf(":%d%s", microsteps, (interp) ? "(on)" : "");
}
}
}
break;
case 400: // Wait for current moves to finish
if (!AllMovesAreFinishedAndMoveBufferIsLoaded())
{
return false;
}
break;
case 404: // Filament width and nozzle diameter
@ -4610,7 +4680,7 @@ bool GCodes::HandleMcode(GCodeBuffer* gb, StringRef& reply)
break;
#endif
case 579: // Scale Cartesian axes (for Delta configurations)
case 579: // Scale Cartesian axes (mostly for Delta configurations)
{
bool seen = false;
for(size_t axis = 0; axis < AXES; axis++)
@ -4820,6 +4890,10 @@ bool GCodes::HandleMcode(GCodeBuffer* gb, StringRef& reply)
break;
case 906: // Set/report Motor currents
if (!AllMovesAreFinishedAndMoveBufferIsLoaded())
{
return false;
}
{
bool seen = false;
for (size_t axis = 0; axis < AXES; axis++)
@ -4856,13 +4930,17 @@ bool GCodes::HandleMcode(GCodeBuffer* gb, StringRef& reply)
if (!seen)
{
reply.printf("Axis currents (mA) - X:%d, Y:%d, Z:%d, E:", (int) platform->MotorCurrent(X_AXIS),
(int) platform->MotorCurrent(Y_AXIS), (int) platform->MotorCurrent(Z_AXIS));
reply.copy("Axis currents (mA) - ");
for (size_t axis = 0; axis < AXES; ++axis)
{
reply.catf("%c:%d, ", axisLetters[axis], (int) platform->MotorCurrent(axis));
}
reply.cat("E");
for (size_t drive = AXES; drive < DRIVES; drive++)
{
reply.catf("%d%c", (int) platform->MotorCurrent(drive), (drive < DRIVES - 1) ? ':' : ',');
reply.catf(":%d", (int) platform->MotorCurrent(drive));
}
reply.catf(" idle factor %d", (int)(platform->GetIdleCurrentFactor() * 100.0));
reply.catf(", idle factor %d%%", (int)(platform->GetIdleCurrentFactor() * 100.0));
}
}
break;

View file

@ -163,8 +163,8 @@ void PID::Spin()
// Always know our temperature, regardless of whether we have been switched on or not
Platform::TempError err = Platform::TempError::errOpen; // Initially assign an error; call should update but if it doesn't, we'll treat as an error
temperature = platform->GetTemperature(heater, &err); // In the event of an error, err is set and BAD_ERROR_TEMPERATURE is returned
Platform::TempError err = Platform::TempError::errOk; // assume no error
temperature = platform->GetTemperature(heater, &err); // in the event of an error, err is set and BAD_ERROR_TEMPERATURE is returned
// If we're not switched on, or there's a fault, turn the power off and go home.
// If we're not switched on, then nothing is using us. This probably means that
@ -180,7 +180,16 @@ void PID::Spin()
// We are switched on. Check for faults. Temperature silly-low or silly-high mean open-circuit
// or shorted thermistor respectively.
if (temperature < BAD_LOW_TEMPERATURE || temperature > BAD_HIGH_TEMPERATURE)
if (temperature < BAD_LOW_TEMPERATURE)
{
err = Platform::TempError::errOpen;
}
else if (temperature > platform->GetTemperatureLimit())
{
err = Platform::TempError::errTooHigh;
}
if (err != Platform::TempError::errOk)
{
if (platform->DoThermistorAdc(heater) || !(Platform::TempErrorPermanent(err)))
{
@ -303,6 +312,75 @@ void PID::Spin()
// debugPrintf("Heater %d: e=%f, P=%f, I=%f, d=%f, r=%f\n", heater, error, pp.kP*error, temp_iState, temp_dState, result);
}
void PID::SetActiveTemperature(float t)
{
if (t > platform->GetTemperatureLimit())
{
platform->MessageF(GENERIC_MESSAGE, "Error: Temperature %.1f too high for heater %d!\n", t, heater);
}
SwitchOn();
activeTemperature = t;
}
void PID::SetStandbyTemperature(float t)
{
if (t > platform->GetTemperatureLimit())
{
platform->MessageF(GENERIC_MESSAGE, "Error: Temperature %.1f too high for heater %d!\n", t, heater);
}
SwitchOn();
standbyTemperature = t;
}
void PID::Activate()
{
if (temperatureFault)
{
return;
}
SwitchOn();
active = true;
if (!heatingUp)
{
timeSetHeating = platform->Time();
}
heatingUp = activeTemperature > temperature;
}
void PID::Standby()
{
if (temperatureFault)
{
return;
}
SwitchOn();
active = false;
if (!heatingUp)
{
timeSetHeating = platform->Time();
}
heatingUp = standbyTemperature > temperature;
}
void PID::ResetFault()
{
temperatureFault = false;
timeSetHeating = platform->Time(); // otherwise we will get another timeout immediately
badTemperatureCount = 0;
}
void PID::SwitchOff()
{
SetHeater(0.0);
active = false;
switchedOff = true;
heatingUp = false;
}
float PID::GetAveragePWM() const
{
return averagePWM * invHeatPwmAverageCount;

69
Heat.h
View file

@ -137,33 +137,11 @@ inline bool PID::Active() const
return active;
}
inline void PID::SetActiveTemperature(float t)
{
if (t > BAD_HIGH_TEMPERATURE)
{
platform->MessageF(GENERIC_MESSAGE, "Error: Temperature %.1f too high for heater %d!\n", t, heater);
}
SwitchOn();
activeTemperature = t;
}
inline float PID::GetActiveTemperature() const
{
return activeTemperature;
}
inline void PID::SetStandbyTemperature(float t)
{
if (t > BAD_HIGH_TEMPERATURE)
{
platform->MessageF(GENERIC_MESSAGE, "Error: Temperature %.1f too high for heater %d!\n", t, heater);
}
SwitchOn();
standbyTemperature = t;
}
inline float PID::GetStandbyTemperature() const
{
return standbyTemperature;
@ -174,58 +152,11 @@ inline float PID::GetTemperature() const
return temperature;
}
inline void PID::Activate()
{
if (temperatureFault)
{
return;
}
SwitchOn();
active = true;
if (!heatingUp)
{
timeSetHeating = platform->Time();
}
heatingUp = activeTemperature > temperature;
}
inline void PID::Standby()
{
if (temperatureFault)
{
return;
}
SwitchOn();
active = false;
if (!heatingUp)
{
timeSetHeating = platform->Time();
}
heatingUp = standbyTemperature > temperature;
}
inline bool PID::FaultOccurred() const
{
return temperatureFault;
}
inline void PID::ResetFault()
{
temperatureFault = false;
timeSetHeating = platform->Time(); // otherwise we will get another timeout immediately
badTemperatureCount = 0;
}
inline void PID::SwitchOff()
{
SetHeater(0.0);
active = false;
switchedOff = true;
heatingUp = false;
}
inline bool PID::SwitchedOff() const
{
return switchedOff;

View file

@ -85,7 +85,7 @@ extern void pinModeNonDue(uint32_t ulPin, uint32_t ulMode, uint32_t debounceCuto
return;
}
const PinDescription& pinDesc = (ulPin >= X0) ? nonDuePinDescription[ulPin - X0] : g_APinDescription[ulPin];
const PinDescription& pinDesc = GetPinDescription(ulPin);
if (pinDesc.ulPinType == PIO_NOT_A_PIN)
{
return;
@ -153,7 +153,7 @@ extern void digitalWriteNonDue(uint32_t ulPin, uint32_t ulVal)
return;
}
const PinDescription& pinDesc = (ulPin >= X0) ? nonDuePinDescription[ulPin - X0] : g_APinDescription[ulPin];
const PinDescription& pinDesc = GetPinDescription(ulPin);
if (pinDesc.ulPinType != PIO_NOT_A_PIN)
{
if (ulVal) // we make use of the fact that LOW is zero and HIGH is nonzero
@ -179,7 +179,7 @@ extern int digitalReadNonDue( uint32_t ulPin )
return LOW;
}
const PinDescription& pinDesc = (ulPin >= X0) ? nonDuePinDescription[ulPin - X0] : g_APinDescription[ulPin];
const PinDescription& pinDesc = GetPinDescription(ulPin);
if (pinDesc.ulPinType == PIO_NOT_A_PIN)
{
return LOW ;
@ -191,7 +191,7 @@ extern int digitalReadNonDue( uint32_t ulPin )
// Build a short-form pin descriptor for a IO pin
OutputPin::OutputPin(unsigned int pin)
{
const PinDescription& pinDesc = (pin >= X0) ? nonDuePinDescription[pin - X0] : g_APinDescription[pin];
const PinDescription& pinDesc = GetPinDescription(pin);
pPort = pinDesc.pPort;
ulPin = pinDesc.ulPin;
}
@ -249,7 +249,7 @@ void analogWriteNonDue(uint32_t ulPin, uint32_t ulValue, uint16_t freq)
ulValue = 255;
}
const PinDescription& pinDesc = (ulPin >= X0) ? nonDuePinDescription[ulPin - X0] : g_APinDescription[ulPin];
const PinDescription& pinDesc = GetPinDescription(ulPin);
uint32_t attr = pinDesc.ulPinAttribute;
if ((attr & PIN_ATTR_ANALOG) == PIN_ATTR_ANALOG)
{

View file

@ -84,18 +84,22 @@ public:
void SetLow() const { pPort->PIO_CODR = ulPin; }
};
// struct used to hold the descriptions for the "non Arduino" pins.
// from the Arduino.h files
extern const PinDescription nonDuePinDescription[];
extern void pinModeNonDue(uint32_t ulPin, uint32_t ulMode, uint32_t debounceCutoff = 0); // NB only one debounce cutoff frequency can be set per PIO
extern void digitalWriteNonDue(uint32_t ulPin, uint32_t ulVal);
extern int digitalReadNonDue(uint32_t ulPin);
extern OutputPin getPioPin(uint32_t ulPin);
extern void analogWriteNonDue(uint32_t ulPin, uint32_t ulValue, uint16_t freq);
extern void analogOutputNonDue();
extern void hsmciPinsinit();
extern void ethPinsInit();
extern adc_channel_num_t PinToAdcChannel(int pin); // convert an analog pin number to an ADC channel
inline const PinDescription& GetPinDescription(uint32_t ulPin)
{
return (ulPin >= X0) ? nonDuePinDescription[ulPin - X0] : g_APinDescription[ulPin];
}
void pinModeNonDue(uint32_t ulPin, uint32_t ulMode, uint32_t debounceCutoff = 0); // NB only one debounce cutoff frequency can be set per PIO
void digitalWriteNonDue(uint32_t ulPin, uint32_t ulVal);
int digitalReadNonDue(uint32_t ulPin);
OutputPin getPioPin(uint32_t ulPin);
void analogWriteNonDue(uint32_t ulPin, uint32_t ulValue, uint16_t freq);
void analogOutputNonDue();
void hsmciPinsinit();
void ethPinsInit();
adc_channel_num_t PinToAdcChannel(int pin); // convert an analog pin number to an ADC channel
#endif /* SAM_NON_DUE_PIN_H */

View file

@ -22,6 +22,10 @@
#include "RepRapFirmware.h"
#include "DueFlashStorage.h"
#ifdef EXTERNAL_DRIVERS
#include "ExternalDrivers.h"
#endif
extern char _end;
extern "C" char *sbrk(int i);
@ -109,8 +113,7 @@ bool PidParameters::operator==(const PidParameters& other) const
Platform::Platform() :
autoSaveEnabled(false), board(DEFAULT_BOARD_TYPE), active(false), errorCodeBits(0),
fileStructureInitialised(false), tickState(0), debugCode(0),
messageString(messageStringBuffer, ARRAY_SIZE(messageStringBuffer))
fileStructureInitialised(false), tickState(0), debugCode(0)
{
// Output
auxOutput = new OutputStack();
@ -148,7 +151,9 @@ void Platform::Init()
SERIAL_MAIN_DEVICE.begin(baudRates[0]);
SERIAL_AUX_DEVICE.begin(baudRates[1]); // this can't be done in the constructor because the Arduino port initialisation isn't complete at that point
#ifdef SERIAL_AUX2_DEVICE
SERIAL_AUX2_DEVICE.begin(baudRates[2]);
#endif
// Reconfigure the ADC to avoid crosstalk between channels (especially on Duet 0.8.5)
adc_init(ADC, SystemCoreClock, ADC_FREQ_MIN, ADC_STARTUP_FAST); // reduce clock rate
@ -240,7 +245,11 @@ void Platform::Init()
{
pinModeNonDue(directionPins[drive], OUTPUT);
}
#ifdef EXTERNAL_DRIVERS
if (drive < FIRST_EXTERNAL_DRIVE && enablePins[drive] >= 0)
#else
if (enablePins[drive] >= 0)
#endif
{
pinModeNonDue(enablePins[drive], OUTPUT);
}
@ -261,6 +270,10 @@ void Platform::Init()
}
}
#ifdef EXTERNAL_DRIVERS
ExternalDrivers::Init();
#endif
extrusionAncilliaryPWM = 0.0;
// HEATERS - Bed is assumed to be index 0
@ -275,14 +288,8 @@ void Platform::Init()
thermistorAdcChannels[heater] = PinToAdcChannel(tempSensePins[heater]); // Translate the Arduino Due Analog pin number to the SAM ADC channel number
SetThermistorNumber(heater, heater); // map the thermistor straight through
thermistorFilters[heater].Init(analogRead(tempSensePins[heater]));
// Calculate and store the ADC average sum that corresponds to an overheat condition, so that we can check is quickly in the tick ISR
float thermistorOverheatResistance = nvData.pidParams[heater].GetRInf()
* exp(-nvData.pidParams[heater].GetBeta() / (BAD_HIGH_TEMPERATURE - ABS_ZERO));
float thermistorOverheatAdcValue = (AD_RANGE_REAL + 1) * thermistorOverheatResistance
/ (thermistorOverheatResistance + nvData.pidParams[heater].thermistorSeriesR);
thermistorOverheatSums[heater] = (uint32_t) (thermistorOverheatAdcValue + 0.9) * THERMISTOR_AVERAGE_READINGS;
}
SetTemperatureLimit(DEFAULT_TEMPERATURE_LIMIT);
InitFans();
@ -338,6 +345,20 @@ void Platform::InvalidateFiles()
}
}
void Platform::SetTemperatureLimit(float t)
{
temperatureLimit = t;
for (size_t heater = 0; heater < HEATERS; heater++)
{
// Calculate and store the ADC average sum that corresponds to an overheat condition, so that we can check it quickly in the tick ISR
float thermistorOverheatResistance = nvData.pidParams[heater].GetRInf()
* exp(-nvData.pidParams[heater].GetBeta() / (temperatureLimit - ABS_ZERO));
float thermistorOverheatAdcValue = (AD_RANGE_REAL + 1) * thermistorOverheatResistance
/ (thermistorOverheatResistance + nvData.pidParams[heater].thermistorSeriesR);
thermistorOverheatSums[heater] = (uint32_t) (thermistorOverheatAdcValue + 0.9) * THERMISTOR_AVERAGE_READINGS;
}
}
// Specify which thermistor channel a particular heater uses
void Platform::SetThermistorNumber(size_t heater, size_t thermistor)
//pre(heater < HEATERS && thermistor < HEATERS)
@ -781,6 +802,7 @@ void Platform::Spin()
OutputBuffer *aux2OutputBuffer = aux2Output->GetFirstItem();
if (aux2OutputBuffer != nullptr)
{
#ifdef SERIAL_AUX2_DEVICE
size_t bytesToWrite = min<size_t>(SERIAL_AUX2_DEVICE.canWrite(), aux2OutputBuffer->BytesLeft());
if (bytesToWrite > 0)
{
@ -792,6 +814,9 @@ void Platform::Spin()
aux2OutputBuffer = OutputBuffer::Release(aux2OutputBuffer);
aux2Output->SetFirstItem(aux2OutputBuffer);
}
#else
aux2OutputBuffer = OutputBuffer::Release(aux2OutputBuffer);
#endif
}
// Write non-blocking data to the USB line
@ -868,7 +893,11 @@ void Platform::SoftwareReset(uint16_t reason)
{
reason |= (uint16_t)SoftwareResetReason::inLwipSpin;
}
if (!SERIAL_AUX_DEVICE.canWrite() || !SERIAL_AUX2_DEVICE.canWrite())
if (!SERIAL_AUX_DEVICE.canWrite()
#ifdef SERIAL_AUX2_DEVICE
|| !SERIAL_AUX2_DEVICE.canWrite()
#endif
)
{
reason |= (uint16_t)SoftwareResetReason::inAuxOutput; // if we are resetting because we are stuck in a Spin function, record whether we are trying to send to aux
}
@ -880,15 +909,6 @@ void Platform::SoftwareReset(uint16_t reason)
temp.magic = SoftwareResetData::magicValue;
temp.resetReason = reason;
GetStackUsage(NULL, NULL, &temp.neverUsedRam);
if (reason != (uint16_t)SoftwareResetReason::user)
{
strncpy(temp.lastMessage, messageString.Pointer(), sizeof(temp.lastMessage) - 1);
temp.lastMessage[sizeof(temp.lastMessage) - 1] = 0;
}
else
{
temp.lastMessage[0] = 0;
}
// Save diagnostics data to Flash and reset the software
DueFlashStorage::write(SoftwareResetData::nvAddress, &temp, sizeof(SoftwareResetData));
@ -1022,10 +1042,6 @@ void Platform::Diagnostics()
{
MessageF(GENERIC_MESSAGE, "Last software reset code & available RAM: 0x%04x, %u\n", temp.resetReason, temp.neverUsedRam);
MessageF(GENERIC_MESSAGE, "Spinning module during software reset: %s\n", moduleName[temp.resetReason & 0x0F]);
if (temp.lastMessage[0])
{
MessageF(GENERIC_MESSAGE, "Last message before reset: %s", temp.lastMessage); // usually ends with NL
}
}
}
@ -1178,7 +1194,10 @@ float Platform::GetTemperature(size_t heater, TempError* err) const
else
{
// thermistor short circuit, return a high temperature
if (err) *err = TempError::errShort;
if (err)
{
*err = TempError::errShort;
}
return BAD_ERROR_TEMPERATURE;
}
}
@ -1216,6 +1235,7 @@ float Platform::GetTemperature(size_t heater, TempError* err) const
case TempError::errShortVcc : return "sensor circuit is shorted to the voltage rail";
case TempError::errShortGnd : return "sensor circuit is shorted to ground";
case TempError::errOpen : return "sensor circuit is open/disconnected";
case TempError::errTooHigh: return "temperature above safety limit";
case TempError::errTimeout : return "communication error whilst reading sensor; read took too long";
case TempError::errIO: return "communication error whilst reading sensor; check sensor connections";
}
@ -1238,6 +1258,7 @@ bool Platform::AnyHeaterHot(uint16_t heaters, float t) const
return false;
}
// Update the heater PID parameters or thermistor resistance etc.
void Platform::SetPidParameters(size_t heater, const PidParameters& params)
{
if (heater < HEATERS && params != nvData.pidParams[heater])
@ -1247,6 +1268,7 @@ void Platform::SetPidParameters(size_t heater, const PidParameters& params)
{
WriteNvData();
}
SetTemperatureLimit(temperatureLimit); // recalculate the thermistor resistance at max allowed temperature for the tick ISR
}
}
const PidParameters& Platform::GetPidParameters(size_t heater) const
@ -1329,11 +1351,22 @@ void Platform::EnableDrive(size_t drive)
{
UpdateMotorCurrent(driver); // the current may have been reduced by the idle timeout
#ifdef EXTERNAL_DRIVERS
if (drive >= FIRST_EXTERNAL_DRIVE)
{
ExternalDrivers::EnableDrive(driver - FIRST_EXTERNAL_DRIVE, true);
}
else
{
#endif
const int pin = enablePins[driver];
if (pin >= 0)
{
digitalWriteNonDue(pin, enableValues[driver]);
}
#ifdef EXTERNAL_DRIVERS
}
#endif
}
}
}
@ -1344,12 +1377,23 @@ void Platform::DisableDrive(size_t drive)
if (drive < DRIVES)
{
const size_t driver = driverNumbers[drive];
#ifdef EXTERNAL_DRIVERS
if (drive >= FIRST_EXTERNAL_DRIVE)
{
ExternalDrivers::EnableDrive(driver - FIRST_EXTERNAL_DRIVE, false);
}
else
{
#endif
const int pin = enablePins[driver];
if (pin >= 0)
{
digitalWriteNonDue(pin, !enableValues[driver]);
}
driveState[drive] = DriveStatus::disabled;
#ifdef EXTERNAL_DRIVERS
}
#endif
}
}
@ -1387,9 +1431,17 @@ void Platform::UpdateMotorCurrent(size_t drive)
{
current *= idleCurrentFactor;
}
const size_t driver = driverNumbers[drive];
#ifdef EXTERNAL_DRIVERS
if (driver >= FIRST_EXTERNAL_DRIVE)
{
ExternalDrivers::SetCurrent(driver - FIRST_EXTERNAL_DRIVE, current);
}
else
{
#endif
unsigned short pot = (unsigned short)((0.256*current*8.0*senseResistor + maxStepperDigipotVoltage/2)/maxStepperDigipotVoltage);
unsigned short dac = (unsigned short)((0.256*current*8.0*senseResistor + maxStepperDACVoltage/2)/maxStepperDACVoltage);
const size_t driver = driverNumbers[drive];
if (driver < 4)
{
mcpDuet.setNonVolatileWiper(potWipes[driver], pot);
@ -1416,6 +1468,9 @@ void Platform::UpdateMotorCurrent(size_t drive)
mcpExpansion.setVolatileWiper(potWipes[driver], pot);
}
}
#ifdef EXTERNAL_DRIVERS
}
#endif
}
}
@ -1437,6 +1492,48 @@ void Platform::SetIdleCurrentFactor(float f)
}
}
// Set the microstepping, returning true if successful
bool Platform::SetMicrostepping(size_t drive, int microsteps, int mode)
{
if (drive < DRIVES)
{
#ifdef EXTERNAL_DRIVERS
const size_t driver = driverNumbers[drive];
if (driver >= FIRST_EXTERNAL_DRIVE)
{
return ExternalDrivers::SetMicrostepping(driver - FIRST_EXTERNAL_DRIVE, microsteps, mode);
}
else
{
#endif
// On-board drivers only support x16 microstepping.
// We ignore the interpolation on/off parameter so that e.g. M350 I1 E16:128 won't give an error if E1 supports interpolation but E0 doesn't.
return microsteps == 16;
#ifdef EXTERNAL_DRIVERS
}
#endif
}
return false;
}
unsigned int Platform::GetMicrostepping(size_t drive, bool& interpolation) const
{
#ifdef EXTERNAL_DRIVERS
if (drive < DRIVES)
{
const size_t driver = driverNumbers[drive];
if (driver >= FIRST_EXTERNAL_DRIVE)
{
return ExternalDrivers::GetMicrostepping(driver - FIRST_EXTERNAL_DRIVE, interpolation);
}
}
#endif
// On-board drivers only support x16 microstepping without interpolation
interpolation = false;
return 16;
}
// Set the physical drive (i.e. axis or extruder) number used by this driver
void Platform::SetPhysicalDrive(size_t driverNumber, int8_t physicalDrive)
{
@ -1513,7 +1610,7 @@ void Platform::InitFans()
for (size_t i = 0; i < NUM_FANS; ++i)
{
// The cooling fan 0 output pin gets inverted if HEAT_ON == 0 on a Duet 0.4, 0.6 or 0.7
fans[i].Init(COOLING_FAN_PINS[i], !HEAT_ON && board != BoardType::Duet_085);
fans[i].Init(COOLING_FAN_PINS[i], !HEAT_ON && (board == BoardType::Duet_06 || board == BoardType::Duet_07));
}
if (NUM_FANS > 1)
@ -1711,6 +1808,7 @@ void Platform::Message(MessageType type, const char *message)
break;
case AUX2_MESSAGE:
#ifdef SERIAL_AUX2_DEVICE
// Message that is to be sent to the second auxiliary device (blocking)
if (!aux2Output->IsEmpty())
{
@ -1723,6 +1821,7 @@ void Platform::Message(MessageType type, const char *message)
SERIAL_AUX2_DEVICE.write(message);
SERIAL_AUX2_DEVICE.flush();
}
#endif
break;
case DISPLAY_MESSAGE:
@ -1965,10 +2064,12 @@ void Platform::ResetChannel(size_t chan)
SERIAL_AUX_DEVICE.end();
SERIAL_AUX_DEVICE.begin(baudRates[1]);
break;
#ifdef SERIAL_AUX2_DEVICE
case 2:
SERIAL_AUX2_DEVICE.end();
SERIAL_AUX2_DEVICE.begin(baudRates[2]);
break;
#endif
default:
break;
}
@ -1981,7 +2082,7 @@ void Platform::SetBoardType(BoardType bt)
// 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.
// This may fail if anyone connects a load to the DAC0 pin on and Dur=et 0.6, hence we implement board selection in M115 as well.
// This may fail if anyone connects a load to the DAC0 pin on and Duet 0.6, hence we implement board selection in M115 as well.
pinModeNonDue(Dac0DigitalPin, INPUT_PULLUP);
board = (digitalReadNonDue(Dac0DigitalPin)) ? BoardType::Duet_06 : BoardType::Duet_085;
pinModeNonDue(Dac0DigitalPin, INPUT); // turn pullup off
@ -2088,7 +2189,11 @@ bool Platform::GCodeAvailable(const SerialSource source) const
return SERIAL_AUX_DEVICE.available() > 0;
case SerialSource::AUX2:
#ifdef SERIAL_AUX2_DEVICE
return SERIAL_AUX2_DEVICE.available() > 0;
#else
return false;
#endif
}
return false;
@ -2105,7 +2210,11 @@ char Platform::ReadFromSource(const SerialSource source)
return static_cast<char>(SERIAL_AUX_DEVICE.read());
case SerialSource::AUX2:
#ifdef SERIAL_AUX2_DEVICE
return static_cast<char>(SERIAL_AUX2_DEVICE.read());
#else
return 0;
#endif
}
return 0;
@ -2180,7 +2289,7 @@ void Platform::Tick()
// averaging. As such, the temperature reading is taken directly by Platform::GetTemperature() and
// periodically called by PID::Spin() where temperature fault handling is taken care of. However, we
// must guard against overly long delays between successive calls of PID::Spin().
// Do not call Time() here, it isn't safe. We use millis() instead.
StartAdcConversion(zProbeAdcChannel);
if ((millis() - reprap.GetHeat()->GetLastSampleTime(currentHeater)) > maxPidSpinDelay)
{

View file

@ -175,12 +175,6 @@ const uint8_t MAC_ADDRESS[6] = { 0xBE, 0xEF, 0xDE, 0xAD, 0xFE, 0xED };
/****************************************************************************************************/
// Miscellaneous...
const size_t messageStringLength = 256; // max length of a message chunk sent via Message or AppendMessage
/****************************************************************************************************/
enum class BoardType : uint8_t
{
Auto = 0,
@ -423,7 +417,7 @@ public:
enum class DriveStatus : uint8_t { disabled, idle, enabled };
// Error results generated by GetTemperature()
enum class TempError : uint8_t { errOk, errShort, errShortVcc, errShortGnd, errOpen, errTimeout, errIO };
enum class TempError : uint8_t { errOk, errShort, errShortVcc, errShortGnd, errOpen, errTooHigh, errTimeout, errIO };
Platform();
@ -515,6 +509,8 @@ public:
float MotorCurrent(size_t drive) const;
void SetIdleCurrentFactor(float f);
float GetIdleCurrentFactor() const { return idleCurrentFactor; }
bool SetMicrostepping(size_t drive, int microsteps, int mode);
unsigned int GetMicrostepping(size_t drive, bool& interpolation) const;
float DriveStepsPerUnit(size_t drive) const;
const float *GetDriveStepsPerUnit() const { return driveStepsPerUnit; }
void SetDriveStepsPerUnit(size_t drive, float value);
@ -574,7 +570,8 @@ public:
void SetThermistorNumber(size_t heater, size_t thermistor);
int GetThermistorNumber(size_t heater) const;
bool DoThermistorAdc(uint8_t heater) const;
MAX31855 Max31855Devices[MAX31855_DEVICES];
void SetTemperatureLimit(float t);
float GetTemperatureLimit() const { return temperatureLimit; }
static const char* TempErrorStr(TempError err);
static bool TempErrorPermanent(TempError err);
@ -638,7 +635,6 @@ private:
uint16_t magic;
uint16_t resetReason; // this records why we did a software reset, for diagnostic purposes
size_t neverUsedRam; // the amount of never used RAM at the last abnormal software reset
char lastMessage[256]; // the last known message before a software reset occurred
};
struct FlashData
@ -759,11 +755,13 @@ private:
Pin tempSensePins[HEATERS];
Pin heatOnPins[HEATERS];
MAX31855 Max31855Devices[MAX31855_DEVICES];
Pin max31855CsPins[MAX31855_DEVICES];
float heatSampleTime;
float standbyTemperatures[HEATERS];
float activeTemperatures[HEATERS];
float timeToHot;
float temperatureLimit;
// Fans
@ -836,9 +834,6 @@ private:
static uint16_t GetAdcReading(adc_channel_num_t chan);
static void StartAdcConversion(adc_channel_num_t chan);
char messageStringBuffer[messageStringLength];
StringRef messageString;
// Hotend configuration
float filamentWidth;
float nozzleDiameter;

View file

@ -723,9 +723,11 @@ float PrintMonitor::EstimateTimeLeft(PrintEstimationMethod method) const
const float fractionPrinted = gCodes->FractionOfFilePrinted();
if (numLayerSamples < 2 || !printingFileParsed || printingFileInfo.objectHeight == 0.0)
{
return (fractionPrinted < 0.01)
? 0.0
: realPrintDuration * (1.0 / fractionPrinted) - realPrintDuration;
if (fractionPrinted < 0.01)
{
break;
}
return realPrintDuration * (1.0 / fractionPrinted) - realPrintDuration;
}
// Work out how much progress we made in the layers we have data for, and how long it took.
@ -736,10 +738,12 @@ float PrintMonitor::EstimateTimeLeft(PrintEstimationMethod method) const
duration += layerDurations[layer];
}
const float fractionPrintedInLayers = fileProgressPerLayer[numLayerSamples - 1] - fileProgressPerLayer[0];
return (fractionPrintedInLayers < 0.01)
? 0.0
: duration * (1.0 - fractionPrinted)/fractionPrintedInLayers;
if (fractionPrintedInLayers >= 0.01)
{
return duration * (1.0 - fractionPrinted)/fractionPrintedInLayers;
}
}
break;
case filamentBased:
{
@ -750,7 +754,7 @@ float PrintMonitor::EstimateTimeLeft(PrintEstimationMethod method) const
#endif
)
{
return 0.0;
break;
}
// Sum up the filament usage and the filament needed
@ -770,22 +774,35 @@ float PrintMonitor::EstimateTimeLeft(PrintEstimationMethod method) const
}
if (extrRawTotal >= totalFilamentNeeded)
{
return 0.0; // Avoid division by zero, else the web interface will report AJAX errors
break; // Avoid division by zero, else the web interface will report AJAX errors
}
float filamentRate;
if (numLayerSamples)
if (numLayerSamples != 0)
{
filamentRate = 0.0;
size_t numSamples = 0;
for (size_t i = 0; i < numLayerSamples; i++)
{
if (layerDurations[i] > 0.0)
{
filamentRate += filamentUsagePerLayer[i] / layerDurations[i];
++numSamples;
}
filamentRate /= numLayerSamples;
}
if (numSamples == 0)
{
break;
}
filamentRate /= numSamples;
}
else if (firstLayerDuration > 0.0)
{
filamentRate = firstLayerFilament / firstLayerDuration;
}
else
{
filamentRate = firstLayerFilament / firstLayerDuration;
break;
}
return (totalFilamentNeeded - extrRawTotal) / filamentRate;

Binary file not shown.

View file

@ -163,7 +163,7 @@ void RepRap::Spin()
{
if (t->DisplayColdExtrudeWarning() && ToolWarningsAllowed())
{
platform->MessageF(GENERIC_MESSAGE, "Warning: Tool %d was not driven because its heater temperatures were not high enough\n", t->myNumber);
platform->MessageF(GENERIC_MESSAGE, "Warning: Tool %d was not driven because its temperatures were not high enough or it has a heater fault\n", t->myNumber);
}
}
@ -1341,19 +1341,28 @@ void RepRap::SetName(const char* nm)
network->SetHostname(myName);
}
// Given that we want to extrude/etract the specified extruder drives, check if they are allowed.
// Given that we want to extrude/retract the specified extruder drives, check if they are allowed.
// For each disallowed one, log an error to report later and return a bit in the bitmap.
// This may be called by an ISR!
unsigned int RepRap::GetProhibitedExtruderMovements(unsigned int extrusions, unsigned int retractions)
{
unsigned int result = 0;
Tool *tool = toolList;
while (tool != nullptr)
if (GetHeat()->ColdExtrude())
{
return 0;
}
Tool *tool = currentTool;
if (tool == nullptr)
{
// This should not happen, but if on tool is selected then don't allow any extruder movement
return extrusions | retractions;
}
unsigned int result = 0;
for (size_t driveNum = 0; driveNum < tool->DriveCount(); driveNum++)
{
const int extruderDrive = tool->Drive(driveNum);
unsigned int mask = 1 << extruderDrive;
const unsigned int extruderDrive = (unsigned int)(tool->Drive(driveNum));
const unsigned int mask = 1 << extruderDrive;
if (extrusions & mask)
{
if (!tool->ToolCanDrive(true))
@ -1361,7 +1370,7 @@ unsigned int RepRap::GetProhibitedExtruderMovements(unsigned int extrusions, uns
result |= mask;
}
}
else if (retractions & (1 << extruderDrive))
else if (retractions & mask)
{
if (!tool->ToolCanDrive(false))
{
@ -1370,8 +1379,6 @@ unsigned int RepRap::GetProhibitedExtruderMovements(unsigned int extrusions, uns
}
}
tool = tool->Next();
}
return result;
}

View file

@ -31,13 +31,15 @@ Tool * Tool::freelist = nullptr;
{
if (dCount > DRIVES - AXES)
{
reprap.GetPlatform()->Message(GENERIC_MESSAGE, "Error: Tool creation: attempt to use more drives than there are in the RepRap");
reprap.GetPlatform()->Message(GENERIC_MESSAGE,
"Error: Tool creation: attempt to use more drives than there are in the RepRap");
return nullptr;
}
if (hCount > HEATERS)
{
reprap.GetPlatform()->Message(GENERIC_MESSAGE, "Error: Tool creation: attempt to use more heaters than there are in the RepRap");
reprap.GetPlatform()->Message(GENERIC_MESSAGE,
"Error: Tool creation: attempt to use more heaters than there are in the RepRap");
return nullptr;
}
@ -120,8 +122,7 @@ void Tool::Print(StringRef& reply)
{
comma = ';';
}
reply.catf("%d (%.1f/%.1f)%c", heaters[heater],
activeTemperatures[heater], standbyTemperatures[heater], comma);
reply.catf("%d (%.1f/%.1f)%c", heaters[heater], activeTemperatures[heater], standbyTemperatures[heater], comma);
}
reply.catf(" status: %s", active ? "selected" : "standby");
@ -131,7 +132,8 @@ float Tool::MaxFeedrate() const
{
if (driveCount <= 0)
{
reprap.GetPlatform()->Message(GENERIC_MESSAGE, "Error: Attempt to get maximum feedrate for a tool with no drives.\n");
reprap.GetPlatform()->Message(GENERIC_MESSAGE,
"Error: Attempt to get maximum feedrate for a tool with no drives.\n");
return 1.0;
}
float result = 0.0;
@ -269,12 +271,12 @@ void Tool::SetVariables(const float* standby, const float* active)
}
else
{
if (active[heater] < BAD_HIGH_TEMPERATURE)
if (active[heater] < reprap.GetPlatform()->GetTemperatureLimit())
{
activeTemperatures[heater] = active[heater];
reprap.GetHeat()->SetActiveTemperature(heaters[heater], activeTemperatures[heater]);
}
if (standby[heater] < BAD_HIGH_TEMPERATURE)
if (standby[heater] < reprap.GetPlatform()->GetTemperatureLimit())
{
standbyTemperatures[heater] = standby[heater];
reprap.GetHeat()->SetStandbyTemperature(heaters[heater], standbyTemperatures[heater]);
@ -295,11 +297,10 @@ void Tool::GetVariables(float* standby, float* active) const
// May be called from ISR
bool Tool::ToolCanDrive(bool extrude)
{
if (heaterFault)
return false;
if (reprap.GetHeat()->ColdExtrude() || AllHeatersAtHighTemperature(extrude))
if (!heaterFault && AllHeatersAtHighTemperature(extrude))
{
return true;
}
displayColdExtrudeWarning = true;
return false;

View file

@ -96,8 +96,8 @@
//***************************************************************************************************
static const char* overflowResponse = "overflow";
static const char* badEscapeResponse = "bad escape";
const char* overflowResponse = "overflow";
const char* badEscapeResponse = "bad escape";
//********************************************************************************************