Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/Duet3D/RepRapFirmware.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDavid Crocker <dcrocker@eschertech.com>2016-04-24 13:44:32 +0300
committerDavid Crocker <dcrocker@eschertech.com>2016-04-24 13:44:32 +0300
commit1a0ce16128371645ff8fca34630e791f884e330e (patch)
treec403c2f508f45025e4bd74294ee6cc50da0e9242 /src
parent3236714418b7c83a388de3a81c4326cb3814a59c (diff)
Added support for PT100 and other RTDs
Added support for RTDs using the MAX31865 interface chip
Diffstat (limited to 'src')
-rw-r--r--src/Configuration.h13
-rw-r--r--src/Duet/Pins_duet.h15
-rw-r--r--src/ExternalDrivers.cpp57
-rw-r--r--src/GCodeBuffer.cpp8
-rw-r--r--src/GCodes.cpp87
-rw-r--r--src/Heat.cpp127
-rw-r--r--src/Heat.h99
-rw-r--r--src/Libraries/MAX31855/MAX31855.cpp211
-rw-r--r--src/Libraries/MAX31855/MAX31855.h30
-rw-r--r--src/Libraries/TemperatureSensor/TemperatureSensor.cpp384
-rw-r--r--src/Libraries/TemperatureSensor/TemperatureSensor.h27
-rw-r--r--src/Platform.cpp92
-rw-r--r--src/Platform.h48
-rw-r--r--src/Reprap.cpp22
-rw-r--r--src/TemperatureError.cpp44
-rw-r--r--src/TemperatureError.h35
16 files changed, 791 insertions, 508 deletions
diff --git a/src/Configuration.h b/src/Configuration.h
index 25e2abab..68721557 100644
--- a/src/Configuration.h
+++ b/src/Configuration.h
@@ -30,17 +30,13 @@ Licence: GPL
#endif
#ifndef DATE
-# define DATE "2016-04-21"
+# define DATE "2016-04-24"
#endif
#define AUTHORS "reprappro, dc42, zpl, t3p3, dnewman"
#define FLASH_SAVE_ENABLED (1)
-// If enabled, the following control the use of the optional ExternalDrivers module
-//#define EXTERNAL_DRIVERS (1)
-//#define FIRST_EXTERNAL_DRIVE (5)
-
// Other firmware that we might switch to be compatible with.
enum Compatibility
@@ -87,9 +83,12 @@ 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
-const float DEFAULT_TEMPERATURE_LIMIT = 300.0; // Celsius
+const float DEFAULT_TEMPERATURE_LIMIT = 260.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 DEFAULT_TEMPERATURE_LIMIT
+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
+const unsigned int FirstRtdChannel = 200; // Temperature sensor channels 200... are RTDs
// PWM frequencies
diff --git a/src/Duet/Pins_duet.h b/src/Duet/Pins_duet.h
index 8eefabd4..b6f21512 100644
--- a/src/Duet/Pins_duet.h
+++ b/src/Duet/Pins_duet.h
@@ -17,6 +17,10 @@
const size_t DRIVES = 9; // The number of drives in the machine, including X, Y, and Z plus extruder drives
#define DRIVES_(a,b,c,d,e,f,g,h,i) { a,b,c,d,e,f,g,h,i }
+// If enabled, the following control the use of the optional ExternalDrivers module
+//#define EXTERNAL_DRIVERS (1)
+//#define FIRST_EXTERNAL_DRIVE (5)
+
const int8_t HEATERS = 7; // The number of heaters in the machine; 0 is the heated bed even if there isn't one
#define HEATERS_(a,b,c,d,e,f,g) { a,b,c,d,e,f,g }
@@ -66,23 +70,22 @@ const float EXT_BETA = 4138.0;
// Thermistor series resistor value in Ohms
const float THERMISTOR_SERIES_RS = 1000.0;
-// Number of MAX31855 chips to support
-const size_t MAX31855_START_CHANNEL = 100;
+// Number of SPI temperature sensors to support
#if SUPPORT_ROLAND
// chrishamm's pin assignments
-const size_t MAX31855_DEVICES = 2;
+const size_t MaxSpiTempSensors = 2;
// Digital pins the 31855s have their select lines tied to
-const Pin MAX31855_CS_PINS[MAX31855_DEVICES] = { 77, 87 };
+const Pin SpiTempSensorCsPins[MaxSpiTempSensors] = { 77, 87 };
#else
-const size_t MAX31855_DEVICES = 4;
+const size_t MaxSpiTempSensors = 4;
// Digital pins the 31855s have their select lines tied to
-const Pin MAX31855_CS_PINS[MAX31855_DEVICES] = { 77, 87, 16, 17 };
+const Pin SpiTempSensorCsPins[MaxSpiTempSensors] = { 77, 87, 16, 17 };
#endif
diff --git a/src/ExternalDrivers.cpp b/src/ExternalDrivers.cpp
index 69d8656b..a8a6c92c 100644
--- a/src/ExternalDrivers.cpp
+++ b/src/ExternalDrivers.cpp
@@ -26,12 +26,12 @@ const size_t NumExternalDrivers = DRIVES - FIRST_EXTERNAL_DRIVE;
// CLK 23 connect to ground 2 (GND
// 5V_USB 5 +3.3V 3 (+3.3V)
-// Connections between DuetNG 0.6 and TMC2660-EVAL board:
+// Connections between DuetNG 0.6 and TMC2660-EVAL board (now using USART0):
// Driver signal name Eval board pin Our signal name DuetNG 0.6 expansion connector pin #
-// SDI 29 SPI1_MOSI 29 (SPI1_MOSI)
-// SDO 28 SPI1_MISO 30 (SPI1_MISO)
-// SCK 27 SPI1_SCLK 28 (SPI1_SCLK)
+// SDI 29 SPI1_MOSI 13 (SPI0_MOSI) was 29
+// SDO 28 SPI1_MISO 14 (SPI0_MISO) was 30
+// SCK 27 SPI1_SCLK 12 (SPI0_SCLK) was 28
// /CS 24 /CS 24 (E2_EN)
// GND 2,3,43,44 GND 2 (GND)
// INT_STEP 19 E2_STEP 19 (E2_STEP)
@@ -41,18 +41,32 @@ const size_t NumExternalDrivers = DRIVES - FIRST_EXTERNAL_DRIVE;
// 5V_USB 5 +3.3V 3 (+3.3V)
#ifdef DUET_NG
+# if 1
+// Pin assignments for the first prototype, using USART0 SPI
+const Pin DriversMosiPin = 27; // PB1
+const Pin DriversMisoPin = 26; // PB0
+const Pin DriversSclkPin = 30; // PB13
+# define USART_EXT_DRV USART0
+# define ID_USART_EXT_DRV ID_USART0
+# else
+// Pin assignments for the second prototype, using USART1 SPI
const Pin DriversMosiPin = 22; // PA13
const Pin DriversMisoPin = 21; // PA22
const Pin DriversSclkPin = 23; // PA23
+# define USART_EXT_DRV USART1
+# define ID_USART_EXT_DRV ID_USART1
+# endif
const Pin DriverSelectPins[NumExternalDrivers] = {87, 88, 89, 90};
#else
const Pin DriversMosiPin = 16; // PA13
const Pin DriversMisoPin = 17; // PA12
const Pin DriversSclkPin = 54; // PA16
const Pin DriverSelectPins[NumExternalDrivers] = {37, X8, 50, 47 /*, X13*/ };
+# define USART_EXT_DRV USART1
+# define ID_USART_EXT_DRV ID_USART1
#endif
-const uint32_t DriversSpiClockFrequency = 1000000; // 1MHz SPI clock
+const uint32_t DriversSpiClockFrequency = 1000000; // 1MHz SPI clock for now
// TMC2660 registers
const uint32_t TMC_REG_DRVCTRL = 0;
@@ -176,25 +190,25 @@ const uint32_t defaultSmartEnReg =
// 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.
uint32_t SpiSendWord(uint32_t pin, uint32_t dataOut)
{
- USART1->US_CR = US_CR_RSTRX | US_CR_RSTTX; // reset transmitter and receiver
+ USART_EXT_DRV->US_CR = US_CR_RSTRX | US_CR_RSTTX; // reset transmitter and receiver
digitalWrite(pin, LOW); // set CS low
delayMicroseconds(1); // allow some CS low setup time
- USART1->US_CR = US_CR_RXEN | US_CR_TXEN; // enable transmitter and receiver
+ USART_EXT_DRV->US_CR = US_CR_RXEN | US_CR_TXEN; // enable transmitter and receiver
uint32_t dataIn = 0;
for (int i = 0; i < 3; ++i)
{
- USART1->US_THR = (dataOut >> 16) & 0x000000FFu;
+ USART_EXT_DRV->US_THR = (dataOut >> 16) & 0x000000FFu;
dataOut <<= 8;
dataIn <<= 8;
- for (int j = 0; j < 10000 && (USART1->US_CSR & (US_CSR_RXRDY | US_CSR_TXRDY)) != (US_CSR_RXRDY | US_CSR_TXRDY); ++j)
+ for (int j = 0; j < 10000 && (USART_EXT_DRV->US_CSR & (US_CSR_RXRDY | US_CSR_TXRDY)) != (US_CSR_RXRDY | US_CSR_TXRDY); ++j)
{
// nothing
}
- dataIn |= USART1->US_RHR & 0x000000FF;
+ dataIn |= USART_EXT_DRV->US_RHR & 0x000000FF;
}
delayMicroseconds(1);
digitalWrite(pin, HIGH); // set CS high again
- USART1->US_CR = US_CR_RSTRX | US_CR_RSTTX | US_CR_RXDIS | US_CR_TXDIS; // reset and disable transmitter and receiver
+ 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) & 0x000FFFFF;
}
@@ -315,10 +329,6 @@ namespace ExternalDrivers
void Init()
{
// Set up the SPI pins
-#ifndef DUET_NG
- // 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);
-#endif
#ifdef DUET_NG
// The pins are already set up for SPI in the pins table
@@ -326,6 +336,9 @@ namespace ExternalDrivers
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
+ ADC->ADC_CHDR = (1 << 7);
+
const PinDescription& pin2 = GetPinDescription(DriversMosiPin);
pio_configure(pin2.pPort, PIO_PERIPH_A, pin2.ulPin, PIO_DEFAULT);
const PinDescription& pin3 = GetPinDescription(DriversMisoPin);
@@ -335,7 +348,7 @@ namespace ExternalDrivers
#endif
// Enable the clock to UART1
- pmc_enable_periph_clk(ID_USART1);
+ pmc_enable_periph_clk(ID_USART_EXT_DRV);
// Set up the CS pins and set them all high
// When this becomes the standard code, we must set up the STEP and DIR pins here too.
@@ -345,17 +358,17 @@ namespace ExternalDrivers
pinMode(DriverSelectPins[drive], OUTPUT);
}
- // Set USART1 in SPI mode, with data changing on the falling edge of the clock and captured on the rising edge
- USART1->US_IDR = ~0u;
- USART1->US_CR = US_CR_RSTRX | US_CR_RSTTX | US_CR_RXDIS | US_CR_TXDIS;
- USART1->US_MR = US_MR_USART_MODE_SPI_MASTER
+ // Set USART_EXT_DRV in SPI mode, with data changing on the falling edge of the clock and captured on the rising edge
+ USART_EXT_DRV->US_IDR = ~0u;
+ USART_EXT_DRV->US_CR = US_CR_RSTRX | US_CR_RSTTX | US_CR_RXDIS | US_CR_TXDIS;
+ USART_EXT_DRV->US_MR = US_MR_USART_MODE_SPI_MASTER
| US_MR_USCLKS_MCK
| US_MR_CHRL_8_BIT
| US_MR_CHMODE_NORMAL
| US_MR_CPOL
| US_MR_CLKO;
- USART1->US_BRGR = VARIANT_MCK/DriversSpiClockFrequency; // 1MHz SPI clock
- USART1->US_CR = US_CR_RSTRX | US_CR_RSTTX | US_CR_RXDIS | US_CR_TXDIS | US_CR_RSTSTA;
+ USART_EXT_DRV->US_BRGR = VARIANT_MCK/DriversSpiClockFrequency; // 1MHz SPI clock
+ USART_EXT_DRV->US_CR = US_CR_RSTRX | US_CR_RSTTX | US_CR_RXDIS | US_CR_TXDIS | US_CR_RSTSTA;
// We need a few microseconds of delay here foe the USART to sort itself out,
// otherwise the processor generates two short reset pulses on its own NRST pin, and resets itself.
diff --git a/src/GCodeBuffer.cpp b/src/GCodeBuffer.cpp
index 1beaaa0e..724b779b 100644
--- a/src/GCodeBuffer.cpp
+++ b/src/GCodeBuffer.cpp
@@ -319,6 +319,7 @@ const char* GCodeBuffer::GetString()
// preference use GetString() which requires the string to have
// been preceded by a tag letter.
+// If no string was provided, it produces an error message if the string was not optional, and returns nullptr.
const char* GCodeBuffer::GetUnprecedentedString(bool optional)
{
readPointer = 0;
@@ -330,12 +331,11 @@ const char* GCodeBuffer::GetUnprecedentedString(bool optional)
if (gcodeBuffer[readPointer] == 0)
{
readPointer = -1;
- if (optional)
+ if (!optional)
{
- return nullptr;
+ platform->Message(GENERIC_MESSAGE, "Error: GCodes: String expected but not seen.\n");
}
- platform->Message(GENERIC_MESSAGE, "Error: GCodes: String expected but not seen.\n");
- return gcodeBuffer; // Good idea?
+ return nullptr;
}
const char* result = &gcodeBuffer[readPointer + 1];
diff --git a/src/GCodes.cpp b/src/GCodes.cpp
index b6140b28..e1a04c24 100644
--- a/src/GCodes.cpp
+++ b/src/GCodes.cpp
@@ -2296,7 +2296,8 @@ void GCodes::SetHeaterParameters(GCodeBuffer *gb, StringRef& reply)
{
int thermistor = gb->GetIValue();
if ( (0 <= thermistor && thermistor < HEATERS)
- || ((int)MAX31855_START_CHANNEL <= thermistor && thermistor < (int)(MAX31855_START_CHANNEL + MAX31855_DEVICES))
+ || ((int)FirstThermocoupleChannel <= thermistor && thermistor < (int)(FirstThermocoupleChannel + MaxSpiTempSensors))
+ || ((int)FirstRtdChannel <= thermistor && thermistor < (int)(FirstRtdChannel + MaxSpiTempSensors))
)
{
platform->SetThermistorNumber(heater, thermistor);
@@ -2775,30 +2776,33 @@ bool GCodes::HandleMcode(GCodeBuffer* gb, StringRef& reply)
{
const char* filename = gb->GetUnprecedentedString();
- QueueFileToPrint(filename);
- if (fileToPrint.IsLive())
+ if (filename != nullptr)
{
- reprap.GetPrintMonitor()->StartingPrint(filename);
- if (platform->Emulating() == marlin && gb == serialGCode)
+ QueueFileToPrint(filename);
+ if (fileToPrint.IsLive())
{
- reply.copy("File opened\nFile selected");
+ reprap.GetPrintMonitor()->StartingPrint(filename);
+ if (platform->Emulating() == marlin && gb == serialGCode)
+ {
+ reply.copy("File opened\nFile selected");
+ }
+ else
+ {
+ // Command came from web interface or PanelDue, or not emulating Marlin, so send a nicer response
+ reply.printf("File %s selected for printing", filename);
+ }
+
+ if (code == 32)
+ {
+ fileBeingPrinted.MoveFrom(fileToPrint);
+ reprap.GetPrintMonitor()->StartedPrint();
+ }
}
else
{
- // Command came from web interface or PanelDue, or not emulating Marlin, so send a nicer response
- reply.printf("File %s selected for printing", filename);
- }
-
- if (code == 32)
- {
- fileBeingPrinted.MoveFrom(fileToPrint);
- reprap.GetPrintMonitor()->StartedPrint();
+ reply.printf("Failed to open file %s", filename);
}
}
- else
- {
- reply.printf("Failed to open file %s", filename);
- }
}
break;
@@ -2956,15 +2960,18 @@ bool GCodes::HandleMcode(GCodeBuffer* gb, StringRef& reply)
case 28: // Write to file
{
const char* str = gb->GetUnprecedentedString();
- bool ok = OpenFileToWrite(platform->GetGCodeDir(), str, gb);
- if (ok)
- {
- reply.printf("Writing to file: %s", str);
- }
- else
+ if (str != nullptr)
{
- reply.printf("Can't open file %s for writing.", str);
- error = true;
+ bool ok = OpenFileToWrite(platform->GetGCodeDir(), str, gb);
+ if (ok)
+ {
+ reply.printf("Writing to file: %s", str);
+ }
+ else
+ {
+ reply.printf("Can't open file %s for writing.", str);
+ error = true;
+ }
}
}
break;
@@ -2974,7 +2981,13 @@ bool GCodes::HandleMcode(GCodeBuffer* gb, StringRef& reply)
break;
case 30: // Delete file
- DeleteFile(gb->GetUnprecedentedString());
+ {
+ const char *filename = gb->GetUnprecedentedString();
+ if (filename != nullptr)
+ {
+ DeleteFile(filename);
+ }
+ }
break;
// For case 32, see case 24
@@ -3445,7 +3458,13 @@ bool GCodes::HandleMcode(GCodeBuffer* gb, StringRef& reply)
break;
case 117: // Display message
- reprap.SetMessage(gb->GetUnprecedentedString());
+ {
+ const char *msg = gb->GetUnprecedentedString();
+ if (msg != nullptr)
+ {
+ reprap.SetMessage(msg);
+ }
+ }
break;
case 119:
@@ -4434,8 +4453,16 @@ bool GCodes::HandleMcode(GCodeBuffer* gb, StringRef& reply)
case 562: // Reset temperature fault - use with great caution
if (gb->Seen('P'))
{
- int iValue = gb->GetIValue();
- reprap.GetHeat()->ResetFault(iValue);
+ int heater = gb->GetIValue();
+ if (heater >= 0 && heater < HEATERS)
+ {
+ reprap.ClearTemperatureFault(heater);
+ }
+ else
+ {
+ reply.copy("Invalid heater number.\n");
+ error = true;
+ }
}
break;
diff --git a/src/Heat.cpp b/src/Heat.cpp
index ec04e878..a33fcde5 100644
--- a/src/Heat.cpp
+++ b/src/Heat.cpp
@@ -110,6 +110,105 @@ bool Heat::HeaterAtSetTemperature(int8_t heater) const
return (target < TEMPERATURE_LOW_SO_DONT_CARE) || (fabs(dt - target) <= TEMPERATURE_CLOSE_ENOUGH);
}
+Heat::HeaterStatus Heat::GetStatus(int8_t heater) const
+{
+ if (heater < 0 || heater >= HEATERS)
+ {
+ return 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);
+ }
+}
+
+float Heat::GetActiveTemperature(int8_t heater) const
+{
+ return (heater >= 0 && heater < HEATERS) ? pids[heater]->GetActiveTemperature() : ABS_ZERO;
+}
+
+void Heat::SetStandbyTemperature(int8_t heater, float 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;
+}
+
+float Heat::GetTemperature(int8_t heater) const
+{
+ return (heater >= 0 && heater < HEATERS) ? pids[heater]->GetTemperature() : ABS_ZERO;
+}
+
+void Heat::Activate(int8_t heater)
+{
+ if (heater >= 0 && heater < HEATERS)
+ {
+ pids[heater]->Activate();
+ }
+}
+
+void Heat::SwitchOff(int8_t heater)
+{
+ if (heater >= 0 && heater < HEATERS)
+ {
+ pids[heater]->SwitchOff();
+ }
+}
+
+void Heat::SwitchOffAll()
+{
+ for (size_t heater = 0; heater < HEATERS; ++heater)
+ {
+ pids[heater]->SwitchOff();
+ }
+}
+
+void Heat::Standby(int8_t heater)
+{
+ if (heater >= 0 && heater < HEATERS)
+ {
+ pids[heater]->Standby();
+ }
+}
+
+void Heat::ResetFault(int8_t heater)
+{
+ if (heater >= 0 && heater < HEATERS)
+ {
+ pids[heater]->ResetFault();
+ }
+}
+
+float Heat::GetAveragePWM(int8_t heater) const
+{
+ return pids[heater]->GetAveragePWM();
+}
+
+uint32_t Heat::GetLastSampleTime(int8_t heater) const
+{
+ return pids[heater]->GetLastSampleTime();
+}
+
+bool Heat::UseSlowPwm(int8_t heater) const
+{
+ return heater == bedHeater || heater == chamberHeater;
+}
+
//******************************************************************************************************
PID::PID(Platform* p, int8_t h) : platform(p), heater(h)
@@ -163,7 +262,7 @@ void PID::Spin()
// Always know our temperature, regardless of whether we have been switched on or not
- Platform::TempError err = Platform::TempError::errOk; // assume no error
+ 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.
@@ -180,18 +279,22 @@ void PID::Spin()
// We are switched on. Check for faults. Temperature silly-low or silly-high mean open-circuit
// or shorted thermistor respectively.
- if (temperature < BAD_LOW_TEMPERATURE)
+ if (err == TemperatureError::success && temperature < BAD_LOW_TEMPERATURE)
{
- err = Platform::TempError::errOpen;
+ err = TemperatureError::openCircuit;
}
- else if (temperature > platform->GetTemperatureLimit())
+ else if (err == TemperatureError::success && temperature > platform->GetTemperatureLimit())
{
- err = Platform::TempError::errTooHigh;
+ err = TemperatureError::tooHigh;
}
- if (err != Platform::TempError::errOk)
+ if (err == TemperatureError::success)
+ {
+ badTemperatureCount = 0;
+ }
+ else
{
- if (platform->DoThermistorAdc(heater) || !(Platform::TempErrorPermanent(err)))
+ if (platform->IsThermistorChannel(heater) || !IsPermanentError(err))
{
// Error may be a temporary error and may correct itself after a few additional reads
badTemperatureCount++;
@@ -208,18 +311,10 @@ void PID::Spin()
{
SetHeater(0.0);
temperatureFault = true;
- platform->MessageF(GENERIC_MESSAGE, "Temperature fault on heater %d%s%s, T = %.1f\n",
- heater,
- (err != Platform::TempError::errOk) ? ", " : "",
- (err != Platform::TempError::errOk) ? Platform::TempErrorStr(err) : "",
- temperature);
+ platform->MessageF(GENERIC_MESSAGE, "Error: Temperature fault on heater %d: %s\n", heater, TemperatureErrorString(err));
reprap.FlagTemperatureFault(heater);
}
}
- else
- {
- badTemperatureCount = 0;
- }
// 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()) // FIXME - also check bed warmup time?
diff --git a/src/Heat.h b/src/Heat.h
index b2582e01..c68e4523 100644
--- a/src/Heat.h
+++ b/src/Heat.h
@@ -212,103 +212,4 @@ inline void Heat::SetChamberHeater(int8_t heater)
chamberHeater = heater;
}
-inline Heat::HeaterStatus Heat::GetStatus(int8_t heater) const
-{
- if (heater < 0 || heater >= HEATERS)
- {
- return HS_off;
- }
-
- return (pids[heater]->FaultOccurred() ? HS_fault
- : pids[heater]->SwitchedOff()) ? HS_off
- : (pids[heater]->Active()) ? HS_active
- : HS_standby;
-}
-
-inline void Heat::SetActiveTemperature(int8_t heater, float t)
-{
- if (heater >= 0 && heater < HEATERS)
- {
- pids[heater]->SetActiveTemperature(t);
- }
-}
-
-inline float Heat::GetActiveTemperature(int8_t heater) const
-{
- return (heater >= 0 && heater < HEATERS) ? pids[heater]->GetActiveTemperature() : ABS_ZERO;
-}
-
-inline void Heat::SetStandbyTemperature(int8_t heater, float t)
-{
- if (heater >= 0 && heater < HEATERS)
- {
- pids[heater]->SetStandbyTemperature(t);
- }
-}
-
-inline float Heat::GetStandbyTemperature(int8_t heater) const
-{
- return (heater >= 0 && heater < HEATERS) ? pids[heater]->GetStandbyTemperature() : ABS_ZERO;
-}
-
-inline float Heat::GetTemperature(int8_t heater) const
-{
- return (heater >= 0 && heater < HEATERS) ? pids[heater]->GetTemperature() : ABS_ZERO;
-}
-
-inline void Heat::Activate(int8_t heater)
-{
- if (heater >= 0 && heater < HEATERS)
- {
- pids[heater]->Activate();
- }
-}
-
-inline void Heat::SwitchOff(int8_t heater)
-{
- if (heater >= 0 && heater < HEATERS)
- {
- pids[heater]->SwitchOff();
- }
-}
-
-inline void Heat::SwitchOffAll()
-{
- for (size_t heater = 0; heater < HEATERS; ++heater)
- {
- pids[heater]->SwitchOff();
- }
-}
-
-inline void Heat::Standby(int8_t heater)
-{
- if (heater >= 0 && heater < HEATERS)
- {
- pids[heater]->Standby();
- }
-}
-
-inline void Heat::ResetFault(int8_t heater)
-{
- if (heater >= 0 && heater < HEATERS)
- {
- pids[heater]->ResetFault();
- }
-}
-
-inline float Heat::GetAveragePWM(int8_t heater) const
-{
- return pids[heater]->GetAveragePWM();
-}
-
-inline uint32_t Heat::GetLastSampleTime(int8_t heater) const
-{
- return pids[heater]->GetLastSampleTime();
-}
-
-inline bool Heat::UseSlowPwm(int8_t heater) const
-{
- return heater == bedHeater || heater == chamberHeater;
-}
-
#endif
diff --git a/src/Libraries/MAX31855/MAX31855.cpp b/src/Libraries/MAX31855/MAX31855.cpp
deleted file mode 100644
index 7ec6dbd1..00000000
--- a/src/Libraries/MAX31855/MAX31855.cpp
+++ /dev/null
@@ -1,211 +0,0 @@
-#include "MAX31855.h"
-
-// MAX31855 thermocouple chip
-//
-// The MAX31855 continuously samples a Type K thermocouple. When the MAX31855
-// is selected via its chip select (CS) pin, it unconditionally writes a 32 bit
-// sequence onto the bus. This sequence is designed such that we need only
-// interpret the high 16 bits in order to ascertain the temperature reading and
-// whether there a fault condition exists. The low 16 bits provide the
-// MAX31855's cold junction temperature (and thus the board temperature) as
-// well as finer detail on any existing fault condition.
-//
-// The temperature read from the chip is a signed, two's-complement integer.
-// As it is a 14 bit value (in units of one-quarter degrees Celsius), we
-// convert it to a proper signed 16 bit value by adding two high bits. The
-// high bits added should both be zero if the value is positive (highest bit
-// of the 14 bit value is zero), or both be one if the value is negative
-// (highest bit of the 14 bit value is one).
-//
-// Note Bene: there's a Arduino Due sketch floating about the internet which
-// gets this wrong and for negative temperatures generates incorrect values.
-// E.g, -2047C for what should be -1C; -1798C for what should be -250C. The
-// values of -1C and -250C are shown as examples in Table 4 of the datasheet
-// for the MAX21855.) The incorrect Arduino Due sketch appears in, and may be
-// from, the book Arduino Sketches: Tools and Techniques for Programming
-// Wizardry, James A. Langbridge, January 12, 2015, John Wiley & Sons.
-
-// Bits -- Interpretation
-// ----- -----------------------------------------------------------------
-// 31:18 -- 14 bit, signed thermocouple temperature data. Units of 0.25 C
-// 17 -- Reserved
-// 16 -- Fault indicator (1 if fault detected; 0 otherwise)
-// 15:04 -- 12 bit, signed cold junction temperature data. Units of 0.0625 C
-// 03 -- Reserved
-// 02 -- SCV fault; reads 1 if the thermocouple is shorted to Vcc
-// 01 -- SCG fault; reads 1 if the thermocouple is shorted to ground
-// 00 -- OC fault; reads 1 if the thermocouple is not connected (open)
-
-// For purposes of setting bit transfer widths and timings, we need to use a
-// Peripheral Channel Select (PCS). Use channel #3 as it is unlikely to be
-// used by anything else as the Arduino Due leaves pin 78 unconnected.
-//
-// No warranty given or implied, use at your own risk.
-// dan.newman@mtbaldy.us
-// GPL v3
-
-#define PERIPHERAL_CHANNEL_ID 3
-#define PERIPHERAL_CHANNEL_CS_PIN 78 // NPCS3
-
-// 5.0 MHz Max clock frequency when clocking data out of a MAX31855
-#define MAX31855_MAX_FREQ 5000000u
-
-// Perform the actual hardware initialization for attaching and using this
-// device on the SPI hardware bus.
-void MAX31855::Init(uint8_t cs)
-{
- device.csPin = cs;
- pinMode(cs, OUTPUT);
- digitalWrite(cs, HIGH);
-
-#ifdef DUET_NG
- sspi_master_init(&device, 8);
-#else
- device.id = PERIPHERAL_CHANNEL_ID; // Peripheral channel
- sspi_master_init(&device, 16);
-#endif
-}
-
-MAX31855_error MAX31855::getTemperature(float *t) const
-{
- if (!sspi_acquire())
- {
- return MAX31855_GSPI_BUSY;
- }
-
- // Assume properly initialized
- // Ensure that the configuration is as needed; another GSPI consumer
- // may have changed the bus speed and/or timing delays.
- sspi_master_setup_device(&device, SPI_MODE_0, MAX31855_MAX_FREQ);
-
- // Select the device; enable CS (set it LOW)
- sspi_select_device(&device);
- delayMicroseconds(1); // TODO shorten this (MAX31855 needs 100ns minimum)
-
- // Read in 32 bits
-
-#ifdef DUET_NG
- uint8_t dataOut[4] = {0, 0, 0, 0};
- uint8_t rawBytes[4];
- spi_status_t sts = sspi_transceive_packet(dataOut, rawBytes, 4);
- uint16_t raw[2];
- raw[0] = ((uint16_t)rawBytes[0] << 8) | (uint16_t)rawBytes[1];
- raw[1] = ((uint16_t)rawBytes[2] << 8) | (uint16_t)rawBytes[3];
-#else
- uint16_t dataOut[2] = {0, 0};
- uint16_t raw[2];
- spi_status_t sts = sspi_transceive_packet16(dataOut, raw, 2);
-#endif
-
- // Deselect the device; disable CS (set it HIGH)
- sspi_deselect_device(&device);
-
- sspi_release();
-
- if (sts != SPI_OK)
- {
- return MAX31855_ERR_TMO;
- }
-
- if ((raw[0] & 0x02) || (raw[1] & 0x08))
- {
- // These two bits should always read 0
- // Likely the entire read was 0xFF 0xFF which is not uncommon when first powering up
- return MAX31855_ERR_IO;
- }
-
- // The MAX31855 response is designed such that we can look at
- // just the high 16 bits and know the temperature and whether
- // an error occurred. The low 16 bits contain the cold junction
- // temperature and finer detail on the nature of any error.
-
- // We also want to check for three more types of bad reads:
- //
- // 1. A read in which the fault indicator bit (16) is set but the fault reason bits (0:2)
- // are all clear;
- // 2. A read in which the fault indicator bit (16) is clear, but one or more of the fault
- // reason bits (0:2) are set; and,
- // 3. A read in which more than one of the fault reason bits (0:1) are set.
- //
- // We will perform those three sanity checks as we set the error response code.
-
- if ((raw[0] & 0x01) || (raw[1] & 0x07))
- {
- if (!(raw[0] & 0x01))
- {
- // One or more fault reason bits are set but the fault indicator bit is clear?
- return MAX31855_ERR_IO;
- }
-
- // At this point we are assured that bit 16 (fault indicator) is set
- // and that at least one of the fault reason bits (0:2) are set. We
- // now need to ensure that only one fault reason bit is set.
- uint8_t nbits = 0;
- MAX31855_error err;
-
- if (raw[1] & 0x01)
- {
- // Open Circuit
- ++nbits;
- err = MAX31855_ERR_OC;
- }
- if (raw[1] & 0x02)
- {
- // Short to ground;
- ++nbits;
- err = MAX31855_ERR_SCG;
- }
- if (raw[1] && 0x04)
- {
- // Short to Vcc
- ++nbits;
- err = MAX31855_ERR_SCV;
- }
-
- if (nbits == 1)
- {
- // Looks like a legitimate error response: bit 16 was set and only one of bits 0:2 was set
- return err;
- }
-
- // Fault indicator was set but a fault reason was not set (nbits == 0) or too
- // many fault reason bits were set (nbits > 1): assume that a communication error
- // with the MAX31855 has occurred.
- return MAX31855_ERR_IO;
- }
-
- // Note, the reading is negative if raw[0] & 0x8000 is nonzero
-
- // Shift the data
- raw[0] >>= 2;
- int16_t temp = (int16_t)(raw[0] & 0x3fff);
-
- // Handle negative temperatures
- if (raw[0] & 0x2000)
- {
- temp |= 0xc000;
- }
-
- // And convert to from units of 1/4C to 1C
- *t = (float)(0.25 * (float)temp);
-
- // Success!
- return MAX31855_OK;
-}
-
-const char* MAX31855::errorStr(MAX31855_error err) const
-{
- switch (err)
- {
- default : return "unknown MAX31855 error";
- case MAX31855_OK : return "successful temperature read";
- case MAX31855_ERR_SCV : return "thermocouple is shorted to +Vcc";
- case MAX31855_ERR_SCG : return "thermocouple is shorted to ground";
- case MAX31855_ERR_OC : return "thermocouple is broken (open)";
- case MAX31855_ERR_TMO : return "error communicating with MAX31855; read timed out";
- case MAX31855_ERR_IO : return "error communicating with MAX31855; disconnected?";
- case MAX31855_GSPI_BUSY: return "general SPI bus is busy";
- }
-}
-
-// End
diff --git a/src/Libraries/MAX31855/MAX31855.h b/src/Libraries/MAX31855/MAX31855.h
deleted file mode 100644
index a248a476..00000000
--- a/src/Libraries/MAX31855/MAX31855.h
+++ /dev/null
@@ -1,30 +0,0 @@
-#ifndef MAX31855_H
-#define MAX31855_H
-
-#include "Arduino.h"
-#include "SharedSpi.h" // for gspi_device
-
-enum MAX31855_error
-{
- MAX31855_OK = 0, // Success
- MAX31855_ERR_SCV = 1, // Thermocouple is shorted to Vcc
- MAX31855_ERR_SCG = 2, // Thermocouple is shorted to ground
- MAX31855_ERR_OC = 3, // Thermocouple is open
- MAX31855_ERR_TMO = 4, // Timeout waiting on I/O (bus busy)
- MAX31855_ERR_IO = 5, // Chip not sending output? CS not hooked up?
- MAX31855_GSPI_BUSY // General SPI bus is busy
-};
-
-class MAX31855
-{
-public:
- MAX31855() {}
- MAX31855_error getTemperature(float *temp) const;
- void Init(uint8_t cs);
- const char* errorStr(MAX31855_error err) const;
-
-private:
- sspi_device device;
-};
-
-#endif //MAX31855_H
diff --git a/src/Libraries/TemperatureSensor/TemperatureSensor.cpp b/src/Libraries/TemperatureSensor/TemperatureSensor.cpp
new file mode 100644
index 00000000..266ddd33
--- /dev/null
+++ b/src/Libraries/TemperatureSensor/TemperatureSensor.cpp
@@ -0,0 +1,384 @@
+#include "TemperatureSensor.h"
+#include "RepRapFirmware.h"
+
+// MAX31855 thermocouple chip
+//
+// The MAX31855 continuously samples a Type K thermocouple. When the MAX31855
+// is selected via its chip select (CS) pin, it unconditionally writes a 32 bit
+// sequence onto the bus. This sequence is designed such that we need only
+// interpret the high 16 bits in order to ascertain the temperature reading and
+// whether there a fault condition exists. The low 16 bits provide the
+// MAX31855's cold junction temperature (and thus the board temperature) as
+// well as finer detail on any existing fault condition.
+//
+// The temperature read from the chip is a signed, two's-complement integer.
+// As it is a 14 bit value (in units of one-quarter degrees Celsius), we
+// convert it to a proper signed 16 bit value by adding two high bits. The
+// high bits added should both be zero if the value is positive (highest bit
+// of the 14 bit value is zero), or both be one if the value is negative
+// (highest bit of the 14 bit value is one).
+//
+// Note Bene: there's a Arduino Due sketch floating about the internet which
+// gets this wrong and for negative temperatures generates incorrect values.
+// E.g, -2047C for what should be -1C; -1798C for what should be -250C. The
+// values of -1C and -250C are shown as examples in Table 4 of the datasheet
+// for the MAX21855.) The incorrect Arduino Due sketch appears in, and may be
+// from, the book Arduino Sketches: Tools and Techniques for Programming
+// Wizardry, James A. Langbridge, January 12, 2015, John Wiley & Sons.
+
+// Bits -- Interpretation
+// ----- -----------------------------------------------------------------
+// 31:18 -- 14 bit, signed thermocouple temperature data. Units of 0.25 C
+// 17 -- Reserved
+// 16 -- Fault indicator (1 if fault detected; 0 otherwise)
+// 15:04 -- 12 bit, signed cold junction temperature data. Units of 0.0625 C
+// 03 -- Reserved
+// 02 -- SCV fault; reads 1 if the thermocouple is shorted to Vcc
+// 01 -- SCG fault; reads 1 if the thermocouple is shorted to ground
+// 00 -- OC fault; reads 1 if the thermocouple is not connected (open)
+
+// For purposes of setting bit transfer widths and timings, we need to use a
+// Peripheral Channel Select (PCS). Use channel #3 as it is unlikely to be
+// used by anything else as the Arduino Due leaves pin 78 unconnected.
+//
+// No warranty given or implied, use at your own risk.
+// dan.newman@mtbaldy.us
+// GPL v3
+
+#define PERIPHERAL_CHANNEL_ID 3
+#define PERIPHERAL_CHANNEL_CS_PIN 78 // NPCS3
+
+const uint32_t MAX31855_Frequency = 4000000; // maximum for MAX31855 is 5MHz
+const uint32_t MAX31865_Frequency = 4000000; // maximum for MAX31865 is also 5MHz
+
+// SPI modes:
+// If the inactive state of SCL is LOW (CPOL = 0) (in the case of the MAX31865, this is sampled on the falling edge of CS):
+// The MAX31855 sets up the first data bit after the falling edge of CS, and changes the data on each falling clock edge.
+// So the SAM needs to sample data on the rising clock edge. This requires NCPHA = 1.
+// The MAX31865 changes data after the rising edge of CS, and samples input data on the falling edge.
+// This requires NCPHA = 0.
+
+const uint8_t MAX31855_SpiMode = SPI_MODE_0;
+const uint8_t MAX31865_SpiMode = SPI_MODE_1;
+
+// Define the minimum interval between readings. The MAX31865 needs 62.5ms in 50Hz filter mode.
+const uint32_t MinimumReadInterval = 100; // minimum interval between reads, in milliseconds
+
+// Table of temperature vs. MAX31865 result for PT100 thermistor, from the MAX31865 datasheet
+struct TempTableEntry
+{
+ int16_t temperature;
+ uint16_t adcReading;
+};
+
+static const TempTableEntry tempTable[] =
+{
+ {-30, 7227},
+ {-20, 7550},
+ {-10, 7871},
+ {0, 8192},
+ {10, 8512},
+ {20, 8830},
+ {30, 9148},
+ {40, 9465},
+ {50, 9781},
+ {60, 10096},
+ {70, 10410},
+ {80, 10723},
+ {90, 11035},
+ {100, 11346},
+ {110, 11657},
+ {120, 11966},
+ {130, 12274},
+ {140, 12582},
+ {150, 12888},
+ {160, 13194},
+ {170, 13498},
+ {180, 13802},
+ {190, 14104},
+ {200, 14406},
+ {225, 15156},
+ {250, 15901},
+ {275, 16639},
+ {300, 17371},
+ {325, 18098},
+ {350, 18818},
+ {375, 19533},
+ {400, 20242},
+ {425, 20945},
+ {450, 21642},
+ {475, 22333},
+ {500, 23018},
+ {525, 23697},
+ {550, 24370}
+};
+
+const size_t NumTempTableEntries = sizeof(tempTable)/sizeof(tempTable[0]);
+
+// Perform the actual hardware initialization for attaching and using this device on the SPI hardware bus.
+void TemperatureSensor::InitThermocouple(uint8_t cs)
+{
+ device.csPin = cs;
+ device.spiMode = MAX31855_SpiMode;
+ device.clockFrequency = MAX31855_Frequency;
+#ifndef DUET_NG
+ device.id = PERIPHERAL_CHANNEL_ID; // Peripheral channel
+#endif
+ sspi_master_init(&device, 8);
+
+ lastReadingTime = millis();
+ lastResult = TemperatureError::success;
+ lastTemperature = 0.0;
+}
+
+// Perform the actual hardware initialization for attaching and using this device on the SPI hardware bus.
+void TemperatureSensor::InitRtd(uint8_t cs)
+{
+ device.csPin = cs;
+ device.spiMode = MAX31865_SpiMode;
+ device.clockFrequency = MAX31865_Frequency;
+#ifndef DUET_NG
+ device.id = PERIPHERAL_CHANNEL_ID; // Peripheral channel
+#endif
+ sspi_master_init(&device, 8);
+
+ TemperatureError rslt;
+ for (unsigned int i = 0; i < 3; ++i) // try 3 times
+ {
+ rslt = TryInitRtd();
+ if (rslt == TemperatureError::success)
+ {
+ break;
+ }
+ delay(MinimumReadInterval);
+ }
+
+ lastReadingTime = millis();
+ lastResult = rslt;
+ lastTemperature = 0.0;
+
+ if (rslt != TemperatureError::success)
+ {
+ reprap.GetPlatform()->MessageF(GENERIC_MESSAGE, "Error: failed to initialise RTD: %s\n", TemperatureErrorString(rslt));
+ }
+}
+
+// Try to initialise the RTD
+TemperatureError TemperatureSensor::TryInitRtd() const
+{
+ // Note that to get the MAX31865 to do continuous conversions, we need to set the bias bit as well as the continuous-conversion bit
+ static const uint8_t modeData[2] = { 0x80, 0xC3 }; // write register 0, bias on, auto conversion, clear errors, 50Hz
+ uint32_t rawVal;
+ TemperatureError sts = DoSpiTransaction(modeData, 2, rawVal);
+
+ if (sts == TemperatureError::success)
+ {
+ static const uint8_t readData[2] = { 0x00, 0x00 }; // read register 0
+ sts = DoSpiTransaction(readData, 2, rawVal);
+ }
+
+ //debugPrintf("Status %d data %04x\n", (int)sts, rawVal);
+ return (sts == TemperatureError::success && (uint8_t)rawVal != 0xC1)
+ ? TemperatureError::badResponse
+ : sts;
+}
+
+TemperatureError TemperatureSensor::GetThermocoupleTemperature(float *t)
+{
+ if (inInterrupt() || millis() - lastReadingTime < MinimumReadInterval)
+ {
+ *t = lastTemperature;
+ }
+ else
+ {
+ uint32_t rawVal;
+ TemperatureError sts = DoSpiTransaction(nullptr, 4, rawVal);
+ if (sts != TemperatureError::success)
+ {
+ lastResult = sts;
+ }
+ else
+ {
+ lastReadingTime = millis();
+
+ if ((rawVal & 0x00020008) != 0)
+ {
+ // These two bits should always read 0. Likely the entire read was 0xFF 0xFF which is not uncommon when first powering up
+ lastResult = TemperatureError::ioError;
+ }
+ else if ((rawVal & 0x00010007) != 0) // check the fault bits
+ {
+ // Check for three more types of bad reads as we set the response code:
+ // 1. A read in which the fault indicator bit (16) is set but the fault reason bits (0:2) are all clear;
+ // 2. A read in which the fault indicator bit (16) is clear, but one or more of the fault reason bits (0:2) are set; and,
+ // 3. A read in which more than one of the fault reason bits (0:1) are set.
+ if ((rawVal & 0x00010000) == 0)
+ {
+ // One or more fault reason bits are set but the fault indicator bit is clear
+ lastResult = TemperatureError::ioError;
+ }
+ else
+ {
+ // At this point we are assured that bit 16 (fault indicator) is set and that at least one of the fault reason bits (0:2) are set.
+ // We now need to ensure that only one fault reason bit is set.
+ uint8_t nbits = 0;
+ if (rawVal & 0x01)
+ {
+ // Open Circuit
+ ++nbits;
+ lastResult = TemperatureError::openCircuit;
+ }
+ if (rawVal & 0x02)
+ {
+ // Short to ground;
+ ++nbits;
+ lastResult = TemperatureError::shortToGround;
+ }
+ if (rawVal && 0x04)
+ {
+ // Short to Vcc
+ ++nbits;
+ lastResult = TemperatureError::shortToVcc;
+ }
+
+ if (nbits != 1)
+ {
+ // Fault indicator was set but a fault reason was not set (nbits == 0) or too many fault reason bits were set (nbits > 1).
+ // Assume that a communication error with the MAX31855 has occurred.
+ lastResult = TemperatureError::ioError;
+ }
+ }
+ }
+ else
+ {
+ rawVal >>= 18; // shift the 14-bit temperature data to the bottom of the word
+ rawVal |= (0 - (rawVal & 0x2000)); // sign-extend the sign bit
+
+ // And convert to from units of 1/4C to 1C
+ *t = lastTemperature = (float)(0.25 * (float)(int32_t)rawVal);
+ lastResult = TemperatureError::success;
+ }
+ }
+ }
+ return lastResult;
+}
+
+TemperatureError TemperatureSensor::GetRtdTemperature(float *t)
+{
+ if (inInterrupt() || millis() - lastReadingTime < MinimumReadInterval)
+ {
+ *t = lastTemperature;
+ }
+ else
+ {
+ static const uint8_t dataOut[4] = {0, 55, 55, 55}; // read registers 0 (control), 1 (MSB) and 2 (LSB)
+ uint32_t rawVal;
+ TemperatureError sts = DoSpiTransaction(dataOut, 4, rawVal);
+
+ if (sts != TemperatureError::success)
+ {
+ lastResult = sts;
+ }
+ else
+ {
+ lastReadingTime = millis();
+ if (((rawVal & 0x00C10000) != 0xC10000)
+#if 0
+ // We no longer check the error status bit, because it seems to be impossible to clear it once it has been set.
+ // Perhaps we would need to exit continuous reading mode to do so, and then re-enable it afterwards. But this would
+ // take to long.
+#else
+ || (rawVal & 1) != 0
+#endif
+ )
+ {
+ // Either the continuous conversion bit has got cleared, or the fault bit has been set
+ TryInitRtd();
+ lastResult = TemperatureError::hardwareError;
+ }
+ else
+ {
+ uint16_t adcVal = (rawVal >> 1) & 0x7FFF;
+
+ // Formally-verified binary search routine, adapted from one of the eCv examples
+ size_t low = 0u, high = NumTempTableEntries;
+ while (high > low)
+ //keep(low <= high; high <= NumTempTableEntries)
+ //keep(low == 0u || tempTable[low - 1u].adcReading < adcVal)
+ //keep(high == NumTempTableEntries || adcVal <= tempTable[high].adcReading)
+ //decrease(high - low)
+ {
+ size_t mid = (high - low)/2u + low; // get the mid point, avoiding arithmetic overflow
+ if (adcVal <= tempTable[mid].adcReading)
+ {
+ high = mid;
+ }
+ else
+ {
+ low = mid + 1u;
+ }
+ }
+ //assert(low <= NumTempTableEntries)
+ //assert(low == 0 || table[low - 1] < adcVal)
+ //assert(low == NumTempTableEntries || adcVal <= table[low])
+
+ if (low == 0) // if off the bottom of the table
+ {
+ lastResult = TemperatureError::shortCircuit;
+ }
+ else if (low >= NumTempTableEntries) // if off the top of the table
+ {
+ lastResult = TemperatureError::openCircuit;
+ }
+ else
+ {
+ const float interpolationFraction = (float)(adcVal - tempTable[low - 1].adcReading)/(float)(tempTable[low].adcReading - tempTable[low - 1].adcReading);
+ *t = lastTemperature = ((float)(tempTable[low].temperature - tempTable[low - 1].temperature) * interpolationFraction)
+ + (float)tempTable[low - 1].temperature;
+ //debugPrintf("raw %u low %u interp %f temp %f\n", adcVal, low, interpolationFraction, *t);
+ lastResult = TemperatureError::success;
+ }
+ }
+ }
+ }
+ return lastResult;
+}
+
+// Send and receive 1 to 4 bytes of data and return the result as a single 32-bit word
+TemperatureError TemperatureSensor::DoSpiTransaction(const uint8_t dataOut[], size_t nbytes, uint32_t& rslt) const
+{
+ if (!sspi_acquire())
+ {
+ return TemperatureError::busBusy;
+ }
+
+ sspi_master_setup_device(&device);
+ delayMicroseconds(1);
+ sspi_select_device(&device);
+ delayMicroseconds(1);
+
+ uint8_t rawBytes[4];
+ spi_status_t sts = sspi_transceive_packet(dataOut, rawBytes, nbytes);
+
+ delayMicroseconds(1);
+ sspi_deselect_device(&device);
+ delayMicroseconds(1);
+
+ sspi_release();
+
+ if (sts != SPI_OK)
+ {
+ return TemperatureError::timeout;
+ }
+
+ rslt = rawBytes[0];
+ for (size_t i = 1; i < nbytes; ++i)
+ {
+ rslt <<= 8;
+ rslt |= rawBytes[i];
+ }
+
+ return TemperatureError::success;
+}
+
+// End
diff --git a/src/Libraries/TemperatureSensor/TemperatureSensor.h b/src/Libraries/TemperatureSensor/TemperatureSensor.h
new file mode 100644
index 00000000..37b64a3b
--- /dev/null
+++ b/src/Libraries/TemperatureSensor/TemperatureSensor.h
@@ -0,0 +1,27 @@
+#ifndef TEMPERATURESENSOR_H
+#define TEMPERATURESENSOR_H
+
+#include "TemperatureError.h" // for result codes
+#include "Arduino.h"
+#include "SharedSpi.h" // for sspi_device
+
+class TemperatureSensor
+{
+public:
+ TemperatureSensor() {}
+ void InitThermocouple(uint8_t cs);
+ void InitRtd(uint8_t cs);
+ TemperatureError GetThermocoupleTemperature(float *temp);
+ TemperatureError GetRtdTemperature(float *temp);
+
+private:
+ TemperatureError DoSpiTransaction(const uint8_t dataOut[], size_t nbytes, uint32_t& rslt) const;
+ TemperatureError TryInitRtd() const;
+
+ sspi_device device;
+ uint32_t lastReadingTime;
+ float lastTemperature;
+ TemperatureError lastResult;
+};
+
+#endif // TEMPERATURESENSOR_H
diff --git a/src/Platform.cpp b/src/Platform.cpp
index da45998d..e3ff82b9 100644
--- a/src/Platform.cpp
+++ b/src/Platform.cpp
@@ -228,12 +228,19 @@ void Platform::Init()
ARRAY_INIT(tempSensePins, TEMP_SENSE_PINS);
ARRAY_INIT(heatOnPins, HEAT_ON_PINS);
- ARRAY_INIT(max31855CsPins, MAX31855_CS_PINS);
+ ARRAY_INIT(spiTempSenseCsPins, SpiTempSensorCsPins);
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.
+ for (size_t i = 0; i < MaxSpiTempSensors; ++i)
+ {
+ setPullup(SpiTempSensorCsPins[i], true);
+ }
+
// Motors
for (size_t drive = 0; drive < DRIVES; drive++)
@@ -369,11 +376,17 @@ void Platform::SetThermistorNumber(size_t heater, size_t thermistor)
{
heaterTempChannels[heater] = thermistor;
- // Initialize the associated MAX31855?
- if (thermistor >= MAX31855_START_CHANNEL && thermistor < MAX31855_START_CHANNEL + MAX31855_DEVICES)
+ // Initialize the associated SPI temperature sensor?
+ if (thermistor >= FirstThermocoupleChannel && thermistor < FirstThermocoupleChannel + MaxSpiTempSensors)
{
- Max31855Devices[thermistor - MAX31855_START_CHANNEL].Init(max31855CsPins[thermistor - MAX31855_START_CHANNEL]);
+ SpiTempSensors[thermistor - FirstThermocoupleChannel].InitThermocouple(SpiTempSensorCsPins[thermistor - FirstThermocoupleChannel]);
}
+ else if (thermistor >= FirstRtdChannel && thermistor < FirstRtdChannel + MaxSpiTempSensors)
+ {
+ SpiTempSensors[thermistor - FirstRtdChannel].InitRtd(spiTempSenseCsPins[thermistor - FirstRtdChannel]);
+ }
+
+ reprap.GetHeat()->ResetFault(heater);
}
int Platform::GetThermistorNumber(size_t heater) const
@@ -504,19 +517,21 @@ void Platform::GetZProbeAxes(bool (&axes)[AXES])
}
}
-float Platform::ZProbeStopHeight() const
+float Platform::ZProbeStopHeight()
{
+ const int8_t bedHeater = reprap.GetHeat()->GetBedHeater();
+ float temperature = (bedHeater >= 0) ? GetTemperature(bedHeater) : 25.0;
switch (nvData.zProbeType)
{
case 0:
case 4:
- return nvData.switchZProbeParameters.GetStopHeight(GetTemperature(0));
+ return nvData.switchZProbeParameters.GetStopHeight(temperature);
case 1:
case 2:
- return nvData.irZProbeParameters.GetStopHeight(GetTemperature(0));
+ return nvData.irZProbeParameters.GetStopHeight(temperature);
case 3:
case 5:
- return nvData.alternateZProbeParameters.GetStopHeight(GetTemperature(0));
+ return nvData.alternateZProbeParameters.GetStopHeight(temperature);
default:
return 0;
}
@@ -1355,13 +1370,13 @@ void Platform::ClassReport(float &lastTime)
// Result is in degrees celsius
-float Platform::GetTemperature(size_t heater, TempError* err) const
+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.
- if (DoThermistorAdc(heater))
+ if (IsThermistorChannel(heater))
{
- int rawTemp = GetRawTemperature(heater);
+ int rawTemp = GetRawThermistorTemperature(heater);
// If the ADC reading is N then for an ideal ADC, the input voltage is at least N/(AD_RANGE + 1) and less than (N + 1)/(AD_RANGE + 1), times the analog reference.
// So we add 0.5 to to the reading to get a better estimate of the input.
@@ -1379,7 +1394,7 @@ float Platform::GetTemperature(size_t heater, TempError* err) const
// thermistor is disconnected
if (err)
{
- *err = TempError::errOpen;
+ *err = TemperatureError::openCircuit;
}
return ABS_ZERO;
}
@@ -1398,55 +1413,44 @@ float Platform::GetTemperature(size_t heater, TempError* err) const
// thermistor short circuit, return a high temperature
if (err)
{
- *err = TempError::errShort;
+ *err = TemperatureError::shortCircuit;
}
- return BAD_ERROR_TEMPERATURE;
}
}
- else
+ else if (IsThermocoupleChannel(heater))
{
// MAX31855 thermocouple chip
float temp;
- MAX31855_error res = Max31855Devices[heaterTempChannels[heater] - MAX31855_START_CHANNEL].getTemperature(&temp);
- if (res == MAX31855_OK)
+ TemperatureError res = SpiTempSensors[heaterTempChannels[heater] - FirstThermocoupleChannel].GetThermocoupleTemperature(&temp);
+ if (res == TemperatureError::success)
{
return temp;
}
if (err)
{
- switch(res)
- {
- case MAX31855_OK : *err = TempError::errOk; break; // Success
- case MAX31855_ERR_SCV : *err = TempError::errShortVcc; break; // Short to Vcc
- case MAX31855_ERR_SCG : *err = TempError::errShortGnd; break; // Short to GND
- case MAX31855_ERR_OC : *err = TempError::errOpen; break; // Open connection
- case MAX31855_ERR_TMO : *err = TempError::errTimeout; break; // SPI comms timeout
- case MAX31855_ERR_IO :
- default : *err = TempError::errIO; break; // SPI comms not functioning
- }
+ *err = res;
}
- return BAD_ERROR_TEMPERATURE;
}
-}
-
-/*static*/ const char* Platform::TempErrorStr(TempError err)
-{
- switch(err)
+ else if (IsRtdChannel(heater))
{
- default : return "Unknown temperature read error";
- case TempError::errOk : return "successful temperature read";
- case TempError::errShort : return "sensor circuit is shorted";
- case TempError::errShortVcc : return "sensor circuit is shorted to the voltage rail";
- case TempError::errShortGnd : return "sensor circuit is shorted to ground";
- case TempError::errOpen : return "sensor circuit is open/disconnected";
- case TempError::errTooHigh: return "temperature above safety limit";
- case TempError::errTimeout : return "communication error whilst reading sensor; read took too long";
- case TempError::errIO: return "communication error whilst reading sensor; check sensor connections";
+ // MAX31865 RTD chip
+ float temp;
+ TemperatureError res = SpiTempSensors[heaterTempChannels[heater] - FirstRtdChannel].GetRtdTemperature(&temp);
+ if (res == TemperatureError::success)
+ {
+ return temp;
+ }
+ if (err)
+ {
+ *err = res;
+ }
}
+
+ return BAD_ERROR_TEMPERATURE;
}
// See if we need to turn on the hot end fan
-bool Platform::AnyHeaterHot(uint16_t heaters, float t) const
+bool Platform::AnyHeaterHot(uint16_t heaters, float t)
{
// Check tool heaters first
for (size_t h = 0; h < reprap.GetToolHeatersInUse(); ++h)
@@ -2539,7 +2543,7 @@ void Platform::Tick()
case 1: // last conversion started was a thermistor
case 3:
{
- if (DoThermistorAdc(currentHeater))
+ if (IsThermistorChannel(currentHeater))
{
ThermistorAveragingFilter& currentFilter = const_cast<ThermistorAveragingFilter&>(thermistorFilters[currentHeater]);
currentFilter.ProcessReading(AnalogInReadChannel(thermistorAdcChannels[heaterTempChannels[currentHeater]]));
diff --git a/src/Platform.h b/src/Platform.h
index 328b5c21..d097a2f8 100644
--- a/src/Platform.h
+++ b/src/Platform.h
@@ -36,9 +36,11 @@ Licence: GPL
// Language-specific includes
+#include <Libraries/TemperatureSensor/TemperatureSensor.h>
#include <cctype>
#include <cstring>
#include <malloc.h>
+#include <TemperatureError.h>
#include <cstdlib>
#include <climits>
@@ -47,7 +49,6 @@ Licence: GPL
#include "Arduino.h"
#include "OutputMemory.h"
#include "ff.h"
-#include "MAX31855.h"
#include "MCP4461.h"
#include "MassStorage.h"
#include "FileStore.h"
@@ -417,9 +418,6 @@ public:
// Enumeration to describe the status of a drive
enum class DriveStatus : uint8_t { disabled, idle, enabled };
- // Error results generated by GetTemperature()
- enum class TempError : uint8_t { errOk, errShort, errShortVcc, errShortGnd, errOpen, errTooHigh, errTimeout, errIO };
-
Platform();
//-------------------------------------------------------------------------------------------------------------
@@ -537,7 +535,7 @@ public:
// Z probe
- float ZProbeStopHeight() const;
+ float ZProbeStopHeight();
float GetZProbeDiveHeight() const;
float GetZProbeTravelSpeed() const;
int ZProbe() const;
@@ -560,7 +558,7 @@ public:
// Heat and temperature
- float GetTemperature(size_t heater, TempError* err = nullptr) const; // Result is in degrees Celsius
+ float GetTemperature(size_t heater, TemperatureError* err = nullptr); // Result is in degrees Celsius
void SetHeater(size_t heater, float power); // power is a fraction in [0,1]
float HeatSampleTime() const;
void SetHeatSampleTime(float st);
@@ -570,11 +568,11 @@ public:
void SetTimeToHot(float t);
void SetThermistorNumber(size_t heater, size_t thermistor);
int GetThermistorNumber(size_t heater) const;
- bool DoThermistorAdc(uint8_t heater) const;
+ bool IsThermistorChannel(uint8_t heater) const;
+ bool IsThermocoupleChannel(uint8_t heater) const;
+ bool IsRtdChannel(uint8_t heater) const;
void SetTemperatureLimit(float t);
float GetTemperatureLimit() const { return temperatureLimit; }
- static const char* TempErrorStr(TempError err);
- static bool TempErrorPermanent(TempError err);
void UpdateConfiguredHeaters();
// Fans
@@ -758,14 +756,14 @@ private:
// Heaters - bed is assumed to be the first
- int GetRawTemperature(size_t heater) const;
+ int GetRawThermistorTemperature(size_t heater) const;
void SetHeaterPwm(size_t heater, uint8_t pwm);
- bool AnyHeaterHot(uint16_t heaters, float t) const; // called to see if we need to turn on the hot end fan
+ bool AnyHeaterHot(uint16_t heaters, float t); // called to see if we need to turn on the hot end fan
Pin tempSensePins[HEATERS];
Pin heatOnPins[HEATERS];
- MAX31855 Max31855Devices[MAX31855_DEVICES];
- Pin max31855CsPins[MAX31855_DEVICES];
+ TemperatureSensor SpiTempSensors[MaxSpiTempSensors];
+ Pin spiTempSenseCsPins[MaxSpiTempSensors];
uint32_t configuredHeaters; // Bitmask of all heaters in use
float heatSampleTime;
float timeToHot;
@@ -816,7 +814,7 @@ private:
//
// PinToAdcChannel(tempSensePins[tc])
//
- // if (100 <= tc < 100 + (MAX31855_DEVICES - 1)) then
+ // if (100 <= tc < 100 + (MaxSpiTempSensors - 1)) then
// The temperature channel is a thermocouple attached to a MAX31855 chip
// The MAX31855 object corresponding to the specific MAX31855 chip is
//
@@ -824,14 +822,14 @@ private:
//
// Note that the MAX31855 objects, although statically declared, are not
// initialized until configured via a "M305 Pn X10m" command with 0 <= n < HEATERS
- // and 0 <= m < MAX31855_DEVICES.
+ // and 0 <= m < MaxSpiTempSensors.
//
// NOTE BENE: When a M305 command is processed, the onus is on the gcode processor,
// GCodes.cpp, to range check the value of the X parameter. Code consuming the results
// of the M305 command (e.g., SetThermistorNumber() and array lookups assume range
// checking has already been performed.
- uint8_t heaterTempChannels[HEATERS];
+ unsigned int heaterTempChannels[HEATERS];
AnalogChannelNumber thermistorAdcChannels[HEATERS];
AnalogChannelNumber zProbeAdcChannel;
uint32_t thermistorOverheatSums[HEATERS];
@@ -1146,7 +1144,7 @@ inline void Platform::ExtrudeOff()
// Drive the RepRap machine - Heat and temperature
-inline int Platform::GetRawTemperature(size_t heater) const
+inline int Platform::GetRawThermistorTemperature(size_t heater) const
{
return (heater < HEATERS)
? thermistorFilters[heater].GetSum()/(THERMISTOR_AVERAGE_READINGS >> AD_OVERSAMPLE_BITS)
@@ -1173,17 +1171,21 @@ inline void Platform::SetTimeToHot(float t)
timeToHot = t;
}
-inline bool Platform::DoThermistorAdc(uint8_t heater) const
+inline bool Platform::IsThermistorChannel(uint8_t heater) const
{
return heaterTempChannels[heater] < HEATERS;
}
-// 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.
-inline bool Platform::TempErrorPermanent(TempError err)
+inline bool Platform::IsThermocoupleChannel(uint8_t heater) const
+{
+ return heaterTempChannels[heater] >= FirstThermocoupleChannel
+ && heaterTempChannels[heater] - FirstThermocoupleChannel < MaxSpiTempSensors;
+}
+
+inline bool Platform::IsRtdChannel(uint8_t heater) const
{
- return (err != TempError::errTimeout) && (err != TempError::errIO) && (err != TempError::errOk);
+ return heaterTempChannels[heater] >= FirstRtdChannel
+ && heaterTempChannels[heater] - FirstRtdChannel < MaxSpiTempSensors;
}
inline const uint8_t* Platform::IPAddress() const
diff --git a/src/Reprap.cpp b/src/Reprap.cpp
index 117d2cc9..acae8f9c 100644
--- a/src/Reprap.cpp
+++ b/src/Reprap.cpp
@@ -497,7 +497,7 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source)
char ch = GetStatusCharacter();
response->printf("{\"status\":\"%c\",\"coords\":{", ch);
- /* Coordinates */
+ // Coordinates
{
float liveCoordinates[DRIVES + 1];
#if SUPPORT_ROLAND
@@ -594,7 +594,7 @@ OutputBuffer *RepRap::GetStatusResponse(uint8_t type, ResponseSource source)
}
}
- /* Parameters */
+ // Parameters
{
// ATX power
response->catf(",\"params\":{\"atxPower\":%d", platform->AtxPower() ? 1 : 0);
@@ -1121,20 +1121,10 @@ OutputBuffer *RepRap::GetLegacyStatusResponse(uint8_t type, int seq)
response->catf(",\"fanRPM\":%u", static_cast<unsigned int>(platform->GetFanRPM()));
// Send the home state. To keep the messages short, we send 1 for homed and 0 for not homed, instead of true and false.
- if (type != 0)
- {
- response->catf(",\"homed\":[%d,%d,%d]",
- (gCodes->GetAxisIsHomed(0)) ? 1 : 0,
- (gCodes->GetAxisIsHomed(1)) ? 1 : 0,
- (gCodes->GetAxisIsHomed(2)) ? 1 : 0);
- }
- else
- {
- response->catf(",\"hx\":%d,\"hy\":%d,\"hz\":%d",
- (gCodes->GetAxisIsHomed(0)) ? 1 : 0,
- (gCodes->GetAxisIsHomed(1)) ? 1 : 0,
- (gCodes->GetAxisIsHomed(2)) ? 1 : 0);
- }
+ response->catf(",\"homed\":[%d,%d,%d]",
+ (gCodes->GetAxisIsHomed(0)) ? 1 : 0,
+ (gCodes->GetAxisIsHomed(1)) ? 1 : 0,
+ (gCodes->GetAxisIsHomed(2)) ? 1 : 0);
if (printMonitor->IsPrinting())
{
diff --git a/src/TemperatureError.cpp b/src/TemperatureError.cpp
new file mode 100644
index 00000000..182d0755
--- /dev/null
+++ b/src/TemperatureError.cpp
@@ -0,0 +1,44 @@
+/*
+ * TemperatureError.cpp
+ *
+ * Created on: 21 Apr 2016
+ * Author: David
+ */
+
+#include "TemperatureError.h"
+
+const char* TemperatureErrorString(TemperatureError err)
+{
+ switch(err)
+ {
+ case TemperatureError::success: return "success";
+ case TemperatureError::shortCircuit: return "short-circuit in sensor";
+ case TemperatureError::shortToVcc: return "short to Vcc";
+ case TemperatureError::shortToGround: return "short to ground";
+ case TemperatureError::openCircuit: return "open circuit";
+ case TemperatureError::tooHigh: return "temperature above limit";
+ case TemperatureError::timeout: return "timeout";
+ case TemperatureError::ioError: return "I/O error";
+ case TemperatureError::hardwareError: return "hardware error";
+ case TemperatureError::busBusy: return "bus busy";
+ case TemperatureError::badResponse: return "bad response";
+ 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)
+{
+ switch (err)
+ {
+ case TemperatureError::success:
+ case TemperatureError::busBusy:
+ case TemperatureError::ioError:
+ return false;
+ default:
+ return true;
+ }
+}
+
+// End
diff --git a/src/TemperatureError.h b/src/TemperatureError.h
new file mode 100644
index 00000000..47caf489
--- /dev/null
+++ b/src/TemperatureError.h
@@ -0,0 +1,35 @@
+/*
+ * TemperatureError.h
+ *
+ * Created on: 21 Apr 2016
+ * Author: David
+ */
+
+#ifndef TEMPERATUREERROR_H_
+#define TEMPERATUREERROR_H_
+
+#include <cstdint>
+
+// Result codes returned by temperature sensor drivers
+enum class TemperatureError : uint8_t
+{
+ success,
+ shortCircuit,
+ shortToVcc,
+ shortToGround,
+ openCircuit,
+ tooHigh,
+ timeout,
+ ioError,
+ hardwareError,
+ busBusy,
+ badResponse
+};
+
+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_ */