Browse Source

G6 Direct Stepping (#17853)

vanilla_fb_2.0.x
Colin Godsey 4 years ago
committed by GitHub
parent
commit
8a22ef0c83
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 24
      Marlin/Configuration_adv.h
  2. 24
      Marlin/src/HAL/AVR/MarlinSerial.cpp
  3. 11
      Marlin/src/MarlinCore.cpp
  4. 273
      Marlin/src/feature/direct_stepping.cpp
  5. 137
      Marlin/src/feature/direct_stepping.h
  6. 4
      Marlin/src/gcode/gcode.cpp
  7. 2
      Marlin/src/gcode/gcode.h
  8. 4
      Marlin/src/gcode/geometry/G92.cpp
  9. 61
      Marlin/src/gcode/motion/G6.cpp
  10. 12
      Marlin/src/inc/Conditionals_adv.h
  11. 9
      Marlin/src/inc/SanityCheck.h
  12. 3
      Marlin/src/lcd/language/language_en.h
  13. 86
      Marlin/src/module/planner.cpp
  14. 40
      Marlin/src/module/planner.h
  15. 213
      Marlin/src/module/stepper.cpp
  16. 15
      Marlin/src/module/stepper.h
  17. 5
      buildroot/share/tests/mega2560-tests

24
Marlin/Configuration_adv.h

@ -1663,6 +1663,16 @@
// Support for G5 with XYZE destination and IJPQ offsets. Requires ~2666 bytes.
//#define BEZIER_CURVE_SUPPORT
/**
* Direct Stepping
*
* Comparable to the method used by Klipper, G6 direct stepping significantly
* reduces motion calculations, increases top printing speeds, and results in
* less step aliasing by calculating all motions in advance.
* Preparing your G-code: https://github.com/colinrgodsey/step-daemon
*/
//#define DIRECT_STEPPING
/**
* G38 Probe Target
*
@ -1731,14 +1741,16 @@
//================================= Buffers =================================
//===========================================================================
// @section hidden
// @section motion
// The number of linear motions that can be in the plan at any give time.
// THE BLOCK_BUFFER_SIZE NEEDS TO BE A POWER OF 2 (e.g. 8, 16, 32) because shifts and ors are used to do the ring-buffering.
#if ENABLED(SDSUPPORT)
#define BLOCK_BUFFER_SIZE 16 // SD,LCD,Buttons take more memory, block buffer needs to be smaller
// The number of lineear moves that can be in the planner at once.
// The value of BLOCK_BUFFER_SIZE must be a power of 2 (e.g. 8, 16, 32)
#if BOTH(SDSUPPORT, DIRECT_STEPPING)
#define BLOCK_BUFFER_SIZE 8
#elif ENABLED(SDSUPPORT)
#define BLOCK_BUFFER_SIZE 16
#else
#define BLOCK_BUFFER_SIZE 16 // maximize block buffer
#define BLOCK_BUFFER_SIZE 16
#endif
// @section serial

24
Marlin/src/HAL/AVR/MarlinSerial.cpp

@ -43,6 +43,10 @@
#include "MarlinSerial.h"
#include "../../MarlinCore.h"
#if ENABLED(DIRECT_STEPPING)
#include "../../feature/direct_stepping.h"
#endif
template<typename Cfg> typename MarlinSerial<Cfg>::ring_buffer_r MarlinSerial<Cfg>::rx_buffer = { 0, 0, { 0 } };
template<typename Cfg> typename MarlinSerial<Cfg>::ring_buffer_t MarlinSerial<Cfg>::tx_buffer = { 0 };
template<typename Cfg> bool MarlinSerial<Cfg>::_written = false;
@ -131,6 +135,18 @@
static EmergencyParser::State emergency_state; // = EP_RESET
// This must read the R_UCSRA register before reading the received byte to detect error causes
if (Cfg::DROPPED_RX && B_DOR && !++rx_dropped_bytes) --rx_dropped_bytes;
if (Cfg::RX_OVERRUNS && B_DOR && !++rx_buffer_overruns) --rx_buffer_overruns;
if (Cfg::RX_FRAMING_ERRORS && B_FE && !++rx_framing_errors) --rx_framing_errors;
// Read the character from the USART
uint8_t c = R_UDR;
#if ENABLED(DIRECT_STEPPING)
if (page_manager.maybe_store_rxd_char(c)) return;
#endif
// Get the tail - Nothing can alter its value while this ISR is executing, but there's
// a chance that this ISR interrupted the main process while it was updating the index.
// The backup mechanism ensures the correct value is always returned.
@ -142,14 +158,6 @@
// Get the next element
ring_buffer_pos_t i = (ring_buffer_pos_t)(h + 1) & (ring_buffer_pos_t)(Cfg::RX_SIZE - 1);
// This must read the R_UCSRA register before reading the received byte to detect error causes
if (Cfg::DROPPED_RX && B_DOR && !++rx_dropped_bytes) --rx_dropped_bytes;
if (Cfg::RX_OVERRUNS && B_DOR && !++rx_buffer_overruns) --rx_buffer_overruns;
if (Cfg::RX_FRAMING_ERRORS && B_FE && !++rx_framing_errors) --rx_framing_errors;
// Read the character from the USART
uint8_t c = R_UDR;
if (Cfg::EMERGENCYPARSER) emergency_parser.update(emergency_state, c);
// If the character is to be stored at the index just before the tail

11
Marlin/src/MarlinCore.cpp

@ -59,6 +59,10 @@
#include "gcode/parser.h"
#include "gcode/queue.h"
#if ENABLED(DIRECT_STEPPING)
#include "feature/direct_stepping.h"
#endif
#if ENABLED(TOUCH_BUTTONS)
#include "feature/touch/xpt2046.h"
#endif
@ -713,6 +717,9 @@ void idle(TERN_(ADVANCED_PAUSE_FEATURE, bool no_stepper_sleep/*=false*/)) {
// Handle Joystick jogging
TERN_(POLL_JOG, joystick.inject_jog_moves());
// Direct Stepping
TERN_(DIRECT_STEPPING, page_manager.write_responses());
}
/**
@ -1124,6 +1131,10 @@ void setup() {
SETUP_RUN(max7219.init());
#endif
#if ENABLED(DIRECT_STEPPING)
SETUP_RUN(page_manager.init());
#endif
marlin_state = MF_RUNNING;
SETUP_LOG("setup() completed.");

273
Marlin/src/feature/direct_stepping.cpp

@ -0,0 +1,273 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../inc/MarlinConfigPre.h"
#if ENABLED(DIRECT_STEPPING)
#include "direct_stepping.h"
#include "../MarlinCore.h"
#define CHECK_PAGE(I, R) do{ \
if (I >= sizeof(page_states) / sizeof(page_states[0])) { \
fatal_error = true; \
return R; \
} \
}while(0)
#define CHECK_PAGE_STATE(I, R, S) do { \
CHECK_PAGE(I, R); \
if (page_states[I] != S) { \
fatal_error = true; \
return R; \
} \
}while(0)
namespace DirectStepping {
template<typename Cfg>
State SerialPageManager<Cfg>::state;
template<typename Cfg>
volatile bool SerialPageManager<Cfg>::fatal_error;
template<typename Cfg>
volatile PageState SerialPageManager<Cfg>::page_states[Cfg::NUM_PAGES];
template<typename Cfg>
volatile bool SerialPageManager<Cfg>::page_states_dirty;
template<typename Cfg>
millis_t SerialPageManager<Cfg>::next_response;
template<typename Cfg>
uint8_t SerialPageManager<Cfg>::pages[Cfg::NUM_PAGES][Cfg::PAGE_SIZE];
template<typename Cfg>
uint8_t SerialPageManager<Cfg>::checksum;
template<typename Cfg>
typename Cfg::write_byte_idx_t SerialPageManager<Cfg>::write_byte_idx;
template<typename Cfg>
typename Cfg::page_idx_t SerialPageManager<Cfg>::write_page_idx;
template<typename Cfg>
typename Cfg::write_byte_idx_t SerialPageManager<Cfg>::write_page_size;
template <typename Cfg>
void SerialPageManager<Cfg>::init() {
for (int i = 0 ; i < Cfg::NUM_PAGES ; i++)
page_states[i] = PageState::FREE;
fatal_error = false;
next_response = 0;
state = State::NEWLINE;
page_states_dirty = false;
SERIAL_ECHOLNPGM("pages_ready");
}
template<typename Cfg>
FORCE_INLINE bool SerialPageManager<Cfg>::maybe_store_rxd_char(uint8_t c) {
switch (state) {
default:
case State::MONITOR:
switch (c) {
case '\n':
case '\r':
state = State::NEWLINE;
default:
return false;
}
case State::NEWLINE:
switch (c) {
case Cfg::CONTROL_CHAR:
state = State::ADDRESS;
return true;
case '\n':
case '\r':
state = State::NEWLINE;
return false;
default:
state = State::MONITOR;
return false;
}
case State::ADDRESS:
//TODO: 16 bit address, State::ADDRESS2
write_page_idx = c;
write_byte_idx = 0;
checksum = 0;
CHECK_PAGE(write_page_idx, true);
if (page_states[write_page_idx] == PageState::FAIL) {
// Special case for fail
state = State::UNFAIL;
return true;
}
set_page_state(write_page_idx, PageState::WRITING);
state = Cfg::DIRECTIONAL ? State::COLLECT : State::SIZE;
return true;
case State::SIZE:
// Zero means full page size
write_page_size = c;
state = State::COLLECT;
return true;
case State::COLLECT:
pages[write_page_idx][write_byte_idx++] = c;
checksum ^= c;
// check if still collecting
if (Cfg::PAGE_SIZE == 256) {
// special case for 8-bit, check if rolled back to 0
if (Cfg::DIRECTIONAL || !write_page_size) { // full 256 bytes
if (write_byte_idx) return true;
} else {
if (write_byte_idx < write_page_size) return true;
}
} else if (Cfg::DIRECTIONAL) {
if (write_byte_idx != Cfg::PAGE_SIZE) return true;
} else {
if (write_byte_idx < write_page_size) return true;
}
state = State::CHECKSUM;
return true;
case State::CHECKSUM: {
const PageState page_state = (checksum == c) ? PageState::OK : PageState::FAIL;
set_page_state(write_page_idx, page_state);
state = State::MONITOR;
return true;
}
case State::UNFAIL:
if (c == 0) {
set_page_state(write_page_idx, PageState::FREE);
} else {
fatal_error = true;
}
state = State::MONITOR;
return true;
}
}
template <typename Cfg>
void SerialPageManager<Cfg>::write_responses() {
if (fatal_error) {
kill(GET_TEXT(MSG_BAD_PAGE));
return;
}
// Runs on a set interval also, as responses may get lost.
if (next_response && next_response < millis()) {
page_states_dirty = true;
}
if (!page_states_dirty) return;
page_states_dirty = false;
next_response = millis() + Cfg::RESPONSE_INTERVAL_MS;
SERIAL_ECHO(Cfg::CONTROL_CHAR);
constexpr int state_bits = 2;
constexpr int n_bytes = Cfg::NUM_PAGES >> state_bits;
volatile uint8_t bits_b[n_bytes] = { 0 };
for (page_idx_t i = 0 ; i < Cfg::NUM_PAGES ; i++) {
bits_b[i >> state_bits] |= page_states[i] << ((i * state_bits) & 0x7);
}
uint8_t crc = 0;
for (uint8_t i = 0 ; i < n_bytes ; i++) {
crc ^= bits_b[i];
SERIAL_ECHO(bits_b[i]);
}
SERIAL_ECHO(crc);
SERIAL_EOL();
}
template <typename Cfg>
FORCE_INLINE void SerialPageManager<Cfg>::set_page_state(const page_idx_t page_idx, const PageState page_state) {
CHECK_PAGE(page_idx,);
page_states[page_idx] = page_state;
page_states_dirty = true;
}
template <>
FORCE_INLINE uint8_t *PageManager::get_page(const page_idx_t page_idx) {
CHECK_PAGE(page_idx, nullptr);
return pages[page_idx];
}
template <>
FORCE_INLINE void PageManager::free_page(const page_idx_t page_idx) {
set_page_state(page_idx, PageState::FREE);
}
};
DirectStepping::PageManager page_manager;
const uint8_t segment_table[DirectStepping::Config::NUM_SEGMENTS][DirectStepping::Config::SEGMENT_STEPS] PROGMEM = {
#if STEPPER_PAGE_FORMAT == SP_4x4D_128
{ 1, 1, 1, 1, 1, 1, 1, 0 }, // 0 = -7
{ 1, 1, 1, 0, 1, 1, 1, 0 }, // 1 = -6
{ 0, 1, 1, 0, 1, 0, 1, 1 }, // 2 = -5
{ 0, 1, 0, 1, 0, 1, 0, 1 }, // 3 = -4
{ 0, 1, 0, 0, 1, 0, 0, 1 }, // 4 = -3
{ 0, 0, 1, 0, 0, 0, 1, 0 }, // 5 = -2
{ 0, 0, 0, 0, 1, 0, 0, 0 }, // 6 = -1
{ 0, 0, 0, 0, 0, 0, 0, 0 }, // 7 = 0
{ 0, 0, 0, 0, 1, 0, 0, 0 }, // 8 = 1
{ 0, 0, 1, 0, 0, 0, 1, 0 }, // 9 = 2
{ 0, 1, 0, 0, 1, 0, 0, 1 }, // 10 = 3
{ 0, 1, 0, 1, 0, 1, 0, 1 }, // 11 = 4
{ 0, 1, 1, 0, 1, 0, 1, 1 }, // 12 = 5
{ 1, 1, 1, 0, 1, 1, 1, 0 }, // 13 = 6
{ 1, 1, 1, 1, 1, 1, 1, 0 }, // 14 = 7
{ 0 }
#elif STEPPER_PAGE_FORMAT == SP_4x2_256
{ 0, 0, 0, 0 }, // 0
{ 0, 1, 0, 0 }, // 1
{ 1, 0, 1, 0 }, // 2
{ 1, 1, 1, 0 }, // 3
#elif STEPPER_PAGE_FORMAT == SP_4x1_512
{0} // Uncompressed format, table not used
#endif
};
#endif // DIRECT_STEPPING

137
Marlin/src/feature/direct_stepping.h

@ -0,0 +1,137 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
#include "../inc/MarlinConfig.h"
namespace DirectStepping {
enum State : char {
MONITOR, NEWLINE, ADDRESS, SIZE, COLLECT, CHECKSUM, UNFAIL
};
enum PageState : uint8_t {
FREE, WRITING, OK, FAIL
};
// Static state used for stepping through direct stepping pages
struct page_step_state_t {
// Current page
uint8_t *page;
// Current segment
uint16_t segment_idx;
// Current steps within segment
uint8_t segment_steps;
// Segment delta
xyze_uint8_t sd;
// Block delta
xyze_int_t bd;
};
template<typename Cfg>
class SerialPageManager {
public:
typedef typename Cfg::page_idx_t page_idx_t;
static bool maybe_store_rxd_char(uint8_t c);
static void write_responses();
// common methods for page managers
static void init();
static uint8_t *get_page(const page_idx_t page_idx);
static void free_page(const page_idx_t page_idx);
protected:
typedef typename Cfg::write_byte_idx_t write_byte_idx_t;
static State state;
static volatile bool fatal_error;
static volatile PageState page_states[Cfg::NUM_PAGES];
static volatile bool page_states_dirty;
static millis_t next_response;
static uint8_t pages[Cfg::NUM_PAGES][Cfg::PAGE_SIZE];
static uint8_t checksum;
static write_byte_idx_t write_byte_idx;
static page_idx_t write_page_idx;
static write_byte_idx_t write_page_size;
static void set_page_state(const page_idx_t page_idx, const PageState page_state);
};
template<bool b, typename T, typename F> struct TypeSelector { typedef T type;} ;
template<typename T, typename F> struct TypeSelector<false, T, F> { typedef F type; };
template <int num_pages, int num_axes, int bits_segment, bool dir, int segments>
struct config_t {
static constexpr char CONTROL_CHAR = '!';
static constexpr int NUM_PAGES = num_pages;
static constexpr int NUM_AXES = num_axes;
static constexpr int BITS_SEGMENT = bits_segment;
static constexpr int DIRECTIONAL = dir ? 1 : 0;
static constexpr int SEGMENTS = segments;
static constexpr int RAW = (BITS_SEGMENT == 1) ? 1 : 0;
static constexpr int NUM_SEGMENTS = 1 << BITS_SEGMENT;
static constexpr int SEGMENT_STEPS = 1 << (BITS_SEGMENT - DIRECTIONAL - RAW);
static constexpr int TOTAL_STEPS = SEGMENT_STEPS * SEGMENTS;
static constexpr int PAGE_SIZE = (NUM_AXES * BITS_SEGMENT * SEGMENTS) / 8;
static constexpr millis_t RESPONSE_INTERVAL_MS = 50;
typedef typename TypeSelector<(PAGE_SIZE>256), uint16_t, uint8_t>::type write_byte_idx_t;
typedef typename TypeSelector<(NUM_PAGES>256), uint16_t, uint8_t>::type page_idx_t;
};
template <uint8_t num_pages>
using SP_4x4D_128 = config_t<num_pages, 4, 4, true, 128>;
template <uint8_t num_pages>
using SP_4x2_256 = config_t<num_pages, 4, 2, false, 256>;
template <uint8_t num_pages>
using SP_4x1_512 = config_t<num_pages, 4, 1, false, 512>;
// configured types
typedef STEPPER_PAGE_FORMAT<STEPPER_PAGES> Config;
template class PAGE_MANAGER<Config>;
typedef PAGE_MANAGER<Config> PageManager;
};
#define SP_4x4D_128 1
//#define SP_4x4_128 2
//#define SP_4x2D_256 3
#define SP_4x2_256 4
#define SP_4x1_512 5
typedef typename DirectStepping::Config::page_idx_t page_idx_t;
// TODO: use config
typedef DirectStepping::page_step_state_t page_step_state_t;
extern const uint8_t segment_table[DirectStepping::Config::NUM_SEGMENTS][DirectStepping::Config::SEGMENT_STEPS];
extern DirectStepping::PageManager page_manager;

4
Marlin/src/gcode/gcode.cpp

@ -261,6 +261,10 @@ void GcodeSuite::process_parsed_command(const bool no_ok/*=false*/) {
case 5: G5(); break; // G5: Cubic B_spline
#endif
#if ENABLED(DIRECT_STEPPING)
case 6: G6(); break; // G6: Direct Stepper Move
#endif
#if ENABLED(FWRETRACT)
case 10: G10(); break; // G10: Retract / Swap Retract
case 11: G11(); break; // G11: Recover / Swap Recover

2
Marlin/src/gcode/gcode.h

@ -402,6 +402,8 @@ private:
TERN_(BEZIER_CURVE_SUPPORT, static void G5());
TERN_(DIRECT_STEPPING, static void G6());
#if ENABLED(FWRETRACT)
static void G10();
static void G11();

4
Marlin/src/gcode/geometry/G92.cpp

@ -99,5 +99,7 @@ void GcodeSuite::G92() {
if (sync_XYZ) sync_plan_position();
else if (sync_E) sync_plan_position_e();
report_current_position();
#if DISABLED(DIRECT_STEPPING)
report_current_position();
#endif
}

61
Marlin/src/gcode/motion/G6.cpp

@ -0,0 +1,61 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "../../inc/MarlinConfig.h"
#if ENABLED(DIRECT_STEPPING)
#include "../../feature/direct_stepping.h"
#include "../gcode.h"
#include "../../module/planner.h"
/**
* G6: Direct Stepper Move
*/
void GcodeSuite::G6() {
// TODO: feedrate support?
if (parser.seen('R'))
planner.last_page_step_rate = parser.value_ulong();
if (!DirectStepping::Config::DIRECTIONAL) {
if (parser.seen('X')) planner.last_page_dir.x = !!parser.value_byte();
if (parser.seen('Y')) planner.last_page_dir.y = !!parser.value_byte();
if (parser.seen('Z')) planner.last_page_dir.z = !!parser.value_byte();
if (parser.seen('E')) planner.last_page_dir.e = !!parser.value_byte();
}
// No index means we just set the state
if (!parser.seen('I')) return;
// No speed is set, can't schedule the move
if (!planner.last_page_step_rate) return;
const page_idx_t page_idx = (page_idx_t) parser.value_ulong();
uint16_t num_steps = DirectStepping::Config::TOTAL_STEPS;
if (parser.seen('S')) num_steps = parser.value_ushort();
planner.buffer_page(page_idx, 0, num_steps);
reset_stepper_timeout();
}
#endif // DIRECT_STEPPING

12
Marlin/src/inc/Conditionals_adv.h

@ -323,6 +323,18 @@
#endif
#endif
#if ENABLED(DIRECT_STEPPING)
#ifndef STEPPER_PAGES
#define STEPPER_PAGES 16
#endif
#ifndef STEPPER_PAGE_FORMAT
#define STEPPER_PAGE_FORMAT SP_4x2_256
#endif
#ifndef PAGE_MANAGER
#define PAGE_MANAGER SerialPageManager
#endif
#endif
//
// SD Card connection methods
// Defined here so pins and sanity checks can use them

9
Marlin/src/inc/SanityCheck.h

@ -2923,3 +2923,12 @@ static_assert( _ARR_TEST(3,0) && _ARR_TEST(3,1) && _ARR_TEST(3,2)
#if SAVED_POSITIONS > 256
#error "SAVED_POSITIONS must be an integer from 0 to 256."
#endif
/**
* Sanity checks for stepper chunk support
*/
#if ENABLED(DIRECT_STEPPING)
#if ENABLED(LIN_ADVANCE)
#error "DIRECT_STEPPING is incompatible with LIN_ADVANCE. Enable in external planner if possible."
#endif
#endif

3
Marlin/src/lcd/language/language_en.h

@ -577,6 +577,9 @@ namespace Language_en {
PROGMEM Language_Str MSG_SNAKE = _UxGT("Sn4k3");
PROGMEM Language_Str MSG_MAZE = _UxGT("Maze");
PROGMEM Language_Str MSG_BAD_PAGE = _UxGT("Bad page index");
PROGMEM Language_Str MSG_BAD_PAGE_SPEED = _UxGT("Bad page speed");
//
// Filament Change screens show up to 3 lines on a 4-line display
// ...or up to 2 lines on a 3-line display

86
Marlin/src/module/planner.cpp

@ -151,6 +151,11 @@ float Planner::steps_to_mm[XYZE_N]; // (mm) Millimeters per step
uint8_t Planner::last_extruder = 0; // Respond to extruder change
#endif
#if ENABLED(DIRECT_STEPPING)
uint32_t Planner::last_page_step_rate = 0;
xyze_bool_t Planner::last_page_dir{0};
#endif
#if EXTRUDERS
int16_t Planner::flow_percentage[EXTRUDERS] = ARRAY_BY_EXTRUDERS1(100); // Extrusion factor for each extruder
float Planner::e_factor[EXTRUDERS] = ARRAY_BY_EXTRUDERS1(1.0f); // The flow percentage and volumetric multiplier combine to scale E movement
@ -235,6 +240,10 @@ void Planner::init() {
TERN_(ABL_PLANAR, bed_level_matrix.set_to_identity());
clear_block_buffer();
delay_before_delivering = 0;
#if ENABLED(DIRECT_STEPPING)
last_page_step_rate = 0;
last_page_dir.reset();
#endif
}
#if ENABLED(S_CURVE_ACCELERATION)
@ -906,7 +915,7 @@ void Planner::calculate_trapezoid_for_block(block_t* const block, const float &e
streaming operating conditions. Use for planning optimizations by avoiding recomputing parts of the
planner buffer that don't change with the addition of a new block, as describe above. In addition,
this block can never be less than block_buffer_tail and will always be pushed forward and maintain
this requirement when encountered by the Planner::discard_current_block() routine during a cycle.
this requirement when encountered by the Planner::release_current_block() routine during a cycle.
NOTE: Since the planner only computes on what's in the planner buffer, some motions with lots of short
line segments, like G2/3 arcs or complex curves, may seem to move slow. This is because there simply isn't
@ -994,8 +1003,8 @@ void Planner::reverse_pass() {
// Perform the reverse pass
block_t *current = &block_buffer[block_index];
// Only consider non sync blocks
if (!TEST(current->flag, BLOCK_BIT_SYNC_POSITION)) {
// Only consider non sync and page blocks
if (!TEST(current->flag, BLOCK_BIT_SYNC_POSITION) && !IS_PAGE(current)) {
reverse_pass_kernel(current, next);
next = current;
}
@ -1089,8 +1098,8 @@ void Planner::forward_pass() {
// Perform the forward pass
block = &block_buffer[block_index];
// Skip SYNC blocks
if (!TEST(block->flag, BLOCK_BIT_SYNC_POSITION)) {
// Skip SYNC and page blocks
if (!TEST(block->flag, BLOCK_BIT_SYNC_POSITION) && !IS_PAGE(block)) {
// If there's no previous block or the previous block is not
// BUSY (thus, modifiable) run the forward_pass_kernel. Otherwise,
// the previous block became BUSY, so assume the current block's
@ -1139,8 +1148,8 @@ void Planner::recalculate_trapezoids() {
next = &block_buffer[block_index];
// Skip sync blocks
if (!TEST(next->flag, BLOCK_BIT_SYNC_POSITION)) {
// Skip sync and page blocks
if (!TEST(next->flag, BLOCK_BIT_SYNC_POSITION) && !IS_PAGE(next)) {
next_entry_speed = SQRT(next->entry_speed_sqr);
if (block) {
@ -2717,6 +2726,69 @@ bool Planner::buffer_line(const float &rx, const float &ry, const float &rz, con
#endif
} // buffer_line()
#if ENABLED(DIRECT_STEPPING)
void Planner::buffer_page(const page_idx_t page_idx, const uint8_t extruder, const uint16_t num_steps) {
if (!last_page_step_rate) {
kill(GET_TEXT(MSG_BAD_PAGE_SPEED));
return;
}
uint8_t next_buffer_head;
block_t * const block = get_next_free_block(next_buffer_head);
block->flag = BLOCK_FLAG_IS_PAGE;
#if FAN_COUNT > 0
FANS_LOOP(i) block->fan_speed[i] = thermalManager.fan_speed[i];
#endif
#if EXTRUDERS > 1
block->extruder = extruder;
#endif
block->page_idx = page_idx;
block->step_event_count = num_steps;
block->initial_rate =
block->final_rate =
block->nominal_rate = last_page_step_rate; // steps/s
block->accelerate_until = 0;
block->decelerate_after = block->step_event_count;
// Will be set to last direction later if directional format.
block->direction_bits = 0;
#define PAGE_UPDATE_DIR(AXIS) \
if (!last_page_dir[_AXIS(AXIS)]) SBI(block->direction_bits, _AXIS(AXIS));
if (!DirectStepping::Config::DIRECTIONAL) {
PAGE_UPDATE_DIR(X);
PAGE_UPDATE_DIR(Y);
PAGE_UPDATE_DIR(Z);
PAGE_UPDATE_DIR(E);
}
// If this is the first added movement, reload the delay, otherwise, cancel it.
if (block_buffer_head == block_buffer_tail) {
// If it was the first queued block, restart the 1st block delivery delay, to
// give the planner an opportunity to queue more movements and plan them
// As there are no queued movements, the Stepper ISR will not touch this
// variable, so there is no risk setting this here (but it MUST be done
// before the following line!!)
delay_before_delivering = BLOCK_DELAY_FOR_1ST_MOVE;
}
// Move buffer head
block_buffer_head = next_buffer_head;
enable_all_steppers();
stepper.wake_up();
}
#endif // DIRECT_STEPPING
/**
* Directly set the planner ABC position (and stepper positions)
* converting mm (or angles for SCARA) into steps.

40
Marlin/src/module/planner.h

@ -66,6 +66,13 @@
#include "../feature/spindle_laser_types.h"
#endif
#if ENABLED(DIRECT_STEPPING)
#include "../feature/direct_stepping.h"
#define IS_PAGE(B) TEST(B->flag, BLOCK_BIT_IS_PAGE)
#else
#define IS_PAGE(B) false
#endif
// Feedrate for manual moves
#ifdef MANUAL_FEEDRATE
constexpr xyze_feedrate_t _mf = MANUAL_FEEDRATE,
@ -90,13 +97,21 @@ enum BlockFlagBit : char {
// Sync the stepper counts from the block
BLOCK_BIT_SYNC_POSITION
// Direct stepping page
#if ENABLED(DIRECT_STEPPING)
, BLOCK_BIT_IS_PAGE
#endif
};
enum BlockFlag : char {
BLOCK_FLAG_RECALCULATE = _BV(BLOCK_BIT_RECALCULATE),
BLOCK_FLAG_NOMINAL_LENGTH = _BV(BLOCK_BIT_NOMINAL_LENGTH),
BLOCK_FLAG_CONTINUED = _BV(BLOCK_BIT_CONTINUED),
BLOCK_FLAG_SYNC_POSITION = _BV(BLOCK_BIT_SYNC_POSITION)
BLOCK_FLAG_RECALCULATE = _BV(BLOCK_BIT_RECALCULATE)
, BLOCK_FLAG_NOMINAL_LENGTH = _BV(BLOCK_BIT_NOMINAL_LENGTH)
, BLOCK_FLAG_CONTINUED = _BV(BLOCK_BIT_CONTINUED)
, BLOCK_FLAG_SYNC_POSITION = _BV(BLOCK_BIT_SYNC_POSITION)
#if ENABLED(DIRECT_STEPPING)
, BLOCK_FLAG_IS_PAGE = _BV(BLOCK_BIT_IS_PAGE)
#endif
};
#if ENABLED(LASER_POWER_INLINE)
@ -180,6 +195,10 @@ typedef struct block_t {
final_rate, // The minimal rate at exit
acceleration_steps_per_s2; // acceleration steps/sec^2
#if ENABLED(DIRECT_STEPPING)
page_idx_t page_idx; // Page index used for direct stepping
#endif
#if HAS_CUTTER
cutter_power_t cutter_power; // Power level for Spindle, Laser, etc.
#endif
@ -296,6 +315,11 @@ class Planner {
static uint8_t last_extruder; // Respond to extruder change
#endif
#if ENABLED(DIRECT_STEPPING)
static uint32_t last_page_step_rate; // Last page step rate given
static xyze_bool_t last_page_dir; // Last page direction given
#endif
#if EXTRUDERS
static int16_t flow_percentage[EXTRUDERS]; // Extrusion factor for each extruder
static float e_factor[EXTRUDERS]; // The flow percentage and volumetric multiplier combine to scale E movement
@ -726,6 +750,10 @@ class Planner {
);
}
#if ENABLED(DIRECT_STEPPING)
static void buffer_page(const page_idx_t page_idx, const uint8_t extruder, const uint16_t num_steps);
#endif
/**
* Set the planner.position and individual stepper positions.
* Used by G92, G28, G29, and other procedures.
@ -811,10 +839,10 @@ class Planner {
static block_t* get_current_block();
/**
* "Discard" the block and "release" the memory.
* "Release" the current block so its slot can be reused.
* Called when the current block is no longer needed.
*/
FORCE_INLINE static void discard_current_block() {
FORCE_INLINE static void release_current_block() {
if (has_blocks_queued())
block_buffer_tail = next_block_index(block_buffer_tail);
}

213
Marlin/src/module/stepper.cpp

@ -230,6 +230,10 @@ uint32_t Stepper::advance_divisor = 0,
uint32_t Stepper::nextBabystepISR = BABYSTEP_NEVER;
#endif
#if ENABLED(DIRECT_STEPPING)
page_step_state_t Stepper::page_step_state;
#endif
int32_t Stepper::ticks_nominal = -1;
#if DISABLED(S_CURVE_ACCELERATION)
uint32_t Stepper::acc_step_rate; // needed for deceleration start point
@ -1520,11 +1524,7 @@ void Stepper::pulse_phase_isr() {
// If we must abort the current block, do so!
if (abort_current_block) {
abort_current_block = false;
if (current_block) {
axis_did_move = 0;
current_block = nullptr;
planner.discard_current_block();
}
if (current_block) discard_current_block();
}
// If there is no current block, do nothing
@ -1558,46 +1558,160 @@ void Stepper::pulse_phase_isr() {
} \
}while(0)
// Start an active pulse, if Bresenham says so, and update position
// Start an active pulse if needed
#define PULSE_START(AXIS) do{ \
if (step_needed[_AXIS(AXIS)]) { \
_APPLY_STEP(AXIS, !_INVERT_STEP_PIN(AXIS), 0); \
} \
}while(0)
// Stop an active pulse, if any, and adjust error term
// Stop an active pulse if needed
#define PULSE_STOP(AXIS) do { \
if (step_needed[_AXIS(AXIS)]) { \
_APPLY_STEP(AXIS, _INVERT_STEP_PIN(AXIS), 0); \
} \
}while(0)
// Determine if pulses are needed
#if HAS_X_STEP
PULSE_PREP(X);
#endif
#if HAS_Y_STEP
PULSE_PREP(Y);
#endif
#if HAS_Z_STEP
PULSE_PREP(Z);
#endif
// Direct Stepping page?
const bool is_page = IS_PAGE(current_block);
#if ENABLED(DIRECT_STEPPING)
if (is_page) {
#if STEPPER_PAGE_FORMAT == SP_4x4D_128
#define PAGE_SEGMENT_UPDATE(AXIS, VALUE, MID) do{ \
if ((VALUE) == MID) {} \
else if ((VALUE) < MID) SBI(dm, _AXIS(AXIS)); \
else CBI(dm, _AXIS(AXIS)); \
page_step_state.sd[_AXIS(AXIS)] = VALUE; \
page_step_state.bd[_AXIS(AXIS)] += VALUE; \
}while(0)
#define PAGE_PULSE_PREP(AXIS) do{ \
step_needed[_AXIS(AXIS)] = \
pgm_read_byte(&segment_table[page_step_state.sd[_AXIS(AXIS)]][page_step_state.segment_steps & 0x7]); \
}while(0)
switch (page_step_state.segment_steps) {
case 8:
page_step_state.segment_idx += 2;
page_step_state.segment_steps = 0;
// fallthru
case 0: {
const uint8_t low = page_step_state.page[page_step_state.segment_idx],
high = page_step_state.page[page_step_state.segment_idx + 1];
uint8_t dm = last_direction_bits;
PAGE_SEGMENT_UPDATE(X, low >> 4, 7);
PAGE_SEGMENT_UPDATE(Y, low & 0xF, 7);
PAGE_SEGMENT_UPDATE(Z, high >> 4, 7);
PAGE_SEGMENT_UPDATE(E, high & 0xF, 7);
if (dm != last_direction_bits) {
last_direction_bits = dm;
set_directions();
}
} break;
default: break;
}
PAGE_PULSE_PREP(X),
PAGE_PULSE_PREP(Y),
PAGE_PULSE_PREP(Z),
PAGE_PULSE_PREP(E);
page_step_state.segment_steps++;
#elif STEPPER_PAGE_FORMAT == SP_4x2_256
#define PAGE_SEGMENT_UPDATE(AXIS, VALUE) \
page_step_state.sd[_AXIS(AXIS)] = VALUE; \
page_step_state.bd[_AXIS(AXIS)] += VALUE;
#define PAGE_PULSE_PREP(AXIS) do{ \
step_needed[_AXIS(AXIS)] = \
pgm_read_byte(&segment_table[page_step_state.sd[_AXIS(AXIS)]][page_step_state.segment_steps & 0x3]); \
}while(0)
switch (page_step_state.segment_steps) {
case 4:
page_step_state.segment_idx++;
page_step_state.segment_steps = 0;
// fallthru
case 0: {
const uint8_t b = page_step_state.page[page_step_state.segment_idx];
PAGE_SEGMENT_UPDATE(X, (b >> 6) & 0x3);
PAGE_SEGMENT_UPDATE(Y, (b >> 4) & 0x3);
PAGE_SEGMENT_UPDATE(Z, (b >> 2) & 0x3);
PAGE_SEGMENT_UPDATE(E, (b >> 0) & 0x3);
} break;
default: break;
}
PAGE_PULSE_PREP(X);
PAGE_PULSE_PREP(Y);
PAGE_PULSE_PREP(Z);
PAGE_PULSE_PREP(E);
page_step_state.segment_steps++;
#elif STEPPER_PAGE_FORMAT == SP_4x1_512
#define PAGE_PULSE_PREP(AXIS, BITS) do{ \
step_needed[_AXIS(AXIS)] = (steps >> BITS) & 0x1; \
if (step_needed[_AXIS(AXIS)]) \
page_step_state.bd[_AXIS(AXIS)]++; \
}while(0)
uint8_t steps = page_step_state.page[page_step_state.segment_idx >> 1];
if (page_step_state.segment_idx & 0x1) steps >>= 4;
PAGE_PULSE_PREP(X, 3);
PAGE_PULSE_PREP(Y, 2);
PAGE_PULSE_PREP(Z, 1);
PAGE_PULSE_PREP(E, 0);
page_step_state.segment_idx++;
#if EITHER(LIN_ADVANCE, MIXING_EXTRUDER)
delta_error.e += advance_dividend.e;
if (delta_error.e >= 0) {
count_position.e += count_direction.e;
#if ENABLED(LIN_ADVANCE)
delta_error.e -= advance_divisor;
// Don't step E here - But remember the number of steps to perform
motor_direction(E_AXIS) ? --LA_steps : ++LA_steps;
#else
step_needed.e = true;
#error "Unknown direct stepping page format!"
#endif
}
#elif HAS_E0_STEP
PULSE_PREP(E);
#endif
#endif // DIRECT_STEPPING
if (!is_page) {
// Determine if pulses are needed
#if HAS_X_STEP
PULSE_PREP(X);
#endif
#if HAS_Y_STEP
PULSE_PREP(Y);
#endif
#if HAS_Z_STEP
PULSE_PREP(Z);
#endif
#if EITHER(LIN_ADVANCE, MIXING_EXTRUDER)
delta_error.e += advance_dividend.e;
if (delta_error.e >= 0) {
count_position.e += count_direction.e;
#if ENABLED(LIN_ADVANCE)
delta_error.e -= advance_divisor;
// Don't step E here - But remember the number of steps to perform
motor_direction(E_AXIS) ? --LA_steps : ++LA_steps;
#else
step_needed.e = true;
#endif
}
#elif HAS_E0_STEP
PULSE_PREP(E);
#endif
}
#if ISR_MULTI_STEPS
if (firstStep)
@ -1676,14 +1790,28 @@ uint32_t Stepper::block_phase_isr() {
// If there is a current block
if (current_block) {
// If current block is finished, reset pointer
// If current block is finished, reset pointer and finalize state
if (step_events_completed >= step_event_count) {
#if ENABLED(DIRECT_STEPPING)
#if STEPPER_PAGE_FORMAT == SP_4x4D_128
#define PAGE_SEGMENT_UPDATE_POS(AXIS) \
count_position[_AXIS(AXIS)] += page_step_state.bd[_AXIS(AXIS)] - 128 * 7;
#elif STEPPER_PAGE_FORMAT == SP_4x1_512 || STEPPER_PAGE_FORMAT == SP_4x2_256
#define PAGE_SEGMENT_UPDATE_POS(AXIS) \
count_position[_AXIS(AXIS)] += page_step_state.bd[_AXIS(AXIS)] * count_direction[_AXIS(AXIS)];
#endif
if (IS_PAGE(current_block)) {
PAGE_SEGMENT_UPDATE_POS(X);
PAGE_SEGMENT_UPDATE_POS(Y);
PAGE_SEGMENT_UPDATE_POS(Z);
PAGE_SEGMENT_UPDATE_POS(E);
}
#endif
#ifdef FILAMENT_RUNOUT_DISTANCE_MM
runout.block_completed(current_block);
#endif
axis_did_move = 0;
current_block = nullptr;
planner.discard_current_block();
discard_current_block();
}
else {
// Step events not completed yet...
@ -1867,7 +1995,7 @@ uint32_t Stepper::block_phase_isr() {
// Sync block? Sync the stepper counts and return
while (TEST(current_block->flag, BLOCK_BIT_SYNC_POSITION)) {
_set_position(current_block->position);
planner.discard_current_block();
discard_current_block();
// Try to get a new block
if (!(current_block = planner.get_current_block()))
@ -1878,6 +2006,23 @@ uint32_t Stepper::block_phase_isr() {
TERN_(POWER_LOSS_RECOVERY, recovery.info.sdpos = current_block->sdpos);
#if ENABLED(DIRECT_STEPPING)
if (IS_PAGE(current_block)) {
page_step_state.segment_steps = 0;
page_step_state.segment_idx = 0;
page_step_state.page = page_manager.get_page(current_block->page_idx);
page_step_state.bd.reset();
if (DirectStepping::Config::DIRECTIONAL)
current_block->direction_bits = last_direction_bits;
if (!page_step_state.page) {
discard_current_block();
return interval;
}
}
#endif
// Flag all moving axes for proper endstop handling
#if IS_CORE

15
Marlin/src/module/stepper.h

@ -334,6 +334,10 @@ class Stepper {
static uint32_t nextBabystepISR;
#endif
#if ENABLED(DIRECT_STEPPING)
static page_step_state_t page_step_state;
#endif
static int32_t ticks_nominal;
#if DISABLED(S_CURVE_ACCELERATION)
static uint32_t acc_step_rate; // needed for deceleration start point
@ -426,6 +430,17 @@ class Stepper {
static void report_a_position(const xyz_long_t &pos);
static void report_positions();
// Discard current block and free any resources
FORCE_INLINE static void discard_current_block() {
#if ENABLED(DIRECT_STEPPING)
if (IS_PAGE(current_block))
page_manager.free_page(current_block->page_idx);
#endif
current_block = nullptr;
axis_did_move = 0;
planner.release_current_block();
}
// Quickly stop all steppers
FORCE_INLINE static void quick_stop() { abort_current_block = true; }

5
buildroot/share/tests/mega2560-tests

@ -71,8 +71,9 @@ opt_set NUM_SERVOS 1
opt_enable ZONESTAR_LCD Z_PROBE_SERVO_NR Z_SERVO_ANGLES DEACTIVATE_SERVOS_AFTER_MOVE BOOT_MARLIN_LOGO_ANIMATED \
AUTO_BED_LEVELING_3POINT DEBUG_LEVELING_FEATURE EEPROM_SETTINGS EEPROM_CHITCHAT M114_DETAIL \
NO_VOLUMETRICS EXTENDED_CAPABILITIES_REPORT AUTO_REPORT_TEMPERATURES AUTOTEMP G38_PROBE_TARGET JOYSTICK \
PRUSA_MMU2 MMU2_MENUS PRUSA_MMU2_S_MODE FILAMENT_RUNOUT_SENSOR NOZZLE_PARK_FEATURE ADVANCED_PAUSE_FEATURE Z_SAFE_HOMING
exec_test $1 $2 "RAMPS | ZONESTAR_LCD | MMU2 | Servo Probe | ABL 3-Pt | Debug Leveling | EEPROM | G38 ..."
PRUSA_MMU2 MMU2_MENUS PRUSA_MMU2_S_MODE DIRECT_STEPPING \
FILAMENT_RUNOUT_SENSOR NOZZLE_PARK_FEATURE ADVANCED_PAUSE_FEATURE Z_SAFE_HOMING
exec_test $1 $2 "RAMPS | ZONESTAR + Chinese | MMU2 | Servo | 3-Point + Debug | G38 ..."
#
# Test MINIRAMBO with PWM_MOTOR_CURRENT and many features

Loading…
Cancel
Save