Combined LPC / Serial fixes (#21178)

Co-authored-by: Scott Lahteine <thinkyhead@users.noreply.github.com>
This commit is contained in:
X-Ryl669
2021-02-25 08:23:29 +01:00
committed by GitHub
parent 13c4eef637
commit f003e52009
11 changed files with 132 additions and 87 deletions

View File

@ -92,12 +92,11 @@ int MarlinSerialUSB::read() {
return c;
}
bool MarlinSerialUSB::available() {
/* If Pending chars */
return pending_char >= 0 ||
/* or USB CDC enumerated and configured on the PC side and some
bytes where sent to us */
(usb_task_cdc_isenabled() && udi_cdc_is_rx_ready());
int MarlinSerialUSB::available() {
if (pending_char > 0) return pending_char;
return pending_char == 0 ||
// or USB CDC enumerated and configured on the PC side and some bytes where sent to us */
(usb_task_cdc_isenabled() && udi_cdc_is_rx_ready());
}
void MarlinSerialUSB::flush() { }

View File

@ -39,7 +39,7 @@ struct MarlinSerialUSB {
int peek();
int read();
void flush();
bool available();
int available();
size_t write(const uint8_t c);
#if ENABLED(SERIAL_STATS_DROPPED_RX)

View File

@ -25,20 +25,46 @@
#include "MarlinSerial.h"
#if ANY_SERIAL_IS(0)
MSerialT MSerial(true, LPC_UART0);
extern "C" void UART0_IRQHandler() { MSerial.IRQHandler(); }
MarlinSerial _MSerial(LPC_UART0);
MSerialT MSerial(true, _MSerial);
extern "C" void UART0_IRQHandler() { _MSerial.IRQHandler(); }
#endif
#if ANY_SERIAL_IS(1)
MSerialT MSerial1(true, (LPC_UART_TypeDef *) LPC_UART1);
extern "C" void UART1_IRQHandler() { MSerial1.IRQHandler(); }
MarlinSerial _MSerial1((LPC_UART_TypeDef *) LPC_UART1);
MSerialT MSerial1(true, _MSerial1);
extern "C" void UART1_IRQHandler() { _MSerial1.IRQHandler(); }
#endif
#if ANY_SERIAL_IS(2)
MSerialT MSerial2(true, LPC_UART2);
extern "C" void UART2_IRQHandler() { MSerial2.IRQHandler(); }
MarlinSerial _MSerial2(LPC_UART2);
MSerialT MSerial2(true, _MSerial2);
extern "C" void UART2_IRQHandler() { _MSerial2.IRQHandler(); }
#endif
#if ANY_SERIAL_IS(3)
MSerialT MSerial3(true, LPC_UART3);
extern "C" void UART3_IRQHandler() { MSerial3.IRQHandler(); }
MarlinSerial _MSerial3(LPC_UART3);
MSerialT MSerial3(true, _MSerial3);
extern "C" void UART3_IRQHandler() { _MSerial3.IRQHandler(); }
#endif
#if ENABLED(EMERGENCY_PARSER)
bool MarlinSerial::recv_callback(const char c) {
// Need to figure out which serial port we are and react in consequence (Marlin does not have CONTAINER_OF macro)
if (false) {}
#if ANY_SERIAL_IS(0)
else if (this == &_MSerial) emergency_parser.update(MSerial.emergency_state, c);
#endif
#if ANY_SERIAL_IS(1)
else if (this == &_MSerial1) emergency_parser.update(MSerial1.emergency_state, c);
#endif
#if ANY_SERIAL_IS(2)
else if (this == &_MSerial2) emergency_parser.update(MSerial2.emergency_state, c);
#endif
#if ANY_SERIAL_IS(3)
else if (this == &_MSerial3) emergency_parser.update(MSerial3.emergency_state, c);
#endif
return true;
}
#endif
#endif // TARGET_LPC1768

View File

@ -47,15 +47,21 @@ public:
void end() {}
#if ENABLED(EMERGENCY_PARSER)
bool recv_callback(const char c) override {
emergency_parser.update(static_cast<Serial0Type<MarlinSerial> *>(this)->emergency_state, c);
return true; // do not discard character
}
bool recv_callback(const char c) override;
#endif
};
typedef Serial0Type<MarlinSerial> MSerialT;
// On LPC176x framework, HardwareSerial does not implement the same interface as Arduino's Serial, so overloads
// of 'available' and 'read' method are not used in this multiple inheritance scenario.
// Instead, use a ForwardSerial here that adapts the interface.
typedef ForwardSerial0Type<MarlinSerial> MSerialT;
extern MSerialT MSerial;
extern MSerialT MSerial1;
extern MSerialT MSerial2;
extern MSerialT MSerial3;
// Consequently, we can't use a RuntimeSerial either. The workaround would be to use a RuntimeSerial<ForwardSerial<MarlinSerial>> type here
// Right now, let's ignore this until it's actually required.
#if ENABLED(SERIAL_RUNTIME_HOOK)
#error "SERIAL_RUNTIME_HOOK is not yet supported for LPC176x."
#endif

View File

@ -29,8 +29,8 @@
EmergencyParser::State emergency_state;
bool CDC_RecvCallback(const char buffer) {
emergency_parser.update(emergency_state, buffer);
bool CDC_RecvCallback(const char c) {
emergency_parser.update(emergency_state, c);
return true;
}

View File

@ -51,7 +51,7 @@
// Use hardware cycle counter instead, it's much safer
void delay_dwt(uint32_t count) {
// Reuse the ASM_CYCLES_PER_ITERATION variable to avoid wasting another useless variable
register uint32_t start = HW_REG(_DWT_CYCCNT) - ASM_CYCLES_PER_ITERATION, elapsed;
uint32_t start = HW_REG(_DWT_CYCCNT) - ASM_CYCLES_PER_ITERATION, elapsed;
do {
elapsed = HW_REG(_DWT_CYCCNT) - start;
} while (elapsed < count);
@ -114,7 +114,7 @@
serialprintPGM(unit);
SERIAL_ECHOLNPAIR(" took: ", total);
serialprintPGM(unit);
if (do_flush) SERIAL_FLUSH();
if (do_flush) SERIAL_FLUSHTX();
};
uint32_t s, e;

View File

@ -58,7 +58,6 @@ extern uint8_t marlin_debug_flags;
//
// Serial redirection
//
typedef int8_t serial_index_t;
#define SERIAL_ALL 0x7F
#if HAS_MULTI_SERIAL
#define _PORT_REDIRECT(n,p) REMEMBER(n,multiSerial.portMask,p)

View File

@ -79,7 +79,7 @@ struct SerialBase {
void end() { static_cast<Child*>(this)->end(); }
/** Check for available data from the port
@param index The port index, usually 0 */
bool available(uint8_t index = 0) { return static_cast<Child*>(this)->available(index); }
int available(uint8_t index = 0) { return static_cast<Child*>(this)->available(index); }
/** Read a value from the port
@param index The port index, usually 0 */
int read(uint8_t index = 0) { return static_cast<Child*>(this)->read(index); }

View File

@ -24,6 +24,9 @@
#include "macros.h"
#include "serial_base.h"
// Used in multiple places
typedef int8_t serial_index_t;
// The most basic serial class: it dispatch to the base serial class with no hook whatsoever. This will compile to nothing but the base serial class
template <class SerialT>
struct BaseSerial : public SerialBase< BaseSerial<SerialT> >, public SerialT {
@ -35,10 +38,11 @@ struct BaseSerial : public SerialBase< BaseSerial<SerialT> >, public SerialT {
void msgDone() {}
bool available(uint8_t index) { return index == 0 && SerialT::available(); }
int read(uint8_t index) { return index == 0 ? SerialT::read() : -1; }
bool connected() { return CALL_IF_EXISTS(bool, static_cast<SerialT*>(this), connected);; }
void flushTX() { CALL_IF_EXISTS(void, static_cast<SerialT*>(this), flushTX); }
// We don't care about indices here, since if one can call us, it's the right index anyway
int available(uint8_t) { return (int)SerialT::available(); }
int read(uint8_t) { return (int)SerialT::read(); }
bool connected() { return CALL_IF_EXISTS(bool, static_cast<SerialT*>(this), connected);; }
void flushTX() { CALL_IF_EXISTS(void, static_cast<SerialT*>(this), flushTX); }
// We have 2 implementation of the same method in both base class, let's say which one we want
using SerialT::available;
@ -65,18 +69,19 @@ struct ConditionalSerial : public SerialBase< ConditionalSerial<SerialT> > {
bool & condition;
SerialT & out;
NO_INLINE size_t write(uint8_t c) { if (condition) return out.write(c); return 0; }
void flush() { if (condition) out.flush(); }
void begin(long br) { out.begin(br); }
void end() { out.end(); }
void flush() { if (condition) out.flush(); }
void begin(long br) { out.begin(br); }
void end() { out.end(); }
void msgDone() {}
bool connected() { return CALL_IF_EXISTS(bool, &out, connected); }
void flushTX() { CALL_IF_EXISTS(void, &out, flushTX); }
bool connected() { return CALL_IF_EXISTS(bool, &out, connected); }
void flushTX() { CALL_IF_EXISTS(void, &out, flushTX); }
int available(uint8_t ) { return (int)out.available(); }
int read(uint8_t ) { return (int)out.read(); }
int available() { return (int)out.available(); }
int read() { return (int)out.read(); }
bool available(uint8_t index) { return index == 0 && out.available(); }
int read(uint8_t index) { return index == 0 ? out.read() : -1; }
using BaseClassT::available;
using BaseClassT::read;
ConditionalSerial(bool & conditionVariable, SerialT & out, const bool e) : BaseClassT(e), condition(conditionVariable), out(out) {}
};
@ -97,10 +102,10 @@ struct ForwardSerial : public SerialBase< ForwardSerial<SerialT> > {
bool connected() { return Private::HasMember_connected<SerialT>::value ? CALL_IF_EXISTS(bool, &out, connected) : (bool)out; }
void flushTX() { CALL_IF_EXISTS(void, &out, flushTX); }
bool available(uint8_t index) { return index == 0 && out.available(); }
int read(uint8_t index) { return index == 0 ? out.read() : -1; }
bool available() { return out.available(); }
int read() { return out.read(); }
int available(uint8_t) { return (int)out.available(); }
int read(uint8_t) { return (int)out.read(); }
int available() { return (int)out.available(); }
int read() { return (int)out.read(); }
ForwardSerial(const bool e, SerialT & out) : BaseClassT(e), out(out) {}
};
@ -125,8 +130,8 @@ struct RuntimeSerial : public SerialBase< RuntimeSerial<SerialT> >, public Seria
if (eofHook) eofHook(userPointer);
}
bool available(uint8_t index) { return index == 0 && SerialT::available(); }
int read(uint8_t index) { return index == 0 ? SerialT::read() : -1; }
int available(uint8_t) { return (int)SerialT::available(); }
int read(uint8_t) { return (int)SerialT::read(); }
using SerialT::available;
using SerialT::read;
using SerialT::flush;
@ -157,21 +162,22 @@ struct RuntimeSerial : public SerialBase< RuntimeSerial<SerialT> >, public Seria
// Forward constructor
template <typename... Args>
RuntimeSerial(const bool e, Args... args) : BaseClassT(e), SerialT(args...) {}
RuntimeSerial(const bool e, Args... args) : BaseClassT(e), SerialT(args...), writeHook(0), eofHook(0), userPointer(0) {}
};
// A class that's duplicating its output conditionally to 2 serial interface
template <class Serial0T, class Serial1T, const uint8_t offset = 0>
struct MultiSerial : public SerialBase< MultiSerial<Serial0T, Serial1T, offset> > {
typedef SerialBase< MultiSerial<Serial0T, Serial1T, offset> > BaseClassT;
template <class Serial0T, class Serial1T, const uint8_t offset = 0, const uint8_t step = 1>
struct MultiSerial : public SerialBase< MultiSerial<Serial0T, Serial1T, offset, step> > {
typedef SerialBase< MultiSerial<Serial0T, Serial1T, offset, step> > BaseClassT;
uint8_t portMask;
Serial0T & serial0;
Serial1T & serial1;
enum Masks {
FirstOutputMask = (1 << offset),
SecondOutputMask = (1 << (offset + 1)),
UsageMask = ((1 << step) - 1), // A bit mask containing as many bits as step
FirstOutputMask = (UsageMask << offset),
SecondOutputMask = (UsageMask << (offset + step)),
AllMask = FirstOutputMask | SecondOutputMask,
};
@ -185,19 +191,19 @@ struct MultiSerial : public SerialBase< MultiSerial<Serial0T, Serial1T, offset>
if (portMask & FirstOutputMask) serial0.msgDone();
if (portMask & SecondOutputMask) serial1.msgDone();
}
bool available(uint8_t index) {
switch(index) {
case 0 + offset: return serial0.available();
case 1 + offset: return serial1.available();
default: return false;
}
int available(uint8_t index) {
if (index >= 0 + offset && index < step + offset)
return serial0.available(index);
else if (index >= step + offset && index < 2 * step + offset)
return serial1.available(index);
return false;
}
NO_INLINE int read(uint8_t index) {
switch(index) {
case 0 + offset: return serial0.read();
case 1 + offset: return serial1.read();
default: return -1;
}
int read(uint8_t index) {
if (index >= 0 + offset && index < step + offset)
return serial0.read(index);
else if (index >= step + offset && index < 2 * step + offset)
return serial1.read(index);
return -1;
}
void begin(const long br) {
if (portMask & FirstOutputMask) serial0.begin(br);

View File

@ -63,6 +63,10 @@ GCodeQueue queue;
// Frequently used G-code strings
PGMSTR(G28_STR, "G28");
#if NO_TIMEOUTS > 0
static millis_t last_command_time = 0;
#endif
/**
* GCode line number handling. Hosts may opt to include line numbers when
* sending commands to Marlin, and lines will be checked for sequentiality.
@ -288,6 +292,10 @@ void GCodeQueue::enqueue_now_P(PGM_P const pgcode) {
* B<int> Block queue space remaining
*/
void GCodeQueue::ok_to_send() {
#if NO_TIMEOUTS > 0
// Start counting from the last command's execution
last_command_time = millis();
#endif
#if HAS_MULTI_SERIAL
const serial_index_t serial_ind = command_port();
if (serial_ind < 0) return;
@ -324,28 +332,19 @@ void GCodeQueue::flush_and_request_resend() {
ok_to_send();
}
inline bool serial_data_available() {
byte data_available = 0;
if (MYSERIAL0.available()) data_available++;
#ifdef SERIAL_PORT_2
const bool port2_open = TERN1(HAS_ETHERNET, ethernet.have_telnet_client);
if (port2_open && MYSERIAL1.available()) data_available++;
#endif
return data_available > 0;
// Multiserial already handle the dispatch to/from multiple port by itself
inline bool serial_data_available(uint8_t index = SERIAL_ALL) {
if (index == SERIAL_ALL) {
for (index = 0; index < NUM_SERIAL; index++) {
if (SERIAL_IMPL.available(index) > 0) return true;
}
return false;
}
return SERIAL_IMPL.available(index) > 0;
}
inline int read_serial(const uint8_t index) {
switch (index) {
case 0: return MYSERIAL0.read();
case 1: {
#if HAS_MULTI_SERIAL
const bool port2_open = TERN1(HAS_ETHERNET, ethernet.have_telnet_client);
if (port2_open) return MYSERIAL1.read();
#endif
}
default: return -1;
}
}
inline int read_serial(const uint8_t index) { return SERIAL_IMPL.read(index); }
void GCodeQueue::gcode_line_error(PGM_P const err, const serial_index_t serial_ind) {
PORT_REDIRECT(SERIAL_PORTMASK(serial_ind)); // Reply to the serial port that sent the command
@ -460,7 +459,6 @@ void GCodeQueue::get_serial_commands() {
// If the command buffer is empty for too long,
// send "wait" to indicate Marlin is still waiting.
#if NO_TIMEOUTS > 0
static millis_t last_command_time = 0;
const millis_t ms = millis();
if (length == 0 && !serial_data_available() && ELAPSED(ms, last_command_time + NO_TIMEOUTS)) {
SERIAL_ECHOLNPGM(STR_WAIT);