Browse Source

🐛 Redundant Temp Sensor followup (#22196)

Katelyn Schiesser 3 years ago
parent
commit
b2f0913083

+ 79 - 57
Marlin/Configuration.h

@@ -397,70 +397,92 @@
 // @section temperature
 
 /**
- * --NORMAL IS 4.7kohm PULLUP!-- 1kohm pullup can be used on hotend sensor, using correct resistor and table
+ * --NORMAL IS 4.7kΩ PULLUP!-- 1kΩ pullup can be used on hotend sensor, using correct resistor and table
  *
  * Temperature sensors available:
  *
- *    -5 : PT100 / PT1000 with MAX31865 (only for sensors 0-1)
- *    -3 : thermocouple with MAX31855 (only for sensors 0-1)
- *    -2 : thermocouple with MAX6675 (only for sensors 0-1)
- *    -4 : thermocouple with AD8495
- *    -1 : thermocouple with AD595
+ *  SPI RTD/Thermocouple Boards - IMPORTANT: Read the NOTE below!
+ *  -------
+ *    -5 : MAX31865 with Pt100/Pt1000, 2, 3, or 4-wire  (only for sensors 0-1)
+ *                  NOTE: You must uncomment/set the MAX31865_*_OHMS_n defines below.
+ *    -3 : MAX31855 with Thermocouple, -200°C to +700°C (only for sensors 0-1)
+ *    -2 : MAX6675  with Thermocouple, 0°C to +700°C    (only for sensors 0-1)
+ *
+ *  NOTE: Ensure TEMP_n_CS_PIN is set in your pins file for each TEMP_SENSOR_n using an SPI Thermocouple. By default,
+ *        Hardware SPI on the default serial bus is used. If you have also set TEMP_n_SCK_PIN and TEMP_n_MISO_PIN,
+ *        Software SPI will be used on those ports instead. You can force Hardware SPI on the default bus in the
+ *        Configuration_adv.h file. At this time, separate Hardware SPI buses for sensors are not supported.
+ *
+ *  Analog Themocouple Boards
+ *  -------
+ *    -4 : AD8495 with Thermocouple
+ *    -1 : AD595  with Thermocouple
+ *
+ *  Analog Thermistors - 4.7kΩ pullup - Normal
+ *  -------
+ *     1 : 100kΩ  EPCOS - Best choice for EPCOS thermistors
+ *   331 : 100kΩ  Same as #1, but 3.3V scaled for MEGA
+ *   332 : 100kΩ  Same as #1, but 3.3V scaled for DUE
+ *     2 : 200kΩ  ATC Semitec 204GT-2
+ *   202 : 200kΩ  Copymaster 3D
+ *     3 : ???Ω   Mendel-parts thermistor
+ *     4 : 10kΩ   Generic Thermistor !! DO NOT use for a hotend - it gives bad resolution at high temp. !!
+ *     5 : 100kΩ  ATC Semitec 104GT-2/104NT-4-R025H42G - Used in ParCan, J-Head, and E3D, SliceEngineering 300°C
+ *   501 : 100kΩ  Zonestar - Tronxy X3A
+ *   502 : 100kΩ  Zonestar - used by hot bed in Zonestar Průša P802M
+ *   512 : 100kΩ  RPW-Ultra hotend
+ *     6 : 100kΩ  EPCOS - Not as accurate as table #1 (created using a fluke thermocouple)
+ *     7 : 100kΩ  Honeywell 135-104LAG-J01
+ *    71 : 100kΩ  Honeywell 135-104LAF-J01
+ *     8 : 100kΩ  Vishay 0603 SMD NTCS0603E3104FXT
+ *     9 : 100kΩ  GE Sensing AL03006-58.2K-97-G1
+ *    10 : 100kΩ  RS PRO 198-961
+ *    11 : 100kΩ  Keenovo AC silicone mats, most Wanhao i3 machines - beta 3950, 1%
+ *    12 : 100kΩ  Vishay 0603 SMD NTCS0603E3104FXT (#8) - calibrated for Makibox hot bed
+ *    13 : 100kΩ  Hisens up to 300°C - for "Simple ONE" & "All In ONE" hotend - beta 3950, 1%
+ *    15 : 100kΩ  Calibrated for JGAurora A5 hotend
+ *    18 : 200kΩ  ATC Semitec 204GT-2 Dagoma.Fr - MKS_Base_DKU001327
+ *    22 : 100kΩ  GTM32 Pro vB - hotend - 4.7kΩ pullup to 3.3V and 220Ω to analog input
+ *    23 : 100kΩ  GTM32 Pro vB - bed - 4.7kΩ pullup to 3.3v and 220Ω to analog input
+ *    30 : 100kΩ  Kis3d Silicone heating mat 200W/300W with 6mm precision cast plate (EN AW 5083) NTC100K - beta 3950
+ *    60 : 100kΩ  Maker's Tool Works Kapton Bed Thermistor - beta 3950
+ *    61 : 100kΩ  Formbot/Vivedino 350°C Thermistor - beta 3950
+ *    66 : 4.7MΩ  Dyze Design High Temperature Thermistor
+ *    67 : 500kΩ  SliceEngineering 450°C Thermistor
+ *    70 : 100kΩ  bq Hephestos 2
+ *    75 : 100kΩ  Generic Silicon Heat Pad with NTC100K MGB18-104F39050L32
+ *
+ *  Analog Thermistors - 1kΩ pullup - Atypical, and requires changing out the 4.7kΩ pullup for 1kΩ.
+ *  -------                           (but gives greater accuracy and more stable PID)
+ *    51 : 100kΩ  EPCOS (1kΩ pullup)
+ *    52 : 200kΩ  ATC Semitec 204GT-2 (1kΩ pullup)
+ *    55 : 100kΩ  ATC Semitec 104GT-2 - Used in ParCan & J-Head (1kΩ pullup)
+ *
+ *  Analog Thermistors - 10kΩ pullup - Atypical
+ *  -------
+ *    99 : 100kΩ  Found on some Wanhao i3 machines with a 10kΩ pull-up resistor
+ *
+ *  Analog RTDs (Pt100/Pt1000)
+ *  -------
+ *   110 : Pt100  with 1kΩ pullup (atypical)
+ *   147 : Pt100  with 4.7kΩ pullup
+ *  1010 : Pt1000 with 1kΩ pullup (atypical)
+ *  1047 : Pt1000 with 4.7kΩ pullup (E3D)
+ *    20 : Pt100  with circuit in the Ultimainboard V2.x with mainboard ADC reference voltage = INA826 amplifier-board supply voltage.
+ *                NOTE: (1) Must use an ADC input with no pullup. (2) Some INA826 amplifiers are unreliable at 3.3V so consider using sensor 147, 110, or 21.
+ *    21 : Pt100  with circuit in the Ultimainboard V2.x with 3.3v ADC reference voltage (STM32, LPC176x....) and 5V INA826 amplifier board supply.
+ *                NOTE: ADC pins are not 5V tolerant. Not recommended because it's possible to damage the CPU by going over 500°C.
+ *   201 : Pt100  with circuit in Overlord, similar to Ultimainboard V2.x
+ *
+ *  Custom/Dummy/Other Thermos
+ *  ------
  *     0 : not used
- *     1 : 100k thermistor - best choice for EPCOS 100k (4.7k pullup)
- *   331 : (3.3V scaled thermistor 1 table for MEGA)
- *   332 : (3.3V scaled thermistor 1 table for DUE)
- *     2 : 200k thermistor - ATC Semitec 204GT-2 (4.7k pullup)
- *   202 : 200k thermistor - Copymaster 3D
- *     3 : Mendel-parts thermistor (4.7k pullup)
- *     4 : 10k thermistor !! do not use it for a hotend. It gives bad resolution at high temp. !!
- *     5 : 100K thermistor - ATC Semitec 104GT-2/104NT-4-R025H42G (Used in ParCan, J-Head, and E3D) (4.7k pullup)
- *   501 : 100K Zonestar (Tronxy X3A) Thermistor
- *   502 : 100K Zonestar Thermistor used by hot bed in Zonestar Průša P802M
- *   512 : 100k RPW-Ultra hotend thermistor (4.7k pullup)
- *     6 : 100k EPCOS - Not as accurate as table 1 (created using a fluke thermocouple) (4.7k pullup)
- *     7 : 100k Honeywell thermistor 135-104LAG-J01 (4.7k pullup)
- *    71 : 100k Honeywell thermistor 135-104LAF-J01 (4.7k pullup)
- *     8 : 100k 0603 SMD Vishay NTCS0603E3104FXT (4.7k pullup)
- *     9 : 100k GE Sensing AL03006-58.2K-97-G1 (4.7k pullup)
- *    10 : 100k RS thermistor 198-961 (4.7k pullup)
- *    11 : 100k beta 3950 1% thermistor (Used in Keenovo AC silicone mats and most Wanhao i3 machines) (4.7k pullup)
- *    12 : 100k 0603 SMD Vishay NTCS0603E3104FXT (4.7k pullup) (calibrated for Makibox hot bed)
- *    13 : 100k Hisens 3950  1% up to 300°C for hotend "Simple ONE " & "Hotend "All In ONE"
- *    15 : 100k thermistor calibration for JGAurora A5 hotend
- *    18 : ATC Semitec 204GT-2 (4.7k pullup) Dagoma.Fr - MKS_Base_DKU001327
- *    20 : Pt100 with circuit in the Ultimainboard V2.x with mainboard ADC reference voltage = INA826 amplifier-board supply voltage.
- *         NOTES: (1) Must use an ADC input with no pullup. (2) Some INA826 amplifiers are unreliable at 3.3V so consider using sensor 147, 110, or 21.
- *    21 : Pt100 with circuit in the Ultimainboard V2.x with 3.3v ADC reference voltage (STM32, LPC176x....) and 5V INA826 amplifier board supply.
- *         NOTE: ADC pins are not 5V tolerant. Not recommended because it's possible to damage the CPU by going over 500°C.
- *    22 : 100k (hotend) with 4.7k pullup to 3.3V and 220R to analog input (as in GTM32 Pro vB)
- *    23 : 100k (bed) with 4.7k pullup to 3.3v and 220R to analog input (as in GTM32 Pro vB)
- *    30 : Kis3d Silicone heating mat 200W/300W with 6mm precision cast plate (EN AW 5083) NTC100K / B3950 (4.7k pullup)
- *   201 : Pt100 with circuit in Overlord, similar to Ultimainboard V2.x
- *    60 : 100k Maker's Tool Works Kapton Bed Thermistor beta=3950
- *    61 : 100k Formbot / Vivedino 3950 350C thermistor 4.7k pullup
- *    66 : 4.7M High Temperature thermistor from Dyze Design
- *    67 : 450C thermistor from SliceEngineering
- *    70 : the 100K thermistor found in the bq Hephestos 2
- *    75 : 100k Generic Silicon Heat Pad with NTC 100K MGB18-104F39050L32 thermistor
- *    99 : 100k thermistor with a 10K pull-up resistor (found on some Wanhao i3 machines)
- *
- *       1k ohm pullup tables - This is atypical, and requires changing out the 4.7k pullup for 1k.
- *                              (but gives greater accuracy and more stable PID)
- *    51 : 100k thermistor - EPCOS (1k pullup)
- *    52 : 200k thermistor - ATC Semitec 204GT-2 (1k pullup)
- *    55 : 100k thermistor - ATC Semitec 104GT-2 (Used in ParCan & J-Head) (1k pullup)
- *
- *  1047 : Pt1000 with 4k7 pullup (E3D)
- *  1010 : Pt1000 with 1k pullup (non standard)
- *   147 : Pt100 with 4k7 pullup
- *   110 : Pt100 with 1k pullup (non standard)
- *
  *  1000 : Custom - Specify parameters in Configuration_adv.h
  *
- *         Use these for Testing or Development purposes. NEVER for production machine.
+ *   !!! Use these for Testing or Development purposes. NEVER for production machine. !!!
  *   998 : Dummy Table that ALWAYS reads 25°C or the temperature defined below.
  *   999 : Dummy Table that ALWAYS reads 100°C or the temperature defined below.
+ *
  */
 #define TEMP_SENSOR_0 1
 #define TEMP_SENSOR_1 0
@@ -482,7 +504,7 @@
 
 // Resistor values when using MAX31865 sensors (-5) on TEMP_SENSOR_0 / 1
 //#define MAX31865_SENSOR_OHMS_0      100   // (Ω) Typically 100 or 1000 (PT100 or PT1000)
-//#define MAX31865_CALIBRATION_OHMS_0 430   // (Ω) Typically 430 for AdaFruit PT100; 4300 for AdaFruit PT1000
+//#define MAX31865_CALIBRATION_OHMS_0 430   // (Ω) Typically 430 for Adafruit PT100; 4300 for Adafruit PT1000
 //#define MAX31865_SENSOR_OHMS_1      100
 //#define MAX31865_CALIBRATION_OHMS_1 430
 

+ 15 - 4
Marlin/Configuration_adv.h

@@ -131,10 +131,21 @@
   #define REDUNDANT_BETA                   3950    // Beta value
 #endif
 
-//
-// Hephestos 2 24V heated bed upgrade kit.
-// https://store.bq.com/en/heated-bed-kit-hephestos2
-//
+/**
+ * Configuration options for MAX Thermocouples (-2, -3, -5).
+ *   FORCE_HW_SPI:   Ignore SCK/MOSI/MISO pins and just use the CS pin & default SPI bus.
+ *   MAX31865_WIRES: Set the number of wires for the probe connected to a MAX31865 board, 2-4. Default: 2
+ *   MAX31865_50HZ:  Enable 50Hz filter instead of the default 60Hz.
+ */
+//#define TEMP_SENSOR_FORCE_HW_SPI
+//#define MAX31865_SENSOR_WIRES_0 2
+//#define MAX31865_SENSOR_WIRES_1 2
+//#define MAX31865_50HZ_FILTER
+
+/**
+ * Hephestos 2 24V heated bed upgrade kit.
+ * https://store.bq.com/en/heated-bed-kit-hephestos2
+ */
 //#define HEPHESTOS2_HEATED_BED_KIT
 #if ENABLED(HEPHESTOS2_HEATED_BED_KIT)
   #undef TEMP_SENSOR_BED

+ 2 - 1
Marlin/src/HAL/SAMD51/inc/SanityCheck.h

@@ -31,7 +31,8 @@
   #error "No custom SD drive cable defined for this board."
 #endif
 
-#if defined(MAX6675_SCK_PIN) && defined(MAX6675_DO_PIN) && (MAX6675_SCK_PIN == SCK1 || MAX6675_DO_PIN == MISO1)
+#if (defined(TEMP_0_SCK_PIN) && defined(TEMP_0_MISO_PIN) && (TEMP_0_SCK_PIN == SCK1 || TEMP_0_MISO_PIN == MISO1)) || \
+    (defined(TEMP_1_SCK_PIN) && defined(TEMP_1_MISO_PIN) && (TEMP_1_SCK_PIN == SCK1 || TEMP_1_MISO_PIN == MISO1))
   #error "OnBoard SPI BUS can't be shared with other devices."
 #endif
 

+ 2 - 2
Marlin/src/MarlinCore.cpp

@@ -1169,10 +1169,10 @@ void setup() {
 
   // Init and disable SPI thermocouples; this is still needed
   #if TEMP_SENSOR_0_IS_MAX_TC || (TEMP_SENSOR_REDUNDANT_IS_MAX_TC && TEMP_SENSOR_REDUNDANT_SOURCE == 0)
-    OUT_WRITE(MAX6675_SS_PIN, HIGH);  // Disable
+    OUT_WRITE(TEMP_0_CS_PIN, HIGH);  // Disable
   #endif
   #if TEMP_SENSOR_1_IS_MAX_TC || (TEMP_SENSOR_REDUNDANT_IS_MAX_TC && TEMP_SENSOR_REDUNDANT_SOURCE == 1)
-    OUT_WRITE(MAX6675_SS2_PIN, HIGH); // Disable
+    OUT_WRITE(TEMP_1_CS_PIN, HIGH);
   #endif
 
   #if ENABLED(DUET_SMART_EFFECTOR) && PIN_EXISTS(SMART_EFFECTOR_MOD)

+ 2 - 2
Marlin/src/core/macros.h

@@ -204,8 +204,8 @@
 #define __TERN(T,V...)      ___TERN(_CAT(_NO,T),V)  // Prepend '_NO' to get '_NOT_0' or '_NOT_1'
 #define ___TERN(P,V...)     THIRD(P,V)              // If first argument has a comma, A. Else B.
 
-#define _OPTARG(A)          , A
-#define OPTARG(O,A)         TERN_(O,DEFER4(_OPTARG)(A))
+#define _OPTARG(A...)       , A
+#define OPTARG(O,A...)      TERN_(O,DEFER4(_OPTARG)(A))
 #define _OPTCODE(A)         A;
 #define OPTCODE(O,A)        TERN_(O,DEFER4(_OPTCODE)(A))
 

+ 7 - 5
Marlin/src/gcode/calibrate/G34_M422.cpp

@@ -314,11 +314,13 @@ void GcodeSuite::G34() {
           sprintf_P(msg,
             PSTR("1:2=%s" TERN_(TRIPLE_Z, " 3-2=%s 3-1=%s") TERN_(QUAD_Z, " 4-3=%s 4-2=%s 4-1=%s")),
             dtostrf(ABS(z_measured[1] - z_measured[0]), 1, 3, fstr1)
-            OPTARG(TRIPLE_Z, dtostrf(ABS(z_measured[2] - z_measured[1]), 1, 3, fstr2))
-            OPTARG(TRIPLE_Z, dtostrf(ABS(z_measured[2] - z_measured[0]), 1, 3, fstr3))
-            OPTARG(QUAD_Z,   dtostrf(ABS(z_measured[3] - z_measured[2]), 1, 3, fstr4))
-            OPTARG(QUAD_Z,   dtostrf(ABS(z_measured[3] - z_measured[1]), 1, 3, fstr5))
-            OPTARG(QUAD_Z,   dtostrf(ABS(z_measured[3] - z_measured[0]), 1, 3, fstr6))
+            OPTARG(TRIPLE_Z,
+              dtostrf(ABS(z_measured[2] - z_measured[1]), 1, 3, fstr2),
+              dtostrf(ABS(z_measured[2] - z_measured[0]), 1, 3, fstr3))
+            OPTARG(QUAD_Z,
+              dtostrf(ABS(z_measured[3] - z_measured[2]), 1, 3, fstr4),
+              dtostrf(ABS(z_measured[3] - z_measured[1]), 1, 3, fstr5),
+              dtostrf(ABS(z_measured[3] - z_measured[0]), 1, 3, fstr6))
           );
           ui.set_status(msg);
         #endif

+ 277 - 182
Marlin/src/inc/Conditionals_post.h

@@ -615,20 +615,21 @@
 
 #if TEMP_SENSOR_0 == -5 || TEMP_SENSOR_0 == -3 || TEMP_SENSOR_0 == -2
   #define TEMP_SENSOR_0_IS_MAX_TC 1
-  #define HAS_MAX_TC 1
-  #if TEMP_SENSOR_0 == -3
-    #define TEMP_SENSOR_0_MAX_TC_TMIN -270
-    #define TEMP_SENSOR_0_MAX_TC_TMAX 1800
-  #else
-    #define TEMP_SENSOR_0_MAX_TC_TMIN    0
-    #define TEMP_SENSOR_0_MAX_TC_TMAX 1024
-  #endif
   #if TEMP_SENSOR_0 == -5
     #define TEMP_SENSOR_0_IS_MAX31865 1
+    #define TEMP_SENSOR_0_MAX_TC_TMIN    0
+    #define TEMP_SENSOR_0_MAX_TC_TMAX 1024
+    #ifndef MAX31865_SENSOR_WIRES_0
+      #define MAX31865_SENSOR_WIRES_0 2
+    #endif
   #elif TEMP_SENSOR_0 == -3
     #define TEMP_SENSOR_0_IS_MAX31855 1
+    #define TEMP_SENSOR_0_MAX_TC_TMIN -270
+    #define TEMP_SENSOR_0_MAX_TC_TMAX 1800
   #elif TEMP_SENSOR_0 == -2
     #define TEMP_SENSOR_0_IS_MAX6675 1
+    #define TEMP_SENSOR_0_MAX_TC_TMIN    0
+    #define TEMP_SENSOR_0_MAX_TC_TMAX 1024
   #endif
 #elif TEMP_SENSOR_0 == -4
   #define TEMP_SENSOR_0_IS_AD8495 1
@@ -648,21 +649,23 @@
 
 #if TEMP_SENSOR_1 == -5 || TEMP_SENSOR_1 == -3 || TEMP_SENSOR_1 == -2
   #define TEMP_SENSOR_1_IS_MAX_TC 1
-  #define HAS_MAX_TC 1
-  #if TEMP_SENSOR_1 == -3
-    #define TEMP_SENSOR_1_MAX_TC_TMIN -270
-    #define TEMP_SENSOR_1_MAX_TC_TMAX 1800
-  #else
-    #define TEMP_SENSOR_1_MAX_TC_TMIN    0
-    #define TEMP_SENSOR_1_MAX_TC_TMAX 1024
-  #endif
   #if TEMP_SENSOR_1 == -5
     #define TEMP_SENSOR_1_IS_MAX31865 1
+    #define TEMP_SENSOR_1_MAX_TC_TMIN    0
+    #define TEMP_SENSOR_1_MAX_TC_TMAX 1024
+    #ifndef MAX31865_SENSOR_WIRES_1
+      #define MAX31865_SENSOR_WIRES_1 2
+    #endif
   #elif TEMP_SENSOR_1 == -3
     #define TEMP_SENSOR_1_IS_MAX31855 1
+    #define TEMP_SENSOR_1_MAX_TC_TMIN -270
+    #define TEMP_SENSOR_1_MAX_TC_TMAX 1800
   #elif TEMP_SENSOR_1 == -2
     #define TEMP_SENSOR_1_IS_MAX6675 1
+    #define TEMP_SENSOR_1_MAX_TC_TMIN    0
+    #define TEMP_SENSOR_1_MAX_TC_TMAX 1024
   #endif
+
   #if TEMP_SENSOR_1 != TEMP_SENSOR_0
     #if   TEMP_SENSOR_1 == -5
       #error "If MAX31865 Thermocouple (-5) is used for TEMP_SENSOR_1 then TEMP_SENSOR_0 must match."
@@ -690,37 +693,48 @@
 
 #if TEMP_SENSOR_REDUNDANT == -5 || TEMP_SENSOR_REDUNDANT == -3 || TEMP_SENSOR_REDUNDANT == -2
   #define TEMP_SENSOR_REDUNDANT_IS_MAX_TC 1
-  #define HAS_MAX_TC 1
-  #if TEMP_SENSOR_REDUNDANT == -3
-    #define TEMP_SENSOR_REDUNDANT_MAX_TC_TMIN -270
-    #define TEMP_SENSOR_REDUNDANT_MAX_TC_TMAX 1800
-  #else
-    #define TEMP_SENSOR_REDUNDANT_MAX_TC_TMIN    0
-    #define TEMP_SENSOR_REDUNDANT_MAX_TC_TMAX 1024
-  #endif
-  #if TEMP_SENSOR_REDUNDANT_SOURCE == 0
-    #define TEMP_SENSOR_0_MAX_TC_TMIN TEMP_SENSOR_REDUNDANT_MAX_TC_TMIN
-    #define TEMP_SENSOR_0_MAX_TC_TMAX TEMP_SENSOR_REDUNDANT_MAX_TC_TMAX
-  #elif TEMP_SENSOR_REDUNDANT_SOURCE == 1
-    #define TEMP_SENSOR_1_MAX_TC_TMIN TEMP_SENSOR_REDUNDANT_MAX_TC_TMIN
-    #define TEMP_SENSOR_1_MAX_TC_TMAX TEMP_SENSOR_REDUNDANT_MAX_TC_TMAX
-  #endif
+
   #if TEMP_SENSOR_REDUNDANT == -5
     #if TEMP_SENSOR_REDUNDANT_SOURCE != 0 && TEMP_SENSOR_REDUNDANT_SOURCE != 1
       #error "MAX31865 Thermocouples (-5) not supported for TEMP_SENSOR_REDUNDANT_SOURCE other than TEMP_SENSOR_0/TEMP_SENSOR_1 (0/1)."
     #endif
-    #define TEMP_SENSOR_REDUNDANT_IS_MAX31865 1
+
+    #define TEMP_SENSOR_REDUNDANT_IS_MAX31865    1
+    #define TEMP_SENSOR_REDUNDANT_MAX_TC_TMIN    0
+    #define TEMP_SENSOR_REDUNDANT_MAX_TC_TMAX 1024
   #elif TEMP_SENSOR_REDUNDANT == -3
     #if TEMP_SENSOR_REDUNDANT_SOURCE != 0 && TEMP_SENSOR_REDUNDANT_SOURCE != 1
       #error "MAX31855 Thermocouples (-3) not supported for TEMP_SENSOR_REDUNDANT_SOURCE other than TEMP_SENSOR_0/TEMP_SENSOR_1 (0/1)."
     #endif
-    #define TEMP_SENSOR_REDUNDANT_IS_MAX31855 1
+
+    #define TEMP_SENSOR_REDUNDANT_IS_MAX31855    1
+    #define TEMP_SENSOR_REDUNDANT_MAX_TC_TMIN -270
+    #define TEMP_SENSOR_REDUNDANT_MAX_TC_TMAX 1800
   #elif TEMP_SENSOR_REDUNDANT == -2
     #if TEMP_SENSOR_REDUNDANT_SOURCE != 0 && TEMP_SENSOR_REDUNDANT_SOURCE != 1
       #error "MAX6675 Thermocouples (-2) not supported for TEMP_SENSOR_REDUNDANT_SOURCE other than TEMP_SENSOR_0/TEMP_SENSOR_1 (0/1)."
     #endif
-    #define TEMP_SENSOR_REDUNDANT_IS_MAX6675 1
+
+    #define TEMP_SENSOR_REDUNDANT_IS_MAX6675     1
+    #define TEMP_SENSOR_REDUNDANT_MAX_TC_TMIN    0
+    #define TEMP_SENSOR_REDUNDANT_MAX_TC_TMAX 1024
   #endif
+
+  // mimic setting up the source TEMP_SENSOR
+  #if TEMP_SENSOR_REDUNDANT_SOURCE == 0
+    #define TEMP_SENSOR_0_MAX_TC_TMIN TEMP_SENSOR_REDUNDANT_MAX_TC_TMIN
+    #define TEMP_SENSOR_0_MAX_TC_TMAX TEMP_SENSOR_REDUNDANT_MAX_TC_TMAX
+    #ifndef MAX31865_SENSOR_WIRES_0
+      #define MAX31865_SENSOR_WIRES_0 2
+    #endif
+  #elif TEMP_SENSOR_REDUNDANT_SOURCE == 1
+    #define TEMP_SENSOR_1_MAX_TC_TMIN TEMP_SENSOR_REDUNDANT_MAX_TC_TMIN
+    #define TEMP_SENSOR_1_MAX_TC_TMAX TEMP_SENSOR_REDUNDANT_MAX_TC_TMAX
+    #ifndef MAX31865_SENSOR_WIRES_1
+      #define MAX31865_SENSOR_WIRES_1 2
+    #endif
+  #endif
+
   #if (TEMP_SENSOR_0_IS_MAX_TC && TEMP_SENSOR_REDUNDANT != TEMP_SENSOR_0) || (TEMP_SENSOR_1_IS_MAX_TC && TEMP_SENSOR_REDUNDANT != TEMP_SENSOR_1)
     #if   TEMP_SENSOR_REDUNDANT == -5
       #error "If MAX31865 Thermocouple (-5) is used for TEMP_SENSOR_0/TEMP_SENSOR_1 then TEMP_SENSOR_REDUNDANT must match."
@@ -743,101 +757,182 @@
   #endif
 #endif
 
+#if TEMP_SENSOR_0_IS_MAX_TC || TEMP_SENSOR_1_IS_MAX_TC || TEMP_SENSOR_REDUNDANT_IS_MAX_TC
+  #define HAS_MAX_TC 1
+#endif
+#if TEMP_SENSOR_0_IS_MAX6675 || TEMP_SENSOR_1_IS_MAX6675 || TEMP_SENSOR_REDUNDANT_IS_MAX6675
+  #define HAS_MAX6675 1
+#endif
 #if TEMP_SENSOR_0_IS_MAX31855 || TEMP_SENSOR_1_IS_MAX31855 || TEMP_SENSOR_REDUNDANT_IS_MAX31855
   #define HAS_MAX31855 1
 #endif
 #if TEMP_SENSOR_0_IS_MAX31865 || TEMP_SENSOR_1_IS_MAX31865 || TEMP_SENSOR_REDUNDANT_IS_MAX31865
   #define HAS_MAX31865 1
 #endif
-#if TEMP_SENSOR_0_IS_MAX6675 || TEMP_SENSOR_1_IS_MAX6675 || TEMP_SENSOR_REDUNDANT_IS_MAX6675
-  #define HAS_MAX6675 1
-#endif
 
 //
 // Compatibility layer for MAX (SPI) temp boards
 //
-#define TEMP_SENSOR_IS_MAX(n, M) (ENABLED(TEMP_SENSOR_##n##_IS_##M) || (ENABLED(TEMP_SENSOR_REDUNDANT_IS_##M) && TEMP_SENSOR_REDUNDANT_SOURCE == (n)))
+#if HAS_MAX_TC
+
+  // Translate old _SS, _CS, _SCK, _DO, _DI, _MISO, and _MOSI PIN defines.
+  #if TEMP_SENSOR_0_IS_MAX_TC || (TEMP_SENSOR_REDUNDANT_IS_MAX_TC && TEMP_SENSOR_REDUNDANT_SOURCE == 1)
+
+    #if !PIN_EXISTS(TEMP_0_CS) // SS, CS
+      #if PIN_EXISTS(MAX6675_SS)
+        #define TEMP_0_CS_PIN MAX6675_SS_PIN
+      #elif PIN_EXISTS(MAX6675_CS)
+        #define TEMP_0_CS_PIN MAX6675_CS_PIN
+      #elif PIN_EXISTS(MAX31855_SS)
+        #define TEMP_0_CS_PIN MAX31855_SS_PIN
+      #elif PIN_EXISTS(MAX31855_CS)
+        #define TEMP_0_CS_PIN MAX31855_CS_PIN
+      #elif PIN_EXISTS(MAX31865_SS)
+        #define TEMP_0_CS_PIN MAX31865_SS_PIN
+      #elif PIN_EXISTS(MAX31865_CS)
+        #define TEMP_0_CS_PIN MAX31865_CS_PIN
+      #endif
+    #endif
 
-#if PIN_EXISTS(MAX6675_SS)
-  #if TEMP_SENSOR_IS_MAX(0, MAX31855)
-    #define MAX31855_CS_PIN MAX6675_SS_PIN
-  #elif TEMP_SENSOR_IS_MAX(0, MAX31865)
-    #define MAX31865_CS_PIN MAX6675_SS_PIN
-  #elif TEMP_SENSOR_IS_MAX(0, MAX6675)
-    #define MAX6675_CS_PIN MAX6675_SS_PIN
-  #endif
-#endif
+    #if TEMP_SENSOR_0_IS_MAX6675
+      #if !PIN_EXISTS(TEMP_0_MISO) // DO
+        #if PIN_EXISTS(MAX6675_MISO)
+          #define TEMP_0_MISO_PIN MAX6675_MISO_PIN
+        #elif PIN_EXISTS(MAX6675_DO)
+          #define TEMP_0_MISO_PIN MAX6675_DO_PIN
+        #endif
+      #endif
+      #if !PIN_EXISTS(TEMP_0_SCK) && PIN_EXISTS(MAX6675_SCK)
+        #define TEMP_0_SCK_PIN MAX6675_SCK_PIN
+      #endif
 
-#if PIN_EXISTS(MAX6675_SS2)
-  #if TEMP_SENSOR_IS_MAX(1, MAX31855)
-    #define MAX31855_CS2_PIN MAX6675_SS2_PIN
-  #elif TEMP_SENSOR_IS_MAX(1, MAX31865)
-    #define MAX31865_CS2_PIN MAX6675_SS2_PIN
-  #elif TEMP_SENSOR_IS_MAX(1, MAX6675)
-    #define MAX6675_CS2_PIN MAX6675_SS2_PIN
-  #endif
-#endif
+    #elif TEMP_SENSOR_0_IS_MAX31855
+      #if !PIN_EXISTS(TEMP_0_MISO) // DO
+        #if PIN_EXISTS(MAX31855_MISO)
+          #define TEMP_0_MISO_PIN MAX31855_MISO_PIN
+        #elif PIN_EXISTS(MAX31855_DO)
+          #define TEMP_0_MISO_PIN MAX31855_DO_PIN
+        #endif
+      #endif
+      #if !PIN_EXISTS(TEMP_0_SCK) && PIN_EXISTS(MAX31855_SCK)
+        #define TEMP_0_SCK_PIN MAX31855_SCK_PIN
+      #endif
 
-#if PIN_EXISTS(MAX6675_DO)
-  #if HAS_MAX31855
-    #define MAX31855_MISO_PIN MAX6675_DO_PIN
-  #elif HAS_MAX31865
-    #define MAX31865_MISO_PIN MAX6675_DO_PIN
-  #elif HAS_MAX6675
-    #define MAX6675_MISO_PIN MAX6675_DO_PIN
-  #endif
-#endif
+    #elif TEMP_SENSOR_1_IS_MAX31865
+      #if !PIN_EXISTS(TEMP_1_MISO) // DO
+        #if PIN_EXISTS(MAX31865_MISO)
+          #define TEMP_1_MISO_PIN MAX31865_MISO_PIN
+        #elif PIN_EXISTS(MAX31865_DO)
+          #define TEMP_1_MISO_PIN MAX31865_DO_PIN
+        #endif
+      #endif
+      #if !PIN_EXISTS(TEMP_1_SCK) && PIN_EXISTS(MAX31865_SCK)
+        #define TEMP_1_SCK_PIN MAX31865_SCK_PIN
+      #endif
+      #if !PIN_EXISTS(TEMP_1_MOSI) && PIN_EXISTS(MAX31865_MOSI) // MOSI for '65 only
+        #define TEMP_1_MOSI_PIN MAX31865_MOSI_PIN
+      #endif
+    #endif
 
-#if PIN_EXISTS(MAX6675_SCK)
-  #if HAS_MAX31855
-    #define MAX31855_SCK_PIN MAX6675_SCK_PIN
-  #elif HAS_MAX31865
-    #define MAX31865_SCK_PIN MAX6675_SCK_PIN
-  #endif
-#endif
+    // Software SPI - enable if MISO/SCK are defined.
+    #if PIN_EXISTS(TEMP_0_MISO) && PIN_EXISTS(TEMP_0_SCK) && DISABLED(TEMP_SENSOR_0_FORCE_HW_SPI)
+      #if TEMP_SENSOR_0_IS_MAX31865 && !PIN_EXISTS(TEMP_0_MOSI)
+        #error "TEMP_SENSOR_0 MAX31865 requires TEMP_0_MOSI_PIN defined for Software SPI. To use Hardware SPI instead, undefine MISO/SCK or enable TEMP_SENSOR_0_FORCE_HW_SPI."
+      #else
+        #define TEMP_SENSOR_0_HAS_SPI_PINS 1
+      #endif
+    #endif
 
-// Compatibility Layer for use when HAL manipulates PINS for MAX31855 and MAX6675
-#if PIN_EXISTS(MAX31855_CS) && !PIN_EXISTS(MAX6675_SS)
-  #define MAX6675_SS_PIN MAX31855_CS_PIN
-#endif
-#if PIN_EXISTS(MAX31855_CS2) && !PIN_EXISTS(MAX6675_SS2)
-  #define MAX6675_SS2_PIN MAX31855_CS2_PIN
-#endif
-#if PIN_EXISTS(MAX6675_CS) && !PIN_EXISTS(MAX6675_SS)
-  #define MAX6675_SS_PIN MAX6675_CS_PIN
-#endif
-#if PIN_EXISTS(MAX6675_CS2) && !PIN_EXISTS(MAX6675_SS2)
-  #define MAX6675_SS2_PIN MAX6675_CS2_PIN
-#endif
-#if PIN_EXISTS(MAX31855_MISO) && !PIN_EXISTS(MAX6675_DO)
-  #define MAX6675_DO_PIN MAX31855_MISO_PIN
-#endif
-#if PIN_EXISTS(MAX6675_MISO) && !PIN_EXISTS(MAX6675_DO)
-  #define MAX6675_DO_PIN MAX6675_MISO_PIN
-#endif
-#if PIN_EXISTS(MAX31855_SCK) && !PIN_EXISTS(MAX6675_SCK)
-  #define MAX6675_SCK_PIN MAX31855_SCK_PIN
-#endif
+  #endif // TEMP_SENSOR_0_IS_MAX_TC
+
+  #if TEMP_SENSOR_1_IS_MAX_TC || (TEMP_SENSOR_REDUNDANT_IS_MAX_TC && TEMP_SENSOR_REDUNDANT_SOURCE == 1)
+
+    #if !PIN_EXISTS(TEMP_1_CS) // SS2, CS2
+      #if PIN_EXISTS(MAX6675_SS2)
+        #define TEMP_1_CS_PIN MAX6675_SS2_PIN
+      #elif PIN_EXISTS(MAX6675_CS)
+        #define TEMP_1_CS_PIN MAX6675_CS2_PIN
+      #elif PIN_EXISTS(MAX31855_SS2)
+        #define TEMP_1_CS_PIN MAX31855_SS2_PIN
+      #elif PIN_EXISTS(MAX31855_CS2)
+        #define TEMP_1_CS_PIN MAX31855_CS2_PIN
+      #elif PIN_EXISTS(MAX31865_SS2)
+        #define TEMP_1_CS_PIN MAX31865_SS2_PIN
+      #elif PIN_EXISTS(MAX31865_CS2)
+        #define TEMP_1_CS_PIN MAX31865_CS2_PIN
+      #endif
+    #endif
 
-//
-// User-defined thermocouple libraries
-//
-// Add LIB_MAX6675 / LIB_MAX31855 / LIB_MAX31865 to the build_flags
-// to select a USER library for MAX6675, MAX31855, MAX31865
-//
-#if BOTH(HAS_MAX6675, LIB_MAX6675)
-  #define LIB_USR_MAX6675 1
-#endif
-#if BOTH(HAS_MAX31855, LIB_MAX31855)
-  #define LIB_USR_MAX31855 1
-#endif
-#if HAS_MAX31865
-  #if ENABLED(LIB_MAX31865)
+    #if TEMP_SENSOR_1_IS_MAX6675
+      #if !PIN_EXISTS(TEMP_1_MISO) // DO
+        #if PIN_EXISTS(MAX6675_MISO)
+          #define TEMP_1_MISO_PIN MAX6675_MISO_PIN
+        #elif PIN_EXISTS(MAX6675_DO)
+          #define TEMP_1_MISO_PIN MAX6675_DO_PIN
+        #endif
+      #endif
+      #if !PIN_EXISTS(TEMP_1_SCK) && PIN_EXISTS(MAX6675_SCK)
+        #define TEMP_1_SCK_PIN MAX6675_SCK_PIN
+      #endif
+
+    #elif TEMP_SENSOR_1_IS_MAX31855
+      #if !PIN_EXISTS(TEMP_1_MISO) // DO
+        #if PIN_EXISTS(MAX31855_MISO)
+          #define TEMP_1_MISO_PIN MAX31855_MISO_PIN
+        #elif PIN_EXISTS(MAX31855_DO)
+          #define TEMP_1_MISO_PIN MAX31855_DO_PIN
+        #endif
+      #endif
+      #if !PIN_EXISTS(TEMP_1_SCK) && PIN_EXISTS(MAX31855_SCK)
+        #define TEMP_1_SCK_PIN MAX31855_SCK_PIN
+      #endif
+
+    #elif TEMP_SENSOR_1_IS_MAX31865
+      #if !PIN_EXISTS(TEMP_1_MISO) // DO
+        #if PIN_EXISTS(MAX31865_MISO)
+          #define TEMP_1_MISO_PIN MAX31865_MISO_PIN
+        #elif PIN_EXISTS(MAX31865_DO)
+          #define TEMP_1_MISO_PIN MAX31865_DO_PIN
+        #endif
+      #endif
+      #if !PIN_EXISTS(TEMP_1_SCK) && PIN_EXISTS(MAX31865_SCK)
+        #define TEMP_1_SCK_PIN MAX31865_SCK_PIN
+      #endif
+      #if !PIN_EXISTS(TEMP_1_MOSI) && PIN_EXISTS(MAX31865_MOSI) // MOSI for '65 only
+        #define TEMP_1_MOSI_PIN MAX31865_MOSI_PIN
+      #endif
+    #endif
+
+    // Software SPI - enable if MISO/SCK are defined.
+    #if PIN_EXISTS(TEMP_1_MISO) && PIN_EXISTS(TEMP_1_SCK) && DISABLED(TEMP_SENSOR_1_FORCE_HW_SPI)
+      #if TEMP_SENSOR_1_IS_MAX31865 && !PIN_EXISTS(TEMP_1_MOSI)
+        #error "TEMP_SENSOR_1 MAX31865 requires TEMP_1_MOSI_PIN defined for Software SPI. To use Hardware SPI instead, undefine MISO/SCK or enable TEMP_SENSOR_1_FORCE_HW_SPI."
+      #else
+        #define TEMP_SENSOR_1_HAS_SPI_PINS 1
+      #endif
+    #endif
+
+  #endif // TEMP_SENSOR_1_IS_MAX_TC
+
+  //
+  // User-defined thermocouple libraries
+  //
+  // Add LIB_MAX6675 / LIB_MAX31855 / LIB_MAX31865 to the build_flags
+  // to select a USER library for MAX6675, MAX31855, MAX31865
+  //
+  #if BOTH(HAS_MAX6675, LIB_MAX6675)
+    #define LIB_USR_MAX6675 1
+  #endif
+  #if BOTH(HAS_MAX31855, LIB_MAX31855)
+    #define LIB_USR_MAX31855 1
+  #endif
+  #if BOTH(HAS_MAX31865, LIB_MAX31865)
     #define LIB_USR_MAX31865 1
-  #else
-    #define LIB_ADAFRUIT_MAX31865 1
+  #elif HAS_MAX31865
+    #define LIB_INTERNAL_MAX31865 1
   #endif
-#endif
+
+#endif //HAS_MAX_TC
 
 #if TEMP_SENSOR_2 == -4
   #define TEMP_SENSOR_2_IS_AD8495 1
@@ -2722,6 +2817,77 @@
   #define BED_OR_CHAMBER_OR_FAN 1
 #endif
 
+/**
+ * Up to 3 PWM fans
+ */
+#ifndef FAN_INVERTING
+  #define FAN_INVERTING false
+#endif
+
+#if HAS_FAN7
+  #define FAN_COUNT 8
+#elif HAS_FAN6
+  #define FAN_COUNT 7
+#elif HAS_FAN5
+  #define FAN_COUNT 6
+#elif HAS_FAN4
+  #define FAN_COUNT 5
+#elif HAS_FAN3
+  #define FAN_COUNT 4
+#elif HAS_FAN2
+  #define FAN_COUNT 3
+#elif HAS_FAN1
+  #define FAN_COUNT 2
+#elif HAS_FAN0
+  #define FAN_COUNT 1
+#else
+  #define FAN_COUNT 0
+#endif
+
+#if FAN_COUNT > 0
+  #define HAS_FAN 1
+#endif
+
+/**
+ * Part Cooling fan multipliexer
+ */
+#if PIN_EXISTS(FANMUX0)
+  #define HAS_FANMUX 1
+#endif
+
+/**
+ * MIN/MAX fan PWM scaling
+ */
+#ifndef FAN_OFF_PWM
+  #define FAN_OFF_PWM 0
+#endif
+#ifndef FAN_MIN_PWM
+  #if FAN_OFF_PWM > 0
+    #define FAN_MIN_PWM (FAN_OFF_PWM + 1)
+  #else
+    #define FAN_MIN_PWM 0
+  #endif
+#endif
+#ifndef FAN_MAX_PWM
+  #define FAN_MAX_PWM 255
+#endif
+#if FAN_MIN_PWM < 0 || FAN_MIN_PWM > 255
+  #error "FAN_MIN_PWM must be a value from 0 to 255."
+#elif FAN_MAX_PWM < 0 || FAN_MAX_PWM > 255
+  #error "FAN_MAX_PWM must be a value from 0 to 255."
+#elif FAN_MIN_PWM > FAN_MAX_PWM
+  #error "FAN_MIN_PWM must be less than or equal to FAN_MAX_PWM."
+#elif FAN_OFF_PWM > FAN_MIN_PWM
+  #error "FAN_OFF_PWM must be less than or equal to FAN_MIN_PWM."
+#endif
+
+/**
+ * FAST PWM FAN Settings
+ */
+#if ENABLED(FAST_PWM_FAN) && !defined(FAST_PWM_FAN_FREQUENCY)
+  #define FAST_PWM_FAN_FREQUENCY ((F_CPU) / (2 * 255 * 1)) // Fan frequency default
+#endif
+
 // Servos
 #if PIN_EXISTS(SERVO0) && NUM_SERVOS > 0
   #define HAS_SERVO_0 1
@@ -2996,77 +3162,6 @@
   #undef PREHEAT_SHORTCUT_MENU_ITEM
 #endif
 
-/**
- * Up to 3 PWM fans
- */
-#ifndef FAN_INVERTING
-  #define FAN_INVERTING false
-#endif
-
-#if HAS_FAN7
-  #define FAN_COUNT 8
-#elif HAS_FAN6
-  #define FAN_COUNT 7
-#elif HAS_FAN5
-  #define FAN_COUNT 6
-#elif HAS_FAN4
-  #define FAN_COUNT 5
-#elif HAS_FAN3
-  #define FAN_COUNT 4
-#elif HAS_FAN2
-  #define FAN_COUNT 3
-#elif HAS_FAN1
-  #define FAN_COUNT 2
-#elif HAS_FAN0
-  #define FAN_COUNT 1
-#else
-  #define FAN_COUNT 0
-#endif
-
-#if FAN_COUNT > 0
-  #define HAS_FAN 1
-#endif
-
-/**
- * Part Cooling fan multipliexer
- */
-#if PIN_EXISTS(FANMUX0)
-  #define HAS_FANMUX 1
-#endif
-
-/**
- * MIN/MAX fan PWM scaling
- */
-#ifndef FAN_OFF_PWM
-  #define FAN_OFF_PWM 0
-#endif
-#ifndef FAN_MIN_PWM
-  #if FAN_OFF_PWM > 0
-    #define FAN_MIN_PWM (FAN_OFF_PWM + 1)
-  #else
-    #define FAN_MIN_PWM 0
-  #endif
-#endif
-#ifndef FAN_MAX_PWM
-  #define FAN_MAX_PWM 255
-#endif
-#if FAN_MIN_PWM < 0 || FAN_MIN_PWM > 255
-  #error "FAN_MIN_PWM must be a value from 0 to 255."
-#elif FAN_MAX_PWM < 0 || FAN_MAX_PWM > 255
-  #error "FAN_MAX_PWM must be a value from 0 to 255."
-#elif FAN_MIN_PWM > FAN_MAX_PWM
-  #error "FAN_MIN_PWM must be less than or equal to FAN_MAX_PWM."
-#elif FAN_OFF_PWM > FAN_MIN_PWM
-  #error "FAN_OFF_PWM must be less than or equal to FAN_MIN_PWM."
-#endif
-
-/**
- * FAST PWM FAN Settings
- */
-#if ENABLED(FAST_PWM_FAN) && !defined(FAST_PWM_FAN_FREQUENCY)
-  #define FAST_PWM_FAN_FREQUENCY ((F_CPU) / (2 * 255 * 1)) // Fan frequency default
-#endif
-
 /**
  * MIN/MAX case light PWM scaling
  */

+ 81 - 55
Marlin/src/inc/SanityCheck.h

@@ -416,8 +416,19 @@
   #error "MBL_Z_STEP is now MESH_EDIT_Z_STEP."
 #elif defined(CHDK)
   #error "CHDK is now CHDK_PIN."
-#elif defined(MAX6675_SS) || defined(MAX6675_SS2)
-  #error "MAX6675_SS / MAX6675_SS2 is now MAX6675_SS_PIN / MAX6675_SS2_PIN."
+#elif ANY_PIN( \
+        MAX6675_SS, MAX6675_SS2, MAX6675_CS, MAX6675_CS2, \
+        MAX31855_SS, MAX31855_SS2, MAX31855_CS, MAX31855_CS2, \
+        MAX31865_SS, MAX31865_SS2, MAX31865_CS, MAX31865_CS2)
+  #warning "MAX*_SS_PIN, MAX*_SS2_PIN, MAX*_CS_PIN, and MAX*_CS2_PIN are deprecated and will be removed in a future version. Please use TEMP_0_CS_PIN/TEMP_1_CS_PIN instead."
+#elif ANY_PIN(MAX6675_SCK, MAX31855_SCK, MAX31865_SCK)
+  #warning "MAX*_SCK_PIN is deprecated and will be removed in a future version. Please use TEMP_0_SCK_PIN/TEMP_1_SCK_PIN instead."
+#elif ANY_PIN(MAX6675_MISO, MAX6675_DO, MAX31855_MISO, MAX31855_DO, MAX31865_MISO, MAX31865_DO)
+  #warning "MAX*_MISO_PIN and MAX*_DO_PIN are deprecated and will be removed in a future version. Please use TEMP_0_MISO_PIN/TEMP_1_MISO_PIN instead."
+#elif PIN_EXISTS(MAX31865_MOSI)
+  #warning "MAX31865_MOSI_PIN is deprecated and will be removed in a future version. Please use TEMP_0_MOSI_PIN/TEMP_1_MOSI_PIN instead."
+#elif ANY_PIN(THERMO_CS1_PIN, THERMO_CS2_PIN, THERMO_DO_PIN, THERMO_SCK_PIN)
+  #error "THERMO_*_PIN is now TEMP_n_CS_PIN, TEMP_n_SCK_PIN, TEMP_n_MOSI_PIN, TEMP_n_MISO_PIN."
 #elif defined(MAX31865_SENSOR_OHMS)
   #error "MAX31865_SENSOR_OHMS is now MAX31865_SENSOR_OHMS_0."
 #elif defined(MAX31865_CALIBRATION_OHMS)
@@ -1953,14 +1964,21 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS
 #endif
 
 /**
- * Pins and Sensor IDs must be set for each heater
+ * Required MAX31865 settings
  */
-#if TEMP_SENSOR_0_IS_MAX6675 && !ANY_PIN(MAX6675_SS, MAX31855_CS, MAX31865_CS, MAX6675_CS)
-  #error "TEMP_SENSOR_0 -2 requires a MAX6675_SS_PIN, MAX6675_CS_PIN, MAX31855_CS_PIN, or MAX31865_CS_PIN."
-#elif HAS_HOTEND && !HAS_TEMP_HOTEND && !TEMP_SENSOR_0_IS_DUMMY
-  #error "TEMP_0_PIN (required for TEMP_SENSOR_0) not defined for this board."
-#elif EITHER(HAS_MULTI_HOTEND, HEATERS_PARALLEL) && !HAS_HEATER_1
-  #error "HEATER_1_PIN is not defined. TEMP_SENSOR_1 might not be set, or the board (not EEB / EEF?) doesn't define a pin."
+#if TEMP_SENSOR_0_IS_MAX31865 || (TEMP_SENSOR_REDUNDANT_IS_MAX31865 && TEMP_SENSOR_REDUNDANT_SOURCE == 0)
+  #if !defined(MAX31865_SENSOR_WIRES_0) || !WITHIN(MAX31865_SENSOR_WIRES_0, 2, 4)
+    #error "MAX31865_SENSOR_WIRES_0 must be defined as an integer between 2 and 4."
+  #elif !defined(MAX31865_SENSOR_OHMS_0) || !defined(MAX31865_CALIBRATION_OHMS_0)
+    #error "MAX31865_SENSOR_OHMS_0 and MAX31865_CALIBRATION_OHMS_0 must be set if TEMP_SENSOR_0/TEMP_SENSOR_REDUNDANT is MAX31865."
+  #endif
+#endif
+#if TEMP_SENSOR_1_IS_MAX31865 || (TEMP_SENSOR_REDUNDANT_IS_MAX31865 && TEMP_SENSOR_REDUNDANT_SOURCE == 1)
+  #if !defined(MAX31865_SENSOR_WIRES_1) || !WITHIN(MAX31865_SENSOR_WIRES_1, 2, 4)
+    #error "MAX31865_SENSOR_WIRES_1 must be defined as an integer between 2 and 4."
+  #elif !defined(MAX31865_SENSOR_OHMS_1) || !defined(MAX31865_CALIBRATION_OHMS_1)
+    #error "MAX31865_SENSOR_OHMS_1 and MAX31865_CALIBRATION_OHMS_1 must be set if TEMP_SENSOR_1/TEMP_SENSOR_REDUNDANT is MAX31865."
+  #endif
 #endif
 
 /**
@@ -2023,20 +2041,36 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS
     #error "TEMP_SENSOR_REDUNDANT_TARGET can't be Cooler (-5): requires TEMP_COOLER_PIN"
   #endif
 
-  #if TEMP_SENSOR_REDUNDANT_IS_MAX_TC && TEMP_SENSOR_REDUNDANT_SOURCE == 0 && !PIN_EXISTS(MAX6675_SS)
-    #error "TEMP_SENSOR_REDUNDANT MAX Thermocouple with TEMP_SENSOR_REDUNDANT_SOURCE 0 requires a MAX6675_SS_PIN, MAX6675_CS_PIN, MAX31855_CS_PIN, or MAX31865_CS_PIN."
-  #elif TEMP_SENSOR_REDUNDANT_IS_MAX_TC && TEMP_SENSOR_REDUNDANT_SOURCE == 1 && !PIN_EXISTS(MAX6675_SS2)
-    #error "TEMP_SENSOR_REDUNDANT MAX Thermocouple with TEMP_SENSOR_REDUNDANT_SOURCE 1 requires a MAX6675_SS2_PIN, MAX6675_CS_PIN, MAX31855_CS_PIN, or MAX31865_CS_PIN."
+  #if TEMP_SENSOR_REDUNDANT_IS_MAX_TC && TEMP_SENSOR_REDUNDANT_SOURCE == 0 && !PIN_EXISTS(TEMP_0_CS)
+    #error "TEMP_SENSOR_REDUNDANT MAX Thermocouple with TEMP_SENSOR_REDUNDANT_SOURCE 0 requires TEMP_0_CS_PIN."
+  #elif TEMP_SENSOR_REDUNDANT_IS_MAX_TC && TEMP_SENSOR_REDUNDANT_SOURCE == 1 && !PIN_EXISTS(TEMP_1_CS)
+    #error "TEMP_SENSOR_REDUNDANT MAX Thermocouple with TEMP_SENSOR_REDUNDANT_SOURCE 1 requires TEMP_1_CS_PIN."
   #endif
 #endif
 
+/**
+ * Test Sensor & Heater pin combos.
+ * Pins and Sensor IDs must be set for each heater
+ */
+#if !ANY_PIN(TEMP_0, TEMP_0_CS)
+  #error "TEMP_0_PIN or TEMP_0_CS_PIN not defined for this board."
+#elif !HAS_HEATER_0 && EXTRUDERS
+  #error "HEATER_0_PIN not defined for this board."
+#elif TEMP_SENSOR_0_IS_MAX_TC && !PIN_EXISTS(TEMP_0_CS)
+  #error "TEMP_SENSOR_0 MAX thermocouple requires TEMP_0_CS_PIN."
+#elif HAS_HOTEND && !HAS_TEMP_HOTEND && !TEMP_SENSOR_0_IS_DUMMY
+  #error "TEMP_0_PIN (required for TEMP_SENSOR_0) not defined for this board."
+#elif EITHER(HAS_MULTI_HOTEND, HEATERS_PARALLEL) && !HAS_HEATER_1
+  #error "HEATER_1_PIN is not defined. TEMP_SENSOR_1 might not be set, or the board (not EEB / EEF?) doesn't define a pin."
+#endif
+
 #if HAS_MULTI_HOTEND
-  #if TEMP_SENSOR_1_IS_MAX6675 && !ANY_PIN(MAX6675_SS2, MAX31855_CS2, MAX31865_CS2, MAX6675_CS2)
-    #error "TEMP_SENSOR_1 requires a MAX6675_SS2_PIN, MAX6675_CS2_PIN, MAX31855_CS2_PIN, or MAX31865_CS2_PIN."
+  #if TEMP_SENSOR_1_IS_MAX_TC && !PIN_EXISTS(TEMP_1_CS)
+    #error "TEMP_SENSOR_1 MAX thermocouple requires TEMP_1_CS_PIN."
   #elif TEMP_SENSOR_1 == 0
     #error "TEMP_SENSOR_1 is required with 2 or more HOTENDS."
-  #elif !ANY_PIN(TEMP_1, MAX6675_SS2) && !TEMP_SENSOR_1_IS_DUMMY
-    #error "TEMP_1_PIN or MAX6675_SS2_PIN not defined for this board."
+  #elif !ANY_PIN(TEMP_1, TEMP_1_CS) && !TEMP_SENSOR_1_IS_DUMMY
+    #error "TEMP_1_PIN or TEMP_1_CS_PIN not defined for this board."
   #endif
   #if HOTENDS > 2
     #if TEMP_SENSOR_2 == 0
@@ -2148,14 +2182,31 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS
   #error "TEMP_SENSOR_6 shouldn't be set with only 1 HOTEND."
 #elif TEMP_SENSOR_7 != 0
   #error "TEMP_SENSOR_7 shouldn't be set with only 1 HOTEND."
-#endif
+#endif // HAS_MULTI_HOTEND
 
+/**
+ * Pins must be set for temp sensors, with some other feature requirements.
+ */
 #if TEMP_SENSOR_CHAMBER && !PIN_EXISTS(TEMP_CHAMBER)
   #error "TEMP_SENSOR_CHAMBER requires TEMP_CHAMBER_PIN."
 #endif
 
-#if TEMP_SENSOR_COOLER && !(PIN_EXISTS(TEMP_COOLER) && ENABLED(LASER_FEATURE))
-  #error "TEMP_SENSOR_COOLER requires LASER_FEATURE and TEMP_COOLER_PIN."
+#if TEMP_SENSOR_COOLER
+  #if !PIN_EXISTS(TEMP_COOLER)
+    #error "TEMP_SENSOR_COOLER requires TEMP_COOLER_PIN."
+  #elif DISABLED(LASER_FEATURE)
+    #error "TEMP_SENSOR_COOLER requires LASER_FEATURE."
+  #endif
+#endif
+
+#if TEMP_SENSOR_PROBE
+  #if !PIN_EXISTS(TEMP_PROBE)
+    #error "TEMP_SENSOR_PROBE requires TEMP_PROBE_PIN."
+  #elif !HAS_TEMP_ADC_PROBE
+    #error "TEMP_PROBE_PIN must be an ADC pin."
+  #elif DISABLED(FIX_MOUNTED_PROBE)
+    #error "TEMP_SENSOR_PROBE shouldn't be set without FIX_MOUNTED_PROBE."
+  #endif
 #endif
 
 #if ENABLED(LASER_COOLANT_FLOW_METER) && !(PIN_EXISTS(FLOWMETER) && ENABLED(LASER_FEATURE))
@@ -2186,41 +2237,6 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS
   #endif
 #endif
 
-#if TEMP_SENSOR_PROBE
-  #if !PIN_EXISTS(TEMP_PROBE)
-    #error "TEMP_SENSOR_PROBE requires TEMP_PROBE_PIN."
-  #elif !HAS_TEMP_ADC_PROBE
-    #error "TEMP_PROBE_PIN must be an ADC pin."
-  #elif DISABLED(FIX_MOUNTED_PROBE)
-    #error "TEMP_SENSOR_PROBE shouldn't be set without FIX_MOUNTED_PROBE."
-  #endif
-#endif
-
-#if TEMP_SENSOR_IS_MAX(0, MAX31865) && !(defined(MAX31865_SENSOR_OHMS_0) && defined(MAX31865_CALIBRATION_OHMS_0))
-  #error "MAX31865_SENSOR_OHMS_0 and MAX31865_CALIBRATION_OHMS_0 must be set if TEMP_SENSOR_0/TEMP_SENSOR_REDUNDANT is MAX31865."
-#elif TEMP_SENSOR_IS_MAX(1, MAX31865) && !(defined(MAX31865_SENSOR_OHMS_1) && defined(MAX31865_CALIBRATION_OHMS_1))
-  #error "MAX31865_SENSOR_OHMS_1 and MAX31865_CALIBRATION_OHMS_1 must be set if TEMP_SENSOR_1/TEMP_SENSOR_REDUNDANT is MAX31865."
-#endif
-
-/**
- * Test Heater, Temp Sensor, and Extruder Pins
- */
-#if !HAS_HEATER_0 && EXTRUDERS
-  #error "HEATER_0_PIN not defined for this board."
-#elif !ANY_PIN(TEMP_0, MAX6675_SS)
-  #error "TEMP_0_PIN or MAX6675_SS not defined for this board."
-#endif
-
-#if HAS_EXTRUDERS
-  #if ((defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__)) && !PINS_EXIST(E0_STEP, E0_DIR))
-    #error "E0_STEP_PIN or E0_DIR_PIN not defined for this board."
-  #elif ( !(defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__)) && (!PINS_EXIST(E0_STEP, E0_DIR) || !HAS_E0_ENABLE))
-    #error "E0_STEP_PIN, E0_DIR_PIN, or E0_ENABLE_PIN not defined for this board."
-  #elif EXTRUDERS && TEMP_SENSOR_0 == 0
-    #error "TEMP_SENSOR_0 is required if there are any extruders."
-  #endif
-#endif
-
 /**
  * Temperature status LEDs
  */
@@ -2262,6 +2278,16 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS
 /**
  * Test Extruder Stepper Pins
  */
+#if HAS_EXTRUDERS
+  #if ((defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__)) && !PINS_EXIST(E0_STEP, E0_DIR))
+    #error "E0_STEP_PIN or E0_DIR_PIN not defined for this board."
+  #elif ( !(defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__)) && (!PINS_EXIST(E0_STEP, E0_DIR) || !HAS_E0_ENABLE))
+    #error "E0_STEP_PIN, E0_DIR_PIN, or E0_ENABLE_PIN not defined for this board."
+  #elif EXTRUDERS && TEMP_SENSOR_0 == 0
+    #error "TEMP_SENSOR_0 is required if there are any extruders."
+  #endif
+#endif
+
 #if E_STEPPERS
   #if !(PINS_EXIST(E0_STEP, E0_DIR) && HAS_E0_ENABLE)
     #error "E0_STEP_PIN, E0_DIR_PIN, or E0_ENABLE_PIN not defined for this board."

+ 500 - 0
Marlin/src/libs/MAX31865.cpp

@@ -0,0 +1,500 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ *
+ * Based on Sprinter and grbl.
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+/**
+ * Based on Based on Adafruit MAX31865 library:
+ *
+ * This is a library for the Adafruit PT100/P1000 RTD Sensor w/MAX31865
+ * Designed specifically to work with the Adafruit RTD Sensor
+ * https://www.adafruit.com/products/3328
+ *
+ * This sensor uses SPI to communicate, 4 pins are required to interface.
+ *
+ * Adafruit invests time and resources providing this open source code,
+ * please support Adafruit and open-source hardware by purchasing
+ * products from Adafruit!
+ *
+ * Written by Limor Fried/Ladyada for Adafruit Industries.
+ *
+ * Modifications by JoAnn Manges (@GadgetAngel)
+ * Copyright (c) 2020, JoAnn Manges
+ * All rights reserved.
+ */
+
+// Useful for RTD debugging.
+//#define MAX31865_DEBUG
+//#define MAX31865_DEBUG_SPI
+
+//TODO: switch to SPIclass/SoftSPI
+
+#include "../inc/MarlinConfig.h"
+
+#if HAS_MAX31865 && !LIB_USR_MAX31865
+
+#include "MAX31865.h"
+
+// The maximum speed the MAX31865 can do is 5 MHz
+SPISettings MAX31865::spiConfig = SPISettings(
+  #if defined(TARGET_LPC1768)
+    SPI_QUARTER_SPEED
+  #elif defined(ARDUINO_ARCH_STM32)
+    SPI_CLOCK_DIV4
+  #else
+    500000
+  #endif
+  , MSBFIRST
+  , SPI_MODE_1 // CPOL0 CPHA1
+);
+
+#ifndef LARGE_PINMAP
+
+  /**
+   * Create the interface object using software (bitbang) SPI for PIN values
+   * less than or equal to 127.
+   *
+   * @param spi_cs    the SPI CS pin to use
+   * @param spi_mosi  the SPI MOSI pin to use
+   * @param spi_miso  the SPI MISO pin to use
+   * @param spi_clk   the SPI clock pin to use
+  */
+  MAX31865::MAX31865(int8_t spi_cs, int8_t spi_mosi, int8_t spi_miso, int8_t spi_clk) {
+    _cs = spi_cs;
+    _mosi = spi_mosi;
+    _miso = spi_miso;
+    _sclk = spi_clk;
+  }
+
+  /**
+   * Create the interface object using hardware SPI for PIN for PIN values less
+   * than or equal to 127.
+   *
+   * @param spi_cs  the SPI CS pin to use along with the default SPI device
+   */
+  MAX31865::MAX31865(int8_t spi_cs) {
+    _cs = spi_cs;
+    _sclk = _miso = _mosi = -1;
+  }
+
+#else
+
+  /**
+   * Create the interface object using software (bitbang) SPI for PIN values
+   * which are larger than 127. If you have PIN values less than or equal to
+   * 127 use the other call for SW SPI.
+   *
+   * @param spi_cs       the SPI CS pin to use
+   * @param spi_mosi     the SPI MOSI pin to use
+   * @param spi_miso     the SPI MISO pin to use
+   * @param spi_clk      the SPI clock pin to use
+   * @param pin_mapping  set to 1 for positive pin values
+   */
+  MAX31865::MAX31865(uint32_t spi_cs, uint32_t spi_mosi,
+                     uint32_t spi_miso, uint32_t spi_clk,
+                     uint8_t pin_mapping) {
+    _cs = spi_cs;
+    _mosi = spi_mosi;
+    _miso = spi_miso;
+    _sclk = spi_clk;
+  }
+
+  /**
+   * Create the interface object using hardware SPI for PIN values which are
+   * larger than 127. If you have PIN values less than or equal to 127 use
+   * the other call for HW SPI.
+   *
+   * @param spi_cs       the SPI CS pin to use along with the default SPI device
+   * @param pin_mapping  set to 1 for positive pin values
+   */
+  MAX31865::MAX31865(uint32_t spi_cs, uint8_t pin_mapping) {
+    _cs = spi_cs;
+    _sclk = _miso = _mosi = -1UL;  //-1UL or 0xFFFFFFFF or 4294967295
+  }
+
+#endif // LARGE_PINMAP
+
+
+/**
+ *
+ * Instance & Class methods
+ *
+ */
+
+
+/**
+ * Initialize the SPI interface and set the number of RTD wires used
+ *
+ * @param wires  The number of wires in enum format. Can be MAX31865_2WIRE, MAX31865_3WIRE, or MAX31865_4WIRE.
+ * @param zero   The resistance of the RTD at 0 degC, in ohms.
+ * @param ref    The resistance of the reference resistor, in ohms.
+ */
+void MAX31865::begin(max31865_numwires_t wires, float zero, float ref) {
+  Rzero = zero;
+  Rref = ref;
+
+  OUT_WRITE(_cs, HIGH);
+
+  if (_sclk != TERN(LARGE_PINMAP, -1UL, -1)) {
+    // define pin modes for Software SPI
+    #ifdef MAX31865_DEBUG
+      SERIAL_ECHOLN("Initializing MAX31865 Software SPI");
+    #endif
+
+    OUT_WRITE(_sclk, LOW);
+    SET_OUTPUT(_mosi);
+    SET_INPUT(_miso);
+  } else {
+    // start and configure hardware SPI
+    #ifdef MAX31865_DEBUG
+      SERIAL_ECHOLN("Initializing MAX31865 Hardware SPI");
+    #endif
+
+    SPI.begin();
+  }
+
+  setWires(wires);
+  enableBias(false);
+  autoConvert(false);
+  clearFault();
+
+  #ifdef MAX31865_DEBUG_SPI
+    #ifndef LARGE_PINMAP
+      SERIAL_ECHOLNPAIR(
+        "Regular begin call with _cs: ", _cs,
+        " _miso: ", _miso,
+        " _sclk: ", _sclk,
+        " _mosi: ", _mosi
+      );
+    #else
+      SERIAL_ECHOLNPAIR(
+        "LARGE_PINMAP begin call with _cs: ", _cs,
+        " _miso: ", _miso,
+        " _sclk: ", _sclk,
+        " _mosi: ", _mosi
+      );
+    #endif // LARGE_PINMAP
+
+    SERIAL_ECHOLNPAIR("config: ", readRegister8(MAX31856_CONFIG_REG));
+    SERIAL_EOL();
+  #endif // MAX31865_DEBUG_SPI
+}
+
+/**
+ * Read the raw 8-bit FAULTSTAT register
+ *
+ * @return The raw unsigned 8-bit FAULT status register
+ */
+uint8_t MAX31865::readFault() {
+  return readRegister8(MAX31856_FAULTSTAT_REG);
+}
+
+/**
+ * Clear all faults in FAULTSTAT.
+ */
+void MAX31865::clearFault() {
+  setConfig(MAX31856_CONFIG_FAULTSTAT, 1);
+}
+
+/**
+ * Whether we want to have continuous conversions (50/60 Hz)
+ *
+ * @param b  If true, auto conversion is enabled
+ */
+void MAX31865::autoConvert(bool b) {
+  setConfig(MAX31856_CONFIG_MODEAUTO, b);
+}
+
+/**
+ * Whether we want filter out 50Hz noise or 60Hz noise
+ *
+ * @param b  If true, 50Hz noise is filtered, else 60Hz(default)
+ */
+void MAX31865::enable50HzFilter(bool b) {
+  setConfig(MAX31856_CONFIG_FILT50HZ, b);
+}
+
+/**
+ * Enable the bias voltage on the RTD sensor
+ *
+ * @param b  If true bias is enabled, else disabled
+ */
+void MAX31865::enableBias(bool b) {
+  setConfig(MAX31856_CONFIG_BIAS, b);
+
+  // From the datasheet:
+  // Note that if VBIAS is off (to reduce supply current between conversions), any filter
+  // capacitors at the RTDIN inputs need to charge before an accurate conversion can be
+  // performed. Therefore, enable VBIAS and wait at least 10.5 time constants of the input
+  // RC network plus an additional 1ms before initiating the conversion.
+  if (b)
+    DELAY_US(11500); //11.5ms
+}
+
+/**
+ * Start a one-shot temperature reading.
+ */
+void MAX31865::oneShot() {
+  setConfig(MAX31856_CONFIG_1SHOT, 1);
+
+  // From the datasheet:
+  // Note that a single conversion requires approximately 52ms in 60Hz filter
+  // mode or 62.5ms in 50Hz filter mode to complete. 1-Shot is a self-clearing bit.
+  // TODO: switch this out depeding on the filter mode.
+  DELAY_US(65000); // 65ms
+}
+
+/**
+ * How many wires we have in our RTD setup, can be MAX31865_2WIRE,
+ * MAX31865_3WIRE, or MAX31865_4WIRE
+ *
+ * @param wires The number of wires in enum format
+ */
+void MAX31865::setWires(max31865_numwires_t wires) {
+  uint8_t t = readRegister8(MAX31856_CONFIG_REG);
+  if (wires == MAX31865_3WIRE)
+    t |= MAX31856_CONFIG_3WIRE;
+  else // 2 or 4 wire
+    t &= ~MAX31856_CONFIG_3WIRE;
+  writeRegister8(MAX31856_CONFIG_REG, t);
+}
+
+/**
+ * Read the raw 16-bit value from the RTD_REG in one shot mode. This will include
+ * the fault bit, D0.
+ *
+ * @return The raw unsigned 16-bit register value with ERROR bit attached, NOT temperature!
+ */
+uint16_t MAX31865::readRaw() {
+  clearFault();
+  enableBias(true);
+
+  oneShot();
+  uint16_t rtd = readRegister16(MAX31856_RTDMSB_REG);
+
+  #ifdef MAX31865_DEBUG
+    SERIAL_ECHOLNPAIR("RTD MSB:", (rtd >> 8), "  RTD LSB:", (rtd & 0x00FF));
+  #endif
+
+  // Disable the bias to lower power dissipation between reads.
+  // If the ref resistor heats up, the temperature reading will be skewed.
+  enableBias(false);
+
+  return rtd;
+}
+
+/**
+ * Calulate and return the resistance value of the connected RTD.
+ *
+ * @param  refResistor The value of the matching reference resistor, usually 430 or 4300
+ * @return             The raw RTD resistance value, NOT temperature!
+ */
+float MAX31865::readResistance() {
+  // Strip the error bit (D0) and convert to a float ratio.
+  // less precise method: (readRaw() * Rref) >> 16
+  return (((readRaw() >> 1) / 32768.0f) * Rref);
+}
+
+/**
+ * Read the RTD and pass it to temperature(float) for calculation.
+ *
+ * @return  Temperature in C
+ */
+float MAX31865::temperature() {
+  return temperature(readResistance());
+}
+
+/**
+ * Given the 15-bit ADC value, calculate the resistance and pass it to temperature(float) for calculation.
+ *
+ * @return  Temperature in C
+ */
+float MAX31865::temperature(uint16_t adcVal) {
+  return temperature(((adcVal) / 32768.0f) * Rref);
+}
+
+/**
+ * Calculate the temperature in C from the RTD resistance.
+ * Uses the technique outlined in this PDF:
+ * http://www.analog.com/media/en/technical-documentation/application-notes/AN709_0.pdf
+ *
+ * @param    Rrtd  the resistance value in ohms
+ * @return         the temperature in degC
+ */
+float MAX31865::temperature(float Rrtd) {
+  float temp = (RTD_Z1 + sqrt(RTD_Z2 + (RTD_Z3 * Rrtd))) / RTD_Z4;
+
+  // From the PDF...
+  //
+  // The previous equation is valid only for temperatures of 0°C and above.
+  // The equation for RRTD(t) that defines negative temperature behavior is a
+  // fourth-order polynomial (after expanding the third term) and is quite
+  // impractical to solve for a single expression of temperature as a function
+  // of resistance.
+  //
+  if (temp < 0) {
+    Rrtd = (Rrtd / Rzero) * 100; // normalize to 100 ohm
+    float rpoly = Rrtd;
+
+    temp = -242.02 + (2.2228 * rpoly);
+    rpoly *= Rrtd; // square
+    temp += 2.5859e-3 * rpoly;
+    rpoly *= Rrtd; // ^3
+    temp -= 4.8260e-6 * rpoly;
+    rpoly *= Rrtd; // ^4
+    temp -= 2.8183e-8 * rpoly;
+    rpoly *= Rrtd; // ^5
+    temp += 1.5243e-10 * rpoly;
+  }
+
+  return temp;
+}
+
+//
+// private:
+//
+
+
+/**
+ * Set a value in the configuration register.
+ *
+ * @param config  8-bit value for the config item
+ * @param enable  whether to enable or disable the value
+ */
+void MAX31865::setConfig(uint8_t config, bool enable) {
+  uint8_t t = readRegister8(MAX31856_CONFIG_REG);
+  if (enable)
+    t |= config;
+  else
+    t &= ~config; // disable
+  writeRegister8(MAX31856_CONFIG_REG, t);
+}
+
+/**
+ * Read a single byte from the specified register address.
+ *
+ * @param  addr  the register address
+ * @return       the register contents
+ */
+uint8_t MAX31865::readRegister8(uint8_t addr) {
+  uint8_t ret = 0;
+  readRegisterN(addr, &ret, 1);
+
+  return ret;
+}
+
+/**
+ * Read two bytes: 1 from the specified register address, and 1 from the next address.
+ *
+ * @param  addr  the first register address
+ * @return       both register contents as a single 16-bit int
+ */
+uint16_t MAX31865::readRegister16(uint8_t addr) {
+  uint8_t buffer[2] = {0, 0};
+  readRegisterN(addr, buffer, 2);
+
+  uint16_t ret = buffer[0];
+  ret <<= 8;
+  ret |= buffer[1];
+
+  return ret;
+}
+
+/**
+ * Read +n+ bytes from a specified address into +buffer+. Set D7 to 0 to specify a read.
+ *
+ * @param addr    the first register address
+ * @param buffer  storage for the read bytes
+ * @param n       the number of bytes to read
+ */
+void MAX31865::readRegisterN(uint8_t addr, uint8_t buffer[], uint8_t n) {
+  addr &= 0x7F; // make sure top bit is not set
+  if (_sclk == TERN(LARGE_PINMAP, -1UL, -1))
+    SPI.beginTransaction(spiConfig);
+  else
+    WRITE(_sclk, LOW);
+
+  WRITE(_cs, LOW);
+  spixfer(addr);
+
+  while (n--) {
+    buffer[0] = spixfer(0xFF);
+    #ifdef MAX31865_DEBUG_SPI
+      SERIAL_ECHOLNPAIR("buffer read ", n, " data: ", buffer[0]);
+    #endif
+    buffer++;
+  }
+
+  if (_sclk == TERN(LARGE_PINMAP, -1UL, -1))
+    SPI.endTransaction();
+
+  WRITE(_cs, HIGH);
+}
+
+/**
+ * Write an 8-bit value to a register. Set D7 to 1 to specify a write.
+ *
+ * @param addr  the address to write to
+ * @param data  the data to write
+ */
+void MAX31865::writeRegister8(uint8_t addr, uint8_t data) {
+  if (_sclk == TERN(LARGE_PINMAP, -1UL, -1))
+    SPI.beginTransaction(spiConfig);
+  else
+    WRITE(_sclk, LOW);
+
+  WRITE(_cs, LOW);
+
+  spixfer(addr | 0x80); // make sure top bit is set
+  spixfer(data);
+
+  if (_sclk == TERN(LARGE_PINMAP, -1UL, -1))
+    SPI.endTransaction();
+
+  WRITE(_cs, HIGH);
+}
+
+/**
+ * Transfer SPI data +x+ and read the response. From the datasheet...
+ * Input data (SDI) is latched on the internal strobe edge and output data (SDO) is
+ * shifted out on the shift edge. There is one clock for each bit transferred.
+ * Address and data bits are transferred in groups of eight, MSB first.
+ *
+ * @param  x  an 8-bit chunk of data to write
+ * @return    the 8-bit response
+ */
+uint8_t MAX31865::spixfer(uint8_t x) {
+  if (_sclk == TERN(LARGE_PINMAP, -1UL, -1))
+    return SPI.transfer(x);
+
+  uint8_t reply = 0;
+  for (int i = 7; i >= 0; i--) {
+    reply <<= 1;
+    WRITE(_sclk, HIGH);
+    WRITE(_mosi, x & (1 << i));
+    WRITE(_sclk, LOW);
+    if (READ(_miso))
+      reply |= 1;
+  }
+
+  return reply;
+}
+
+#endif // HAS_MAX31865 && !LIB_USR_MAX31865

+ 131 - 0
Marlin/src/libs/MAX31865.h

@@ -0,0 +1,131 @@
+/**
+ * Marlin 3D Printer Firmware
+ * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
+ *
+ * Based on Sprinter and grbl.
+ * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+/**
+ * Based on Adafruit MAX31865 library:
+ *
+ * This is a library for the Adafruit PT100/P1000 RTD Sensor w/MAX31865
+ * Designed specifically to work with the Adafruit RTD Sensor
+ * https://www.adafruit.com/products/3328
+ *
+ * This sensor uses SPI to communicate, 4 pins are required to interface.
+ *
+ * Adafruit invests time and resources providing this open source code,
+ * please support Adafruit and open-source hardware by purchasing
+ * products from Adafruit!
+ *
+ * Written by Limor Fried/Ladyada for Adafruit Industries.
+ *
+ * Modifications by JoAnn Manges (@GadgetAngel)
+ * Copyright (c) 2020, JoAnn Manges
+ * All rights reserved.
+ */
+#pragma once
+
+#include "../inc/MarlinConfig.h"
+#include "../HAL/shared/Delay.h"
+#include HAL_PATH(../HAL, MarlinSPI.h)
+
+#define MAX31856_CONFIG_REG 0x00
+#define MAX31856_CONFIG_BIAS 0x80
+#define MAX31856_CONFIG_MODEAUTO 0x40
+#define MAX31856_CONFIG_MODEOFF 0x00
+#define MAX31856_CONFIG_1SHOT 0x20
+#define MAX31856_CONFIG_3WIRE 0x10
+#define MAX31856_CONFIG_24WIRE 0x00
+#define MAX31856_CONFIG_FAULTSTAT 0x02
+#define MAX31856_CONFIG_FILT50HZ 0x01
+#define MAX31856_CONFIG_FILT60HZ 0x00
+
+#define MAX31856_RTDMSB_REG 0x01
+#define MAX31856_RTDLSB_REG 0x02
+#define MAX31856_HFAULTMSB_REG 0x03
+#define MAX31856_HFAULTLSB_REG 0x04
+#define MAX31856_LFAULTMSB_REG 0x05
+#define MAX31856_LFAULTLSB_REG 0x06
+#define MAX31856_FAULTSTAT_REG 0x07
+
+#define MAX31865_FAULT_HIGHTHRESH 0x80  // D7
+#define MAX31865_FAULT_LOWTHRESH 0x40   // D6
+#define MAX31865_FAULT_REFINLOW 0x20    // D5
+#define MAX31865_FAULT_REFINHIGH 0x10   // D4
+#define MAX31865_FAULT_RTDINLOW 0x08    // D3
+#define MAX31865_FAULT_OVUV 0x04        // D2
+
+// http://www.analog.com/media/en/technical-documentation/application-notes/AN709_0.pdf
+// constants for calulating temperature from the measured RTD resistance.
+#define RTD_Z1 -0.0039083
+#define RTD_Z2 0.00001758480889
+#define RTD_Z3 -0.0000000231
+#define RTD_Z4 -0.000001155
+
+typedef enum max31865_numwires {
+  MAX31865_2WIRE = 0,
+  MAX31865_3WIRE = 1,
+  MAX31865_4WIRE = 0
+} max31865_numwires_t;
+
+/* Interface class for the MAX31865 RTD Sensor reader */
+class MAX31865 {
+private:
+  static SPISettings spiConfig;
+
+  TERN(LARGE_PINMAP, uint32_t, uint8_t) _sclk, _miso, _mosi, _cs;
+  float Rzero, Rref;
+
+  void setConfig(uint8_t config, bool enable);
+
+  void readRegisterN(uint8_t addr, uint8_t buffer[], uint8_t n);
+  uint8_t readRegister8(uint8_t addr);
+  uint16_t readRegister16(uint8_t addr);
+
+  void writeRegister8(uint8_t addr, uint8_t reg);
+  uint8_t spixfer(uint8_t addr);
+
+public:
+  #ifdef LARGE_PINMAP
+    MAX31865(uint32_t spi_cs, uint8_t pin_mapping);
+    MAX31865(uint32_t spi_cs, uint32_t spi_mosi, uint32_t spi_miso,
+             uint32_t spi_clk, uint8_t pin_mapping);
+  #else
+    MAX31865(int8_t spi_cs);
+    MAX31865(int8_t spi_cs, int8_t spi_mosi, int8_t spi_miso,
+             int8_t spi_clk);
+  #endif
+
+  void begin(max31865_numwires_t wires, float zero, float ref);
+
+  uint8_t readFault();
+  void clearFault();
+
+  void setWires(max31865_numwires_t wires);
+  void autoConvert(bool b);
+  void enable50HzFilter(bool b);
+  void enableBias(bool b);
+  void oneShot();
+
+  uint16_t readRaw();
+  float readResistance();
+  float temperature();
+  float temperature(uint16_t adcVal);
+  float temperature(float Rrtd);
+};

Some files were not shown because too many files changed in this diff