mirror of
https://github.com/grblHAL/core.git
synced 2026-02-06 09:02:33 +08:00
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.
326 lines
10 KiB
C
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
|