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
diff options
context:
space:
mode:
Diffstat (limited to 'src/Heating')
-rw-r--r--src/Heating/Heat.cpp186
-rw-r--r--src/Heating/Heat.h47
-rw-r--r--src/Heating/Pid.cpp15
-rw-r--r--src/Heating/Sensors/CpuTemperatureSensor.cpp31
-rw-r--r--src/Heating/Sensors/CpuTemperatureSensor.h25
-rw-r--r--src/Heating/Sensors/CurrentLoopTemperatureSensor.cpp123
-rw-r--r--src/Heating/Sensors/CurrentLoopTemperatureSensor.h35
-rw-r--r--src/Heating/Sensors/RtdSensor31865.cpp207
-rw-r--r--src/Heating/Sensors/RtdSensor31865.h24
-rw-r--r--src/Heating/Sensors/SpiTemperatureSensor.cpp63
-rw-r--r--src/Heating/Sensors/SpiTemperatureSensor.h27
-rw-r--r--src/Heating/Sensors/TemperatureSensor.cpp120
-rw-r--r--src/Heating/Sensors/TemperatureSensor.h55
-rw-r--r--src/Heating/Sensors/Thermistor.cpp139
-rw-r--r--src/Heating/Sensors/Thermistor.h (renamed from src/Heating/Thermistor.h)13
-rw-r--r--src/Heating/Sensors/ThermocoupleSensor31855.cpp156
-rw-r--r--src/Heating/Sensors/ThermocoupleSensor31855.h21
-rw-r--r--src/Heating/Sensors/TmcDriverTemperatureSensor.cpp30
-rw-r--r--src/Heating/Sensors/TmcDriverTemperatureSensor.h25
-rw-r--r--src/Heating/TemperatureError.cpp2
-rw-r--r--src/Heating/TemperatureError.h4
-rw-r--r--src/Heating/TemperatureSensor.cpp449
-rw-r--r--src/Heating/TemperatureSensor.h33
-rw-r--r--src/Heating/Thermistor.cpp70
24 files changed, 1281 insertions, 619 deletions
diff --git a/src/Heating/Heat.cpp b/src/Heating/Heat.cpp
index ecd184d0..eb5b5ff9 100644
--- a/src/Heating/Heat.cpp
+++ b/src/Heating/Heat.cpp
@@ -19,14 +19,14 @@ Licence: GPL
****************************************************************************************************/
#include "Heat.h"
-
#include "Platform.h"
#include "RepRap.h"
+#include "Sensors/TemperatureSensor.h"
Heat::Heat(Platform& p)
: platform(p), active(false), coldExtrude(false), bedHeater(DefaultBedHeater), chamberHeater(DefaultChamberHeater), heaterBeingTuned(-1), lastHeaterTuned(-1)
{
- for (size_t heater = 0; heater < HEATERS; heater++)
+ for (size_t heater : ARRAY_INDICES(pids))
{
pids[heater] = new PID(platform, heater);
}
@@ -35,11 +35,11 @@ Heat::Heat(Platform& p)
// Reset all heater models to defaults. Called when running M502.
void Heat::ResetHeaterModels()
{
- for (int heater = 0; heater < HEATERS; heater++)
+ for (size_t heater : ARRAY_INDICES(pids))
{
if (pids[heater]->IsHeaterEnabled())
{
- if (heater == DefaultBedHeater || heater == DefaultChamberHeater)
+ if ((int)heater == DefaultBedHeater || (int)heater == DefaultChamberHeater)
{
pids[heater]->SetModel(DefaultBedHeaterGain, DefaultBedHeaterTimeConstant, DefaultBedHeaterDeadTime, 1.0, false);
}
@@ -53,27 +53,44 @@ void Heat::ResetHeaterModels()
void Heat::Init()
{
- for (int heater = 0; heater < HEATERS; heater++)
+ // Set up the real heaters and the corresponding PIDs
+ for (size_t heater : ARRAY_INDICES(pids))
{
- if (heater == DefaultBedHeater || heater == DefaultChamberHeater)
+ heaterSensors[heater] = nullptr; // no temperature sensor assigned yet
+ if ((int)heater == DefaultBedHeater || (int)heater == DefaultChamberHeater)
{
- pids[heater]->Init(DefaultBedHeaterGain, DefaultBedHeaterTimeConstant, DefaultBedHeaterDeadTime,
- DefaultBedTemperatureLimit, false);
+ pids[heater]->Init(DefaultBedHeaterGain, DefaultBedHeaterTimeConstant, DefaultBedHeaterDeadTime, DefaultBedTemperatureLimit, false);
}
#if !defined(DUET_NG) && !defined(__RADDS__) && !defined(__ALLIGATOR__)
- else if (heater == HEATERS - 1)
+ else if (heater == Heaters - 1)
{
- // Heater 6 pin is shared with fan 1. By default we support fan 1, so disable heater 6.
+ // On the Duet 085, the heater 6 pin is also the fan 1 pin. By default we support fan 1, so disable heater 6.
pids[heater]->Init(-1.0, -1.0, -1.0, DefaultExtruderTemperatureLimit, true);
}
#endif
else
{
- pids[heater]->Init(DefaultHotEndHeaterGain, DefaultHotEndHeaterTimeConstant, DefaultHotEndHeaterDeadTime,
- DefaultExtruderTemperatureLimit, true);
+ pids[heater]->Init(DefaultHotEndHeaterGain, DefaultHotEndHeaterTimeConstant, DefaultHotEndHeaterDeadTime, DefaultExtruderTemperatureLimit, true);
}
}
+ // Set up the virtual heaters
+ // Clear the user-defined virtual heaters
+ for (TemperatureSensor* &v : virtualHeaterSensors)
+ {
+ v = nullptr;
+ }
+
+ // Set up default virtual heaters for MCU temperature and TMC driver overheat sensors
+#ifndef __RADDS
+ virtualHeaterSensors[0] = TemperatureSensor::Create(CpuTemperatureSenseChannel);
+ virtualHeaterSensors[0]->SetHeaterName("MCU"); // name this virtual heater so that it appears in DWC
+#endif
+#ifdef DUET_NG
+ virtualHeaterSensors[1] = TemperatureSensor::Create(FirstTmcDriversSenseChannel);
+ virtualHeaterSensors[2] = TemperatureSensor::Create(FirstTmcDriversSenseChannel + 1);
+#endif
+
lastTime = millis() - platform.HeatSampleInterval(); // flag the PIDS as due for spinning
longWait = platform.Time();
coldExtrude = false;
@@ -82,9 +99,9 @@ void Heat::Init()
void Heat::Exit()
{
- for (size_t heater = 0; heater < HEATERS; heater++)
+ for (PID *pid : pids)
{
- pids[heater]->SwitchOff();
+ pid->SwitchOff();
}
platform.Message(HOST_MESSAGE, "Heat class exited.\n");
active = false;
@@ -99,7 +116,7 @@ void Heat::Spin()
if (now - lastTime >= platform.HeatSampleInterval())
{
lastTime = now;
- for (size_t heater=0; heater < HEATERS; heater++)
+ for (size_t heater=0; heater < Heaters; heater++)
{
pids[heater]->Spin();
}
@@ -118,7 +135,7 @@ void Heat::Spin()
void Heat::Diagnostics(MessageType mtype)
{
platform.MessageF(mtype, "=== Heat ===\nBed heater = %d, chamber heater = %d\n", bedHeater, chamberHeater);
- for (size_t heater=0; heater < HEATERS; heater++)
+ for (size_t heater : ARRAY_INDICES(pids))
{
if (pids[heater]->Active())
{
@@ -129,9 +146,9 @@ void Heat::Diagnostics(MessageType mtype)
bool Heat::AllHeatersAtSetTemperatures(bool includingBed) const
{
- for(int8_t heater = 0; heater < HEATERS; heater++)
+ for (size_t heater : ARRAY_INDICES(pids))
{
- if (!HeaterAtSetTemperature(heater, true) && (includingBed || heater != bedHeater))
+ if (!HeaterAtSetTemperature(heater, true) && (includingBed || (int)heater != bedHeater))
{
return false;
}
@@ -143,7 +160,7 @@ bool Heat::AllHeatersAtSetTemperatures(bool includingBed) const
bool Heat::HeaterAtSetTemperature(int8_t heater, bool waitWhenCooling) const
{
// If it hasn't anything to do, it must be right wherever it is...
- if (heater < 0 || heater >= HEATERS || pids[heater]->SwitchedOff() || pids[heater]->FaultOccurred())
+ if (heater < 0 || heater >= (int)Heaters || pids[heater]->SwitchedOff() || pids[heater]->FaultOccurred())
{
return true;
}
@@ -157,7 +174,7 @@ bool Heat::HeaterAtSetTemperature(int8_t heater, bool waitWhenCooling) const
Heat::HeaterStatus Heat::GetStatus(int8_t heater) const
{
- if (heater < 0 || heater >= HEATERS)
+ if (heater < 0 || heater >= (int)Heaters)
{
return HS_off;
}
@@ -171,7 +188,7 @@ Heat::HeaterStatus Heat::GetStatus(int8_t heater) const
void Heat::SetActiveTemperature(int8_t heater, float t)
{
- if (heater >= 0 && heater < HEATERS)
+ if (heater >= 0 && heater < (int)Heaters)
{
pids[heater]->SetActiveTemperature(t);
}
@@ -179,12 +196,12 @@ void Heat::SetActiveTemperature(int8_t heater, float t)
float Heat::GetActiveTemperature(int8_t heater) const
{
- return (heater >= 0 && heater < HEATERS) ? pids[heater]->GetActiveTemperature() : ABS_ZERO;
+ return (heater >= 0 && heater < (int)Heaters) ? pids[heater]->GetActiveTemperature() : ABS_ZERO;
}
void Heat::SetStandbyTemperature(int8_t heater, float t)
{
- if (heater >= 0 && heater < HEATERS)
+ if (heater >= 0 && heater < (int)Heaters)
{
pids[heater]->SetStandbyTemperature(t);
}
@@ -192,12 +209,12 @@ void Heat::SetStandbyTemperature(int8_t heater, float t)
float Heat::GetStandbyTemperature(int8_t heater) const
{
- return (heater >= 0 && heater < HEATERS) ? pids[heater]->GetStandbyTemperature() : ABS_ZERO;
+ return (heater >= 0 && heater < (int)Heaters) ? pids[heater]->GetStandbyTemperature() : ABS_ZERO;
}
void Heat::SetTemperatureLimit(int8_t heater, float t)
{
- if (heater >= 0 && heater < HEATERS)
+ if (heater >= 0 && heater < (int)Heaters)
{
pids[heater]->SetTemperatureLimit(t);
}
@@ -205,17 +222,17 @@ void Heat::SetTemperatureLimit(int8_t heater, float t)
float Heat::GetTemperatureLimit(int8_t heater) const
{
- return (heater >= 0 && heater < HEATERS) ? pids[heater]->GetTemperatureLimit() : ABS_ZERO;
+ return (heater >= 0 && heater < (int)Heaters) ? pids[heater]->GetTemperatureLimit() : ABS_ZERO;
}
float Heat::GetTemperature(int8_t heater) const
{
- return (heater >= 0 && heater < HEATERS) ? pids[heater]->GetTemperature() : ABS_ZERO;
+ return (heater >= 0 && heater < (int)Heaters) ? pids[heater]->GetTemperature() : ABS_ZERO;
}
void Heat::Activate(int8_t heater)
{
- if (heater >= 0 && heater < HEATERS)
+ if (heater >= 0 && heater < (int)Heaters)
{
pids[heater]->Activate();
}
@@ -223,7 +240,7 @@ void Heat::Activate(int8_t heater)
void Heat::SwitchOff(int8_t heater)
{
- if (heater >= 0 && heater < HEATERS)
+ if (heater >= 0 && heater < (int)Heaters)
{
pids[heater]->SwitchOff();
}
@@ -231,7 +248,7 @@ void Heat::SwitchOff(int8_t heater)
void Heat::SwitchOffAll()
{
- for (size_t heater = 0; heater < HEATERS; ++heater)
+ for (size_t heater = 0; heater < Heaters; ++heater)
{
pids[heater]->SwitchOff();
}
@@ -239,7 +256,7 @@ void Heat::SwitchOffAll()
void Heat::Standby(int8_t heater)
{
- if (heater >= 0 && heater < HEATERS)
+ if (heater >= 0 && heater < (int)Heaters)
{
pids[heater]->Standby();
}
@@ -247,7 +264,7 @@ void Heat::Standby(int8_t heater)
void Heat::ResetFault(int8_t heater)
{
- if (heater >= 0 && heater < HEATERS)
+ if (heater >= 0 && heater < (int)Heaters)
{
pids[heater]->ResetFault();
}
@@ -305,7 +322,7 @@ void Heat::GetAutoTuneStatus(StringRef& reply) const
float Heat::GetHighestTemperatureLimit() const
{
float limit = ABS_ZERO;
- for (size_t h = 0; h < HEATERS; ++h)
+ for (size_t h : ARRAY_INDICES(pids))
{
if (h < reprap.GetToolHeatersInUse() || (int)h == bedHeater || (int)h == chamberHeater)
{
@@ -329,7 +346,7 @@ void Heat::SetM301PidParameters(size_t heater, const M301PidParameters& params)
bool Heat::WriteModelParameters(FileStore *f) const
{
bool ok = f->Write("; Heater model parameters\n");
- for (size_t h = 0; ok && h < HEATERS; ++h)
+ for (size_t h : ARRAY_INDICES(pids))
{
const FopDt& model = pids[h]->GetModel();
if (model.IsEnabled())
@@ -340,4 +357,105 @@ bool Heat::WriteModelParameters(FileStore *f) const
return ok;
}
+// Return the channel used by a particular heater, or -1 if not configured
+int Heat::GetHeaterChannel(size_t heater) const
+{
+ const TemperatureSensor * const * const spp = GetSensor(heater);
+ return (spp != nullptr && *spp != nullptr) ? (*spp)->GetSensorChannel() : -1;
+}
+
+// Set the channel used by a heater, returning true if bad heater or channel number
+bool Heat::SetHeaterChannel(size_t heater, int channel)
+{
+ TemperatureSensor ** const spp = GetSensor(heater);
+ if (spp == nullptr)
+ {
+ return true; // bad heater number
+ }
+
+ TemperatureSensor *sp = TemperatureSensor::Create(channel);
+ if (sp == nullptr)
+ {
+ return true; // bad channel number
+ }
+
+ delete *spp; // release the old sensor object, if any
+ *spp = sp;
+ return false;
+}
+
+// Configure the temperature sensor for a channel
+bool Heat::ConfigureHeaterSensor(unsigned int mcode, size_t heater, GCodeBuffer& gb, StringRef& reply, bool& error)
+{
+ TemperatureSensor ** const spp = GetSensor(heater);
+ if (spp == nullptr || *spp == nullptr)
+ {
+ reply.printf("heater %d is not configured", heater);
+ error = true;
+ return false;
+ }
+
+ return (*spp)->Configure(mcode, heater, gb, reply, error);
+}
+
+// Get a pointer to the temperature sensor entry, or nullptr if the heater number is bad
+TemperatureSensor **Heat::GetSensor(size_t heater)
+{
+ if (heater < Heaters)
+ {
+ return &heaterSensors[heater];
+ }
+ if (heater >= FirstVirtualHeater && heater < FirstVirtualHeater + ARRAY_SIZE(virtualHeaterSensors))
+ {
+ return &virtualHeaterSensors[heater - FirstVirtualHeater];
+ }
+ return nullptr;
+}
+
+// Get a pointer to the temperature sensor entry, or nullptr if the heater number is bad (const version of above)
+TemperatureSensor * const *Heat::GetSensor(size_t heater) const
+{
+ if (heater < Heaters)
+ {
+ return &heaterSensors[heater];
+ }
+ if (heater >= FirstVirtualHeater && heater < FirstVirtualHeater + ARRAY_SIZE(virtualHeaterSensors))
+ {
+ return &virtualHeaterSensors[heater - FirstVirtualHeater];
+ }
+ return nullptr;
+}
+
+// Get the name of a heater, or nullptr if it hasn't been named
+const char *Heat::GetHeaterName(size_t heater) const
+{
+ const TemperatureSensor * const * const spp = GetSensor(heater);
+ return (spp == nullptr || *spp == nullptr) ? nullptr : (*spp)->GetHeaterName();
+}
+
+// Get the temperature of a real or virtual heater
+float Heat::GetTemperature(size_t heater, TemperatureError& err)
+{
+ TemperatureSensor ** const spp = GetSensor(heater);
+ if (spp == nullptr)
+ {
+ err = TemperatureError::unknownHeater;
+ return BAD_ERROR_TEMPERATURE;
+ }
+
+ if (*spp == nullptr)
+ {
+ err = TemperatureError::unknownChannel;
+ return BAD_ERROR_TEMPERATURE;
+ }
+
+ float t;
+ err = (*spp)->GetTemperature(t);
+ if (err != TemperatureError::success)
+ {
+ t = BAD_ERROR_TEMPERATURE;
+ }
+ return t;
+}
+
// End
diff --git a/src/Heating/Heat.h b/src/Heating/Heat.h
index 2c487344..3ebb5c6d 100644
--- a/src/Heating/Heat.h
+++ b/src/Heating/Heat.h
@@ -29,6 +29,9 @@ Licence: GPL
#include "Pid.h"
#include "MessageType.h"
+class TemperatureSensor;
+class GCodeBuffer;
+
class Heat
{
public:
@@ -45,16 +48,16 @@ public:
void AllowColdExtrude(bool b); // Allow or deny cold extrusion
int8_t GetBedHeater() const // Get hot bed heater number
- post(-1 <= result; result < HEATERS);
+ post(-1 <= result; result < Heaters);
void SetBedHeater(int8_t heater) // Set hot bed heater number
- pre(-1 <= heater; heater < HEATERS);
+ pre(-1 <= heater; heater < Heaters);
int8_t GetChamberHeater() const // Get chamber heater number
- post(-1 <= result; result < HEATERS);
+ post(-1 <= result; result < Heaters);
void SetChamberHeater(int8_t heater) // Set chamber heater number
- pre(-1 <= heater; heater < HEATERS);
+ pre(-1 <= heater; heater < Heaters);
void SetActiveTemperature(int8_t heater, float t);
float GetActiveTemperature(int8_t heater) const;
@@ -74,48 +77,60 @@ public:
void Diagnostics(MessageType mtype); // Output useful information
float GetAveragePWM(size_t heater) const // Return the running average PWM to the heater as a fraction in [0, 1].
- pre(heater < HEATERS);
+ pre(heater < Heaters);
bool UseSlowPwm(int8_t heater) const; // Queried by the Platform class
uint32_t GetLastSampleTime(size_t heater) const
- pre(heater < HEATERS);
+ pre(heater < Heaters);
void StartAutoTune(size_t heater, float temperature, float maxPwm, StringRef& reply) // Auto tune a PID
- pre(heater < HEATERS);
+ pre(heater < Heaters);
bool IsTuning(size_t heater) const // Return true if the specified heater is auto tuning
- pre(heater < HEATERS);
+ pre(heater < Heaters);
void GetAutoTuneStatus(StringRef& reply) const; // Get the status of the current or last auto tune
const FopDt& GetHeaterModel(size_t heater) const // Get the process model for the specified heater
- pre(heater < HEATERS);
+ pre(heater < Heaters);
bool SetHeaterModel(size_t heater, float gain, float tc, float td, float maxPwm, bool usePid) // Set the heater process model
- pre(heater < HEATERS);
+ pre(heater < Heaters);
void GetHeaterProtection(size_t heater, float& maxTempExcursion, float& maxFaultTime) const
- pre(heater < HEATERS);
+ pre(heater < Heaters);
void SetHeaterProtection(size_t heater, float maxTempExcursion, float maxFaultTime)
- pre(heater < HEATERS);
+ pre(heater < Heaters);
bool IsHeaterEnabled(size_t heater) const // Is this heater enabled?
- pre(heater < HEATERS);
+ pre(heater < Heaters);
float GetHighestTemperatureLimit() const; // Get the highest temperature limit of any heater
void SetM301PidParameters(size_t heater, const M301PidParameters& params)
- pre(heater < HEATERS);
+ pre(heater < Heaters);
bool WriteModelParameters(FileStore *f) const; // Write heater model parameters to file returning true if no error
+ int GetHeaterChannel(size_t heater) const; // Return the channel used by a particular heater, or -1 if not configured
+ bool SetHeaterChannel(size_t heater, int channel); // Set the channel used by a heater, returning true if bad heater or channel number
+ bool ConfigureHeaterSensor(size_t heater, unsigned int mcode, GCodeBuffer& gb, StringRef& reply, bool& error); // Configure the temperature sensor for a channel
+ const char *GetHeaterName(size_t heater) const; // Get the name of a heater, or nullptr if it hasn't been named
+
+ float GetTemperature(size_t heater, TemperatureError& err); // Result is in degrees Celsius
+
private:
- Heat(const Heat&); // private copy constructor to prevent copying
+ Heat(const Heat&); // Private copy constructor to prevent copying
+ TemperatureSensor **GetSensor(size_t heater); // Get a pointer to the temperature sensor entry
+ TemperatureSensor * const *GetSensor(size_t heater) const; // Get a pointer to the temperature sensor entry
Platform& platform; // The instance of the RepRap hardware class
- PID* pids[HEATERS]; // A PID controller for each heater
+
+ PID* pids[Heaters]; // A PID controller for each heater
+ TemperatureSensor *heaterSensors[Heaters]; // The sensor used by the real heaters
+ TemperatureSensor *virtualHeaterSensors[MaxVirtualHeaters]; // Sensors for virtual heaters
uint32_t lastTime; // The last time our Spin() was called
float longWait; // Long time for things that happen occasionally
diff --git a/src/Heating/Pid.cpp b/src/Heating/Pid.cpp
index 88ca87ec..145ff014 100644
--- a/src/Heating/Pid.cpp
+++ b/src/Heating/Pid.cpp
@@ -83,7 +83,7 @@ bool PID::SetModel(float gain, float tc, float td, float maxPwm, bool usePid)
if (rslt)
{
#if !defined(DUET_NG) && !defined(__RADDS__) && !defined(__ALLIGATOR__)
- if (heater == HEATERS - 1)
+ if (heater == Heaters - 1)
{
// The last heater on the Duet 0.8.5 + DueX4 shares its pin with Fan1
platform.EnableSharedFan(!model.IsEnabled());
@@ -112,17 +112,10 @@ bool PID::SetModel(float gain, float tc, float td, float maxPwm, bool usePid)
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)
+ temperature = reprap.GetHeat().GetTemperature(heater, err); // in the event of an error, err is set and BAD_ERROR_TEMPERATURE is returned
+ if (err == TemperatureError::success && temperature > temperatureLimit)
{
- if (temperature < BAD_LOW_TEMPERATURE)
- {
- err = TemperatureError::openCircuit;
- }
- else if (temperature > temperatureLimit)
- {
- err = TemperatureError::tooHigh;
- }
+ err = TemperatureError::tooHigh;
}
return err;
}
diff --git a/src/Heating/Sensors/CpuTemperatureSensor.cpp b/src/Heating/Sensors/CpuTemperatureSensor.cpp
new file mode 100644
index 00000000..85ff00ba
--- /dev/null
+++ b/src/Heating/Sensors/CpuTemperatureSensor.cpp
@@ -0,0 +1,31 @@
+/*
+ * CpuTemperatureSensor.cpp
+ *
+ * Created on: 8 Jun 2017
+ * Author: David
+ */
+
+#include "CpuTemperatureSensor.h"
+#include "Platform.h"
+#include "RepRap.h"
+
+#ifndef __RADDS__
+
+CpuTemperatureSensor::CpuTemperatureSensor(unsigned int channel) : TemperatureSensor(channel, "microcontroller embedded temperature sensor")
+{
+}
+
+void CpuTemperatureSensor::Init()
+{
+}
+
+TemperatureError CpuTemperatureSensor::GetTemperature(float& t)
+{
+ float minT, maxT;
+ reprap.GetPlatform().GetMcuTemperatures(minT, t, maxT);
+ return TemperatureError::success;
+}
+
+#endif
+
+// End
diff --git a/src/Heating/Sensors/CpuTemperatureSensor.h b/src/Heating/Sensors/CpuTemperatureSensor.h
new file mode 100644
index 00000000..7e23ac77
--- /dev/null
+++ b/src/Heating/Sensors/CpuTemperatureSensor.h
@@ -0,0 +1,25 @@
+/*
+ * CpuTemperatureSensor.h
+ *
+ * Created on: 8 Jun 2017
+ * Author: David
+ */
+
+#ifndef SRC_HEATING_SENSORS_CPUTEMPERATURESENSOR_H_
+#define SRC_HEATING_SENSORS_CPUTEMPERATURESENSOR_H_
+
+#include "TemperatureSensor.h"
+
+#ifndef __RADDS__
+
+class CpuTemperatureSensor : public TemperatureSensor
+{
+public:
+ CpuTemperatureSensor(unsigned int channel);
+ void Init() override;
+ TemperatureError GetTemperature(float& t) override;
+};
+
+#endif
+
+#endif /* SRC_HEATING_SENSORS_CPUTEMPERATURESENSOR_H_ */
diff --git a/src/Heating/Sensors/CurrentLoopTemperatureSensor.cpp b/src/Heating/Sensors/CurrentLoopTemperatureSensor.cpp
new file mode 100644
index 00000000..388fdbc8
--- /dev/null
+++ b/src/Heating/Sensors/CurrentLoopTemperatureSensor.cpp
@@ -0,0 +1,123 @@
+/*
+ * LinearAdcTemperatureSensor.cpp
+ *
+ * Created on: 8 Jun 2017
+ * Author: David
+ */
+
+#include <Heating/Sensors/CurrentLoopTemperatureSensor.h>
+#include "RepRap.h"
+#include "Platform.h"
+#include "GCodes/GCodeBuffer.h"
+
+const uint32_t MCP3204_Frequency = 1000000; // maximum for MCP3204 is 1MHz @ 2.7V, will be slightly higher at 3.3V
+
+// The MCP3204 samples input data on the rising edge and changes the output data on the rising edge.
+const uint8_t MCP3204_SpiMode = SPI_MODE_0;
+
+// Define the minimum interval between readings
+const uint32_t MinimumReadInterval = 100; // minimum interval between reads, in milliseconds
+
+CurrentLoopTemperatureSensor::CurrentLoopTemperatureSensor(unsigned int channel)
+ : SpiTemperatureSensor(channel, "Current Loop", channel - FirstLinearAdcChannel, MCP3204_SpiMode, MCP3204_Frequency),
+ tempAt4mA(DefaultTempAt4mA), tempAt20mA(DefaultTempAt20mA)
+{
+ CalcDerivedParameters();
+}
+
+// Initialise the linear ADC
+void CurrentLoopTemperatureSensor::Init()
+{
+ InitSpi();
+
+ for (unsigned int i = 0; i < 3; ++i) // try 3 times
+ {
+ TryGetLinearAdcTemperature();
+ if (lastResult == TemperatureError::success)
+ {
+ break;
+ }
+ delay(MinimumReadInterval);
+ }
+
+ lastReadingTime = millis();
+
+ if (lastResult != TemperatureError::success)
+ {
+ reprap.GetPlatform().MessageF(GENERIC_MESSAGE, "Error: failed to initialise daughter board ADC: %s\n", TemperatureErrorString(lastResult));
+ }
+}
+
+// Configure this temperature sensor
+bool CurrentLoopTemperatureSensor::Configure(unsigned int mCode, unsigned int heater, GCodeBuffer& gb, StringRef& reply, bool& error)
+{
+ if (mCode == 305)
+ {
+ bool seen = false;
+ gb.TryGetFValue('L', tempAt4mA, seen);
+ gb.TryGetFValue('H', tempAt20mA, seen);
+ TryConfigureHeaterName(gb, seen);
+
+ if (seen)
+ {
+ CalcDerivedParameters();
+ }
+ else if (!gb.Seen('X'))
+ {
+ CopyBasicHeaterDetails(heater, reply);
+ reply.catf(", temperature range %.1f to %.1fC", tempAt4mA, tempAt20mA);
+ }
+ }
+ return false;
+}
+
+TemperatureError CurrentLoopTemperatureSensor::GetTemperature(float& t)
+{
+ if (!inInterrupt() && millis() - lastReadingTime >= MinimumReadInterval)
+ {
+ TryGetLinearAdcTemperature();
+ }
+
+ t = lastTemperature;
+ return lastResult;
+}
+
+void CurrentLoopTemperatureSensor::CalcDerivedParameters()
+{
+ minLinearAdcTemp = tempAt4mA - 0.25 * (tempAt20mA - tempAt4mA);
+ linearAdcDegCPerCount = (tempAt20mA - minLinearAdcTemp) / 4096.0;
+}
+
+// Try to get a temperature reading from the linear ADC by doing an SPI transaction
+void CurrentLoopTemperatureSensor::TryGetLinearAdcTemperature()
+{
+ // The MCP3204 waits for a high input input bit before it does anything. Call this clock 1.
+ // The next input bit it high for single-ended operation, low for differential. This is clock 2.
+ // The next 3 input bits are the channel selection bits. These are clocks 3..5.
+ // Clock 6 produces a null bit on its trailing edge, which is read by the processor on clock 7.
+ // Clocks 7..18 produce data bits B11..B0 on their trailing edges, which are read by the MCU on the leading edges of clocks 8-19.
+ // If we supply further clocks, then clocks 18..29 are the same data but LSB first, omitting bit 0.
+ // Clocks 30 onwards will be zeros.
+ // So we need to use at least 19 clocks. We round this up to 24 clocks, and we check that the extra 5 bits we receive are the 5 least significant data bits in reverse order.
+
+ static const uint8_t adcData[] = { 0xC0, 0x00, 0x00 }; // start bit, single ended, channel 0
+ uint32_t rawVal;
+ lastResult = DoSpiTransaction(adcData, 3, rawVal);
+ //debugPrintf("ADC data %u\n", rawVal);
+
+ if (lastResult == TemperatureError::success)
+ {
+ const uint32_t adcVal1 = (rawVal >> 5) & ((1 << 13) - 1);
+ const uint32_t adcVal2 = ((rawVal & 1) << 5) | ((rawVal & 2) << 3) | ((rawVal & 4) << 1) | ((rawVal & 8) >> 1) | ((rawVal & 16) >> 3) | ((rawVal & 32) >> 5);
+ if (adcVal1 >= 4096 || adcVal2 != (adcVal1 & ((1 << 6) - 1)))
+ {
+ lastResult = TemperatureError::badResponse;
+ }
+ else
+ {
+ lastTemperature = minLinearAdcTemp + (linearAdcDegCPerCount * (float)adcVal1);
+ }
+ }
+}
+
+// End
diff --git a/src/Heating/Sensors/CurrentLoopTemperatureSensor.h b/src/Heating/Sensors/CurrentLoopTemperatureSensor.h
new file mode 100644
index 00000000..92815f2e
--- /dev/null
+++ b/src/Heating/Sensors/CurrentLoopTemperatureSensor.h
@@ -0,0 +1,35 @@
+/*
+ * LinearAdcTemperatureSensor.h
+ *
+ * Created on: 8 Jun 2017
+ * Author: David
+ */
+
+#ifndef SRC_HEATING_LINEARADCTEMPERATURESENSOR_H_
+#define SRC_HEATING_LINEARADCTEMPERATURESENSOR_H_
+
+#include "SpiTemperatureSensor.h"
+
+class CurrentLoopTemperatureSensor : public SpiTemperatureSensor
+{
+public:
+ CurrentLoopTemperatureSensor(unsigned int channel);
+ bool Configure(unsigned int mCode, unsigned int heater, GCodeBuffer& gb, StringRef& reply, bool& error) override;
+ void Init() override;
+ TemperatureError GetTemperature(float& t) override;
+
+private:
+ void TryGetLinearAdcTemperature();
+ void CalcDerivedParameters();
+
+ // Configurable parameters
+ float tempAt4mA, tempAt20mA;
+
+ // Derived parameters
+ float minLinearAdcTemp, linearAdcDegCPerCount;
+
+ static constexpr float DefaultTempAt4mA = 385.0;
+ static constexpr float DefaultTempAt20mA = 1600.0;
+};
+
+#endif /* SRC_HEATING_LINEARADCTEMPERATURESENSOR_H_ */
diff --git a/src/Heating/Sensors/RtdSensor31865.cpp b/src/Heating/Sensors/RtdSensor31865.cpp
new file mode 100644
index 00000000..35c4d2f7
--- /dev/null
+++ b/src/Heating/Sensors/RtdSensor31865.cpp
@@ -0,0 +1,207 @@
+/*
+ * RtdSensor31865.cpp
+ *
+ * Created on: 8 Jun 2017
+ * Author: David
+ */
+
+#include "RtdSensor31865.h"
+#include "RepRap.h"
+#include "Platform.h"
+#include "Core.h"
+
+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 MAX31865 changes data after the rising edge of CLK, and samples input data on the falling edge.
+// This requires NCPHA = 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]);
+
+RtdSensor31865::RtdSensor31865(unsigned int channel)
+ : SpiTemperatureSensor(channel, "PT100 (MAX31865)", channel - FirstRtdChannel, MAX31865_SpiMode, MAX31865_Frequency)
+{
+}
+
+// Perform the actual hardware initialization for attaching and using this device on the SPI hardware bus.
+void RtdSensor31865::Init()
+{
+ InitSpi();
+
+ 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 RtdSensor31865::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 RtdSensor31865::GetTemperature(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 || tempTable[low - 1] < adcVal);
+ assert(low == NumTempTableEntries || adcVal <= tempTable[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;
+}
+
+// End
diff --git a/src/Heating/Sensors/RtdSensor31865.h b/src/Heating/Sensors/RtdSensor31865.h
new file mode 100644
index 00000000..0ff0a110
--- /dev/null
+++ b/src/Heating/Sensors/RtdSensor31865.h
@@ -0,0 +1,24 @@
+/*
+ * RtdSensor31865.h
+ *
+ * Created on: 8 Jun 2017
+ * Author: David
+ */
+
+#ifndef SRC_HEATING_RTDSENSOR31865_H_
+#define SRC_HEATING_RTDSENSOR31865_H_
+
+#include "SpiTemperatureSensor.h"
+
+class RtdSensor31865 : public SpiTemperatureSensor
+{
+public:
+ RtdSensor31865(unsigned int channel);
+ void Init() override;
+ TemperatureError GetTemperature(float& t) override;
+
+private:
+ TemperatureError TryInitRtd() const;
+};
+
+#endif /* SRC_HEATING_RTDSENSOR31865_H_ */
diff --git a/src/Heating/Sensors/SpiTemperatureSensor.cpp b/src/Heating/Sensors/SpiTemperatureSensor.cpp
new file mode 100644
index 00000000..dbf6c5b7
--- /dev/null
+++ b/src/Heating/Sensors/SpiTemperatureSensor.cpp
@@ -0,0 +1,63 @@
+/*
+ * SpiTemperatureSensor.cpp
+ *
+ * Created on: 8 Jun 2017
+ * Author: David
+ */
+
+#include "SpiTemperatureSensor.h"
+
+SpiTemperatureSensor::SpiTemperatureSensor(unsigned int channel, const char *name, unsigned int relativeChannel, uint8_t spiMode, uint32_t clockFrequency)
+ : TemperatureSensor(channel, name)
+{
+ device.csPin = SpiTempSensorCsPins[relativeChannel];
+ device.spiMode = spiMode;
+ device.clockFrequency = clockFrequency;
+ lastTemperature = 0.0;
+ lastResult = TemperatureError::notInitialised;
+}
+
+void SpiTemperatureSensor::InitSpi()
+{
+ sspi_master_init(&device, 8);
+ lastReadingTime = millis();
+}
+
+// Send and receive 1 to 4 bytes of data and return the result as a single 32-bit word
+TemperatureError SpiTemperatureSensor::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/Heating/Sensors/SpiTemperatureSensor.h b/src/Heating/Sensors/SpiTemperatureSensor.h
new file mode 100644
index 00000000..81399cbf
--- /dev/null
+++ b/src/Heating/Sensors/SpiTemperatureSensor.h
@@ -0,0 +1,27 @@
+/*
+ * SpiTemperatureSensor.h
+ *
+ * Created on: 8 Jun 2017
+ * Author: David
+ */
+
+#ifndef SRC_HEATING_SPITEMPERATURESENSOR_H_
+#define SRC_HEATING_SPITEMPERATURESENSOR_H_
+
+#include "TemperatureSensor.h"
+#include "SharedSpi.h" // for sspi_device
+
+class SpiTemperatureSensor : public TemperatureSensor
+{
+protected:
+ SpiTemperatureSensor(unsigned int channel, const char *name, unsigned int relativeChannel, uint8_t spiMode, uint32_t clockFrequency);
+ void InitSpi();
+ TemperatureError DoSpiTransaction(const uint8_t dataOut[], size_t nbytes, uint32_t& rslt) const;
+
+ sspi_device device;
+ uint32_t lastReadingTime;
+ float lastTemperature;
+ TemperatureError lastResult;
+};
+
+#endif /* SRC_HEATING_SPITEMPERATURESENSOR_H_ */
diff --git a/src/Heating/Sensors/TemperatureSensor.cpp b/src/Heating/Sensors/TemperatureSensor.cpp
new file mode 100644
index 00000000..9fa8637e
--- /dev/null
+++ b/src/Heating/Sensors/TemperatureSensor.cpp
@@ -0,0 +1,120 @@
+#include <Heating/Sensors/CurrentLoopTemperatureSensor.h>
+#include "TemperatureSensor.h"
+#include "Thermistor.h"
+#include "ThermocoupleSensor31855.h"
+#include "RtdSensor31865.h"
+#include "GCodes/GCodeBuffer.h"
+
+#ifndef __RADDS__
+#include "CpuTemperatureSensor.h"
+#endif
+
+#ifdef DUET_NG
+#include "TmcDriverTemperatureSensor.h"
+#endif
+
+// Constructor
+TemperatureSensor::TemperatureSensor(unsigned int chan, const char *t) : sensorChannel(chan), sensorType(t), heaterName(nullptr) {}
+
+// Virtual destructor
+TemperatureSensor::~TemperatureSensor()
+{
+ delete heaterName;
+}
+
+// Set the name - normally called only once
+void TemperatureSensor::SetHeaterName(const char *newName)
+{
+ // Change the heater name in a thread-safe manner
+ const char *oldName = heaterName;
+ heaterName = nullptr;
+ delete oldName;
+
+ if (newName != nullptr)
+ {
+ char *temp = new char[strlen(newName)];
+ strcpy(temp, newName);
+ heaterName = temp;
+ }
+}
+
+// Default implementation of Configure, for sensors that have no configurable parameters
+bool TemperatureSensor::Configure(unsigned int mCode, unsigned int heater, GCodeBuffer& gb, StringRef& reply, bool& error)
+{
+ bool seen = false;
+ if (mCode == 305)
+ {
+ TryConfigureHeaterName(gb, seen);
+ if (!seen && !gb.Seen('X'))
+ {
+ // No parameters provided, so report the current configuration
+ CopyBasicHeaterDetails(heater, reply);
+ }
+ }
+ return seen;
+}
+
+void TemperatureSensor::CopyBasicHeaterDetails(unsigned int heater, StringRef& reply) const
+{
+ reply.printf("Heater %u", heater);
+ if (heaterName != nullptr)
+ {
+ reply.catf(" (%s)", heaterName);
+ }
+ reply.catf(" uses %s sensor channel %u", sensorType, sensorChannel);
+}
+
+// Configure then heater name, if it is provided
+void TemperatureSensor::TryConfigureHeaterName(GCodeBuffer& gb, bool& seen)
+{
+ char buf[MaxHeaterNameLength + 1];
+ bool localSeen = false;
+ gb.TryGetQuotedString('H', buf, ARRAY_SIZE(buf), localSeen);
+ if (localSeen)
+ {
+ SetHeaterName(buf);
+ seen = true;
+ }
+}
+
+// Factory method
+TemperatureSensor *TemperatureSensor::Create(unsigned int channel)
+{
+ TemperatureSensor *ts = nullptr;
+ if (channel < Heaters)
+ {
+ ts = new Thermistor(channel);
+ }
+ else if (FirstThermocoupleChannel <= channel && channel < FirstThermocoupleChannel + MaxSpiTempSensors)
+ {
+ ts = new ThermocoupleSensor31855(channel);
+ }
+ else if (FirstRtdChannel <= channel && channel < FirstRtdChannel + MaxSpiTempSensors)
+ {
+ ts = new RtdSensor31865(channel);
+ }
+ else if (FirstLinearAdcChannel <= channel && channel < FirstLinearAdcChannel + MaxSpiTempSensors)
+ {
+ ts = new CurrentLoopTemperatureSensor(channel);
+ }
+#ifndef __RADDS__
+ else if (channel == CpuTemperatureSenseChannel)
+ {
+ ts = new CpuTemperatureSensor(channel);
+ }
+#endif
+#ifdef DUET_NG
+ else if (channel >= FirstTmcDriversSenseChannel && channel < FirstTmcDriversSenseChannel + 2)
+ {
+ ts = new TmcDriverTemperatureSensor(channel);
+ }
+#endif
+
+ if (ts != nullptr)
+ {
+ ts->Init();
+ }
+ return ts;
+}
+
+// End
diff --git a/src/Heating/Sensors/TemperatureSensor.h b/src/Heating/Sensors/TemperatureSensor.h
new file mode 100644
index 00000000..26aec74e
--- /dev/null
+++ b/src/Heating/Sensors/TemperatureSensor.h
@@ -0,0 +1,55 @@
+#ifndef TEMPERATURESENSOR_H
+#define TEMPERATURESENSOR_H
+
+#include "RepRapFirmware.h"
+#include "Heating/TemperatureError.h" // for result codes
+
+class GCodeBuffer;
+
+class TemperatureSensor
+{
+public:
+ TemperatureSensor(unsigned int chan, const char *type);
+
+ // Configure the sensor from M305 parameters.
+ // If we find any parameters, process them and return true. If an error occurs while processing them, set 'error' to true and write an error message to 'reply.
+ // if we find no relevant parameters, report the current parameters to 'reply' and return 'false'.
+ virtual bool Configure(unsigned int mCode, unsigned int heater, GCodeBuffer& gb, StringRef& reply, bool& error);
+
+ // Initialise or re-initialise the temperature sensor
+ virtual void Init() = 0;
+
+ // Try to get a temperature reading
+ virtual TemperatureError GetTemperature(float& t) = 0;
+
+ // Return the channel number
+ unsigned int GetSensorChannel() const { return sensorChannel; }
+
+ // Return the sensor type
+ const char *GetSensorType() const { return sensorType; }
+
+ // Configure then heater name, if it is provided
+ void TryConfigureHeaterName(GCodeBuffer& gb, bool& seen);
+
+ // Virtual destructor
+ virtual ~TemperatureSensor();
+
+ // Set the name - normally called only once
+ void SetHeaterName(const char *newName);
+
+ // Get the name. Returns nullptr if no name has been assigned.
+ const char *GetHeaterName() const { return heaterName; }
+
+ // Factory method
+ static TemperatureSensor *Create(unsigned int channel);
+
+protected:
+ void CopyBasicHeaterDetails(unsigned int heater, StringRef& reply) const;
+
+private:
+ const unsigned int sensorChannel;
+ const char * const sensorType;
+ const char *heaterName;
+};
+
+#endif // TEMPERATURESENSOR_H
diff --git a/src/Heating/Sensors/Thermistor.cpp b/src/Heating/Sensors/Thermistor.cpp
new file mode 100644
index 00000000..83e967df
--- /dev/null
+++ b/src/Heating/Sensors/Thermistor.cpp
@@ -0,0 +1,139 @@
+/*
+ * Thermistor.cpp
+ *
+ * Created on: 10 Nov 2016
+ * Author: David
+ */
+
+#include "Thermistor.h"
+#include "Platform.h"
+#include "RepRap.h"
+#include "GCodes/GCodeBuffer.h"
+
+// The Steinhart-Hart equation for thermistor resistance is:
+// 1/T = A + B ln(R) + C [ln(R)]^3
+//
+// The simplified (beta) equation assumes C=0 and is:
+// 1/T = A + (1/Beta) ln(R)
+//
+// The parameters that can be configured in RRF are R25 (the resistance at 25C), Beta, and optionally C.
+
+// Create an instance with default values
+Thermistor::Thermistor(unsigned int channel)
+ : TemperatureSensor(channel - FirstThermistorChannel, "Thermistor"), adcLowOffset(0), adcHighOffset(0)
+{
+ r25 = (channel == FirstThermistorChannel) ? BED_R25 : EXT_R25;
+ beta = (channel == FirstThermistorChannel) ? BED_BETA : EXT_BETA;
+ shC = (channel == FirstThermistorChannel) ? BED_SHC : EXT_SHC;
+ seriesR = THERMISTOR_SERIES_RS;
+ CalcDerivedParameters();
+}
+
+void Thermistor::Init()
+{
+ reprap.GetPlatform().GetThermistorFilter(GetSensorChannel() - FirstThermistorChannel).Init((1 << AdcBits) - 1);
+}
+
+// Configure the temperature sensor
+bool Thermistor::Configure(unsigned int mCode, unsigned int heater, GCodeBuffer& gb, StringRef& reply, bool& error)
+{
+ bool seen = false;
+ if (mCode == 305)
+ {
+ // We must set the 25C resistance and beta together in order to calculate Rinf. Check for these first.
+
+ gb.TryGetFValue('T', r25, seen);
+ gb.TryGetFValue('B', beta, seen);
+ gb.TryGetFValue('C', shC, seen);
+ gb.TryGetFValue('R', seriesR, seen);
+ if (seen)
+ {
+ CalcDerivedParameters();
+ }
+
+ if (gb.Seen('L'))
+ {
+ adcLowOffset = (int8_t)constrain<int>(gb.GetIValue(), -100, 100);
+ seen = true;
+ }
+ if (gb.Seen('H'))
+ {
+ adcHighOffset = (int8_t)constrain<int>(gb.GetIValue(), -100, 100);
+ seen = true;
+ }
+
+ TryConfigureHeaterName(gb, seen);
+
+ if (!seen && !gb.Seen('X'))
+ {
+ CopyBasicHeaterDetails(heater, reply);
+ reply.catf(", T:%.1f B:%.1f C:%.2e R:%.1f L:%d H:%d",
+ r25, beta, shC, seriesR, adcLowOffset, adcHighOffset);
+ }
+ }
+
+ return seen;
+}
+
+// Get the temperature
+TemperatureError Thermistor::GetTemperature(float& t)
+{
+ const volatile ThermistorAveragingFilter& filter = reprap.GetPlatform().GetThermistorFilter(GetSensorChannel() - FirstThermistorChannel);
+ if (filter.IsValid())
+ {
+ const int32_t averagedReading = filter.GetSum()/(ThermistorAverageReadings >> Thermistor::AdcOversampleBits);
+ const float temp = CalcTemperature(averagedReading);
+
+ if (temp < MinimumConnectedTemperature)
+ {
+ // thermistor is disconnected
+ t = ABS_ZERO;
+ return TemperatureError::openCircuit;
+ }
+
+ t = temp;
+ return TemperatureError::success;
+ }
+
+ // Filter is not ready yet
+ t = BAD_ERROR_TEMPERATURE;
+ return TemperatureError::busBusy;
+}
+
+// Calculate temperature from an ADC reading in the range 0..1
+float Thermistor::CalcTemperature(int32_t adcReading) const
+{
+ const float denom = (float)(AdcRange + (int)adcHighOffset - adcReading) - 0.5;
+ if (denom <= 0.0)
+ {
+ return ABS_ZERO;
+ }
+ const float resistance = seriesR * ((float)(adcReading - (int)adcLowOffset) + 0.5)/denom;
+ const float logResistance = log(resistance);
+ const float recipT = shA + shB * logResistance + shC * logResistance * logResistance * logResistance;
+ return (recipT > 0.0) ? (1.0/recipT) + ABS_ZERO : BAD_ERROR_TEMPERATURE;
+}
+
+// Calculate expected ADC reading at a particular temperature, rounded down as the ADC does
+int32_t Thermistor::CalcAdcReading(float temperature) const
+{
+ const double bDFiv3c = shB/(3.0 * shC);
+ const double halfY = (shA - 1.0/(temperature - ABS_ZERO))/(2.0 * shC);
+ const double x = sqrt((bDFiv3c * bDFiv3c * bDFiv3c) + (halfY * halfY));
+ const double oneThird = 1.0/3.0;
+ const float resistance = exp(pow(x - halfY, oneThird) - pow(x + halfY, oneThird));
+ const float fraction = resistance/(resistance + seriesR);
+ const int32_t actualAdcRange = AdcRange + (int)adcHighOffset - (int)adcLowOffset;
+ const int32_t val = (int32_t)(fraction * (float)actualAdcRange) + (int)adcLowOffset;
+ return constrain<int>(val, 0, AdcRange - 1);
+}
+
+// Calculate shA and shB from the other parameters
+void Thermistor::CalcDerivedParameters()
+{
+ shB = 1.0/beta;
+ const double lnR25 = log(r25);
+ shA = 1.0/(25.0 - ABS_ZERO) - shB * lnR25 - shC * lnR25 * lnR25 * lnR25;
+}
+
+// End
diff --git a/src/Heating/Thermistor.h b/src/Heating/Sensors/Thermistor.h
index eabc45f9..1c002c34 100644
--- a/src/Heating/Thermistor.h
+++ b/src/Heating/Sensors/Thermistor.h
@@ -8,7 +8,7 @@
#ifndef SRC_HEATING_THERMISTOR_H_
#define SRC_HEATING_THERMISTOR_H_
-#include "RepRapFirmware.h"
+#include "TemperatureSensor.h"
// The Steinhart-Hart equation for thermistor resistance is:
// 1/T = A + B ln(R) + C [ln(R)]^3
@@ -18,10 +18,15 @@
//
// The parameters that can be configured in RRF are R25 (the resistance at 25C), Beta, and optionally C.
-class Thermistor
+class Thermistor : public TemperatureSensor
{
public:
- Thermistor(); // create an instance with default values
+ Thermistor(unsigned int channel); // create an instance with default values
+ bool Configure(unsigned int mCode, unsigned int heater, GCodeBuffer& gb, StringRef& reply, bool& error) override; // configure the sensor from M305 parameters
+ void Init() override;
+ TemperatureError GetTemperature(float& t) override;
+
+private:
float CalcTemperature(int32_t adcReading) const; // calculate temperature from an ADC reading in the range 0..1
int32_t CalcAdcReading(float temperature) const; // calculate expected ADC reading at a particular temperature
@@ -32,14 +37,12 @@ public:
int8_t GetLowOffset() const { return adcLowOffset; }
int8_t GetHighOffset() const { return adcHighOffset; }
- void SetParameters(float p_r25, float p_beta, float p_shC, float p_seriesR); // initialise the main parameters
void SetLowOffset(int8_t p_offset) { adcLowOffset = p_offset; }
void SetHighOffset(int8_t p_offset) { adcHighOffset = p_offset; }
// For the theory behind ADC oversampling, see http://www.atmel.com/Images/doc8003.pdf
static const unsigned int AdcOversampleBits = 1 ; // we use 1-bit oversampling
-private:
void CalcDerivedParameters(); // calculate shA and shB
// The following are configurable parameters
diff --git a/src/Heating/Sensors/ThermocoupleSensor31855.cpp b/src/Heating/Sensors/ThermocoupleSensor31855.cpp
new file mode 100644
index 00000000..10d6f2bc
--- /dev/null
+++ b/src/Heating/Sensors/ThermocoupleSensor31855.cpp
@@ -0,0 +1,156 @@
+/*
+ * ThermocoupleSensor31855.cpp
+ *
+ * Created on: 8 Jun 2017
+ * Author: David
+ */
+
+// 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.
+//
+#include "ThermocoupleSensor31855.h"
+#include "RepRap.h"
+#include "Platform.h"
+#include "Core.h"
+
+const uint32_t MAX31855_Frequency = 4000000; // maximum for MAX31855 is 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 CLK, 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.
+const uint8_t MAX31855_SpiMode = SPI_MODE_0;
+
+// Define the minimum interval between readings
+const uint32_t MinimumReadInterval = 100; // minimum interval between reads, in milliseconds
+
+ThermocoupleSensor31855::ThermocoupleSensor31855(unsigned int channel)
+ : SpiTemperatureSensor(channel, "Thermocouple (MAX31855)", channel - FirstThermocoupleChannel, MAX31855_SpiMode, MAX31855_Frequency)
+{
+}
+
+// Perform the actual hardware initialization for attaching and using this device on the SPI hardware bus.
+void ThermocoupleSensor31855::Init()
+{
+ InitSpi();
+ lastReadingTime = millis();
+}
+
+TemperatureError ThermocoupleSensor31855::GetTemperature(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;
+}
+
+// End
diff --git a/src/Heating/Sensors/ThermocoupleSensor31855.h b/src/Heating/Sensors/ThermocoupleSensor31855.h
new file mode 100644
index 00000000..0b877f62
--- /dev/null
+++ b/src/Heating/Sensors/ThermocoupleSensor31855.h
@@ -0,0 +1,21 @@
+/*
+ * ThermocoupleSensor31855.h
+ *
+ * Created on: 8 Jun 2017
+ * Author: David
+ */
+
+#ifndef SRC_HEATING_THERMOCOUPLESENSOR31855_H_
+#define SRC_HEATING_THERMOCOUPLESENSOR31855_H_
+
+#include "SpiTemperatureSensor.h"
+
+class ThermocoupleSensor31855 : public SpiTemperatureSensor
+{
+public:
+ ThermocoupleSensor31855(unsigned int channel);
+ void Init() override;
+ TemperatureError GetTemperature(float& t) override;
+};
+
+#endif /* SRC_HEATING_THERMOCOUPLESENSOR31855_H_ */
diff --git a/src/Heating/Sensors/TmcDriverTemperatureSensor.cpp b/src/Heating/Sensors/TmcDriverTemperatureSensor.cpp
new file mode 100644
index 00000000..fe2ccbf9
--- /dev/null
+++ b/src/Heating/Sensors/TmcDriverTemperatureSensor.cpp
@@ -0,0 +1,30 @@
+/*
+ * TmcDriverTemperatureSensor.cpp
+ *
+ * Created on: 8 Jun 2017
+ * Author: David
+ */
+
+#include "TmcDriverTemperatureSensor.h"
+#include "Platform.h"
+#include "RepRap.h"
+
+#ifdef DUET_NG
+
+TmcDriverTemperatureSensor::TmcDriverTemperatureSensor(unsigned int channel) : TemperatureSensor(channel, "TMC2660 temperature warnings")
+{
+}
+
+void TmcDriverTemperatureSensor::Init()
+{
+}
+
+TemperatureError TmcDriverTemperatureSensor::GetTemperature(float& t)
+{
+ t = reprap.GetPlatform().GetTmcDriversTemperature(GetSensorChannel() - FirstTmcDriversSenseChannel);
+ return TemperatureError::success;
+}
+
+#endif
+
+// End
diff --git a/src/Heating/Sensors/TmcDriverTemperatureSensor.h b/src/Heating/Sensors/TmcDriverTemperatureSensor.h
new file mode 100644
index 00000000..be18dd5c
--- /dev/null
+++ b/src/Heating/Sensors/TmcDriverTemperatureSensor.h
@@ -0,0 +1,25 @@
+/*
+ * TmcDriverTemperatureSensor.h
+ *
+ * Created on: 8 Jun 2017
+ * Author: David
+ */
+
+#ifndef SRC_HEATING_SENSORS_TMCDRIVERTEMPERATURESENSOR_H_
+#define SRC_HEATING_SENSORS_TMCDRIVERTEMPERATURESENSOR_H_
+
+#include "TemperatureSensor.h"
+
+#ifdef DUET_NG
+
+class TmcDriverTemperatureSensor : public TemperatureSensor
+{
+public:
+ TmcDriverTemperatureSensor(unsigned int channel);
+ void Init() override;
+ TemperatureError GetTemperature(float& t) override;
+};
+
+#endif
+
+#endif /* SRC_HEATING_SENSORS_TMCDRIVERTEMPERATURESENSOR_H_ */
diff --git a/src/Heating/TemperatureError.cpp b/src/Heating/TemperatureError.cpp
index 294c37bd..f50d1c0c 100644
--- a/src/Heating/TemperatureError.cpp
+++ b/src/Heating/TemperatureError.cpp
@@ -23,6 +23,8 @@ const char* TemperatureErrorString(TemperatureError err)
case TemperatureError::busBusy: return "sensor bus busy";
case TemperatureError::badResponse: return "bad response from sensor";
case TemperatureError::unknownChannel: return "unknown temperature sensor channel";
+ case TemperatureError::notInitialised: return "sensor not initialised";
+ case TemperatureError::unknownHeater: return "unknown heater";
default: return "unknown temperature sense error";
}
}
diff --git a/src/Heating/TemperatureError.h b/src/Heating/TemperatureError.h
index c9f3a09a..24ae9a41 100644
--- a/src/Heating/TemperatureError.h
+++ b/src/Heating/TemperatureError.h
@@ -24,7 +24,9 @@ enum class TemperatureError : uint8_t
hardwareError,
busBusy,
badResponse,
- unknownChannel
+ unknownChannel,
+ notInitialised,
+ unknownHeater
};
const char* TemperatureErrorString(TemperatureError err);
diff --git a/src/Heating/TemperatureSensor.cpp b/src/Heating/TemperatureSensor.cpp
deleted file mode 100644
index 74a9c399..00000000
--- a/src/Heating/TemperatureSensor.cpp
+++ /dev/null
@@ -1,449 +0,0 @@
-#include "TemperatureSensor.h"
-#include "Platform.h"
-#include "RepRap.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
-
-const uint32_t MAX31855_Frequency = 4000000; // maximum for MAX31855 is 5MHz
-const uint32_t MAX31865_Frequency = 4000000; // maximum for MAX31865 is also 5MHz
-const uint32_t MCP3204_Frequency = 1000000; // maximum for MCP3204 is 1MHz @ 2.7V, will be slightly higher at 3.3V
-
-// 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 CLK, 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 CLK, and samples input data on the falling edge.
-// This requires NCPHA = 0.
-// The MCP3204 samples input data on the rising edge and changes the output data on the rising edge.
-
-const uint8_t MAX31855_SpiMode = SPI_MODE_0;
-const uint8_t MAX31865_SpiMode = SPI_MODE_1;
-const uint8_t MCP3204_SpiMode = SPI_MODE_0;
-
-// 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;
- 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;
- 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;
-}
-
-// Initialise the linear ADC
-void TemperatureSensor::InitLinearAdc(uint8_t cs)
-{
- device.csPin = cs;
- device.spiMode = MCP3204_SpiMode;
- device.clockFrequency = MCP3204_Frequency;
- sspi_master_init(&device, 8);
-
- for (unsigned int i = 0; i < 3; ++i) // try 3 times
- {
- TryGetLinearAdcTemperature();
- if (lastResult == TemperatureError::success)
- {
- break;
- }
- delay(MinimumReadInterval);
- }
-
- lastReadingTime = millis();
-
- if (lastResult != TemperatureError::success)
- {
- reprap.GetPlatform().MessageF(GENERIC_MESSAGE, "Error: failed to initialise daughter board ADC: %s\n", TemperatureErrorString(lastResult));
- }
-
-}
-
-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 || tempTable[low - 1] < adcVal);
- assert(low == NumTempTableEntries || adcVal <= tempTable[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;
-}
-
-TemperatureError TemperatureSensor::GetLinearAdcTemperature(float& t)
-{
- if (!inInterrupt() && millis() - lastReadingTime >= MinimumReadInterval)
- {
- TryGetLinearAdcTemperature();
- }
-
- t = lastTemperature;
- return lastResult;
-}
-
-// Try to get a temperature reading from the linear ADC by doing an SPI transaction
-void TemperatureSensor::TryGetLinearAdcTemperature()
-{
- // The MCP3204 waits for a high input input bit before it does anything. Call this clock 1.
- // The next input bit it high for single-ended operation, low for differential. This is clock 2.
- // The next 3 input bits are the channel selection bits. These are clocks 3..5.
- // Clock 6 produces a null bit on its trailing edge, which is read by the processor on clock 7.
- // Clocks 7..18 produce data bits B11..B0 on their trailing edges, which are read by the MCU on the leading edges of clocks 8-19.
- // If we supply further clocks, then clocks 18..29 are the same data but LSB first, omitting bit 0.
- // Clocks 30 onwards will be zeros.
- // So we need to use at least 19 clocks. We round this up to 24 clocks, and we check that the extra 5 bits we receive are the 5 least significant data bits in reverse order.
-
- static const uint8_t adcData[] = { 0xC0, 0x00, 0x00 }; // start bit, single ended, channel 0
- uint32_t rawVal;
- lastResult = DoSpiTransaction(adcData, 3, rawVal);
- //debugPrintf("ADC data %u\n", rawVal);
-
- if (lastResult == TemperatureError::success)
- {
- const uint32_t adcVal1 = (rawVal >> 5) & ((1 << 13) - 1);
- const uint32_t adcVal2 = ((rawVal & 1) << 5) | ((rawVal & 2) << 3) | ((rawVal & 4) << 1) | ((rawVal & 8) >> 1) | ((rawVal & 16) >> 3) | ((rawVal & 32) >> 5);
- if (adcVal1 >= 4096 || adcVal2 != (adcVal1 & ((1 << 6) - 1)))
- {
- lastResult = TemperatureError::badResponse;
- }
- else
- {
- lastTemperature = MinLinearAdcTemp + (LinearAdcDegCPerCount * (float)adcVal1);
- }
- }
-}
-
-// 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/Heating/TemperatureSensor.h b/src/Heating/TemperatureSensor.h
deleted file mode 100644
index d54338df..00000000
--- a/src/Heating/TemperatureSensor.h
+++ /dev/null
@@ -1,33 +0,0 @@
-#ifndef TEMPERATURESENSOR_H
-#define TEMPERATURESENSOR_H
-
-#include "RepRapFirmware.h"
-#include "TemperatureError.h" // for result codes
-#include "SharedSpi.h" // for sspi_device
-
-class TemperatureSensor
-{
-public:
- TemperatureSensor() {}
- void InitThermocouple(uint8_t cs);
- void InitRtd(uint8_t cs);
- void InitLinearAdc(uint8_t cs);
- TemperatureError GetThermocoupleTemperature(float& t);
- TemperatureError GetRtdTemperature(float& t);
- TemperatureError GetLinearAdcTemperature(float& t);
-
-private:
- TemperatureError DoSpiTransaction(const uint8_t dataOut[], size_t nbytes, uint32_t& rslt) const;
- TemperatureError TryInitRtd() const;
- void TryGetLinearAdcTemperature();
-
- sspi_device device;
- uint32_t lastReadingTime;
- float lastTemperature;
- TemperatureError lastResult;
-
- static constexpr float MinLinearAdcTemp = 385.0 - (1600.0 - 385.0) * (4.0/16.0);
- static constexpr float LinearAdcDegCPerCount = (1600.0 - 385.0)/3200.0;
-};
-
-#endif // TEMPERATURESENSOR_H
diff --git a/src/Heating/Thermistor.cpp b/src/Heating/Thermistor.cpp
deleted file mode 100644
index d8357573..00000000
--- a/src/Heating/Thermistor.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Thermistor.cpp
- *
- * Created on: 10 Nov 2016
- * Author: David
- */
-
-#include "Thermistor.h"
-
-// The Steinhart-Hart equation for thermistor resistance is:
-// 1/T = A + B ln(R) + C [ln(R)]^3
-//
-// The simplified (beta) equation assumes C=0 and is:
-// 1/T = A + (1/Beta) ln(R)
-//
-// The parameters that can be configured in RRF are R25 (the resistance at 25C), Beta, and optionally C.
-
-// Create an instance with default values
-Thermistor::Thermistor() : adcLowOffset(0), adcHighOffset(0)
-{
- SetParameters(EXT_R25, EXT_BETA, EXT_SHC, THERMISTOR_SERIES_RS);
-}
-
-// Initialise the instance
-void Thermistor::SetParameters(float p_r25, float p_beta, float p_shC, float p_seriesR)
-{
- r25 = p_r25;
- beta = p_beta;
- shC = p_shC;
- seriesR = p_seriesR;
- CalcDerivedParameters();
-}
-
-// Calculate temperature from an ADC reading in the range 0..1
-float Thermistor::CalcTemperature(int32_t adcReading) const
-{
- const float denom = (float)(AdcRange + (int)adcHighOffset - adcReading) - 0.5;
- if (denom <= 0.0)
- {
- return ABS_ZERO;
- }
- const float resistance = seriesR * ((float)(adcReading - (int)adcLowOffset) + 0.5)/denom;
- const float logResistance = log(resistance);
- const float recipT = shA + shB * logResistance + shC * logResistance * logResistance * logResistance;
- return (recipT > 0.0) ? (1.0/recipT) + ABS_ZERO : BAD_ERROR_TEMPERATURE;
-}
-
-// Calculate expected ADC reading at a particular temperature, rounded down as the ADC does
-int32_t Thermistor::CalcAdcReading(float temperature) const
-{
- const double bDFiv3c = shB/(3.0 * shC);
- const double halfY = (shA - 1.0/(temperature - ABS_ZERO))/(2.0 * shC);
- const double x = sqrt((bDFiv3c * bDFiv3c * bDFiv3c) + (halfY * halfY));
- const double oneThird = 1.0/3.0;
- const float resistance = exp(pow(x - halfY, oneThird) - pow(x + halfY, oneThird));
- const float fraction = resistance/(resistance + seriesR);
- const int32_t actualAdcRange = AdcRange + (int)adcHighOffset - (int)adcLowOffset;
- const int32_t val = (int32_t)(fraction * (float)actualAdcRange) + (int)adcLowOffset;
- return constrain<int>(val, 0, AdcRange - 1);
-}
-
-// Calculate shA and shB from the other parameters
-void Thermistor::CalcDerivedParameters()
-{
- shB = 1.0/beta;
- const double lnR25 = log(r25);
- shA = 1.0/(25.0 - ABS_ZERO) - shB * lnR25 - shC * lnR25 * lnR25 * lnR25;
-}
-
-// End