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.

spindle_laser.h 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. /**
  2. * Marlin 3D Printer Firmware
  3. * Copyright (c) 2020 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. #pragma once
  23. /**
  24. * feature/spindle_laser.h
  25. * Support for Laser Power or Spindle Power & Direction
  26. */
  27. #include "../inc/MarlinConfig.h"
  28. #include "spindle_laser_types.h"
  29. #if HAS_BEEPER
  30. #include "../libs/buzzer.h"
  31. #endif
  32. #if ENABLED(LASER_POWER_INLINE)
  33. #include "../module/planner.h"
  34. #endif
  35. #define PCT_TO_PWM(X) ((X) * 255 / 100)
  36. #define PCT_TO_SERVO(X) ((X) * 180 / 100)
  37. class SpindleLaser {
  38. public:
  39. static const inline uint8_t pct_to_ocr(const_float_t pct) { return uint8_t(PCT_TO_PWM(pct)); }
  40. // cpower = configured values (e.g., SPEED_POWER_MAX)
  41. // Convert configured power range to a percentage
  42. static const inline uint8_t cpwr_to_pct(const cutter_cpower_t cpwr) {
  43. constexpr cutter_cpower_t power_floor = TERN(CUTTER_POWER_RELATIVE, SPEED_POWER_MIN, 0),
  44. power_range = SPEED_POWER_MAX - power_floor;
  45. return cpwr ? round(100.0f * (cpwr - power_floor) / power_range) : 0;
  46. }
  47. // Convert a cpower (e.g., SPEED_POWER_STARTUP) to unit power (upwr, upower),
  48. // which can be PWM, Percent, Servo angle, or RPM (rel/abs).
  49. static const inline cutter_power_t cpwr_to_upwr(const cutter_cpower_t cpwr) { // STARTUP power to Unit power
  50. const cutter_power_t upwr = (
  51. #if ENABLED(SPINDLE_FEATURE)
  52. // Spindle configured values are in RPM
  53. #if CUTTER_UNIT_IS(RPM)
  54. cpwr // to RPM
  55. #elif CUTTER_UNIT_IS(PERCENT) // to PCT
  56. cpwr_to_pct(cpwr)
  57. #elif CUTTER_UNIT_IS(SERVO) // to SERVO angle
  58. PCT_TO_SERVO(cpwr_to_pct(cpwr))
  59. #else // to PWM
  60. PCT_TO_PWM(cpwr_to_pct(cpwr))
  61. #endif
  62. #else
  63. // Laser configured values are in PCT
  64. #if CUTTER_UNIT_IS(PWM255)
  65. PCT_TO_PWM(cpwr)
  66. #else
  67. cpwr // to RPM/PCT
  68. #endif
  69. #endif
  70. );
  71. return upwr;
  72. }
  73. static const cutter_power_t mpower_min() { return cpwr_to_upwr(SPEED_POWER_MIN); }
  74. static const cutter_power_t mpower_max() { return cpwr_to_upwr(SPEED_POWER_MAX); }
  75. #if ENABLED(LASER_FEATURE)
  76. static cutter_test_pulse_t testPulse; // Test fire Pulse ms value
  77. #endif
  78. static bool isReady; // Ready to apply power setting from the UI to OCR
  79. static uint8_t power,
  80. last_power_applied; // Basic power state tracking
  81. #if ENABLED(MARLIN_DEV_MODE)
  82. static cutter_frequency_t frequency; // Set PWM frequency; range: 2K-50K
  83. #endif
  84. static cutter_power_t menuPower, // Power as set via LCD menu in PWM, Percentage or RPM
  85. unitPower; // Power as displayed status in PWM, Percentage or RPM
  86. static void init();
  87. #if ENABLED(MARLIN_DEV_MODE)
  88. static void refresh_frequency() { hal.set_pwm_frequency(pin_t(SPINDLE_LASER_PWM_PIN), frequency); }
  89. #endif
  90. // Modifying this function should update everywhere
  91. static bool enabled(const cutter_power_t opwr) { return opwr > 0; }
  92. static bool enabled() { return enabled(power); }
  93. static void apply_power(const uint8_t inpow);
  94. FORCE_INLINE static void refresh() { apply_power(power); }
  95. FORCE_INLINE static void set_power(const uint8_t upwr) { power = upwr; refresh(); }
  96. #if ENABLED(SPINDLE_LASER_USE_PWM)
  97. private:
  98. static void _set_ocr(const uint8_t ocr);
  99. public:
  100. static void set_ocr(const uint8_t ocr);
  101. static void ocr_set_power(const uint8_t ocr) { power = ocr; set_ocr(ocr); }
  102. static void ocr_off();
  103. /**
  104. * Update output for power->OCR translation
  105. */
  106. static uint8_t upower_to_ocr(const cutter_power_t upwr) {
  107. return uint8_t(
  108. #if CUTTER_UNIT_IS(PWM255)
  109. upwr
  110. #elif CUTTER_UNIT_IS(PERCENT)
  111. pct_to_ocr(upwr)
  112. #else
  113. pct_to_ocr(cpwr_to_pct(upwr))
  114. #endif
  115. );
  116. }
  117. /**
  118. * Correct power to configured range
  119. */
  120. static cutter_power_t power_to_range(const cutter_power_t pwr) {
  121. return power_to_range(pwr, _CUTTER_POWER(CUTTER_POWER_UNIT));
  122. }
  123. static cutter_power_t power_to_range(const cutter_power_t pwr, const uint8_t pwrUnit) {
  124. static constexpr float
  125. min_pct = TERN(CUTTER_POWER_RELATIVE, 0, TERN(SPINDLE_FEATURE, round(100.0f * (SPEED_POWER_MIN) / (SPEED_POWER_MAX)), SPEED_POWER_MIN)),
  126. max_pct = TERN(SPINDLE_FEATURE, 100, SPEED_POWER_MAX);
  127. if (pwr <= 0) return 0;
  128. cutter_power_t upwr;
  129. switch (pwrUnit) {
  130. case _CUTTER_POWER_PWM255:
  131. upwr = cutter_power_t(
  132. (pwr < pct_to_ocr(min_pct)) ? pct_to_ocr(min_pct) // Use minimum if set below
  133. : (pwr > pct_to_ocr(max_pct)) ? pct_to_ocr(max_pct) // Use maximum if set above
  134. : pwr
  135. );
  136. break;
  137. case _CUTTER_POWER_PERCENT:
  138. upwr = cutter_power_t(
  139. (pwr < min_pct) ? min_pct // Use minimum if set below
  140. : (pwr > max_pct) ? max_pct // Use maximum if set above
  141. : pwr // PCT
  142. );
  143. break;
  144. case _CUTTER_POWER_RPM:
  145. upwr = cutter_power_t(
  146. (pwr < SPEED_POWER_MIN) ? SPEED_POWER_MIN // Use minimum if set below
  147. : (pwr > SPEED_POWER_MAX) ? SPEED_POWER_MAX // Use maximum if set above
  148. : pwr // Calculate OCR value
  149. );
  150. break;
  151. default: break;
  152. }
  153. return upwr;
  154. }
  155. #endif // SPINDLE_LASER_USE_PWM
  156. /**
  157. * Enable/Disable spindle/laser
  158. * @param enable true = enable; false = disable
  159. */
  160. static void set_enabled(const bool enable) {
  161. uint8_t value = 0;
  162. if (enable) {
  163. #if ENABLED(SPINDLE_LASER_USE_PWM)
  164. if (power)
  165. value = power;
  166. else if (unitPower)
  167. value = upower_to_ocr(cpwr_to_upwr(SPEED_POWER_STARTUP));
  168. #else
  169. value = 255;
  170. #endif
  171. }
  172. set_power(value);
  173. }
  174. static void disable() { isReady = false; set_enabled(false); }
  175. /**
  176. * Wait for spindle to spin up or spin down
  177. *
  178. * @param on true = state to on; false = state to off.
  179. */
  180. static void power_delay(const bool on) {
  181. #if DISABLED(LASER_POWER_INLINE)
  182. safe_delay(on ? SPINDLE_LASER_POWERUP_DELAY : SPINDLE_LASER_POWERDOWN_DELAY);
  183. #endif
  184. }
  185. #if ENABLED(SPINDLE_CHANGE_DIR)
  186. static void set_reverse(const bool reverse);
  187. static bool is_reverse() { return READ(SPINDLE_DIR_PIN) == SPINDLE_INVERT_DIR; }
  188. #else
  189. static void set_reverse(const bool) {}
  190. static bool is_reverse() { return false; }
  191. #endif
  192. #if ENABLED(AIR_EVACUATION)
  193. static void air_evac_enable(); // Turn On Cutter Vacuum or Laser Blower motor
  194. static void air_evac_disable(); // Turn Off Cutter Vacuum or Laser Blower motor
  195. static void air_evac_toggle(); // Toggle Cutter Vacuum or Laser Blower motor
  196. static bool air_evac_state() { // Get current state
  197. return (READ(AIR_EVACUATION_PIN) == AIR_EVACUATION_ACTIVE);
  198. }
  199. #endif
  200. #if ENABLED(AIR_ASSIST)
  201. static void air_assist_enable(); // Turn on air assist
  202. static void air_assist_disable(); // Turn off air assist
  203. static void air_assist_toggle(); // Toggle air assist
  204. static bool air_assist_state() { // Get current state
  205. return (READ(AIR_ASSIST_PIN) == AIR_ASSIST_ACTIVE);
  206. }
  207. #endif
  208. #if HAS_MARLINUI_MENU
  209. static void enable_with_dir(const bool reverse) {
  210. isReady = true;
  211. const uint8_t ocr = TERN(SPINDLE_LASER_USE_PWM, upower_to_ocr(menuPower), 255);
  212. if (menuPower)
  213. power = ocr;
  214. else
  215. menuPower = cpwr_to_upwr(SPEED_POWER_STARTUP);
  216. unitPower = menuPower;
  217. set_reverse(reverse);
  218. set_enabled(true);
  219. }
  220. FORCE_INLINE static void enable_forward() { enable_with_dir(false); }
  221. FORCE_INLINE static void enable_reverse() { enable_with_dir(true); }
  222. FORCE_INLINE static void enable_same_dir() { enable_with_dir(is_reverse()); }
  223. #if ENABLED(SPINDLE_LASER_USE_PWM)
  224. static void update_from_mpower() {
  225. if (isReady) power = upower_to_ocr(menuPower);
  226. unitPower = menuPower;
  227. }
  228. #endif
  229. #if ENABLED(LASER_FEATURE)
  230. /**
  231. * Test fire the laser using the testPulse ms duration
  232. * Also fires with any PWM power that was previous set
  233. * If not set defaults to 80% power
  234. */
  235. static void test_fire_pulse() {
  236. TERN_(HAS_BEEPER, buzzer.tone(30, 3000));
  237. enable_forward(); // Turn Laser on (Spindle speak but same funct)
  238. delay(testPulse); // Delay for time set by user in pulse ms menu screen.
  239. disable(); // Turn laser off
  240. }
  241. #endif
  242. #endif // HAS_MARLINUI_MENU
  243. #if ENABLED(LASER_POWER_INLINE)
  244. /**
  245. * Inline power adds extra fields to the planner block
  246. * to handle laser power and scale to movement speed.
  247. */
  248. // Force disengage planner power control
  249. static void inline_disable() {
  250. isReady = false;
  251. unitPower = 0;
  252. planner.laser_inline.status.isPlanned = false;
  253. planner.laser_inline.status.isEnabled = false;
  254. planner.laser_inline.power = 0;
  255. }
  256. // Inline modes of all other functions; all enable planner inline power control
  257. static void set_inline_enabled(const bool enable) {
  258. if (enable)
  259. inline_power(255);
  260. else {
  261. isReady = false;
  262. unitPower = menuPower = 0;
  263. planner.laser_inline.status.isPlanned = false;
  264. TERN(SPINDLE_LASER_USE_PWM, inline_ocr_power, inline_power)(0);
  265. }
  266. }
  267. // Set the power for subsequent movement blocks
  268. static void inline_power(const cutter_power_t upwr) {
  269. unitPower = menuPower = upwr;
  270. #if ENABLED(SPINDLE_LASER_USE_PWM)
  271. #if ENABLED(SPEED_POWER_RELATIVE) && !CUTTER_UNIT_IS(RPM) // relative mode does not turn laser off at 0, except for RPM
  272. planner.laser_inline.status.isEnabled = true;
  273. planner.laser_inline.power = upower_to_ocr(upwr);
  274. isReady = true;
  275. #else
  276. inline_ocr_power(upower_to_ocr(upwr));
  277. #endif
  278. #else
  279. planner.laser_inline.status.isEnabled = enabled(upwr);
  280. planner.laser_inline.power = upwr;
  281. isReady = enabled(upwr);
  282. #endif
  283. }
  284. static void inline_direction(const bool) { /* never */ }
  285. #if ENABLED(SPINDLE_LASER_USE_PWM)
  286. static void inline_ocr_power(const uint8_t ocrpwr) {
  287. isReady = ocrpwr > 0;
  288. planner.laser_inline.status.isEnabled = ocrpwr > 0;
  289. planner.laser_inline.power = ocrpwr;
  290. }
  291. #endif
  292. #endif // LASER_POWER_INLINE
  293. static void kill() {
  294. TERN_(LASER_POWER_INLINE, inline_disable());
  295. disable();
  296. }
  297. };
  298. extern SpindleLaser cutter;