Files
grblHAL/wall_plotter.c
Terje Io e8530a45ab Fix for incorrect sequencing of init calls when corexy and backlash compensation is enabled at the same time.
Added call to driver to immediately set stepper enable signals when $37 (Stepper deenergize) is changed.
Some minor improvements in settings handling and options reporting.
2022-07-09 18:36:01 +02:00

326 lines
10 KiB
C

/*
wall_plotter.c - wall plotter kinematics implementation
Part of grblHAL
Code lifted from Grbl_Esp32 pull request by user @ https://github.com/rognlien
Original code here: https://github.com/jasonwebb/grbl-mega-wall-plotter
Note: homing is not implemented!
Bits also pulled from: https://github.com/ldocull/MaslowDue
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Grbl is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Grbl. If not, see <http://www.gnu.org/licenses/>.
*/
#include "grbl.h"
#ifdef WALL_PLOTTER
#include <math.h>
#include <string.h>
#include "hal.h"
#include "settings.h"
#include "planner.h"
#include "kinematics.h"
#define A_MOTOR X_AXIS // Must be X_AXIS
#define B_MOTOR Y_AXIS // Must be Y_AXIS
#define MAX_SEG_LENGTH_MM 2.0f
typedef struct {
int32_t width;
float width_mm;
float width_pow;
int32_t height;
int32_t width_2;
int32_t height_2;
int32_t spindlezero[2];
float spindlezero_mm[2];
} machine_t;
typedef struct {
float a;
float b;
} coord_t;
static bool jog_cancel = false;
static machine_t machine = {0};
static on_report_options_ptr on_report_options;
// Returns machine position in mm converted from system position steps.
// TODO: perhaps change to double precision here - float calculation results in errors of a couple of micrometers.
static float *wp_convert_array_steps_to_mpos (float *position, int32_t *steps)
{
coord_t len;
len.a = (float)steps[A_MOTOR] / settings.axis[A_MOTOR].steps_per_mm;
len.b = (float)steps[B_MOTOR] / settings.axis[B_MOTOR].steps_per_mm;
position[X_AXIS] = (machine.width_pow + len.a * len.a - len.b * len.b) / (2.0f * machine.width_mm);
len.a = machine.width_mm - position[X_AXIS];
position[Y_AXIS] = sqrtf(len.b * len.b - len.a * len.a );
position[Z_AXIS] = steps[Z_AXIS] / settings.axis[Z_AXIS].steps_per_mm;
return position;
}
// Returns machine position in mm converted from system position steps.
// TODO: perhaps change to double precision here - float calculation results in errors of a couple of micrometers.
static float *transform_to_cartesian (float *target, float *position)
{
coord_t len;
len.a = position[A_MOTOR];
len.b = position[B_MOTOR];
target[X_AXIS] = (machine.width_pow + len.a * len.a - len.b * len.b) / (2.0f * machine.width_mm);
len.a = machine.width_mm - target[X_AXIS];
target[Y_AXIS] = sqrtf(len.b * len.b - len.a * len.a );
target[Z_AXIS] = position[Z_AXIS];
return target;
}
// Wall plotter calculation only. Returns x or y-axis "steps" based on wall plotter motor steps.
// A length = sqrt( X^2 + Y^2 )
// B length = sqrt( (MACHINE_WIDTH - X)^2 + Y^2 )
inline static float wp_convert_to_a_motor_steps (float *target)
{
return sqrtf(target[A_MOTOR] * target[A_MOTOR] + target[B_MOTOR] * target[B_MOTOR]);
}
inline static float wp_convert_to_b_motor_steps (float *target)
{
float xpos = machine.width_mm - target[A_MOTOR];
return sqrtf(xpos * xpos + target[B_MOTOR] * target[B_MOTOR]);
}
// Transform absolute position from cartesian coordinate system to wall plotter coordinate system
static float *transform_from_cartesian (float *target, float *position)
{
uint_fast8_t idx = N_AXIS - 1;
do {
target[idx] = position[idx];
} while(--idx > Y_AXIS);
target[A_MOTOR] = wp_convert_to_a_motor_steps(position);
target[B_MOTOR] = wp_convert_to_b_motor_steps(position);
return target;
}
static inline float get_distance (float *p0, float *p1)
{
uint_fast8_t idx = Z_AXIS;
float distance = 0.0f;
do {
idx--;
distance += (p0[idx] - p1[idx]) * (p0[idx] - p1[idx]);
} while(idx);
return sqrtf(distance);
}
// Wall plotter is circular in motion, so long lines must be divided up
static float *wp_segment_line (float *target, float *position, plan_line_data_t *pl_data, bool init)
{
static uint_fast16_t iterations;
static bool segmented;
static coord_data_t delta, segment_target, final_target, cpos;
// static plan_line_data_t plan;
uint_fast8_t idx = N_AXIS;
if(init) {
jog_cancel = false;
memcpy(final_target.values, target, sizeof(final_target));
transform_to_cartesian(segment_target.values, position);
delta.x = target[X_AXIS] - segment_target.x;
delta.y = target[Y_AXIS] - segment_target.y;
delta.z = target[Z_AXIS] - segment_target.z;
float distance = sqrtf(delta.x * delta.x + delta.y * delta.y);
if((segmented = !pl_data->condition.rapid_motion && distance > MAX_SEG_LENGTH_MM && !(delta.x == 0.0f && delta.y == 0.0f))) {
idx = N_AXIS;
iterations = (uint_fast16_t)ceilf(distance / MAX_SEG_LENGTH_MM);
do {
--idx;
delta.values[idx] = delta.values[idx] / (float)iterations;
} while(idx);
} else {
iterations = 1;
memcpy(&segment_target, &final_target, sizeof(coord_data_t));
}
iterations++; // return at least one iteration
} else {
iterations--;
if(segmented && iterations > 1) {
do {
idx--;
segment_target.values[idx] += delta.values[idx];
} while(idx);
} else
memcpy(&segment_target, &final_target, sizeof(coord_data_t));
transform_from_cartesian(cpos.values, segment_target.values);
}
return iterations == 0 || jog_cancel ? NULL : cpos.values;
}
static uint_fast8_t wp_limits_get_axis_mask (uint_fast8_t idx)
{
return ((idx == A_MOTOR) || (idx == B_MOTOR)) ? (bit(X_AXIS) | bit(Y_AXIS)) : bit(idx);
}
static void wp_limits_set_target_pos (uint_fast8_t idx) // fn name?
{
float xy[2];
int32_t axis_position;
xy[X_AXIS] = sys.position[X_AXIS] / settings.axis[X_AXIS].steps_per_mm;
xy[Y_AXIS] = sys.position[Y_AXIS] / settings.axis[Y_AXIS].steps_per_mm;
switch(idx) {
case X_AXIS:
axis_position = wp_convert_to_b_motor_steps(xy);
sys.position[A_MOTOR] = axis_position;
sys.position[B_MOTOR] = -axis_position;
break;
case Y_AXIS:
sys.position[A_MOTOR] = sys.position[B_MOTOR] = wp_convert_to_a_motor_steps(xy);
break;
default:
sys.position[idx] = 0;
break;
}
}
// Set machine positions for homed limit switches. Don't update non-homed axes.
// NOTE: settings.max_travel[] is stored as a negative value.
static void wp_limits_set_machine_positions (axes_signals_t cycle)
{
float xy[2];
uint_fast8_t idx = N_AXIS;
xy[X_AXIS] = sys.position[X_AXIS] / settings.axis[X_AXIS].steps_per_mm;
xy[Y_AXIS] = sys.position[Y_AXIS] / settings.axis[Y_AXIS].steps_per_mm;
if(settings.homing.flags.force_set_origin) {
if (cycle.mask & bit(--idx)) do {
switch(--idx) {
case X_AXIS:
sys.position[A_MOTOR] = wp_convert_to_b_motor_steps(xy);
sys.position[B_MOTOR] = - sys.position[A_MOTOR];
break;
case Y_AXIS:
sys.position[A_MOTOR] = wp_convert_to_a_motor_steps(xy);
sys.position[B_MOTOR] = sys.position[A_MOTOR];
break;
default:
sys.position[idx] = 0;
break;
}
} while (idx);
} else do {
if (cycle.mask & bit(--idx)) {
int32_t off_axis_position;
int32_t set_axis_position = bit_istrue(settings.homing.dir_mask.value, bit(idx))
? lroundf((settings.axis[idx].max_travel + settings.homing.pulloff) * settings.axis[idx].steps_per_mm)
: lroundf(-settings.homing.pulloff * settings.axis[idx].steps_per_mm);
switch(idx) {
case X_AXIS:
off_axis_position = wp_convert_to_b_motor_steps(xy);
sys.position[A_MOTOR] = set_axis_position + off_axis_position;
sys.position[B_MOTOR] = set_axis_position - off_axis_position;
break;
case Y_AXIS:
off_axis_position = wp_convert_to_a_motor_steps(xy);
sys.position[A_MOTOR] = off_axis_position + set_axis_position;
sys.position[B_MOTOR] = off_axis_position - set_axis_position;
break;
default:
sys.position[idx] = set_axis_position;
break;
}
}
} while(idx);
}
static void cancel_jog (sys_state_t state)
{
jog_cancel = true;
}
static void report_options (bool newopt)
{
on_report_options(newopt);
if(!newopt)
hal.stream.write("[KINEMATICS:WallPlotter v2.00]" ASCII_EOL);
}
// Initialize API pointers for Wall Plotter kinematics
void wall_plotter_init (void)
{
machine.width_mm = -settings.axis[A_MOTOR].max_travel;
machine.width = (int32_t)(machine.width_mm * settings.axis[A_MOTOR].steps_per_mm);
machine.width_2 = machine.width >> 1;
machine.width_pow = machine.width_mm * machine.width_mm;
machine.height = (int32_t)((float)settings.axis[B_MOTOR].max_travel * settings.axis[B_MOTOR].steps_per_mm);
machine.height_2 = machine.height >> 1;
machine.spindlezero[A_MOTOR] = 0; // machine.width_2;
machine.spindlezero[B_MOTOR] = 0; // machine.height_2;
machine.spindlezero_mm[A_MOTOR] = (float)machine.spindlezero[A_MOTOR] / settings.axis[A_MOTOR].steps_per_mm;
machine.spindlezero_mm[B_MOTOR] = (float)machine.spindlezero[B_MOTOR] / settings.axis[B_MOTOR].steps_per_mm;
sys.position[B_MOTOR] = machine.width;
kinematics.limits_set_target_pos = wp_limits_set_target_pos;
kinematics.limits_get_axis_mask = wp_limits_get_axis_mask;
kinematics.limits_set_machine_positions = wp_limits_set_machine_positions;
kinematics.transform_from_cartesian = transform_from_cartesian;
kinematics.transform_steps_to_cartesian = wp_convert_array_steps_to_mpos;
kinematics.segment_line = wp_segment_line;
grbl.on_jog_cancel = cancel_jog;
on_report_options = grbl.on_report_options;
grbl.on_report_options = report_options;
}
#endif