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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  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. // Useful for RTD debugging.
  42. //#define MAX31865_DEBUG
  43. //#define MAX31865_DEBUG_SPI
  44. #include "../inc/MarlinConfig.h"
  45. #if HAS_MAX31865 && !USE_ADAFRUIT_MAX31865
  46. #include "MAX31865.h"
  47. #ifdef TARGET_LPC1768
  48. #include <SoftwareSPI.h>
  49. #endif
  50. // The maximum speed the MAX31865 can do is 5 MHz
  51. SPISettings MAX31865::spiConfig = SPISettings(
  52. TERN(TARGET_LPC1768, SPI_QUARTER_SPEED, TERN(ARDUINO_ARCH_STM32, SPI_CLOCK_DIV4, 500000)),
  53. MSBFIRST,
  54. SPI_MODE1 // CPOL0 CPHA1
  55. );
  56. #ifndef LARGE_PINMAP
  57. /**
  58. * Create the interface object using software (bitbang) SPI for PIN values
  59. * less than or equal to 127.
  60. *
  61. * @param spi_cs the SPI CS pin to use
  62. * @param spi_mosi the SPI MOSI pin to use
  63. * @param spi_miso the SPI MISO pin to use
  64. * @param spi_clk the SPI clock pin to use
  65. */
  66. MAX31865::MAX31865(int8_t spi_cs, int8_t spi_mosi, int8_t spi_miso, int8_t spi_clk) {
  67. _cs = spi_cs;
  68. _mosi = spi_mosi;
  69. _miso = spi_miso;
  70. _sclk = spi_clk;
  71. }
  72. /**
  73. * Create the interface object using hardware SPI for PIN for PIN values less
  74. * than or equal to 127.
  75. *
  76. * @param spi_cs the SPI CS pin to use along with the default SPI device
  77. */
  78. MAX31865::MAX31865(int8_t spi_cs) {
  79. _cs = spi_cs;
  80. _sclk = _miso = _mosi = -1;
  81. }
  82. #else // LARGE_PINMAP
  83. /**
  84. * Create the interface object using software (bitbang) SPI for PIN values
  85. * which are larger than 127. If you have PIN values less than or equal to
  86. * 127 use the other call for SW SPI.
  87. *
  88. * @param spi_cs the SPI CS pin to use
  89. * @param spi_mosi the SPI MOSI pin to use
  90. * @param spi_miso the SPI MISO pin to use
  91. * @param spi_clk the SPI clock pin to use
  92. * @param pin_mapping set to 1 for positive pin values
  93. */
  94. MAX31865::MAX31865(uint32_t spi_cs, uint32_t spi_mosi, uint32_t spi_miso, uint32_t spi_clk, uint8_t pin_mapping) {
  95. _cs = spi_cs;
  96. _mosi = spi_mosi;
  97. _miso = spi_miso;
  98. _sclk = spi_clk;
  99. }
  100. /**
  101. * Create the interface object using hardware SPI for PIN values which are
  102. * larger than 127. If you have PIN values less than or equal to 127 use
  103. * the other call for HW SPI.
  104. *
  105. * @param spi_cs the SPI CS pin to use along with the default SPI device
  106. * @param pin_mapping set to 1 for positive pin values
  107. */
  108. MAX31865::MAX31865(uint32_t spi_cs, uint8_t pin_mapping) {
  109. _cs = spi_cs;
  110. _sclk = _miso = _mosi = -1UL; //-1UL or 0xFFFFFFFF or 4294967295
  111. }
  112. #endif // LARGE_PINMAP
  113. /**
  114. *
  115. * Instance & Class methods
  116. *
  117. */
  118. /**
  119. * Initialize the SPI interface and set the number of RTD wires used
  120. *
  121. * @param wires The number of wires in enum format. Can be MAX31865_2WIRE, MAX31865_3WIRE, or MAX31865_4WIRE.
  122. * @param zero The resistance of the RTD at 0 degC, in ohms.
  123. * @param ref The resistance of the reference resistor, in ohms.
  124. */
  125. void MAX31865::begin(max31865_numwires_t wires, float zero, float ref) {
  126. Rzero = zero;
  127. Rref = ref;
  128. OUT_WRITE(_cs, HIGH);
  129. if (_sclk != TERN(LARGE_PINMAP, -1UL, -1)) {
  130. softSpiBegin(SPI_QUARTER_SPEED); // Define pin modes for Software SPI
  131. }
  132. else {
  133. #ifdef MAX31865_DEBUG
  134. SERIAL_ECHOLNPGM("Initializing MAX31865 Hardware SPI");
  135. #endif
  136. SPI.begin(); // Start and configure hardware SPI
  137. }
  138. setWires(wires);
  139. enableBias(false);
  140. autoConvert(false);
  141. clearFault();
  142. #ifdef MAX31865_DEBUG_SPI
  143. SERIAL_ECHOLNPGM(
  144. TERN(LARGE_PINMAP, "LARGE_PINMAP", "Regular")
  145. " begin call with _cs: ", _cs,
  146. " _miso: ", _miso,
  147. " _sclk: ", _sclk,
  148. " _mosi: ", _mosi,
  149. " config: ", readRegister8(MAX31856_CONFIG_REG)
  150. );
  151. #endif
  152. }
  153. /**
  154. * Read the raw 8-bit FAULTSTAT register
  155. *
  156. * @return The raw unsigned 8-bit FAULT status register
  157. */
  158. uint8_t MAX31865::readFault() {
  159. return readRegister8(MAX31856_FAULTSTAT_REG);
  160. }
  161. /**
  162. * Clear all faults in FAULTSTAT.
  163. */
  164. void MAX31865::clearFault() {
  165. setConfig(MAX31856_CONFIG_FAULTSTAT, 1);
  166. }
  167. /**
  168. * Whether we want to have continuous conversions (50/60 Hz)
  169. *
  170. * @param b If true, auto conversion is enabled
  171. */
  172. void MAX31865::autoConvert(bool b) {
  173. setConfig(MAX31856_CONFIG_MODEAUTO, b);
  174. }
  175. /**
  176. * Whether we want filter out 50Hz noise or 60Hz noise
  177. *
  178. * @param b If true, 50Hz noise is filtered, else 60Hz(default)
  179. */
  180. void MAX31865::enable50HzFilter(bool b) {
  181. setConfig(MAX31856_CONFIG_FILT50HZ, b);
  182. }
  183. /**
  184. * Enable the bias voltage on the RTD sensor
  185. *
  186. * @param b If true bias is enabled, else disabled
  187. */
  188. void MAX31865::enableBias(bool b) {
  189. setConfig(MAX31856_CONFIG_BIAS, b);
  190. // From the datasheet:
  191. // Note that if VBIAS is off (to reduce supply current between conversions), any filter
  192. // capacitors at the RTDIN inputs need to charge before an accurate conversion can be
  193. // performed. Therefore, enable VBIAS and wait at least 10.5 time constants of the input
  194. // RC network plus an additional 1ms before initiating the conversion.
  195. if (b)
  196. DELAY_US(11500); //11.5ms
  197. }
  198. /**
  199. * Start a one-shot temperature reading.
  200. */
  201. void MAX31865::oneShot() {
  202. setConfig(MAX31856_CONFIG_1SHOT, 1);
  203. // From the datasheet:
  204. // Note that a single conversion requires approximately 52ms in 60Hz filter
  205. // mode or 62.5ms in 50Hz filter mode to complete. 1-Shot is a self-clearing bit.
  206. // TODO: switch this out depending on the filter mode.
  207. DELAY_US(65000); // 65ms
  208. }
  209. /**
  210. * How many wires we have in our RTD setup, can be MAX31865_2WIRE,
  211. * MAX31865_3WIRE, or MAX31865_4WIRE
  212. *
  213. * @param wires The number of wires in enum format
  214. */
  215. void MAX31865::setWires(max31865_numwires_t wires) {
  216. uint8_t t = readRegister8(MAX31856_CONFIG_REG);
  217. if (wires == MAX31865_3WIRE)
  218. t |= MAX31856_CONFIG_3WIRE;
  219. else // 2 or 4 wire
  220. t &= ~MAX31856_CONFIG_3WIRE;
  221. writeRegister8(MAX31856_CONFIG_REG, t);
  222. }
  223. /**
  224. * Read the raw 16-bit value from the RTD_REG in one shot mode. This will include
  225. * the fault bit, D0.
  226. *
  227. * @return The raw unsigned 16-bit register value with ERROR bit attached, NOT temperature!
  228. */
  229. uint16_t MAX31865::readRaw() {
  230. clearFault();
  231. enableBias(true);
  232. oneShot();
  233. uint16_t rtd = readRegister16(MAX31856_RTDMSB_REG);
  234. #ifdef MAX31865_DEBUG
  235. SERIAL_ECHOLNPGM("RTD MSB:", (rtd >> 8), " RTD LSB:", (rtd & 0x00FF));
  236. #endif
  237. // Disable the bias to lower power dissipation between reads.
  238. // If the ref resistor heats up, the temperature reading will be skewed.
  239. enableBias(false);
  240. return rtd;
  241. }
  242. /**
  243. * Calculate and return the resistance value of the connected RTD.
  244. *
  245. * @param refResistor The value of the matching reference resistor, usually 430 or 4300
  246. * @return The raw RTD resistance value, NOT temperature!
  247. */
  248. float MAX31865::readResistance() {
  249. // Strip the error bit (D0) and convert to a float ratio.
  250. // less precise method: (readRaw() * Rref) >> 16
  251. return (((readRaw() >> 1) / 32768.0f) * Rref);
  252. }
  253. /**
  254. * Read the RTD and pass it to temperature(float) for calculation.
  255. *
  256. * @return Temperature in C
  257. */
  258. float MAX31865::temperature() {
  259. return temperature(readResistance());
  260. }
  261. /**
  262. * Given the 15-bit ADC value, calculate the resistance and pass it to temperature(float) for calculation.
  263. *
  264. * @return Temperature in C
  265. */
  266. float MAX31865::temperature(uint16_t adcVal) {
  267. return temperature(((adcVal) / 32768.0f) * Rref);
  268. }
  269. /**
  270. * Calculate the temperature in C from the RTD resistance.
  271. * Uses the technique outlined in this PDF:
  272. * http://www.analog.com/media/en/technical-documentation/application-notes/AN709_0.pdf
  273. *
  274. * @param Rrtd the resistance value in ohms
  275. * @return the temperature in degC
  276. */
  277. float MAX31865::temperature(float Rrtd) {
  278. float temp = (RTD_Z1 + sqrt(RTD_Z2 + (RTD_Z3 * Rrtd))) / RTD_Z4;
  279. // From the PDF...
  280. //
  281. // The previous equation is valid only for temperatures of 0°C and above.
  282. // The equation for RRTD(t) that defines negative temperature behavior is a
  283. // fourth-order polynomial (after expanding the third term) and is quite
  284. // impractical to solve for a single expression of temperature as a function
  285. // of resistance.
  286. //
  287. if (temp < 0) {
  288. Rrtd = (Rrtd / Rzero) * 100; // normalize to 100 ohm
  289. float rpoly = Rrtd;
  290. temp = -242.02 + (2.2228 * rpoly);
  291. rpoly *= Rrtd; // square
  292. temp += 2.5859e-3 * rpoly;
  293. rpoly *= Rrtd; // ^3
  294. temp -= 4.8260e-6 * rpoly;
  295. rpoly *= Rrtd; // ^4
  296. temp -= 2.8183e-8 * rpoly;
  297. rpoly *= Rrtd; // ^5
  298. temp += 1.5243e-10 * rpoly;
  299. }
  300. return temp;
  301. }
  302. //
  303. // private:
  304. //
  305. /**
  306. * Set a value in the configuration register.
  307. *
  308. * @param config 8-bit value for the config item
  309. * @param enable whether to enable or disable the value
  310. */
  311. void MAX31865::setConfig(uint8_t config, bool enable) {
  312. uint8_t t = readRegister8(MAX31856_CONFIG_REG);
  313. if (enable)
  314. t |= config;
  315. else
  316. t &= ~config; // disable
  317. writeRegister8(MAX31856_CONFIG_REG, t);
  318. }
  319. /**
  320. * Read a single byte from the specified register address.
  321. *
  322. * @param addr the register address
  323. * @return the register contents
  324. */
  325. uint8_t MAX31865::readRegister8(uint8_t addr) {
  326. uint8_t ret = 0;
  327. readRegisterN(addr, &ret, 1);
  328. return ret;
  329. }
  330. /**
  331. * Read two bytes: 1 from the specified register address, and 1 from the next address.
  332. *
  333. * @param addr the first register address
  334. * @return both register contents as a single 16-bit int
  335. */
  336. uint16_t MAX31865::readRegister16(uint8_t addr) {
  337. uint8_t buffer[2] = {0, 0};
  338. readRegisterN(addr, buffer, 2);
  339. uint16_t ret = buffer[0];
  340. ret <<= 8;
  341. ret |= buffer[1];
  342. return ret;
  343. }
  344. /**
  345. * Read +n+ bytes from a specified address into +buffer+. Set D7 to 0 to specify a read.
  346. *
  347. * @param addr the first register address
  348. * @param buffer storage for the read bytes
  349. * @param n the number of bytes to read
  350. */
  351. void MAX31865::readRegisterN(uint8_t addr, uint8_t buffer[], uint8_t n) {
  352. addr &= 0x7F; // make sure top bit is not set
  353. if (_sclk == TERN(LARGE_PINMAP, -1UL, -1))
  354. SPI.beginTransaction(spiConfig);
  355. else
  356. WRITE(_sclk, LOW);
  357. WRITE(_cs, LOW);
  358. #ifdef TARGET_LPC1768
  359. DELAY_CYCLES(_spi_speed);
  360. #endif
  361. spiTransfer(addr);
  362. while (n--) {
  363. buffer[0] = spiTransfer(0xFF);
  364. #ifdef MAX31865_DEBUG_SPI
  365. SERIAL_ECHOLNPGM("buffer read ", n, " data: ", buffer[0]);
  366. #endif
  367. buffer++;
  368. }
  369. if (_sclk == TERN(LARGE_PINMAP, -1UL, -1))
  370. SPI.endTransaction();
  371. WRITE(_cs, HIGH);
  372. }
  373. /**
  374. * Write an 8-bit value to a register. Set D7 to 1 to specify a write.
  375. *
  376. * @param addr the address to write to
  377. * @param data the data to write
  378. */
  379. void MAX31865::writeRegister8(uint8_t addr, uint8_t data) {
  380. if (_sclk == TERN(LARGE_PINMAP, -1UL, -1))
  381. SPI.beginTransaction(spiConfig);
  382. else
  383. WRITE(_sclk, LOW);
  384. WRITE(_cs, LOW);
  385. #ifdef TARGET_LPC1768
  386. DELAY_CYCLES(_spi_speed);
  387. #endif
  388. spiTransfer(addr | 0x80); // make sure top bit is set
  389. spiTransfer(data);
  390. if (_sclk == TERN(LARGE_PINMAP, -1UL, -1))
  391. SPI.endTransaction();
  392. WRITE(_cs, HIGH);
  393. }
  394. /**
  395. * Transfer SPI data +x+ and read the response. From the datasheet...
  396. * Input data (SDI) is latched on the internal strobe edge and output data (SDO) is
  397. * shifted out on the shift edge. There is one clock for each bit transferred.
  398. * Address and data bits are transferred in groups of eight, MSB first.
  399. *
  400. * @param x an 8-bit chunk of data to write
  401. * @return the 8-bit response
  402. */
  403. uint8_t MAX31865::spiTransfer(uint8_t x) {
  404. if (_sclk == TERN(LARGE_PINMAP, -1UL, -1))
  405. return SPI.transfer(x);
  406. #ifdef TARGET_LPC1768
  407. return swSpiTransfer(x, _spi_speed, _sclk, _miso, _mosi);
  408. #else
  409. uint8_t reply = 0;
  410. for (int i = 7; i >= 0; i--) {
  411. WRITE(_sclk, HIGH); DELAY_NS_VAR(_spi_delay);
  412. reply <<= 1;
  413. WRITE(_mosi, x & _BV(i)); DELAY_NS_VAR(_spi_delay);
  414. if (READ(_miso)) reply |= 1;
  415. WRITE(_sclk, LOW); DELAY_NS_VAR(_spi_delay);
  416. }
  417. return reply;
  418. #endif
  419. }
  420. void MAX31865::softSpiBegin(const uint8_t spi_speed) {
  421. #ifdef MAX31865_DEBUG
  422. SERIAL_ECHOLNPGM("Initializing MAX31865 Software SPI");
  423. #endif
  424. #ifdef TARGET_LPC1768
  425. swSpiBegin(_sclk, _miso, _mosi);
  426. _spi_speed = swSpiInit(spi_speed, _sclk, _mosi);
  427. #else
  428. _spi_delay = (100UL << spi_speed) / 3; // Calculate delay in ns. Top speed is ~10MHz, or 100ns delay between bits.
  429. OUT_WRITE(_sclk, LOW);
  430. SET_OUTPUT(_mosi);
  431. SET_INPUT(_miso);
  432. #endif
  433. }
  434. #endif // HAS_MAX31865 && !USE_ADAFRUIT_MAX31865