2016-03-25 07:19:46 +01:00
|
|
|
/**
|
2016-03-24 19:01:20 +01:00
|
|
|
* Marlin 3D Printer Firmware
|
|
|
|
* Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
|
|
|
*
|
|
|
|
* Based on Sprinter and grbl.
|
|
|
|
* Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
|
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2016-03-25 07:19:46 +01:00
|
|
|
/**
|
2016-07-26 05:06:00 +02:00
|
|
|
* MarlinSerial.cpp - Hardware serial library for Wiring
|
|
|
|
* Copyright (c) 2006 Nicholas Zambetti. All right reserved.
|
|
|
|
*
|
|
|
|
* Modified 23 November 2006 by David A. Mellis
|
|
|
|
* Modified 28 September 2010 by Mark Sproul
|
|
|
|
* Modified 14 February 2016 by Andreas Hardtung (added tx buffer)
|
2017-10-02 07:26:56 +02:00
|
|
|
* Modified 01 October 2017 by Eduardo José Tagle (added XON/XOFF)
|
2016-07-26 05:06:00 +02:00
|
|
|
*/
|
2016-08-03 04:36:58 +02:00
|
|
|
|
2017-04-02 07:47:20 +02:00
|
|
|
// Disable HardwareSerial.cpp to support chips without a UART (Attiny, etc.)
|
2011-11-27 21:12:55 +01:00
|
|
|
|
2017-10-02 07:26:56 +02:00
|
|
|
#include "MarlinConfig.h"
|
|
|
|
|
2018-05-24 12:47:55 +02:00
|
|
|
#if USE_MARLINSERIAL && (defined(UBRRH) || defined(UBRR0H) || defined(UBRR1H) || defined(UBRR2H) || defined(UBRR3H))
|
2017-04-02 07:47:20 +02:00
|
|
|
|
2017-10-02 07:26:56 +02:00
|
|
|
#include "MarlinSerial.h"
|
|
|
|
#include "Marlin.h"
|
|
|
|
|
|
|
|
struct ring_buffer_r {
|
|
|
|
unsigned char buffer[RX_BUFFER_SIZE];
|
|
|
|
volatile ring_buffer_pos_t head, tail;
|
|
|
|
};
|
|
|
|
|
|
|
|
#if TX_BUFFER_SIZE > 0
|
|
|
|
struct ring_buffer_t {
|
|
|
|
unsigned char buffer[TX_BUFFER_SIZE];
|
|
|
|
volatile uint8_t head, tail;
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
2017-04-02 07:47:20 +02:00
|
|
|
#if UART_PRESENT(SERIAL_PORT)
|
2017-04-02 11:24:28 +02:00
|
|
|
ring_buffer_r rx_buffer = { { 0 }, 0, 0 };
|
2017-04-02 07:47:20 +02:00
|
|
|
#if TX_BUFFER_SIZE > 0
|
2017-04-02 11:24:28 +02:00
|
|
|
ring_buffer_t tx_buffer = { { 0 }, 0, 0 };
|
2017-04-02 07:47:20 +02:00
|
|
|
#endif
|
2018-06-10 04:39:57 +02:00
|
|
|
static bool _written;
|
2016-07-08 17:25:21 +02:00
|
|
|
#endif
|
2011-11-27 21:12:55 +01:00
|
|
|
|
2017-08-08 07:46:37 +02:00
|
|
|
#if ENABLED(SERIAL_XON_XOFF)
|
2018-06-10 04:39:57 +02:00
|
|
|
constexpr uint8_t XON_XOFF_CHAR_SENT = 0x80, // XON / XOFF Character was sent
|
|
|
|
XON_XOFF_CHAR_MASK = 0x1F; // XON / XOFF character to send
|
2017-10-02 07:26:56 +02:00
|
|
|
// XON / XOFF character definitions
|
2018-06-10 04:39:57 +02:00
|
|
|
constexpr uint8_t XON_CHAR = 17, XOFF_CHAR = 19;
|
2017-10-04 19:10:15 +02:00
|
|
|
uint8_t xon_xoff_state = XON_XOFF_CHAR_SENT | XON_CHAR;
|
2017-08-08 07:46:37 +02:00
|
|
|
#endif
|
2017-10-02 07:26:56 +02:00
|
|
|
|
2017-08-08 07:46:37 +02:00
|
|
|
#if ENABLED(SERIAL_STATS_DROPPED_RX)
|
2017-10-02 07:26:56 +02:00
|
|
|
uint8_t rx_dropped_bytes = 0;
|
2017-08-08 07:46:37 +02:00
|
|
|
#endif
|
2017-10-02 07:26:56 +02:00
|
|
|
|
2017-08-08 07:46:37 +02:00
|
|
|
#if ENABLED(SERIAL_STATS_MAX_RX_QUEUED)
|
2017-10-02 07:26:56 +02:00
|
|
|
ring_buffer_pos_t rx_max_enqueued = 0;
|
|
|
|
#endif
|
2017-08-08 07:46:37 +02:00
|
|
|
|
2018-06-02 02:00:59 +02:00
|
|
|
// A SW memory barrier, to ensure GCC does not overoptimize loops
|
|
|
|
#define sw_barrier() asm volatile("": : :"memory");
|
|
|
|
|
2017-04-02 07:47:20 +02:00
|
|
|
#if ENABLED(EMERGENCY_PARSER)
|
2018-04-26 05:42:43 +02:00
|
|
|
#include "emergency_parser.h"
|
|
|
|
#endif
|
2016-08-19 23:53:20 +02:00
|
|
|
|
2018-06-02 02:00:59 +02:00
|
|
|
// (called with RX interrupts disabled)
|
2017-08-08 07:46:37 +02:00
|
|
|
FORCE_INLINE void store_rxd_char() {
|
2018-06-10 04:39:57 +02:00
|
|
|
|
|
|
|
// Get the tail - Nothing can alter its value while we are at this ISR
|
|
|
|
const ring_buffer_pos_t t = rx_buffer.tail;
|
|
|
|
|
|
|
|
// Get the head pointer
|
|
|
|
ring_buffer_pos_t h = rx_buffer.head;
|
|
|
|
|
|
|
|
// Get the next element
|
|
|
|
ring_buffer_pos_t i = (ring_buffer_pos_t)(h + 1) & (ring_buffer_pos_t)(RX_BUFFER_SIZE - 1);
|
|
|
|
|
|
|
|
// Read the character from the USART
|
|
|
|
uint8_t c = M_UDRx;
|
|
|
|
|
|
|
|
#if ENABLED(EMERGENCY_PARSER)
|
|
|
|
emergency_parser.update(c);
|
|
|
|
#endif
|
2017-04-02 07:47:20 +02:00
|
|
|
|
2017-10-02 07:26:56 +02:00
|
|
|
// If the character is to be stored at the index just before the tail
|
2018-06-10 04:39:57 +02:00
|
|
|
// (such that the head would advance to the current tail), the RX FIFO is
|
|
|
|
// full, so don't write the character or advance the head.
|
|
|
|
if (i != t) {
|
2017-10-14 22:54:17 +02:00
|
|
|
rx_buffer.buffer[h] = c;
|
2018-06-10 04:39:57 +02:00
|
|
|
h = i;
|
2017-10-02 07:26:56 +02:00
|
|
|
}
|
2018-06-10 04:39:57 +02:00
|
|
|
#if ENABLED(SERIAL_STATS_DROPPED_RX)
|
|
|
|
else if (!++rx_dropped_bytes) --rx_dropped_bytes;
|
|
|
|
#endif
|
2017-10-02 07:26:56 +02:00
|
|
|
|
|
|
|
#if ENABLED(SERIAL_STATS_MAX_RX_QUEUED)
|
2018-06-10 04:39:57 +02:00
|
|
|
// Calculate count of bytes stored into the RX buffer
|
|
|
|
const ring_buffer_pos_t rx_count = (ring_buffer_pos_t)(h - t) & (ring_buffer_pos_t)(RX_BUFFER_SIZE - 1);
|
|
|
|
|
2017-10-02 07:26:56 +02:00
|
|
|
// Keep track of the maximum count of enqueued bytes
|
|
|
|
NOLESS(rx_max_enqueued, rx_count);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if ENABLED(SERIAL_XON_XOFF)
|
2018-06-10 04:39:57 +02:00
|
|
|
// If the last char that was sent was an XON
|
2017-10-02 07:26:56 +02:00
|
|
|
if ((xon_xoff_state & XON_XOFF_CHAR_MASK) == XON_CHAR) {
|
|
|
|
|
2018-06-10 04:39:57 +02:00
|
|
|
// Bytes stored into the RX buffer
|
|
|
|
const ring_buffer_pos_t rx_count = (ring_buffer_pos_t)(h - t) & (ring_buffer_pos_t)(RX_BUFFER_SIZE - 1);
|
2017-10-02 07:26:56 +02:00
|
|
|
|
2018-06-10 04:39:57 +02:00
|
|
|
// If over 12.5% of RX buffer capacity, send XOFF before running out of
|
|
|
|
// RX buffer space .. 325 bytes @ 250kbits/s needed to let the host react
|
|
|
|
// and stop sending bytes. This translates to 13mS propagation time.
|
2017-10-02 07:26:56 +02:00
|
|
|
if (rx_count >= (RX_BUFFER_SIZE) / 8) {
|
2018-06-02 02:00:59 +02:00
|
|
|
|
2018-06-10 04:39:57 +02:00
|
|
|
// At this point, definitely no TX interrupt was executing, since the TX isr can't be preempted.
|
|
|
|
// Don't enable the TX interrupt here as a means to trigger the XOFF char, because if it happens
|
|
|
|
// to be in the middle of trying to disable the RX interrupt in the main program, eventually the
|
|
|
|
// enabling of the TX interrupt could be undone. The ONLY reliable thing this can do to ensure
|
|
|
|
// the sending of the XOFF char is to send it HERE AND NOW.
|
|
|
|
|
|
|
|
// About to send the XOFF char
|
|
|
|
xon_xoff_state = XOFF_CHAR | XON_XOFF_CHAR_SENT;
|
|
|
|
|
|
|
|
// Wait until the TX register becomes empty and send it - Here there could be a problem
|
|
|
|
// - While waiting for the TX register to empty, the RX register could receive a new
|
|
|
|
// character. This must also handle that situation!
|
|
|
|
while (!TEST(M_UCSRxA, M_UDREx)) {
|
|
|
|
|
|
|
|
if (TEST(M_UCSRxA,M_RXCx)) {
|
|
|
|
// A char arrived while waiting for the TX buffer to be empty - Receive and process it!
|
|
|
|
|
|
|
|
i = (ring_buffer_pos_t)(h + 1) & (ring_buffer_pos_t)(RX_BUFFER_SIZE - 1);
|
|
|
|
|
|
|
|
// Read the character from the USART
|
|
|
|
c = M_UDRx;
|
|
|
|
|
|
|
|
#if ENABLED(EMERGENCY_PARSER)
|
|
|
|
emergency_parser.update(c);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// If the character is to be stored at the index just before the tail
|
|
|
|
// (such that the head would advance to the current tail), the FIFO is
|
|
|
|
// full, so don't write the character or advance the head.
|
|
|
|
if (i != t) {
|
|
|
|
rx_buffer.buffer[h] = c;
|
|
|
|
h = i;
|
|
|
|
}
|
|
|
|
#if ENABLED(SERIAL_STATS_DROPPED_RX)
|
|
|
|
else if (!++rx_dropped_bytes) --rx_dropped_bytes;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
sw_barrier();
|
2017-10-02 07:26:56 +02:00
|
|
|
}
|
2018-06-10 04:39:57 +02:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
// At this point there could be a race condition between the write() function
|
|
|
|
// and this sending of the XOFF char. This interrupt could happen between the
|
|
|
|
// wait to be empty TX buffer loop and the actual write of the character. Since
|
|
|
|
// the TX buffer is full because it's sending the XOFF char, the only way to be
|
|
|
|
// sure the write() function will succeed is to wait for the XOFF char to be
|
|
|
|
// completely sent. Since an extra character could be received during the wait
|
|
|
|
// it must also be handled!
|
|
|
|
while (!TEST(M_UCSRxA, M_UDREx)) {
|
|
|
|
|
|
|
|
if (TEST(M_UCSRxA,M_RXCx)) {
|
|
|
|
// A char arrived while waiting for the TX buffer to be empty - Receive and process it!
|
|
|
|
|
|
|
|
i = (ring_buffer_pos_t)(h + 1) & (ring_buffer_pos_t)(RX_BUFFER_SIZE - 1);
|
|
|
|
|
|
|
|
// Read the character from the USART
|
|
|
|
c = M_UDRx;
|
|
|
|
|
|
|
|
#if ENABLED(EMERGENCY_PARSER)
|
|
|
|
emergency_parser.update(c);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// If the character is to be stored at the index just before the tail
|
|
|
|
// (such that the head would advance to the current tail), the FIFO is
|
|
|
|
// full, so don't write the character or advance the head.
|
|
|
|
if (i != t) {
|
|
|
|
rx_buffer.buffer[h] = c;
|
|
|
|
h = i;
|
|
|
|
}
|
|
|
|
#if ENABLED(SERIAL_STATS_DROPPED_RX)
|
|
|
|
else if (!++rx_dropped_bytes) --rx_dropped_bytes;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
sw_barrier();
|
2017-10-02 07:26:56 +02:00
|
|
|
}
|
2018-06-10 04:39:57 +02:00
|
|
|
|
|
|
|
// At this point everything is ready. The write() function won't
|
|
|
|
// have any issues writing to the UART TX register if it needs to!
|
2017-10-02 07:26:56 +02:00
|
|
|
}
|
2017-04-02 07:47:20 +02:00
|
|
|
}
|
2017-10-02 07:26:56 +02:00
|
|
|
#endif // SERIAL_XON_XOFF
|
2016-07-08 17:25:21 +02:00
|
|
|
|
2018-06-10 04:39:57 +02:00
|
|
|
// Store the new head value
|
|
|
|
rx_buffer.head = h;
|
2017-04-02 07:47:20 +02:00
|
|
|
}
|
2016-07-08 17:25:21 +02:00
|
|
|
|
2017-04-02 07:47:20 +02:00
|
|
|
#if TX_BUFFER_SIZE > 0
|
2016-07-08 17:25:21 +02:00
|
|
|
|
2018-06-02 02:00:59 +02:00
|
|
|
// (called with TX irqs disabled)
|
2017-04-02 07:47:20 +02:00
|
|
|
FORCE_INLINE void _tx_udr_empty_irq(void) {
|
2018-06-10 04:39:57 +02:00
|
|
|
|
|
|
|
// Read positions
|
|
|
|
uint8_t t = tx_buffer.tail;
|
|
|
|
const uint8_t h = tx_buffer.head;
|
2017-08-08 07:46:37 +02:00
|
|
|
|
2017-10-02 07:26:56 +02:00
|
|
|
#if ENABLED(SERIAL_XON_XOFF)
|
2018-06-10 04:39:57 +02:00
|
|
|
// If an XON char is pending to be sent, do it now
|
|
|
|
if (xon_xoff_state == XON_CHAR) {
|
|
|
|
|
|
|
|
// Send the character
|
|
|
|
M_UDRx = XON_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);
|
|
|
|
|
|
|
|
// Remember we sent it.
|
|
|
|
xon_xoff_state = XON_CHAR | XON_XOFF_CHAR_SENT;
|
|
|
|
|
|
|
|
// If nothing else to transmit, just disable TX interrupts.
|
|
|
|
if (h == t) CBI(M_UCSRxB, M_UDRIEx); // (Non-atomic, could be reenabled by the main program, but eventually this will succeed)
|
|
|
|
|
|
|
|
return;
|
2017-10-02 07:26:56 +02:00
|
|
|
}
|
|
|
|
#endif
|
2018-06-10 04:39:57 +02:00
|
|
|
|
|
|
|
// If nothing to transmit, just disable TX interrupts. This could
|
|
|
|
// happen as the result of the non atomicity of the disabling of RX
|
|
|
|
// interrupts that could end reenabling TX interrupts as a side effect.
|
|
|
|
if (h == t) {
|
|
|
|
CBI(M_UCSRxB, M_UDRIEx); // (Non-atomic, could be reenabled by the main program, but eventually this will succeed)
|
|
|
|
return;
|
2017-10-02 07:26:56 +02:00
|
|
|
}
|
2017-04-02 07:47:20 +02:00
|
|
|
|
2018-06-10 04:39:57 +02:00
|
|
|
// There is something to TX, Send the next byte
|
|
|
|
const uint8_t c = tx_buffer.buffer[t];
|
|
|
|
t = (t + 1) & (TX_BUFFER_SIZE - 1);
|
|
|
|
M_UDRx = c;
|
|
|
|
tx_buffer.tail = t;
|
|
|
|
|
|
|
|
// Clear the TXC bit (by writing a one to its bit location).
|
|
|
|
// Ensures flush() won't return until the bytes are actually written/
|
2017-04-02 07:47:20 +02:00
|
|
|
SBI(M_UCSRxA, M_TXCx);
|
|
|
|
|
2018-06-10 04:39:57 +02:00
|
|
|
// Disable interrupts if there is nothing to transmit following this byte
|
|
|
|
if (h == t) CBI(M_UCSRxB, M_UDRIEx); // (Non-atomic, could be reenabled by the main program, but eventually this will succeed)
|
2016-07-08 17:25:21 +02:00
|
|
|
}
|
|
|
|
|
2017-04-02 07:47:20 +02:00
|
|
|
#ifdef M_USARTx_UDRE_vect
|
2017-10-02 07:26:56 +02:00
|
|
|
ISR(M_USARTx_UDRE_vect) { _tx_udr_empty_irq(); }
|
2017-04-02 07:47:20 +02:00
|
|
|
#endif
|
2011-11-27 21:12:55 +01:00
|
|
|
|
2017-04-02 07:47:20 +02:00
|
|
|
#endif // TX_BUFFER_SIZE
|
2011-11-27 21:12:55 +01:00
|
|
|
|
2017-04-02 07:47:20 +02:00
|
|
|
#ifdef M_USARTx_RX_vect
|
2017-10-02 07:26:56 +02:00
|
|
|
ISR(M_USARTx_RX_vect) { store_rxd_char(); }
|
2017-04-02 07:47:20 +02:00
|
|
|
#endif
|
2011-11-27 21:12:55 +01:00
|
|
|
|
2017-04-02 07:47:20 +02:00
|
|
|
// Public Methods
|
2011-11-27 21:12:55 +01:00
|
|
|
|
2017-04-02 11:24:28 +02:00
|
|
|
void MarlinSerial::begin(const long baud) {
|
2017-04-02 07:47:20 +02:00
|
|
|
uint16_t baud_setting;
|
|
|
|
bool useU2X = true;
|
2011-11-27 21:12:55 +01:00
|
|
|
|
2017-04-02 07:47:20 +02:00
|
|
|
#if F_CPU == 16000000UL && SERIAL_PORT == 0
|
2017-10-02 07:26:56 +02:00
|
|
|
// Hard-coded exception for compatibility with the bootloader shipped
|
|
|
|
// with the Duemilanove and previous boards, and the firmware on the
|
|
|
|
// 8U2 on the Uno and Mega 2560.
|
2017-04-02 11:24:28 +02:00
|
|
|
if (baud == 57600) useU2X = false;
|
2017-04-02 07:47:20 +02:00
|
|
|
#endif
|
2011-11-27 21:12:55 +01:00
|
|
|
|
2017-04-02 07:47:20 +02:00
|
|
|
if (useU2X) {
|
|
|
|
M_UCSRxA = _BV(M_U2Xx);
|
|
|
|
baud_setting = (F_CPU / 4 / baud - 1) / 2;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
M_UCSRxA = 0;
|
|
|
|
baud_setting = (F_CPU / 8 / baud - 1) / 2;
|
2015-01-24 06:11:50 +01:00
|
|
|
}
|
2015-10-03 08:08:58 +02:00
|
|
|
|
2017-04-02 07:47:20 +02:00
|
|
|
// assign the baud_setting, a.k.a. ubbr (USART Baud Rate Register)
|
|
|
|
M_UBRRxH = baud_setting >> 8;
|
|
|
|
M_UBRRxL = baud_setting;
|
2011-11-27 21:12:55 +01:00
|
|
|
|
2017-04-02 07:47:20 +02:00
|
|
|
SBI(M_UCSRxB, M_RXENx);
|
|
|
|
SBI(M_UCSRxB, M_TXENx);
|
|
|
|
SBI(M_UCSRxB, M_RXCIEx);
|
|
|
|
#if TX_BUFFER_SIZE > 0
|
|
|
|
CBI(M_UCSRxB, M_UDRIEx);
|
|
|
|
#endif
|
2018-06-10 04:39:57 +02:00
|
|
|
_written = false;
|
2017-04-02 07:47:20 +02:00
|
|
|
}
|
2011-11-27 21:12:55 +01:00
|
|
|
|
2017-04-02 07:47:20 +02:00
|
|
|
void MarlinSerial::end() {
|
|
|
|
CBI(M_UCSRxB, M_RXENx);
|
|
|
|
CBI(M_UCSRxB, M_TXENx);
|
|
|
|
CBI(M_UCSRxB, M_RXCIEx);
|
2016-07-08 17:25:21 +02:00
|
|
|
CBI(M_UCSRxB, M_UDRIEx);
|
|
|
|
}
|
2017-04-02 07:47:20 +02:00
|
|
|
|
|
|
|
int MarlinSerial::peek(void) {
|
2018-06-02 02:00:59 +02:00
|
|
|
#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
|
2018-06-08 00:56:47 +02:00
|
|
|
|
|
|
|
const int v = rx_buffer.head == rx_buffer.tail ? -1 : rx_buffer.buffer[rx_buffer.tail];
|
|
|
|
|
2018-06-02 02:00:59 +02:00
|
|
|
#if RX_BUFFER_SIZE > 256
|
|
|
|
// Reenable RX interrupts if they were enabled
|
|
|
|
if (isr_enabled) SBI(M_UCSRxB, M_RXCIEx);
|
|
|
|
#endif
|
2017-04-02 07:47:20 +02:00
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
|
|
|
int MarlinSerial::read(void) {
|
2018-06-02 02:00:59 +02:00
|
|
|
|
|
|
|
#if RX_BUFFER_SIZE > 256
|
2018-06-10 04:39:57 +02:00
|
|
|
// Disable RX interrupts to ensure atomic reads - This could reenable TX interrupts,
|
|
|
|
// but this situation is explicitly handled at the TX isr, so no problems there
|
|
|
|
bool isr_enabled = TEST(M_UCSRxB, M_RXCIEx);
|
2018-06-02 02:00:59 +02:00
|
|
|
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;
|
|
|
|
|
2018-06-10 04:39:57 +02:00
|
|
|
// If nothing to read, return now
|
|
|
|
if (h == t) return -1;
|
2018-06-02 02:00:59 +02:00
|
|
|
|
2018-06-10 04:39:57 +02:00
|
|
|
// Get the next char
|
|
|
|
const int v = rx_buffer.buffer[t];
|
|
|
|
t = (ring_buffer_pos_t)(t + 1) & (RX_BUFFER_SIZE - 1);
|
2018-06-02 02:00:59 +02:00
|
|
|
|
2018-06-10 04:39:57 +02:00
|
|
|
#if RX_BUFFER_SIZE > 256
|
|
|
|
// Disable RX interrupts to ensure atomic write to tail, so
|
|
|
|
// the RX isr can't read partially updated values - This could
|
|
|
|
// reenable TX interrupts, but this situation is explicitly
|
|
|
|
// handled at the TX isr, so no problems there
|
|
|
|
isr_enabled = TEST(M_UCSRxB, M_RXCIEx);
|
|
|
|
CBI(M_UCSRxB, M_RXCIEx);
|
|
|
|
#endif
|
2018-06-02 02:00:59 +02:00
|
|
|
|
2018-06-10 04:39:57 +02:00
|
|
|
// Advance tail
|
|
|
|
rx_buffer.tail = t;
|
2018-06-02 02:00:59 +02:00
|
|
|
|
2018-06-10 04:39:57 +02:00
|
|
|
#if RX_BUFFER_SIZE > 256
|
|
|
|
// End critical section
|
|
|
|
if (isr_enabled) SBI(M_UCSRxB, M_RXCIEx);
|
|
|
|
#endif
|
2018-06-02 02:00:59 +02:00
|
|
|
|
2018-06-10 04:39:57 +02:00
|
|
|
#if ENABLED(SERIAL_XON_XOFF)
|
|
|
|
// If the XOFF char was sent, or about to be sent...
|
|
|
|
if ((xon_xoff_state & XON_XOFF_CHAR_MASK) == XOFF_CHAR) {
|
|
|
|
// Get count of bytes in the RX buffer
|
|
|
|
const ring_buffer_pos_t rx_count = (ring_buffer_pos_t)(h - t) & (ring_buffer_pos_t)(RX_BUFFER_SIZE - 1);
|
|
|
|
if (rx_count < (RX_BUFFER_SIZE) / 10) {
|
|
|
|
#if TX_BUFFER_SIZE > 0
|
|
|
|
// Signal we want an XON character to be sent.
|
|
|
|
xon_xoff_state = XON_CHAR;
|
|
|
|
// Enable TX isr. Non atomic, but it will eventually enable them
|
|
|
|
SBI(M_UCSRxB, M_UDRIEx);
|
|
|
|
#else
|
|
|
|
// If not using TX interrupts, we must send the XON char now
|
2018-06-02 02:00:59 +02:00
|
|
|
xon_xoff_state = XON_CHAR | XON_XOFF_CHAR_SENT;
|
2018-06-10 04:39:57 +02:00
|
|
|
while (!TEST(M_UCSRxA, M_UDREx)) sw_barrier();
|
|
|
|
M_UDRx = XON_CHAR;
|
|
|
|
#endif
|
2018-06-02 02:00:59 +02:00
|
|
|
}
|
2018-06-10 04:39:57 +02:00
|
|
|
}
|
|
|
|
#endif
|
2018-06-02 02:00:59 +02:00
|
|
|
|
2017-04-02 07:47:20 +02:00
|
|
|
return v;
|
2016-07-08 17:25:21 +02:00
|
|
|
}
|
|
|
|
|
2017-08-08 07:46:37 +02:00
|
|
|
ring_buffer_pos_t MarlinSerial::available(void) {
|
2018-06-02 02:00:59 +02:00
|
|
|
#if RX_BUFFER_SIZE > 256
|
|
|
|
const bool isr_enabled = TEST(M_UCSRxB, M_RXCIEx);
|
|
|
|
CBI(M_UCSRxB, M_RXCIEx);
|
|
|
|
#endif
|
|
|
|
|
2018-06-10 04:39:57 +02:00
|
|
|
const ring_buffer_pos_t h = rx_buffer.head, t = rx_buffer.tail;
|
2018-06-02 02:00:59 +02:00
|
|
|
|
|
|
|
#if RX_BUFFER_SIZE > 256
|
|
|
|
if (isr_enabled) SBI(M_UCSRxB, M_RXCIEx);
|
|
|
|
#endif
|
|
|
|
|
2017-08-08 07:46:37 +02:00
|
|
|
return (ring_buffer_pos_t)(RX_BUFFER_SIZE + h - t) & (RX_BUFFER_SIZE - 1);
|
2017-04-02 07:47:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void MarlinSerial::flush(void) {
|
2018-06-02 02:00:59 +02:00
|
|
|
#if RX_BUFFER_SIZE > 256
|
|
|
|
const bool isr_enabled = TEST(M_UCSRxB, M_RXCIEx);
|
|
|
|
CBI(M_UCSRxB, M_RXCIEx);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
rx_buffer.tail = rx_buffer.head;
|
|
|
|
|
|
|
|
#if RX_BUFFER_SIZE > 256
|
|
|
|
if (isr_enabled) SBI(M_UCSRxB, M_RXCIEx);
|
|
|
|
#endif
|
2017-10-02 07:26:56 +02:00
|
|
|
|
|
|
|
#if ENABLED(SERIAL_XON_XOFF)
|
2018-06-10 04:39:57 +02:00
|
|
|
// If the XOFF char was sent, or about to be sent...
|
2017-10-02 07:26:56 +02:00
|
|
|
if ((xon_xoff_state & XON_XOFF_CHAR_MASK) == XOFF_CHAR) {
|
2018-06-10 04:39:57 +02:00
|
|
|
#if TX_BUFFER_SIZE > 0
|
|
|
|
// Signal we want an XON character to be sent.
|
|
|
|
xon_xoff_state = XON_CHAR;
|
|
|
|
// Enable TX isr. Non atomic, but it will eventually enable it.
|
|
|
|
SBI(M_UCSRxB, M_UDRIEx);
|
|
|
|
#else
|
|
|
|
// If not using TX interrupts, we must send the XON char now
|
|
|
|
xon_xoff_state = XON_CHAR | XON_XOFF_CHAR_SENT;
|
|
|
|
while (!TEST(M_UCSRxA, M_UDREx)) sw_barrier();
|
|
|
|
M_UDRx = XON_CHAR;
|
|
|
|
#endif
|
2017-10-02 07:26:56 +02:00
|
|
|
}
|
|
|
|
#endif
|
2017-04-02 07:47:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#if TX_BUFFER_SIZE > 0
|
2017-04-02 11:24:28 +02:00
|
|
|
void MarlinSerial::write(const uint8_t c) {
|
2017-04-02 07:47:20 +02:00
|
|
|
_written = true;
|
2018-06-02 02:00:59 +02:00
|
|
|
|
|
|
|
// 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.
|
2018-06-10 04:39:57 +02:00
|
|
|
// Yes, there is a race condition between the sending of the
|
|
|
|
// XOFF char at the RX isr, but it is properly handled there
|
2018-06-02 02:00:59 +02:00
|
|
|
if (!TEST(M_UCSRxB, M_UDRIEx) && TEST(M_UCSRxA, M_UDREx)) {
|
|
|
|
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);
|
2017-04-02 07:47:20 +02:00
|
|
|
return;
|
|
|
|
}
|
2018-06-02 02:00:59 +02:00
|
|
|
|
2017-04-02 11:24:28 +02:00
|
|
|
const uint8_t i = (tx_buffer.head + 1) & (TX_BUFFER_SIZE - 1);
|
2017-04-02 07:47:20 +02:00
|
|
|
|
2018-06-10 04:39:57 +02:00
|
|
|
// If global interrupts are disabled (as the result of being called from an ISR)...
|
|
|
|
if (!ISRS_ENABLED()) {
|
|
|
|
|
|
|
|
// Make room by polling if it is possible to transmit, and do so!
|
|
|
|
while (i == tx_buffer.tail) {
|
2018-06-02 02:00:59 +02:00
|
|
|
|
2018-06-10 04:39:57 +02:00
|
|
|
// If we can transmit another byte, do it.
|
|
|
|
if (TEST(M_UCSRxA, M_UDREx)) _tx_udr_empty_irq();
|
|
|
|
|
|
|
|
// Make sure compiler rereads tx_buffer.tail
|
|
|
|
sw_barrier();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Interrupts are enabled, just wait until there is space
|
|
|
|
while (i == tx_buffer.tail) { sw_barrier(); }
|
2017-04-02 07:47:20 +02:00
|
|
|
}
|
2016-07-08 17:25:21 +02:00
|
|
|
|
2018-06-02 02:00:59 +02:00
|
|
|
// Store new char. head is always safe to move
|
2017-04-02 07:47:20 +02:00
|
|
|
tx_buffer.buffer[tx_buffer.head] = c;
|
2018-06-02 02:00:59 +02:00
|
|
|
tx_buffer.head = i;
|
|
|
|
|
2018-06-10 04:39:57 +02:00
|
|
|
// Enable TX isr - Non atomic, but it will eventually enable TX isr
|
2018-06-02 02:00:59 +02:00
|
|
|
SBI(M_UCSRxB, M_UDRIEx);
|
2016-07-08 17:25:21 +02:00
|
|
|
}
|
2017-04-02 07:47:20 +02:00
|
|
|
|
|
|
|
void MarlinSerial::flushTX(void) {
|
2018-06-10 04:39:57 +02:00
|
|
|
// No bytes written, no need to flush. This special case is needed since there's
|
|
|
|
// no way to force the TXC (transmit complete) bit to 1 during initialization.
|
|
|
|
if (!_written) return;
|
|
|
|
|
|
|
|
// If global interrupts are disabled (as the result of being called from an ISR)...
|
|
|
|
if (!ISRS_ENABLED()) {
|
2017-04-02 07:47:20 +02:00
|
|
|
|
2018-06-10 04:39:57 +02:00
|
|
|
// Wait until everything was transmitted - We must do polling, as interrupts are disabled
|
|
|
|
while (tx_buffer.head != tx_buffer.tail || !TEST(M_UCSRxA, M_TXCx)) {
|
|
|
|
|
|
|
|
// If there is more space, send an extra character
|
2017-04-02 07:47:20 +02:00
|
|
|
if (TEST(M_UCSRxA, M_UDREx))
|
|
|
|
_tx_udr_empty_irq();
|
2018-06-10 04:39:57 +02:00
|
|
|
|
|
|
|
sw_barrier();
|
2018-06-02 02:00:59 +02:00
|
|
|
}
|
2018-06-10 04:39:57 +02:00
|
|
|
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Wait until everything was transmitted
|
|
|
|
while (tx_buffer.head != tx_buffer.tail || !TEST(M_UCSRxA, M_TXCx)) sw_barrier();
|
2017-04-02 07:47:20 +02:00
|
|
|
}
|
2018-06-10 04:39:57 +02:00
|
|
|
|
|
|
|
// At this point nothing is queued anymore (DRIE is disabled) and
|
2018-06-02 02:00:59 +02:00
|
|
|
// the hardware finished transmission (TXC is set).
|
2017-10-02 07:26:56 +02:00
|
|
|
}
|
2016-07-08 17:25:21 +02:00
|
|
|
|
2017-10-02 07:26:56 +02:00
|
|
|
#else // TX_BUFFER_SIZE == 0
|
|
|
|
|
|
|
|
void MarlinSerial::write(const uint8_t c) {
|
2018-06-10 04:39:57 +02:00
|
|
|
_written = true;
|
2018-06-02 02:00:59 +02:00
|
|
|
while (!TEST(M_UCSRxA, M_UDREx)) sw_barrier();
|
2017-04-02 07:47:20 +02:00
|
|
|
M_UDRx = c;
|
|
|
|
}
|
2011-11-27 21:12:55 +01:00
|
|
|
|
2018-06-10 04:39:57 +02:00
|
|
|
void MarlinSerial::flushTX(void) {
|
|
|
|
// No bytes written, no need to flush. This special case is needed since there's
|
|
|
|
// no way to force the TXC (transmit complete) bit to 1 during initialization.
|
|
|
|
if (!_written) return;
|
|
|
|
|
|
|
|
// Wait until everything was transmitted
|
|
|
|
while (!TEST(M_UCSRxA, M_TXCx)) sw_barrier();
|
|
|
|
|
|
|
|
// At this point nothing is queued anymore (DRIE is disabled) and
|
|
|
|
// the hardware finished transmission (TXC is set).
|
|
|
|
}
|
2017-10-02 07:26:56 +02:00
|
|
|
#endif // TX_BUFFER_SIZE == 0
|
2011-11-27 21:12:55 +01:00
|
|
|
|
2017-10-02 07:26:56 +02:00
|
|
|
/**
|
|
|
|
* Imports from print.h
|
|
|
|
*/
|
2011-11-28 19:13:40 +01:00
|
|
|
|
2017-04-02 07:47:20 +02:00
|
|
|
void MarlinSerial::print(char c, int base) {
|
2017-04-02 11:24:28 +02:00
|
|
|
print((long)c, base);
|
2017-04-02 07:47:20 +02:00
|
|
|
}
|
2011-11-28 19:13:40 +01:00
|
|
|
|
2017-04-02 07:47:20 +02:00
|
|
|
void MarlinSerial::print(unsigned char b, int base) {
|
2017-04-02 11:24:28 +02:00
|
|
|
print((unsigned long)b, base);
|
2017-04-02 07:47:20 +02:00
|
|
|
}
|
2011-11-28 19:13:40 +01:00
|
|
|
|
2017-04-02 07:47:20 +02:00
|
|
|
void MarlinSerial::print(int n, int base) {
|
2017-04-02 11:24:28 +02:00
|
|
|
print((long)n, base);
|
2017-04-02 07:47:20 +02:00
|
|
|
}
|
2011-11-28 19:13:40 +01:00
|
|
|
|
2017-04-02 07:47:20 +02:00
|
|
|
void MarlinSerial::print(unsigned int n, int base) {
|
2017-04-02 11:24:28 +02:00
|
|
|
print((unsigned long)n, base);
|
2015-01-24 06:11:50 +01:00
|
|
|
}
|
2017-04-02 07:47:20 +02:00
|
|
|
|
|
|
|
void MarlinSerial::print(long n, int base) {
|
2018-06-10 04:39:57 +02:00
|
|
|
if (base == 0) write(n);
|
2017-04-02 07:47:20 +02:00
|
|
|
else if (base == 10) {
|
2018-06-10 04:39:57 +02:00
|
|
|
if (n < 0) { print('-'); n = -n; }
|
2017-04-02 07:47:20 +02:00
|
|
|
printNumber(n, 10);
|
|
|
|
}
|
2017-04-02 11:24:28 +02:00
|
|
|
else
|
2017-04-02 07:47:20 +02:00
|
|
|
printNumber(n, base);
|
2015-10-03 08:08:58 +02:00
|
|
|
}
|
2017-04-02 07:47:20 +02:00
|
|
|
|
|
|
|
void MarlinSerial::print(unsigned long n, int base) {
|
|
|
|
if (base == 0) write(n);
|
|
|
|
else printNumber(n, base);
|
2011-11-28 19:13:40 +01:00
|
|
|
}
|
2017-04-02 07:47:20 +02:00
|
|
|
|
|
|
|
void MarlinSerial::print(double n, int digits) {
|
|
|
|
printFloat(n, digits);
|
2011-11-28 19:13:40 +01:00
|
|
|
}
|
2017-04-02 07:47:20 +02:00
|
|
|
|
|
|
|
void MarlinSerial::println(void) {
|
|
|
|
print('\r');
|
|
|
|
print('\n');
|
2011-11-28 19:13:40 +01:00
|
|
|
}
|
|
|
|
|
2017-04-02 07:47:20 +02:00
|
|
|
void MarlinSerial::println(const String& s) {
|
|
|
|
print(s);
|
|
|
|
println();
|
2015-10-03 08:08:58 +02:00
|
|
|
}
|
2011-11-27 21:12:55 +01:00
|
|
|
|
2017-04-02 07:47:20 +02:00
|
|
|
void MarlinSerial::println(const char c[]) {
|
|
|
|
print(c);
|
|
|
|
println();
|
|
|
|
}
|
2011-11-27 21:12:55 +01:00
|
|
|
|
2017-04-02 07:47:20 +02:00
|
|
|
void MarlinSerial::println(char c, int base) {
|
|
|
|
print(c, base);
|
|
|
|
println();
|
|
|
|
}
|
2011-11-27 21:12:55 +01:00
|
|
|
|
2017-04-02 07:47:20 +02:00
|
|
|
void MarlinSerial::println(unsigned char b, int base) {
|
|
|
|
print(b, base);
|
|
|
|
println();
|
|
|
|
}
|
2013-10-30 11:45:32 +01:00
|
|
|
|
2017-04-02 07:47:20 +02:00
|
|
|
void MarlinSerial::println(int n, int base) {
|
|
|
|
print(n, base);
|
|
|
|
println();
|
|
|
|
}
|
Add an emergency-command parser to MarlinSerial (supporting M108)
Add an emergency-command parser to MarlinSerial's RX interrupt.
The parser tries to find and execute M108,M112,M410 before the commands disappear in the RX-buffer.
To avoid false positives for M117, comments and commands followed by filenames (M23, M28, M30, M32, M33) are filtered.
This enables Marlin to receive and react on the Emergency command at all times - regardless of whether the buffers are full or not. It remains to convince hosts to send the commands. To inform the hosts about the new feature a new entry in the M115-report was made. "`EMERGENCY_CODES:M112,M108,M410;`".
The parser is fast. It only ever needs two switch decisions and one assignment of the new state for every character.
One problem remains. If the host has sent an incomplete line before sending an emergency command the emergency command could be omitted when the parser is in `state_IGNORE`.
In that case the host should send "\ncommand\n"
Also introduces M108 to break the waiting for the heaters in M109, M190 and M303.
Rename `cancel_heatup` to `wait_for_heatup` to better see the purpose.
2016-07-04 23:23:22 +02:00
|
|
|
|
2017-04-02 07:47:20 +02:00
|
|
|
void MarlinSerial::println(unsigned int n, int base) {
|
|
|
|
print(n, base);
|
|
|
|
println();
|
|
|
|
}
|
Add an emergency-command parser to MarlinSerial (supporting M108)
Add an emergency-command parser to MarlinSerial's RX interrupt.
The parser tries to find and execute M108,M112,M410 before the commands disappear in the RX-buffer.
To avoid false positives for M117, comments and commands followed by filenames (M23, M28, M30, M32, M33) are filtered.
This enables Marlin to receive and react on the Emergency command at all times - regardless of whether the buffers are full or not. It remains to convince hosts to send the commands. To inform the hosts about the new feature a new entry in the M115-report was made. "`EMERGENCY_CODES:M112,M108,M410;`".
The parser is fast. It only ever needs two switch decisions and one assignment of the new state for every character.
One problem remains. If the host has sent an incomplete line before sending an emergency command the emergency command could be omitted when the parser is in `state_IGNORE`.
In that case the host should send "\ncommand\n"
Also introduces M108 to break the waiting for the heaters in M109, M190 and M303.
Rename `cancel_heatup` to `wait_for_heatup` to better see the purpose.
2016-07-04 23:23:22 +02:00
|
|
|
|
2017-04-02 07:47:20 +02:00
|
|
|
void MarlinSerial::println(long n, int base) {
|
|
|
|
print(n, base);
|
|
|
|
println();
|
|
|
|
}
|
Add an emergency-command parser to MarlinSerial (supporting M108)
Add an emergency-command parser to MarlinSerial's RX interrupt.
The parser tries to find and execute M108,M112,M410 before the commands disappear in the RX-buffer.
To avoid false positives for M117, comments and commands followed by filenames (M23, M28, M30, M32, M33) are filtered.
This enables Marlin to receive and react on the Emergency command at all times - regardless of whether the buffers are full or not. It remains to convince hosts to send the commands. To inform the hosts about the new feature a new entry in the M115-report was made. "`EMERGENCY_CODES:M112,M108,M410;`".
The parser is fast. It only ever needs two switch decisions and one assignment of the new state for every character.
One problem remains. If the host has sent an incomplete line before sending an emergency command the emergency command could be omitted when the parser is in `state_IGNORE`.
In that case the host should send "\ncommand\n"
Also introduces M108 to break the waiting for the heaters in M109, M190 and M303.
Rename `cancel_heatup` to `wait_for_heatup` to better see the purpose.
2016-07-04 23:23:22 +02:00
|
|
|
|
2017-04-02 07:47:20 +02:00
|
|
|
void MarlinSerial::println(unsigned long n, int base) {
|
|
|
|
print(n, base);
|
|
|
|
println();
|
|
|
|
}
|
Add an emergency-command parser to MarlinSerial (supporting M108)
Add an emergency-command parser to MarlinSerial's RX interrupt.
The parser tries to find and execute M108,M112,M410 before the commands disappear in the RX-buffer.
To avoid false positives for M117, comments and commands followed by filenames (M23, M28, M30, M32, M33) are filtered.
This enables Marlin to receive and react on the Emergency command at all times - regardless of whether the buffers are full or not. It remains to convince hosts to send the commands. To inform the hosts about the new feature a new entry in the M115-report was made. "`EMERGENCY_CODES:M112,M108,M410;`".
The parser is fast. It only ever needs two switch decisions and one assignment of the new state for every character.
One problem remains. If the host has sent an incomplete line before sending an emergency command the emergency command could be omitted when the parser is in `state_IGNORE`.
In that case the host should send "\ncommand\n"
Also introduces M108 to break the waiting for the heaters in M109, M190 and M303.
Rename `cancel_heatup` to `wait_for_heatup` to better see the purpose.
2016-07-04 23:23:22 +02:00
|
|
|
|
2017-04-02 07:47:20 +02:00
|
|
|
void MarlinSerial::println(double n, int digits) {
|
|
|
|
print(n, digits);
|
|
|
|
println();
|
|
|
|
}
|
Add an emergency-command parser to MarlinSerial (supporting M108)
Add an emergency-command parser to MarlinSerial's RX interrupt.
The parser tries to find and execute M108,M112,M410 before the commands disappear in the RX-buffer.
To avoid false positives for M117, comments and commands followed by filenames (M23, M28, M30, M32, M33) are filtered.
This enables Marlin to receive and react on the Emergency command at all times - regardless of whether the buffers are full or not. It remains to convince hosts to send the commands. To inform the hosts about the new feature a new entry in the M115-report was made. "`EMERGENCY_CODES:M112,M108,M410;`".
The parser is fast. It only ever needs two switch decisions and one assignment of the new state for every character.
One problem remains. If the host has sent an incomplete line before sending an emergency command the emergency command could be omitted when the parser is in `state_IGNORE`.
In that case the host should send "\ncommand\n"
Also introduces M108 to break the waiting for the heaters in M109, M190 and M303.
Rename `cancel_heatup` to `wait_for_heatup` to better see the purpose.
2016-07-04 23:23:22 +02:00
|
|
|
|
2017-04-02 07:47:20 +02:00
|
|
|
// Private Methods
|
Add an emergency-command parser to MarlinSerial (supporting M108)
Add an emergency-command parser to MarlinSerial's RX interrupt.
The parser tries to find and execute M108,M112,M410 before the commands disappear in the RX-buffer.
To avoid false positives for M117, comments and commands followed by filenames (M23, M28, M30, M32, M33) are filtered.
This enables Marlin to receive and react on the Emergency command at all times - regardless of whether the buffers are full or not. It remains to convince hosts to send the commands. To inform the hosts about the new feature a new entry in the M115-report was made. "`EMERGENCY_CODES:M112,M108,M410;`".
The parser is fast. It only ever needs two switch decisions and one assignment of the new state for every character.
One problem remains. If the host has sent an incomplete line before sending an emergency command the emergency command could be omitted when the parser is in `state_IGNORE`.
In that case the host should send "\ncommand\n"
Also introduces M108 to break the waiting for the heaters in M109, M190 and M303.
Rename `cancel_heatup` to `wait_for_heatup` to better see the purpose.
2016-07-04 23:23:22 +02:00
|
|
|
|
2017-04-02 07:47:20 +02:00
|
|
|
void MarlinSerial::printNumber(unsigned long n, uint8_t base) {
|
|
|
|
if (n) {
|
|
|
|
unsigned char buf[8 * sizeof(long)]; // Enough space for base 2
|
|
|
|
int8_t i = 0;
|
|
|
|
while (n) {
|
|
|
|
buf[i++] = n % base;
|
|
|
|
n /= base;
|
|
|
|
}
|
|
|
|
while (i--)
|
|
|
|
print((char)(buf[i] + (buf[i] < 10 ? '0' : 'A' - 10)));
|
Add an emergency-command parser to MarlinSerial (supporting M108)
Add an emergency-command parser to MarlinSerial's RX interrupt.
The parser tries to find and execute M108,M112,M410 before the commands disappear in the RX-buffer.
To avoid false positives for M117, comments and commands followed by filenames (M23, M28, M30, M32, M33) are filtered.
This enables Marlin to receive and react on the Emergency command at all times - regardless of whether the buffers are full or not. It remains to convince hosts to send the commands. To inform the hosts about the new feature a new entry in the M115-report was made. "`EMERGENCY_CODES:M112,M108,M410;`".
The parser is fast. It only ever needs two switch decisions and one assignment of the new state for every character.
One problem remains. If the host has sent an incomplete line before sending an emergency command the emergency command could be omitted when the parser is in `state_IGNORE`.
In that case the host should send "\ncommand\n"
Also introduces M108 to break the waiting for the heaters in M109, M190 and M303.
Rename `cancel_heatup` to `wait_for_heatup` to better see the purpose.
2016-07-04 23:23:22 +02:00
|
|
|
}
|
2017-04-02 07:47:20 +02:00
|
|
|
else
|
|
|
|
print('0');
|
Add an emergency-command parser to MarlinSerial (supporting M108)
Add an emergency-command parser to MarlinSerial's RX interrupt.
The parser tries to find and execute M108,M112,M410 before the commands disappear in the RX-buffer.
To avoid false positives for M117, comments and commands followed by filenames (M23, M28, M30, M32, M33) are filtered.
This enables Marlin to receive and react on the Emergency command at all times - regardless of whether the buffers are full or not. It remains to convince hosts to send the commands. To inform the hosts about the new feature a new entry in the M115-report was made. "`EMERGENCY_CODES:M112,M108,M410;`".
The parser is fast. It only ever needs two switch decisions and one assignment of the new state for every character.
One problem remains. If the host has sent an incomplete line before sending an emergency command the emergency command could be omitted when the parser is in `state_IGNORE`.
In that case the host should send "\ncommand\n"
Also introduces M108 to break the waiting for the heaters in M109, M190 and M303.
Rename `cancel_heatup` to `wait_for_heatup` to better see the purpose.
2016-07-04 23:23:22 +02:00
|
|
|
}
|
2016-08-19 23:53:20 +02:00
|
|
|
|
2017-04-02 07:47:20 +02:00
|
|
|
void MarlinSerial::printFloat(double number, uint8_t digits) {
|
|
|
|
// Handle negative numbers
|
|
|
|
if (number < 0.0) {
|
|
|
|
print('-');
|
|
|
|
number = -number;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Round correctly so that print(1.999, 2) prints as "2.00"
|
|
|
|
double rounding = 0.5;
|
|
|
|
for (uint8_t i = 0; i < digits; ++i)
|
|
|
|
rounding *= 0.1;
|
|
|
|
|
|
|
|
number += rounding;
|
|
|
|
|
|
|
|
// Extract the integer part of the number and print it
|
|
|
|
unsigned long int_part = (unsigned long)number;
|
|
|
|
double remainder = number - (double)int_part;
|
|
|
|
print(int_part);
|
|
|
|
|
|
|
|
// Print the decimal point, but only if there are digits beyond
|
|
|
|
if (digits) {
|
|
|
|
print('.');
|
|
|
|
// Extract digits from the remainder one at a time
|
|
|
|
while (digits--) {
|
|
|
|
remainder *= 10.0;
|
|
|
|
int toPrint = int(remainder);
|
|
|
|
print(toPrint);
|
|
|
|
remainder -= toPrint;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Preinstantiate
|
|
|
|
MarlinSerial customizedSerial;
|
|
|
|
|
2018-05-24 12:47:55 +02:00
|
|
|
#endif // USE_MARLINSERIAL && (UBRRH || UBRR0H || UBRR1H || UBRR2H || UBRR3H)
|
2017-04-02 07:47:20 +02:00
|
|
|
|
|
|
|
// For AT90USB targets use the UART for BT interfacing
|
2018-05-24 12:47:55 +02:00
|
|
|
#if !USE_MARLINSERIAL && ENABLED(BLUETOOTH)
|
2017-04-02 07:47:20 +02:00
|
|
|
HardwareSerial bluetoothSerial;
|
Add an emergency-command parser to MarlinSerial (supporting M108)
Add an emergency-command parser to MarlinSerial's RX interrupt.
The parser tries to find and execute M108,M112,M410 before the commands disappear in the RX-buffer.
To avoid false positives for M117, comments and commands followed by filenames (M23, M28, M30, M32, M33) are filtered.
This enables Marlin to receive and react on the Emergency command at all times - regardless of whether the buffers are full or not. It remains to convince hosts to send the commands. To inform the hosts about the new feature a new entry in the M115-report was made. "`EMERGENCY_CODES:M112,M108,M410;`".
The parser is fast. It only ever needs two switch decisions and one assignment of the new state for every character.
One problem remains. If the host has sent an incomplete line before sending an emergency command the emergency command could be omitted when the parser is in `state_IGNORE`.
In that case the host should send "\ncommand\n"
Also introduces M108 to break the waiting for the heaters in M109, M190 and M303.
Rename `cancel_heatup` to `wait_for_heatup` to better see the purpose.
2016-07-04 23:23:22 +02:00
|
|
|
#endif
|