Skip to content

Commit

Permalink
Revisiting HW vs SW SPI
Browse files Browse the repository at this point in the history
Dove in to how Hardware vs Software SPI is selected, and realized I was going about the
'SEPARATE' and 'SW' stuff all wrong. Our choices are more-or-less HW SPI on the default
bus, or Software SPI otherwise. I added a config option to force hardware SPI for pins
files that have a SCK/MISO/MOSI defined (i.e. for built-in SoftSPI Thermocouples), and
added separate SoftSPI busses for probes on different busses.
  • Loading branch information
slowbro committed Jun 25, 2021
1 parent b3527cb commit ab1b48e
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 37 deletions.
11 changes: 7 additions & 4 deletions Marlin/Configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -399,13 +399,18 @@
*
* Temperature sensors available:
*
* SPI RTD/Thermocouple Boards - NOTE: Ensure TEMP_n_CS_PIN is set in your pins file for each TEMP_SENSOR_n, as well as the
* ------- following for Software SPI: TEMP_n_MOSI_PIN (MAX31865 only), TEMP_n_MISO_PIN, TEMP_n_SCK_PIN.
* 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
Expand Down Expand Up @@ -496,10 +501,8 @@
#define DUMMY_THERMISTOR_999_VALUE 100

// Number of probe wires, and resistor values when using MAX31865 sensors (-5) on TEMP_SENSOR_0 / 1
//#define MAX31865_SENSOR_WIRES_0 2 // Number of wires the connected probe uses, 2-4 (default: 2)
//#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_SENSOR_WIRES_1 2
//#define MAX31865_SENSOR_OHMS_1 100
//#define MAX31865_CALIBRATION_OHMS_1 430

Expand Down
11 changes: 11 additions & 0 deletions Marlin/Configuration_adv.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,17 @@
#define REDUNDANT_BETA 3950 // Beta value
#endif

/**
* 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
*/
//#define TEMP_SENSOR_0_FORCE_HW_SPI
//#define TEMP_SENSOR_1_FORCE_HW_SPI
//#define MAX31865_SENSOR_WIRES_0 2
//#define MAX31865_SENSOR_WIRES_1 2


//
// Hephestos 2 24V heated bed upgrade kit.
// https://store.bq.com/en/heated-bed-kit-hephestos2
Expand Down
12 changes: 6 additions & 6 deletions Marlin/src/inc/Conditionals_post.h
Original file line number Diff line number Diff line change
Expand Up @@ -836,11 +836,11 @@
#endif

// Software SPI - enable if MISO/SCK are defined.
#if PIN_EXISTS(TEMP_0_MISO) && PIN_EXISTS(TEMP_0_SCK)
#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)
#warning "TEMP_SENSOR_0 MAX31865 also needs TEMP_0_MOSI_PIN defined for Software SPI; defaulting to Hardware SPI."
#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_USES_SW_SPI 1
#define TEMP_SENSOR_0_HAS_SPI_PINS 1
#endif
#endif

Expand Down Expand Up @@ -905,11 +905,11 @@
#endif

// Software SPI - enable if MISO/SCK are defined.
#if PIN_EXISTS(TEMP_1_MISO) && PIN_EXISTS(TEMP_1_SCK)
#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)
#warning "TEMP_SENSOR_1 MAX31865 also needs TEMP_1_MOSI_PIN defined for Software SPI; defaulting to Hardware SPI."
#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_USES_SW_SPI 1
#define TEMP_SENSOR_1_HAS_SPI_PINS 1
#endif
#endif

Expand Down
92 changes: 65 additions & 27 deletions Marlin/src/module/temperature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,19 +89,29 @@
#define HAS_MAXTC_LIBRARIES 1
#endif

// If we have a MAX TC with SCK and MISO pins defined, and we're not using an external library,
// the MAX TC will be read via Hardware SPI on a different pin.
#if (TEMP_SENSOR_IS_ANY_MAX_TC(0) && PINS_EXIST(TEMP_0_SCK, TEMP_0_MOSI))
#define TEMP_SENSOR_0_USES_SEPARATE_SPI 1
#endif
#if (TEMP_SENSOR_IS_ANY_MAX_TC(1) && PINS_EXIST(TEMP_1_SCK, TEMP_1_MOSI))
#define TEMP_SENSOR_1_USES_SEPARATE_SPI 1
// If we have a MAX TC with SCK and MISO pins defined, it's either on a separate/dedicated Hardware
// SPI bus, or some pins for Software SPI. Alternate Hardware SPI buses are not supported yet, so
// your SPI options are:
//
// 1. Only CS pin(s) defined: Hardware SPI on the default bus (usually the SD card SPI).
// 2. CS, MISO, and SCK pins defined: Software SPI on a separate bus, as defined by MISO, SCK.
// 3. CS, MISO, and SCK pins w/ FORCE_HW_SPI: Hardware SPI on the default bus, ignoring MISO, SCK.
//
#if TEMP_SENSOR_IS_ANY_MAX_TC(0) && TEMP_SENSOR_0_HAS_SPI_PINS && DISABLED(TEMP_SENSOR_0_FORCE_HW_SPI)
#define TEMP_SENSOR_0_USES_SW_SPI 1
#endif
#if TEMP_SENSOR_IS_ANY_MAX_TC(1) && TEMP_SENSOR_1_HAS_SPI_PINS && DISABLED(TEMP_SENSOR_1_FORCE_HW_SPI)
#define TEMP_SENSOR_1_USES_SW_SPI 1

// Two sensors using the same Software SPI bus
#if TEMP_SENSOR_0_USES_SW_SPI && TEMP_0_SCK_PIN == TEMP_1_SCK_PIN && TEMP_0_MISO_PIN == TEMP_1_MISO_PIN
#define TEMP_SENSOR_SAME_SPI_BUS 1
#endif
#endif

#if TEMP_SENSOR_0_USES_SEPARATE_SPI || TEMP_SENSOR_1_USES_SEPARATE_SPI
#if TEMP_SENSOR_0_USES_SW_SPI || TEMP_SENSOR_1_USES_SW_SPI
#include "../libs/private_spi.h"
#define HAS_MAXTC_SEPARATE_SPI 1
#define HAS_MAXTC_SW_SPI 1
#endif

#if ENABLED(PID_EXTRUSION_SCALING)
Expand Down Expand Up @@ -189,13 +199,22 @@ const char str_t_thermal_runaway[] PROGMEM = STR_T_THERMAL_RUNAWAY,
//
#if HAS_MAX_TC

#if HAS_MAXTC_SEPARATE_SPI
//TODO: what about multi_tc?
template<uint8_t MisoPin, uint8_t MosiPin, uint8_t SckPin> SoftSPI<MisoPin, MosiPin, SckPin> SPIclass<MisoPin, MosiPin, SckPin>::softSPI;
SPIclass<TEMP_0_MISO_PIN, SD_MOSI_PIN, TEMP_0_SCK_PIN> max_tc_spi;
#if !HAS_MAXTC_LIBRARIES
// Initialize SoftSPI for non-lib Software SPI; Libraries take care of it themselves.
#if TEMP_SENSOR_0_USES_SW_SPI
template<uint8_t MisoPin, uint8_t MosiPin, uint8_t SckPin> SoftSPI<MisoPin, MosiPin, SckPin> SPIclass<MisoPin, MosiPin, SckPin>::softSPI;
SPIclass<TEMP_0_MISO_PIN, SD_MOSI_PIN, TEMP_0_SCK_PIN> max_tc_spi_0;
#endif
#if TEMP_SENSOR_1_USES_SW_SPI
#if TEMP_SENSOR_SAME_SPI_BUS
SPIclass &max_tc_spi_1 = max_tc_spi_0;
#else
template<uint8_t MisoPin, uint8_t MosiPin, uint8_t SckPin> SoftSPI<MisoPin, MosiPin, SckPin> SPIclass<MisoPin, MosiPin, SckPin>::softSPI;
SPIclass<TEMP_1_MISO_PIN, SD_MOSI_PIN, TEMP_1_SCK_PIN> max_tc_spi_1;
#endif
#endif
#endif


#define MAXTC_INIT(n, M) \
MAX##M max##M##_##n = MAX##M( \
TEMP_##n##_CS_PIN \
Expand Down Expand Up @@ -226,6 +245,7 @@ const char str_t_thermal_runaway[] PROGMEM = STR_T_THERMAL_RUNAWAY,
#endif

// MAX31865 always uses a library, unlike '55 & 6675
// FIXME: The MAX31865 lib should use SoftSPI/MarlinSerial instead of SPI/raw pin manipulation
#if HAS_MAX31865
#define _MAX31865_0_SW TEMP_SENSOR_0_USES_SW_SPI
#define _MAX31865_1_SW TEMP_SENSOR_1_USES_SW_SPI
Expand Down Expand Up @@ -2189,8 +2209,13 @@ void Temperature::init() {
INIT_FAN_PIN(CONTROLLER_FAN_PIN);
#endif

#if HAS_MAXTC_SEPARATE_SPI
max_tc_spi.init();
#if !HAS_MAXTC_LIBRARIES
#if TEMP_SENSOR_0_USES_SW_SPI
max_tc_spi_0.init();
#endif
#if TEMP_SENSOR_1_USES_SW_SPI && !TEMP_SENSOR_SAME_SPI_BUS
max_tc_spi_1.init();
#endif
#endif

HAL_adc_init();
Expand Down Expand Up @@ -2618,21 +2643,29 @@ void Temperature::disable_all_heaters() {
static celsius_t max_tc_temp_previous[MAX_TC_COUNT] = { 0 };
#define THERMO_TEMP(I) max_tc_temp_previous[I]
#define THERMO_SEL(A,B) (hindex ? (B) : (A))
#define MAXTC_WRITE(V) do{ switch (hindex) { case 1: WRITE(TEMP_1_CS_PIN, V); break; default: WRITE(TEMP_0_CS_PIN, V); } }while(0)
#define MAXTC_CS_WRITE(V) do{ switch (hindex) { case 1: WRITE(TEMP_1_CS_PIN, V); break; default: WRITE(TEMP_0_CS_PIN, V); } }while(0)
#else
// When we have only 1 max tc, THERMO_SEL will pick the appropriate sensor
// variable, and MAXTC_*() macros will be hardcoded to the correct CS pin.
constexpr uint8_t hindex = 0;
#define THERMO_TEMP(I) max_tc_temp
#if TEMP_SENSOR_IS_ANY_MAX_TC(0)
#define THERMO_SEL(A,B) A
#define MAXTC_WRITE(V) WRITE(TEMP_0_CS_PIN, V)
#define MAXTC_CS_WRITE(V) WRITE(TEMP_0_CS_PIN, V)
#else
#define THERMO_SEL(A,B) B
#define MAXTC_WRITE(V) WRITE(TEMP_1_CS_PIN, V)
#define MAXTC_CS_WRITE(V) WRITE(TEMP_1_CS_PIN, V)
#endif
#endif

#if TEMP_SENSOR_0_USES_SW_SPI && TEMP_SENSOR_1_USES_SW_SPI
#define THERMO_SPI_SEL(A,B) (hindex ? (B) : (A))
#elif TEMP_SENSOR_0_USES_SW_SPI
#define THERMO_SPI_SEL(A,B) A
#elif TEMP_SENSOR_1_USES_SW_SPI
#define THERMO_SPI_SEL(A,B) B
#endif

static uint8_t max_tc_errors[MAX_TC_COUNT] = { 0 };

// Return last-read value between readings
Expand All @@ -2644,25 +2677,30 @@ void Temperature::disable_all_heaters() {
max_tc_temp = 0;

#if !HAS_MAXTC_LIBRARIES
#if !HAS_MAXTC_SEPARATE_SPI
// Initialize SPI using the default SPI bus.
#if !HAS_MAXTC_SW_SPI
// Initialize SPI using the default Hardware SPI bus.
// FIXME: spiBegin, spiRec and spiInit doesn't work when soft spi is used.
spiBegin();
spiInit(MAX_TC_SPEED_BITS);
#endif

// Read a big-endian temperature value without using a library
MAXTC_WRITE(LOW); // enable MAXTC
DELAY_NS(100); // Ensure 100ns delay
MAXTC_CS_WRITE(LOW); // enable MAXTC
DELAY_NS(100); // Ensure 100ns delay

// Read a big-endian temperature value without using a library
for (uint8_t i = sizeof(max_tc_temp); i--;) {
max_tc_temp |= TERN(HAS_MAXTC_SEPARATE_SPI, max_tc_spi.receive(), spiRec());
max_tc_temp |= (
#if HAS_MAXTC_SW_SPI
THERMO_SPI_SEL(max_tc_spi_0, max_tc_spi_1).receive()
#else
spiRec()
#endif
);
if (i > 0) max_tc_temp <<= 8; // shift left if not the last byte
}

MAXTC_WRITE(HIGH); // disable MAXTC
MAXTC_CS_WRITE(HIGH); // disable MAXTC
#else
// TODO: a lot of this is references to the GadgetAngel ModM versions...
#if HAS_MAX6675_LIBRARY
MAX6675 &max6675ref = THERMO_SEL(max6675_0, max6675_1);
max_tc_temp = max6675ref.readRaw16();
Expand Down

0 comments on commit ab1b48e

Please sign in to comment.