diff --git a/src/Configuration.h b/src/Configuration.h index b0368d4..8ef6a32 100644 --- a/src/Configuration.h +++ b/src/Configuration.h @@ -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 diff --git a/src/DDA.cpp b/src/DDA.cpp index faa68a4..703650c 100644 --- a/src/DDA.cpp +++ b/src/DDA.cpp @@ -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; diff --git a/src/DriveMovement.cpp b/src/DriveMovement.cpp index 379b8af..298fc73 100644 --- a/src/DriveMovement.cpp +++ b/src/DriveMovement.cpp @@ -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 diff --git a/src/Duet/Pins_Duet.h b/src/Duet/Pins_Duet.h index 46ed7e8..caa5b38 100644 --- a/src/Duet/Pins_Duet.h +++ b/src/Duet/Pins_Duet.h @@ -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 diff --git a/src/DuetNG/Network.cpp b/src/DuetNG/Network.cpp index 8355058..7f9495c 100644 --- a/src/DuetNG/Network.cpp +++ b/src/DuetNG/Network.cpp @@ -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) { diff --git a/src/DuetNG/Pins_DuetNG.h b/src/DuetNG/Pins_DuetNG.h index 917d4a6..1caefc0 100644 --- a/src/DuetNG/Pins_DuetNG.h +++ b/src/DuetNG/Pins_DuetNG.h @@ -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 diff --git a/src/DuetNG/TMC2660.cpp b/src/DuetNG/TMC2660.cpp index 53f06a9..7962e87 100644 --- a/src/DuetNG/TMC2660.cpp +++ b/src/DuetNG/TMC2660.cpp @@ -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(constrain(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 diff --git a/src/DuetNG/TransactionBuffer.h b/src/DuetNG/TransactionBuffer.h index 3d0821b..f27f9ac 100644 --- a/src/DuetNG/TransactionBuffer.h +++ b/src/DuetNG/TransactionBuffer.h @@ -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) ; }; diff --git a/src/DuetNG/WifiFirmwareUploader.cpp b/src/DuetNG/WifiFirmwareUploader.cpp index 8bc740d..23cd54e 100644 --- a/src/DuetNG/WifiFirmwareUploader.cpp +++ b/src/DuetNG/WifiFirmwareUploader.cpp @@ -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(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(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; diff --git a/src/Fan.cpp b/src/Fan.cpp index c70385b..4e66b71 100644 --- a/src/Fan.cpp +++ b/src/Fan.cpp @@ -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); } } diff --git a/src/FileStore.cpp b/src/FileStore.cpp index be3ca3d..8fce085 100644 --- a/src/FileStore.cpp +++ b/src/FileStore.cpp @@ -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) diff --git a/src/FileStore.h b/src/FileStore.h index 470c9c2..7f84147 100644 --- a/src/FileStore.h +++ b/src/FileStore.h @@ -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 diff --git a/src/GCodes.cpp b/src/GCodes.cpp index 7c4e399..bb3abc7 100644 --- a/src/GCodes.cpp +++ b/src/GCodes.cpp @@ -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 diff --git a/src/Heat.cpp b/src/Heat.cpp index b4060b5..d683780 100644 --- a/src/Heat.cpp +++ b/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(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(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 diff --git a/src/Heat.h b/src/Heat.h index 30c58ba..3cf906e 100644 --- a/src/Heat.h +++ b/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; diff --git a/src/Libraries/Fatfs/conf_fatfs.h b/src/Libraries/Fatfs/conf_fatfs.h index 94c30c5..ec780a0 100644 --- a/src/Libraries/Fatfs/conf_fatfs.h +++ b/src/Libraries/Fatfs/conf_fatfs.h @@ -171,7 +171,7 @@ / Physical Drive Configurations /----------------------------------------------------------------------------*/ -#define _VOLUMES 8 +#define _VOLUMES 2 /* Number of volumes (logical drives) to be used. */ diff --git a/src/Libraries/Fatfs/diskio.c b/src/Libraries/Fatfs/diskio.c index e302eed..744511e 100644 --- a/src/Libraries/Fatfs/diskio.c +++ b/src/Libraries/Fatfs/diskio.c @@ -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; diff --git a/src/MassStorage.cpp b/src/MassStorage.cpp index c6354df..900fab9 100644 --- a/src/MassStorage.cpp +++ b/src/MassStorage.cpp @@ -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 diff --git a/src/MassStorage.h b/src/MassStorage.h index 06972a4..8b36881 100644 --- a/src/MassStorage.h +++ b/src/MassStorage.h @@ -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]; }; diff --git a/src/Matrix.h b/src/Matrix.h index d88783a..6fec706 100644 --- a/src/Matrix.h +++ b/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); } diff --git a/src/Move.cpp b/src/Move.cpp index 270fbad..5e6bc25 100644 --- a/src/Move.cpp +++ b/src/Move.cpp @@ -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; } diff --git a/src/Move.h b/src/Move.h index 923b87c..9293888 100644 --- a/src/Move.h +++ b/src/Move.h @@ -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); diff --git a/src/OutputMemory.cpp b/src/OutputMemory.cpp index e6155bd..1c0aebf 100644 --- a/src/OutputMemory.cpp +++ b/src/OutputMemory.cpp @@ -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; diff --git a/src/Pid.cpp b/src/Pid.cpp new file mode 100644 index 0000000..b4124b0 --- /dev/null +++ b/src/Pid.cpp @@ -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(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(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 diff --git a/src/Pid.h b/src/Pid.h new file mode 100644 index 0000000..9ad2112 --- /dev/null +++ b/src/Pid.h @@ -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_ */ diff --git a/src/Platform.cpp b/src/Platform.cpp index 0f2263f..d2724eb 100644 --- a/src/Platform.cpp +++ b/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 @@ -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(1.0, max(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(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(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); } } diff --git a/src/Platform.h b/src/Platform.h index d068b3d..ea3b709 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -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]; diff --git a/src/PrintMonitor.cpp b/src/PrintMonitor.cpp index 79cedc0..65f68c0 100644 --- a/src/PrintMonitor.cpp +++ b/src/PrintMonitor.cpp @@ -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 { diff --git a/src/RepRapFirmware.cpp b/src/RepRapFirmware.cpp index e9017f5..043b81f 100644 --- a/src/RepRapFirmware.cpp +++ b/src/RepRapFirmware.cpp @@ -173,7 +173,8 @@ const char *moduleName[] = "DDA", "Roland", "PrintMonitor", - "?","?","?","?","?","?", + "Storage", + "?","?","?","?","?", "none" }; diff --git a/src/RepRapFirmware.h b/src/RepRapFirmware.h index 414c9d3..192d7d5 100644 --- a/src/RepRapFirmware.h +++ b/src/RepRapFirmware.h @@ -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 }; diff --git a/src/Reprap.cpp b/src/Reprap.cpp index 615d7ae..f903bc2 100644 --- a/src/Reprap.cpp +++ b/src/Reprap.cpp @@ -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) { diff --git a/src/StringRef.h b/src/StringRef.h index dd9ece2..9806ed7 100644 --- a/src/StringRef.h +++ b/src/StringRef.h @@ -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; diff --git a/src/TemperatureError.cpp b/src/TemperatureError.cpp index 115d284..0d13983 100644 --- a/src/TemperatureError.cpp +++ b/src/TemperatureError.cpp @@ -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 diff --git a/src/TemperatureError.h b/src/TemperatureError.h index 47caf48..c9f3a09 100644 --- a/src/TemperatureError.h +++ b/src/TemperatureError.h @@ -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_ */