[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

@ -55,8 +55,11 @@
#define analogInputToDigitalPin(p) ((p < 12u) ? (p) + 54u : -1)
#endif
#define CRITICAL_SECTION_START uint32_t primask = __get_PRIMASK(); __disable_irq();
#define CRITICAL_SECTION_END if (!primask) __enable_irq();
#define CRITICAL_SECTION_START uint32_t primask = __get_PRIMASK(); __disable_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?
#define square(x) ((x)*(x))

View File

@ -154,12 +154,14 @@
// let the host react and stop sending bytes. This translates to 13mS
// propagation time.
if (rx_count >= (RX_BUFFER_SIZE) / 8) {
// If TX interrupts are disabled and data register is empty,
// just write the byte to the data register and be done. This
// shortcut helps significantly improve the effective datarate
// at high (>500kbit/s) bitrates, where interrupt overhead
// becomes a slowdown.
if (!(HWUART->UART_IMR & UART_IMR_TXRDY) && (HWUART->UART_SR & UART_SR_TXRDY)) {
// Send an XOFF character
HWUART->UART_THR = XOFF_CHAR;
@ -175,8 +177,9 @@
xon_xoff_state = XOFF_CHAR;
#else
// 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;
// And remember we already sent it
xon_xoff_state = XOFF_CHAR | XON_XOFF_CHAR_SENT;
#endif
@ -303,116 +306,81 @@
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) {
CRITICAL_SECTION_START;
const int v = rx_buffer.head == rx_buffer.tail ? -1 : rx_buffer.buffer[rx_buffer.tail];
CRITICAL_SECTION_END;
return v;
}
int MarlinSerial::read(void) {
int v;
CRITICAL_SECTION_START;
const ring_buffer_pos_t t = rx_buffer.tail;
if (rx_buffer.head == t)
const ring_buffer_pos_t h = rx_buffer.head;
ring_buffer_pos_t t = rx_buffer.tail;
if (h == t)
v = -1;
else {
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 ((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)(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
// running out of RX buffer bytes
if (rx_count < (RX_BUFFER_SIZE) / 10) {
xon_xoff_state = XON_CHAR | XON_XOFF_CHAR_SENT;
CRITICAL_SECTION_END; // End critical section before returning!
writeNoHandshake(XON_CHAR);
write(XON_CHAR);
return v;
}
}
#endif
}
CRITICAL_SECTION_END;
return v;
}
ring_buffer_pos_t MarlinSerial::available(void) {
CRITICAL_SECTION_START;
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);
}
void MarlinSerial::flush(void) {
// Don't change this order of operations. If the RX interrupt occurs between
// 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;
rx_buffer.tail = rx_buffer.head;
#if ENABLED(SERIAL_XON_XOFF)
if ((xon_xoff_state & XON_XOFF_CHAR_MASK) == XOFF_CHAR) {
xon_xoff_state = XON_CHAR | XON_XOFF_CHAR_SENT;
writeNoHandshake(XON_CHAR);
write(XON_CHAR);
}
#endif
}
#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) {
#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;
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
// to the data register and be done. This shortcut helps
// significantly improve the effective datarate at high (>
// 500kbit/s) bitrates, where interrupt overhead becomes a slowdown.
if (emty && (HWUART->UART_SR & UART_SR_TXRDY)) {
CRITICAL_SECTION_START;
HWUART->UART_THR = c;
HWUART->UART_IER = UART_IER_TXRDY;
CRITICAL_SECTION_END;
// If the TX interrupts are disabled and the data register
// is empty, just write the byte to the data register and
// be done. This shortcut helps significantly improve the
// effective datarate at high (>500kbit/s) bitrates, where
// interrupt overhead becomes a slowdown.
if (!(HWUART->UART_IMR & UART_IMR_TXRDY) && (HWUART->UART_SR & UART_SR_TXRDY)) {
HWUART->UART_THR = c;
return;
}
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
// wait for the interrupt handler to empty it a bit
while (i == tx_buffer.tail) {
if (__get_PRIMASK()) {
if (!ISRS_ENABLED()) {
// Interrupts are disabled, so we'll have to poll the data
// register empty flag ourselves. If it is set, pretend an
// interrupt has happened and call the handler to free up
@ -420,31 +388,30 @@
if (HWUART->UART_SR & UART_SR_TXRDY)
_tx_thr_empty_irq();
}
else {
// nop, the interrupt handler will free up space for us
}
// (else , the interrupt handler will free up space for us)
// Make sure compiler rereads tx_buffer.tail
sw_barrier();
}
tx_buffer.buffer[tx_buffer.head] = c;
{ CRITICAL_SECTION_START;
tx_buffer.head = i;
HWUART->UART_IER = UART_IER_TXRDY;
CRITICAL_SECTION_END;
}
tx_buffer.head = i;
// Enable TX isr
HWUART->UART_IER = UART_IER_TXRDY;
return;
}
void MarlinSerial::flushTX(void) {
// TX
// If we have never written a byte, no need to flush.
if (!_written)
return;
if (!_written) return;
while ((HWUART->UART_IMR & UART_IMR_TXRDY) || !(HWUART->UART_SR & UART_SR_TXEMPTY)) {
if (__get_PRIMASK())
if ((HWUART->UART_SR & UART_SR_TXRDY))
if (!ISRS_ENABLED()) {
if (HWUART->UART_SR & UART_SR_TXRDY)
_tx_thr_empty_irq();
}
sw_barrier();
}
// If we get here, nothing is queued anymore (TX interrupts are disabled) and
@ -454,19 +421,7 @@
#else // TX_BUFFER_SIZE == 0
void MarlinSerial::write(const uint8_t c) {
#if ENABLED(SERIAL_XON_XOFF)
// 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(); };
while (!(HWUART->UART_SR & UART_SR_TXRDY)) sw_barrier();
HWUART->UART_THR = c;
}

View File

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