mirror of
https://github.com/grblHAL/core.git
synced 2026-02-06 18:29:49 +08:00
394 lines
12 KiB
C
394 lines
12 KiB
C
/*
|
|
stepper2.c - secondary stepper motor driver
|
|
|
|
Part of grblHAL
|
|
|
|
Copyright (c) 2023 Terje Io
|
|
|
|
Algorithm based on article/code by David Austin:
|
|
https://www.embedded.com/generate-stepper-motor-speed-profiles-in-real-time/
|
|
|
|
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 "hal.h"
|
|
|
|
#include <math.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "stepper2.h"
|
|
|
|
typedef enum {
|
|
State_Idle = 0,
|
|
State_Accel,
|
|
State_Run,
|
|
State_RunInfinite,
|
|
State_DecelTo,
|
|
State_Decel
|
|
} st2_state_t;
|
|
|
|
struct st2_motor {
|
|
uint_fast8_t idx;
|
|
axes_signals_t axis;
|
|
volatile int64_t position; // absolute step number
|
|
position_t ptype; //
|
|
st2_state_t state; // state machine state
|
|
uint32_t move; // total steps to move
|
|
uint32_t step_no; // progress of move
|
|
uint32_t step_run; //
|
|
uint32_t step_down; // start of down-ramp
|
|
uint64_t c64; // 24.16 fixed point delay count
|
|
uint32_t delay; // integer delay count
|
|
uint32_t first_delay; // integer delay count
|
|
uint16_t min_delay; // integer delay count
|
|
int32_t denom; // 4.n+1 in ramp algo
|
|
uint32_t n; // accel/decel steps
|
|
float speed; // speed steps/s
|
|
float prev_speed; // speed steps/s
|
|
float acceleration; // acceleration steps/s^2
|
|
axes_signals_t dir; // current direction
|
|
uint32_t next_step;
|
|
st2_motor_t *next;
|
|
};
|
|
|
|
static st2_motor_t *motors = NULL;
|
|
static settings_changed_ptr settings_changed;
|
|
static on_reset_ptr on_reset;
|
|
|
|
static void st_motor_config (st2_motor_t *motor)
|
|
{
|
|
motor->acceleration = settings.axis[motor->idx].acceleration * settings.axis[motor->idx].steps_per_mm / 3600.0f;
|
|
motor->first_delay = (uint32_t)(0.676f * sqrtf(2.0f / motor->acceleration) * 1000000.0f);
|
|
}
|
|
|
|
static void st2_reset (void)
|
|
{
|
|
st2_motor_t *motor = motors;
|
|
|
|
while(motor) {
|
|
motor->state = State_Idle;
|
|
motor = motor->next;
|
|
}
|
|
}
|
|
|
|
static void st2_settings_changed (settings_t *settings, settings_changed_flags_t changed)
|
|
{
|
|
st2_motor_t *motor = motors;
|
|
|
|
settings_changed(settings, changed);
|
|
|
|
while(motor) {
|
|
st_motor_config(motor);
|
|
motor = motor->next;
|
|
}
|
|
}
|
|
|
|
st2_motor_t *st2_motor_init (uint_fast8_t axis_idx)
|
|
{
|
|
st2_motor_t *motor, *new = motors;
|
|
|
|
if((motor = malloc(sizeof(st2_motor_t)))) {
|
|
|
|
memset(motor, 0, sizeof(st2_motor_t));
|
|
motor->idx = axis_idx;
|
|
motor->axis.mask = 1 << axis_idx;
|
|
|
|
st_motor_config(motor);
|
|
|
|
if(new == NULL) {
|
|
motors = motor;
|
|
|
|
settings_changed = hal.settings_changed;
|
|
hal.settings_changed = st2_settings_changed;
|
|
|
|
on_reset = grbl.on_reset;
|
|
grbl.on_reset = st2_reset;
|
|
|
|
} else {
|
|
while(new->next)
|
|
new = new->next;
|
|
new->next = motor;
|
|
}
|
|
}
|
|
|
|
return motor;
|
|
}
|
|
|
|
float st2_motor_set_speed (st2_motor_t *motor, float speed)
|
|
{
|
|
motor->speed = speed > settings.axis[motor->idx].max_rate ? settings.axis[motor->idx].max_rate : speed;
|
|
motor->speed *= settings.axis[motor->idx].steps_per_mm / 60.0f;
|
|
|
|
if(motor->speed == motor->prev_speed)
|
|
return motor->speed;
|
|
|
|
motor->min_delay = (uint32_t)(1000000.0f / motor->speed);
|
|
motor->n = (uint32_t)(motor->speed * motor->speed) / (2.0f * motor->acceleration);
|
|
|
|
if(motor->n == 0)
|
|
motor->n = 1;
|
|
|
|
if(motor->state != State_Idle) {
|
|
|
|
int32_t pn = motor->n - ((motor->denom - 1) >> 2);
|
|
|
|
if(pn == 0)
|
|
return motor->speed;
|
|
|
|
#ifdef DEBUGOUT
|
|
debug_writeln("!!");
|
|
debug_writeln(uitoa(motor->state));
|
|
debug_writeln(ftoa(motor->prev_speed, 2));
|
|
debug_writeln(ftoa(motor->speed, 2));
|
|
debug_writeln(uitoa((motor->denom - 1) >> 2));
|
|
debug_writeln(uitoa(motor->n));
|
|
debug_write(pn < 0 ? "-" : "+");
|
|
debug_writeln(uitoa(pn < 0 ? -pn : pn));
|
|
debug_writeln(uitoa(motor->denom));
|
|
#endif
|
|
|
|
if(motor->speed > motor->prev_speed) {
|
|
if(motor->state == State_Accel)
|
|
motor->step_run += pn;
|
|
else {
|
|
motor->step_run = motor->step_no + pn;
|
|
motor->state = State_Accel;
|
|
}
|
|
} else {
|
|
if(motor->speed == 0.0f)
|
|
motor->state = State_Decel;
|
|
if(motor->state != State_Decel) {
|
|
motor->step_run = motor->step_no - pn;
|
|
motor->state = State_DecelTo;
|
|
}
|
|
}
|
|
}
|
|
|
|
motor->prev_speed = motor->speed;
|
|
|
|
if(motor->first_delay < motor->min_delay)
|
|
motor->first_delay = motor->min_delay;
|
|
|
|
return motor->prev_speed;
|
|
}
|
|
|
|
bool st2_motor_move (st2_motor_t *motor, const float move, const float speed, position_t type)
|
|
{
|
|
bool dir = move < 0.0f;
|
|
|
|
if(speed == 0.0f)
|
|
return false;
|
|
|
|
if((motor->dir.mask == 0) != dir)
|
|
motor->dir.mask = dir ? 0 : motor->axis.mask;
|
|
|
|
motor->ptype = type;
|
|
|
|
switch(type) {
|
|
|
|
case Stepper2_Steps:
|
|
motor->move = (uint32_t)fabs((int32_t)move);
|
|
break;
|
|
|
|
case Stepper2_InfiniteSteps:
|
|
motor->move = (uint32_t)fabs((int32_t)move);
|
|
break;
|
|
|
|
case Stepper2_mm:
|
|
motor->move = (uint32_t)fabs(move * settings.axis[motor->idx].steps_per_mm);
|
|
|
|
break;
|
|
}
|
|
|
|
st2_motor_set_speed(motor, speed);
|
|
|
|
motor->step_no = 0; // step counter
|
|
|
|
if(type == Stepper2_InfiniteSteps) {
|
|
|
|
motor->state = State_Accel;
|
|
motor->step_run = motor->n;
|
|
motor->step_down = motor->n + 1;
|
|
motor->delay = motor->first_delay;
|
|
motor->c64 = ((uint32_t)motor->delay) << 16; // keep delay in 24.16 fixed-point format for ramp calcs
|
|
motor->denom = 1; // 4.n + 1, n = 0
|
|
motor->next_step = hal.get_micros();
|
|
} else if(motor->move == 1) {
|
|
|
|
motor->step_run = 1;
|
|
motor->step_down = 1;
|
|
motor->delay = motor->first_delay;
|
|
motor->c64 = ((uint32_t)motor->delay) << 8; // keep delay in 24.16 fixed-point format for ramp calcs
|
|
motor->denom = 1; // 4.n + 1, n = 0
|
|
|
|
hal.stepper.output_step(motor->axis, motor->dir);
|
|
|
|
} else if(motor->move != 0) {
|
|
|
|
motor->state = State_Accel;
|
|
motor->step_run = (motor->move - ((motor->move & 0x0001) ? 1 : 0)) >> 1;
|
|
if(motor->step_run > motor->n)
|
|
motor->step_run = motor->n;
|
|
motor->step_down = motor->move - motor->step_run;
|
|
motor->delay = motor->first_delay;
|
|
motor->c64 = ((uint32_t)motor->delay) << 8; // keep delay in 24.16 fixed-point format for ramp calcs
|
|
motor->denom = 1; // 4.n + 1, n = 0
|
|
motor->next_step = hal.get_micros();
|
|
}
|
|
|
|
|
|
#ifdef DEBUGOUT
|
|
uint32_t nn = motor->n;
|
|
float cn = motor->first_delay;
|
|
do {
|
|
cn -= (2.0f * cn) / (4.0f * nn + 1);
|
|
} while(--nn);
|
|
|
|
debug_writeln("move");
|
|
debug_writeln(ftoa(speed, 2));
|
|
debug_writeln(ftoa(settings.axis[motor->idx].steps_per_mm, 3));
|
|
debug_writeln(uitoa(motor->n));
|
|
debug_writeln(uitoa(motor->delay));
|
|
debug_writeln(uitoa(motor->min_delay));
|
|
debug_writeln(ftoa(cn, 2));
|
|
debug_writeln(ftoa(motor->speed, 2));
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
int64_t st2_get_position (st2_motor_t *motor)
|
|
{
|
|
return motor->position;
|
|
}
|
|
|
|
bool st2_set_position (st2_motor_t *motor, int64_t position)
|
|
{
|
|
if(motor->state == State_Idle)
|
|
motor->position = position;
|
|
|
|
return motor->state == State_Idle;
|
|
}
|
|
|
|
bool st2_motor_run (st2_motor_t *motor)
|
|
{
|
|
uint32_t t = hal.get_micros();
|
|
|
|
if(motor->state == State_Idle || t - motor->next_step < motor->delay)
|
|
return motor->state != State_Idle;
|
|
|
|
// output step;
|
|
|
|
hal.stepper.output_step(motor->axis, motor->dir);
|
|
|
|
if(motor->dir.mask)
|
|
motor->position--;
|
|
else
|
|
motor->position++;
|
|
|
|
motor->step_no++;
|
|
|
|
switch(motor->state) {
|
|
|
|
case State_Accel:
|
|
if(motor->step_no == motor->step_run) {
|
|
motor->state = motor->step_run == motor->step_down ? State_Decel : (motor->ptype == Stepper2_InfiniteSteps ? State_RunInfinite : State_Run);
|
|
motor->denom -= 2;
|
|
if(motor->state == State_Run || motor->state == State_RunInfinite)
|
|
motor->delay = motor->min_delay;
|
|
} else {
|
|
motor->denom += 4;
|
|
motor->c64 -= (motor->c64 << 1) / motor->denom; // ramp algorithm
|
|
motor->delay = (motor->c64 + 32768) >> 16; // round 24.16 format -> int16
|
|
if (motor->delay < motor->min_delay) { // go to constant speed?
|
|
motor->denom -= 6; // causes issues with speed override for infinite moves
|
|
motor->state = motor->ptype == Stepper2_InfiniteSteps ? State_RunInfinite : State_Run;
|
|
motor->step_down = motor->move - motor->step_no;
|
|
motor->delay = motor->min_delay;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case State_Run:
|
|
if(motor->step_no == motor->step_down)
|
|
motor->state = State_Decel;
|
|
break;
|
|
|
|
case State_Decel:
|
|
if(motor->denom < 2) { // done?
|
|
motor->state = State_Idle;
|
|
motor->prev_speed = 0.0f;
|
|
motor->n = 0;
|
|
#ifdef DEBUGOUT
|
|
debug_writeln(uitoa(motor->position));
|
|
#endif
|
|
} else {
|
|
motor->c64 += (motor->c64 << 1) / motor->denom; // ramp algorithm
|
|
motor->delay = (motor->c64 - 32768) >> 16; // round 24.16 format -> int16
|
|
motor->denom -= 4;
|
|
}
|
|
break;
|
|
|
|
case State_DecelTo:
|
|
if(motor->step_no != motor->step_run) {
|
|
motor->c64 += (motor->c64 << 1) / motor->denom; // ramp algorithm
|
|
motor->delay = (motor->c64 - 32768) >> 16; // round 24.16 format -> int16
|
|
motor->denom -= 4;
|
|
} else
|
|
motor->state = motor->ptype == Stepper2_InfiniteSteps ? State_RunInfinite : State_Run;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
motor->next_step = t;
|
|
|
|
return motor->state != State_Idle;
|
|
}
|
|
|
|
bool st2_motor_stop (st2_motor_t *motor)
|
|
{
|
|
switch(motor->state) {
|
|
|
|
case State_Accel:
|
|
motor->step_no = motor->step_down - 1;
|
|
motor->step_run = motor->step_down;
|
|
break;
|
|
|
|
case State_Run:
|
|
motor->step_no = motor->step_down - 1;
|
|
break;
|
|
|
|
case State_RunInfinite:
|
|
case State_DecelTo:
|
|
motor->state = State_Decel;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return motor->state != State_Idle;
|
|
}
|
|
|
|
bool st2_motor_running (st2_motor_t *motor)
|
|
{
|
|
return motor->state != State_Idle;
|
|
}
|
|
|
|
bool st2_motor_cruising (st2_motor_t *motor)
|
|
{
|
|
return motor->state == State_Run || motor->state == State_RunInfinite;
|
|
}
|