Various Laser / Spindle improvements (#15335)

This commit is contained in:
Ben
2020-04-03 01:31:08 +01:00
committed by GitHub
parent e7e9304819
commit df8b7dfc40
24 changed files with 776 additions and 169 deletions

View File

@ -815,11 +815,10 @@ void Planner::calculate_trapezoid_for_block(block_t* const block, const float &e
#if ENABLED(S_CURVE_ACCELERATION)
// Jerk controlled speed requires to express speed versus time, NOT steps
uint32_t acceleration_time = ((float)(cruise_rate - initial_rate) / accel) * (STEPPER_TIMER_RATE),
deceleration_time = ((float)(cruise_rate - final_rate) / accel) * (STEPPER_TIMER_RATE);
deceleration_time = ((float)(cruise_rate - final_rate) / accel) * (STEPPER_TIMER_RATE),
// And to offload calculations from the ISR, we also calculate the inverse of those times here
uint32_t acceleration_time_inverse = get_period_inverse(acceleration_time);
uint32_t deceleration_time_inverse = get_period_inverse(deceleration_time);
acceleration_time_inverse = get_period_inverse(acceleration_time),
deceleration_time_inverse = get_period_inverse(deceleration_time);
#endif
// Store new block parameters
@ -834,6 +833,47 @@ void Planner::calculate_trapezoid_for_block(block_t* const block, const float &e
block->cruise_rate = cruise_rate;
#endif
block->final_rate = final_rate;
/**
* Laser trapezoid calculations
*
* Approximate the trapezoid with the laser, incrementing the power every `entry_per` while accelerating
* and decrementing it every `exit_power_per` while decelerating, thus ensuring power is related to feedrate.
*
* LASER_POWER_INLINE_TRAPEZOID_CONT doesn't need this as it continuously approximates
*
* Note this may behave unreliably when running with S_CURVE_ACCELERATION
*/
#if ENABLED(LASER_POWER_INLINE_TRAPEZOID)
if (block->laser.power > 0) { // No need to care if power == 0
const uint8_t entry_power = block->laser.power * entry_factor; // Power on block entry
#if DISABLED(LASER_POWER_INLINE_TRAPEZOID_CONT)
// Speedup power
const uint8_t entry_power_diff = block->laser.power - entry_power;
if (entry_power_diff) {
block->laser.entry_per = accelerate_steps / entry_power_diff;
block->laser.power_entry = entry_power;
}
else {
block->laser.entry_per = 0;
block->laser.power_entry = block->laser.power;
}
// Slowdown power
const uint8_t exit_power = block->laser.power * exit_factor, // Power on block entry
exit_power_diff = block->laser.power - exit_power;
if (exit_power_diff) {
block->laser.exit_per = (block->step_event_count - block->decelerate_after) / exit_power_diff;
block->laser.power_exit = exit_power;
}
else {
block->laser.exit_per = 0;
block->laser.power_exit = block->laser.power;
}
#else
block->laser.power_entry = entry_power;
#endif
}
#endif
}
/* PLANNER SPEED DEFINITION
@ -1813,6 +1853,12 @@ bool Planner::_populate_block(block_t * const block, bool split_move,
// Set direction bits
block->direction_bits = dm;
// Update block laser power
#if ENABLED(LASER_POWER_INLINE)
block->laser.status = settings.laser.status;
block->laser.power = settings.laser.power;
#endif
// Number of steps for each axis
// See http://www.corexy.com/theory.html
#if CORE_IS_XY

View File

@ -52,7 +52,7 @@
#endif
#if HAS_CUTTER
#include "../feature/spindle_laser.h"
#include "../feature/spindle_laser_types.h"
#endif
// Feedrate for manual moves
@ -88,6 +88,23 @@ enum BlockFlag : char {
BLOCK_FLAG_SYNC_POSITION = _BV(BLOCK_BIT_SYNC_POSITION)
};
#if ENABLED(LASER_POWER_INLINE)
typedef struct {
uint8_t status, // See planner settings for meaning
power; // Ditto; When in trapezoid mode this is nominal power
#if ENABLED(LASER_POWER_INLINE_TRAPEZOID)
uint8_t power_entry; // Entry power for the laser
#if DISABLED(LASER_POWER_INLINE_TRAPEZOID_CONT)
uint8_t power_exit; // Exit power for the laser
uint32_t entry_per, // Steps per power increment (to avoid floats in stepper calcs)
exit_per; // Steps per power decrement
#endif
#endif
} block_laser_t;
#endif
/**
* struct block_t
*
@ -174,12 +191,36 @@ typedef struct block_t {
uint32_t sdpos;
#endif
#if ENABLED(LASER_POWER_INLINE)
block_laser_t laser;
#endif
} block_t;
#define HAS_POSITION_FLOAT ANY(LIN_ADVANCE, SCARA_FEEDRATE_SCALING, GRADIENT_MIX, LCD_SHOW_E_TOTAL)
#define BLOCK_MOD(n) ((n)&(BLOCK_BUFFER_SIZE-1))
#if ENABLED(LASER_POWER_INLINE)
typedef struct {
/**
* Laser status bitmask; most bits are unused;
* 0: Planner buffer enable
* 1: Laser enable
* 2: Reserved for direction
*/
uint8_t status;
/**
* Laser power: 0 or 255 in case of PWM-less laser,
* or the OCR value;
*
* Using OCR instead of raw power,
* as it avoids floating points during move loop
*/
uint8_t power;
} settings_laser_t;
#endif
typedef struct {
uint32_t max_acceleration_mm_per_s2[XYZE_N], // (mm/s^2) M201 XYZE
min_segment_time_us; // (µs) M205 B
@ -190,6 +231,9 @@ typedef struct {
travel_acceleration; // (mm/s^2) M204 T - Travel acceleration. DEFAULT ACCELERATION for all NON printing moves.
feedRate_t min_feedrate_mm_s, // (mm/s) M205 S - Minimum linear feedrate
min_travel_feedrate_mm_s; // (mm/s) M205 T - Minimum travel feedrate
#if ENABLED(LASER_POWER_INLINE)
settings_laser_t laser;
#endif
} planner_settings_t;
#if DISABLED(SKEW_CORRECTION)

View File

@ -133,6 +133,10 @@ Stepper stepper; // Singleton
#include "../feature/powerloss.h"
#endif
#if HAS_CUTTER
#include "../feature/spindle_laser.h"
#endif
// public:
#if HAS_EXTRA_ENDSTOPS || ENABLED(Z_STEPPER_AUTO_ALIGN)
@ -236,6 +240,20 @@ xyz_long_t Stepper::endstops_trigsteps;
xyze_long_t Stepper::count_position{0};
xyze_int8_t Stepper::count_direction{0};
#if ENABLED(LASER_POWER_INLINE_TRAPEZOID)
Stepper::stepper_laser_t Stepper::laser = {
.trap_en = false,
.cur_power = 0,
.cruise_set = false,
#if DISABLED(LASER_POWER_INLINE_TRAPEZOID_CONT)
.last_step_count = 0,
.acc_step_count = 0
#else
.till_update = 0
#endif
};
#endif
#define DUAL_ENDSTOP_APPLY_STEP(A,V) \
if (separate_multi_axis) { \
if (A##_HOME_DIR < 0) { \
@ -1674,10 +1692,9 @@ uint32_t Stepper::block_phase_isr() {
#if ENABLED(S_CURVE_ACCELERATION)
// Get the next speed to use (Jerk limited!)
uint32_t acc_step_rate =
acceleration_time < current_block->acceleration_time
? _eval_bezier_curve(acceleration_time)
: current_block->cruise_rate;
uint32_t acc_step_rate = acceleration_time < current_block->acceleration_time
? _eval_bezier_curve(acceleration_time)
: current_block->cruise_rate;
#else
acc_step_rate = STEP_MULTIPLY(acceleration_time, current_block->acceleration_rate) + current_block->initial_rate;
NOMORE(acc_step_rate, current_block->nominal_rate);
@ -1690,9 +1707,40 @@ uint32_t Stepper::block_phase_isr() {
acceleration_time += interval;
#if ENABLED(LIN_ADVANCE)
// Fire ISR if final adv_rate is reached
if (LA_steps && (!LA_use_advance_lead || LA_isr_rate != current_block->advance_speed))
initiateLA();
if (LA_use_advance_lead) {
// Fire ISR if final adv_rate is reached
if (LA_steps && LA_isr_rate != current_block->advance_speed) nextAdvanceISR = 0;
}
else if (LA_steps) nextAdvanceISR = 0;
#endif
// Update laser - Accelerating
#if ENABLED(LASER_POWER_INLINE_TRAPEZOID)
if (laser.trap_en) {
#if DISABLED(LASER_POWER_INLINE_TRAPEZOID_CONT)
if (current_block->laser.entry_per) {
laser.acc_step_count -= step_events_completed - laser.last_step_count;
laser.last_step_count = step_events_completed;
// Should be faster than a divide, since this should trip just once
if (laser.acc_step_count < 0) {
while (laser.acc_step_count < 0) {
laser.acc_step_count += current_block->laser.entry_per;
if (laser.cur_power < current_block->laser.power) laser.cur_power++;
}
cutter.set_ocr_power(laser.cur_power);
}
}
#else
if (laser.till_update)
laser.till_update--;
else {
laser.till_update = LASER_POWER_INLINE_TRAPEZOID_CONT_PER;
laser.cur_power = (current_block->laser.power * acc_step_rate) / current_block->nominal_rate;
cutter.set_ocr_power(laser.cur_power); // Cycle efficiency is irrelevant it the last line was many cycles
}
#endif
}
#endif
}
// Are we in Deceleration phase ?
@ -1740,10 +1788,39 @@ uint32_t Stepper::block_phase_isr() {
LA_isr_rate = current_block->advance_speed;
}
}
else if (LA_steps) initiateLA();
else if (LA_steps) nextAdvanceISR = 0;
#endif // LIN_ADVANCE
// Update laser - Decelerating
#if ENABLED(LASER_POWER_INLINE_TRAPEZOID)
if (laser.trap_en) {
#if DISABLED(LASER_POWER_INLINE_TRAPEZOID_CONT)
if (current_block->laser.exit_per) {
laser.acc_step_count -= step_events_completed - laser.last_step_count;
laser.last_step_count = step_events_completed;
// Should be faster than a divide, since this should trip just once
if (laser.acc_step_count < 0) {
while (laser.acc_step_count < 0) {
laser.acc_step_count += current_block->laser.exit_per;
if (laser.cur_power > current_block->laser.power_exit) laser.cur_power--;
}
cutter.set_ocr_power(laser.cur_power);
}
}
#else
if (laser.till_update)
laser.till_update--;
else {
laser.till_update = LASER_POWER_INLINE_TRAPEZOID_CONT_PER;
laser.cur_power = (current_block->laser.power * step_rate) / current_block->nominal_rate;
cutter.set_ocr_power(laser.cur_power); // Cycle efficiency isn't relevant when the last line was many cycles
}
#endif
}
#endif
}
// We must be in cruise phase otherwise
// Must be in cruise phase otherwise
else {
#if ENABLED(LIN_ADVANCE)
@ -1759,6 +1836,22 @@ uint32_t Stepper::block_phase_isr() {
// The timer interval is just the nominal value for the nominal speed
interval = ticks_nominal;
// Update laser - Cruising
#if ENABLED(LASER_POWER_INLINE_TRAPEZOID)
if (laser.trap_en) {
if (!laser.cruise_set) {
laser.cur_power = current_block->laser.power;
cutter.set_ocr_power(laser.cur_power);
laser.cruise_set = true;
}
#if ENABLED(LASER_POWER_INLINE_TRAPEZOID_CONT)
laser.till_update = LASER_POWER_INLINE_TRAPEZOID_CONT_PER;
#else
laser.last_step_count = step_events_completed;
#endif
}
#endif
}
}
}
@ -1805,11 +1898,11 @@ uint32_t Stepper::block_phase_isr() {
* If DeltaA == DeltaB, the movement is only in the 1st axis (X)
*/
#if EITHER(COREXY, COREXZ)
#define X_CMP ==
#define X_CMP(A,B) ((A)==(B))
#else
#define X_CMP !=
#define X_CMP(A,B) ((A)!=(B))
#endif
#define X_MOVE_TEST ( S_(1) != S_(2) || (S_(1) > 0 && D_(1) X_CMP D_(2)) )
#define X_MOVE_TEST ( S_(1) != S_(2) || (S_(1) > 0 && X_CMP(D_(1),D_(2))) )
#else
#define X_MOVE_TEST !!current_block->steps.a
#endif
@ -1823,11 +1916,11 @@ uint32_t Stepper::block_phase_isr() {
* If DeltaA == -DeltaB, the movement is only in the 2nd axis (Y or Z)
*/
#if EITHER(COREYX, COREYZ)
#define Y_CMP ==
#define Y_CMP(A,B) ((A)==(B))
#else
#define Y_CMP !=
#define Y_CMP(A,B) ((A)!=(B))
#endif
#define Y_MOVE_TEST ( S_(1) != S_(2) || (S_(1) > 0 && D_(1) Y_CMP D_(2)) )
#define Y_MOVE_TEST ( S_(1) != S_(2) || (S_(1) > 0 && Y_CMP(D_(1),D_(2))) )
#else
#define Y_MOVE_TEST !!current_block->steps.b
#endif
@ -1841,11 +1934,11 @@ uint32_t Stepper::block_phase_isr() {
* If DeltaA == -DeltaB, the movement is only in the 2nd axis (Z)
*/
#if EITHER(COREZX, COREZY)
#define Z_CMP ==
#define Z_CMP(A,B) ((A)==(B))
#else
#define Z_CMP !=
#define Z_CMP(A,B) ((A)!=(B))
#endif
#define Z_MOVE_TEST ( S_(1) != S_(2) || (S_(1) > 0 && D_(1) Z_CMP D_(2)) )
#define Z_MOVE_TEST ( S_(1) != S_(2) || (S_(1) > 0 && Z_CMP(D_(1),D_(2))) )
#else
#define Z_MOVE_TEST !!current_block->steps.c
#endif
@ -1938,6 +2031,39 @@ uint32_t Stepper::block_phase_isr() {
set_directions();
}
#if ENABLED(LASER_POWER_INLINE)
const uint8_t stat = current_block->laser.status;
#if ENABLED(LASER_POWER_INLINE_TRAPEZOID)
laser.trap_en = (stat & 0x03) == 0x03;
laser.cur_power = current_block->laser.power_entry; // RESET STATE
laser.cruise_set = false;
#if DISABLED(LASER_POWER_INLINE_TRAPEZOID_CONT)
laser.last_step_count = 0;
laser.acc_step_count = current_block->laser.entry_per / 2;
#else
laser.till_update = 0;
#endif
// Always have PWM in this case
if (TEST(stat, 0)) { // Planner controls the laser
if (TEST(stat, 1)) // Laser is on
cutter.set_ocr_power(laser.cur_power);
else
cutter.set_power(0);
}
#else
if (TEST(stat, 0)) { // Planner controls the laser
#if ENABLED(SPINDLE_LASER_PWM)
if (TEST(stat, 1)) // Laser is on
cutter.set_ocr_power(current_block->laser.power);
else
cutter.set_power(0);
#else
cutter.set_enabled(TEST(stat, 1));
#endif
}
#endif
#endif // LASER_POWER_INLINE
// At this point, we must ensure the movement about to execute isn't
// trying to force the head against a limit switch. If using interrupt-
// driven change detection, and already against a limit then no call to
@ -1957,21 +2083,35 @@ uint32_t Stepper::block_phase_isr() {
// Mark the time_nominal as not calculated yet
ticks_nominal = -1;
#if DISABLED(S_CURVE_ACCELERATION)
// Set as deceleration point the initial rate of the block
acc_step_rate = current_block->initial_rate;
#endif
#if ENABLED(S_CURVE_ACCELERATION)
// Initialize the Bézier speed curve
_calc_bezier_curve_coeffs(current_block->initial_rate, current_block->cruise_rate, current_block->acceleration_time_inverse);
// We haven't started the 2nd half of the trapezoid
bezier_2nd_half = false;
#else
// Set as deceleration point the initial rate of the block
acc_step_rate = current_block->initial_rate;
#endif
// Calculate the initial timer interval
interval = calc_timer_interval(current_block->initial_rate, &steps_per_isr);
}
#if ENABLED(LASER_POWER_INLINE_CONTINUOUS)
else { // No new block found; so apply inline laser parameters
// This should mean ending file with 'M5 I' will stop the laser; thus the inline flag isn't needed
const uint8_t stat = planner.settings.laser.status;
if (TEST(stat, 0)) { // Planner controls the laser
#if ENABLED(SPINDLE_LASER_PWM)
if (TEST(stat, 1)) // Laser is on
cutter.set_ocr_power(planner.settings.laser.power);
else
cutter.set_power(0);
#else
cutter.set_enabled(TEST(stat, 1));
#endif
}
}
#endif
}
// Return the interval to wait

View File

@ -339,23 +339,35 @@ class Stepper {
static uint32_t acc_step_rate; // needed for deceleration start point
#endif
//
// Exact steps at which an endstop was triggered
//
static xyz_long_t endstops_trigsteps;
//
// Positions of stepper motors, in step units
//
static xyze_long_t count_position;
//
// Current direction of stepper motors (+1 or -1)
//
// Current stepper motor directions (+1 or -1)
static xyze_int8_t count_direction;
public:
#if ENABLED(LASER_POWER_INLINE_TRAPEZOID)
typedef struct {
bool trap_en; // Trapezoid needed flag (i.e., laser on, planner in control)
uint8_t cur_power; // Current laser power
bool cruise_set; // Power set up for cruising?
#if DISABLED(LASER_POWER_INLINE_TRAPEZOID_CONT)
uint32_t last_step_count, // Step count from the last update
acc_step_count; // Bresenham counter for laser accel/decel
#else
uint16_t till_update; // Countdown to the next update
#endif
} stepper_laser_t;
static stepper_laser_t laser;
#endif
public:
// Initialize stepper hardware
static void init();