1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2025-02-18 15:06:03 +01:00

Add CMRI work-arounds for ArduinoCMRI library

Work-arounds enabled by "#define ARDUINOCMRI_COMPATIBLE TRUE" in config.h
This commit is contained in:
Neil McKechnie 2023-03-31 13:49:11 +01:00
parent 0154ab7631
commit bbc4339b69
2 changed files with 44 additions and 13 deletions

View File

@ -18,6 +18,7 @@
*/
#include "IO_CMRI.h"
#include "defines.h"
/************************************************************
* CMRIbus implementation
@ -36,12 +37,18 @@ CMRIbus::CMRIbus(uint8_t busNo, HardwareSerial &serial, unsigned long baud, uint
}
// Max message length is 256+6=262 bytes.
// Each byte is one start bit, 8 data bits and 1 stop bit = 10 bits per byte.
// Calculate timeout based on double this time.
_timeoutPeriod = 2 * 10 * 262 * 1000UL / (_baud / 1000UL);
// Each byte is one start bit, 8 data bits and 1 or 2 stop bits, assume 11 bits per byte.
// Calculate timeout based on treble this time.
_timeoutPeriod = 3 * 11 * 262 * 1000UL / (_baud / 1000UL);
#if ARDUINOCMRI_COMPATIBLE
// NOTE: The ArduinoCMRI library, unless modified, contains a 'delay(50)' between
// receiving the end of the prompt message and starting to send the response. This
// is allowed for below.
_timeoutPeriod += 50000UL;
#endif
// Calculate the time in microseconds to transmit one byte (10 bits).
_byteTransmitTime = 1000000UL * 10 / _baud;
// Calculate the time in microseconds to transmit one byte (11 bits max).
_byteTransmitTime = 1000000UL * 11 / _baud;
// Postdelay is only required if we need to allow for data still being sent when
// we want to switch off the transmitter. The flush() method of HardwareSerial
// ensures that the data has completed being sent over the line.
@ -148,9 +155,9 @@ void CMRIbus::processOutgoing() {
case TD_TRANSMIT:
charsSent = sendData(_currentNode);
_transmitState = TD_PROMPT;
// Defer next entry for as long as it takes to transmit the characters,
// to allow output queue to empty.
delayUntil(_currentMicros+_byteTransmitTime*charsSent);
// Defer next entry for as long as it takes to transmit the characters,
// to allow output queue to empty. Allow 2 bytes extra.
delayUntil(_currentMicros+_byteTransmitTime*(charsSent+2));
break;
case TD_PROMPT:
charsSent = requestData(_currentNode);
@ -184,7 +191,7 @@ void CMRIbus::processIncoming() {
if (data == SYN) nextState = RD_SYN2;
break;
case RD_SYN2:
if (data == SYN) nextState = RD_STX;
if (data == SYN) nextState = RD_STX; else nextState = RD_SYN2;
break;
case RD_STX:
if (data == STX) nextState = RD_ADDR;
@ -225,9 +232,17 @@ void CMRIbus::processIncoming() {
void CMRIbus::enableTransmitter() {
if (_transmitEnablePin != VPIN_NONE)
ArduinoPins::fastWriteDigital(_transmitEnablePin, 1);
// Send an extra SYN character to ensure transmitter and
// remote receiver have stabilised before we start the packet.
// If we need a delay before we start the packet header,
// we can send a character or two to synchronise the
// transmitter and receiver.
// SYN characters should be used, but a bug in the
// ArduinoCMRI library causes it to ignore the packet if
// it's preceded by an odd number of SYN characters.
// So send a SYN followed by a NUL in that case.
_serial->write(SYN);
#if ARDUINOCMRI_COMPATIBLE
_serial->write(NUL); // Reset the ArduinoCMRI library's parser
#endif
}
// If configured for half duplex RS485, switch RS485 interface

View File

@ -18,6 +18,8 @@
*/
/*
* CMRIbus
* =======
* To define a CMRI bus, example syntax:
* CMRIbus::create(bus, serial, baud[, cycletime[, pin]]);
*
@ -29,6 +31,15 @@
*
* Each bus must use a different serial port.
*
* IMPORTANT: If you are using ArduinoCMRI library code by Michael Adams, at the time of writing this library
* is not compliant with the LCS-9.10.1 specification for CMRInet protocol.
* Various work-arounds may be enabled within the driver by adding the following line to your config.h file,
* to allow nodes running the ArduinoCMRI library to communicate:
*
* #define ARDUINOCMRI_COMPATIBLE TRUE
*
* CMRINode
* ========
* To define a CMRI node and associate it with a CMRI bus,
* CMRInode::create(firstVPIN, numVPINs, bus, address, type [, inputs, outputs]);
*
@ -196,6 +207,7 @@ private:
// Definition of special characters in CMRInet protocol
enum : uint8_t {
NUL = 0x00,
STX = 0x02,
ETX = 0x03,
DLE = 0x10,
@ -209,8 +221,12 @@ public:
// Device-specific initialisation
void _begin() override {
// Some sources quote one stop bit, some two.
// CMRInet spec states one stop bit, JMRI and ArduinoCMRI use two stop bits
#if ARDUINOCMRI_COMPATIBLE
_serial->begin(_baud, SERIAL_8N2);
#else
_serial->begin(_baud, SERIAL_8N1);
#endif
#if defined(DIAG_IO)
_display();
#endif