mirror of
https://github.com/synthetos/g2.git
synced 2026-02-06 11:11:57 +08:00
Highly experimental segment linear-velocity-interpolation
This commit is contained in:
@@ -50,17 +50,17 @@ static void _init_forward_diffs(float v_0, float v_1);
|
||||
* mp_forward_plan() - plan commands and moves ahead of exec; call ramping for moves
|
||||
*
|
||||
**** WARNING ****
|
||||
* This function should NOT be called directly!
|
||||
* Instead call st_request_forward_plan(), which mediates access.
|
||||
* This function should NOT be called directly!
|
||||
* Instead call st_request_forward_plan(), which mediates access.
|
||||
*
|
||||
* mp_forward_plan() performs just-in-time forward planning immediately before
|
||||
* lines and commands are queued to the move execution runtime (exec).
|
||||
* mp_forward_plan() performs just-in-time forward planning immediately before
|
||||
* lines and commands are queued to the move execution runtime (exec).
|
||||
* Unlike backward planning, buffers are only forward planned once.
|
||||
*
|
||||
* mp_forward_plan() is called aggressively via st_request_forward_plan().
|
||||
* It has a relatively low interrupt level to call its own.
|
||||
* See also: Planner Overview notes in planner.h
|
||||
*
|
||||
*
|
||||
* It examines the currently running buffer and its adjacent buffers to:
|
||||
*
|
||||
* - Stop the system from re-planning or planning something that's not prepped
|
||||
@@ -68,43 +68,43 @@ static void _init_forward_diffs(float v_0, float v_1);
|
||||
* - Skip past/ or pre-plan COMMAND blocks while labeling them as PLANNED
|
||||
*
|
||||
* Returns:
|
||||
* - STAT_OK if exec should be called to kickstart (or continue) movement
|
||||
* - STAT_OK if exec should be called to kickstart (or continue) movement
|
||||
* - STAT_NOOP to exit with no action taken (do not call exec)
|
||||
*/
|
||||
/*
|
||||
* --- Forward Planning Processing and Cases ---
|
||||
*
|
||||
* These cases describe all possible sequences of buffers in the planner queue starting
|
||||
*
|
||||
* These cases describe all possible sequences of buffers in the planner queue starting
|
||||
* with the currently executing (or about to execute) Run buffer, looking forward
|
||||
* to more recently arrived buffers. In most cases only one or two buffers need to
|
||||
* be examined, but contiguous groups of commands may need to be processed.
|
||||
* to more recently arrived buffers. In most cases only one or two buffers need to
|
||||
* be examined, but contiguous groups of commands may need to be processed.
|
||||
*
|
||||
* 'Running' cases are where the run buffer state is RUNNING. Bootstrap handles all other cases.
|
||||
* 'Bootstrap' occurs during the startup phase where moves are collected before starting movement.
|
||||
* Conditions that are impossible based on this definition are not listed in the tables below.
|
||||
*
|
||||
* See planner.h / bufferState enum for shorthand used in the descriptions.
|
||||
* See planner.h / bufferState enum for shorthand used in the descriptions.
|
||||
* All cases assume a mix of moves and commands, as noted in the shorthand.
|
||||
* All cases assume 2 'blocks' - Run block & Plan block. Cases will need to be
|
||||
* revisited and generalized if more blocks are used in the future (deeper
|
||||
* All cases assume 2 'blocks' - Run block & Plan block. Cases will need to be
|
||||
* revisited and generalized if more blocks are used in the future (deeper
|
||||
* forward planning).
|
||||
*
|
||||
* 'NOT_PREPPED' refers to any preliminary state below PREPPED, i.e. < PREPPED.
|
||||
* ' NOT_PREPPED' can be either a move or command, we don't care so it's not specified.
|
||||
*
|
||||
* 'COMMAND' or 'COMMAND(s)' refers to one command or a contiguous group of command buffers
|
||||
* that may be in PREPPED or PLANNED states. Processing is always the same. Plan all
|
||||
* that may be in PREPPED or PLANNED states. Processing is always the same. Plan all
|
||||
* PREPPED commands and skip past all PLANNED commands.
|
||||
*
|
||||
* Note 1: For MOVEs use the exit velocity of the Run block (mr.r->exit_velocity) as the
|
||||
* Note 1: For MOVEs use the exit velocity of the Run block (mr.r->exit_velocity) as the
|
||||
* entry velocity of the next adjacent move.
|
||||
*
|
||||
* Note 1a: In this special COMMAND case we trust mr.r->exit_velocity because the
|
||||
* Note 1a: In this special COMMAND case we trust mr.r->exit_velocity because the
|
||||
* backplanner has already handled this case for us.
|
||||
*
|
||||
* Note 2: For COMMANDs use the entry velocity of the current runtime (mr.entry_velocity)
|
||||
* as the entry velocity for the next adjacent move. mr.entry_velocity is almost always 0,
|
||||
* but could be non-0 in a race condition.
|
||||
* as the entry velocity for the next adjacent move. mr.entry_velocity is almost always 0,
|
||||
* but could be non-0 in a race condition.
|
||||
* FYI: mr.entry_velocity is set at the end of the last running block in mp_exec_aline().
|
||||
*
|
||||
*
|
||||
@@ -197,7 +197,7 @@ stat_t mp_forward_plan()
|
||||
{
|
||||
mpBuf_t *bf = mp_get_run_buffer();
|
||||
float entry_velocity;
|
||||
|
||||
|
||||
// Case 0: Examine current running buffer for early exit conditions
|
||||
if (bf == NULL) { // case 0a: NULL means nothing is running - this is OK
|
||||
st_prep_null();
|
||||
@@ -221,11 +221,11 @@ stat_t mp_forward_plan()
|
||||
// bf now points to the first non-command buffer past the command(s)
|
||||
if ((bf->block_type == BLOCK_TYPE_ALINE) && (bf->buffer_state > MP_BUFFER_PREPPED )) { // case 1i
|
||||
entry_velocity = mr.r->exit_velocity; // set entry_velocity for Note 1a
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// bf will always be on a non-command at this point - either a move or empty buffer
|
||||
|
||||
// process move
|
||||
// process move
|
||||
if (bf->block_type == BLOCK_TYPE_ALINE) { // do cases 1a - 1e; finish cases 1f - 1k
|
||||
if (bf->buffer_state == MP_BUFFER_PREPPED) {// do 1a; finish 1f, 1j, 2d, 2i
|
||||
return (_plan_move(bf, entry_velocity));
|
||||
@@ -322,12 +322,12 @@ stat_t mp_exec_move()
|
||||
* STAT_NOOP cause no operation from the steppers - do not load the move
|
||||
* STAT_xxxxx fatal error. Ends the move and frees the bf buffer
|
||||
*
|
||||
* This routine is called from the (LO) interrupt level. The interrupt sequencing
|
||||
* relies on the behaviors of the routines being exactly correct. Each call to
|
||||
* _exec_aline() must execute and prep *one and only one* segment. If the segment
|
||||
* is the not the last segment in the bf buffer the _aline() must return STAT_EAGAIN.
|
||||
* If it's the last segment it must return STAT_OK. If it encounters a fatal error
|
||||
* that would terminate the move it should return a valid error code. Failure to
|
||||
* This routine is called from the (LO) interrupt level. The interrupt sequencing
|
||||
* relies on the behaviors of the routines being exactly correct. Each call to
|
||||
* _exec_aline() must execute and prep *one and only one* segment. If the segment
|
||||
* is the not the last segment in the bf buffer the _aline() must return STAT_EAGAIN.
|
||||
* If it's the last segment it must return STAT_OK. If it encounters a fatal error
|
||||
* that would terminate the move it should return a valid error code. Failure to
|
||||
* obey this will introduce subtle and very difficult to diagnose bugs (trust us on this).
|
||||
*
|
||||
* Note 1: Returning STAT_OK ends the move and frees the bf buffer.
|
||||
@@ -358,26 +358,26 @@ stat_t mp_exec_move()
|
||||
*/
|
||||
/* Synchronization of run BUFFER and run BLOCK
|
||||
*
|
||||
* Note first: mp_exec_aline() makes a huge assumption: When it comes time to get a
|
||||
* new run block (mr.r) it assumes the planner block (mr.p) has been fully planned
|
||||
* Note first: mp_exec_aline() makes a huge assumption: When it comes time to get a
|
||||
* new run block (mr.r) it assumes the planner block (mr.p) has been fully planned
|
||||
* via the JIT forward planning and is ready for use as the new run block.
|
||||
*
|
||||
* The runtime uses 2 structures for the current move or commend, the run BUFFER
|
||||
* from the planner queue (mb.r, aka bf), and the run BLOCK from the runtime
|
||||
* singleton (mr.r). These structures are synchronized implicitly, but not
|
||||
* explicitly referenced, as pointers can lead to race conditions.
|
||||
* The runtime uses 2 structures for the current move or commend, the run BUFFER
|
||||
* from the planner queue (mb.r, aka bf), and the run BLOCK from the runtime
|
||||
* singleton (mr.r). These structures are synchronized implicitly, but not
|
||||
* explicitly referenced, as pointers can lead to race conditions.
|
||||
* See plan_zoid.cpp / mp_calculate_ramps() for more details
|
||||
*
|
||||
* When mp_exec_aline() needs to grab a new planner buffer for a new move or command
|
||||
* When mp_exec_aline() needs to grab a new planner buffer for a new move or command
|
||||
* (i.e. block state is inactive) it swaps (rolls) the run and planner BLOCKS so that
|
||||
* mr.p (planner block) is now the mr.r (run block), and the old mr.r block becomes
|
||||
* mr.p (planner block) is now the mr.r (run block), and the old mr.r block becomes
|
||||
* available for planning; it becomes mr.p block.
|
||||
*
|
||||
* At the same time, it's when finished with its current run buffer (mb.r), it has already
|
||||
* At the same time, it's when finished with its current run buffer (mb.r), it has already
|
||||
* advanced to the next buffer. mp_exec_move() does this at the end of previous move.
|
||||
* Or in the bootstrap case, there never was a previous mb.r, so the current one is OK.
|
||||
*
|
||||
* As if by magic, the new mb.r aligns with the run block that was just moved in from
|
||||
*
|
||||
* As if by magic, the new mb.r aligns with the run block that was just moved in from
|
||||
* the planning block.
|
||||
*/
|
||||
|
||||
@@ -786,6 +786,16 @@ void mp_exit_hold_state()
|
||||
* F_1 = 120Ah^5
|
||||
*
|
||||
* Note that with our current control points, D and E are actually 0.
|
||||
*
|
||||
* Further expansion, if we can do linear extroplation during a segment, we actually do want to start
|
||||
* at t = 0.
|
||||
*
|
||||
* F_5(h)-F_5(0) = Ah^5 + Bh^4 + Ch^3 + Dh^2 + Eh
|
||||
* F_4(h)-F_4(0) = 30Ah^5 + 14Bh^4 + 6Ch^3 + 2Dh^2
|
||||
* F_3(h)-F_3(0) = 150Ah^5 + 36Bh^4 + 6Ch^3
|
||||
* F_2(h)-F_2(0) = 240Ah^5 + 24Bh^4
|
||||
* F_1(h)-F_1(0) = 120Ah^5
|
||||
*
|
||||
*/
|
||||
|
||||
// Total time: 147us
|
||||
@@ -829,6 +839,7 @@ static void _init_forward_diffs(const float v_0, const float v_1)
|
||||
const float Bh_4 = B * h_4;
|
||||
const float Ch_3 = C * h_3;
|
||||
|
||||
#if !defined(NEW_FWD_DIFF) || (NEW_FWD_DIFF==0)
|
||||
const float const1 = 7.5625; // (121.0/16.0)
|
||||
const float const2 = 3.25; // ( 13.0/ 4.0)
|
||||
const float const3 = 82.5; // (165.0/ 2.0)
|
||||
@@ -858,6 +869,28 @@ static void _init_forward_diffs(const float v_0, const float v_1)
|
||||
const float half_Ah_5 = A * half_h_5;
|
||||
|
||||
mr.segment_velocity = half_Ah_5 + half_Bh_4 + half_Ch_3 + v_0;
|
||||
|
||||
#else
|
||||
// NEW_FWD_DIFF == 1
|
||||
|
||||
/*
|
||||
* F_5 = A h^5 + B h^4 + C h^3 + D h^2 + E h
|
||||
* F_4 = 30 A h^5 + 14 B h^4 + 6 C h^3 + 2 D h^2
|
||||
* F_3 = 150 A h^5 + 36 B h^4 + 6 C h^3
|
||||
* F_2 = 240 A h^5 + 24 B h^4
|
||||
* F_1 = 120 A h^5
|
||||
*/
|
||||
|
||||
mr.forward_diff_5 = Ah_5 + Bh_4 + Ch_3;
|
||||
mr.forward_diff_4 = 30.0*Ah_5 + 14.0*Bh_4 + 6.0*Ch_3;
|
||||
mr.forward_diff_3 = 150.0*Ah_5 + 36.0*Bh_4 + 6.0*Ch_3;
|
||||
mr.forward_diff_2 = 240.0*Ah_5 + 24.0*Bh_4;
|
||||
mr.forward_diff_1 = 120.0*Ah_5;
|
||||
|
||||
mr.segment_velocity = v_0;
|
||||
mr.target_velocity = v_0 + mr.forward_diff_5;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
/*********************************************************************************************
|
||||
@@ -868,7 +901,9 @@ static stat_t _exec_aline_head(mpBuf_t *bf)
|
||||
{
|
||||
bool first_pass = false;
|
||||
if (mr.section_state == SECTION_NEW) { // INITIALIZATION
|
||||
#if !defined(NEW_FWD_DIFF) || (NEW_FWD_DIFF==0)
|
||||
first_pass = true;
|
||||
#endif
|
||||
if (fp_ZERO(mr.r->head_length)) {
|
||||
mr.section = SECTION_BODY;
|
||||
return(_exec_aline_body(bf)); // skip ahead to the body generator
|
||||
@@ -879,7 +914,13 @@ static stat_t _exec_aline_head(mpBuf_t *bf)
|
||||
|
||||
if (mr.segment_count == 1) {
|
||||
// We will only have one segment, simply average the velocities
|
||||
#if !defined(NEW_FWD_DIFF) || (NEW_FWD_DIFF==0)
|
||||
mr.segment_velocity = mr.r->head_length / mr.segment_time;
|
||||
#else
|
||||
mr.segment_velocity = mr.entry_velocity;
|
||||
mr.target_velocity = mr.r->cruise_velocity;
|
||||
#endif
|
||||
|
||||
} else {
|
||||
_init_forward_diffs(mr.entry_velocity, mr.r->cruise_velocity); // <-- sets inital segment_velocity
|
||||
}
|
||||
@@ -890,7 +931,12 @@ static stat_t _exec_aline_head(mpBuf_t *bf)
|
||||
mr.section = SECTION_HEAD;
|
||||
mr.section_state = SECTION_RUNNING;
|
||||
} else {
|
||||
#if !defined(NEW_FWD_DIFF) || (NEW_FWD_DIFF==0)
|
||||
mr.segment_velocity += mr.forward_diff_5;
|
||||
#else
|
||||
mr.segment_velocity = mr.target_velocity;
|
||||
mr.target_velocity += mr.forward_diff_5;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (_exec_aline_segment() == STAT_OK) { // set up for second half
|
||||
@@ -927,6 +973,7 @@ static stat_t _exec_aline_body(mpBuf_t *bf)
|
||||
mr.segments = ceil(uSec(body_time) / NOM_SEGMENT_USEC);
|
||||
mr.segment_time = body_time / mr.segments;
|
||||
mr.segment_velocity = mr.r->cruise_velocity;
|
||||
mr.target_velocity = mr.segment_velocity;
|
||||
mr.segment_count = (uint32_t)mr.segments;
|
||||
if (mr.segment_time < MIN_SEGMENT_TIME) {
|
||||
_debug_trap("mr.segment_time < MIN_SEGMENT_TIME");
|
||||
@@ -954,7 +1001,9 @@ static stat_t _exec_aline_tail(mpBuf_t *bf)
|
||||
{
|
||||
bool first_pass = false;
|
||||
if (mr.section_state == SECTION_NEW) { // INITIALIZATION
|
||||
#if !defined(NEW_FWD_DIFF) || (NEW_FWD_DIFF==0)
|
||||
first_pass = true;
|
||||
#endif
|
||||
|
||||
// Mark the block as unplannable
|
||||
bf->plannable = false;
|
||||
@@ -965,7 +1014,12 @@ static stat_t _exec_aline_tail(mpBuf_t *bf)
|
||||
mr.segment_time = mr.r->tail_time / mr.segments; // time to advance for each segment
|
||||
|
||||
if (mr.segment_count == 1) {
|
||||
#if !defined(NEW_FWD_DIFF) || (NEW_FWD_DIFF==0)
|
||||
mr.segment_velocity = mr.r->tail_length / mr.segment_time;
|
||||
#else
|
||||
mr.segment_velocity = mr.r->cruise_velocity;
|
||||
mr.target_velocity = mr.r->exit_velocity;
|
||||
#endif
|
||||
} else {
|
||||
_init_forward_diffs(mr.r->cruise_velocity, mr.r->exit_velocity); // <-- sets inital segment_velocity
|
||||
}
|
||||
@@ -977,7 +1031,12 @@ static stat_t _exec_aline_tail(mpBuf_t *bf)
|
||||
mr.section = SECTION_TAIL;
|
||||
mr.section_state = SECTION_RUNNING;
|
||||
} else {
|
||||
#if !defined(NEW_FWD_DIFF) || (NEW_FWD_DIFF==0)
|
||||
mr.segment_velocity += mr.forward_diff_5;
|
||||
#else
|
||||
mr.segment_velocity = mr.target_velocity;
|
||||
mr.target_velocity += mr.forward_diff_5;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (_exec_aline_segment() == STAT_OK) {
|
||||
@@ -1023,18 +1082,21 @@ static stat_t _exec_aline_segment()
|
||||
if ((--mr.segment_count == 0) && (cm.motion_state != MOTION_HOLD)) {
|
||||
copy_vector(mr.gm.target, mr.waypoint[mr.section]);
|
||||
} else {
|
||||
#if !defined(NEW_FWD_DIFF) || (NEW_FWD_DIFF==0)
|
||||
float segment_length = mr.segment_velocity * mr.segment_time;
|
||||
#else
|
||||
float segment_length = (mr.segment_velocity+mr.target_velocity) * 0.5 * mr.segment_time;
|
||||
#endif
|
||||
// see https://en.wikipedia.org/wiki/Kahan_summation_algorithm
|
||||
// for the summation compensation description
|
||||
for (uint8_t a=0; a<AXES; a++) {
|
||||
#if 1
|
||||
// The following is equivalent to:
|
||||
// mr.gm.target[a] = mr.position[a] + (mr.unit[a] * segment_length);
|
||||
|
||||
float to_add = (mr.unit[a] * segment_length) - mr.gm.target_comp[a];
|
||||
float target = mr.position[a] + to_add;
|
||||
mr.gm.target_comp[a] = (target - mr.position[a]) - to_add;
|
||||
mr.gm.target[a] = target;
|
||||
#else
|
||||
mr.gm.target[a] = mr.position[a] + (mr.unit[a] * segment_length);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1063,7 +1125,12 @@ static stat_t _exec_aline_segment()
|
||||
}
|
||||
|
||||
// Call the stepper prep function
|
||||
#if !defined(NEW_FWD_DIFF) || (NEW_FWD_DIFF==0)
|
||||
ritorno(st_prep_line(travel_steps, mr.following_error, mr.segment_time));
|
||||
#else
|
||||
ritorno(st_prep_line(mr.segment_velocity, mr.target_velocity, travel_steps, mr.following_error, mr.segment_time));
|
||||
#endif
|
||||
|
||||
copy_vector(mr.position, mr.gm.target); // update position from target
|
||||
if (mr.segment_count == 0) {
|
||||
return (STAT_OK); // this section has run all its segments
|
||||
|
||||
@@ -149,6 +149,8 @@
|
||||
#ifndef PLANNER_H_ONCE
|
||||
#define PLANNER_H_ONCE
|
||||
|
||||
#define NEW_FWD_DIFF 1
|
||||
|
||||
#include "canonical_machine.h" // used for GCodeState_t
|
||||
#include "hardware.h" // for MIN_SEGMENT_MS
|
||||
|
||||
@@ -506,7 +508,8 @@ typedef struct mpMotionRuntimeSingleton { // persistent runtime variables
|
||||
|
||||
float segments; // number of segments in line (also used by arc generation)
|
||||
uint32_t segment_count; // count of running segments
|
||||
float segment_velocity; // computed velocity for aline segment
|
||||
float segment_velocity; // computed start velocity for aline segment
|
||||
float target_velocity; // computed end velocity for aline segment
|
||||
float segment_time; // actual time increment per aline segment
|
||||
|
||||
float forward_diff_1; // forward difference level 1
|
||||
|
||||
@@ -302,67 +302,85 @@ void dda_timer_type::interrupt()
|
||||
// }
|
||||
|
||||
// process DDAs for each motor
|
||||
if ((st_run.mot[MOTOR_1].substep_accumulator += st_run.mot[MOTOR_1].substep_increment) > 0) {
|
||||
motor_1.stepStart(); // turn step bit on
|
||||
if ((st_run.mot[MOTOR_1].substep_accumulator += st_run.mot[MOTOR_1].substep_increment) > 0) {
|
||||
motor_1.stepStart(); // turn step bit on
|
||||
#if NEW_DDA == 1
|
||||
st_run.mot[MOTOR_1].substep_accumulator -= DDA_SUBSTEPS; //st_run.dda_ticks_X_substeps;
|
||||
st_run.mot[MOTOR_1].substep_accumulator -= DDA_SUBSTEPS; //st_run.dda_ticks_X_substeps;
|
||||
#else
|
||||
st_run.mot[MOTOR_1].substep_accumulator -= st_run.dda_ticks_X_substeps;
|
||||
st_run.mot[MOTOR_1].substep_accumulator -= st_run.dda_ticks_X_substeps;
|
||||
#endif
|
||||
INCREMENT_ENCODER(MOTOR_1);
|
||||
}
|
||||
if ((st_run.mot[MOTOR_2].substep_accumulator += st_run.mot[MOTOR_2].substep_increment) > 0) {
|
||||
motor_2.stepStart(); // turn step bit on
|
||||
INCREMENT_ENCODER(MOTOR_1);
|
||||
}
|
||||
#if defined(NEW_FWD_DIFF) && (NEW_FWD_DIFF==1)
|
||||
st_run.mot[MOTOR_1].substep_increment += st_run.mot[MOTOR_1].substep_increment_increment;
|
||||
#endif
|
||||
if ((st_run.mot[MOTOR_2].substep_accumulator += st_run.mot[MOTOR_2].substep_increment) > 0) {
|
||||
motor_2.stepStart(); // turn step bit on
|
||||
#if NEW_DDA == 1
|
||||
st_run.mot[MOTOR_2].substep_accumulator -= DDA_SUBSTEPS; //st_run.dda_ticks_X_substeps;
|
||||
st_run.mot[MOTOR_2].substep_accumulator -= DDA_SUBSTEPS; //st_run.dda_ticks_X_substeps;
|
||||
#else
|
||||
st_run.mot[MOTOR_2].substep_accumulator -= st_run.dda_ticks_X_substeps;
|
||||
st_run.mot[MOTOR_2].substep_accumulator -= st_run.dda_ticks_X_substeps;
|
||||
#endif
|
||||
INCREMENT_ENCODER(MOTOR_2);
|
||||
}
|
||||
#if defined(NEW_FWD_DIFF) && (NEW_FWD_DIFF==1)
|
||||
st_run.mot[MOTOR_2].substep_increment += st_run.mot[MOTOR_2].substep_increment_increment;
|
||||
#endif
|
||||
INCREMENT_ENCODER(MOTOR_2);
|
||||
}
|
||||
#if MOTORS > 2
|
||||
if ((st_run.mot[MOTOR_3].substep_accumulator += st_run.mot[MOTOR_3].substep_increment) > 0) {
|
||||
motor_3.stepStart(); // turn step bit on
|
||||
if ((st_run.mot[MOTOR_3].substep_accumulator += st_run.mot[MOTOR_3].substep_increment) > 0) {
|
||||
motor_3.stepStart(); // turn step bit on
|
||||
#if NEW_DDA == 1
|
||||
st_run.mot[MOTOR_3].substep_accumulator -= DDA_SUBSTEPS; //st_run.dda_ticks_X_substeps;
|
||||
st_run.mot[MOTOR_3].substep_accumulator -= DDA_SUBSTEPS; //st_run.dda_ticks_X_substeps;
|
||||
#else
|
||||
st_run.mot[MOTOR_3].substep_accumulator -= st_run.dda_ticks_X_substeps;
|
||||
st_run.mot[MOTOR_3].substep_accumulator -= st_run.dda_ticks_X_substeps;
|
||||
#endif
|
||||
INCREMENT_ENCODER(MOTOR_3);
|
||||
}
|
||||
#if defined(NEW_FWD_DIFF) && (NEW_FWD_DIFF==1)
|
||||
st_run.mot[MOTOR_3].substep_increment += st_run.mot[MOTOR_3].substep_increment_increment;
|
||||
#endif
|
||||
INCREMENT_ENCODER(MOTOR_3);
|
||||
}
|
||||
#endif
|
||||
#if MOTORS > 3
|
||||
if ((st_run.mot[MOTOR_4].substep_accumulator += st_run.mot[MOTOR_4].substep_increment) > 0) {
|
||||
motor_4.stepStart(); // turn step bit on
|
||||
if ((st_run.mot[MOTOR_4].substep_accumulator += st_run.mot[MOTOR_4].substep_increment) > 0) {
|
||||
motor_4.stepStart(); // turn step bit on
|
||||
#if NEW_DDA == 1
|
||||
st_run.mot[MOTOR_4].substep_accumulator -= DDA_SUBSTEPS; //st_run.dda_ticks_X_substeps;
|
||||
st_run.mot[MOTOR_4].substep_accumulator -= DDA_SUBSTEPS; //st_run.dda_ticks_X_substeps;
|
||||
#else
|
||||
st_run.mot[MOTOR_4].substep_accumulator -= st_run.dda_ticks_X_substeps;
|
||||
st_run.mot[MOTOR_4].substep_accumulator -= st_run.dda_ticks_X_substeps;
|
||||
#endif
|
||||
INCREMENT_ENCODER(MOTOR_4);
|
||||
}
|
||||
#if defined(NEW_FWD_DIFF) && (NEW_FWD_DIFF==1)
|
||||
st_run.mot[MOTOR_4].substep_increment += st_run.mot[MOTOR_4].substep_increment_increment;
|
||||
#endif
|
||||
INCREMENT_ENCODER(MOTOR_4);
|
||||
}
|
||||
#endif
|
||||
#if MOTORS > 4
|
||||
if ((st_run.mot[MOTOR_5].substep_accumulator += st_run.mot[MOTOR_5].substep_increment) > 0) {
|
||||
motor_5.stepStart(); // turn step bit on
|
||||
if ((st_run.mot[MOTOR_5].substep_accumulator += st_run.mot[MOTOR_5].substep_increment) > 0) {
|
||||
motor_5.stepStart(); // turn step bit on
|
||||
#if NEW_DDA == 1
|
||||
st_run.mot[MOTOR_5].substep_accumulator -= DDA_SUBSTEPS; //st_run.dda_ticks_X_substeps;
|
||||
st_run.mot[MOTOR_5].substep_accumulator -= DDA_SUBSTEPS; //st_run.dda_ticks_X_substeps;
|
||||
#else
|
||||
st_run.mot[MOTOR_5].substep_accumulator -= st_run.dda_ticks_X_substeps;
|
||||
st_run.mot[MOTOR_5].substep_accumulator -= st_run.dda_ticks_X_substeps;
|
||||
#endif
|
||||
INCREMENT_ENCODER(MOTOR_5);
|
||||
}
|
||||
INCREMENT_ENCODER(MOTOR_5);
|
||||
#if defined(NEW_FWD_DIFF) && (NEW_FWD_DIFF==1)
|
||||
st_run.mot[MOTOR_5].substep_increment += st_run.mot[MOTOR_5].substep_increment_increment;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#if MOTORS > 5
|
||||
if ((st_run.mot[MOTOR_6].substep_accumulator += st_run.mot[MOTOR_6].substep_increment) > 0) {
|
||||
motor_6.stepStart(); // turn step bit on
|
||||
if ((st_run.mot[MOTOR_6].substep_accumulator += st_run.mot[MOTOR_6].substep_increment) > 0) {
|
||||
motor_6.stepStart(); // turn step bit on
|
||||
#if NEW_DDA == 1
|
||||
st_run.mot[MOTOR_6].substep_accumulator -= DDA_SUBSTEPS; //st_run.dda_ticks_X_substeps;
|
||||
st_run.mot[MOTOR_6].substep_accumulator -= DDA_SUBSTEPS; //st_run.dda_ticks_X_substeps;
|
||||
#else
|
||||
st_run.mot[MOTOR_6].substep_accumulator -= st_run.dda_ticks_X_substeps;
|
||||
st_run.mot[MOTOR_6].substep_accumulator -= st_run.dda_ticks_X_substeps;
|
||||
#endif
|
||||
INCREMENT_ENCODER(MOTOR_6);
|
||||
}
|
||||
#if defined(NEW_FWD_DIFF) && (NEW_FWD_DIFF==1)
|
||||
st_run.mot[MOTOR_6].substep_increment += st_run.mot[MOTOR_6].substep_increment_increment;
|
||||
#endif
|
||||
INCREMENT_ENCODER(MOTOR_6);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Process end of segment.
|
||||
@@ -527,13 +545,15 @@ static void _load_move()
|
||||
|
||||
// the following if() statement sets the runtime substep increment value or zeroes it
|
||||
if ((st_run.mot[MOTOR_1].substep_increment = st_pre.mot[MOTOR_1].substep_increment) != 0) {
|
||||
|
||||
// NB: If motor has 0 steps the following is all skipped. This ensures that state comparisons
|
||||
// always operate on the last segment actually run by this motor, regardless of how many
|
||||
// segments it may have been inactive in between.
|
||||
|
||||
// Apply accumulator correction if the time base has changed since previous segment
|
||||
#if NEW_DDA == 1
|
||||
#if defined(NEW_FWD_DIFF) && (NEW_FWD_DIFF==1)
|
||||
st_run.mot[MOTOR_1].substep_increment_increment = st_pre.mot[MOTOR_1].substep_increment_increment;
|
||||
#endif
|
||||
#else
|
||||
if (st_pre.mot[MOTOR_1].accumulator_correction_flag == true) {
|
||||
st_pre.mot[MOTOR_1].accumulator_correction_flag = false;
|
||||
@@ -560,6 +580,9 @@ static void _load_move()
|
||||
SET_ENCODER_STEP_SIGN(MOTOR_1, st_pre.mot[MOTOR_1].step_sign);
|
||||
|
||||
} else { // Motor has 0 steps; might need to energize motor for power mode processing
|
||||
#if defined(NEW_FWD_DIFF) && (NEW_FWD_DIFF==1)
|
||||
st_run.mot[MOTOR_1].substep_increment_increment = 0;
|
||||
#endif
|
||||
motor_1.motionStopped();
|
||||
}
|
||||
// accumulate counted steps to the step position and zero out counted steps for the segment currently being loaded
|
||||
@@ -568,6 +591,9 @@ static void _load_move()
|
||||
#if (MOTORS >= 2)
|
||||
if ((st_run.mot[MOTOR_2].substep_increment = st_pre.mot[MOTOR_2].substep_increment) != 0) {
|
||||
#if NEW_DDA == 1
|
||||
#if defined(NEW_FWD_DIFF) && (NEW_FWD_DIFF==1)
|
||||
st_run.mot[MOTOR_2].substep_increment_increment = st_pre.mot[MOTOR_2].substep_increment_increment;
|
||||
#endif
|
||||
#else
|
||||
if (st_pre.mot[MOTOR_2].accumulator_correction_flag == true) {
|
||||
st_pre.mot[MOTOR_2].accumulator_correction_flag = false;
|
||||
@@ -586,6 +612,9 @@ static void _load_move()
|
||||
motor_2.enable();
|
||||
SET_ENCODER_STEP_SIGN(MOTOR_2, st_pre.mot[MOTOR_2].step_sign);
|
||||
} else {
|
||||
#if defined(NEW_FWD_DIFF) && (NEW_FWD_DIFF==1)
|
||||
st_run.mot[MOTOR_2].substep_increment_increment = 0;
|
||||
#endif
|
||||
motor_2.motionStopped();
|
||||
}
|
||||
ACCUMULATE_ENCODER(MOTOR_2);
|
||||
@@ -593,6 +622,9 @@ static void _load_move()
|
||||
#if (MOTORS >= 3)
|
||||
if ((st_run.mot[MOTOR_3].substep_increment = st_pre.mot[MOTOR_3].substep_increment) != 0) {
|
||||
#if NEW_DDA == 1
|
||||
#if defined(NEW_FWD_DIFF) && (NEW_FWD_DIFF==1)
|
||||
st_run.mot[MOTOR_3].substep_increment_increment = st_pre.mot[MOTOR_3].substep_increment_increment;
|
||||
#endif
|
||||
#else
|
||||
if (st_pre.mot[MOTOR_3].accumulator_correction_flag == true) {
|
||||
st_pre.mot[MOTOR_3].accumulator_correction_flag = false;
|
||||
@@ -611,6 +643,9 @@ static void _load_move()
|
||||
motor_3.enable();
|
||||
SET_ENCODER_STEP_SIGN(MOTOR_3, st_pre.mot[MOTOR_3].step_sign);
|
||||
} else {
|
||||
#if defined(NEW_FWD_DIFF) && (NEW_FWD_DIFF==1)
|
||||
st_run.mot[MOTOR_3].substep_increment_increment = 0;
|
||||
#endif
|
||||
motor_3.motionStopped();
|
||||
}
|
||||
ACCUMULATE_ENCODER(MOTOR_3);
|
||||
@@ -618,6 +653,9 @@ static void _load_move()
|
||||
#if (MOTORS >= 4)
|
||||
if ((st_run.mot[MOTOR_4].substep_increment = st_pre.mot[MOTOR_4].substep_increment) != 0) {
|
||||
#if NEW_DDA == 1
|
||||
#if defined(NEW_FWD_DIFF) && (NEW_FWD_DIFF==1)
|
||||
st_run.mot[MOTOR_4].substep_increment_increment = st_pre.mot[MOTOR_4].substep_increment_increment;
|
||||
#endif
|
||||
#else
|
||||
if (st_pre.mot[MOTOR_4].accumulator_correction_flag == true) {
|
||||
st_pre.mot[MOTOR_4].accumulator_correction_flag = false;
|
||||
@@ -636,6 +674,9 @@ static void _load_move()
|
||||
motor_4.enable();
|
||||
SET_ENCODER_STEP_SIGN(MOTOR_4, st_pre.mot[MOTOR_4].step_sign);
|
||||
} else {
|
||||
#if defined(NEW_FWD_DIFF) && (NEW_FWD_DIFF==1)
|
||||
st_run.mot[MOTOR_4].substep_increment_increment = 0;
|
||||
#endif
|
||||
motor_4.motionStopped();
|
||||
}
|
||||
ACCUMULATE_ENCODER(MOTOR_4);
|
||||
@@ -643,6 +684,9 @@ static void _load_move()
|
||||
#if (MOTORS >= 5)
|
||||
if ((st_run.mot[MOTOR_5].substep_increment = st_pre.mot[MOTOR_5].substep_increment) != 0) {
|
||||
#if NEW_DDA == 1
|
||||
#if defined(NEW_FWD_DIFF) && (NEW_FWD_DIFF==1)
|
||||
st_run.mot[MOTOR_5].substep_increment_increment = st_pre.mot[MOTOR_5].substep_increment_increment;
|
||||
#endif
|
||||
#else
|
||||
if (st_pre.mot[MOTOR_5].accumulator_correction_flag == true) {
|
||||
st_pre.mot[MOTOR_5].accumulator_correction_flag = false;
|
||||
@@ -661,6 +705,9 @@ static void _load_move()
|
||||
motor_5.enable();
|
||||
SET_ENCODER_STEP_SIGN(MOTOR_5, st_pre.mot[MOTOR_5].step_sign);
|
||||
} else {
|
||||
#if defined(NEW_FWD_DIFF) && (NEW_FWD_DIFF==1)
|
||||
st_run.mot[MOTOR_5].substep_increment_increment = 0;
|
||||
#endif
|
||||
motor_5.motionStopped();
|
||||
}
|
||||
ACCUMULATE_ENCODER(MOTOR_5);
|
||||
@@ -668,6 +715,9 @@ static void _load_move()
|
||||
#if (MOTORS >= 6)
|
||||
if ((st_run.mot[MOTOR_6].substep_increment = st_pre.mot[MOTOR_6].substep_increment) != 0) {
|
||||
#if NEW_DDA == 1
|
||||
#if defined(NEW_FWD_DIFF) && (NEW_FWD_DIFF==1)
|
||||
st_run.mot[MOTOR_6].substep_increment_increment = st_pre.mot[MOTOR_6].substep_increment_increment;
|
||||
#endif
|
||||
#else
|
||||
if (st_pre.mot[MOTOR_6].accumulator_correction_flag == true) {
|
||||
st_pre.mot[MOTOR_6].accumulator_correction_flag = false;
|
||||
@@ -686,6 +736,9 @@ static void _load_move()
|
||||
motor_6.enable();
|
||||
SET_ENCODER_STEP_SIGN(MOTOR_6, st_pre.mot[MOTOR_6].step_sign);
|
||||
} else {
|
||||
#if defined(NEW_FWD_DIFF) && (NEW_FWD_DIFF==1)
|
||||
st_run.mot[MOTOR_6].substep_increment_increment = 0;
|
||||
#endif
|
||||
motor_6.motionStopped();
|
||||
}
|
||||
ACCUMULATE_ENCODER(MOTOR_6);
|
||||
@@ -738,7 +791,11 @@ static void _load_move()
|
||||
* dda_ticks_X_substeps = (int32_t)((microseconds/1000000) * f_dda * dda_substeps);
|
||||
*/
|
||||
|
||||
#if !defined(NEW_FWD_DIFF) || (NEW_FWD_DIFF==0)
|
||||
stat_t st_prep_line(float travel_steps[], float following_error[], float segment_time)
|
||||
#else
|
||||
stat_t st_prep_line(float start_velocity, float end_velocity, float travel_steps[], float following_error[], float segment_time)
|
||||
#endif
|
||||
{
|
||||
stepper_debug("😶");
|
||||
// trap assertion failures and other conditions that would prevent queuing the line
|
||||
@@ -763,6 +820,10 @@ stat_t st_prep_line(float travel_steps[], float following_error[], float segment
|
||||
#endif
|
||||
|
||||
// setup motor parameters
|
||||
#if defined(NEW_FWD_DIFF) && (NEW_FWD_DIFF==1)
|
||||
// this is explained later
|
||||
float t_v0_v1 = (float)st_pre.dda_ticks * (start_velocity + end_velocity);
|
||||
#endif
|
||||
|
||||
float correction_steps;
|
||||
for (uint8_t motor=0; motor<MOTORS; motor++) { // remind us that this is motors, not axes
|
||||
@@ -822,7 +883,56 @@ stat_t st_prep_line(float travel_steps[], float following_error[], float segment
|
||||
// that results in long-term negative drift. (fabs/round order doesn't matter)
|
||||
|
||||
#if NEW_DDA == 1
|
||||
#if !defined(NEW_FWD_DIFF) || (NEW_FWD_DIFF==0)
|
||||
// t = v n s
|
||||
// m = v n
|
||||
// t = m s
|
||||
// m = t/s
|
||||
// Needed is steps/tick
|
||||
// 1/m = s/t
|
||||
|
||||
st_pre.mot[motor].substep_increment = round(fabs(travel_steps[motor] / (float)st_pre.dda_ticks) * (float)DDA_SUBSTEPS);
|
||||
#else
|
||||
// t is ticks duration of the move
|
||||
// T is time duration of the move in minutes
|
||||
// f is dda frequency, ticks/sec
|
||||
// s is steps for the move
|
||||
// n is unknown scale factor
|
||||
// whatever the kinematics end up with to convert mm to steps for this motor and segment
|
||||
// v_0 and v_1 are the start and end velocity (in mm/min)
|
||||
//
|
||||
// t = T 60 f
|
||||
// Note: conversion from minutes to seconds cancels out in n
|
||||
// n = (s/(T 60))/(((v_0/60)+(v_1/60))/2) = (2 s)/(T(v_0 + v_1))
|
||||
//
|
||||
// Needed is steps/tick
|
||||
// 1/m_0 = (n (v_0/60))/f
|
||||
// 1/m_1 = (n (v_1/60))/f
|
||||
//
|
||||
// Substitute n:
|
||||
// 1/m_0 = ((2 s)/(T(v_0 + v_1)) (v_0/60))/f = (s v_0)/(T 30 f (v_0 + v_1)) = (2 s v_0)/(t (v_0 + v_1))
|
||||
// 1/m_1 = ((2 s)/(T(v_0 + v_1)) (v_1/60))/f = (s v_1)/(T 30 f (v_0 + v_1)) = (2 s v_1)/(t (v_0 + v_1))
|
||||
// d = (1/m_1-1/m_0)/(t-1) = (2 s (v_1 - v_0))/((t - 1) t (v_0 + v_1))
|
||||
// Some common terms:
|
||||
// a = t (v_0 + v_1)
|
||||
// b = 2 s
|
||||
// c = 1/m_0 = (b v_0)/a
|
||||
// option 1:
|
||||
// d = ((b v_1)/a - c)/(t-1)
|
||||
// option 2:
|
||||
// d = (b (v_1 - v_0))/((t-1) a)
|
||||
|
||||
float s_double = fabs(travel_steps[motor] * 2.0);
|
||||
|
||||
// 1/m_0 = (2 s v_0)/(t (v_0 + v_1))
|
||||
st_pre.mot[motor].substep_increment = round(((s_double * start_velocity)/(t_v0_v1)) * (float)DDA_SUBSTEPS);
|
||||
// option 1:
|
||||
// d = ((b v_1)/a - c)/(t-1)
|
||||
// option 2:
|
||||
// d = (b (v_1 - v_0))/((t-1) a)
|
||||
st_pre.mot[motor].substep_increment_increment = round(((s_double*(end_velocity-start_velocity))/(((float)st_pre.dda_ticks-1.0)*t_v0_v1)) * (float)DDA_SUBSTEPS);
|
||||
#warning Using new increment style!
|
||||
#endif
|
||||
#else
|
||||
st_pre.mot[motor].substep_increment = round(fabs(travel_steps[motor] * DDA_SUBSTEPS));
|
||||
#endif
|
||||
|
||||
@@ -372,7 +372,8 @@ typedef struct stConfig { // stepper configs
|
||||
// Motor runtime structure. Used exclusively by step generation ISR (HI)
|
||||
|
||||
typedef struct stRunMotor { // one per controlled motor
|
||||
uint32_t substep_increment; // total steps in axis times substeps factor
|
||||
uint32_t substep_increment; // partial steps to increment substep_accumulator per tick
|
||||
uint32_t substep_increment_increment; // partial steps to increment substep_increment per tick
|
||||
int32_t substep_accumulator; // DDA phase angle accumulator
|
||||
bool motor_flag; // true if motor is participating in this move
|
||||
uint32_t power_systick; // sys_tick for next motor power state transition
|
||||
@@ -396,7 +397,8 @@ typedef struct stRunSingleton { // Stepper static values and axis pa
|
||||
// Must be careful about volatiles in this one
|
||||
|
||||
typedef struct stPrepMotor {
|
||||
uint32_t substep_increment; // total steps in axis times substep factor
|
||||
uint32_t substep_increment; // partial steps to increment substep_accumulator per tick
|
||||
uint32_t substep_increment_increment; // partial steps to increment substep_increment per tick
|
||||
bool motor_flag; // true if motor is participating in this move
|
||||
|
||||
// direction and direction change
|
||||
@@ -492,7 +494,7 @@ struct Stepper {
|
||||
// turn on motor in all cases unless it's disabled
|
||||
// this version is called from the loader, and explicitly does NOT have floating point computations
|
||||
// HOT - called from the DDA interrupt
|
||||
void enable() HOT_FUNC
|
||||
void enable() //HOT_FUNC
|
||||
{
|
||||
if (_power_mode == MOTOR_DISABLED) {
|
||||
return;
|
||||
@@ -505,7 +507,7 @@ struct Stepper {
|
||||
|
||||
// turn on motor in all cases unless it's disabled
|
||||
// this version has the timeout computed here, as provided
|
||||
void enable(float timeout) HOT_FUNC
|
||||
void enable(float timeout) //HOT_FUNC
|
||||
{
|
||||
if (_power_mode == MOTOR_DISABLED) {
|
||||
return;
|
||||
@@ -523,7 +525,7 @@ struct Stepper {
|
||||
|
||||
// turn off motor in all cases unless it's permanently enabled
|
||||
// HOT - called from the DDA interrupt
|
||||
void disable() HOT_FUNC
|
||||
void disable() //HOT_FUNC
|
||||
{
|
||||
if (this->getPowerMode() == MOTOR_ALWAYS_POWERED) {
|
||||
return;
|
||||
@@ -535,7 +537,8 @@ struct Stepper {
|
||||
|
||||
// turn off motor is only powered when moving
|
||||
// HOT - called from the DDA interrupt
|
||||
void motionStopped() HOT_FUNC {
|
||||
void motionStopped() //HOT_FUNC
|
||||
{
|
||||
if (_power_mode == MOTOR_POWERED_IN_CYCLE) {
|
||||
this->enable();
|
||||
_power_state = MOTOR_POWER_TIMEOUT_START;
|
||||
@@ -607,7 +610,11 @@ void st_prep_null(void);
|
||||
void st_prep_command(void *bf); // use a void pointer since we don't know about mpBuf_t yet)
|
||||
void st_prep_dwell(float milliseconds);
|
||||
void st_request_out_of_band_dwell(float microseconds);
|
||||
#if !defined(NEW_FWD_DIFF) || (NEW_FWD_DIFF==0)
|
||||
stat_t st_prep_line(float travel_steps[], float following_error[], float segment_time) HOT_FUNC;
|
||||
#else
|
||||
stat_t st_prep_line(float start_velocity, float end_velocity, float travel_steps[], float following_error[], float segment_time) HOT_FUNC;
|
||||
#endif
|
||||
|
||||
stat_t st_set_ma(nvObj_t *nv);
|
||||
stat_t st_set_sa(nvObj_t *nv);
|
||||
|
||||
Reference in New Issue
Block a user