* Laser: * M3 - Laser ON/Power (Ramped power) * M4 - Laser ON/Power (Ramped power) * * M3I - Enable continuous inline power to be processed by the planner, with power * calculated and set in the planner blocks, processed inline during stepping. * In inline mode M3 S-Values will set the power for the next moves. * (e.g., G1 X10 Y10 powers on with the last S-Value.) *
| 74 | * PWM duty cycle goes from 0 (off) to 255 (always on). |
| 75 | */ |
| 76 | void GcodeSuite::M3_M4(const bool is_M4) { |
| 77 | #if LASER_SAFETY_TIMEOUT_MS > 0 |
| 78 | reset_stepper_timeout(); // Reset timeout to allow subsequent G-code to power the laser (imm.) |
| 79 | #endif |
| 80 | |
| 81 | if (cutter.cutter_mode == CUTTER_MODE_STANDARD) |
| 82 | planner.synchronize(); // Wait for previous movement commands (G0/G1/G2/G3) to complete before changing power |
| 83 | |
| 84 | #if ENABLED(LASER_FEATURE) |
| 85 | if (parser.seen_test('I')) { |
| 86 | cutter.cutter_mode = is_M4 ? CUTTER_MODE_DYNAMIC : CUTTER_MODE_CONTINUOUS; |
| 87 | cutter.inline_power(0); |
| 88 | cutter.set_enabled(true); |
| 89 | } |
| 90 | #endif |
| 91 | |
| 92 | auto get_s_power = [] { |
| 93 | if (parser.seenval('S')) { |
| 94 | const float v = parser.value_float(); |
| 95 | cutter.menuPower = cutter.unitPower = TERN(LASER_POWER_TRAP, constrain( v, 0, CUTTER_POWER_MAX), cutter.power_to_range(v)); |
| 96 | } |
| 97 | else if (parser.seenval('O')) { // pwr in PWM units |
| 98 | const float v = parser.value_float(); |
| 99 | cutter.menuPower = cutter.unitPower = CUTTER_PWM_TO_SPWR(constrain(v, 0, 255)); |
| 100 | } |
| 101 | else if (cutter.cutter_mode == CUTTER_MODE_STANDARD) |
| 102 | cutter.menuPower = cutter.unitPower = cutter.cpwr_to_upwr(SPEED_POWER_STARTUP); |
| 103 | |
| 104 | // PWM not implied, power converted to OCR from unit definition and on/off if not PWM. |
| 105 | cutter.power = TERN(SPINDLE_LASER_USE_PWM, cutter.upower_to_ocr(cutter.unitPower), cutter.unitPower > 0 ? 255 : 0); |
| 106 | }; |
| 107 | |
| 108 | if (cutter.cutter_mode == CUTTER_MODE_CONTINUOUS || cutter.cutter_mode == CUTTER_MODE_DYNAMIC) { // Laser power in inline mode |
| 109 | #if ENABLED(LASER_FEATURE) |
| 110 | planner.laser_inline.status.isPowered = true; // M3 or M4 is powered either way |
| 111 | get_s_power(); // Update cutter.power if seen |
| 112 | #if ENABLED(LASER_POWER_SYNC) |
| 113 | // With power sync we only set power so it does not effect queued inline power sets |
| 114 | planner.buffer_sync_block(BLOCK_BIT_LASER_PWR); // Send the flag, queueing inline power |
| 115 | #else |
| 116 | planner.synchronize(); |
| 117 | cutter.inline_power(cutter.power); |
| 118 | #endif |
| 119 | #endif |
| 120 | } |
| 121 | else { |
| 122 | cutter.set_enabled(true); |
| 123 | get_s_power(); |
| 124 | cutter.apply_power( |
| 125 | #if ENABLED(SPINDLE_SERVO) |
| 126 | cutter.unitPower |
| 127 | #elif ENABLED(SPINDLE_LASER_USE_PWM) |
| 128 | cutter.upower_to_ocr(cutter.unitPower) |
| 129 | #else |
| 130 | cutter.unitPower > 0 ? 255 : 0 |
| 131 | #endif |
| 132 | ); |
| 133 | TERN_(SPINDLE_CHANGE_DIR, cutter.set_reverse(is_M4)); |
nothing calls this directly
no test coverage detected