Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Direct Stepping feature #17853

Merged
merged 9 commits into from
May 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 18 additions & 6 deletions Marlin/Configuration_adv.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand Down Expand Up @@ -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
Expand Down
24 changes: 16 additions & 8 deletions Marlin/src/HAL/AVR/MarlinSerial.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand All @@ -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
Expand Down
11 changes: 11 additions & 0 deletions Marlin/src/MarlinCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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());
}

/**
Expand Down Expand Up @@ -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.");
Expand Down
273 changes: 273 additions & 0 deletions Marlin/src/feature/direct_stepping.cpp
Original file line number Diff line number Diff line change
@@ -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
Loading