Version 1.15 beta 6
Add support for second SD card on SPI bus for Duet WiFi including implementation of M21 and M22 New temperature monitoring Increased PWM resolution When uploading files to wifi module, skip empty blocks Revised TMC driver in preparation for standstill current reduction Fixed PrintMonitor bug when the slicer comment includes a '\' character
This commit is contained in:
parent
868f60589b
commit
4bca2a787b
34 changed files with 923 additions and 743 deletions
|
@ -26,11 +26,11 @@ Licence: GPL
|
|||
// Firmware name is now defined in the Pins file
|
||||
|
||||
#ifndef VERSION
|
||||
# define VERSION "1.15-beta3"
|
||||
# define VERSION "1.15-beta6"
|
||||
#endif
|
||||
|
||||
#ifndef DATE
|
||||
# define DATE "2016-07-09"
|
||||
# define DATE "2016-08-07"
|
||||
#endif
|
||||
|
||||
#define AUTHORS "reprappro, dc42, zpl, t3p3, dnewman"
|
||||
|
@ -76,10 +76,11 @@ const float HEAT_SAMPLE_TIME = 0.5; // Seconds
|
|||
const float HEAT_PWM_AVERAGE_TIME = 5.0; // Seconds
|
||||
|
||||
const float TEMPERATURE_CLOSE_ENOUGH = 2.5; // Celsius
|
||||
const float MaxStableTemperatureError = 5.0; // How much error we tolerate when maintaining temperature before deciding that a heater fault has occurred
|
||||
static_assert(MaxStableTemperatureError > TEMPERATURE_CLOSE_ENOUGH, "MaxStableTemperatureError is too low");
|
||||
const float TEMPERATURE_LOW_SO_DONT_CARE = 40.0; // Celsius
|
||||
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 = 4; // Number of bad temperature samples permitted before a heater fault is reported
|
||||
const float BAD_LOW_TEMPERATURE = -10.0; // Celsius
|
||||
|
@ -87,7 +88,24 @@ const float DEFAULT_TEMPERATURE_LIMIT = 262.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 any reasonable 5temperature limit including DEFAULT_TEMPERATURE_LIMIT
|
||||
|
||||
const unsigned int FirstThermocoupleChannel = 100; // Temperature sensor channels 100.. are thermocouples
|
||||
// Parameters used to detect heating errors
|
||||
const float MaxHeatingFaultTime = 3; // How many seconds we allow a heating fault to persist
|
||||
const float MaxAmbientTemperature = 45.0; // We expect heaters to cool to this temperature or lower when switched off
|
||||
const float NormalAmbientTemperature = 20.0; // Temperature at which the heating rates (below) apply
|
||||
|
||||
const float BedHeatingRate = 0.2; // The minimum rate of rise of bed temperature from cold after the startup time
|
||||
const float BedHeaterAchievableTemp = 90.0; // Lowest temperature at which the bed heater might top out
|
||||
const float BedHeaterStartupTime = 10.0; // How long we allow for the bed heater to reach its normal heating rate
|
||||
|
||||
const float ChamberHeatingRate = 0.2; // The minimum rate of rise of chamber temperature from cold after the startup time
|
||||
const float ChamberHeaterAchievableTemp = 50.0; // Lowest temperature at which the chamber heater might top out
|
||||
const float ChamberHeaterStartupTime = 10.0; // How long we allow for the chamber heater to reach its normal heating rate
|
||||
|
||||
const float ExtruderHeatingRate = 1.0; // The minimum rate of rise of extruder temperature from cold after the startup time
|
||||
const float ExtruderHeaterAchievableTemp = 300.0; // Lowest temperature at which the extruder temperature might top out
|
||||
const float ExtruderHeaterStartupTime = 3.0; // How long we allow for the extruder heater to reach its normal heating rate
|
||||
|
||||
const unsigned int FirstThermocoupleChannel = 100; // Temperature sensor channels 100... are thermocouples
|
||||
const unsigned int FirstRtdChannel = 200; // Temperature sensor channels 200... are RTDs
|
||||
|
||||
// PWM frequencies
|
||||
|
|
|
@ -16,7 +16,7 @@ DDA::DDA(DDA* n) : next(n), prev(nullptr), state(empty)
|
|||
// Return the number of clocks this DDA still needs to execute.
|
||||
// This could be slightly negative, if the move is overdue for completion.
|
||||
int32_t DDA::GetTimeLeft() const
|
||||
//pre(state == executing || state == frozen || state == completed)
|
||||
pre(state == executing || state == frozen || state == completed)
|
||||
{
|
||||
return (state == completed) ? 0
|
||||
: (state == executing) ? (int32_t)(moveStartTime + clocksNeeded - Platform::GetInterruptClocks())
|
||||
|
@ -341,7 +341,7 @@ float DDA::GetMotorPosition(size_t drive) const
|
|||
|
||||
// Try to increase the ending speed of this move to allow the next move to start at targetNextSpeed
|
||||
void DDA::DoLookahead(DDA *laDDA)
|
||||
//pre(state == provisional)
|
||||
pre(state == provisional)
|
||||
{
|
||||
// if (reprap.Debug(moduleDda)) debugPrintf("Adjusting, %f\n", laDDA->targetNextSpeed);
|
||||
unsigned int laDepth = 0;
|
||||
|
@ -573,7 +573,7 @@ void DDA::SetPositions(const float move[DRIVES], size_t numDrives)
|
|||
|
||||
// Get a Cartesian end coordinate from this move
|
||||
float DDA::GetEndCoordinate(size_t drive, bool disableDeltaMapping)
|
||||
//pre(disableDeltaMapping || drive < AXES)
|
||||
pre(disableDeltaMapping || drive < AXES)
|
||||
{
|
||||
if (disableDeltaMapping)
|
||||
{
|
||||
|
@ -739,7 +739,7 @@ void DDA::Prepare()
|
|||
// Start executing this move, returning true if Step() needs to be called immediately. Must be called with interrupts disabled, to avoid a race condition.
|
||||
// Returns true if the caller needs to call the step ISR immediately.
|
||||
bool DDA::Start(uint32_t tim)
|
||||
//pre(state == frozen)
|
||||
pre(state == frozen)
|
||||
{
|
||||
moveStartTime = tim;
|
||||
state = executing;
|
||||
|
|
|
@ -196,7 +196,7 @@ void DriveMovement::DebugPrint(char c, bool isDeltaMovement) const
|
|||
// Return true if there are more steps to do.
|
||||
// This is also used for extruders on delta machines.
|
||||
bool DriveMovement::CalcNextStepTimeCartesianFull(const DDA &dda, bool live)
|
||||
//pre(nextStep < totalSteps; stepsTillRecalc == 0)
|
||||
pre(nextStep < totalSteps; stepsTillRecalc == 0)
|
||||
{
|
||||
// Work out how many steps to calculate at a time.
|
||||
uint32_t shiftFactor;
|
||||
|
@ -294,7 +294,7 @@ bool DriveMovement::CalcNextStepTimeCartesianFull(const DDA &dda, bool live)
|
|||
// Calculate the time since the start of the move when the next step for the specified DriveMovement is due
|
||||
// Return true if there are more steps to do
|
||||
bool DriveMovement::CalcNextStepTimeDeltaFull(const DDA &dda, bool live)
|
||||
//pre(nextStep < totalSteps; stepsTillRecalc == 0)
|
||||
pre(nextStep < totalSteps; stepsTillRecalc == 0)
|
||||
{
|
||||
// Work out how many steps to calculate at a time.
|
||||
// The simulator suggests that at 200steps/mm, the minimum step pulse interval for 400mm/sec movement is 4.5us
|
||||
|
|
|
@ -101,11 +101,13 @@ const Pin Z_PROBE_MOD_PIN07 = X12; // Digital pin number to turn the IR
|
|||
const int Dac0DigitalPin = 66; // Arduino Due pin number corresponding to DAC0 output pin
|
||||
|
||||
// COOLING FANS
|
||||
|
||||
const size_t NUM_FANS = 2;
|
||||
const Pin COOLING_FAN_PINS[NUM_FANS] = { X6, X17 }; // Pin D34 is PWM capable but not an Arduino PWM pin - use X6 instead
|
||||
const Pin COOLING_FAN_RPM_PIN = 23; // Pin PA15
|
||||
|
||||
// SD cards
|
||||
const size_t NumSdCards = 1; // Only HSMCI card supported for now
|
||||
|
||||
#if SUPPORT_INKJET
|
||||
// Inkjet control pins
|
||||
const Pin INKJET_SERIAL_OUT = 21; // Serial bitpattern into the shift register
|
||||
|
|
|
@ -1018,7 +1018,7 @@ void Network::EspRequestsTransfer()
|
|||
|
||||
// Set up the DMA controller and assert transfer ready. Must be called with interrupts disabled.
|
||||
void Network::PrepareForTransfer(bool dataToSend, bool allowReceive)
|
||||
//pre(state == idle || state == sending)
|
||||
pre(state == idle || state == sending)
|
||||
{
|
||||
if (allowReceive)
|
||||
{
|
||||
|
|
|
@ -38,6 +38,7 @@ const size_t NUM_SERIAL_CHANNELS = 2; // The number of serial IO channels (USB
|
|||
|
||||
// DRIVES
|
||||
|
||||
const Pin GlobalTmcEnablePin = 38; // The pin that drives ENN of all TMC2660 drivers on production boards (on pre-production boards they are grounded)
|
||||
const Pin ENABLE_PINS[DRIVES] = { 78, 41, 42, 49, 57, 87, 88, 89, 90, 31 };
|
||||
const bool ENABLE_VALUES[DRIVES] = { false, false, false, false, false, false, false, false, false, false }; // What to send to enable a drive
|
||||
const Pin STEP_PINS[DRIVES] = { 70, 71, 72, 69, 68, 66, 65, 64, 67, 91 };
|
||||
|
@ -118,11 +119,13 @@ const float PowerFailVoltageRange = 11.0 * 3.3; // we use an 11:1 voltage
|
|||
const Pin Z_PROBE_MOD_PIN = 34; // Digital pin number to turn the IR LED on (high) or off (low) on Duet v0.6 and v1.0 (PB21)
|
||||
|
||||
// COOLING FANS
|
||||
|
||||
const size_t NUM_FANS = 3;
|
||||
const Pin COOLING_FAN_PINS[NUM_FANS] = { 55, 58, 00 };
|
||||
const Pin COOLING_FAN_RPM_PIN = 32;
|
||||
|
||||
// SD cards
|
||||
const size_t NumSdCards = 2;
|
||||
|
||||
#if SUPPORT_INKJET
|
||||
// Inkjet control pins
|
||||
const Pin INKJET_SERIAL_OUT = xx; // Serial bitpattern into the shift register
|
||||
|
|
|
@ -175,9 +175,42 @@ const uint32_t defaultSmartEnReg =
|
|||
//----------------------------------------------------------------------------------------------------------------------------------
|
||||
// Private types and methods
|
||||
|
||||
struct TmcDriverState
|
||||
{
|
||||
uint32_t drvCtrlReg;
|
||||
uint32_t chopConfReg;
|
||||
uint32_t smartEnReg;
|
||||
uint32_t sgcsConfReg;
|
||||
uint32_t drvConfReg;
|
||||
uint32_t lastReadValue;
|
||||
uint32_t pin;
|
||||
|
||||
void Init(uint32_t p_pin);
|
||||
void WriteAll();
|
||||
void SetChopConf(uint32_t newVal);
|
||||
void SetMicrostepping(uint32_t shift, bool interpolate);
|
||||
void SetCurrent(float current);
|
||||
void Enable(bool en);
|
||||
void SpiSendWord(uint32_t dataOut);
|
||||
uint32_t ReadStatus();
|
||||
};
|
||||
|
||||
// Initialise the state of the driver.
|
||||
void TmcDriverState::Init(uint32_t p_pin)
|
||||
pre(!driversPowered)
|
||||
{
|
||||
drvCtrlReg = defaultDrvCtrlReg;
|
||||
chopConfReg = defaultChopConfReg;
|
||||
smartEnReg = defaultSmartEnReg;
|
||||
sgcsConfReg = defaultSgscConfReg;
|
||||
drvConfReg = defaultDrvConfReg;
|
||||
pin = p_pin;
|
||||
// No point in calling WriteAll() here because the drivers are assumed to be not powered up.
|
||||
}
|
||||
|
||||
// Send an SPI control string. The drivers need 20 bits. We send and receive 24 because the USART only supports 5 to 9 bit transfers.
|
||||
// If the drivers are not powered up, don't attempt a transaction, and return 0xFFFFFFFF
|
||||
uint32_t SpiSendWord(uint32_t pin, uint32_t dataOut)
|
||||
void TmcDriverState::SpiSendWord( uint32_t dataOut)
|
||||
{
|
||||
if (driversPowered)
|
||||
{
|
||||
|
@ -201,61 +234,28 @@ uint32_t SpiSendWord(uint32_t pin, uint32_t dataOut)
|
|||
digitalWrite(pin, HIGH); // set CS high again
|
||||
USART_EXT_DRV->US_CR = US_CR_RSTRX | US_CR_RSTTX | US_CR_RXDIS | US_CR_TXDIS; // reset and disable transmitter and receiver
|
||||
delayMicroseconds(1); // ensure it stays high for long enough before the next write
|
||||
return dataIn >> 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0xFFFFFFFF;
|
||||
lastReadValue = dataIn >> 4;
|
||||
}
|
||||
}
|
||||
|
||||
struct TmcDriverState
|
||||
{
|
||||
uint32_t drvCtrlReg;
|
||||
uint32_t chopConfReg;
|
||||
uint32_t smartEnReg;
|
||||
uint32_t sgcsConfReg;
|
||||
uint32_t drvConfReg;
|
||||
uint32_t lastReadValue;
|
||||
uint32_t pin;
|
||||
|
||||
void Init(uint32_t p_pin);
|
||||
void WriteAll();
|
||||
void SetChopConf(uint32_t newVal);
|
||||
void SetMicrostepping(uint32_t shift, bool interpolate);
|
||||
void SetCurrent(float current);
|
||||
void Enable(bool en);
|
||||
uint32_t GetStatus() const;
|
||||
};
|
||||
|
||||
// Initialise the state of the driver.
|
||||
void TmcDriverState::Init(uint32_t p_pin)
|
||||
//pre(!driversPowered)
|
||||
{
|
||||
drvCtrlReg = defaultDrvCtrlReg;
|
||||
chopConfReg = defaultChopConfReg;
|
||||
smartEnReg = defaultSmartEnReg;
|
||||
sgcsConfReg = defaultSgscConfReg;
|
||||
drvConfReg = defaultDrvConfReg;
|
||||
pin = p_pin;
|
||||
// No point in calling WriteAll() here because the drivers are assumed to be not powered up.
|
||||
}
|
||||
|
||||
// Write all registers, if the drivers are powered up
|
||||
void TmcDriverState::WriteAll()
|
||||
{
|
||||
SpiSendWord(pin, chopConfReg);
|
||||
SpiSendWord(pin, sgcsConfReg);
|
||||
SpiSendWord(pin, drvConfReg);
|
||||
SpiSendWord(pin, drvCtrlReg);
|
||||
SpiSendWord(pin, smartEnReg);
|
||||
SpiSendWord(chopConfReg);
|
||||
SpiSendWord(sgcsConfReg);
|
||||
SpiSendWord(drvConfReg);
|
||||
SpiSendWord(drvCtrlReg);
|
||||
SpiSendWord(smartEnReg);
|
||||
}
|
||||
|
||||
// Set the chopper control register
|
||||
void TmcDriverState::SetChopConf(uint32_t newVal)
|
||||
{
|
||||
chopConfReg = (newVal & 0x0001FFFF) | TMC_REG_CHOPCONF;
|
||||
SpiSendWord(pin, chopConfReg);
|
||||
SpiSendWord(chopConfReg);
|
||||
}
|
||||
|
||||
// Set the microstepping and microstep interpolation
|
||||
void TmcDriverState::SetMicrostepping(uint32_t shift, bool interpolate)
|
||||
{
|
||||
drvCtrlReg &= ~TMC_DRVCTRL_MRES_MASK;
|
||||
|
@ -268,23 +268,24 @@ void TmcDriverState::SetMicrostepping(uint32_t shift, bool interpolate)
|
|||
{
|
||||
drvCtrlReg &= ~TMC_DRVCTRL_INTPOL;
|
||||
}
|
||||
SpiSendWord(pin, drvCtrlReg);
|
||||
SpiSendWord(drvCtrlReg);
|
||||
}
|
||||
|
||||
// Set the motor current
|
||||
void TmcDriverState::SetCurrent(float current)
|
||||
{
|
||||
#if defined(DUET_NG) && !defined(PROTOTYPE_1)
|
||||
|
||||
// The current sense resistor is 0.051 ohms.
|
||||
// The current sense resistor on the production Duet WiFi is 0.051 ohms.
|
||||
// This gives us a range of 101mA to 3.236A in 101mA steps in the high sensitivity range (VSENSE = 1)
|
||||
drvConfReg |= TMC_DRVCONF_VSENSE; // this should always be set, but send it again just in case
|
||||
SpiSendWord(pin, drvConfReg);
|
||||
drvConfReg |= TMC_DRVCONF_VSENSE; // this should always be set, but send it again just in case
|
||||
SpiSendWord(drvConfReg);
|
||||
|
||||
const uint32_t iCurrent = (current > 2000.0) ? 2000 : (current < 100) ? 100 : (uint32_t)current;
|
||||
const uint32_t csBits = (uint32_t)((32 * iCurrent - 1600)/3236); // formula checked by simulation on a spreadsheet
|
||||
const uint32_t iCurrent = static_cast<uint32_t>(constrain<float>(current, 100.0, 2000.0));
|
||||
const uint32_t csBits = (32 * iCurrent - 1600)/3236; // formula checked by simulation on a spreadsheet
|
||||
sgcsConfReg &= ~TMC_SGCSCONF_CS_MASK;
|
||||
sgcsConfReg |= TMC_SGCSCONF_CS(csBits);
|
||||
SpiSendWord(pin, sgcsConfReg);
|
||||
SpiSendWord(sgcsConfReg);
|
||||
|
||||
#else
|
||||
|
||||
|
@ -295,30 +296,31 @@ void TmcDriverState::SetCurrent(float current)
|
|||
{
|
||||
// Need VSENSE = 0, but set up the current first to avoid temporarily exceeding the 2A rating
|
||||
const uint32_t iCurrent = (current > 2000.0) ? 2000 : (uint32_t)current;
|
||||
const uint32_t csBits = (uint32_t)((32 * iCurrent - 1500)/3050); // formula checked by simulation on a spreadsheet
|
||||
const uint32_t csBits = (32 * iCurrent - 1500)/3050; // formula checked by simulation on a spreadsheet
|
||||
sgcsConfReg &= ~TMC_SGCSCONF_CS_MASK;
|
||||
sgcsConfReg |= TMC_SGCSCONF_CS(csBits);
|
||||
SpiSendWord(pin, sgcsConfReg);
|
||||
SpiSendWord(sgcsConfReg);
|
||||
|
||||
drvConfReg &= ~TMC_DRVCONF_VSENSE;
|
||||
SpiSendWord(pin, drvConfReg);
|
||||
SpiSendWord(drvConfReg);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use VSENSE = 1
|
||||
drvConfReg |= TMC_DRVCONF_VSENSE;
|
||||
SpiSendWord(pin, drvConfReg);
|
||||
SpiSendWord(drvConfReg);
|
||||
|
||||
const uint32_t iCurrent = (current < 50) ? 50 : (uint32_t)current;
|
||||
const uint32_t csBits = (uint32_t)((32 * iCurrent - 800)/1650); // formula checked by simulation on a spreadsheet
|
||||
const uint32_t csBits = (32 * iCurrent - 800)/1650; // formula checked by simulation on a spreadsheet
|
||||
sgcsConfReg &= ~TMC_SGCSCONF_CS_MASK;
|
||||
sgcsConfReg |= TMC_SGCSCONF_CS(csBits);
|
||||
SpiSendWord(pin, sgcsConfReg);
|
||||
SpiSendWord(sgcsConfReg);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
// Enable or disable the driver
|
||||
void TmcDriverState::Enable(bool en)
|
||||
{
|
||||
chopConfReg &= ~TMC_CHOPCONF_TOFF_MASK;
|
||||
|
@ -326,12 +328,15 @@ void TmcDriverState::Enable(bool en)
|
|||
{
|
||||
chopConfReg |= TMC_CHOPCONF_TOFF(defaultChopConfToff);
|
||||
}
|
||||
SpiSendWord(pin, chopConfReg);
|
||||
SpiSendWord(chopConfReg);
|
||||
}
|
||||
|
||||
uint32_t TmcDriverState::GetStatus() const
|
||||
// Read the status
|
||||
uint32_t TmcDriverState::ReadStatus()
|
||||
{
|
||||
return SpiSendWord(pin, smartEnReg) & (TMC_RR_SG | TMC_RR_OT | TMC_RR_OTPW | TMC_RR_S2G | TMC_RR_OLA | TMC_RR_OLB | TMC_RR_STST);
|
||||
// We need to send a command in order to get up-to-date status
|
||||
SpiSendWord(smartEnReg);
|
||||
return lastReadValue & (TMC_RR_SG | TMC_RR_OT | TMC_RR_OTPW | TMC_RR_S2G | TMC_RR_OLA | TMC_RR_OLB | TMC_RR_STST);
|
||||
}
|
||||
|
||||
static TmcDriverState driverStates[NumTmc2660Drivers];
|
||||
|
@ -344,6 +349,9 @@ namespace TMC2660
|
|||
// It is assumed that the drivers are not powered, so driversPowered(true) must be called after calling this before the motors can be moved.
|
||||
void Init(const Pin driverSelectPins[NumTmc2660Drivers])
|
||||
{
|
||||
// Make sure the ENN pins are high
|
||||
pinMode(GlobalTmcEnablePin, OUTPUT_HIGH);
|
||||
|
||||
// Set up the SPI pins
|
||||
|
||||
#ifdef DUET_NG
|
||||
|
@ -352,7 +360,7 @@ namespace TMC2660
|
|||
ConfigurePin(GetPinDescription(DriversMisoPin));
|
||||
ConfigurePin(GetPinDescription(DriversSclkPin));
|
||||
#else
|
||||
// PinS AD0 and AD7 may have already be set up as an ADC pin by the Arduino core, so undo that here or we won't get a clock output
|
||||
// Pins AD0 and AD7 may have already be set up as an ADC pin by the Arduino core, so undo that here or we won't get a clock output
|
||||
ADC->ADC_CHDR = (1 << 7);
|
||||
|
||||
const PinDescription& pin2 = GetPinDescription(DriversMosiPin);
|
||||
|
@ -415,7 +423,7 @@ namespace TMC2660
|
|||
|
||||
uint32_t GetStatus(size_t drive)
|
||||
{
|
||||
return (drive < NumTmc2660Drivers) ? driverStates[drive].GetStatus() : 0;
|
||||
return (drive < NumTmc2660Drivers) ? driverStates[drive].ReadStatus() : 0;
|
||||
}
|
||||
|
||||
bool SetMicrostepping(size_t drive, int microsteps, int mode)
|
||||
|
@ -466,12 +474,19 @@ namespace TMC2660
|
|||
driversPowered = powered;
|
||||
if (powered && !wasPowered)
|
||||
{
|
||||
// Power to the drivers has been provided or restored, so we need to re-initialise them
|
||||
// Power to the drivers has been provided or restored, so we need to enable and re-initialise them
|
||||
digitalWrite(GlobalTmcEnablePin, LOW);
|
||||
delayMicroseconds(10);
|
||||
|
||||
for (size_t drive = 0; drive < NumTmc2660Drivers; ++drive)
|
||||
{
|
||||
driverStates[drive].WriteAll();
|
||||
}
|
||||
}
|
||||
else if (!powered and wasPowered)
|
||||
{
|
||||
digitalWrite(GlobalTmcEnablePin, HIGH); // disable the drivers
|
||||
}
|
||||
}
|
||||
|
||||
}; // end namespace
|
||||
|
|
|
@ -143,7 +143,7 @@ public:
|
|||
// Ensure there is a null at the specified data position.
|
||||
// Used to make sure that received fields that should be null terminated really are.
|
||||
void EnsureNull(size_t offset)
|
||||
//pre(offset < dataLength)
|
||||
pre(offset < dataLength)
|
||||
{
|
||||
*((char*)data + offset) = 0;
|
||||
}
|
||||
|
@ -162,7 +162,7 @@ public:
|
|||
|
||||
// Append a single 32-bit integer to the output buffer
|
||||
size_t AppendU32(uint32_t val)
|
||||
//pre(dataLength + sizeof(uint32_t) <= maxSpiDataLength)
|
||||
pre(dataLength + sizeof(uint32_t) <= maxSpiDataLength)
|
||||
{
|
||||
return AppendData(&val, sizeof(val));
|
||||
}
|
||||
|
@ -178,7 +178,7 @@ public:
|
|||
|
||||
// Tell the buffer that we have appended some data. Call this after writing data directly.
|
||||
void DataAppended(size_t length)
|
||||
//pre(dataLength + length <= maxSpiDataLength)
|
||||
pre(dataLength + length <= maxSpiDataLength)
|
||||
;
|
||||
};
|
||||
|
||||
|
|
|
@ -514,7 +514,8 @@ WifiFirmwareUploader::EspUploadResult WifiFirmwareUploader::flashWriteBlock(uint
|
|||
const uint16_t hdrOfst = 0;
|
||||
const uint16_t dataOfst = 16;
|
||||
const uint16_t blkBufSize = dataOfst + blkSize;
|
||||
uint8_t blkBuf[blkBufSize];
|
||||
uint32_t blkBuf32[blkBufSize/4];
|
||||
uint8_t * const blkBuf = reinterpret_cast<uint8_t*>(blkBuf32);
|
||||
|
||||
// Prepare the header for the block
|
||||
putData(blkSize, 4, blkBuf, hdrOfst + 0);
|
||||
|
@ -523,8 +524,8 @@ WifiFirmwareUploader::EspUploadResult WifiFirmwareUploader::flashWriteBlock(uint
|
|||
putData(0, 4, blkBuf, hdrOfst + 12);
|
||||
|
||||
// Get the data for the block
|
||||
size_t cnt = uploadFile->Read((char *)blkBuf + dataOfst, blkSize);
|
||||
if (cnt != EspFlashBlockSize)
|
||||
size_t cnt = uploadFile->Read(reinterpret_cast<char *>(blkBuf + dataOfst), blkSize);
|
||||
if (cnt != blkSize)
|
||||
{
|
||||
if (uploadFile->Position() == fileSize)
|
||||
{
|
||||
|
@ -545,6 +546,23 @@ WifiFirmwareUploader::EspUploadResult WifiFirmwareUploader::flashWriteBlock(uint
|
|||
putData(flashParm | flashParmVal, 2, blkBuf + dataOfst + 2, 0);
|
||||
}
|
||||
|
||||
// If the block is all 0xFF characters, don't bother to send it because that's what the flash gets erased to
|
||||
bool empty = true;
|
||||
for (size_t i = 0; i < blkSize/4; ++i)
|
||||
{
|
||||
static_assert(dataOfst % 4 == 0, "Code only works when dataOfst is a multiple of 4");
|
||||
if (blkBuf32[i + dataOfst/4] != 0xFFFFFFFF)
|
||||
{
|
||||
empty = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty)
|
||||
{
|
||||
return EspUploadResult::success;
|
||||
}
|
||||
|
||||
// Calculate the block checksum
|
||||
uint16_t cksum = checksum(blkBuf + dataOfst, blkSize, ESP_CHECKSUM_MAGIC);
|
||||
EspUploadResult stat;
|
||||
|
|
|
@ -40,13 +40,12 @@ void Fan::SetHardwarePwm(float pwmVal)
|
|||
{
|
||||
if (pin >= 0)
|
||||
{
|
||||
uint32_t p = (uint32_t)(255.0 * pwmVal);
|
||||
bool invert = hardwareInverted;
|
||||
if (inverted)
|
||||
{
|
||||
invert = !invert;
|
||||
}
|
||||
AnalogWrite(pin, (invert) ? (255 - p) : p, freq);
|
||||
AnalogOut(pin, (invert) ? (1.0 - pwmVal) : pwmVal, freq);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,15 @@ void FileStore::Init()
|
|||
closeRequested = false;
|
||||
}
|
||||
|
||||
// Invalidate the file if it uses the specified FATFS object
|
||||
void FileStore::Invalidate(const FATFS *fs)
|
||||
{
|
||||
if (file.fs == fs)
|
||||
{
|
||||
Init();
|
||||
}
|
||||
}
|
||||
|
||||
// Open a local file (for example on an SD card).
|
||||
// This is protected - only Platform can access it.
|
||||
bool FileStore::Open(const char* directory, const char* fileName, bool write)
|
||||
|
|
|
@ -41,6 +41,7 @@ public:
|
|||
float FractionRead() const; // How far in we are
|
||||
void Duplicate(); // Create a second reference to this file
|
||||
bool Flush(); // Write remaining buffer data
|
||||
void Invalidate(const FATFS *fs); // Invalidate the file if it uses the specified FATFS object
|
||||
|
||||
#if 0 // not currently used
|
||||
bool SetClusterMap(uint32_t[]); // Provide a cluster map for fast seeking
|
||||
|
|
|
@ -1589,7 +1589,7 @@ bool GCodes::SetPrintZProbe(GCodeBuffer* gb, StringRef& reply)
|
|||
else
|
||||
{
|
||||
// Use the current bed temperature as the calibration temperature if no value was provided
|
||||
params.calibTemperature = platform->GetTemperature(BED_HEATER);
|
||||
params.calibTemperature = platform->GetZProbeTemperature();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2934,7 +2934,18 @@ bool GCodes::HandleMcode(GCodeBuffer* gb, StringRef& reply)
|
|||
return true;
|
||||
}
|
||||
|
||||
case 21: // Initialise SD - ignore
|
||||
case 21: // Initialise SD card
|
||||
{
|
||||
size_t card = (gb->Seen('P')) ? gb->GetIValue() : 0;
|
||||
result = platform->GetMassStorage()->Mount(card, reply);
|
||||
}
|
||||
break;
|
||||
|
||||
case 22: // Release SD card
|
||||
{
|
||||
size_t card = (gb->Seen('P')) ? gb->GetIValue() : 0;
|
||||
result = platform->GetMassStorage()->Unmount(card, reply);
|
||||
}
|
||||
break;
|
||||
|
||||
case 23: // Set file to print
|
||||
|
@ -4758,14 +4769,7 @@ bool GCodes::HandleMcode(GCodeBuffer* gb, StringRef& reply)
|
|||
break;
|
||||
|
||||
case 570: // Set/report heater timeout
|
||||
if (gb->Seen('S'))
|
||||
{
|
||||
platform->SetTimeToHot(gb->GetFValue());
|
||||
}
|
||||
else
|
||||
{
|
||||
reply.printf("Time allowed to get to temperature: %.1f seconds.", platform->TimeToHot());
|
||||
}
|
||||
reply.copy("M570 is no longer required or supported");
|
||||
break;
|
||||
|
||||
case 571: // Set output on extrude
|
||||
|
|
332
src/Heat.cpp
332
src/Heat.cpp
|
@ -19,8 +19,7 @@ Licence: GPL
|
|||
****************************************************************************************************/
|
||||
|
||||
#include "RepRapFirmware.h"
|
||||
|
||||
const float invHeatPwmAverageCount = HEAT_SAMPLE_TIME/HEAT_PWM_AVERAGE_TIME;
|
||||
#include "Pid.h"
|
||||
|
||||
Heat::Heat(Platform* p) : platform(p), active(false), coldExtrude(false), bedHeater(BED_HEATER), chamberHeater(-1)
|
||||
{
|
||||
|
@ -100,13 +99,13 @@ bool Heat::AllHeatersAtSetTemperatures(bool includingBed) const
|
|||
bool Heat::HeaterAtSetTemperature(int8_t heater) const
|
||||
{
|
||||
// If it hasn't anything to do, it must be right wherever it is...
|
||||
if (heater < 0 || pids[heater]->SwitchedOff() || pids[heater]->FaultOccurred())
|
||||
if (heater < 0 || heater >= HEATERS || pids[heater]->SwitchedOff() || pids[heater]->FaultOccurred())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
float dt = GetTemperature(heater);
|
||||
float target = (pids[heater]->Active()) ? GetActiveTemperature(heater) : GetStandbyTemperature(heater);
|
||||
const float dt = GetTemperature(heater);
|
||||
const float target = (pids[heater]->Active()) ? GetActiveTemperature(heater) : GetStandbyTemperature(heater);
|
||||
return (target < TEMPERATURE_LOW_SO_DONT_CARE) || (fabs(dt - target) <= TEMPERATURE_CLOSE_ENOUGH);
|
||||
}
|
||||
|
||||
|
@ -117,18 +116,18 @@ Heat::HeaterStatus Heat::GetStatus(int8_t heater) const
|
|||
return HS_off;
|
||||
}
|
||||
|
||||
return (pids[heater]->FaultOccurred() ? HS_fault
|
||||
: pids[heater]->SwitchedOff()) ? HS_off
|
||||
return (pids[heater]->FaultOccurred()) ? HS_fault
|
||||
: (pids[heater]->SwitchedOff()) ? HS_off
|
||||
: (pids[heater]->Active()) ? HS_active
|
||||
: HS_standby;
|
||||
}
|
||||
|
||||
void Heat::SetActiveTemperature(int8_t heater, float t)
|
||||
{
|
||||
if (heater >= 0 && heater < HEATERS)
|
||||
{
|
||||
pids[heater]->SetActiveTemperature(t);
|
||||
}
|
||||
if (heater >= 0 && heater < HEATERS)
|
||||
{
|
||||
pids[heater]->SetActiveTemperature(t);
|
||||
}
|
||||
}
|
||||
|
||||
float Heat::GetActiveTemperature(int8_t heater) const
|
||||
|
@ -138,28 +137,28 @@ float Heat::GetActiveTemperature(int8_t heater) const
|
|||
|
||||
void Heat::SetStandbyTemperature(int8_t heater, float t)
|
||||
{
|
||||
if (heater >= 0 && heater < HEATERS)
|
||||
{
|
||||
pids[heater]->SetStandbyTemperature(t);
|
||||
}
|
||||
if (heater >= 0 && heater < HEATERS)
|
||||
{
|
||||
pids[heater]->SetStandbyTemperature(t);
|
||||
}
|
||||
}
|
||||
|
||||
float Heat::GetStandbyTemperature(int8_t heater) const
|
||||
{
|
||||
return (heater >= 0 && heater < HEATERS) ? pids[heater]->GetStandbyTemperature() : ABS_ZERO;
|
||||
return (heater >= 0 && heater < HEATERS) ? pids[heater]->GetStandbyTemperature() : ABS_ZERO;
|
||||
}
|
||||
|
||||
float Heat::GetTemperature(int8_t heater) const
|
||||
{
|
||||
return (heater >= 0 && heater < HEATERS) ? pids[heater]->GetTemperature() : ABS_ZERO;
|
||||
return (heater >= 0 && heater < HEATERS) ? pids[heater]->GetTemperature() : ABS_ZERO;
|
||||
}
|
||||
|
||||
void Heat::Activate(int8_t heater)
|
||||
{
|
||||
if (heater >= 0 && heater < HEATERS)
|
||||
{
|
||||
pids[heater]->Activate();
|
||||
}
|
||||
if (heater >= 0 && heater < HEATERS)
|
||||
{
|
||||
pids[heater]->Activate();
|
||||
}
|
||||
}
|
||||
|
||||
void Heat::SwitchOff(int8_t heater)
|
||||
|
@ -180,18 +179,18 @@ void Heat::SwitchOffAll()
|
|||
|
||||
void Heat::Standby(int8_t heater)
|
||||
{
|
||||
if (heater >= 0 && heater < HEATERS)
|
||||
{
|
||||
pids[heater]->Standby();
|
||||
}
|
||||
if (heater >= 0 && heater < HEATERS)
|
||||
{
|
||||
pids[heater]->Standby();
|
||||
}
|
||||
}
|
||||
|
||||
void Heat::ResetFault(int8_t heater)
|
||||
{
|
||||
if (heater >= 0 && heater < HEATERS)
|
||||
{
|
||||
pids[heater]->ResetFault();
|
||||
}
|
||||
if (heater >= 0 && heater < HEATERS)
|
||||
{
|
||||
pids[heater]->ResetFault();
|
||||
}
|
||||
}
|
||||
|
||||
float Heat::GetAveragePWM(int8_t heater) const
|
||||
|
@ -209,279 +208,4 @@ bool Heat::UseSlowPwm(int8_t heater) const
|
|||
return heater == bedHeater || heater == chamberHeater;
|
||||
}
|
||||
|
||||
//******************************************************************************************************
|
||||
|
||||
PID::PID(Platform* p, int8_t h) : platform(p), heater(h)
|
||||
{
|
||||
}
|
||||
|
||||
void PID::Init()
|
||||
{
|
||||
SetHeater(0.0);
|
||||
temperature = platform->GetTemperature(heater);
|
||||
activeTemperature = ABS_ZERO;
|
||||
standbyTemperature = ABS_ZERO;
|
||||
lastTemperature = temperature;
|
||||
temp_iState = 0.0;
|
||||
badTemperatureCount = 0;
|
||||
temperatureFault = false;
|
||||
active = false; // Default to standby temperature
|
||||
switchedOff = true;
|
||||
heatingUp = false;
|
||||
averagePWM = 0.0;
|
||||
|
||||
// Time the sensor was last sampled. During startup, we use the current
|
||||
// time as the initial value so as to not trigger an immediate warning from the Tick ISR.
|
||||
lastSampleTime = millis();
|
||||
}
|
||||
|
||||
void PID::SwitchOn()
|
||||
{
|
||||
if (reprap.Debug(Module::moduleHeat))
|
||||
{
|
||||
platform->MessageF(GENERIC_MESSAGE, "Heater %d %s\n", heater, (temperatureFault) ? "not switched on due to temperature fault" : "switched on");
|
||||
}
|
||||
switchedOff = temperatureFault;
|
||||
}
|
||||
|
||||
void PID::SetHeater(float power) const
|
||||
{
|
||||
platform->SetHeater(heater, power);
|
||||
}
|
||||
|
||||
void PID::Spin()
|
||||
{
|
||||
// For temperature sensors which do not require frequent sampling and averaging,
|
||||
// their temperature is read here and error/safety handling performed. However,
|
||||
// unlike the Tick ISR, this code is not executed at interrupt level and consequently
|
||||
// runs the risk of having undesirable delays between calls. To guard against this,
|
||||
// we record for each PID object when it was last sampled and have the Tick ISR
|
||||
// take action if there is a significant delay since the time of last sampling.
|
||||
lastSampleTime = millis();
|
||||
|
||||
// Always know our temperature, regardless of whether we have been switched on or not
|
||||
|
||||
TemperatureError err = TemperatureError::success; // 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
|
||||
// we don't even have a thermistor connected. So don't even check for faults if we
|
||||
// are not switched on. This is safe, as the next bit of code always turns our
|
||||
// heater off in that case anyway.
|
||||
if (temperatureFault || switchedOff)
|
||||
{
|
||||
SetHeater(0.0); // Make sure to turn it off...
|
||||
averagePWM *= (1.0 - invHeatPwmAverageCount);
|
||||
return;
|
||||
}
|
||||
|
||||
// We are switched on. Check for faults. Temperature silly-low or silly-high mean open-circuit
|
||||
// or shorted thermistor respectively.
|
||||
if (err == TemperatureError::success && temperature < BAD_LOW_TEMPERATURE)
|
||||
{
|
||||
err = TemperatureError::openCircuit;
|
||||
}
|
||||
else if (err == TemperatureError::success && temperature > platform->GetTemperatureLimit())
|
||||
{
|
||||
err = TemperatureError::tooHigh;
|
||||
}
|
||||
|
||||
if (err == TemperatureError::success)
|
||||
{
|
||||
badTemperatureCount = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (platform->IsThermistorChannel(heater) || !IsPermanentError(err))
|
||||
{
|
||||
// Error may be a temporary error and may correct itself after a few additional reads
|
||||
badTemperatureCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Error condition is not expected to correct itself (e.g., digital device such
|
||||
// as a MAX31855 reporting that the temp sensor is shorted -- even a temporary
|
||||
// short in such a situation warrants manual attention and correction).
|
||||
badTemperatureCount = MAX_BAD_TEMPERATURE_COUNT + 1;
|
||||
}
|
||||
|
||||
if (badTemperatureCount > MAX_BAD_TEMPERATURE_COUNT)
|
||||
{
|
||||
SetHeater(0.0);
|
||||
temperatureFault = true;
|
||||
platform->MessageF(GENERIC_MESSAGE, "Error: Temperature fault on heater %d: %s\n", heater, TemperatureErrorString(err));
|
||||
reprap.FlagTemperatureFault(heater);
|
||||
}
|
||||
}
|
||||
|
||||
// Now check how long it takes to warm up. If too long, maybe the thermistor is not in contact with the heater
|
||||
if (heatingUp && heater != reprap.GetHeat()->GetBedHeater() && heater != reprap.GetHeat()->GetChamberHeater()) // FIXME - also check bed warmup time?
|
||||
{
|
||||
float tmp = (active) ? activeTemperature : standbyTemperature;
|
||||
if (temperature < tmp - TEMPERATURE_CLOSE_ENOUGH)
|
||||
{
|
||||
float tim = platform->Time() - timeSetHeating;
|
||||
float limit = platform->TimeToHot();
|
||||
if (tim > limit && limit > 0.0)
|
||||
{
|
||||
SetHeater(0.0);
|
||||
temperatureFault = true;
|
||||
platform->MessageF(GENERIC_MESSAGE, "Heating fault on heater %d, T = %.1f C; still not at temperature %.1f after %f seconds.\n", heater, temperature, tmp, tim);
|
||||
reprap.FlagTemperatureFault(heater);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
heatingUp = false;
|
||||
}
|
||||
}
|
||||
|
||||
float targetTemperature = (active) ? activeTemperature : standbyTemperature;
|
||||
float error = targetTemperature - temperature;
|
||||
const PidParameters& pp = platform->GetPidParameters(heater);
|
||||
|
||||
if (!pp.UsePID())
|
||||
{
|
||||
float heaterValue = (error > 0.0) ? min<float>(pp.kS, 1.0) : 0.0;
|
||||
SetHeater(heaterValue);
|
||||
averagePWM = averagePWM * (1.0 - invHeatPwmAverageCount) + heaterValue;
|
||||
return;
|
||||
}
|
||||
|
||||
if (error < -pp.fullBand)
|
||||
{
|
||||
// actual temperature is well above target
|
||||
temp_iState = (targetTemperature + pp.fullBand - 25.0) * pp.kT; // set the I term to our estimate of what will be needed ready for the switch to PID
|
||||
SetHeater(0.0);
|
||||
averagePWM *= (1.0 - invHeatPwmAverageCount);
|
||||
lastTemperature = temperature;
|
||||
return;
|
||||
}
|
||||
|
||||
if (error > pp.fullBand)
|
||||
{
|
||||
// actual temperature is well below target
|
||||
temp_iState = (targetTemperature - pp.fullBand - 25.0) * pp.kT; // set the I term to our estimate of what will be needed ready for the switch to PID
|
||||
float heaterValue = min<float>(pp.kS, 1.0);
|
||||
SetHeater(heaterValue);
|
||||
averagePWM = averagePWM * (1.0 - invHeatPwmAverageCount) + heaterValue;
|
||||
lastTemperature = temperature;
|
||||
return;
|
||||
}
|
||||
|
||||
float sampleInterval = platform->HeatSampleTime();
|
||||
temp_iState += error * pp.kI * sampleInterval;
|
||||
|
||||
if (temp_iState < pp.pidMin)
|
||||
{
|
||||
temp_iState = pp.pidMin;
|
||||
}
|
||||
else if (temp_iState > pp.pidMax)
|
||||
{
|
||||
temp_iState = pp.pidMax;
|
||||
}
|
||||
|
||||
float temp_dState = pp.kD * (temperature - lastTemperature) / sampleInterval;
|
||||
float result = (pp.kP * error + temp_iState - temp_dState) * pp.kS / 255.0;
|
||||
|
||||
lastTemperature = temperature;
|
||||
|
||||
if (result < 0.0)
|
||||
{
|
||||
result = 0.0;
|
||||
}
|
||||
else if (result > 1.0)
|
||||
{
|
||||
result = 1.0;
|
||||
}
|
||||
|
||||
if (!temperatureFault)
|
||||
{
|
||||
SetHeater(result);
|
||||
}
|
||||
|
||||
averagePWM = averagePWM * (1.0 - invHeatPwmAverageCount) + result;
|
||||
|
||||
// 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;
|
||||
if (reprap.Debug(Module::moduleHeat))
|
||||
{
|
||||
platform->MessageF(GENERIC_MESSAGE, "Heater %d switched off", heater);
|
||||
}
|
||||
}
|
||||
|
||||
float PID::GetAveragePWM() const
|
||||
{
|
||||
return averagePWM * invHeatPwmAverageCount;
|
||||
}
|
||||
|
||||
// End
|
||||
|
|
117
src/Heat.h
117
src/Heat.h
|
@ -21,62 +21,15 @@ Licence: GPL
|
|||
#ifndef HEAT_H
|
||||
#define HEAT_H
|
||||
|
||||
/**
|
||||
* This class implements a PID controller for the heaters
|
||||
*/
|
||||
|
||||
class PID
|
||||
{
|
||||
public:
|
||||
|
||||
PID(Platform* p, int8_t h);
|
||||
void Init(); // (Re)Set everything to start
|
||||
void Spin(); // Called in a tight loop to keep things running
|
||||
void SetActiveTemperature(float t);
|
||||
float GetActiveTemperature() const;
|
||||
void SetStandbyTemperature(float t);
|
||||
float GetStandbyTemperature() const;
|
||||
void Activate(); // Switch from idle to active
|
||||
void Standby(); // Switch from active to idle
|
||||
bool Active() const; // Are we active?
|
||||
void SwitchOff(); // Not even standby - all heater power off
|
||||
bool SwitchedOff() const; // Are we switched off?
|
||||
bool FaultOccurred() const; // Has a heater fault occurred?
|
||||
void ResetFault(); // Reset a fault condition - only call this if you know what you are doing
|
||||
float GetTemperature() const; // Get the current temperature
|
||||
float GetAveragePWM() const; // Return the running average PWM to the heater. Answer is a fraction in [0, 1].
|
||||
uint32_t GetLastSampleTime() const; // Return when the temp sensor was last sampled
|
||||
float GetAccumulator() const; // Return the integral accumulator
|
||||
|
||||
private:
|
||||
|
||||
void SwitchOn();
|
||||
void SetHeater(float power) const; // power is a fraction in [0,1]
|
||||
|
||||
Platform* platform; // The instance of the class that is the RepRap hardware
|
||||
float activeTemperature; // The required active temperature
|
||||
float standbyTemperature; // The required standby temperature
|
||||
float temperature; // The current temperature
|
||||
float lastTemperature; // The previous current temperature
|
||||
float temp_iState; // The integral PID component
|
||||
bool active; // Are we active or standby?
|
||||
bool switchedOff; // Becomes false when someone tells us our active or standby temperatures
|
||||
int8_t heater; // The index of our heater
|
||||
uint8_t badTemperatureCount; // Count of sequential dud readings
|
||||
bool temperatureFault; // Has our heater developed a fault?
|
||||
float timeSetHeating; // When we were switched on
|
||||
bool heatingUp; // Are we heating up?
|
||||
float averagePWM; // The running average of the PWM.
|
||||
uint32_t lastSampleTime; // Time when the temperature was last sampled by Spin()
|
||||
};
|
||||
|
||||
/**
|
||||
* The master class that controls all the heaters in the RepRap machine
|
||||
*/
|
||||
|
||||
class PID;
|
||||
|
||||
class Heat
|
||||
{
|
||||
public:
|
||||
public:
|
||||
// Enumeration to describe the status of a heater. Note that the web interface returns the numerical values, so don't change them.
|
||||
enum HeaterStatus { HS_off = 0, HS_standby = 1, HS_active = 2, HS_fault = 3 };
|
||||
|
||||
|
@ -114,69 +67,23 @@ class Heat
|
|||
bool UseSlowPwm(int8_t heater) const; // Queried by the Platform class
|
||||
uint32_t GetLastSampleTime(int8_t heater) const;
|
||||
|
||||
private:
|
||||
private:
|
||||
|
||||
Platform* platform; // The instance of the RepRap hardware class
|
||||
Platform* platform; // The instance of the RepRap hardware class
|
||||
|
||||
bool active; // Are we active?
|
||||
PID* pids[HEATERS]; // A PID controller for each heater
|
||||
bool active; // Are we active?
|
||||
PID* pids[HEATERS]; // A PID controller for each heater
|
||||
|
||||
bool coldExtrude; // Is cold extrusion allowed?
|
||||
int8_t bedHeater; // Index of the hot bed heater to use or -1 if none is available
|
||||
int8_t chamberHeater; // Index of the chamber heater to use or -1 if none is available
|
||||
bool coldExtrude; // Is cold extrusion allowed?
|
||||
int8_t bedHeater; // Index of the hot bed heater to use or -1 if none is available
|
||||
int8_t chamberHeater; // Index of the chamber heater to use or -1 if none is available
|
||||
|
||||
float lastTime; // The last time our Spin() was called
|
||||
float longWait; // Long time for things that happen occasionally
|
||||
float lastTime; // The last time our Spin() was called
|
||||
float longWait; // Long time for things that happen occasionally
|
||||
};
|
||||
|
||||
|
||||
//***********************************************************************************************************
|
||||
|
||||
inline bool PID::Active() const
|
||||
{
|
||||
return active;
|
||||
}
|
||||
|
||||
inline float PID::GetActiveTemperature() const
|
||||
{
|
||||
return activeTemperature;
|
||||
}
|
||||
|
||||
inline float PID::GetStandbyTemperature() const
|
||||
{
|
||||
return standbyTemperature;
|
||||
}
|
||||
|
||||
inline float PID::GetTemperature() const
|
||||
{
|
||||
return temperature;
|
||||
}
|
||||
|
||||
inline bool PID::FaultOccurred() const
|
||||
{
|
||||
return temperatureFault;
|
||||
}
|
||||
|
||||
inline bool PID::SwitchedOff() const
|
||||
{
|
||||
return switchedOff;
|
||||
}
|
||||
|
||||
inline uint32_t PID::GetLastSampleTime() const
|
||||
{
|
||||
return lastSampleTime;
|
||||
}
|
||||
|
||||
inline float PID::GetAccumulator() const
|
||||
{
|
||||
return temp_iState;
|
||||
}
|
||||
|
||||
|
||||
//**********************************************************************************
|
||||
|
||||
// Heat
|
||||
|
||||
inline bool Heat::ColdExtrude() const
|
||||
{
|
||||
return coldExtrude;
|
||||
|
|
|
@ -171,7 +171,7 @@
|
|||
/ Physical Drive Configurations
|
||||
/----------------------------------------------------------------------------*/
|
||||
|
||||
#define _VOLUMES 8
|
||||
#define _VOLUMES 2
|
||||
/* Number of volumes (logical drives) to be used. */
|
||||
|
||||
|
||||
|
|
|
@ -93,9 +93,6 @@ extern "C" {
|
|||
*/
|
||||
DSTATUS disk_initialize(BYTE drv)
|
||||
{
|
||||
int i;
|
||||
Ctrl_status mem_status;
|
||||
|
||||
#if (SAM3S || SAM3U || SAM3N || SAM3XA_SERIES || SAM4S)
|
||||
/* Default RTC configuration, 24-hour mode */
|
||||
/**@TODO FIX THIS - need an RTC*/
|
||||
|
@ -113,8 +110,11 @@ DSTATUS disk_initialize(BYTE drv)
|
|||
return STA_NOINIT;
|
||||
}
|
||||
#endif
|
||||
|
||||
Ctrl_status mem_status;
|
||||
|
||||
/* Check LUN ready (USB disk report CTRL_BUSY then CTRL_GOOD) */
|
||||
for (i = 0; i < 2; i ++) {
|
||||
for (int i = 0; i < 2; i ++) {
|
||||
mem_status = mem_test_unit_ready(drv);
|
||||
if (CTRL_BUSY != mem_status) {
|
||||
break;
|
||||
|
|
|
@ -3,133 +3,67 @@
|
|||
|
||||
MassStorage::MassStorage(Platform* p) : platform(p)
|
||||
{
|
||||
memset(&fileSystem, 0, sizeof(fileSystem));
|
||||
memset(&fileSystems, 0, sizeof(fileSystems));
|
||||
}
|
||||
|
||||
void MassStorage::Init()
|
||||
{
|
||||
// Initialize SD MMC stack
|
||||
sd_mmc_init();
|
||||
sd_mmc_init(); // Initialize SD MMC stack
|
||||
|
||||
const size_t startTime = millis();
|
||||
sd_mmc_err_t err;
|
||||
do {
|
||||
err = sd_mmc_check(0);
|
||||
delay(1);
|
||||
} while (err != SD_MMC_OK && millis() - startTime < 5000);
|
||||
|
||||
if (err != SD_MMC_OK)
|
||||
// Try to mount the SD cards
|
||||
for (size_t card = 0; card < NumSdCards; ++card)
|
||||
{
|
||||
delay(3000); // Wait a few seconds so users have a chance to see this
|
||||
|
||||
platform->Message(HOST_MESSAGE, "Cannot initialise the SD card: ");
|
||||
switch (err)
|
||||
char replyBuffer[100];
|
||||
StringRef reply(replyBuffer, ARRAY_SIZE(replyBuffer));
|
||||
do { } while (!Mount(card, reply));
|
||||
if (reply.strlen() != 0)
|
||||
{
|
||||
case SD_MMC_ERR_NO_CARD:
|
||||
platform->Message(HOST_MESSAGE, "Card not found\n");
|
||||
break;
|
||||
case SD_MMC_ERR_UNUSABLE:
|
||||
platform->Message(HOST_MESSAGE, "Card is unusable, try another one\n");
|
||||
break;
|
||||
case SD_MMC_ERR_SLOT:
|
||||
platform->Message(HOST_MESSAGE, "Slot unknown\n");
|
||||
break;
|
||||
case SD_MMC_ERR_COMM:
|
||||
platform->Message(HOST_MESSAGE, "General communication error\n");
|
||||
break;
|
||||
case SD_MMC_ERR_PARAM:
|
||||
platform->Message(HOST_MESSAGE, "Illegal input parameter\n");
|
||||
break;
|
||||
case SD_MMC_ERR_WP:
|
||||
platform->Message(HOST_MESSAGE, "Card write protected\n");
|
||||
break;
|
||||
default:
|
||||
platform->MessageF(HOST_MESSAGE, "Unknown (code %d)\n", err);
|
||||
break;
|
||||
delay(3000); // Wait a few seconds so users have a chance to see this
|
||||
platform->Message(HOST_MESSAGE, reply.Pointer());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Print some card details (optional)
|
||||
|
||||
/*platform->Message(HOST_MESSAGE, "SD card detected!\nCapacity: %d\n", sd_mmc_get_capacity(0));
|
||||
platform->AppendMessage(HOST_MESSAGE, "Bus clock: %d\n", sd_mmc_get_bus_clock(0));
|
||||
platform->AppendMessage(HOST_MESSAGE, "Bus width: %d\nCard type: ", sd_mmc_get_bus_width(0));
|
||||
switch (sd_mmc_get_type(0))
|
||||
{
|
||||
case CARD_TYPE_SD | CARD_TYPE_HC:
|
||||
platform->AppendMessage(HOST_MESSAGE, "SDHC\n");
|
||||
break;
|
||||
case CARD_TYPE_SD:
|
||||
platform->AppendMessage(HOST_MESSAGE, "SD\n");
|
||||
break;
|
||||
case CARD_TYPE_MMC | CARD_TYPE_HC:
|
||||
platform->AppendMessage(HOST_MESSAGE, "MMC High Density\n");
|
||||
break;
|
||||
case CARD_TYPE_MMC:
|
||||
platform->AppendMessage(HOST_MESSAGE, "MMC\n");
|
||||
break;
|
||||
case CARD_TYPE_SDIO:
|
||||
platform->AppendMessage(HOST_MESSAGE, "SDIO\n");
|
||||
return;
|
||||
case CARD_TYPE_SD_COMBO:
|
||||
platform->AppendMessage(HOST_MESSAGE, "SD COMBO\n");
|
||||
break;
|
||||
case CARD_TYPE_UNKNOWN:
|
||||
default:
|
||||
platform->AppendMessage(HOST_MESSAGE, "Unknown\n");
|
||||
return;
|
||||
}*/
|
||||
|
||||
// Mount the file system
|
||||
|
||||
FRESULT mounted = f_mount(0, &fileSystem);
|
||||
if (mounted != FR_OK)
|
||||
{
|
||||
platform->MessageF(HOST_MESSAGE, "Can't mount filesystem 0: code %d\n", mounted);
|
||||
}
|
||||
}
|
||||
|
||||
const char* MassStorage::CombineName(const char* directory, const char* fileName)
|
||||
{
|
||||
size_t out = 0;
|
||||
size_t in = 0;
|
||||
size_t outIndex = 0;
|
||||
size_t inIndex = 0;
|
||||
|
||||
// DC 2015-11-25 Only prepend the directory if the filename does not have an absolute path
|
||||
if (directory != NULL && fileName[0] != '/' && (strlen(fileName) < 3 || !isdigit(fileName[0]) || fileName[1] != ':' || fileName[2] != '/'))
|
||||
{
|
||||
while (directory[in] != 0 && directory[in] != '\n')
|
||||
while (directory[inIndex] != 0 && directory[inIndex] != '\n')
|
||||
{
|
||||
combinedName[out] = directory[in];
|
||||
in++;
|
||||
out++;
|
||||
if (out >= ARRAY_SIZE(combinedName))
|
||||
combinedName[outIndex] = directory[inIndex];
|
||||
inIndex++;
|
||||
outIndex++;
|
||||
if (outIndex >= ARRAY_SIZE(combinedName))
|
||||
{
|
||||
platform->MessageF(GENERIC_MESSAGE, "CombineName() buffer overflow.");
|
||||
out = 0;
|
||||
outIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (in > 0 && directory[in - 1] != '/' && out < ARRAY_UPB(combinedName))
|
||||
if (inIndex > 0 && directory[inIndex - 1] != '/' && outIndex < ARRAY_UPB(combinedName))
|
||||
{
|
||||
combinedName[out] = '/';
|
||||
out++;
|
||||
combinedName[outIndex] = '/';
|
||||
outIndex++;
|
||||
}
|
||||
in = 0;
|
||||
inIndex = 0;
|
||||
}
|
||||
|
||||
while (fileName[in] != 0 && fileName[in] != '\n')
|
||||
while (fileName[inIndex] != 0 && fileName[inIndex] != '\n')
|
||||
{
|
||||
combinedName[out] = fileName[in];
|
||||
in++;
|
||||
out++;
|
||||
if (out >= ARRAY_SIZE(combinedName))
|
||||
combinedName[outIndex] = fileName[inIndex];
|
||||
inIndex++;
|
||||
outIndex++;
|
||||
if (outIndex >= ARRAY_SIZE(combinedName))
|
||||
{
|
||||
platform->Message(GENERIC_MESSAGE, "CombineName() buffer overflow.");
|
||||
out = 0;
|
||||
outIndex = 0;
|
||||
}
|
||||
}
|
||||
combinedName[out] = 0;
|
||||
combinedName[outIndex] = 0;
|
||||
|
||||
return combinedName;
|
||||
}
|
||||
|
@ -311,4 +245,124 @@ bool MassStorage::DirectoryExists(const char* directory, const char* subDirector
|
|||
return DirectoryExists(CombineName(directory, subDirectory));
|
||||
}
|
||||
|
||||
// Mount the specified SD card, returning true if done, false if needs to be called again.
|
||||
// If an error occurs, return true with the error message in 'reply'.
|
||||
// This may only be called to mount one card at a time.
|
||||
bool MassStorage::Mount(size_t card, StringRef& reply)
|
||||
{
|
||||
if (card >= NumSdCards)
|
||||
{
|
||||
reply.copy("SD card number out of range");
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool mounting = false;
|
||||
static size_t startTime;
|
||||
|
||||
if (!mounting)
|
||||
{
|
||||
sd_mmc_unmount(card); // this forces it to re-initialise the card
|
||||
startTime = millis();
|
||||
mounting = true;
|
||||
delay(2);
|
||||
}
|
||||
|
||||
sd_mmc_err_t err = sd_mmc_check(card);
|
||||
if (err != SD_MMC_OK && millis() - startTime < 5000)
|
||||
{
|
||||
delay(2);
|
||||
return false;
|
||||
}
|
||||
|
||||
mounting = false;
|
||||
if (err != SD_MMC_OK)
|
||||
{
|
||||
f_mount(card, NULL);
|
||||
|
||||
reply.printf("Cannot initialise SD card %u: ", card);
|
||||
switch (err)
|
||||
{
|
||||
case SD_MMC_ERR_NO_CARD:
|
||||
reply.cat("Card not found");
|
||||
break;
|
||||
case SD_MMC_ERR_UNUSABLE:
|
||||
reply.cat("Card is unusable, try another one");
|
||||
break;
|
||||
case SD_MMC_ERR_SLOT:
|
||||
reply.cat("Slot unknown");
|
||||
break;
|
||||
case SD_MMC_ERR_COMM:
|
||||
reply.cat("General communication error");
|
||||
break;
|
||||
case SD_MMC_ERR_PARAM:
|
||||
reply.cat("Illegal input parameter");
|
||||
break;
|
||||
case SD_MMC_ERR_WP:
|
||||
reply.cat("Card write protected");
|
||||
break;
|
||||
default:
|
||||
reply.catf("Unknown (code %d)", err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (reprap.Debug(moduleStorage))
|
||||
{
|
||||
// Print some card details
|
||||
platform->MessageF(DEBUG_MESSAGE, "SD card %u detected, capacity: %u\n", card, sd_mmc_get_capacity(card));
|
||||
platform->Message(DEBUG_MESSAGE, "Card type: ");
|
||||
switch (sd_mmc_get_type(card))
|
||||
{
|
||||
case CARD_TYPE_SD | CARD_TYPE_HC:
|
||||
platform->Message(DEBUG_MESSAGE, "SDHC\n");
|
||||
break;
|
||||
case CARD_TYPE_SD:
|
||||
platform->Message(DEBUG_MESSAGE, "SD\n");
|
||||
break;
|
||||
case CARD_TYPE_MMC | CARD_TYPE_HC:
|
||||
platform->Message(DEBUG_MESSAGE, "MMC High Density\n");
|
||||
break;
|
||||
case CARD_TYPE_MMC:
|
||||
platform->Message(DEBUG_MESSAGE, "MMC\n");
|
||||
break;
|
||||
case CARD_TYPE_SDIO:
|
||||
platform->Message(DEBUG_MESSAGE, "SDIO\n");
|
||||
break;
|
||||
case CARD_TYPE_SD_COMBO:
|
||||
platform->Message(DEBUG_MESSAGE, "SD COMBO\n");
|
||||
break;
|
||||
case CARD_TYPE_UNKNOWN:
|
||||
default:
|
||||
platform->Message(DEBUG_MESSAGE, "Unknown\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Mount the file systems
|
||||
FRESULT mounted = f_mount(card, &fileSystems[card]);
|
||||
if (mounted != FR_OK)
|
||||
{
|
||||
reply.printf("Can't mount SD card %u: code %d\n", card, mounted);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Unmount the specified SD card, returning true if done, false if needs to be called again.
|
||||
// If an error occurs, return true with the error message in 'reply'.
|
||||
bool MassStorage::Unmount(size_t card, StringRef& reply)
|
||||
{
|
||||
if (card >= NumSdCards)
|
||||
{
|
||||
reply.copy("SD card number out of range");
|
||||
return true;
|
||||
}
|
||||
|
||||
platform->InvalidateFiles(&fileSystems[card]);
|
||||
f_mount(card, NULL);
|
||||
sd_mmc_unmount(card);
|
||||
return true;
|
||||
}
|
||||
|
||||
// End
|
||||
|
|
|
@ -21,6 +21,8 @@ public:
|
|||
bool FileExists(const char* directory, const char *fileName) const;
|
||||
bool DirectoryExists(const char *path) const;
|
||||
bool DirectoryExists(const char* directory, const char* subDirectory);
|
||||
bool Mount(size_t card, StringRef& reply);
|
||||
bool Unmount(size_t card, StringRef& reply);
|
||||
|
||||
friend class Platform;
|
||||
|
||||
|
@ -32,7 +34,7 @@ protected:
|
|||
private:
|
||||
|
||||
Platform* platform;
|
||||
FATFS fileSystem;
|
||||
FATFS fileSystems[NumSdCards];
|
||||
DIR findDir;
|
||||
char combinedName[FILENAME_LENGTH + 1];
|
||||
};
|
||||
|
|
12
src/Matrix.h
12
src/Matrix.h
|
@ -30,36 +30,36 @@ public:
|
|||
|
||||
// Indexing operator, non-const version
|
||||
T& operator() (size_t r, size_t c) override
|
||||
//pre(r < ROWS; c < COLS)
|
||||
pre(r < ROWS; c < COLS)
|
||||
{
|
||||
return data[r * COLS + c];
|
||||
}
|
||||
|
||||
// Indexing operator, const version
|
||||
const T& operator() (size_t r, size_t c) const override
|
||||
//pre(r < ROWS; c < COLS)
|
||||
pre(r < ROWS; c < COLS)
|
||||
{
|
||||
return data[r * COLS + c];
|
||||
}
|
||||
|
||||
void SwapRows(size_t i, size_t j, size_t numCols = COLS)
|
||||
//pre(i < ROWS; j < ROWS)
|
||||
pre(i < ROWS; j < ROWS)
|
||||
;
|
||||
|
||||
void GaussJordan(T *solution, size_t numRows)
|
||||
//pre(numRows <= ROWS; numRows + 1 <= COLS)
|
||||
pre(numRows <= ROWS; numRows + 1 <= COLS)
|
||||
;
|
||||
|
||||
// Return a pointer to a specified row, non-const version
|
||||
T* GetRow(size_t r)
|
||||
//pre(r < ROWS)
|
||||
pre(r < ROWS)
|
||||
{
|
||||
return data + (r * COLS);
|
||||
}
|
||||
|
||||
// Return a pointer to a specified row, const version
|
||||
const T* GetRow(size_t r) const
|
||||
//pre(r < ROWS)
|
||||
pre(r < ROWS)
|
||||
{
|
||||
return data + (r * COLS);
|
||||
}
|
||||
|
|
|
@ -93,7 +93,7 @@ void Move::Init()
|
|||
|
||||
void Move::Exit()
|
||||
{
|
||||
reprap.GetPlatform()->Message(GENERIC_MESSAGE, "Move class exited.\n");
|
||||
reprap.GetPlatform()->Message(HOST_MESSAGE, "Move class exited.\n");
|
||||
active = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -204,7 +204,7 @@ inline void Move::ResumeMoving()
|
|||
|
||||
// Start the next move. Must be called with interrupts disabled, to avoid a race with the step ISR.
|
||||
inline bool Move::StartNextMove(uint32_t startTime)
|
||||
//pre(ddaRingGetPointer->GetState() == DDA::frozen)
|
||||
pre(ddaRingGetPointer->GetState() == DDA::frozen)
|
||||
{
|
||||
currentDda = ddaRingGetPointer;
|
||||
return currentDda->Start(startTime);
|
||||
|
|
|
@ -300,10 +300,9 @@ size_t OutputBuffer::EncodeString(const char *src, size_t srcLength, bool allowC
|
|||
esc = 't';
|
||||
break;
|
||||
case '"':
|
||||
esc = '"';
|
||||
break;
|
||||
case '\\':
|
||||
esc = '\\';
|
||||
case '/':
|
||||
esc = c;
|
||||
break;
|
||||
default:
|
||||
esc = 0;
|
||||
|
|
348
src/Pid.cpp
Normal file
348
src/Pid.cpp
Normal file
|
@ -0,0 +1,348 @@
|
|||
/*
|
||||
* Pid.cpp
|
||||
*
|
||||
* Created on: 21 Jul 2016
|
||||
* Author: David
|
||||
*/
|
||||
|
||||
#include "RepRapFirmware.h"
|
||||
#include "Pid.h"
|
||||
|
||||
//******************************************************************************************************
|
||||
|
||||
PID::PID(Platform* p, int8_t h) : platform(p), heater(h), mode(HeaterMode::off)
|
||||
{
|
||||
}
|
||||
|
||||
void PID::Init()
|
||||
{
|
||||
SetHeater(0.0);
|
||||
mode = HeaterMode::off;
|
||||
previousTemperaturesGood = 0;
|
||||
previousTemperatureIndex = 0;
|
||||
activeTemperature = ABS_ZERO;
|
||||
standbyTemperature = ABS_ZERO;
|
||||
temp_iState = 0.0;
|
||||
badTemperatureCount = 0;
|
||||
active = false; // Default to standby temperature
|
||||
averagePWM = lastPWM = 0.0;
|
||||
|
||||
// Time the sensor was last sampled. During startup, we use the current
|
||||
// time as the initial value so as to not trigger an immediate warning from the Tick ISR.
|
||||
lastSampleTime = millis();
|
||||
}
|
||||
|
||||
// Read and store the temperature of this heater and returns the error code.
|
||||
TemperatureError PID::ReadTemperature()
|
||||
{
|
||||
TemperatureError err = TemperatureError::success; // assume no error
|
||||
temperature = platform->GetTemperature(heater, err); // in the event of an error, err is set and BAD_ERROR_TEMPERATURE is returned
|
||||
if (err == TemperatureError::success)
|
||||
{
|
||||
if (temperature < BAD_LOW_TEMPERATURE)
|
||||
{
|
||||
err = TemperatureError::openCircuit;
|
||||
}
|
||||
else if (temperature > platform->GetTemperatureLimit())
|
||||
{
|
||||
err = TemperatureError::tooHigh;
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
// This must be called whenever the heater is turned on, and any time the heater is active and the target temperature is changed
|
||||
void PID::SwitchOn()
|
||||
{
|
||||
if (mode == HeaterMode::fault)
|
||||
{
|
||||
if (reprap.Debug(Module::moduleHeat))
|
||||
{
|
||||
platform->MessageF(GENERIC_MESSAGE, "Heater %d not switched on due to temperature fault\n", heater);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const float target = (active) ? activeTemperature : standbyTemperature;
|
||||
const HeaterMode oldMode = mode;
|
||||
mode = (temperature + TEMPERATURE_CLOSE_ENOUGH < target) ? HeaterMode::heating
|
||||
: (temperature > target + TEMPERATURE_CLOSE_ENOUGH) ? HeaterMode::cooling
|
||||
: HeaterMode::stable;
|
||||
if (mode != oldMode)
|
||||
{
|
||||
heatingFaultCount = 0;
|
||||
if (mode == HeaterMode::heating)
|
||||
{
|
||||
timeSetHeating = platform->Time();
|
||||
}
|
||||
if (reprap.Debug(Module::moduleHeat) && oldMode == HeaterMode::off)
|
||||
{
|
||||
platform->MessageF(GENERIC_MESSAGE, "Heater %d switched on\n", heater);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PID::Spin()
|
||||
{
|
||||
// For temperature sensors which do not require frequent sampling and averaging,
|
||||
// their temperature is read here and error/safety handling performed. However,
|
||||
// unlike the Tick ISR, this code is not executed at interrupt level and consequently
|
||||
// runs the risk of having undesirable delays between calls. To guard against this,
|
||||
// we record for each PID object when it was last sampled and have the Tick ISR
|
||||
// take action if there is a significant delay since the time of last sampling.
|
||||
lastSampleTime = millis();
|
||||
|
||||
// Set up the default PWM value
|
||||
float heaterPwm = 0.0;
|
||||
|
||||
// Read the temperature
|
||||
TemperatureError err = ReadTemperature();
|
||||
const PidParameters& pp = platform->GetPidParameters(heater);
|
||||
|
||||
// Handle any temperature reading error and calculate the temperature rate of change, if possible
|
||||
if (err != TemperatureError::success)
|
||||
{
|
||||
previousTemperaturesGood <<= 1; // this reading isn't a good one
|
||||
if (mode > HeaterMode::off) // don't worry about errors when reading heaters that are switched off or flagged as having faults
|
||||
{
|
||||
// Error may be a temporary error and may correct itself after a few additional reads
|
||||
badTemperatureCount++;
|
||||
if (badTemperatureCount > MAX_BAD_TEMPERATURE_COUNT)
|
||||
{
|
||||
SetHeater(0.0); // do this here just to be sure, in case the call to platform->Message causes a delay
|
||||
mode = HeaterMode::fault;
|
||||
platform->MessageF(GENERIC_MESSAGE, "Error: Temperature reading fault on heater %d: %s\n", heater, TemperatureErrorString(err));
|
||||
reprap.FlagTemperatureFault(heater);
|
||||
}
|
||||
}
|
||||
|
||||
// We have already initialised heaterPwm to 0.0 so no need to do that here
|
||||
}
|
||||
else
|
||||
{
|
||||
// We have a good temperature reading. Calculate the derivative, if possible.
|
||||
float derivative = 0.0;
|
||||
bool gotDerivative = false;
|
||||
badTemperatureCount = 0;
|
||||
if ((previousTemperaturesGood & (1 << (NumPreviousTemperatures - 1))) != 0)
|
||||
{
|
||||
derivative = (temperature - previousTemperatures[previousTemperatureIndex]) / (platform->HeatSampleTime() * NumPreviousTemperatures);
|
||||
gotDerivative = true;
|
||||
}
|
||||
previousTemperatures[previousTemperatureIndex] = temperature;
|
||||
previousTemperaturesGood = (previousTemperaturesGood << 1) | 1;
|
||||
|
||||
// Get the target temperature and the error
|
||||
const float targetTemperature = (active) ? activeTemperature : standbyTemperature;
|
||||
const float error = targetTemperature - temperature;
|
||||
|
||||
// Do the heating checks
|
||||
switch(mode)
|
||||
{
|
||||
case HeaterMode::heating:
|
||||
{
|
||||
if (error <= TEMPERATURE_CLOSE_ENOUGH)
|
||||
{
|
||||
mode = HeaterMode::stable;
|
||||
heatingFaultCount = 0;
|
||||
}
|
||||
else if (gotDerivative)
|
||||
{
|
||||
float startupTime;
|
||||
if (derivative < GetExpectedHeatingRate(temperature, lastPWM, startupTime) && platform->Time() - timeSetHeating > startupTime)
|
||||
{
|
||||
++heatingFaultCount;
|
||||
if (heatingFaultCount * platform->HeatSampleTime() > MaxHeatingFaultTime)
|
||||
{
|
||||
SetHeater(0.0); // do this here just to be sure
|
||||
mode = HeaterMode::fault;
|
||||
platform->MessageF(GENERIC_MESSAGE, "Error: heating fault on heater %d, temperature rising too slowly\n", heater);
|
||||
reprap.FlagTemperatureFault(heater);
|
||||
}
|
||||
}
|
||||
else if (heatingFaultCount != 0)
|
||||
{
|
||||
--heatingFaultCount;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Leave the heating fault count alone
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case HeaterMode::stable:
|
||||
if (fabs(error) > MaxStableTemperatureError)
|
||||
{
|
||||
mode = HeaterMode::fault;
|
||||
platform->MessageF(GENERIC_MESSAGE, "Error: heating fault on heater %d, temperature excursion too large\n", heater);
|
||||
}
|
||||
break;
|
||||
|
||||
case HeaterMode::cooling:
|
||||
if (-error <= TEMPERATURE_CLOSE_ENOUGH && targetTemperature > MaxAmbientTemperature)
|
||||
{
|
||||
// We have cooled to close to the target temperature, so we should now maintain that temperature
|
||||
mode = HeaterMode::stable;
|
||||
heatingFaultCount = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We could check for temperature excessive or not falling here, but without an alarm there is not much we can do
|
||||
}
|
||||
break;
|
||||
|
||||
case HeaterMode::off:
|
||||
case HeaterMode::fault:
|
||||
break;
|
||||
}
|
||||
|
||||
// If we are still switched on, calculate the PWM
|
||||
if (mode > HeaterMode::off)
|
||||
{
|
||||
if (!pp.UsePID())
|
||||
{
|
||||
heaterPwm = (error > 0.0) ? 1.0 : 0.0;
|
||||
}
|
||||
else if (error < -pp.fullBand)
|
||||
{
|
||||
// actual temperature is well above target
|
||||
temp_iState = (targetTemperature + pp.fullBand - 25.0) * pp.kT; // set the I term to our estimate of what will be needed ready for the switch to PID
|
||||
heaterPwm = 0.0;
|
||||
}
|
||||
else if (error > pp.fullBand)
|
||||
{
|
||||
// actual temperature is well below target
|
||||
temp_iState = (targetTemperature - pp.fullBand - 25.0) * pp.kT; // set the I term to our estimate of what will be needed ready for the switch to PID
|
||||
heaterPwm = 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
temp_iState = constrain<float>(temp_iState + (error * pp.kI * platform->HeatSampleTime()), pp.pidMin, pp.pidMax);
|
||||
heaterPwm = ((pp.kP * error) + temp_iState - (pp.kD * derivative)) * (1.0/255.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Scale the heater power by kS, constrain it, set the heater power, and update the average PWM
|
||||
lastPWM = heaterPwm;
|
||||
heaterPwm = constrain<float>(heaterPwm * pp.kS, 0.0, 1.0);
|
||||
SetHeater(heaterPwm);
|
||||
averagePWM = averagePWM * (1.0 - platform->HeatSampleTime()/HEAT_PWM_AVERAGE_TIME) + heaterPwm;
|
||||
previousTemperatureIndex = (previousTemperatureIndex + 1) % NumPreviousTemperatures;
|
||||
|
||||
// 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);
|
||||
}
|
||||
else
|
||||
{
|
||||
activeTemperature = t;
|
||||
if (mode > HeaterMode::off && active)
|
||||
{
|
||||
SwitchOn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PID::SetStandbyTemperature(float t)
|
||||
{
|
||||
if (t > platform->GetTemperatureLimit())
|
||||
{
|
||||
platform->MessageF(GENERIC_MESSAGE, "Error: Temperature %.1f too high for heater %d!\n", t, heater);
|
||||
}
|
||||
else
|
||||
{
|
||||
standbyTemperature = t;
|
||||
if (mode > HeaterMode::off && !active)
|
||||
{
|
||||
SwitchOn();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PID::Activate()
|
||||
{
|
||||
if (mode != HeaterMode::fault)
|
||||
{
|
||||
active = true;
|
||||
SwitchOn();
|
||||
}
|
||||
}
|
||||
|
||||
void PID::Standby()
|
||||
{
|
||||
if (mode != HeaterMode::fault)
|
||||
{
|
||||
active = false;
|
||||
SwitchOn();
|
||||
}
|
||||
}
|
||||
|
||||
void PID::ResetFault()
|
||||
{
|
||||
mode = HeaterMode::off;
|
||||
SwitchOff();
|
||||
badTemperatureCount = 0;
|
||||
}
|
||||
|
||||
void PID::SwitchOff()
|
||||
{
|
||||
SetHeater(0.0);
|
||||
if (mode > HeaterMode::off)
|
||||
{
|
||||
mode = HeaterMode::off;
|
||||
}
|
||||
if (reprap.Debug(Module::moduleHeat))
|
||||
{
|
||||
platform->MessageF(GENERIC_MESSAGE, "Heater %d switched off", heater);
|
||||
}
|
||||
}
|
||||
|
||||
float PID::GetAveragePWM() const
|
||||
{
|
||||
return averagePWM * platform->HeatSampleTime()/HEAT_PWM_AVERAGE_TIME;
|
||||
}
|
||||
|
||||
// Get the minimum heating rate we expect at the specified temperature and pwm, and the heater startup time
|
||||
float PID::GetExpectedHeatingRate(float temp, float pwm, float& startupTime) const
|
||||
{
|
||||
float initialHeatingRate; // this will be the minimum heating rate @ 20C
|
||||
float maxTemperature; // this will be the highest temperature that we are certain the heater can reach
|
||||
|
||||
if (heater == reprap.GetHeat()->GetBedHeater())
|
||||
{
|
||||
initialHeatingRate = BedHeatingRate;
|
||||
maxTemperature = BedHeaterAchievableTemp;
|
||||
startupTime = BedHeaterStartupTime;
|
||||
}
|
||||
else if (heater == reprap.GetHeat()->GetChamberHeater())
|
||||
{
|
||||
initialHeatingRate = ChamberHeatingRate;
|
||||
maxTemperature = ChamberHeaterAchievableTemp;
|
||||
startupTime = ChamberHeaterStartupTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
initialHeatingRate = ExtruderHeatingRate;
|
||||
maxTemperature = ExtruderHeaterAchievableTemp;
|
||||
startupTime = ExtruderHeaterStartupTime;
|
||||
}
|
||||
|
||||
if (pwm < 0.1)
|
||||
{
|
||||
return 0.0; // avoid overflow etc. in the following calculation because a low or silly PWM value was passed
|
||||
}
|
||||
|
||||
const float adjustedMaxTemp = pwm * (maxTemperature - NormalAmbientTemperature) + NormalAmbientTemperature;
|
||||
return (adjustedMaxTemp - temp) * initialHeatingRate * pwm/(adjustedMaxTemp - NormalAmbientTemperature);
|
||||
}
|
||||
|
||||
// End
|
125
src/Pid.h
Normal file
125
src/Pid.h
Normal file
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* Pid.h
|
||||
*
|
||||
* Created on: 21 Jul 2016
|
||||
* Author: David
|
||||
*/
|
||||
|
||||
#ifndef SRC_PID_H_
|
||||
#define SRC_PID_H_
|
||||
|
||||
/**
|
||||
* This class implements a PID controller for the heaters
|
||||
*/
|
||||
|
||||
class PID
|
||||
{
|
||||
enum class HeaterMode : uint8_t
|
||||
{
|
||||
// The order of these is important because we test "mode > HeatingMode::off" to determine whether the heater is active
|
||||
fault,
|
||||
off,
|
||||
heating,
|
||||
cooling,
|
||||
stable
|
||||
};
|
||||
|
||||
static const size_t NumPreviousTemperatures = 4; // How many samples we average the temperature derivative over
|
||||
|
||||
public:
|
||||
|
||||
PID(Platform* p, int8_t h);
|
||||
void Init(); // (Re)Set everything to start
|
||||
void Spin(); // Called in a tight loop to keep things running
|
||||
void SetActiveTemperature(float t);
|
||||
float GetActiveTemperature() const;
|
||||
void SetStandbyTemperature(float t);
|
||||
float GetStandbyTemperature() const;
|
||||
void Activate(); // Switch from idle to active
|
||||
void Standby(); // Switch from active to idle
|
||||
bool Active() const; // Are we active?
|
||||
void SwitchOff(); // Not even standby - all heater power off
|
||||
bool SwitchedOff() const; // Are we switched off?
|
||||
bool FaultOccurred() const; // Has a heater fault occurred?
|
||||
void ResetFault(); // Reset a fault condition - only call this if you know what you are doing
|
||||
float GetTemperature() const; // Get the current temperature
|
||||
float GetAveragePWM() const; // Return the running average PWM to the heater. Answer is a fraction in [0, 1].
|
||||
uint32_t GetLastSampleTime() const; // Return when the temp sensor was last sampled
|
||||
float GetAccumulator() const; // Return the integral accumulator
|
||||
|
||||
private:
|
||||
|
||||
void SwitchOn(); // Turn the heater on and set the mode
|
||||
void SetHeater(float power) const; // Power is a fraction in [0,1]
|
||||
TemperatureError ReadTemperature(); // Read and store the temperature of this heater
|
||||
float GetExpectedHeatingRate(float temp, float pwm, float& startupTime) const; // Get the minimum heating rate we expect and the heater startup time
|
||||
|
||||
Platform* platform; // The instance of the class that is the RepRap hardware
|
||||
float activeTemperature; // The required active temperature
|
||||
float standbyTemperature; // The required standby temperature
|
||||
float temperature; // The current temperature
|
||||
float previousTemperatures[NumPreviousTemperatures]; // The temperatures of the previous NumDerivativeSamples measurements, used for calculating the derivative
|
||||
size_t previousTemperatureIndex; // Which slot in previousTemperature we fill in next
|
||||
float temp_iState; // The integral PID component
|
||||
float lastPWM; // The last PWM value we output, before scaling by kS
|
||||
float averagePWM; // The running average of the PWM, after scaling.
|
||||
float timeSetHeating; // When we turned on the heater
|
||||
uint32_t lastSampleTime; // Time when the temperature was last sampled by Spin()
|
||||
|
||||
uint16_t heatingFaultCount; // Count of questionable heating behaviours
|
||||
|
||||
int8_t heater; // The index of our heater
|
||||
uint8_t previousTemperaturesGood; // Bitmap indicating which previous temperature were good readings
|
||||
HeaterMode mode; // Current state of the heater
|
||||
bool active; // Are we active or standby?
|
||||
uint8_t badTemperatureCount; // Count of sequential dud readings
|
||||
|
||||
static_assert(sizeof(previousTemperaturesGood) * 8 >= NumPreviousTemperatures, "too few bits in previousTemperaturesGood");
|
||||
};
|
||||
|
||||
inline bool PID::Active() const
|
||||
{
|
||||
return active;
|
||||
}
|
||||
|
||||
inline float PID::GetActiveTemperature() const
|
||||
{
|
||||
return activeTemperature;
|
||||
}
|
||||
|
||||
inline float PID::GetStandbyTemperature() const
|
||||
{
|
||||
return standbyTemperature;
|
||||
}
|
||||
|
||||
inline float PID::GetTemperature() const
|
||||
{
|
||||
return temperature;
|
||||
}
|
||||
|
||||
inline bool PID::FaultOccurred() const
|
||||
{
|
||||
return mode == HeaterMode::fault;
|
||||
}
|
||||
|
||||
inline bool PID::SwitchedOff() const
|
||||
{
|
||||
return mode == HeaterMode::off;
|
||||
}
|
||||
|
||||
inline uint32_t PID::GetLastSampleTime() const
|
||||
{
|
||||
return lastSampleTime;
|
||||
}
|
||||
|
||||
inline float PID::GetAccumulator() const
|
||||
{
|
||||
return temp_iState;
|
||||
}
|
||||
|
||||
inline void PID::SetHeater(float power) const
|
||||
{
|
||||
platform->SetHeater(heater, power);
|
||||
}
|
||||
|
||||
#endif /* SRC_PID_H_ */
|
137
src/Platform.cpp
137
src/Platform.cpp
|
@ -24,6 +24,7 @@
|
|||
|
||||
#include "sam/drivers/tc/tc.h"
|
||||
#include "sam/drivers/hsmci/hsmci.h"
|
||||
#include "sd_mmc.h"
|
||||
|
||||
#if defined(DUET_NG) && !defined(PROTOTYPE_1)
|
||||
# include <TMC2660.h>
|
||||
|
@ -39,8 +40,8 @@ extern "C" char *sbrk(int i);
|
|||
#ifdef DUET_NG
|
||||
const uint16_t driverPowerOnAdcReading = (uint16_t)(4096 * 10.0/PowerFailVoltageRange); // minimum voltage at which we initialise the drivers
|
||||
const uint16_t driverPowerOffAdcReading = (uint16_t)(4096 * 9.5/PowerFailVoltageRange); // voltages below this flag the drivers as unusable
|
||||
const uint16_t driverOverVoltageAdcReading = (uint16_t)(4096 * 29.0/PowerFailVoltageRange); // voltages above this cause driver shutdown
|
||||
const uint16_t driverNormalVoltageAdcReading = (uint16_t)(4096 * 25.5/PowerFailVoltageRange); // voltages at or below this are normal
|
||||
const uint16_t driverOverVoltageAdcReading = (uint16_t)(4096 * 29.5/PowerFailVoltageRange); // voltages above this cause driver shutdown
|
||||
const uint16_t driverNormalVoltageAdcReading = (uint16_t)(4096 * 27.5/PowerFailVoltageRange); // voltages at or below this are normal
|
||||
#endif
|
||||
|
||||
const uint8_t memPattern = 0xA5;
|
||||
|
@ -265,7 +266,6 @@ void Platform::Init()
|
|||
|
||||
configuredHeaters = (BED_HEATER >= 0) ? (1 << BED_HEATER) : 0;
|
||||
heatSampleTime = HEAT_SAMPLE_TIME;
|
||||
timeToHot = TIME_TO_HOT;
|
||||
|
||||
// Enable pullups on all the SPI CS pins. This is required if we are using more than one device on the SPI bus.
|
||||
// Otherwise, when we try to initialise the first device, the other devices may respond as well because their CS lines are not high.
|
||||
|
@ -407,11 +407,11 @@ void Platform::Init()
|
|||
InitialiseInterrupts(); // also sets 'active' to true
|
||||
}
|
||||
|
||||
void Platform::InvalidateFiles()
|
||||
void Platform::InvalidateFiles(const FATFS *fs)
|
||||
{
|
||||
for (size_t i = 0; i < MAX_FILES; i++)
|
||||
{
|
||||
files[i]->Init();
|
||||
files[i]->Invalidate(fs);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -431,7 +431,7 @@ void Platform::SetTemperatureLimit(float t)
|
|||
|
||||
// Specify which thermistor channel a particular heater uses
|
||||
void Platform::SetThermistorNumber(size_t heater, size_t thermistor)
|
||||
//pre(heater < HEATERS && thermistor < HEATERS)
|
||||
pre(heater < HEATERS && thermistor < HEATERS)
|
||||
{
|
||||
heaterTempChannels[heater] = thermistor;
|
||||
|
||||
|
@ -570,10 +570,25 @@ void Platform::GetZProbeAxes(bool (&axes)[AXES])
|
|||
}
|
||||
}
|
||||
|
||||
float Platform::ZProbeStopHeight()
|
||||
// Get our best estimate of the Z probe temperature
|
||||
float Platform::GetZProbeTemperature()
|
||||
{
|
||||
const int8_t bedHeater = reprap.GetHeat()->GetBedHeater();
|
||||
float temperature = (bedHeater >= 0) ? GetTemperature(bedHeater) : 25.0;
|
||||
if (bedHeater >= 0)
|
||||
{
|
||||
TemperatureError err;
|
||||
const float temp = GetTemperature(bedHeater, err);
|
||||
if (err == TemperatureError::success)
|
||||
{
|
||||
return temp;
|
||||
}
|
||||
}
|
||||
return 25.0; // assume 25C if we can't read he bed temperature
|
||||
}
|
||||
|
||||
float Platform::ZProbeStopHeight()
|
||||
{
|
||||
const float temperature = GetZProbeTemperature();
|
||||
switch (nvData.zProbeType)
|
||||
{
|
||||
case 1:
|
||||
|
@ -1405,8 +1420,8 @@ void Platform::Diagnostics(MessageType mtype)
|
|||
}
|
||||
MessageF(mtype, "Free file entries: %u\n", numFreeFiles);
|
||||
|
||||
// Show the HSMCI speed
|
||||
MessageF(mtype, "SD card interface speed: %.1fMBytes/sec\n", (float)hsmci_get_speed()/1000000.0);
|
||||
// Show the HSMCI CD pin and speed
|
||||
MessageF(mtype, "SD card 0 %s, interface speed: %.1fMBytes/sec\n", (sd_mmc_card_detected(0) ? "detected" : "not detected"), (float)hsmci_get_speed()/1000000.0);
|
||||
|
||||
// Show the longest SD card write time
|
||||
MessageF(mtype, "SD card longest block write time: %.1fms\n", FileStore::GetAndClearLongestWriteTime());
|
||||
|
@ -1513,7 +1528,7 @@ void Platform::ClassReport(float &lastTime)
|
|||
|
||||
// Result is in degrees celsius
|
||||
|
||||
float Platform::GetTemperature(size_t heater, TemperatureError* err)
|
||||
float Platform::GetTemperature(size_t heater, TemperatureError& err)
|
||||
{
|
||||
// Note that at this point we're actually getting an averaged ADC read, not a "raw" temp. For thermistors,
|
||||
// we're getting an averaged voltage reading which we'll convert to a temperature.
|
||||
|
@ -1535,10 +1550,7 @@ float Platform::GetTemperature(size_t heater, TemperatureError* err)
|
|||
if (rawTemp >= (int)AD_DISCONNECTED_VIRTUAL)
|
||||
{
|
||||
// thermistor is disconnected
|
||||
if (err)
|
||||
{
|
||||
*err = TemperatureError::openCircuit;
|
||||
}
|
||||
err = TemperatureError::openCircuit;
|
||||
return ABS_ZERO;
|
||||
}
|
||||
|
||||
|
@ -1549,46 +1561,32 @@ float Platform::GetTemperature(size_t heater, TemperatureError* err)
|
|||
float resistance = reading * p.thermistorSeriesR / ((AD_RANGE_VIRTUAL + 1) - reading);
|
||||
if (resistance > p.GetRInf())
|
||||
{
|
||||
err = TemperatureError::success;
|
||||
return ABS_ZERO + p.GetBeta() / log(resistance / p.GetRInf());
|
||||
}
|
||||
else
|
||||
{
|
||||
// thermistor short circuit, return a high temperature
|
||||
if (err)
|
||||
{
|
||||
*err = TemperatureError::shortCircuit;
|
||||
}
|
||||
}
|
||||
|
||||
// thermistor short circuit, return a high temperature
|
||||
err = TemperatureError::shortCircuit;
|
||||
return BAD_ERROR_TEMPERATURE;
|
||||
}
|
||||
else if (IsThermocoupleChannel(heater))
|
||||
|
||||
if (IsThermocoupleChannel(heater))
|
||||
{
|
||||
// MAX31855 thermocouple chip
|
||||
float temp;
|
||||
TemperatureError res = SpiTempSensors[heaterTempChannels[heater] - FirstThermocoupleChannel].GetThermocoupleTemperature(&temp);
|
||||
if (res == TemperatureError::success)
|
||||
{
|
||||
return temp;
|
||||
}
|
||||
if (err)
|
||||
{
|
||||
*err = res;
|
||||
}
|
||||
err = SpiTempSensors[heaterTempChannels[heater] - FirstThermocoupleChannel].GetThermocoupleTemperature(&temp);
|
||||
return (err == TemperatureError::success) ? temp : BAD_ERROR_TEMPERATURE;
|
||||
}
|
||||
else if (IsRtdChannel(heater))
|
||||
|
||||
if (IsRtdChannel(heater))
|
||||
{
|
||||
// MAX31865 RTD chip
|
||||
float temp;
|
||||
TemperatureError res = SpiTempSensors[heaterTempChannels[heater] - FirstRtdChannel].GetRtdTemperature(&temp);
|
||||
if (res == TemperatureError::success)
|
||||
{
|
||||
return temp;
|
||||
}
|
||||
if (err)
|
||||
{
|
||||
*err = res;
|
||||
}
|
||||
err = SpiTempSensors[heaterTempChannels[heater] - FirstRtdChannel].GetRtdTemperature(&temp);
|
||||
return (err == TemperatureError::success) ? temp : BAD_ERROR_TEMPERATURE;
|
||||
}
|
||||
|
||||
err = TemperatureError::unknownChannel;
|
||||
return BAD_ERROR_TEMPERATURE;
|
||||
}
|
||||
|
||||
|
@ -1600,8 +1598,9 @@ bool Platform::AnyHeaterHot(uint16_t heaters, float t)
|
|||
{
|
||||
if (((1 << h) & heaters) != 0)
|
||||
{
|
||||
const float ht = GetTemperature(h);
|
||||
if (ht >= t || ht < BAD_LOW_TEMPERATURE)
|
||||
TemperatureError err;
|
||||
const float ht = GetTemperature(h, err);
|
||||
if (err != TemperatureError::success || ht >= t || ht < BAD_LOW_TEMPERATURE)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -1612,8 +1611,9 @@ bool Platform::AnyHeaterHot(uint16_t heaters, float t)
|
|||
int8_t bedHeater = reprap.GetHeat()->GetBedHeater();
|
||||
if (bedHeater >= 0 && ((1 << (unsigned int)bedHeater) & heaters) != 0)
|
||||
{
|
||||
const float ht = GetTemperature(bedHeater);
|
||||
if (ht >= t || ht < BAD_LOW_TEMPERATURE)
|
||||
TemperatureError err;
|
||||
const float ht = GetTemperature(bedHeater, err);
|
||||
if (err != TemperatureError::success || ht >= t || ht < BAD_LOW_TEMPERATURE)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -1623,8 +1623,9 @@ bool Platform::AnyHeaterHot(uint16_t heaters, float t)
|
|||
int8_t chamberHeater = reprap.GetHeat()->GetChamberHeater();
|
||||
if (chamberHeater >= 0 && ((1 << (unsigned int)chamberHeater) & heaters) != 0)
|
||||
{
|
||||
const float ht = GetTemperature(chamberHeater);
|
||||
if (ht >= t || ht < BAD_LOW_TEMPERATURE)
|
||||
TemperatureError err;
|
||||
const float ht = GetTemperature(chamberHeater, err);
|
||||
if (err != TemperatureError::success || ht >= t || ht < BAD_LOW_TEMPERATURE)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -1653,16 +1654,11 @@ const PidParameters& Platform::GetPidParameters(size_t heater) const
|
|||
// power is a fraction in [0,1]
|
||||
|
||||
void Platform::SetHeater(size_t heater, float power)
|
||||
{
|
||||
SetHeaterPwm(heater, (uint8_t)(255.0 * min<float>(1.0, max<float>(0.0, power))));
|
||||
}
|
||||
|
||||
void Platform::SetHeaterPwm(size_t heater, uint8_t power)
|
||||
{
|
||||
if (heatOnPins[heater] >= 0)
|
||||
{
|
||||
uint16_t freq = (reprap.GetHeat()->UseSlowPwm(heater)) ? SlowHeaterPwmFreq : NormalHeaterPwmFreq;
|
||||
AnalogWrite(heatOnPins[heater], (HEAT_ON) ? power : 255 - power, freq);
|
||||
AnalogOut(heatOnPins[heater], (HEAT_ON) ? power : 1.0 - power, freq);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1921,12 +1917,12 @@ void Platform::UpdateMotorCurrent(size_t driver)
|
|||
// Extruder 0 is on DAC channel 0
|
||||
if (driver == 4)
|
||||
{
|
||||
float dacVoltage = max<float>(current * 0.008*senseResistor + stepperDacVoltageOffset, 0.0); // the voltage we want from the DAC relative to its minimum
|
||||
uint32_t dac = (uint32_t)((256 * dacVoltage + 0.5 * stepperDacVoltageRange)/stepperDacVoltageRange);
|
||||
const float dacVoltage = max<float>(current * 0.008 * senseResistor + stepperDacVoltageOffset, 0.0); // the voltage we want from the DAC relative to its minimum
|
||||
const float dac = dacVoltage/stepperDacVoltageRange;
|
||||
# ifdef DUET_NG
|
||||
AnalogWrite(DAC1, dac);
|
||||
AnalogOut(DAC1, dac);
|
||||
# else
|
||||
AnalogWrite(DAC0, dac);
|
||||
AnalogOut(DAC0, dac);
|
||||
# endif
|
||||
}
|
||||
else
|
||||
|
@ -2611,28 +2607,15 @@ const char* Platform::GetElectronicsString() const
|
|||
|
||||
// Direct pin operations
|
||||
// Set the specified pin to the specified output level. Return true if success, false if not allowed.
|
||||
bool Platform::SetPin(int pin, int level)
|
||||
bool Platform::SetPin(int pin, float level)
|
||||
{
|
||||
if (pin >= 0 && (unsigned int)pin < NUM_PINS_ALLOWED && (level >= 0 || level <= 255))
|
||||
if (pin >= 0 && (unsigned int)pin < NUM_PINS_ALLOWED)
|
||||
{
|
||||
const size_t index = (unsigned int)pin/8;
|
||||
const uint8_t mask = 1 << ((unsigned int)pin & 7);
|
||||
if ((pinAccessAllowed[index] & mask) != 0)
|
||||
{
|
||||
#ifdef DUET_NG //TODO temporary
|
||||
if (level == 1)
|
||||
{
|
||||
level = 255;
|
||||
}
|
||||
AnalogWrite(pin, level, 1000);
|
||||
#else
|
||||
if ((pinInitialised[index] & mask) == 0)
|
||||
{
|
||||
pinMode(pin, (level == 0) ? OUTPUT_LOW : OUTPUT_HIGH);
|
||||
pinInitialised[index] |= mask;
|
||||
}
|
||||
digitalWrite(pin, level);
|
||||
#endif
|
||||
AnalogOut(pin, level);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -2841,7 +2824,7 @@ void Platform::Tick()
|
|||
if (sum < thermistorOverheatSums[currentHeater] || sum >= AD_DISCONNECTED_REAL * THERMISTOR_AVERAGE_READINGS)
|
||||
{
|
||||
// We have an over-temperature or disconnected reading from this thermistor, so turn off the heater
|
||||
SetHeaterPwm(currentHeater, 0);
|
||||
SetHeater(currentHeater, 0.0);
|
||||
LogError(ErrorCode::BadTemp);
|
||||
}
|
||||
}
|
||||
|
@ -2855,7 +2838,7 @@ void Platform::Tick()
|
|||
// Do not call Time() here, it isn't safe. We use millis() instead.
|
||||
if ((millis() - reprap.GetHeat()->GetLastSampleTime(currentHeater)) > maxPidSpinDelay)
|
||||
{
|
||||
SetHeaterPwm(currentHeater, 0);
|
||||
SetHeater(currentHeater, 0.0);
|
||||
LogError(ErrorCode::BadTemp);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,7 +55,6 @@ Licence: GPL
|
|||
# include "MCP4461.h"
|
||||
#endif
|
||||
|
||||
#include "MassStorage.h"
|
||||
#include "FileStore.h"
|
||||
#include "MessageType.h"
|
||||
#include "Fan.h"
|
||||
|
@ -66,6 +65,7 @@ const bool FORWARDS = true;
|
|||
const bool BACKWARDS = !FORWARDS;
|
||||
|
||||
#include "Pins.h"
|
||||
#include "MassStorage.h"
|
||||
|
||||
/**************************************************************************************************/
|
||||
|
||||
|
@ -489,7 +489,7 @@ public:
|
|||
const char* GetMacroDir() const; // Where the user-defined macros are
|
||||
const char* GetConfigFile() const; // Where the configuration is stored (in the system dir).
|
||||
const char* GetDefaultFile() const; // Where the default configuration is stored (in the system dir).
|
||||
void InvalidateFiles(); // Called to invalidate files when the SD card is removed
|
||||
void InvalidateFiles(const FATFS *fs); // Called to invalidate files when the SD card is removed
|
||||
|
||||
// Message output (see MessageType for further details)
|
||||
|
||||
|
@ -589,14 +589,13 @@ public:
|
|||
|
||||
// Heat and temperature
|
||||
|
||||
float GetTemperature(size_t heater, TemperatureError* err = nullptr); // Result is in degrees Celsius
|
||||
float GetTemperature(size_t heater, TemperatureError& err); // Result is in degrees Celsius
|
||||
float GetZProbeTemperature(); // Get our best estimate of the Z probe temperature
|
||||
void SetHeater(size_t heater, float power); // power is a fraction in [0,1]
|
||||
float HeatSampleTime() const;
|
||||
void SetHeatSampleTime(float st);
|
||||
void SetPidParameters(size_t heater, const PidParameters& params);
|
||||
const PidParameters& GetPidParameters(size_t heater) const;
|
||||
float TimeToHot() const;
|
||||
void SetTimeToHot(float t);
|
||||
void SetThermistorNumber(size_t heater, size_t thermistor);
|
||||
int GetThermistorNumber(size_t heater) const;
|
||||
bool IsThermistorChannel(uint8_t heater) const;
|
||||
|
@ -646,7 +645,7 @@ public:
|
|||
bool Inkjet(int bitPattern);
|
||||
|
||||
// Direct pin operations
|
||||
bool SetPin(int pin, int level);
|
||||
bool SetPin(int pin, float level);
|
||||
|
||||
// MCU temperature
|
||||
void GetMcuTemperatures(float& minT, float& currT, float& maxT) const;
|
||||
|
@ -791,7 +790,6 @@ private:
|
|||
// Heaters - bed is assumed to be the first
|
||||
|
||||
int GetRawThermistorTemperature(size_t heater) const;
|
||||
void SetHeaterPwm(size_t heater, uint8_t pwm);
|
||||
|
||||
Pin tempSensePins[HEATERS];
|
||||
Pin heatOnPins[HEATERS];
|
||||
|
@ -799,7 +797,6 @@ private:
|
|||
Pin spiTempSenseCsPins[MaxSpiTempSensors];
|
||||
uint32_t configuredHeaters; // bitmask of all heaters in use
|
||||
float heatSampleTime;
|
||||
float timeToHot;
|
||||
float temperatureLimit;
|
||||
|
||||
// Fans
|
||||
|
@ -1192,16 +1189,6 @@ inline void Platform::SetHeatSampleTime(float st)
|
|||
heatSampleTime = st;
|
||||
}
|
||||
|
||||
inline float Platform::TimeToHot() const
|
||||
{
|
||||
return timeToHot;
|
||||
}
|
||||
|
||||
inline void Platform::SetTimeToHot(float t)
|
||||
{
|
||||
timeToHot = t;
|
||||
}
|
||||
|
||||
inline bool Platform::IsThermistorChannel(uint8_t heater) const
|
||||
{
|
||||
return heaterTempChannels[heater] < HEATERS;
|
||||
|
@ -1245,14 +1232,14 @@ inline float Platform::GetElasticComp(size_t extruder) const
|
|||
}
|
||||
|
||||
inline void Platform::SetEndStopConfiguration(size_t axis, EndStopType esType, bool logicLevel)
|
||||
//pre(axis < AXES)
|
||||
pre(axis < AXES)
|
||||
{
|
||||
endStopType[axis] = esType;
|
||||
endStopLogicLevel[axis] = logicLevel;
|
||||
}
|
||||
|
||||
inline void Platform::GetEndStopConfiguration(size_t axis, EndStopType& esType, bool& logicLevel) const
|
||||
//pre(axis < AXES)
|
||||
pre(axis < AXES)
|
||||
{
|
||||
esType = endStopType[axis];
|
||||
logicLevel = endStopLogicLevel[axis];
|
||||
|
|
|
@ -655,8 +655,10 @@ bool PrintMonitor::GetFileInfoResponse(const char *filename, OutputBuffer *&resp
|
|||
ch = ',';
|
||||
}
|
||||
}
|
||||
response->catf("],\"generatedBy\":\"%s\",\"printDuration\":%d,\"fileName\":\"%s\"}",
|
||||
printingFileInfo.generatedBy, (int)GetPrintDuration(), filenameBeingPrinted);
|
||||
response->cat("],\"generatedBy\":");
|
||||
response->EncodeString(printingFileInfo.generatedBy, ARRAY_SIZE(printingFileInfo.generatedBy), false);
|
||||
response->catf(",\"printDuration\":%d,\"fileName\":", (int)GetPrintDuration());
|
||||
response->EncodeString(filenameBeingPrinted, ARRAY_SIZE(filenameBeingPrinted), false);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -173,7 +173,8 @@ const char *moduleName[] =
|
|||
"DDA",
|
||||
"Roland",
|
||||
"PrintMonitor",
|
||||
"?","?","?","?","?","?",
|
||||
"Storage",
|
||||
"?","?","?","?","?",
|
||||
"none"
|
||||
};
|
||||
|
||||
|
|
|
@ -42,7 +42,8 @@ enum Module : uint8_t
|
|||
moduleDda = 6,
|
||||
moduleRoland = 7,
|
||||
modulePrintMonitor = 8,
|
||||
numModules = 9, // make this one greater than the last module number
|
||||
moduleStorage = 9,
|
||||
numModules = 10, // make this one greater than the last module number
|
||||
noModule = 15
|
||||
};
|
||||
|
||||
|
|
|
@ -1044,7 +1044,7 @@ OutputBuffer *RepRap::GetLegacyStatusResponse(uint8_t type, int seq)
|
|||
}
|
||||
response->cat((ch == '[') ? "[]" : "]");
|
||||
|
||||
// Send the heater statuses (0=off, 1=standby, 2=active)
|
||||
// Send the heater statuses (0=off, 1=standby, 2=active, 3 = fault)
|
||||
response->cat(",\"hstat\":");
|
||||
if (bedHeater != -1)
|
||||
{
|
||||
|
|
|
@ -20,7 +20,7 @@ class StringRef
|
|||
size_t len; // number of characters in the storage
|
||||
|
||||
public:
|
||||
StringRef(char *pp, size_t pl) : p(pp), len(pl) { }
|
||||
StringRef(char *pp, size_t pl) : p(pp), len(pl) { p[0] = 0; }
|
||||
|
||||
size_t Length() const { return len; }
|
||||
size_t strlen() const;
|
||||
|
|
|
@ -22,28 +22,9 @@ const char* TemperatureErrorString(TemperatureError err)
|
|||
case TemperatureError::hardwareError: return "hardware error";
|
||||
case TemperatureError::busBusy: return "bus busy";
|
||||
case TemperatureError::badResponse: return "bad response";
|
||||
case TemperatureError::unknownChannel: return "unknown temperature sensor channel";
|
||||
default: return "unknown temperature sense error";
|
||||
}
|
||||
}
|
||||
|
||||
// Indicate if a temp sensor error is a "permanent" error: whether it is an error condition which will not rectify over time
|
||||
// and so the heater should just be shut off immediately.
|
||||
bool IsPermanentError(TemperatureError err)
|
||||
{
|
||||
#if 1
|
||||
return false;
|
||||
#else
|
||||
switch (err)
|
||||
{
|
||||
case TemperatureError::success:
|
||||
case TemperatureError::busBusy:
|
||||
case TemperatureError::ioError:
|
||||
case TemperatureError::hardwareError: // need this one because it comes up sometimes (Jimustanguitar on seemecnc forum)
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// End
|
||||
|
|
|
@ -23,13 +23,10 @@ enum class TemperatureError : uint8_t
|
|||
ioError,
|
||||
hardwareError,
|
||||
busBusy,
|
||||
badResponse
|
||||
badResponse,
|
||||
unknownChannel
|
||||
};
|
||||
|
||||
const char* TemperatureErrorString(TemperatureError err);
|
||||
|
||||
// Indicate if a temp sensor error is a "permanent" error: whether it is an error condition which will not rectify over time
|
||||
// and so the heater should just be shut off immediately.
|
||||
bool IsPermanentError(TemperatureError err);
|
||||
|
||||
#endif /* TEMPERATUREERROR_H_ */
|
||||
|
|
Reference in a new issue