My Marlin configs for Fabrikator Mini and CTC i3 Pro B
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

MAX31865.cpp 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638
  1. /**
  2. * Marlin 3D Printer Firmware
  3. * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
  4. *
  5. * Based on Sprinter and grbl.
  6. * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
  7. *
  8. * This program is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation, either version 3 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. *
  21. */
  22. /**
  23. * Based on Based on Adafruit MAX31865 library:
  24. *
  25. * This is a library for the Adafruit PT100/P1000 RTD Sensor w/MAX31865
  26. * Designed specifically to work with the Adafruit RTD Sensor
  27. * https://www.adafruit.com/products/3328
  28. *
  29. * This sensor uses SPI to communicate, 4 pins are required to interface.
  30. *
  31. * Adafruit invests time and resources providing this open source code,
  32. * please support Adafruit and open-source hardware by purchasing
  33. * products from Adafruit!
  34. *
  35. * Written by Limor Fried/Ladyada for Adafruit Industries.
  36. *
  37. * Modifications by JoAnn Manges (@GadgetAngel)
  38. * Copyright (c) 2020, JoAnn Manges
  39. * All rights reserved.
  40. */
  41. #include "../inc/MarlinConfig.h"
  42. #if HAS_MAX31865 && !USE_ADAFRUIT_MAX31865
  43. #include "MAX31865.h"
  44. #ifndef MAX31865_MIN_SAMPLING_TIME_MSEC
  45. #define MAX31865_MIN_SAMPLING_TIME_MSEC 0
  46. #endif
  47. #define DEBUG_OUT ENABLED(DEBUG_MAX31865)
  48. #include "../core/debug_out.h"
  49. // The maximum speed the MAX31865 can do is 5 MHz
  50. SPISettings MAX31865::spiConfig = SPISettings(
  51. TERN(TARGET_LPC1768, SPI_QUARTER_SPEED, TERN(ARDUINO_ARCH_STM32, SPI_CLOCK_DIV4, 500000)),
  52. MSBFIRST,
  53. SPI_MODE1 // CPOL0 CPHA1
  54. );
  55. #if DISABLED(LARGE_PINMAP)
  56. /**
  57. * Create the interface object using software (bitbang) SPI for PIN values
  58. * less than or equal to 127.
  59. *
  60. * @param spi_cs the SPI CS pin to use
  61. * @param spi_mosi the SPI MOSI pin to use
  62. * @param spi_miso the SPI MISO pin to use
  63. * @param spi_clk the SPI clock pin to use
  64. */
  65. MAX31865::MAX31865(int8_t spi_cs, int8_t spi_mosi, int8_t spi_miso, int8_t spi_clk) {
  66. cselPin = spi_cs;
  67. mosiPin = spi_mosi;
  68. misoPin = spi_miso;
  69. sclkPin = spi_clk;
  70. }
  71. /**
  72. * Create the interface object using hardware SPI for PIN for PIN values less
  73. * than or equal to 127.
  74. *
  75. * @param spi_cs the SPI CS pin to use along with the default SPI device
  76. */
  77. MAX31865::MAX31865(int8_t spi_cs) {
  78. cselPin = spi_cs;
  79. sclkPin = misoPin = mosiPin = -1;
  80. }
  81. #else // LARGE_PINMAP
  82. /**
  83. * Create the interface object using software (bitbang) SPI for PIN values
  84. * which are larger than 127. If you have PIN values less than or equal to
  85. * 127 use the other call for SW SPI.
  86. *
  87. * @param spi_cs the SPI CS pin to use
  88. * @param spi_mosi the SPI MOSI pin to use
  89. * @param spi_miso the SPI MISO pin to use
  90. * @param spi_clk the SPI clock pin to use
  91. * @param pin_mapping set to 1 for positive pin values
  92. */
  93. MAX31865::MAX31865(uint32_t spi_cs, uint32_t spi_mosi, uint32_t spi_miso, uint32_t spi_clk, uint8_t pin_mapping) {
  94. cselPin = spi_cs;
  95. mosiPin = spi_mosi;
  96. misoPin = spi_miso;
  97. sclkPin = spi_clk;
  98. }
  99. /**
  100. * Create the interface object using hardware SPI for PIN values which are
  101. * larger than 127. If you have PIN values less than or equal to 127 use
  102. * the other call for HW SPI.
  103. *
  104. * @param spi_cs the SPI CS pin to use along with the default SPI device
  105. * @param pin_mapping set to 1 for positive pin values
  106. */
  107. MAX31865::MAX31865(uint32_t spi_cs, uint8_t pin_mapping) {
  108. cselPin = spi_cs;
  109. sclkPin = misoPin = mosiPin = -1UL; //-1UL or 0xFFFFFFFF or 4294967295
  110. }
  111. #endif // LARGE_PINMAP
  112. /**
  113. *
  114. * Instance & Class methods
  115. *
  116. */
  117. /**
  118. * Initialize the SPI interface and set the number of RTD wires used
  119. *
  120. * @param wires The number of wires as an enum: MAX31865_2WIRE, MAX31865_3WIRE, or MAX31865_4WIRE.
  121. * @param zero_res The resistance of the RTD at 0°C, in ohms.
  122. * @param ref_res The resistance of the reference resistor, in ohms.
  123. * @param wire_res The resistance of the wire connecting the sensor to the RTD, in ohms.
  124. */
  125. void MAX31865::begin(max31865_numwires_t wires, const_float_t zero_res, const_float_t ref_res, const_float_t wire_res) {
  126. resNormalizer = 100.0f / zero_res; // reciprocal of resistance, scaled by 100
  127. refRes = ref_res;
  128. wireRes = wire_res;
  129. pinMode(cselPin, OUTPUT);
  130. digitalWrite(cselPin, HIGH);
  131. if (sclkPin != TERN(LARGE_PINMAP, -1UL, 255))
  132. softSpiInit(); // Define pin modes for Software SPI
  133. else {
  134. DEBUG_ECHOLNPGM("Init MAX31865 Hardware SPI");
  135. SPI.begin(); // Start and configure hardware SPI
  136. }
  137. initFixedFlags(wires);
  138. DEBUG_ECHOLNPGM("MAX31865 Regs: CFG ", readRegister8(MAX31865_CONFIG_REG),
  139. "|RTD ", readRegister16(MAX31865_RTDMSB_REG),
  140. "|HTHRS ", readRegister16(MAX31865_HFAULTMSB_REG),
  141. "|LTHRS ", readRegister16(MAX31865_LFAULTMSB_REG),
  142. "|FLT ", readRegister8(MAX31865_FAULTSTAT_REG));
  143. // fault detection cycle seems to initialize the sensor better
  144. runAutoFaultDetectionCycle(); // also initializes flags
  145. if (lastFault)
  146. SERIAL_ECHOLNPGM("MAX31865 init fault ", lastFault);
  147. writeRegister16(MAX31865_HFAULTMSB_REG, 0xFFFF);
  148. writeRegister16(MAX31865_LFAULTMSB_REG, 0);
  149. #if ENABLED(MAX31865_USE_AUTO_MODE) // make a proper first read to initialize _lastRead
  150. uint16_t rtd = readRegister16(MAX31865_RTDMSB_REG);
  151. #if MAX31865_IGNORE_INITIAL_FAULTY_READS > 0
  152. rtd = fixFault(rtd);
  153. #endif
  154. if (rtd & 1) {
  155. lastRead = 0xFFFF; // some invalid value
  156. lastFault = readRegister8(MAX31865_FAULTSTAT_REG);
  157. clearFault(); // also clears the bias voltage flag, so no further action is required
  158. DEBUG_ECHOLNPGM("MAX31865 read fault: ", rtd);
  159. }
  160. else {
  161. DEBUG_ECHOLNPGM("RTD MSB:", (rtd >> 8), " RTD LSB:", (rtd & 0x00FF));
  162. lastRead = rtd;
  163. TERN_(MAX31865_USE_READ_ERROR_DETECTION, lastReadStamp = millis());
  164. }
  165. #else
  166. enableBias();
  167. DELAY_US(2000); // according to the datasheet, 10.5τ+1msec (see below)
  168. oneShot();
  169. DELAY_US(63000);
  170. uint16_t rtd = readRegister16(MAX31865_RTDMSB_REG);
  171. #if MAX31865_IGNORE_INITIAL_FAULTY_READS > 0
  172. rtd = fixFault(rtd);
  173. #endif
  174. if (rtd & 1) {
  175. lastRead = 0xFFFF; // some invalid value
  176. lastFault = readRegister8(MAX31865_FAULTSTAT_REG);
  177. clearFault(); // also clears the bias voltage flag, so no further action is required
  178. DEBUG_ECHOLNPGM("MAX31865 read fault: ", rtd);
  179. }
  180. else {
  181. DEBUG_ECHOLNPGM("RTD MSB:", (rtd >> 8), " RTD LSB:", (rtd & 0x00FF));
  182. resetFlags();
  183. lastRead = rtd;
  184. nextEvent = SETUP_BIAS_VOLTAGE;
  185. millis_t now = millis();
  186. nextEventStamp = now + MAX31865_MIN_SAMPLING_TIME_MSEC;
  187. TERN_(MAX31865_USE_READ_ERROR_DETECTION, lastReadStamp = now);
  188. }
  189. #endif // MAX31865_USE_AUTO_MODE
  190. DEBUG_ECHOLNPGM(
  191. TERN(LARGE_PINMAP, "LARGE_PINMAP", "Regular")
  192. " begin call with cselPin: ", cselPin,
  193. " misoPin: ", misoPin,
  194. " sclkPin: ", sclkPin,
  195. " mosiPin: ", mosiPin,
  196. " config: ", readRegister8(MAX31865_CONFIG_REG)
  197. );
  198. }
  199. /**
  200. * Return and clear the last fault value
  201. *
  202. * @return The raw unsigned 8-bit FAULT status register or spike fault
  203. */
  204. uint8_t MAX31865::readFault() {
  205. uint8_t r = lastFault;
  206. lastFault = 0;
  207. return r;
  208. }
  209. /**
  210. * Clear last fault
  211. */
  212. void MAX31865::clearFault() {
  213. setConfig(MAX31865_CONFIG_FAULTSTAT, 1);
  214. }
  215. /**
  216. * Reset flags
  217. */
  218. void MAX31865::resetFlags() {
  219. writeRegister8(MAX31865_CONFIG_REG, stdFlags);
  220. }
  221. /**
  222. * Enable the bias voltage on the RTD sensor
  223. */
  224. void MAX31865::enableBias() {
  225. setConfig(MAX31865_CONFIG_BIAS, 1);
  226. }
  227. /**
  228. * Start a one-shot temperature reading.
  229. */
  230. void MAX31865::oneShot() {
  231. setConfig(MAX31865_CONFIG_1SHOT | MAX31865_CONFIG_BIAS, 1);
  232. }
  233. void MAX31865::runAutoFaultDetectionCycle() {
  234. writeRegister8(MAX31865_CONFIG_REG, (stdFlags & 0x11) | 0x84 ); // cfg reg = 100X010Xb
  235. DELAY_US(600);
  236. for (int i = 0; i < 10 && (readRegister8(MAX31865_CONFIG_REG) & 0xC) > 0; i++) DELAY_US(100); // Fault det completes when bits 2 and 3 are zero (or after 10 tries)
  237. readFault();
  238. clearFault();
  239. }
  240. /**
  241. * Set a value in the configuration register.
  242. *
  243. * @param config 8-bit value for the config item
  244. * @param enable whether to enable or disable the value
  245. */
  246. void MAX31865::setConfig(uint8_t config, bool enable) {
  247. uint8_t t = stdFlags;
  248. if (enable)
  249. t |= config;
  250. else
  251. t &= ~config;
  252. writeRegister8(MAX31865_CONFIG_REG, t);
  253. }
  254. /**
  255. * Initialize standard flags with flags that will not change during operation (Hz, polling mode and no. of wires)
  256. *
  257. * @param wires The number of wires in enum format
  258. */
  259. void MAX31865::initFixedFlags(max31865_numwires_t wires) {
  260. // set config-defined flags (same for all sensors)
  261. stdFlags = TERN(MAX31865_50HZ_FILTER, MAX31865_CONFIG_FILT50HZ, MAX31865_CONFIG_FILT60HZ) |
  262. TERN(MAX31865_USE_AUTO_MODE, MAX31865_CONFIG_MODEAUTO | MAX31865_CONFIG_BIAS, MAX31865_CONFIG_MODEOFF);
  263. if (wires == MAX31865_3WIRE)
  264. stdFlags |= MAX31865_CONFIG_3WIRE; // 3 wire
  265. else
  266. stdFlags &= ~MAX31865_CONFIG_3WIRE; // 2 or 4 wire
  267. }
  268. #if MAX31865_IGNORE_INITIAL_FAULTY_READS > 0
  269. inline uint16_t MAX31865::fixFault(uint16_t rtd) {
  270. if (!ignore_faults || !(rtd & 1))
  271. return rtd;
  272. ignore_faults--;
  273. clearFault();
  274. DEBUG_ECHOLNPGM("MAX31865 ignoring fault ", (MAX31865_IGNORE_INITIAL_FAULTY_READS) - ignore_faults);
  275. return rtd & ~1; // 0xFFFE
  276. }
  277. #endif
  278. inline uint16_t MAX31865::readRawImmediate() {
  279. uint16_t rtd = readRegister16(MAX31865_RTDMSB_REG);
  280. DEBUG_ECHOLNPGM("MAX31865 RTD MSB:", (rtd >> 8), " LSB:", (rtd & 0x00FF));
  281. #if MAX31865_IGNORE_INITIAL_FAULTY_READS > 0
  282. rtd = fixFault(rtd);
  283. #endif
  284. if (rtd & 1) {
  285. lastFault = readRegister8(MAX31865_FAULTSTAT_REG);
  286. lastRead |= 1;
  287. clearFault(); // also clears the bias voltage flag, so no further action is required
  288. DEBUG_ECHOLNPGM("MAX31865 read fault: ", lastFault);
  289. }
  290. else {
  291. TERN_(MAX31865_USE_READ_ERROR_DETECTION, const millis_t ms = millis());
  292. if (TERN0(MAX31865_USE_READ_ERROR_DETECTION, ABS((int)(lastRead - rtd)) > 500 && PENDING(ms, lastReadStamp + 1000))) {
  293. // If 2 readings within 1s differ too much (~20°C) it's a read error.
  294. lastFault = 0x01;
  295. lastRead |= 1;
  296. DEBUG_ECHOLNPGM("MAX31865 read error: ", rtd);
  297. }
  298. else {
  299. lastRead = rtd;
  300. TERN_(MAX31865_USE_READ_ERROR_DETECTION, lastReadStamp = ms);
  301. }
  302. }
  303. return rtd;
  304. }
  305. /**
  306. * Read the raw 16-bit value from the RTD_REG in one shot mode. This will include
  307. * the fault bit, D0.
  308. *
  309. * @return The raw unsigned 16-bit register value with ERROR bit attached, NOT temperature!
  310. */
  311. uint16_t MAX31865::readRaw() {
  312. #if ENABLED(MAX31865_USE_AUTO_MODE)
  313. readRawImmediate();
  314. #else
  315. const millis_t ms = millis();
  316. if (PENDING(ms, nextEventStamp)) {
  317. DEBUG_ECHOLNPGM("MAX31865 waiting for event ", nextEvent);
  318. return lastRead;
  319. }
  320. switch (nextEvent) {
  321. case SETUP_BIAS_VOLTAGE:
  322. enableBias();
  323. nextEventStamp = ms + 2; // wait at least 10.5*τ (τ = 100nF*430Ω max for PT100 / 10nF*4.3ΚΩ for PT1000 = 43μsec) + 1msec
  324. nextEvent = SETUP_1_SHOT_MODE;
  325. DEBUG_ECHOLNPGM("MAX31865 bias voltage enabled");
  326. break;
  327. case SETUP_1_SHOT_MODE:
  328. oneShot();
  329. nextEventStamp = ms + TERN(MAX31865_50HZ_FILTER, 63, 52); // wait at least 52msec for 60Hz (63msec for 50Hz) before reading RTD register
  330. nextEvent = READ_RTD_REG;
  331. DEBUG_ECHOLNPGM("MAX31865 1 shot mode enabled");
  332. break;
  333. case READ_RTD_REG:
  334. if (!(readRawImmediate() & 1)) // if clearFault() was not invoked, need to clear the bias voltage and 1-shot flags
  335. resetFlags();
  336. nextEvent = SETUP_BIAS_VOLTAGE;
  337. nextEventStamp = ms + (MAX31865_MIN_SAMPLING_TIME_MSEC); // next step should not occur within less than MAX31865_MIN_SAMPLING_TIME_MSEC from the last one
  338. break;
  339. }
  340. #endif
  341. return lastRead;
  342. }
  343. /**
  344. * Calculate and return the resistance value of the connected RTD.
  345. *
  346. * @return The raw RTD resistance value, NOT temperature!
  347. */
  348. float MAX31865::readResistance() {
  349. // Strip the error bit (D0) and convert to a float ratio.
  350. // less precise method: (readRaw() * refRes) >> 16
  351. return ((readRaw() * RECIPROCAL(65536.0f)) * refRes - wireRes);
  352. }
  353. /**
  354. * Read the RTD and pass it to temperature(float) for calculation.
  355. *
  356. * @return Temperature in C
  357. */
  358. float MAX31865::temperature() {
  359. return temperature(readResistance());
  360. }
  361. /**
  362. * Given the 15-bit ADC value, calculate the resistance and pass it to temperature(float) for calculation.
  363. *
  364. * @return Temperature in C
  365. */
  366. float MAX31865::temperature(const uint16_t adc_val) {
  367. return temperature(((adc_val) * RECIPROCAL(32768.0f)) * refRes - wireRes);
  368. }
  369. /**
  370. * Calculate the temperature in C from the RTD resistance.
  371. *
  372. * @param rtd_res the resistance value in ohms
  373. * @return the temperature in °C
  374. */
  375. float MAX31865::temperature(float rtd_res) {
  376. rtd_res *= resNormalizer; // normalize to 100 ohm
  377. // Constants for calculating temperature from the measured RTD resistance.
  378. // http://www.analog.com/media/en/technical-documentation/application-notes/AN709_0.pdf
  379. constexpr float RTD_Z1 = -0.0039083,
  380. RTD_Z2 = +1.758480889e-5,
  381. RTD_Z3 = -2.31e-8,
  382. RTD_Z4 = -1.155e-6;
  383. // Callender-Van Dusen equation
  384. float temp = (RTD_Z1 + sqrt(RTD_Z2 + (RTD_Z3 * rtd_res))) * RECIPROCAL(RTD_Z4);
  385. //
  386. // The previous equation is valid only for temperatures of 0°C and above.
  387. // The equation for RRTD(t) that defines negative temperature behavior is a
  388. // fourth-order polynomial (after expanding the third term) and is quite
  389. // impractical to solve for a single expression of temperature as a function
  390. // of resistance. So here we use a Linear Approximation instead.
  391. //
  392. if (temp < 0) {
  393. #ifndef MAX31865_APPROX
  394. #define MAX31865_APPROX 5
  395. #endif
  396. constexpr float RTD_C[] = {
  397. #if MAX31865_APPROX == 5
  398. -242.02, +2.2228, +2.5859e-3, -4.8260e-6, -2.8183e-8, +1.5243e-10
  399. #elif MAX31865_APPROX == 4
  400. -241.96, +2.2163, +2.8541e-3, -9.9121e-6, -1.7152e-8
  401. #elif MAX31865_APPROX == 3
  402. -242.09, +2.2276, +2.5178e-3, -5.8620e-6
  403. #else
  404. -242.97, +2.2838, +1.4727e-3
  405. #endif
  406. };
  407. float rpoly = rtd_res;
  408. temp = RTD_C[0];
  409. temp += rpoly * RTD_C[1];
  410. rpoly *= rtd_res; temp += rpoly * RTD_C[2];
  411. if (MAX31865_APPROX >= 3) { rpoly *= rtd_res; temp += rpoly * RTD_C[3]; }
  412. if (MAX31865_APPROX >= 4) { rpoly *= rtd_res; temp += rpoly * RTD_C[4]; }
  413. if (MAX31865_APPROX >= 5) { rpoly *= rtd_res; temp += rpoly * RTD_C[5]; }
  414. }
  415. return temp;
  416. }
  417. /**
  418. * MAX31865 SPI Timing constants
  419. * See MAX31865 datasheet (https://datasheets.maximintegrated.com/en/ds/MAX31865.pdf)
  420. * All timings in nsec, minimum values.
  421. */
  422. #define MAX31865_SPI_TIMING_TCC 400 // CS to SCLK setup
  423. #define MAX31865_SPI_TIMING_TDC 35 // Data to SCLK setup
  424. #define MAX31865_SPI_TIMING_TCL 100 // SCK half period
  425. #define MAX31865_SPI_TIMING_TCCH 100 // SCK to CS hold
  426. #define MAX31865_SPI_TIMING_TCWH 400 // CS inactive time (min)
  427. /**
  428. * Read a single byte from the specified register address.
  429. *
  430. * @param addr the register address
  431. * @return the register contents
  432. */
  433. uint8_t MAX31865::readRegister8(uint8_t addr) {
  434. uint8_t ret = 0;
  435. readRegisterN(addr, &ret, 1);
  436. return ret;
  437. }
  438. /**
  439. * Read two bytes: 1 from the specified register address, and 1 from the next address.
  440. *
  441. * @param addr the first register address
  442. * @return both register contents as a single 16-bit int
  443. */
  444. uint16_t MAX31865::readRegister16(uint8_t addr) {
  445. uint8_t buffer[2] = { 0 };
  446. readRegisterN(addr, buffer, 2);
  447. return uint16_t(buffer[0]) << 8 | buffer[1];
  448. }
  449. /**
  450. * Read +n+ bytes from a specified address into +buffer+. Set D7 to 0 to specify a read.
  451. *
  452. * @param addr the first register address
  453. * @param buffer storage for the read bytes
  454. * @param n the number of bytes to read
  455. */
  456. void MAX31865::readRegisterN(uint8_t addr, uint8_t buffer[], uint8_t n) {
  457. addr &= 0x7F; // make sure top bit is not set
  458. spiBeginTransaction();
  459. spiTransfer(addr);
  460. while (n--) {
  461. buffer[0] = spiTransfer(0xFF);
  462. buffer++;
  463. }
  464. spiEndTransaction();
  465. }
  466. void MAX31865::writeRegister16(uint8_t addr, uint16_t data) {
  467. spiBeginTransaction();
  468. spiTransfer(addr | 0x80); // make sure top bit is set
  469. spiTransfer(data >> 8);
  470. spiTransfer(data & 0xFF);
  471. spiEndTransaction();
  472. }
  473. /**
  474. * Write an 8-bit value to a register. Set D7 to 1 to specify a write.
  475. *
  476. * @param addr the address to write to
  477. * @param data the data to write
  478. */
  479. void MAX31865::writeRegister8(uint8_t addr, uint8_t data) {
  480. spiBeginTransaction();
  481. spiTransfer(addr | 0x80); // make sure top bit is set
  482. spiTransfer(data);
  483. spiEndTransaction();
  484. }
  485. void MAX31865::spiBeginTransaction() {
  486. digitalWrite(sclkPin, LOW); // ensure CPOL0
  487. DELAY_NS_VAR(MAX31865_SPI_TIMING_TCWH); // ensure minimum time of CS inactivity after previous operation
  488. digitalWrite(cselPin, LOW);
  489. DELAY_NS_VAR(MAX31865_SPI_TIMING_TCC);
  490. if (sclkPin == TERN(LARGE_PINMAP, -1UL, 255))
  491. SPI.beginTransaction(spiConfig);
  492. else
  493. digitalWrite(sclkPin, HIGH);
  494. }
  495. void MAX31865::spiEndTransaction() {
  496. if (sclkPin == TERN(LARGE_PINMAP, -1UL, 255))
  497. SPI.endTransaction();
  498. else
  499. digitalWrite(sclkPin, LOW);
  500. DELAY_NS_VAR(MAX31865_SPI_TIMING_TCCH);
  501. digitalWrite(cselPin, HIGH);
  502. }
  503. /**
  504. * Transfer SPI data +x+ and read the response. From the datasheet...
  505. * Input data (SDI) is latched on the internal strobe edge and output data (SDO) is
  506. * shifted out on the shift edge. There is one clock for each bit transferred.
  507. * Address and data bits are transferred in groups of eight, MSB first.
  508. *
  509. * @param x an 8-bit chunk of data to write
  510. * @return the 8-bit response
  511. */
  512. uint8_t MAX31865::spiTransfer(uint8_t x) {
  513. if (sclkPin == TERN(LARGE_PINMAP, -1UL, 255))
  514. return SPI.transfer(x);
  515. uint8_t reply = 0;
  516. for (int i = 7; i >= 0; i--) {
  517. digitalWrite(mosiPin, x & _BV(i));
  518. DELAY_NS_VAR(MAX31865_SPI_TIMING_TDC);
  519. digitalWrite(sclkPin, LOW);
  520. DELAY_NS_VAR(MAX31865_SPI_TIMING_TCL - MAX31865_SPI_TIMING_TDC);
  521. reply <<= 1;
  522. if (digitalRead(misoPin)) reply |= 1;
  523. DELAY_NS_VAR(MAX31865_SPI_TIMING_TDC);
  524. digitalWrite(sclkPin, HIGH);
  525. DELAY_NS_VAR(MAX31865_SPI_TIMING_TCL - MAX31865_SPI_TIMING_TDC);
  526. }
  527. return reply;
  528. }
  529. void MAX31865::softSpiInit() {
  530. DEBUG_ECHOLNPGM("Initializing MAX31865 Software SPI");
  531. pinMode(sclkPin, OUTPUT);
  532. digitalWrite(sclkPin, LOW);
  533. pinMode(mosiPin, OUTPUT);
  534. pinMode(misoPin, INPUT);
  535. }
  536. #endif // HAS_MAX31865 && !USE_ADAFRUIT_MAX31865