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

[Experimental] Direct stepper chunk support #2 #7047

Closed
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,15 @@ script:
#
# Test 2 extruders (one MAX6675) and heated bed on basic RAMPS 1.4
# plus a "Fix Mounted" Probe with Safe Homing and some arc options
# Also test CHUNK_SUPPORT
#
- opt_set MOTHERBOARD BOARD_RAMPS_14_EEB
- opt_set EXTRUDERS 2
- opt_set TEMP_SENSOR_0 -2
- opt_set TEMP_SENSOR_1 1
- opt_set TEMP_SENSOR_BED 1
- opt_enable PIDTEMPBED FIX_MOUNTED_PROBE Z_SAFE_HOMING ARC_P_CIRCLES CNC_WORKSPACE_PLANES
- opt_enable_adv CHUNK_SUPPORT
- build_marlin
#
# ...with AUTO_BED_LEVELING_LINEAR, Z_MIN_PROBE_REPEATABILITY_TEST, and DEBUG_LEVELING_FEATURE
Expand Down
36 changes: 30 additions & 6 deletions Marlin/Configuration_adv.h
Original file line number Diff line number Diff line change
Expand Up @@ -719,7 +719,9 @@

// 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, i.g. 8,16,32 because shifts and ors are used to do the ring-buffering.
#if ENABLED(SDSUPPORT)
#if ENABLED(CHUNK_SUPPORT)
#define BLOCK_BUFFER_SIZE 4 // Needs more memory for chunks, but can do more steps per block
#elif ENABLED(SDSUPPORT)
#define BLOCK_BUFFER_SIZE 16 // SD,LCD,Buttons take more memory, block buffer needs to be smaller
#else
#define BLOCK_BUFFER_SIZE 16 // maximize block buffer
Expand Down Expand Up @@ -755,6 +757,28 @@
// Some clients will have this feature soon. This could make the NO_TIMEOUTS unnecessary.
//#define ADVANCED_OK

// @section chunk

/**
* This is an experimental feature that allows a host device to send
* direct stepper buffers (chunks) instead of G0/1 movement commands.
* This frees up processing power on the Marlin device and allows
* a higher consistent step rate.
*
* This is useful when used with an "external planner" that
* acts as the USB host and controls the GCode stream while
* producing and triggering direct stepper chunks using the
* described protocol:
*
* <!insert links to docs here!>
Copy link
Contributor Author

@colinrgodsey colinrgodsey Jul 7, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@thinkyhead do you know where would be a good place to write a draft specification for this support? ill have to dig around the reprap wiki and see if theres a good dumping ground somewhere, but that might not be the right place...

EDIT: actually, that looks like it might be the right place

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. Use the RepRap wiki. All kinds of draft specifications there!

*/

//#define CHUNK_SUPPORT
#if ENABLED(CHUNK_SUPPORT)
#define NUM_CHUNK_BUFFERS 16 // must be a power of 2
#define CHUNK_OVERRIDE_TX_BUFFER_CHECK false // override sanity check for TX_BUFFER_SIZE
#endif

// @section fwretract

// Firmware based and LCD controlled retract
Expand Down Expand Up @@ -1283,13 +1307,13 @@
* I2C position encoders for closed loop control.
* Developed by Chris Barr at Aus3D.
*
* Wiki: http://wiki.aus3d.com.au/Magnetic_Encoder
* Github: https://github.com/Aus3D/MagneticEncoder
* Wiki: http://wiki.aus3d.com.au/Magnetic_Encoder
* Github: https://github.com/Aus3D/MagneticEncoder
*
* Supplier: http://aus3d.com.au/magnetic-encoder-module
* Alternative Supplier: http://reliabuild3d.com/
* Supplier: http://aus3d.com.au/magnetic-encoder-module
* Alt. Supplier: http://reliabuild3d.com/
*
* Reilabuild encoders have been modified to improve reliability.
* Reliabuild encoders have been modified to improve reliability.
*/

//#define I2C_POSITION_ENCODERS
Expand Down
2 changes: 2 additions & 0 deletions Marlin/Marlin.h
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,8 @@ extern uint8_t active_extruder;

void calculate_volumetric_multipliers();

void send_chunk_ok();

/**
* Blocking movement and shorthand functions
*/
Expand Down
109 changes: 96 additions & 13 deletions Marlin/MarlinSerial.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@

// Disable HardwareSerial.cpp to support chips without a UART (Attiny, etc.)

#if ENABLED(CHUNK_SUPPORT)

unsigned char chunk_buffer[NUM_CHUNK_BUFFERS][CHUNK_BUFFER_SIZE] = { { 0 } };
volatile uint8_t chunk_response[NUM_CHUNK_BUFFERS] = { CHUNK_RESPONSE_NONE };
volatile uint8_t chunk_respond_busy = 0;

#endif // CHUNK_SUPPORT

#if !defined(USBCON) && (defined(UBRRH) || defined(UBRR0H) || defined(UBRR1H) || defined(UBRR2H) || defined(UBRR3H))

#if UART_PRESENT(SERIAL_PORT)
Expand Down Expand Up @@ -136,19 +144,93 @@

#endif // EMERGENCY_PARSER

FORCE_INLINE void buffer_char(unsigned char c) {
const uint8_t h = rx_buffer.head,
i = (uint8_t)(h + 1) & (RX_BUFFER_SIZE - 1);

// if we should be storing the received character into the location
// just before the tail (meaning that the head would advance to the
// current location of the tail), we're about to overflow the buffer
// and so we don't write the character or advance the head.
if (i != rx_buffer.tail) {
rx_buffer.buffer[h] = c;
rx_buffer.head = i;
}
}

FORCE_INLINE void store_char(unsigned char c) {
CRITICAL_SECTION_START;
const uint8_t h = rx_buffer.head,
i = (uint8_t)(h + 1) & (RX_BUFFER_SIZE - 1);

// if we should be storing the received character into the location
// just before the tail (meaning that the head would advance to the
// current location of the tail), we're about to overflow the buffer
// and so we don't write the character or advance the head.
if (i != rx_buffer.tail) {
rx_buffer.buffer[h] = c;
rx_buffer.head = i;

#if ENABLED(CHUNK_SUPPORT)
static uint8_t chunk_stage = CHUNK_STAGE_WAIT,
chunk_buffer_iter = 0,
chunk_checksum = 0,
chunk_buffer_idx = 0;

const uint8_t cur_idx = chunk_buffer_idx;

/*
* Chunk storage state machine. After control char is processed,
* start buffering the chunk directly in this ISR. After the chunk
* is full, wait for the checksum character and set chunk_response
* accordingly.
*/
switch (chunk_stage) {
case CHUNK_STAGE_COLLECT:
chunk_buffer[cur_idx][chunk_buffer_iter++] = c;
chunk_checksum ^= c;

// has not rolled back to 0, buffer still filling
if (chunk_buffer_iter) break;

chunk_stage = CHUNK_STAGE_CHECKSUM;
break;

case CHUNK_STAGE_CHECKSUM:
chunk_stage = CHUNK_STAGE_WAIT;

//iterate index after checksum completes
chunk_buffer_idx = (chunk_buffer_idx + 1) & (NUM_CHUNK_BUFFERS - 1);

chunk_response[cur_idx] = CHUNK_RESPONSE_OK;

if (chunk_checksum == c) break;

chunk_response[cur_idx] = CHUNK_RESPONSE_FAIL;
break;

case CHUNK_STAGE_DRAIN:
if (++chunk_buffer_iter) break;

chunk_stage = CHUNK_STAGE_DRAIN_POST;
break;

case CHUNK_STAGE_DRAIN_POST:
chunk_stage = CHUNK_STAGE_WAIT;
chunk_respond_busy++;
break;

case CHUNK_STAGE_WAIT:
default:
if (c == CHUNK_START_CHAR) {
chunk_stage = CHUNK_STAGE_COLLECT;
chunk_buffer_iter = chunk_checksum = 0;

// if chunk is still busy, drain data and respond with a busy response
if (chunk_response[cur_idx] != CHUNK_RESPONSE_NONE)
chunk_stage = CHUNK_STAGE_DRAIN;
}
else {
buffer_char(c);
}
}

#else // !CHUNK_SUPPORT

buffer_char(c);

#endif // !CHUNK_SUPPORT

CRITICAL_SECTION_END;

#if ENABLED(EMERGENCY_PARSER)
Expand Down Expand Up @@ -188,7 +270,7 @@

#ifdef M_USARTx_RX_vect
ISR(M_USARTx_RX_vect) {
const unsigned char c = M_UDRx;
unsigned char c = M_UDRx;
store_char(c);
}
#endif
Expand Down Expand Up @@ -356,9 +438,10 @@
}

#else

void MarlinSerial::write(uint8_t c) {
while (!TEST(M_UCSRxA, M_UDREx))
;
while (!TEST(M_UCSRxA, M_UDREx));

M_UDRx = c;
}
#endif
Expand Down
21 changes: 21 additions & 0 deletions Marlin/MarlinSerial.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,27 @@
#define BIN 2
#define BYTE 0

#if ENABLED(CHUNK_SUPPORT)
#define CHUNK_BUFFER_SIZE 256
#define CHUNK_START_CHAR '!'

#define CHUNK_STAGE_COLLECT 0
#define CHUNK_STAGE_CHECKSUM 1
#define CHUNK_STAGE_DRAIN 2
#define CHUNK_STAGE_DRAIN_POST 3
#define CHUNK_STAGE_WAIT 4

#define CHUNK_RESPONSE_NONE 0
#define CHUNK_RESPONSE_PENDING 1
#define CHUNK_RESPONSE_OK 2
#define CHUNK_RESPONSE_FAIL 3
#define CHUNK_RESPONSE_BUSY 4

extern unsigned char chunk_buffer[NUM_CHUNK_BUFFERS][CHUNK_BUFFER_SIZE];
extern volatile uint8_t chunk_response[NUM_CHUNK_BUFFERS];
extern volatile uint8_t chunk_respond_busy;
#endif // CHUNK_SUPPORT

#ifndef USBCON
// Define constants and variables for buffering incoming serial data. We're
// using a ring buffer (I think), in which rx_buffer_head is the index of the
Expand Down
48 changes: 46 additions & 2 deletions Marlin/Marlin_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@
*
* -----------------
*
* "C" Codes
*
* C0 - Execute buffered step chunk(s)
*
* "G" Codes
*
* G0 -> G1
Expand Down Expand Up @@ -3226,6 +3230,24 @@ void gcode_get_destination() {
***************** GCode Handlers *****************
**************************************************/

#if ENABLED(CHUNK_SUPPORT)

inline void gcode_C0() {
static uint32_t step_speed = 0;

if (parser.seen('S')) step_speed = parser.value_feedrate();

if (!parser.seen('I')) return;

uint8_t chunk_idx = parser.value_byte(),
chunk_num = parser.seen('R') ? parser.value_byte() : 1;

if (step_speed)
planner.buffer_chunk(chunk_idx, chunk_num, active_extruder, step_speed);
}

#endif // CHUNK_SUPPORT

/**
* G0, G1: Coordinated movement of X Y Z E axes
*/
Expand Down Expand Up @@ -5715,7 +5737,9 @@ inline void gcode_G92() {
else if (didE)
sync_plan_position_e();

report_current_position();
#if DISABLED(CHUNK_SUPPORT)
report_current_position();
#endif
}

#if HAS_RESUME_CONTINUE
Expand Down Expand Up @@ -10402,8 +10426,19 @@ void process_next_command() {
// Parse the next command in the queue
parser.parse(current_command);

// Handle a known G, M, or T
// Handle a known C, G, M, or T
switch (parser.command_letter) {

#if ENABLED(CHUNK_SUPPORT)

case 'C': switch (parser.codenum) {
case 0:
gcode_C0();
break;
}
break;
#endif // CHUNK_SUPPORT

case 'G': switch (parser.codenum) {

// G0, G1
Expand Down Expand Up @@ -12804,6 +12839,11 @@ void idle(
bool no_stepper_sleep/*=false*/
#endif
) {

#if ENABLED(CHUNK_SUPPORT)
send_chunk_ok();
#endif

lcd_update();

host_keepalive();
Expand Down Expand Up @@ -13122,6 +13162,10 @@ void setup() {
#if ENABLED(SWITCHING_NOZZLE)
move_nozzle_servo(0); // Initialize nozzle servo
#endif

#if ENABLED(CHUNK_SUPPORT)
SERIAL_PROTOCOLLNPGM("setup_done");
#endif
}

/**
Expand Down
15 changes: 15 additions & 0 deletions Marlin/SanityCheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -1314,3 +1314,18 @@ static_assert(COUNT(sanity_arr_3) <= XYZE_N, "DEFAULT_MAX_ACCELERATION has too m
#endif
#endif
#endif // SPINDLE_LASER_ENABLE

/**
* Sanity checks for stepper chunk support
*/
#if ENABLED(CHUNK_SUPPORT)
#if (NUM_CHUNK_BUFFERS & (NUM_CHUNK_BUFFERS - 1)) != 0
#error "CHUNK_SUPPORT: NUM_CHUNK_BUFFERS must be a power of 2."
#endif
#if (TX_BUFFER_SIZE < 8 && !CHUNK_OVERRIDE_TX_BUFFER_CHECK)
#warning "CHUNK_SUPPORT: TX_BUFFER_SIZE should be at least 8 bytes. Can use CHUNK_OVERRIDE_TX_BUFFER_CHECK to override."
#endif
#if ENABLED(LIN_ADVANCE)
#error "CHUNK_SUPPORT: currently not compatible with LIN_ADVANCE, enable in external planner if possible."
#endif
#endif
Loading