416 lines
13 KiB
C++
416 lines
13 KiB
C++
|
|
/**
|
||
|
|
* This file initially contained methods that handle motor control
|
||
|
|
* I combined the motor control with motion control
|
||
|
|
*
|
||
|
|
* File contains methods for linear movement, arc generator and motor control
|
||
|
|
*
|
||
|
|
*/
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Part of Grbl interpreter modified to work with Adafruit Motor Driver V2
|
||
|
|
* File created by Catalin Vasiliu
|
||
|
|
* email <vasiliu.catalin.mihai@gmail.com>
|
||
|
|
*/
|
||
|
|
|
||
|
|
#include <Wire.h>
|
||
|
|
#include <Adafruit_MotorShield.h>
|
||
|
|
#include "utility/Adafruit_PWMServoDriver.h"
|
||
|
|
//#include <AFMotor.h>
|
||
|
|
|
||
|
|
#include <arduino.h>
|
||
|
|
|
||
|
|
#include "config.h"
|
||
|
|
#include "nuts_bolts.h"
|
||
|
|
#include "settings.h"
|
||
|
|
#include "limit_switch.h"
|
||
|
|
#include "protocol.h"
|
||
|
|
#include "gcode.h"
|
||
|
|
|
||
|
|
#define X_motor 0
|
||
|
|
#define Y_motor 1
|
||
|
|
#define Z_motor 2
|
||
|
|
#define NUM_AXIES 3
|
||
|
|
|
||
|
|
Adafruit_MotorShield YZ_shield = Adafruit_MotorShield(0x60);
|
||
|
|
Adafruit_MotorShield XSpindle_shield = Adafruit_MotorShield(0x61);
|
||
|
|
|
||
|
|
Adafruit_StepperMotor *Motor[3];
|
||
|
|
Adafruit_DCMotor *spindle = XSpindle_shield.getMotor(SPINDLE_PORT);
|
||
|
|
|
||
|
|
static int current_direction;
|
||
|
|
|
||
|
|
typedef struct {
|
||
|
|
long steps_to_travel;
|
||
|
|
long abs_steps_to_travel;
|
||
|
|
int dir;
|
||
|
|
long step_counter;
|
||
|
|
} Axis;
|
||
|
|
|
||
|
|
Axis axis[3];
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Change I2C clock to 400 KHz
|
||
|
|
* Initialize Adafruit Motor Shields
|
||
|
|
*/
|
||
|
|
void shield_begin() {
|
||
|
|
//Change the i2c clock to 400KHz
|
||
|
|
TWBR = ((F_CPU / 400000l) - 16) / 2;
|
||
|
|
|
||
|
|
YZ_shield.begin();
|
||
|
|
XSpindle_shield.begin();
|
||
|
|
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Initialize motors
|
||
|
|
*/
|
||
|
|
void motors_init(){
|
||
|
|
Motor[X_motor] = XSpindle_shield.getStepper(settings.steps_per_turn[X_AXIS], 2); //x
|
||
|
|
Motor[Y_motor] = YZ_shield.getStepper(settings.steps_per_turn[Y_AXIS], 2); //y
|
||
|
|
Motor[Z_motor] = YZ_shield.getStepper(settings.steps_per_turn[Z_AXIS], 1); //z
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Turn ON/OFF spindle motor
|
||
|
|
* Set direction and speed
|
||
|
|
* RPM not implemented use values for pwm (0-255)
|
||
|
|
*
|
||
|
|
* @param int direction
|
||
|
|
* @param 32bit int rpm
|
||
|
|
*/
|
||
|
|
void spindle_run(int direction, uint32_t rpm) {
|
||
|
|
if (direction != current_direction) {
|
||
|
|
spindle->setSpeed(rpm);
|
||
|
|
if (direction > 0) {
|
||
|
|
spindle->run(FORWARD);
|
||
|
|
} else if (direction < 0) {
|
||
|
|
spindle->run(BACKWARD);
|
||
|
|
} else {
|
||
|
|
spindle->run(RELEASE);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
current_direction = direction;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Stop spindle
|
||
|
|
*/
|
||
|
|
void spindle_stop() {
|
||
|
|
spindle_run(0, 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Check the limit switch according to axis and direction
|
||
|
|
* Move the motor one step in the direction...motors are define Motor[]
|
||
|
|
* Return status ok or limit switch error
|
||
|
|
*
|
||
|
|
* @param int motor
|
||
|
|
* @param int direction
|
||
|
|
* @return int status
|
||
|
|
*/
|
||
|
|
uint8_t st_onestep(int motor, int direction) {
|
||
|
|
if(settings.limit_switch == 1){
|
||
|
|
uint8_t switchCheck = ls_check(motor, direction);
|
||
|
|
if (switchCheck > 0) {
|
||
|
|
return switchCheck;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
Motor[motor]->onestep(direction > 0 ? FORWARD : BACKWARD, DOUBLE);
|
||
|
|
return STATUS_OK;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Pause for the @param seconds ... just delay
|
||
|
|
*
|
||
|
|
* @param double seconds
|
||
|
|
*/
|
||
|
|
void st_dwell(double seconds) {
|
||
|
|
delay_ms(seconds * 1000);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Move the tool from current position to xyz position in a straight line
|
||
|
|
* Uses oneStep() function to move motors
|
||
|
|
* Calculate the time that took to travel one step on every axes
|
||
|
|
* (eg x1, y1, z0 steps is approx 1.4/step_per_mm and it takes about 8ms to travel)
|
||
|
|
* Calculate the time that the action should take according to feed_rate and pause the diference
|
||
|
|
* Return status
|
||
|
|
*
|
||
|
|
* @param double[3] position current position (gc.position)
|
||
|
|
* @param double x
|
||
|
|
* @param double y
|
||
|
|
* @param double z
|
||
|
|
* @param double feed_rate / seek_rate
|
||
|
|
* @param invert_feed_rate
|
||
|
|
* @return int status ...from oneStep / ls_check
|
||
|
|
*/
|
||
|
|
uint8_t st_line(double *position, double x, double y, double z, double feed_rate, uint8_t invert_feed_rate) {
|
||
|
|
uint8_t status = STATUS_OK;
|
||
|
|
//calculate steps to make on each axis
|
||
|
|
axis[X_AXIS].steps_to_travel = lround((x - position[X_AXIS]) * settings.steps_per_mm[X_AXIS]);
|
||
|
|
axis[Y_AXIS].steps_to_travel = lround((y - position[Y_AXIS]) * settings.steps_per_mm[Y_AXIS]);
|
||
|
|
axis[Z_AXIS].steps_to_travel = lround((z - position[Z_AXIS]) * settings.steps_per_mm[Z_AXIS]);
|
||
|
|
|
||
|
|
long i, j, maxsteps = 0;
|
||
|
|
|
||
|
|
for (i = 0; i < NUM_AXIES; ++i) {
|
||
|
|
//do not take minus vall
|
||
|
|
axis[i].abs_steps_to_travel = abs(axis[i].steps_to_travel);
|
||
|
|
//calculate dir
|
||
|
|
axis[i].dir = axis[i].steps_to_travel > 0 ? 1 : -1;
|
||
|
|
//get the longest distance
|
||
|
|
if (maxsteps < axis[i].abs_steps_to_travel) {
|
||
|
|
maxsteps = axis[i].abs_steps_to_travel;
|
||
|
|
}
|
||
|
|
axis[i].step_counter = 0;
|
||
|
|
}
|
||
|
|
//store here if a step was made on a axis, need to calculate time
|
||
|
|
bool axis_step[3];
|
||
|
|
|
||
|
|
for (i = 0; i < maxsteps; ++i) {
|
||
|
|
//get the start time
|
||
|
|
unsigned long start_step_time = millis();
|
||
|
|
for (j = 0; j < NUM_AXIES; ++j) {
|
||
|
|
axis[j].step_counter += axis[j].abs_steps_to_travel;
|
||
|
|
if (axis[j].step_counter >= maxsteps) {
|
||
|
|
axis[j].step_counter -= maxsteps;
|
||
|
|
status = st_onestep(j, axis[j].dir);
|
||
|
|
//check status and return if limit reach
|
||
|
|
if (status != STATUS_OK) {
|
||
|
|
return status;
|
||
|
|
}
|
||
|
|
axis_step[j] = 1;
|
||
|
|
} else {
|
||
|
|
axis_step[j] = 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//calculate distance traveled
|
||
|
|
float distance_traveled_mm = sqrt(
|
||
|
|
((axis_step[X_AXIS] / settings.steps_per_mm[X_AXIS]) * (axis_step[X_AXIS] / settings.steps_per_mm[X_AXIS])) +
|
||
|
|
((axis_step[Y_AXIS] / settings.steps_per_mm[Y_AXIS]) * (axis_step[Y_AXIS] / settings.steps_per_mm[Y_AXIS])) +
|
||
|
|
((axis_step[Z_AXIS] / settings.steps_per_mm[Z_AXIS]) * (axis_step[Z_AXIS] / settings.steps_per_mm[Z_AXIS]))
|
||
|
|
);
|
||
|
|
|
||
|
|
//calculate how much time(in milliseconds) should take to travel distance_traveled_mm according to feed rate
|
||
|
|
double time_should_take = distance_traveled_mm * 60000 / feed_rate;
|
||
|
|
//time that actualy take to travel
|
||
|
|
unsigned long step_time = millis() - start_step_time;
|
||
|
|
//calculate time to pause, do not accept val < 0
|
||
|
|
if (time_should_take - step_time > 0) {
|
||
|
|
delay_ms(time_should_take - step_time);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
//release motors...see config.h comments for more info
|
||
|
|
if(settings.release_after_move == 1){
|
||
|
|
for (int j = 0; j < 3; j++) {
|
||
|
|
Motor[j]->release();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return status;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Move tool to program Zero position
|
||
|
|
*
|
||
|
|
* @param double[3] position
|
||
|
|
*/
|
||
|
|
void st_go_home(double *position) {
|
||
|
|
st_line(position, 0, 0, 0, settings.default_feed_rate, false);
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
float atan3(float dy, float dx) {
|
||
|
|
float a = atan2(dy, dx);
|
||
|
|
if (a < 0) a = (PI * 2.0) + a;
|
||
|
|
return a;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* This method assumes the limits have already been checked.
|
||
|
|
* This method assumes the start and end radius match.
|
||
|
|
* This method assumes arcs are not >180 degrees (PI radians)
|
||
|
|
*
|
||
|
|
* Calculates the length of the arc according to start and end coordinates
|
||
|
|
* Brake the arc in segments (segment length defined in settings)
|
||
|
|
* Uses the st_line() function to draw each segment according to feed_rate
|
||
|
|
*
|
||
|
|
*
|
||
|
|
* @param double[3] position current position array[x, y, z] also the start of the arc
|
||
|
|
* @param double[3] target end at the arc array[x, y, z]
|
||
|
|
* @param double[3] offset the center of the circle array[x, y, z]
|
||
|
|
* @param uint8_t axis_0 contain 0, 1 or 2 and is the first axis on the selected plan
|
||
|
|
* the value represent the physical axis (ex 0 is X_AXIS, 1 is Y_AXIS..)
|
||
|
|
* @param uint8_t axis_1 the second axis on the selected plan
|
||
|
|
* @param uint8_t axis_linear the third axis on the selected plan, this one does not move
|
||
|
|
* @param double feed_rate in mm/minute or inch/minute
|
||
|
|
* @param uint8_t invert_feed_rate
|
||
|
|
* @param double radius the radius of the arc calculated in gcode.cpp
|
||
|
|
* @param bool isclockwise direction to draw the circle
|
||
|
|
* @return uint8_t status
|
||
|
|
*/
|
||
|
|
uint8_t st_arc(double *position, double *target, double *offset, uint8_t axis_0, uint8_t axis_1,
|
||
|
|
uint8_t axis_linear, double feed_rate, uint8_t invert_feed_rate, double radius, uint8_t isclockwise) {
|
||
|
|
|
||
|
|
uint8_t status = STATUS_OK;
|
||
|
|
|
||
|
|
// find angle of arc (sweep)
|
||
|
|
double angle1 = atan3(position[axis_1] - offset[axis_1], position[axis_0] - offset[axis_0]);
|
||
|
|
double angle2 = atan3( target[axis_1] - offset[axis_1], target[axis_0] - offset[axis_0]);
|
||
|
|
double theta = angle2 - angle1;
|
||
|
|
|
||
|
|
if (isclockwise == 1 && theta < 0){
|
||
|
|
angle2 += 2 * PI;
|
||
|
|
}
|
||
|
|
else if (isclockwise == 0 && theta > 0) {
|
||
|
|
angle1 += 2 * PI;
|
||
|
|
}
|
||
|
|
|
||
|
|
theta = angle2 - angle1;
|
||
|
|
|
||
|
|
// get length of arc
|
||
|
|
double len = abs(theta) * radius;
|
||
|
|
|
||
|
|
int i, segments = ceil(len * settings.mm_per_arc_segment);
|
||
|
|
|
||
|
|
double nx, ny, angle3, scale;
|
||
|
|
double arc_target[3];
|
||
|
|
|
||
|
|
for (i = 0; i < segments; ++i) {
|
||
|
|
// interpolate around the arc
|
||
|
|
scale = ((double) i) / ((double) segments);
|
||
|
|
|
||
|
|
angle3 = (theta * scale) + angle1;
|
||
|
|
|
||
|
|
//update arc target
|
||
|
|
arc_target[axis_0] = offset[axis_0] + cos(angle3) * radius;
|
||
|
|
arc_target[axis_1] = offset[axis_1] + sin(angle3) * radius;
|
||
|
|
arc_target[axis_linear] = position[axis_linear];
|
||
|
|
|
||
|
|
// make a line to that intermediate position
|
||
|
|
status = st_line(position, arc_target[X_AXIS], arc_target[Y_AXIS], arc_target[Z_AXIS], feed_rate, 0);
|
||
|
|
//check the status
|
||
|
|
if (status != STATUS_OK) {
|
||
|
|
return status;
|
||
|
|
}
|
||
|
|
//update new position
|
||
|
|
position[X_AXIS] = arc_target[X_AXIS];
|
||
|
|
position[Y_AXIS] = arc_target[Y_AXIS];
|
||
|
|
position[Z_AXIS] = arc_target[Z_AXIS];
|
||
|
|
}
|
||
|
|
//draw the last segment
|
||
|
|
status = st_line(position, target[X_AXIS], target[Y_AXIS], target[Z_AXIS], feed_rate, 0);
|
||
|
|
return status;
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* put all motors to mechanical zero except the z axis witch is optional
|
||
|
|
* be aware of the tool on z axis, it may be lower so it can not reach the limit
|
||
|
|
* switch this will stall the motor and also can cause damages
|
||
|
|
*
|
||
|
|
* Works only with limit switch !!!
|
||
|
|
*
|
||
|
|
* @param bool zero_Z_axis
|
||
|
|
*/
|
||
|
|
void st_go_to_zero(bool zero_Z_axis) {
|
||
|
|
if(settings.limit_switch == 1){
|
||
|
|
uint8_t xLimit = 0;
|
||
|
|
uint8_t yLimit = 0;
|
||
|
|
uint8_t zLimit = 0;
|
||
|
|
|
||
|
|
while (true) {
|
||
|
|
if (st_onestep(X_AXIS, -1) == X_LIMIT_START_ENABLE) {
|
||
|
|
xLimit = 1;
|
||
|
|
}
|
||
|
|
if (st_onestep(Y_AXIS, -1) == Y_LIMIT_START_ENABLE) {
|
||
|
|
yLimit = 1;
|
||
|
|
}
|
||
|
|
if (zero_Z_axis) {
|
||
|
|
if (st_onestep(Z_AXIS, -1) == Z_LIMIT_START_ENABLE) {
|
||
|
|
zLimit = 1;
|
||
|
|
}
|
||
|
|
if (xLimit + yLimit + zLimit == 3) {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
if (xLimit + yLimit == 2) {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Count steps from mechanical zero to mechanical max
|
||
|
|
* Set values in settings.work_area[X_AXIS]
|
||
|
|
* Values in mm
|
||
|
|
*
|
||
|
|
* Works only with limit switch !!!
|
||
|
|
*/
|
||
|
|
void st_calibrate() {
|
||
|
|
if(settings.limit_switch == 1){
|
||
|
|
//Go all to 0
|
||
|
|
st_go_to_zero(true);
|
||
|
|
|
||
|
|
uint8_t xLimit = 0;
|
||
|
|
uint8_t yLimit = 0;
|
||
|
|
uint8_t zLimit = 0;
|
||
|
|
|
||
|
|
double xCounter = 0;
|
||
|
|
double yCounter = 0;
|
||
|
|
double zCounter = 0;
|
||
|
|
|
||
|
|
while (true) {
|
||
|
|
if (st_onestep(X_AXIS, 1) == X_LIMIT_END_ENABLE) {
|
||
|
|
xLimit = 1;
|
||
|
|
} else {
|
||
|
|
xCounter++;
|
||
|
|
}
|
||
|
|
if (st_onestep(Y_AXIS, 1) == Y_LIMIT_END_ENABLE) {
|
||
|
|
yLimit = 1;
|
||
|
|
} else {
|
||
|
|
yCounter++;
|
||
|
|
}
|
||
|
|
if (st_onestep(Z_AXIS, 1) == Z_LIMIT_END_ENABLE) {
|
||
|
|
zLimit = 1;
|
||
|
|
} else {
|
||
|
|
zCounter++;
|
||
|
|
}
|
||
|
|
if (xLimit + yLimit + zLimit == 3) {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
settings.work_area[X_AXIS] = xCounter / settings.steps_per_mm[X_AXIS];
|
||
|
|
settings.work_area[Y_AXIS] = yCounter / settings.steps_per_mm[Y_AXIS];
|
||
|
|
settings.work_area[Z_AXIS] = zCounter / settings.steps_per_mm[Z_AXIS];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Works with M102 and limit swiths
|
||
|
|
* Put jogs to a compact position for storage
|
||
|
|
* Park it :P
|
||
|
|
* In my configuration is easyer to store the machine with
|
||
|
|
* x axis to minimum ad y axis to maximum
|
||
|
|
*/
|
||
|
|
void st_machine_park(){
|
||
|
|
if(settings.limit_switch == 1){
|
||
|
|
uint8_t xLimit = 0;
|
||
|
|
uint8_t yLimit = 0;
|
||
|
|
|
||
|
|
while (true) {
|
||
|
|
if (st_onestep(X_AXIS, -1) == X_LIMIT_START_ENABLE) {
|
||
|
|
xLimit = 1;
|
||
|
|
}
|
||
|
|
if (st_onestep(Y_AXIS, 1) == Y_LIMIT_END_ENABLE) {
|
||
|
|
yLimit = 1;
|
||
|
|
}
|
||
|
|
if (xLimit + yLimit == 2) {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|