1
0
mirror of https://github.com/MarlinFirmware/Marlin.git synced 2024-11-30 07:17:59 +00:00

[1.1.x] Assorted fixes and improvements (#10914)

Co-Authored-By: ejtagle
This commit is contained in:
Scott Lahteine 2018-06-01 19:00:59 -05:00 committed by GitHub
parent 67d9d1870c
commit 3b06a8e917
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 238 additions and 183 deletions

View File

@ -52,6 +52,10 @@
#define CRITICAL_SECTION_END SREG = _sreg; #define CRITICAL_SECTION_END SREG = _sreg;
#endif #endif
#define ISRS_ENABLED() TEST(SREG, SREG_I)
#define ENABLE_ISRS() sei()
#define DISABLE_ISRS() cli()
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
// Types // Types
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
@ -148,7 +152,6 @@ void TIMER1_COMPA_vect (void) { \
A("lds r16, %[timsk1]") /* 2 Load into R0 the stepper timer Interrupt mask register [TIMSK1] */ \ A("lds r16, %[timsk1]") /* 2 Load into R0 the stepper timer Interrupt mask register [TIMSK1] */ \
A("andi r16,~%[msk1]") /* 1 Disable the stepper ISR */ \ A("andi r16,~%[msk1]") /* 1 Disable the stepper ISR */ \
A("sts %[timsk1], r16") /* 2 And set the new value */ \ A("sts %[timsk1], r16") /* 2 And set the new value */ \
A("sei") /* 1 Enable global interrupts - stepper and temperature ISRs are disabled, so no risk of reentry or being preempted by the temperature ISR */ \
A("push r16") /* 2 Save TIMSK1 into stack */ \ A("push r16") /* 2 Save TIMSK1 into stack */ \
A("in r16, 0x3B") /* 1 Get RAMPZ register */ \ A("in r16, 0x3B") /* 1 Get RAMPZ register */ \
A("push r16") /* 2 Save RAMPZ into stack */ \ A("push r16") /* 2 Save RAMPZ into stack */ \
@ -258,7 +261,7 @@ void TIMER0_COMPB_vect (void) { \
A("out 0x3B, r16") /* 1 Restore RAMPZ register to its original value */ \ A("out 0x3B, r16") /* 1 Restore RAMPZ register to its original value */ \
A("pop r16") /* 2 Get the original TIMSK0 value but with temperature ISR disabled */ \ A("pop r16") /* 2 Get the original TIMSK0 value but with temperature ISR disabled */ \
A("ori r16,%[msk0]") /* 1 Enable temperature ISR */ \ A("ori r16,%[msk0]") /* 1 Enable temperature ISR */ \
A("cli") /* 1 Disable global interrupts - We must do this, as we will reenable the temperature ISR, and we don´t want to reenter this handler until the current one is done */ \ A("cli") /* 1 Disable global interrupts - We must do this, as we will reenable the temperature ISR, and we don't want to reenter this handler until the current one is done */ \
A("sts %[timsk0], r16") /* 2 And restore the old value */ \ A("sts %[timsk0], r16") /* 2 And restore the old value */ \
A("pop r16") /* 2 Get the old SREG */ \ A("pop r16") /* 2 Get the old SREG */ \
A("out __SREG__, r16") /* 1 And restore the SREG value */ \ A("out __SREG__, r16") /* 1 And restore the SREG value */ \

View File

@ -68,8 +68,6 @@
uint8_t xon_xoff_state = XON_XOFF_CHAR_SENT | XON_CHAR; uint8_t xon_xoff_state = XON_XOFF_CHAR_SENT | XON_CHAR;
#endif #endif
void clear_command_queue();
#if ENABLED(SERIAL_STATS_DROPPED_RX) #if ENABLED(SERIAL_STATS_DROPPED_RX)
uint8_t rx_dropped_bytes = 0; uint8_t rx_dropped_bytes = 0;
#endif #endif
@ -78,10 +76,14 @@
ring_buffer_pos_t rx_max_enqueued = 0; ring_buffer_pos_t rx_max_enqueued = 0;
#endif #endif
// A SW memory barrier, to ensure GCC does not overoptimize loops
#define sw_barrier() asm volatile("": : :"memory");
#if ENABLED(EMERGENCY_PARSER) #if ENABLED(EMERGENCY_PARSER)
#include "emergency_parser.h" #include "emergency_parser.h"
#endif #endif
// (called with RX interrupts disabled)
FORCE_INLINE void store_rxd_char() { FORCE_INLINE void store_rxd_char() {
const ring_buffer_pos_t h = rx_buffer.head, const ring_buffer_pos_t h = rx_buffer.head,
i = (ring_buffer_pos_t)(h + 1) & (ring_buffer_pos_t)(RX_BUFFER_SIZE - 1); i = (ring_buffer_pos_t)(h + 1) & (ring_buffer_pos_t)(RX_BUFFER_SIZE - 1);
@ -121,18 +123,22 @@
// let the host react and stop sending bytes. This translates to 13mS // let the host react and stop sending bytes. This translates to 13mS
// propagation time. // propagation time.
if (rx_count >= (RX_BUFFER_SIZE) / 8) { if (rx_count >= (RX_BUFFER_SIZE) / 8) {
// If TX interrupts are disabled and data register is empty, // If TX interrupts are disabled and data register is empty,
// just write the byte to the data register and be done. This // just write the byte to the data register and be done. This
// shortcut helps significantly improve the effective datarate // shortcut helps significantly improve the effective datarate
// at high (>500kbit/s) bitrates, where interrupt overhead // at high (>500kbit/s) bitrates, where interrupt overhead
// becomes a slowdown. // becomes a slowdown.
if (!TEST(M_UCSRxB, M_UDRIEx) && TEST(M_UCSRxA, M_UDREx)) { if (!TEST(M_UCSRxB, M_UDRIEx) && TEST(M_UCSRxA, M_UDREx)) {
// Send an XOFF character // Send an XOFF character
M_UDRx = XOFF_CHAR; M_UDRx = XOFF_CHAR;
// clear the TXC bit -- "can be cleared by writing a one to its bit // clear the TXC bit -- "can be cleared by writing a one to its bit
// location". This makes sure flush() won't return until the bytes // location". This makes sure flush() won't return until the bytes
// actually got written // actually got written
SBI(M_UCSRxA, M_TXCx); SBI(M_UCSRxA, M_TXCx);
// And remember it was sent // And remember it was sent
xon_xoff_state = XOFF_CHAR | XON_XOFF_CHAR_SENT; xon_xoff_state = XOFF_CHAR | XON_XOFF_CHAR_SENT;
} }
@ -145,8 +151,14 @@
xon_xoff_state = XOFF_CHAR; xon_xoff_state = XOFF_CHAR;
#else #else
// We are not using TX interrupts, we will have to send this manually // We are not using TX interrupts, we will have to send this manually
while (!TEST(M_UCSRxA, M_UDREx)) {/* nada */} while (!TEST(M_UCSRxA, M_UDREx)) sw_barrier();
M_UDRx = XOFF_CHAR; M_UDRx = XOFF_CHAR;
// clear the TXC bit -- "can be cleared by writing a one to its bit
// location". This makes sure flush() won't return until the bytes
// actually got written
SBI(M_UCSRxA, M_TXCx);
// And remember we already sent it // And remember we already sent it
xon_xoff_state = XOFF_CHAR | XON_XOFF_CHAR_SENT; xon_xoff_state = XOFF_CHAR | XON_XOFF_CHAR_SENT;
#endif #endif
@ -162,6 +174,7 @@
#if TX_BUFFER_SIZE > 0 #if TX_BUFFER_SIZE > 0
// (called with TX irqs disabled)
FORCE_INLINE void _tx_udr_empty_irq(void) { FORCE_INLINE void _tx_udr_empty_irq(void) {
// If interrupts are enabled, there must be more data in the output // If interrupts are enabled, there must be more data in the output
// buffer. // buffer.
@ -243,117 +256,139 @@
CBI(M_UCSRxB, M_UDRIEx); CBI(M_UCSRxB, M_UDRIEx);
} }
void MarlinSerial::checkRx(void) {
if (TEST(M_UCSRxA, M_RXCx)) {
CRITICAL_SECTION_START;
store_rxd_char();
CRITICAL_SECTION_END;
}
}
int MarlinSerial::peek(void) { int MarlinSerial::peek(void) {
CRITICAL_SECTION_START; #if RX_BUFFER_SIZE > 256
// Disable RX interrupts, but only if non atomic reads
const bool isr_enabled = TEST(M_UCSRxB, M_RXCIEx);
CBI(M_UCSRxB, M_RXCIEx);
#endif
const int v = rx_buffer.head == rx_buffer.tail ? -1 : rx_buffer.buffer[rx_buffer.tail]; const int v = rx_buffer.head == rx_buffer.tail ? -1 : rx_buffer.buffer[rx_buffer.tail];
CRITICAL_SECTION_END; #if RX_BUFFER_SIZE > 256
// Reenable RX interrupts if they were enabled
if (isr_enabled) SBI(M_UCSRxB, M_RXCIEx);
#endif
return v; return v;
} }
int MarlinSerial::read(void) { int MarlinSerial::read(void) {
int v; int v;
CRITICAL_SECTION_START;
const ring_buffer_pos_t t = rx_buffer.tail; #if RX_BUFFER_SIZE > 256
if (rx_buffer.head == t) // Disable RX interrupts to ensure atomic reads
const bool isr_enabled = TEST(M_UCSRxB, M_RXCIEx);
CBI(M_UCSRxB, M_RXCIEx);
#endif
const ring_buffer_pos_t h = rx_buffer.head;
#if RX_BUFFER_SIZE > 256
// End critical section
if (isr_enabled) SBI(M_UCSRxB, M_RXCIEx);
#endif
ring_buffer_pos_t t = rx_buffer.tail;
if (h == t)
v = -1; v = -1;
else { else {
v = rx_buffer.buffer[t]; v = rx_buffer.buffer[t];
rx_buffer.tail = (ring_buffer_pos_t)(t + 1) & (RX_BUFFER_SIZE - 1); t = (ring_buffer_pos_t)(t + 1) & (RX_BUFFER_SIZE - 1);
#if RX_BUFFER_SIZE > 256
// Disable RX interrupts to ensure atomic write to tail, so
// the RX isr can't read partially updated values
const bool isr_enabled = TEST(M_UCSRxB, M_RXCIEx);
CBI(M_UCSRxB, M_RXCIEx);
#endif
// Advance tail
rx_buffer.tail = t;
#if RX_BUFFER_SIZE > 256
// End critical section
if (isr_enabled) SBI(M_UCSRxB, M_RXCIEx);
#endif
#if ENABLED(SERIAL_XON_XOFF) #if ENABLED(SERIAL_XON_XOFF)
if ((xon_xoff_state & XON_XOFF_CHAR_MASK) == XOFF_CHAR) { if ((xon_xoff_state & XON_XOFF_CHAR_MASK) == XOFF_CHAR) {
// Get count of bytes in the RX buffer // Get count of bytes in the RX buffer
ring_buffer_pos_t rx_count = (ring_buffer_pos_t)(rx_buffer.head - rx_buffer.tail) & (ring_buffer_pos_t)(RX_BUFFER_SIZE - 1); ring_buffer_pos_t rx_count = (ring_buffer_pos_t)(h - t) & (ring_buffer_pos_t)(RX_BUFFER_SIZE - 1);
// When below 10% of RX buffer capacity, send XON before // When below 10% of RX buffer capacity, send XON before
// running out of RX buffer bytes // running out of RX buffer bytes
if (rx_count < (RX_BUFFER_SIZE) / 10) { if (rx_count < (RX_BUFFER_SIZE) / 10) {
xon_xoff_state = XON_CHAR | XON_XOFF_CHAR_SENT; xon_xoff_state = XON_CHAR | XON_XOFF_CHAR_SENT;
CRITICAL_SECTION_END; // End critical section before returning! write(XON_CHAR);
writeNoHandshake(XON_CHAR);
return v; return v;
} }
} }
#endif #endif
} }
CRITICAL_SECTION_END;
return v; return v;
} }
ring_buffer_pos_t MarlinSerial::available(void) { ring_buffer_pos_t MarlinSerial::available(void) {
CRITICAL_SECTION_START; #if RX_BUFFER_SIZE > 256
const bool isr_enabled = TEST(M_UCSRxB, M_RXCIEx);
CBI(M_UCSRxB, M_RXCIEx);
#endif
const ring_buffer_pos_t h = rx_buffer.head, t = rx_buffer.tail; const ring_buffer_pos_t h = rx_buffer.head, t = rx_buffer.tail;
CRITICAL_SECTION_END;
#if RX_BUFFER_SIZE > 256
if (isr_enabled) SBI(M_UCSRxB, M_RXCIEx);
#endif
return (ring_buffer_pos_t)(RX_BUFFER_SIZE + h - t) & (RX_BUFFER_SIZE - 1); return (ring_buffer_pos_t)(RX_BUFFER_SIZE + h - t) & (RX_BUFFER_SIZE - 1);
} }
void MarlinSerial::flush(void) { void MarlinSerial::flush(void) {
// Don't change this order of operations. If the RX interrupt occurs between #if RX_BUFFER_SIZE > 256
// reading rx_buffer_head and updating rx_buffer_tail, the previous rx_buffer_head const bool isr_enabled = TEST(M_UCSRxB, M_RXCIEx);
// may be written to rx_buffer_tail, making the buffer appear full rather than empty. CBI(M_UCSRxB, M_RXCIEx);
CRITICAL_SECTION_START; #endif
rx_buffer.head = rx_buffer.tail = 0;
clear_command_queue(); rx_buffer.tail = rx_buffer.head;
CRITICAL_SECTION_END;
#if RX_BUFFER_SIZE > 256
if (isr_enabled) SBI(M_UCSRxB, M_RXCIEx);
#endif
#if ENABLED(SERIAL_XON_XOFF) #if ENABLED(SERIAL_XON_XOFF)
if ((xon_xoff_state & XON_XOFF_CHAR_MASK) == XOFF_CHAR) { if ((xon_xoff_state & XON_XOFF_CHAR_MASK) == XOFF_CHAR) {
xon_xoff_state = XON_CHAR | XON_XOFF_CHAR_SENT; xon_xoff_state = XON_CHAR | XON_XOFF_CHAR_SENT;
writeNoHandshake(XON_CHAR); write(XON_CHAR);
} }
#endif #endif
} }
#if TX_BUFFER_SIZE > 0 #if TX_BUFFER_SIZE > 0
uint8_t MarlinSerial::availableForWrite(void) {
CRITICAL_SECTION_START;
const uint8_t h = tx_buffer.head, t = tx_buffer.tail;
CRITICAL_SECTION_END;
return (uint8_t)(TX_BUFFER_SIZE + h - t) & (TX_BUFFER_SIZE - 1);
}
void MarlinSerial::write(const uint8_t c) { void MarlinSerial::write(const uint8_t c) {
#if ENABLED(SERIAL_XON_XOFF)
const uint8_t state = xon_xoff_state;
if (!(state & XON_XOFF_CHAR_SENT)) {
// Send 2 chars: XON/XOFF, then a user-specified char
writeNoHandshake(state & XON_XOFF_CHAR_MASK);
xon_xoff_state = state | XON_XOFF_CHAR_SENT;
}
#endif
writeNoHandshake(c);
}
void MarlinSerial::writeNoHandshake(const uint8_t c) {
_written = true; _written = true;
CRITICAL_SECTION_START;
bool emty = (tx_buffer.head == tx_buffer.tail);
CRITICAL_SECTION_END;
// If the buffer and the data register is empty, just write the byte // If the TX interrupts are disabled and the data register
// to the data register and be done. This shortcut helps // is empty, just write the byte to the data register and
// significantly improve the effective datarate at high (> // be done. This shortcut helps significantly improve the
// 500kbit/s) bitrates, where interrupt overhead becomes a slowdown. // effective datarate at high (>500kbit/s) bitrates, where
if (emty && TEST(M_UCSRxA, M_UDREx)) { // interrupt overhead becomes a slowdown.
CRITICAL_SECTION_START; if (!TEST(M_UCSRxB, M_UDRIEx) && TEST(M_UCSRxA, M_UDREx)) {
M_UDRx = c; M_UDRx = c;
// clear the TXC bit -- "can be cleared by writing a one to its bit
// location". This makes sure flush() won't return until the bytes
// actually got written
SBI(M_UCSRxA, M_TXCx); SBI(M_UCSRxA, M_TXCx);
CRITICAL_SECTION_END;
return; return;
} }
const uint8_t i = (tx_buffer.head + 1) & (TX_BUFFER_SIZE - 1); const uint8_t i = (tx_buffer.head + 1) & (TX_BUFFER_SIZE - 1);
// If the output buffer is full, there's nothing for it other than to // If the output buffer is full, there's nothing for it other than to
// wait for the interrupt handler to empty it a bit // wait for the interrupt handler to empty it a bit
while (i == tx_buffer.tail) { while (i == tx_buffer.tail) {
if (!TEST(SREG, SREG_I)) { if (!ISRS_ENABLED()) {
// Interrupts are disabled, so we'll have to poll the data // Interrupts are disabled, so we'll have to poll the data
// register empty flag ourselves. If it is set, pretend an // register empty flag ourselves. If it is set, pretend an
// interrupt has happened and call the handler to free up // interrupt has happened and call the handler to free up
@ -361,17 +396,18 @@
if (TEST(M_UCSRxA, M_UDREx)) if (TEST(M_UCSRxA, M_UDREx))
_tx_udr_empty_irq(); _tx_udr_empty_irq();
} }
else { // (else , the interrupt handler will free up space for us)
// nop, the interrupt handler will free up space for us
} // Make sure compiler rereads tx_buffer.tail
sw_barrier();
} }
// Store new char. head is always safe to move
tx_buffer.buffer[tx_buffer.head] = c; tx_buffer.buffer[tx_buffer.head] = c;
{ CRITICAL_SECTION_START;
tx_buffer.head = i; tx_buffer.head = i;
// Enable TX isr
SBI(M_UCSRxB, M_UDRIEx); SBI(M_UCSRxB, M_UDRIEx);
CRITICAL_SECTION_END;
}
return; return;
} }
@ -384,33 +420,23 @@
return; return;
while (TEST(M_UCSRxB, M_UDRIEx) || !TEST(M_UCSRxA, M_TXCx)) { while (TEST(M_UCSRxB, M_UDRIEx) || !TEST(M_UCSRxA, M_TXCx)) {
if (!TEST(SREG, SREG_I) && TEST(M_UCSRxB, M_UDRIEx)) if (!ISRS_ENABLED()) {
// Interrupts are globally disabled, but the DR empty // Interrupts are globally disabled, but the DR empty
// interrupt should be enabled, so poll the DR empty flag to // interrupt should be enabled, so poll the DR empty flag to
// prevent deadlock // prevent deadlock
if (TEST(M_UCSRxA, M_UDREx)) if (TEST(M_UCSRxA, M_UDREx))
_tx_udr_empty_irq(); _tx_udr_empty_irq();
} }
sw_barrier();
}
// If we get here, nothing is queued anymore (DRIE is disabled) and // If we get here, nothing is queued anymore (DRIE is disabled) and
// the hardware finished tranmission (TXC is set). // the hardware finished transmission (TXC is set).
} }
#else // TX_BUFFER_SIZE == 0 #else // TX_BUFFER_SIZE == 0
void MarlinSerial::write(const uint8_t c) { void MarlinSerial::write(const uint8_t c) {
#if ENABLED(SERIAL_XON_XOFF) while (!TEST(M_UCSRxA, M_UDREx)) sw_barrier();
// Do a priority insertion of an XON/XOFF char, if needed.
const uint8_t state = xon_xoff_state;
if (!(state & XON_XOFF_CHAR_SENT)) {
writeNoHandshake(state & XON_XOFF_CHAR_MASK);
xon_xoff_state = state | XON_XOFF_CHAR_SENT;
}
#endif
writeNoHandshake(c);
}
void MarlinSerial::writeNoHandshake(uint8_t c) {
while (!TEST(M_UCSRxA, M_UDREx)) {/* nada */}
M_UDRx = c; M_UDRx = c;
} }

View File

@ -101,7 +101,7 @@
extern ring_buffer_pos_t rx_max_enqueued; extern ring_buffer_pos_t rx_max_enqueued;
#endif #endif
class MarlinSerial { //: public Stream class MarlinSerial {
public: public:
MarlinSerial() {}; MarlinSerial() {};
@ -111,13 +111,10 @@
static int read(void); static int read(void);
static void flush(void); static void flush(void);
static ring_buffer_pos_t available(void); static ring_buffer_pos_t available(void);
static void checkRx(void);
static void write(const uint8_t c); static void write(const uint8_t c);
#if TX_BUFFER_SIZE > 0 #if TX_BUFFER_SIZE > 0
static uint8_t availableForWrite(void);
static void flushTX(void); static void flushTX(void);
#endif #endif
static void writeNoHandshake(const uint8_t c);
#if ENABLED(SERIAL_STATS_DROPPED_RX) #if ENABLED(SERIAL_STATS_DROPPED_RX)
FORCE_INLINE static uint32_t dropped() { return rx_dropped_bytes; } FORCE_INLINE static uint32_t dropped() { return rx_dropped_bytes; }

View File

@ -556,10 +556,13 @@ void Endstops::update() {
} \ } \
}while(0) }while(0)
// Call the endstop triggered routine for single endstops // Call the endstop triggered routine for dual endstops
#define PROCESS_DUAL_ENDSTOP(AXIS1, AXIS2, MINMAX) do { \ #define PROCESS_DUAL_ENDSTOP(AXIS1, AXIS2, MINMAX) do { \
if (TEST_ENDSTOP(_ENDSTOP(AXIS1, MINMAX)) || TEST_ENDSTOP(_ENDSTOP(AXIS2, MINMAX))) { \ const byte dual_hit = TEST_ENDSTOP(_ENDSTOP(AXIS1, MINMAX)) | (TEST_ENDSTOP(_ENDSTOP(AXIS2, MINMAX)) << 1); \
if (dual_hit) { \
_ENDSTOP_HIT(AXIS1, MINMAX); \ _ENDSTOP_HIT(AXIS1, MINMAX); \
/* if not performing home or if both endstops were trigged during homing... */ \
if (!stepper.performing_homing || dual_hit == 0x3) \
planner.endstop_triggered(_AXIS(AXIS1)); \ planner.endstop_triggered(_AXIS(AXIS1)); \
} \ } \
}while(0) }while(0)

View File

@ -107,7 +107,15 @@ class Endstops {
/** /**
* Get current endstops state * Get current endstops state
*/ */
FORCE_INLINE static esbits_t state() { return live_state; } FORCE_INLINE static esbits_t state() {
return
#if ENABLED(ENDSTOP_NOISE_FILTER)
validated_live_state
#else
live_state
#endif
;
}
/** /**
* Report endstop hits to serial. Called from loop(). * Report endstop hits to serial. Called from loop().

View File

@ -2449,9 +2449,13 @@ void Planner::_set_position_mm(const float &a, const float &b, const float &c, c
position_float[C_AXIS] = c; position_float[C_AXIS] = c;
position_float[E_AXIS] = e; position_float[E_AXIS] = e;
#endif #endif
previous_nominal_speed_sqr = 0.0; // Resets planner junction speeds. Assumes start from rest. if (has_blocks_queued()) {
ZERO(previous_speed); //previous_nominal_speed_sqr = 0.0; // Reset planner junction speeds. Assume start from rest.
//ZERO(previous_speed);
buffer_sync_block(); buffer_sync_block();
}
else
stepper.set_position(position[A_AXIS], position[B_AXIS], position[C_AXIS], position[E_AXIS]);
} }
void Planner::set_position_mm_kinematic(const float (&cart)[XYZE]) { void Planner::set_position_mm_kinematic(const float (&cart)[XYZE]) {
@ -2483,8 +2487,12 @@ void Planner::set_position_mm(const AxisEnum axis, const float &v) {
#if HAS_POSITION_FLOAT #if HAS_POSITION_FLOAT
position_float[axis] = v; position_float[axis] = v;
#endif #endif
previous_speed[axis] = 0.0; if (has_blocks_queued()) {
//previous_speed[axis] = 0.0;
buffer_sync_block(); buffer_sync_block();
}
else
stepper.set_position(axis, position[axis]);
} }
// Recalculate the steps/s^2 acceleration rates, based on the mm/s^2 // Recalculate the steps/s^2 acceleration rates, based on the mm/s^2

View File

@ -91,13 +91,13 @@ uint8_t Stepper::last_direction_bits = 0,
bool Stepper::abort_current_block; bool Stepper::abort_current_block;
#if ENABLED(X_DUAL_ENDSTOPS) #if ENABLED(X_DUAL_ENDSTOPS)
bool Stepper::locked_x_motor = false, Stepper::locked_x2_motor = false; bool Stepper::locked_X_motor = false, Stepper::locked_X2_motor = false;
#endif #endif
#if ENABLED(Y_DUAL_ENDSTOPS) #if ENABLED(Y_DUAL_ENDSTOPS)
bool Stepper::locked_y_motor = false, Stepper::locked_y2_motor = false; bool Stepper::locked_Y_motor = false, Stepper::locked_Y2_motor = false;
#endif #endif
#if ENABLED(Z_DUAL_ENDSTOPS) #if ENABLED(Z_DUAL_ENDSTOPS)
bool Stepper::locked_z_motor = false, Stepper::locked_z2_motor = false; bool Stepper::locked_Z_motor = false, Stepper::locked_Z2_motor = false;
#endif #endif
/** /**
@ -169,21 +169,15 @@ uint8_t Stepper::step_loops, Stepper::step_loops_nominal;
volatile int32_t Stepper::endstops_trigsteps[XYZ]; volatile int32_t Stepper::endstops_trigsteps[XYZ];
#if ENABLED(X_DUAL_ENDSTOPS) || ENABLED(Y_DUAL_ENDSTOPS) || ENABLED(Z_DUAL_ENDSTOPS) #if ENABLED(X_DUAL_ENDSTOPS) || ENABLED(Y_DUAL_ENDSTOPS) || ENABLED(Z_DUAL_ENDSTOPS)
#define LOCKED_X_MOTOR locked_x_motor
#define LOCKED_Y_MOTOR locked_y_motor
#define LOCKED_Z_MOTOR locked_z_motor
#define LOCKED_X2_MOTOR locked_x2_motor
#define LOCKED_Y2_MOTOR locked_y2_motor
#define LOCKED_Z2_MOTOR locked_z2_motor
#define DUAL_ENDSTOP_APPLY_STEP(A,V) \ #define DUAL_ENDSTOP_APPLY_STEP(A,V) \
if (performing_homing) { \ if (performing_homing) { \
if (A##_HOME_DIR < 0) { \ if (A##_HOME_DIR < 0) { \
if (!(TEST(endstops.state(), A##_MIN) && count_direction[_AXIS(A)] < 0) && !LOCKED_##A##_MOTOR) A##_STEP_WRITE(V); \ if (!(TEST(endstops.state(), A##_MIN) && count_direction[_AXIS(A)] < 0) && !locked_##A##_motor) A##_STEP_WRITE(V); \
if (!(TEST(endstops.state(), A##2_MIN) && count_direction[_AXIS(A)] < 0) && !LOCKED_##A##2_MOTOR) A##2_STEP_WRITE(V); \ if (!(TEST(endstops.state(), A##2_MIN) && count_direction[_AXIS(A)] < 0) && !locked_##A##2_motor) A##2_STEP_WRITE(V); \
} \ } \
else { \ else { \
if (!(TEST(endstops.state(), A##_MAX) && count_direction[_AXIS(A)] > 0) && !LOCKED_##A##_MOTOR) A##_STEP_WRITE(V); \ if (!(TEST(endstops.state(), A##_MAX) && count_direction[_AXIS(A)] > 0) && !locked_##A##_motor) A##_STEP_WRITE(V); \
if (!(TEST(endstops.state(), A##2_MAX) && count_direction[_AXIS(A)] > 0) && !LOCKED_##A##2_MOTOR) A##2_STEP_WRITE(V); \ if (!(TEST(endstops.state(), A##2_MAX) && count_direction[_AXIS(A)] > 0) && !locked_##A##2_motor) A##2_STEP_WRITE(V); \
} \ } \
} \ } \
else { \ else { \
@ -1117,27 +1111,22 @@ void Stepper::set_directions() {
HAL_STEP_TIMER_ISR { HAL_STEP_TIMER_ISR {
HAL_timer_isr_prologue(STEP_TIMER_NUM); HAL_timer_isr_prologue(STEP_TIMER_NUM);
// Program timer compare for the maximum period, so it does NOT Stepper::isr();
// flag an interrupt while this ISR is running - So changes from small
// periods to big periods are respected and the timer does not reset to 0
HAL_timer_set_compare(STEP_TIMER_NUM, HAL_TIMER_TYPE_MAX);
// Call the ISR scheduler
hal_timer_t ticks = Stepper::isr_scheduler();
// Now 'ticks' contains the period to the next Stepper ISR - And we are
// sure that the time has not arrived yet - Warrantied by the scheduler
// Set the next ISR to fire at the proper time
HAL_timer_set_compare(STEP_TIMER_NUM, ticks);
HAL_timer_isr_epilogue(STEP_TIMER_NUM); HAL_timer_isr_epilogue(STEP_TIMER_NUM);
} }
#define STEP_MULTIPLY(A,B) MultiU24X32toH16(A, B) #define STEP_MULTIPLY(A,B) MultiU24X32toH16(A, B)
hal_timer_t Stepper::isr_scheduler() { void Stepper::isr() {
uint32_t interval;
// Disable interrupts, to avoid ISR preemption while we reprogram the period
DISABLE_ISRS();
// Program timer compare for the maximum period, so it does NOT
// flag an interrupt while this ISR is running - So changes from small
// periods to big periods are respected and the timer does not reset to 0
HAL_timer_set_compare(STEP_TIMER_NUM, HAL_TIMER_TYPE_MAX);
// Count of ticks for the next ISR // Count of ticks for the next ISR
hal_timer_t next_isr_ticks = 0; hal_timer_t next_isr_ticks = 0;
@ -1148,6 +1137,9 @@ hal_timer_t Stepper::isr_scheduler() {
// We need this variable here to be able to use it in the following loop // We need this variable here to be able to use it in the following loop
hal_timer_t min_ticks; hal_timer_t min_ticks;
do { do {
// Enable ISRs to reduce USART processing latency
ENABLE_ISRS();
// Run main stepping pulse phase ISR if we have to // Run main stepping pulse phase ISR if we have to
if (!nextMainISR) Stepper::stepper_pulse_phase_isr(); if (!nextMainISR) Stepper::stepper_pulse_phase_isr();
@ -1161,13 +1153,13 @@ hal_timer_t Stepper::isr_scheduler() {
// Run main stepping block processing ISR if we have to // Run main stepping block processing ISR if we have to
if (!nextMainISR) nextMainISR = Stepper::stepper_block_phase_isr(); if (!nextMainISR) nextMainISR = Stepper::stepper_block_phase_isr();
uint32_t interval =
#if ENABLED(LIN_ADVANCE) #if ENABLED(LIN_ADVANCE)
// Select the closest interval in time MIN(nextAdvanceISR, nextMainISR) // Nearest time interval
interval = (nextAdvanceISR <= nextMainISR) ? nextAdvanceISR : nextMainISR;
#else #else
// The interval is just the remaining time to the stepper ISR nextMainISR // Remaining stepper ISR time
interval = nextMainISR;
#endif #endif
;
// Limit the value to the maximum possible value of the timer // Limit the value to the maximum possible value of the timer
NOMORE(interval, HAL_TIMER_TYPE_MAX); NOMORE(interval, HAL_TIMER_TYPE_MAX);
@ -1206,6 +1198,16 @@ hal_timer_t Stepper::isr_scheduler() {
// Compute the tick count for the next ISR // Compute the tick count for the next ISR
next_isr_ticks += interval; next_isr_ticks += interval;
/**
* The following section must be done with global interrupts disabled.
* We want nothing to interrupt it, as that could mess the calculations
* we do for the next value to program in the period register of the
* stepper timer and lead to skipped ISRs (if the value we happen to program
* is less than the current count due to something preempting between the
* read and the write of the new period value).
*/
DISABLE_ISRS();
/** /**
* Get the current tick value + margin * Get the current tick value + margin
* Assuming at least 6µs between calls to this ISR... * Assuming at least 6µs between calls to this ISR...
@ -1227,8 +1229,14 @@ hal_timer_t Stepper::isr_scheduler() {
// Advance pulses if not enough time to wait for the next ISR // Advance pulses if not enough time to wait for the next ISR
} while (next_isr_ticks < min_ticks); } while (next_isr_ticks < min_ticks);
// Return the count of ticks for the next ISR // Now 'next_isr_ticks' contains the period to the next Stepper ISR - And we are
return (hal_timer_t)next_isr_ticks; // sure that the time has not arrived yet - Warrantied by the scheduler
// Set the next ISR to fire at the proper time
HAL_timer_set_compare(STEP_TIMER_NUM, hal_timer_t(next_isr_ticks));
// Don't forget to finally reenable interrupts
ENABLE_ISRS();
} }
/** /**

View File

@ -104,13 +104,13 @@ class Stepper {
static bool abort_current_block; // Signals to the stepper that current block should be aborted static bool abort_current_block; // Signals to the stepper that current block should be aborted
#if ENABLED(X_DUAL_ENDSTOPS) #if ENABLED(X_DUAL_ENDSTOPS)
static bool locked_x_motor, locked_x2_motor; static bool locked_X_motor, locked_X2_motor;
#endif #endif
#if ENABLED(Y_DUAL_ENDSTOPS) #if ENABLED(Y_DUAL_ENDSTOPS)
static bool locked_y_motor, locked_y2_motor; static bool locked_Y_motor, locked_Y2_motor;
#endif #endif
#if ENABLED(Z_DUAL_ENDSTOPS) #if ENABLED(Z_DUAL_ENDSTOPS)
static bool locked_z_motor, locked_z2_motor; static bool locked_Z_motor, locked_Z2_motor;
#endif #endif
// Counter variables for the Bresenham line tracer // Counter variables for the Bresenham line tracer
@ -189,7 +189,7 @@ class Stepper {
// Interrupt Service Routines // Interrupt Service Routines
// The ISR scheduler // The ISR scheduler
static hal_timer_t isr_scheduler(); static void isr();
// The stepper pulse phase ISR // The stepper pulse phase ISR
static void stepper_pulse_phase_isr(); static void stepper_pulse_phase_isr();
@ -243,18 +243,18 @@ class Stepper {
#if ENABLED(X_DUAL_ENDSTOPS) #if ENABLED(X_DUAL_ENDSTOPS)
FORCE_INLINE static void set_homing_flag_x(const bool state) { performing_homing = state; } FORCE_INLINE static void set_homing_flag_x(const bool state) { performing_homing = state; }
FORCE_INLINE static void set_x_lock(const bool state) { locked_x_motor = state; } FORCE_INLINE static void set_x_lock(const bool state) { locked_X_motor = state; }
FORCE_INLINE static void set_x2_lock(const bool state) { locked_x2_motor = state; } FORCE_INLINE static void set_x2_lock(const bool state) { locked_X2_motor = state; }
#endif #endif
#if ENABLED(Y_DUAL_ENDSTOPS) #if ENABLED(Y_DUAL_ENDSTOPS)
FORCE_INLINE static void set_homing_flag_y(const bool state) { performing_homing = state; } FORCE_INLINE static void set_homing_flag_y(const bool state) { performing_homing = state; }
FORCE_INLINE static void set_y_lock(const bool state) { locked_y_motor = state; } FORCE_INLINE static void set_y_lock(const bool state) { locked_Y_motor = state; }
FORCE_INLINE static void set_y2_lock(const bool state) { locked_y2_motor = state; } FORCE_INLINE static void set_y2_lock(const bool state) { locked_Y2_motor = state; }
#endif #endif
#if ENABLED(Z_DUAL_ENDSTOPS) #if ENABLED(Z_DUAL_ENDSTOPS)
FORCE_INLINE static void set_homing_flag_z(const bool state) { performing_homing = state; } FORCE_INLINE static void set_homing_flag_z(const bool state) { performing_homing = state; }
FORCE_INLINE static void set_z_lock(const bool state) { locked_z_motor = state; } FORCE_INLINE static void set_z_lock(const bool state) { locked_Z_motor = state; }
FORCE_INLINE static void set_z2_lock(const bool state) { locked_z2_motor = state; } FORCE_INLINE static void set_z2_lock(const bool state) { locked_Z2_motor = state; }
#endif #endif
#if ENABLED(BABYSTEPPING) #if ENABLED(BABYSTEPPING)
@ -268,16 +268,18 @@ class Stepper {
// Set the current position in steps // Set the current position in steps
inline static void set_position(const int32_t &a, const int32_t &b, const int32_t &c, const int32_t &e) { inline static void set_position(const int32_t &a, const int32_t &b, const int32_t &c, const int32_t &e) {
planner.synchronize(); planner.synchronize();
CRITICAL_SECTION_START; const bool was_enabled = STEPPER_ISR_ENABLED();
if (was_enabled) DISABLE_STEPPER_DRIVER_INTERRUPT();
_set_position(a, b, c, e); _set_position(a, b, c, e);
CRITICAL_SECTION_END; if (was_enabled) ENABLE_STEPPER_DRIVER_INTERRUPT();
} }
inline static void set_position(const AxisEnum a, const int32_t &v) { inline static void set_position(const AxisEnum a, const int32_t &v) {
planner.synchronize(); planner.synchronize();
CRITICAL_SECTION_START; const bool was_enabled = STEPPER_ISR_ENABLED();
if (was_enabled) DISABLE_STEPPER_DRIVER_INTERRUPT();
count_position[a] = v; count_position[a] = v;
CRITICAL_SECTION_END; if (was_enabled) ENABLE_STEPPER_DRIVER_INTERRUPT();
} }
private: private: