[2.0.x] Small assorted collection of fixes and improvements (#10911)

* Misc fixes and improvements

- Get rid of most critical sections on the Serial port drivers for AVR and DUE. Proper usage of FIFOs should allow interrupts to stay enabled without harm to queuing and dequeuing.
  Also, with 8-bit indices (for AVR) and up to 32-bit indices (for ARM), there is no need to protect reads and writes to those indices.
- Simplify the XON/XOFF logic quite a bit. Much cleaner now (both for AVR and ARM)
- Prevent a race condition (edge case) that could happen when estimating the proper value for the stepper timer (by reading it) and writing the calculated value for the time to the next ISR by disabling interrupts in those critical and small sections of the code - The problem could lead to lost steps.
- Fix dual endstops not properly homing bug (maybe).

* Set position immediately when possible
This commit is contained in:
Eduardo José Tagle 2018-06-01 21:02:22 -03:00 committed by Scott Lahteine
parent ae1be0fa53
commit d3c02410a8
16 changed files with 328 additions and 285 deletions

View file

@ -64,7 +64,9 @@
#define CRITICAL_SECTION_START unsigned char _sreg = SREG; cli(); #define CRITICAL_SECTION_START unsigned char _sreg = SREG; cli();
#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()
// On AVR this is in math.h? // On AVR this is in math.h?
//#define square(x) ((x)*(x)) //#define square(x) ((x)*(x))
@ -181,7 +183,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 */ \
@ -291,7 +292,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

@ -69,8 +69,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
@ -79,10 +77,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 "../../feature/emergency_parser.h" #include "../../feature/emergency_parser.h"
#endif #endif
// (called with RX interrupts disabled)
FORCE_INLINE void store_rxd_char() { FORCE_INLINE void store_rxd_char() {
#if ENABLED(EMERGENCY_PARSER) #if ENABLED(EMERGENCY_PARSER)
@ -129,18 +131,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;
} }
@ -153,8 +159,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
@ -170,6 +182,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.
@ -251,116 +264,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.head == t)
v = -1;
else {
v = rx_buffer.buffer[t];
rx_buffer.tail = (ring_buffer_pos_t)(t + 1) & (RX_BUFFER_SIZE - 1);
#if ENABLED(SERIAL_XON_XOFF) #if RX_BUFFER_SIZE > 256
if ((xon_xoff_state & XON_XOFF_CHAR_MASK) == XOFF_CHAR) { // Disable RX interrupts to ensure atomic reads
// Get count of bytes in the RX buffer const bool isr_enabled = TEST(M_UCSRxB, M_RXCIEx);
ring_buffer_pos_t rx_count = (ring_buffer_pos_t)(rx_buffer.head - rx_buffer.tail) & (ring_buffer_pos_t)(RX_BUFFER_SIZE - 1); CBI(M_UCSRxB, M_RXCIEx);
// When below 10% of RX buffer capacity, send XON before #endif
// running out of RX buffer bytes
if (rx_count < (RX_BUFFER_SIZE) / 10) { const ring_buffer_pos_t h = rx_buffer.head;
xon_xoff_state = XON_CHAR | XON_XOFF_CHAR_SENT;
CRITICAL_SECTION_END; // End critical section before returning! #if RX_BUFFER_SIZE > 256
writeNoHandshake(XON_CHAR); // End critical section
return v; if (isr_enabled) SBI(M_UCSRxB, M_RXCIEx);
} #endif
ring_buffer_pos_t t = rx_buffer.tail;
if (h == t)
v = -1;
else {
v = rx_buffer.buffer[t];
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 ((xon_xoff_state & XON_XOFF_CHAR_MASK) == XOFF_CHAR) {
// Get count of bytes in the RX buffer
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
// running out of RX buffer bytes
if (rx_count < (RX_BUFFER_SIZE) / 10) {
xon_xoff_state = XON_CHAR | XON_XOFF_CHAR_SENT;
write(XON_CHAR);
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); // If the TX interrupts are disabled and the data register
CRITICAL_SECTION_END; // is empty, just write the byte to the data register and
// If the buffer and the data register is empty, just write the byte // be done. This shortcut helps significantly improve the
// to the data register and be done. This shortcut helps // effective datarate at high (>500kbit/s) bitrates, where
// significantly improve the effective datarate at high (> // interrupt overhead becomes a slowdown.
// 500kbit/s) bitrates, where interrupt overhead becomes a slowdown. if (!TEST(M_UCSRxB, M_UDRIEx) && TEST(M_UCSRxA, M_UDREx)) {
if (emty && TEST(M_UCSRxA, M_UDREx)) { M_UDRx = c;
CRITICAL_SECTION_START;
M_UDRx = c; // clear the TXC bit -- "can be cleared by writing a one to its bit
SBI(M_UCSRxA, M_TXCx); // location". This makes sure flush() won't return until the bytes
CRITICAL_SECTION_END; // actually got written
SBI(M_UCSRxA, M_TXCx);
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
@ -368,17 +404,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;
SBI(M_UCSRxB, M_UDRIEx); // Enable TX isr
CRITICAL_SECTION_END; SBI(M_UCSRxB, M_UDRIEx);
}
return; return;
} }
@ -391,33 +428,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(const uint8_t c) {
while (!TEST(M_UCSRxA, M_UDREx)) { /* nada */ }
M_UDRx = c; M_UDRx = c;
} }

View file

@ -94,7 +94,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() {};
@ -104,13 +104,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

@ -55,8 +55,11 @@
#define analogInputToDigitalPin(p) ((p < 12u) ? (p) + 54u : -1) #define analogInputToDigitalPin(p) ((p < 12u) ? (p) + 54u : -1)
#endif #endif
#define CRITICAL_SECTION_START uint32_t primask = __get_PRIMASK(); __disable_irq(); #define CRITICAL_SECTION_START uint32_t primask = __get_PRIMASK(); __disable_irq()
#define CRITICAL_SECTION_END if (!primask) __enable_irq(); #define CRITICAL_SECTION_END if (!primask) __enable_irq()
#define ISRS_ENABLED() (!__get_PRIMASK())
#define ENABLE_ISRS() __enable_irq()
#define DISABLE_ISRS() __disable_irq()
// On AVR this is in math.h? // On AVR this is in math.h?
#define square(x) ((x)*(x)) #define square(x) ((x)*(x))

View file

@ -154,12 +154,14 @@
// 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 (!(HWUART->UART_IMR & UART_IMR_TXRDY) && (HWUART->UART_SR & UART_SR_TXRDY)) { if (!(HWUART->UART_IMR & UART_IMR_TXRDY) && (HWUART->UART_SR & UART_SR_TXRDY)) {
// Send an XOFF character // Send an XOFF character
HWUART->UART_THR = XOFF_CHAR; HWUART->UART_THR = XOFF_CHAR;
@ -175,8 +177,9 @@
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 (!(HWUART->UART_SR & UART_SR_TXRDY)) { sw_barrier(); }; while (!(HWUART->UART_SR & UART_SR_TXRDY)) sw_barrier();
HWUART->UART_THR = XOFF_CHAR; HWUART->UART_THR = XOFF_CHAR;
// 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
@ -303,116 +306,81 @@
pmc_disable_periph_clk( HWUART_IRQ_ID ); pmc_disable_periph_clk( HWUART_IRQ_ID );
} }
void MarlinSerial::checkRx(void) {
if (HWUART->UART_SR & UART_SR_RXRDY) {
CRITICAL_SECTION_START;
store_rxd_char();
CRITICAL_SECTION_END;
}
}
int MarlinSerial::peek(void) { int MarlinSerial::peek(void) {
CRITICAL_SECTION_START;
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;
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; const ring_buffer_pos_t h = rx_buffer.head;
if (rx_buffer.head == t) 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);
// Advance tail
rx_buffer.tail = t;
#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;
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;
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 rx_buffer.tail = rx_buffer.head;
// reading rx_buffer_head and updating rx_buffer_tail, the previous rx_buffer_head
// may be written to rx_buffer_tail, making the buffer appear full rather than empty.
CRITICAL_SECTION_START;
rx_buffer.head = rx_buffer.tail;
CRITICAL_SECTION_END;
#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); // If the TX interrupts are disabled and the data register
CRITICAL_SECTION_END; // is empty, just write the byte to the data register and
// If the buffer and the data register is empty, just write the byte // be done. This shortcut helps significantly improve the
// to the data register and be done. This shortcut helps // effective datarate at high (>500kbit/s) bitrates, where
// significantly improve the effective datarate at high (> // interrupt overhead becomes a slowdown.
// 500kbit/s) bitrates, where interrupt overhead becomes a slowdown. if (!(HWUART->UART_IMR & UART_IMR_TXRDY) && (HWUART->UART_SR & UART_SR_TXRDY)) {
if (emty && (HWUART->UART_SR & UART_SR_TXRDY)) { HWUART->UART_THR = c;
CRITICAL_SECTION_START;
HWUART->UART_THR = c;
HWUART->UART_IER = UART_IER_TXRDY;
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 (__get_PRIMASK()) { 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
@ -420,31 +388,30 @@
if (HWUART->UART_SR & UART_SR_TXRDY) if (HWUART->UART_SR & UART_SR_TXRDY)
_tx_thr_empty_irq(); _tx_thr_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(); sw_barrier();
} }
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;
HWUART->UART_IER = UART_IER_TXRDY; // Enable TX isr
CRITICAL_SECTION_END; HWUART->UART_IER = UART_IER_TXRDY;
}
return; return;
} }
void MarlinSerial::flushTX(void) { void MarlinSerial::flushTX(void) {
// TX // TX
// If we have never written a byte, no need to flush. // If we have never written a byte, no need to flush.
if (!_written) if (!_written) return;
return;
while ((HWUART->UART_IMR & UART_IMR_TXRDY) || !(HWUART->UART_SR & UART_SR_TXEMPTY)) { while ((HWUART->UART_IMR & UART_IMR_TXRDY) || !(HWUART->UART_SR & UART_SR_TXEMPTY)) {
if (__get_PRIMASK()) if (!ISRS_ENABLED()) {
if ((HWUART->UART_SR & UART_SR_TXRDY)) if (HWUART->UART_SR & UART_SR_TXRDY)
_tx_thr_empty_irq(); _tx_thr_empty_irq();
}
sw_barrier(); sw_barrier();
} }
// If we get here, nothing is queued anymore (TX interrupts are disabled) and // If we get here, nothing is queued anymore (TX interrupts are disabled) and
@ -454,19 +421,7 @@
#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 (!(HWUART->UART_SR & UART_SR_TXRDY)) 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(const uint8_t c) {
while (!(HWUART->UART_SR & UART_SR_TXRDY)) { sw_barrier(); };
HWUART->UART_THR = c; HWUART->UART_THR = c;
} }

View file

@ -84,13 +84,10 @@ public:
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

@ -120,8 +120,11 @@ extern HalSerial usb_serial;
#define NUM_SERIAL 1 #define NUM_SERIAL 1
#endif #endif
#define CRITICAL_SECTION_START uint32_t primask = __get_PRIMASK(); __disable_irq(); #define CRITICAL_SECTION_START uint32_t primask = __get_PRIMASK(); __disable_irq()
#define CRITICAL_SECTION_END if (!primask) __enable_irq(); #define CRITICAL_SECTION_END if (!primask) __enable_irq()
#define ISRS_ENABLED() (!__get_PRIMASK())
#define ENABLE_ISRS() __enable_irq()
#define DISABLE_ISRS() __disable_irq()
//Utility functions //Utility functions
int freeMemory(void); int freeMemory(void);

View file

@ -119,8 +119,11 @@ void HAL_init();
#define analogInputToDigitalPin(p) (p) #define analogInputToDigitalPin(p) (p)
#endif #endif
#define CRITICAL_SECTION_START noInterrupts(); #define CRITICAL_SECTION_START uint32_t primask = __get_PRIMASK(); __disable_irq()
#define CRITICAL_SECTION_END interrupts(); #define CRITICAL_SECTION_END if (!primask) __enable_irq()
#define ISRS_ENABLED() (!__get_PRIMASK())
#define ENABLE_ISRS() __enable_irq()
#define DISABLE_ISRS() __disable_irq()
// On AVR this is in math.h? // On AVR this is in math.h?
#define square(x) ((x)*(x)) #define square(x) ((x)*(x))

View file

@ -118,8 +118,11 @@
#define analogInputToDigitalPin(p) (p) #define analogInputToDigitalPin(p) (p)
#endif #endif
#define CRITICAL_SECTION_START noInterrupts(); #define CRITICAL_SECTION_START uint32_t primask = __get_PRIMASK(); __disable_irq()
#define CRITICAL_SECTION_END interrupts(); #define CRITICAL_SECTION_END if (!primask) __enable_irq()
#define ISRS_ENABLED() (!__get_PRIMASK())
#define ENABLE_ISRS() __enable_irq()
#define DISABLE_ISRS() __disable_irq()
// On AVR this is in math.h? // On AVR this is in math.h?
#define square(x) ((x)*(x)) #define square(x) ((x)*(x))

View file

@ -111,8 +111,11 @@
#define analogInputToDigitalPin(p) (p) #define analogInputToDigitalPin(p) (p)
#endif #endif
#define CRITICAL_SECTION_START noInterrupts(); #define CRITICAL_SECTION_START uint32_t primask = __get_PRIMASK(); __disable_irq()
#define CRITICAL_SECTION_END interrupts(); #define CRITICAL_SECTION_END if (!primask) __enable_irq()
#define ISRS_ENABLED() (!__get_PRIMASK())
#define ENABLE_ISRS() __enable_irq()
#define DISABLE_ISRS() __disable_irq()
// On AVR this is in math.h? // On AVR this is in math.h?
#define square(x) ((x)*(x)) #define square(x) ((x)*(x))

View file

@ -88,8 +88,11 @@ typedef int8_t pin_t;
#define analogInputToDigitalPin(p) ((p < 12u) ? (p) + 54u : -1) #define analogInputToDigitalPin(p) ((p < 12u) ? (p) + 54u : -1)
#endif #endif
#define CRITICAL_SECTION_START unsigned char _sreg = SREG; cli(); #define CRITICAL_SECTION_START uint32_t primask = __get_PRIMASK(); __disable_irq()
#define CRITICAL_SECTION_END SREG = _sreg; #define CRITICAL_SECTION_END if (!primask) __enable_irq()
#define ISRS_ENABLED() (!__get_PRIMASK())
#define ENABLE_ISRS() __enable_irq()
#define DISABLE_ISRS() __disable_irq()
#undef sq #undef sq
#define sq(x) ((x)*(x)) #define sq(x) ((x)*(x))

View file

@ -578,19 +578,22 @@ void Endstops::update() {
// Call the endstop triggered routine for single endstops // Call the endstop triggered routine for single endstops
#define PROCESS_ENDSTOP(AXIS,MINMAX) do { \ #define PROCESS_ENDSTOP(AXIS,MINMAX) do { \
if (TEST_ENDSTOP(_ENDSTOP(AXIS, MINMAX))) { \ if (TEST_ENDSTOP(_ENDSTOP(AXIS, MINMAX))) { \
_ENDSTOP_HIT(AXIS, MINMAX); \ _ENDSTOP_HIT(AXIS, MINMAX); \
planner.endstop_triggered(_AXIS(AXIS)); \ planner.endstop_triggered(_AXIS(AXIS)); \
} \ } \
}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); \
_ENDSTOP_HIT(AXIS1, MINMAX); \ if (dual_hit) { \
_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)
#if ENABLED(G38_PROBE_TARGET) && PIN_EXISTS(Z_MIN_PROBE) && !(CORE_IS_XY || CORE_IS_XZ) #if ENABLED(G38_PROBE_TARGET) && PIN_EXISTS(Z_MIN_PROBE) && !(CORE_IS_XY || CORE_IS_XZ)
// If G38 command is active check Z_MIN_PROBE for ALL movement // If G38 command is active check Z_MIN_PROBE for ALL movement

View file

@ -108,7 +108,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

@ -2467,9 +2467,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.
buffer_sync_block(); //ZERO(previous_speed);
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]) {
@ -2501,8 +2505,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()) {
buffer_sync_block(); //previous_speed[axis] = 0.0;
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

@ -102,13 +102,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
/** /**
@ -182,26 +182,20 @@ 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 DUAL_ENDSTOP_APPLY_STEP(A,V) \
#define LOCKED_Y_MOTOR locked_y_motor if (performing_homing) { \
#define LOCKED_Z_MOTOR locked_z_motor if (A##_HOME_DIR < 0) { \
#define LOCKED_X2_MOTOR locked_x2_motor if (!(TEST(endstops.state(), A##_MIN) && count_direction[_AXIS(A)] < 0) && !locked_##A##_motor) A##_STEP_WRITE(V); \
#define LOCKED_Y2_MOTOR locked_y2_motor if (!(TEST(endstops.state(), A##2_MIN) && count_direction[_AXIS(A)] < 0) && !locked_##A##2_motor) A##2_STEP_WRITE(V); \
#define LOCKED_Z2_MOTOR locked_z2_motor } \
#define DUAL_ENDSTOP_APPLY_STEP(A,V) \ else { \
if (performing_homing) { \ if (!(TEST(endstops.state(), A##_MAX) && count_direction[_AXIS(A)] > 0) && !locked_##A##_motor) A##_STEP_WRITE(V); \
if (A##_HOME_DIR < 0) { \ 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##_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); \ } \
} \ else { \
else { \ A##_STEP_WRITE(V); \
if (!(TEST(endstops.state(), A##_MAX) && count_direction[_AXIS(A)] > 0) && !LOCKED_##A##_MOTOR) A##_STEP_WRITE(V); \ 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 { \
A##_STEP_WRITE(V); \
A##2_STEP_WRITE(V); \
} }
#endif #endif
@ -1150,19 +1144,8 @@ 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 // Call the ISR
// flag an interrupt while this ISR is running - So changes from small Stepper::isr();
// 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);
} }
@ -1173,8 +1156,15 @@ HAL_STEP_TIMER_ISR {
#define STEP_MULTIPLY(A,B) MultiU24X32toH16(A, B) #define STEP_MULTIPLY(A,B) MultiU24X32toH16(A, B)
#endif #endif
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;
@ -1185,6 +1175,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 so the USART processing latency is reduced
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();
@ -1198,13 +1191,15 @@ 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();
#if ENABLED(LIN_ADVANCE) uint32_t interval =
// Select the closest interval in time #if ENABLED(LIN_ADVANCE)
interval = (nextAdvanceISR <= nextMainISR) ? nextAdvanceISR : nextMainISR; // Select the closest interval in time
#else MIN(nextAdvanceISR, nextMainISR)
// The interval is just the remaining time to the stepper ISR #else
interval = nextMainISR; // The interval is just the remaining time to the stepper ISR
#endif nextMainISR
#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);
@ -1243,6 +1238,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...
@ -1264,8 +1269,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

@ -81,13 +81,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
@ -168,7 +168,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();
@ -222,18 +222,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)
@ -247,16 +247,34 @@ 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;
// Disable stepper interrupts, to ensure atomic setting of all the position variables
const bool was_enabled = STEPPER_ISR_ENABLED();
if (was_enabled) DISABLE_STEPPER_DRIVER_INTERRUPT();
// Set position
_set_position(a, b, c, e); _set_position(a, b, c, e);
CRITICAL_SECTION_END;
// Reenable Stepper ISR
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;
#ifdef __AVR__
// Protect the access to the position. Only required for AVR, as
// any 32bit CPU offers atomic access to 32bit variables
const bool was_enabled = STEPPER_ISR_ENABLED();
if (was_enabled) DISABLE_STEPPER_DRIVER_INTERRUPT();
#endif
count_position[a] = v; count_position[a] = v;
CRITICAL_SECTION_END;
#ifdef __AVR__
// Reenable Stepper ISR
if (was_enabled) ENABLE_STEPPER_DRIVER_INTERRUPT();
#endif
} }
private: private: