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:
David Crocker 2016-08-10 15:24:14 +01:00
parent 868f60589b
commit 4bca2a787b
34 changed files with 923 additions and 743 deletions

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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)
{

View file

@ -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

View file

@ -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

View file

@ -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)
;
};

View file

@ -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;

View file

@ -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);
}
}

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -171,7 +171,7 @@
/ Physical Drive Configurations
/----------------------------------------------------------------------------*/
#define _VOLUMES 8
#define _VOLUMES 2
/* Number of volumes (logical drives) to be used. */

View file

@ -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;

View file

@ -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

View file

@ -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];
};

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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);

View file

@ -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
View 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
View 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_ */

View file

@ -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);
}
}

View file

@ -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];

View file

@ -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
{

View file

@ -173,7 +173,8 @@ const char *moduleName[] =
"DDA",
"Roland",
"PrintMonitor",
"?","?","?","?","?","?",
"Storage",
"?","?","?","?","?",
"none"
};

View file

@ -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
};

View file

@ -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)
{

View file

@ -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;

View file

@ -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

View file

@ -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_ */