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 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
  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. #ifdef TARGET_LPC1768
  48. #include <SoftwareSPI.h>
  49. #endif
  50. #define DEBUG_OUT ENABLED(DEBUG_MAX31865)
  51. #include "../core/debug_out.h"
  52. // The maximum speed the MAX31865 can do is 5 MHz
  53. SPISettings MAX31865::spiConfig = SPISettings(
  54. TERN(TARGET_LPC1768, SPI_QUARTER_SPEED, TERN(ARDUINO_ARCH_STM32, SPI_CLOCK_DIV4, 500000)),
  55. MSBFIRST,
  56. SPI_MODE1 // CPOL0 CPHA1
  57. );
  58. #if DISABLED(LARGE_PINMAP)
  59. /**
  60. * Create the interface object using software (bitbang) SPI for PIN values
  61. * less than or equal to 127.
  62. *
  63. * @param spi_cs the SPI CS pin to use
  64. * @param spi_mosi the SPI MOSI pin to use
  65. * @param spi_miso the SPI MISO pin to use
  66. * @param spi_clk the SPI clock pin to use
  67. */
  68. MAX31865::MAX31865(int8_t spi_cs, int8_t spi_mosi, int8_t spi_miso, int8_t spi_clk) {
  69. cselPin = spi_cs;
  70. mosiPin = spi_mosi;
  71. misoPin = spi_miso;
  72. sclkPin = spi_clk;
  73. }
  74. /**
  75. * Create the interface object using hardware SPI for PIN for PIN values less
  76. * than or equal to 127.
  77. *
  78. * @param spi_cs the SPI CS pin to use along with the default SPI device
  79. */
  80. MAX31865::MAX31865(int8_t spi_cs) {
  81. cselPin = spi_cs;
  82. sclkPin = misoPin = mosiPin = -1;
  83. }
  84. #else // LARGE_PINMAP
  85. /**
  86. * Create the interface object using software (bitbang) SPI for PIN values
  87. * which are larger than 127. If you have PIN values less than or equal to
  88. * 127 use the other call for SW SPI.
  89. *
  90. * @param spi_cs the SPI CS pin to use
  91. * @param spi_mosi the SPI MOSI pin to use
  92. * @param spi_miso the SPI MISO pin to use
  93. * @param spi_clk the SPI clock pin to use
  94. * @param pin_mapping set to 1 for positive pin values
  95. */
  96. MAX31865::MAX31865(uint32_t spi_cs, uint32_t spi_mosi, uint32_t spi_miso, uint32_t spi_clk, uint8_t pin_mapping) {
  97. cselPin = spi_cs;
  98. mosiPin = spi_mosi;
  99. misoPin = spi_miso;
  100. sclkPin = spi_clk;
  101. }
  102. /**
  103. * Create the interface object using hardware SPI for PIN values which are
  104. * larger than 127. If you have PIN values less than or equal to 127 use
  105. * the other call for HW SPI.
  106. *
  107. * @param spi_cs the SPI CS pin to use along with the default SPI device
  108. * @param pin_mapping set to 1 for positive pin values
  109. */
  110. MAX31865::MAX31865(uint32_t spi_cs, uint8_t pin_mapping) {
  111. cselPin = spi_cs;
  112. sclkPin = misoPin = mosiPin = -1UL; //-1UL or 0xFFFFFFFF or 4294967295
  113. }
  114. #endif // LARGE_PINMAP
  115. /**
  116. *
  117. * Instance & Class methods
  118. *
  119. */
  120. /**
  121. * Initialize the SPI interface and set the number of RTD wires used
  122. *
  123. * @param wires The number of wires in enum format. Can be MAX31865_2WIRE, MAX31865_3WIRE, or MAX31865_4WIRE.
  124. * @param zero The resistance of the RTD at 0 degC, in ohms.
  125. * @param ref The resistance of the reference resistor, in ohms.
  126. * @param wire The resistance of the wire connecting the sensor to the RTD, in ohms.
  127. */
  128. void MAX31865::begin(max31865_numwires_t wires, float zero_res, float ref_res, float wire_res) {
  129. zeroRes = zero_res;
  130. refRes = ref_res;
  131. wireRes = wire_res;
  132. pinMode(cselPin, OUTPUT);
  133. digitalWrite(cselPin, HIGH);
  134. if (sclkPin != TERN(LARGE_PINMAP, -1UL, 255))
  135. softSpiBegin(SPI_QUARTER_SPEED); // Define pin modes for Software SPI
  136. else {
  137. DEBUG_ECHOLNPGM("Initializing MAX31865 Hardware SPI");
  138. SPI.begin(); // Start and configure hardware SPI
  139. }
  140. initFixedFlags(wires);
  141. clearFault(); // also initializes flags
  142. #if DISABLED(MAX31865_USE_AUTO_MODE) // make a proper first 1 shot read to initialize _lastRead
  143. enableBias();
  144. DELAY_US(11500);
  145. oneShot();
  146. DELAY_US(65000);
  147. uint16_t rtd = readRegister16(MAX31865_RTDMSB_REG);
  148. if (rtd & 1) {
  149. lastRead = 0xFFFF; // some invalid value
  150. lastFault = readRegister8(MAX31865_FAULTSTAT_REG);
  151. clearFault(); // also clears the bias voltage flag, so no further action is required
  152. DEBUG_ECHOLNPGM("MAX31865 read fault: ", rtd);
  153. }
  154. else {
  155. DEBUG_ECHOLNPGM("RTD MSB:", (rtd >> 8), " RTD LSB:", (rtd & 0x00FF));
  156. resetFlags();
  157. lastRead = rtd;
  158. nextEvent = SETUP_BIAS_VOLTAGE;
  159. millis_t now = millis();
  160. nextEventStamp = now + MAX31865_MIN_SAMPLING_TIME_MSEC;
  161. TERN_(MAX31865_USE_READ_ERROR_DETECTION, lastReadStamp = now);
  162. }
  163. #endif // !MAX31865_USE_AUTO_MODE
  164. DEBUG_ECHOLNPGM(
  165. TERN(LARGE_PINMAP, "LARGE_PINMAP", "Regular")
  166. " begin call with cselPin: ", cselPin,
  167. " misoPin: ", misoPin,
  168. " sclkPin: ", sclkPin,
  169. " mosiPin: ", mosiPin,
  170. " config: ", readRegister8(MAX31865_CONFIG_REG)
  171. );
  172. }
  173. /**
  174. * Return and clear the last fault value
  175. *
  176. * @return The raw unsigned 8-bit FAULT status register or spike fault
  177. */
  178. uint8_t MAX31865::readFault() {
  179. uint8_t r = lastFault;
  180. lastFault = 0;
  181. return r;
  182. }
  183. /**
  184. * Clear last fault
  185. */
  186. void MAX31865::clearFault() {
  187. setConfig(MAX31865_CONFIG_FAULTSTAT, 1);
  188. }
  189. /**
  190. * Reset flags
  191. */
  192. void MAX31865::resetFlags() {
  193. writeRegister8(MAX31865_CONFIG_REG, stdFlags);
  194. }
  195. /**
  196. * Enable the bias voltage on the RTD sensor
  197. */
  198. void MAX31865::enableBias() {
  199. setConfig(MAX31865_CONFIG_BIAS, 1);
  200. }
  201. /**
  202. * Start a one-shot temperature reading.
  203. */
  204. void MAX31865::oneShot() {
  205. setConfig(MAX31865_CONFIG_1SHOT | MAX31865_CONFIG_BIAS, 1);
  206. }
  207. /**
  208. * Initialize standard flags with flags that will not change during operation (Hz, polling mode and no. of wires)
  209. *
  210. * @param wires The number of wires in enum format
  211. */
  212. void MAX31865::initFixedFlags(max31865_numwires_t wires) {
  213. // set config-defined flags (same for all sensors)
  214. stdFlags = TERN(MAX31865_50HZ_FILTER, MAX31865_CONFIG_FILT50HZ, MAX31865_CONFIG_FILT60HZ) |
  215. TERN(MAX31865_USE_AUTO_MODE, MAX31865_CONFIG_MODEAUTO | MAX31865_CONFIG_BIAS, MAX31865_CONFIG_MODEOFF);
  216. if (wires == MAX31865_3WIRE)
  217. stdFlags |= MAX31865_CONFIG_3WIRE;
  218. else // 2 or 4 wire
  219. stdFlags &= ~MAX31865_CONFIG_3WIRE;
  220. }
  221. /**
  222. * Read the raw 16-bit value from the RTD_REG in one shot mode. This will include
  223. * the fault bit, D0.
  224. *
  225. * @return The raw unsigned 16-bit register value with ERROR bit attached, NOT temperature!
  226. */
  227. uint16_t MAX31865::readRaw() {
  228. #if ENABLED(MAX31865_USE_AUTO_MODE)
  229. const uint16_t rtd = readRegister16(MAX31865_RTDMSB_REG);
  230. DEBUG_ECHOLNPGM("MAX31865 RTD MSB:", (rtd >> 8), " LSB:", (rtd & 0x00FF));
  231. if (rtd & 1) {
  232. lastFault = readRegister8(MAX31865_FAULTSTAT_REG);
  233. lastRead |= 1;
  234. clearFault(); // also clears the bias voltage flag, so no further action is required
  235. DEBUG_ECHOLNPGM("MAX31865 read fault: ", rtd);
  236. }
  237. #if ENABLED(MAX31865_USE_READ_ERROR_DETECTION)
  238. else if (ABS(lastRead - rtd) > 500 && PENDING(millis(), lastReadStamp + 1000)) { // if two readings within a second differ too much (~20°C), consider it a read error.
  239. lastFault = 0x01;
  240. lastRead |= 1;
  241. DEBUG_ECHOLNPGM("MAX31865 read error: ", rtd);
  242. }
  243. #endif
  244. else {
  245. lastRead = rtd;
  246. TERN_(MAX31865_USE_READ_ERROR_DETECTION, lastReadStamp = millis());
  247. }
  248. #else
  249. if (PENDING(millis(), nextEventStamp)) {
  250. DEBUG_ECHOLNPGM("MAX31865 waiting for event ", nextEvent);
  251. return lastRead;
  252. }
  253. switch (nextEvent) {
  254. case SETUP_BIAS_VOLTAGE:
  255. enableBias();
  256. nextEventStamp = millis() + 11; // wait at least 11msec before enabling 1shot
  257. nextEvent = SETUP_1_SHOT_MODE;
  258. DEBUG_ECHOLN("MAX31865 bias voltage enabled");
  259. break;
  260. case SETUP_1_SHOT_MODE:
  261. oneShot();
  262. nextEventStamp = millis() + 65; // wait at least 65msec before reading RTD register
  263. nextEvent = READ_RTD_REG;
  264. DEBUG_ECHOLN("MAX31865 1 shot mode enabled");
  265. break;
  266. case READ_RTD_REG: {
  267. const uint16_t rtd = readRegister16(MAX31865_RTDMSB_REG);
  268. DEBUG_ECHOLNPGM("MAX31865 RTD MSB:", (rtd >> 8), " LSB:", (rtd & 0x00FF));
  269. if (rtd & 1) {
  270. lastFault = readRegister8(MAX31865_FAULTSTAT_REG);
  271. lastRead |= 1;
  272. clearFault(); // also clears the bias voltage flag, so no further action is required
  273. DEBUG_ECHOLNPGM("MAX31865 read fault: ", rtd);
  274. }
  275. #if ENABLED(MAX31865_USE_READ_ERROR_DETECTION)
  276. else if (ABS(lastRead - rtd) > 500 && PENDING(millis(), lastReadStamp + 1000)) { // if two readings within a second differ too much (~20°C), consider it a read error.
  277. lastFault = 0x01;
  278. lastRead |= 1;
  279. DEBUG_ECHOLNPGM("MAX31865 read error: ", rtd);
  280. }
  281. #endif
  282. else {
  283. lastRead = rtd;
  284. TERN_(MAX31865_USE_READ_ERROR_DETECTION, lastReadStamp = millis());
  285. }
  286. if (!(rtd & 1)) // if clearFault() was not invoked, need to clear the bias voltage and 1-shot flags
  287. resetFlags();
  288. nextEvent = SETUP_BIAS_VOLTAGE;
  289. nextEventStamp = millis() + MAX31865_MIN_SAMPLING_TIME_MSEC; // next step should not occur within less than MAX31865_MIN_SAMPLING_TIME_MSEC from the last one
  290. } break;
  291. }
  292. #endif
  293. return lastRead;
  294. }
  295. /**
  296. * Calculate and return the resistance value of the connected RTD.
  297. *
  298. * @return The raw RTD resistance value, NOT temperature!
  299. */
  300. float MAX31865::readResistance() {
  301. // Strip the error bit (D0) and convert to a float ratio.
  302. // less precise method: (readRaw() * refRes) >> 16
  303. return ((readRaw() * RECIPROCAL(65536.0f)) * refRes - wireRes);
  304. }
  305. /**
  306. * Read the RTD and pass it to temperature(float) for calculation.
  307. *
  308. * @return Temperature in C
  309. */
  310. float MAX31865::temperature() {
  311. return temperature(readResistance());
  312. }
  313. /**
  314. * Given the 15-bit ADC value, calculate the resistance and pass it to temperature(float) for calculation.
  315. *
  316. * @return Temperature in C
  317. */
  318. float MAX31865::temperature(uint16_t adc_val) {
  319. return temperature(((adc_val) * RECIPROCAL(32768.0f)) * refRes - wireRes);
  320. }
  321. /**
  322. * Calculate the temperature in C from the RTD resistance.
  323. * Uses the technique outlined in this PDF:
  324. * http://www.analog.com/media/en/technical-documentation/application-notes/AN709_0.pdf
  325. *
  326. * @param rtd_res the resistance value in ohms
  327. * @return the temperature in degC
  328. */
  329. float MAX31865::temperature(float rtd_res) {
  330. float temp = (RTD_Z1 + sqrt(RTD_Z2 + (RTD_Z3 * rtd_res))) * RECIPROCAL(RTD_Z4);
  331. // From the PDF...
  332. //
  333. // The previous equation is valid only for temperatures of 0°C and above.
  334. // The equation for RRTD(t) that defines negative temperature behavior is a
  335. // fourth-order polynomial (after expanding the third term) and is quite
  336. // impractical to solve for a single expression of temperature as a function
  337. // of resistance.
  338. //
  339. if (temp < 0) {
  340. rtd_res = (rtd_res / zeroRes) * 100; // normalize to 100 ohm
  341. float rpoly = rtd_res;
  342. temp = -242.02 + (2.2228 * rpoly);
  343. rpoly *= rtd_res; // square
  344. temp += 2.5859e-3 * rpoly;
  345. rpoly *= rtd_res; // ^3
  346. temp -= 4.8260e-6 * rpoly;
  347. rpoly *= rtd_res; // ^4
  348. temp -= 2.8183e-8 * rpoly;
  349. rpoly *= rtd_res; // ^5
  350. temp += 1.5243e-10 * rpoly;
  351. }
  352. return temp;
  353. }
  354. //
  355. // private:
  356. //
  357. /**
  358. * Set a value in the configuration register.
  359. *
  360. * @param config 8-bit value for the config item
  361. * @param enable whether to enable or disable the value
  362. */
  363. void MAX31865::setConfig(uint8_t config, bool enable) {
  364. uint8_t t = stdFlags;
  365. if (enable) t |= config; else t &= ~config;
  366. writeRegister8(MAX31865_CONFIG_REG, t);
  367. }
  368. /**
  369. * Read a single byte from the specified register address.
  370. *
  371. * @param addr the register address
  372. * @return the register contents
  373. */
  374. uint8_t MAX31865::readRegister8(uint8_t addr) {
  375. uint8_t ret = 0;
  376. readRegisterN(addr, &ret, 1);
  377. return ret;
  378. }
  379. /**
  380. * Read two bytes: 1 from the specified register address, and 1 from the next address.
  381. *
  382. * @param addr the first register address
  383. * @return both register contents as a single 16-bit int
  384. */
  385. uint16_t MAX31865::readRegister16(uint8_t addr) {
  386. uint8_t buffer[2] = { 0 };
  387. readRegisterN(addr, buffer, 2);
  388. return uint16_t(buffer[0]) << 8 | buffer[1];
  389. }
  390. /**
  391. * Read +n+ bytes from a specified address into +buffer+. Set D7 to 0 to specify a read.
  392. *
  393. * @param addr the first register address
  394. * @param buffer storage for the read bytes
  395. * @param n the number of bytes to read
  396. */
  397. void MAX31865::readRegisterN(uint8_t addr, uint8_t buffer[], uint8_t n) {
  398. addr &= 0x7F; // make sure top bit is not set
  399. if (sclkPin == TERN(LARGE_PINMAP, -1UL, 255))
  400. SPI.beginTransaction(spiConfig);
  401. else
  402. digitalWrite(sclkPin, LOW);
  403. digitalWrite(cselPin, LOW);
  404. #ifdef TARGET_LPC1768
  405. DELAY_CYCLES(spiSpeed);
  406. #endif
  407. spiTransfer(addr);
  408. while (n--) {
  409. buffer[0] = spiTransfer(0xFF);
  410. buffer++;
  411. }
  412. if (sclkPin == TERN(LARGE_PINMAP, -1UL, 255))
  413. SPI.endTransaction();
  414. digitalWrite(cselPin, HIGH);
  415. }
  416. /**
  417. * Write an 8-bit value to a register. Set D7 to 1 to specify a write.
  418. *
  419. * @param addr the address to write to
  420. * @param data the data to write
  421. */
  422. void MAX31865::writeRegister8(uint8_t addr, uint8_t data) {
  423. if (sclkPin == TERN(LARGE_PINMAP, -1UL, 255))
  424. SPI.beginTransaction(spiConfig);
  425. else
  426. digitalWrite(sclkPin, LOW);
  427. digitalWrite(cselPin, LOW);
  428. #ifdef TARGET_LPC1768
  429. DELAY_CYCLES(spiSpeed);
  430. #endif
  431. spiTransfer(addr | 0x80); // make sure top bit is set
  432. spiTransfer(data);
  433. if (sclkPin == TERN(LARGE_PINMAP, -1UL, 255))
  434. SPI.endTransaction();
  435. digitalWrite(cselPin, HIGH);
  436. }
  437. /**
  438. * Transfer SPI data +x+ and read the response. From the datasheet...
  439. * Input data (SDI) is latched on the internal strobe edge and output data (SDO) is
  440. * shifted out on the shift edge. There is one clock for each bit transferred.
  441. * Address and data bits are transferred in groups of eight, MSB first.
  442. *
  443. * @param x an 8-bit chunk of data to write
  444. * @return the 8-bit response
  445. */
  446. uint8_t MAX31865::spiTransfer(uint8_t x) {
  447. if (sclkPin == TERN(LARGE_PINMAP, -1UL, 255))
  448. return SPI.transfer(x);
  449. #ifdef TARGET_LPC1768
  450. return swSpiTransfer(x, spiSpeed, sclkPin, misoPin, mosiPin);
  451. #else
  452. uint8_t reply = 0;
  453. for (int i = 7; i >= 0; i--) {
  454. digitalWrite(sclkPin, HIGH); DELAY_NS_VAR(spiDelay);
  455. reply <<= 1;
  456. digitalWrite(mosiPin, x & _BV(i)); DELAY_NS_VAR(spiDelay);
  457. if (digitalRead(misoPin)) reply |= 1;
  458. digitalWrite(sclkPin, LOW); DELAY_NS_VAR(spiDelay);
  459. }
  460. return reply;
  461. #endif
  462. }
  463. void MAX31865::softSpiBegin(const uint8_t spi_speed) {
  464. DEBUG_ECHOLNPGM("Initializing MAX31865 Software SPI");
  465. #ifdef TARGET_LPC1768
  466. swSpiBegin(sclkPin, misoPin, mosiPin);
  467. spiSpeed = swSpiInit(spi_speed, sclkPin, mosiPin);
  468. #else
  469. spiDelay = (100UL << spi_speed) / 3; // Calculate delay in ns. Top speed is ~10MHz, or 100ns delay between bits.
  470. pinMode(sclkPin, OUTPUT);
  471. digitalWrite(sclkPin, LOW);
  472. pinMode(mosiPin, OUTPUT);
  473. pinMode(misoPin, INPUT);
  474. #endif
  475. }
  476. #endif // HAS_MAX31865 && !USE_ADAFRUIT_MAX31865