Support max31855 thermocouple over spi.
authorhakla <hakan.langemark@gmail.com>
Mon, 14 Apr 2014 18:21:48 +0000 (20:21 +0200)
committerhakla <hakan.langemark@gmail.com>
Mon, 14 Apr 2014 18:21:48 +0000 (20:21 +0200)
src/modules/tools/temperaturecontrol/TempSensor.h [new file with mode: 0644]
src/modules/tools/temperaturecontrol/TemperatureControl.cpp
src/modules/tools/temperaturecontrol/TemperatureControl.h
src/modules/tools/temperaturecontrol/Thermistor.cpp [new file with mode: 0644]
src/modules/tools/temperaturecontrol/Thermistor.h [new file with mode: 0644]
src/modules/tools/temperaturecontrol/max31855.cpp [new file with mode: 0644]
src/modules/tools/temperaturecontrol/max31855.h [new file with mode: 0644]

diff --git a/src/modules/tools/temperaturecontrol/TempSensor.h b/src/modules/tools/temperaturecontrol/TempSensor.h
new file mode 100644 (file)
index 0000000..592acd7
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+      this file is part of smoothie (http://smoothieware.org/). the motion control part is heavily based on grbl (https://github.com/simen/grbl).
+      smoothie is free software: you can redistribute it and/or modify it under the terms of the gnu general public license as published by the free software foundation, either version 3 of the license, or (at your option) any later version.
+      smoothie is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. see the gnu general public license for more details.
+      you should have received a copy of the gnu general public license along with smoothie. if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef tempsensor_h
+#define tempsensor_h
+
+class TempSensor
+{
+public:
+               // Load config parameters using provided "base" names.
+               virtual void UpdateConfig(uint16_t module_checksum, uint16_t name_checksum) = 0;
+
+               // Return temperature in degrees Celsius.
+        virtual float get_temperature() = 0;
+
+               // Make sure the interface provides a destructor.
+               virtual ~TempSensor() {}
+};
+
+#endif
index 1a8214b..d2ff569 100644 (file)
@@ -13,7 +13,6 @@
 #include "TemperatureControl.h"
 #include "TemperatureControlPool.h"
 #include "libs/Pin.h"
-#include "libs/Median.h"
 #include "modules/robot/Conveyor.h"
 #include "PublicDataRequest.h"
 #include "TemperatureControlPublicAccess.h"
 #include "Config.h"
 #include "checksumm.h"
 #include "Gcode.h"
-#include "Adc.h"
 #include "SlowTicker.h"
 #include "Pauser.h"
 #include "ConfigValue.h"
 #include "TemperatureControl.h"
 #include "PID_Autotuner.h"
 
+// Temp sensor implementations:
+#include "Thermistor.h"
+#include "max31855.h" 
+
 #include "MRI_Hooks.h"
 
 #define UNDEFINED -1
 
-#define thermistor_checksum                CHECKSUM("thermistor")
-#define r0_checksum                        CHECKSUM("r0")
+#define sensor_checksum                    CHECKSUM("sensor")
+
 #define readings_per_second_checksum       CHECKSUM("readings_per_second")
 #define max_pwm_checksum                   CHECKSUM("max_pwm")
 #define pwm_frequency_checksum             CHECKSUM("pwm_frequency")
 #define bang_bang_checksum                 CHECKSUM("bang_bang")
 #define hysteresis_checksum                CHECKSUM("hysteresis")
-#define t0_checksum                        CHECKSUM("t0")
-#define beta_checksum                      CHECKSUM("beta")
-#define vadc_checksum                      CHECKSUM("vadc")
-#define vcc_checksum                       CHECKSUM("vcc")
-#define r1_checksum                        CHECKSUM("r1")
-#define r2_checksum                        CHECKSUM("r2")
-#define thermistor_pin_checksum            CHECKSUM("thermistor_pin")
 #define heater_pin_checksum                CHECKSUM("heater_pin")
 
 #define get_m_code_checksum                CHECKSUM("get_m_code")
@@ -89,7 +84,7 @@ void TemperatureControl::on_module_loaded(){
 
 void TemperatureControl::on_main_loop(void* argument){
     if (this->min_temp_violated) {
-        THEKERNEL->streams->printf("Error: MINTEMP triggered on P%d.%d! check your thermistors!\n", this->thermistor_pin.port_number, this->thermistor_pin.pin);
+        THEKERNEL->streams->printf("Error: MINTEMP triggered. Check your temperature sensors!\n");
         this->min_temp_violated = false;
     }
 }
@@ -105,44 +100,28 @@ void TemperatureControl::on_config_reload(void* argument){
 
     this->designator          = THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, designator_checksum)->by_default(string("T"))->as_string();
 
-    // Values are here : http://reprap.org/wiki/Thermistor
-    this->r0   = 100000;
-    this->t0   = 25;
-    this->beta = 4066;
-    this->r1   = 0;
-    this->r2   = 4700;
-
-    // Preset values for various common types of thermistors
-    ConfigValue* thermistor = THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, thermistor_checksum);
-    if(       thermistor->as_string().compare("EPCOS100K"    ) == 0 ){ // Default
-    }else if( thermistor->as_string().compare("RRRF100K"     ) == 0 ){ this->beta = 3960;
-    }else if( thermistor->as_string().compare("RRRF10K"      ) == 0 ){ this->beta = 3964; this->r0 = 10000; this->r1 = 680; this->r2 = 1600;
-    }else if( thermistor->as_string().compare("Honeywell100K") == 0 ){ this->beta = 3974;
-    }else if( thermistor->as_string().compare("Semitec"      ) == 0 ){ this->beta = 4267;
-    }else if( thermistor->as_string().compare("HT100K"       ) == 0 ){ this->beta = 3990; }
-
-    // Preset values are overriden by specified values
-    this->r0 =                  THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, r0_checksum  )->by_default(this->r0  )->as_number();               // Stated resistance eg. 100K
-    this->t0 =                  THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, t0_checksum  )->by_default(this->t0  )->as_number();               // Temperature at stated resistance, eg. 25C
-    this->beta =                THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, beta_checksum)->by_default(this->beta)->as_number();               // Thermistor beta rating. See http://reprap.org/bin/view/Main/MeasuringThermistorBeta
-    this->r1 =                  THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, r1_checksum  )->by_default(this->r1  )->as_number();
-    this->r2 =                  THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, r2_checksum  )->by_default(this->r2  )->as_number();
-
+       // For backward compatibility, default to a thermistor sensor.
+       std::string sensor_type = THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, sensor_checksum)->by_default("thermistor")->as_string();
+
+       // Instantiate correct sensor (TBD: TempSensor factory?)
+       if(sensor_type.compare("thermistor") == 0)
+       {
+               sensor.reset(new Thermistor());
+       }
+       else if(sensor_type.compare("max31855") == 0)
+       {
+               sensor.reset(new Max31855());
+       }
+
+       sensor->UpdateConfig(temperature_control_checksum, this->name_checksum);
+       
     this->preset1 =             THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, preset1_checksum)->by_default(0)->as_number();
     this->preset2 =             THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, preset2_checksum)->by_default(0)->as_number();
 
 
-    // Thermistor math
-    j = (1.0 / beta);
-    k = (1.0 / (t0 + 273.15));
-
     // sigma-delta output modulation
     this->o = 0;
 
-    // Thermistor pin for ADC readings
-    this->thermistor_pin.from_string(THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, thermistor_pin_checksum )->required()->as_string());
-    THEKERNEL->adc->enable_pin(&thermistor_pin);
-
     // Heater pin
     this->heater_pin.from_string(    THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, heater_pin_checksum)->required()->as_string())->as_output();
     this->heater_pin.max_pwm(        THEKERNEL->config->value(temperature_control_checksum, this->name_checksum, max_pwm_checksum)->by_default(255)->as_number() );
@@ -307,24 +286,12 @@ float TemperatureControl::get_temperature(){
     return last_reading;
 }
 
-float TemperatureControl::adc_value_to_temperature(int adc_value)
-{
-    if ((adc_value == 4095) || (adc_value == 0))
-        return INFINITY;
-    float r = r2 / ((4095.0 / adc_value) - 1.0);
-    if (r1 > 0)
-        r = (r1 * r) / (r1 - r);
-    return (1.0 / (k + (j * log(r / r0)))) - 273.15;
-}
-
 uint32_t TemperatureControl::thermistor_read_tick(uint32_t dummy){
-    int r = new_thermistor_reading();
-
-    float temperature = adc_value_to_temperature(r);
+    float temperature = sensor->get_temperature();
 
     if (target_temperature > 0)
     {
-        if ((r <= 1) || (r >= 4094))
+        if ((temperature <= 1) || (temperature >= 500))
         {
             this->min_temp_violated = true;
             target_temperature = UNDEFINED;
@@ -396,21 +363,6 @@ void TemperatureControl::pid_process(float temperature)
     this->lastInput= temperature;
 }
 
-int TemperatureControl::new_thermistor_reading()
-{
-    int last_raw = THEKERNEL->adc->read(&thermistor_pin);
-    if (queue.size() >= queue.capacity()) {
-        uint16_t l;
-        queue.pop_front(l);
-    }
-    uint16_t r = last_raw;
-    queue.push_back(r);
-    for (int i=0; i<queue.size(); i++)
-      median_buffer[i] = *queue.get_ref(i);
-    uint16_t m = median_buffer[quick_median(median_buffer, queue.size())];
-    return m;
-}
-
 void TemperatureControl::on_second_tick(void* argument)
 {
     if (waiting)
index d7143f2..751c684 100644 (file)
 
 #include "Module.h"
 #include "Pwm.h"
-#include <math.h>
-
-#include "RingBuffer.h"
-
-#define QUEUE_LEN 8
+#include "TempSensor.h"
+#include <memory> // for auto_ptr
 
 class TemperatureControlPool;
 
@@ -33,17 +30,14 @@ class TemperatureControl : public Module {
         void on_set_public_data(void* argument);
 
         void set_desired_temperature(float desired_temperature);
-        float get_temperature();
-        float adc_value_to_temperature(int adc_value);
-        uint32_t thermistor_read_tick(uint32_t dummy);
-        int new_thermistor_reading();
-
 
         int pool_index;
         TemperatureControlPool *pool;
         friend class PID_Autotuner;
 
+               float get_temperature();
     private:
+               uint32_t thermistor_read_tick(uint32_t dummy);
         void pid_process(float);
 
         float target_temperature;
@@ -51,16 +45,8 @@ class TemperatureControl : public Module {
         float preset1;
         float preset2;
 
-        // Thermistor computation settings
-        float r0;
-        float t0;
-        int r1;
-        int r2;
-        float beta;
-        float j;
-        float k;
-
-
+               std::auto_ptr<TempSensor> sensor;
+               
         // PID runtime
         float i_max;
 
@@ -71,13 +57,8 @@ class TemperatureControl : public Module {
         float acceleration_factor;
         float readings_per_second;
 
-        RingBuffer<uint16_t,QUEUE_LEN> queue;  // Queue of readings
-        uint16_t median_buffer[QUEUE_LEN];
-        int running_total;
-
         uint16_t name_checksum;
 
-        Pin  thermistor_pin;
         Pwm  heater_pin;
 
         bool use_bangbang;
@@ -90,7 +71,6 @@ class TemperatureControl : public Module {
 
         string designator;
 
-
         void setPIDp(float p);
         void setPIDi(float i);
         void setPIDd(float d);
diff --git a/src/modules/tools/temperaturecontrol/Thermistor.cpp b/src/modules/tools/temperaturecontrol/Thermistor.cpp
new file mode 100644 (file)
index 0000000..ced785a
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+      This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+      Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+      Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+      You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "libs/Kernel.h"
+#include <math.h>
+#include "libs/Pin.h"
+#include "Config.h"
+#include "checksumm.h"
+#include "Adc.h"
+#include "ConfigValue.h"
+#include "libs/Median.h"
+#include "Thermistor.h"
+
+#include "MRI_Hooks.h"
+
+#define UNDEFINED -1
+
+#define thermistor_checksum                CHECKSUM("thermistor")
+#define r0_checksum                        CHECKSUM("r0")
+#define t0_checksum                        CHECKSUM("t0")
+#define beta_checksum                      CHECKSUM("beta")
+#define vadc_checksum                      CHECKSUM("vadc")
+#define vcc_checksum                       CHECKSUM("vcc")
+#define r1_checksum                        CHECKSUM("r1")
+#define r2_checksum                        CHECKSUM("r2")
+#define thermistor_pin_checksum            CHECKSUM("thermistor_pin")
+
+Thermistor::Thermistor()
+{
+}
+
+Thermistor::~Thermistor()
+{
+}
+
+// Get configuration from the config file
+void Thermistor::UpdateConfig(uint16_t module_checksum, uint16_t name_checksum)
+{
+    // Values are here : http://reprap.org/wiki/Thermistor
+    this->r0   = 100000;
+    this->t0   = 25;
+    this->beta = 4066;
+    this->r1   = 0;
+    this->r2   = 4700;
+
+    // Preset values for various common types of thermistors
+    ConfigValue* thermistor = THEKERNEL->config->value(module_checksum, name_checksum, thermistor_checksum);
+    if(       thermistor->as_string().compare("EPCOS100K"    ) == 0 ){ // Default
+    }else if( thermistor->as_string().compare("RRRF100K"     ) == 0 ){ this->beta = 3960;
+    }else if( thermistor->as_string().compare("RRRF10K"      ) == 0 ){ this->beta = 3964; this->r0 = 10000; this->r1 = 680; this->r2 = 1600;
+    }else if( thermistor->as_string().compare("Honeywell100K") == 0 ){ this->beta = 3974;
+    }else if( thermistor->as_string().compare("Semitec"      ) == 0 ){ this->beta = 4267;
+    }else if( thermistor->as_string().compare("HT100K"       ) == 0 ){ this->beta = 3990; }
+
+    // Preset values are overriden by specified values
+    this->r0 =                  THEKERNEL->config->value(module_checksum, name_checksum, r0_checksum  )->by_default(this->r0  )->as_number();               // Stated resistance eg. 100K
+    this->t0 =                  THEKERNEL->config->value(module_checksum, name_checksum, t0_checksum  )->by_default(this->t0  )->as_number();               // Temperature at stated resistance, eg. 25C
+    this->beta =                THEKERNEL->config->value(module_checksum, name_checksum, beta_checksum)->by_default(this->beta)->as_number();               // Thermistor beta rating. See http://reprap.org/bin/view/Main/MeasuringThermistorBeta
+    this->r1 =                  THEKERNEL->config->value(module_checksum, name_checksum, r1_checksum  )->by_default(this->r1  )->as_number();
+    this->r2 =                  THEKERNEL->config->value(module_checksum, name_checksum, r2_checksum  )->by_default(this->r2  )->as_number();
+
+    // Thermistor math
+    j = (1.0 / beta);
+    k = (1.0 / (t0 + 273.15));
+
+    // Thermistor pin for ADC readings
+    this->thermistor_pin.from_string(THEKERNEL->config->value(module_checksum, name_checksum, thermistor_pin_checksum )->required()->as_string());
+    THEKERNEL->adc->enable_pin(&thermistor_pin);
+}
+
+float Thermistor::get_temperature()
+{
+       return adc_value_to_temperature(new_thermistor_reading());
+}
+
+float Thermistor::adc_value_to_temperature(int adc_value)
+{
+    if ((adc_value == 4095) || (adc_value == 0))
+        return INFINITY;
+    float r = r2 / ((4095.0 / adc_value) - 1.0);
+    if (r1 > 0)
+        r = (r1 * r) / (r1 - r);
+    return (1.0 / (k + (j * log(r / r0)))) - 273.15;
+}
+
+int Thermistor::new_thermistor_reading()
+{
+    int last_raw = THEKERNEL->adc->read(&thermistor_pin);
+    if (queue.size() >= queue.capacity()) {
+        uint16_t l;
+        queue.pop_front(l);
+    }
+    uint16_t r = last_raw;
+    queue.push_back(r);
+    for (int i=0; i<queue.size(); i++)
+      median_buffer[i] = *queue.get_ref(i);
+    uint16_t m = median_buffer[quick_median(median_buffer, queue.size())];
+    return m;
+}
diff --git a/src/modules/tools/temperaturecontrol/Thermistor.h b/src/modules/tools/temperaturecontrol/Thermistor.h
new file mode 100644 (file)
index 0000000..0eaa584
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+      this file is part of smoothie (http://smoothieware.org/). the motion control part is heavily based on grbl (https://github.com/simen/grbl).
+      smoothie is free software: you can redistribute it and/or modify it under the terms of the gnu general public license as published by the free software foundation, either version 3 of the license, or (at your option) any later version.
+      smoothie is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. see the gnu general public license for more details.
+      you should have received a copy of the gnu general public license along with smoothie. if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef thermistor_h
+#define thermistor_h
+
+#include "TempSensor.h"
+#include "RingBuffer.h"
+
+#define QUEUE_LEN 8
+
+
+class Thermistor : public TempSensor
+{
+    public:
+               Thermistor();
+               ~Thermistor();
+               
+               // TempSensor interface.
+               void UpdateConfig(uint16_t module_checksum, uint16_t name_checksum);
+        float get_temperature();
+               
+    private:
+               int new_thermistor_reading();
+        float adc_value_to_temperature(int adc_value);
+
+        // Thermistor computation settings
+        float r0;
+        float t0;
+        int r1;
+        int r2;
+        float beta;
+        float j;
+        float k;
+
+        Pin  thermistor_pin;
+
+        RingBuffer<uint16_t,QUEUE_LEN> queue;  // Queue of readings
+        uint16_t median_buffer[QUEUE_LEN];
+        int running_total;             
+               
+};
+
+#endif
diff --git a/src/modules/tools/temperaturecontrol/max31855.cpp b/src/modules/tools/temperaturecontrol/max31855.cpp
new file mode 100644 (file)
index 0000000..b1f7df7
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+      This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl).
+      Smoothie is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+      Smoothie is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+      You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "libs/Kernel.h"
+#include <math.h>
+#include "libs/Pin.h"
+#include "Config.h"
+#include "checksumm.h"
+#include "ConfigValue.h"
+
+#include "max31855.h"
+
+#include "MRI_Hooks.h"
+
+#define chip_select_checksum CHECKSUM("chip_select")
+#define spi_channel_checksum CHECKSUM("spi_channel")
+
+Max31855::Max31855()
+{
+}
+
+Max31855::~Max31855()
+{
+}
+
+// Get configuration from the config file
+void Max31855::UpdateConfig(uint16_t module_checksum, uint16_t name_checksum)
+{
+       // Chip select
+    this->spi_cs_pin.from_string(THEKERNEL->config->value(module_checksum, name_checksum, chip_select_checksum)->by_default("nc")->as_string())->as_output();
+       this->spi_cs_pin.set(true);
+       
+    // select which SPI channel to use
+    int spi_channel = THEKERNEL->config->value(module_checksum, name_checksum, spi_channel_checksum)->by_default(0)->as_number();
+    PinName miso;
+    PinName mosi;
+    PinName sclk;
+    if(spi_channel == 0) {
+               // Channel 0
+        mosi=P0_18; miso=P0_17; sclk=P0_15;
+    } else {
+               // Channel 1
+        mosi=P0_9; miso=P0_8; sclk=P0_7;
+    } 
+
+       spi.reset(new SPI(mosi, miso, sclk));
+
+       this->spi->format(32);
+    this->spi->frequency(1000000);
+}
+
+float Max31855::get_temperature()
+{
+       this->spi_cs_pin.set(false);
+       wait_us(1); // Must wait for first bit valid
+
+       // Read 32 bits
+/*     uint32_t data = spi->write(0);
+       data = data<<8;
+       data |= spi->write(0);
+       data = data<<8;
+       data |= spi->write(0);
+       data = data<<8;
+       data |= spi->write(0);
+*/
+       uint32_t data = uint32_t(spi->write(0));
+       
+       this->spi_cs_pin.set(true);
+       
+       float temperature;
+
+    //Process temp
+    if (data & 0x00010000)
+        temperature = 1000.f; //Some form of error.
+    else
+    {
+        data = data >> 18;
+        temperature = (data & 0x00001FFF) / 4.f;
+
+        if (data & 0x00002000)
+        {
+            data = ~data;
+            temperature = ((data & 0x00001FFF) + 1) / -4.f;
+        }
+    }
+    return temperature;        
+}
\ No newline at end of file
diff --git a/src/modules/tools/temperaturecontrol/max31855.h b/src/modules/tools/temperaturecontrol/max31855.h
new file mode 100644 (file)
index 0000000..e3e16a5
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+      this file is part of smoothie (http://smoothieware.org/). the motion control part is heavily based on grbl (https://github.com/simen/grbl).
+      smoothie is free software: you can redistribute it and/or modify it under the terms of the gnu general public license as published by the free software foundation, either version 3 of the license, or (at your option) any later version.
+      smoothie is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. see the gnu general public license for more details.
+      you should have received a copy of the gnu general public license along with smoothie. if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef max31855_h
+#define max31855_h
+
+#include "TempSensor.h"
+#include <string>
+#include <libs/Pin.h>
+#include <mbed.h>
+#include <memory>
+
+class Max31855 : public TempSensor
+{
+public:
+       Max31855();
+       ~Max31855();
+       void UpdateConfig(uint16_t module_checksum, uint16_t name_checksum);
+       float get_temperature();
+
+private:
+       Pin spi_cs_pin;
+       std::auto_ptr<mbed::SPI> spi;
+};
+
+#endif