TPUART full duplex handling

This commit is contained in:
Mike Pieper 2021-12-18 09:11:45 +01:00
parent fb74931bec
commit 910ccae31b
2 changed files with 219 additions and 161 deletions

View File

@ -11,6 +11,9 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
// Activate trace output
//#define DBG_TRACE
// NCN5120 // NCN5120
//#define NCN5120 //#define NCN5120
@ -75,14 +78,22 @@
#define U_STOP_MODE_IND 0x2B #define U_STOP_MODE_IND 0x2B
#define U_SYSTEM_STAT_IND 0x4B #define U_SYSTEM_STAT_IND 0x4B
//loop states //tx states
#define IDLE 0 enum {
#define RX_FIRST_BYTE 1 TX_IDLE,
#define RX_L_DATA 2 TX_FRAME,
#define RX_WAIT_DATA_CON 3 TX_WAIT_ECHO,
#define TX_FRAME 4 TX_WAIT_CONN
};
#define BYTE_TIMEOUT 10 //milli seconds //rx states
enum {
RX_WAIT_START,
RX_L_DATA,
RX_WAIT_EOP
};
#define EOP_TIMEOUT 2 //milli seconds; end of layer-2 packet gap
#define CONFIRM_TIMEOUT 500 //milli seconds #define CONFIRM_TIMEOUT 500 //milli seconds
#define RESET_TIMEOUT 100 //milli seconds #define RESET_TIMEOUT 100 //milli seconds
@ -107,134 +118,136 @@ void TpUartDataLinkLayer::loop()
if (!_enabled) if (!_enabled)
return; return;
switch (_loopState) bool isEchoComplete = false; // Flag that a complete echo is received
bool dataConnMsg = 0; // The DATA_CONN message just seen or 0
bool isEOP = (millis() - _lastByteRxTime > EOP_TIMEOUT); // Flag that an EOP gap is seen
switch (_rxState)
{ {
case IDLE: case RX_WAIT_START:
if (_platform.uartAvailable()) if (_platform.uartAvailable())
{ {
_loopState = RX_FIRST_BYTE; rxByte = _platform.readUart();
} #ifdef DBG_TRACE
else
{
if (!_waitConfirm && !isTxQueueEmpty())
{
loadNextTxFrame();
_loopState = TX_FRAME;
}
}
break;
case TX_FRAME:
if (sendSingleFrameByte() == false)
{
_waitConfirm = true;
_waitConfirmStartTime = millis();
_loopState = IDLE;
}
break;
case RX_FIRST_BYTE:
rxByte = _platform.readUart();
_lastByteRxTime = millis();
_RxByteCnt = 0;
_xorSum = 0;
if ((rxByte & L_DATA_MASK) == L_DATA_STANDARD_IND)
{
buffer[_RxByteCnt++] = rxByte;
_xorSum ^= rxByte;
_RxByteCnt++; //convert to L_DATA_EXTENDED
_convert = true;
_loopState = RX_L_DATA;
break;
}
else if ((rxByte & L_DATA_MASK) == L_DATA_EXTENDED_IND)
{
buffer[_RxByteCnt++] = rxByte;
_xorSum ^= rxByte;
_convert = false;
_loopState = RX_L_DATA;
break;
}
else if ((rxByte & L_DATA_CON_MASK) == L_DATA_CON)
{
println("got unexpected L_DATA_CON");
}
else if (rxByte == L_POLL_DATA_IND)
{
// not sure if this can happen
println("got L_POLL_DATA_IND");
}
else if ((rxByte & L_ACKN_MASK) == L_ACKN_IND)
{
// this can only happen in bus monitor mode
println("got L_ACKN_IND");
}
else if (rxByte == U_RESET_IND)
{
println("got U_RESET_IND");
}
else if ((rxByte & U_STATE_IND) == U_STATE_IND)
{
print("got U_STATE_IND: 0x");
print(rxByte, HEX); print(rxByte, HEX);
println(); #endif
} _lastByteRxTime = millis();
else if ((rxByte & U_FRAME_STATE_MASK) == U_FRAME_STATE_IND)
{
print("got U_FRAME_STATE_IND: 0x");
print(rxByte, HEX);
println();
}
else if ((rxByte & U_CONFIGURE_MASK) == U_CONFIGURE_IND)
{
print("got U_CONFIGURE_IND: 0x");
print(rxByte, HEX);
println();
}
else if (rxByte == U_FRAME_END_IND)
{
println("got U_FRAME_END_IND");
}
else if (rxByte == U_STOP_MODE_IND)
{
println("got U_STOP_MODE_IND");
}
else if (rxByte == U_SYSTEM_STAT_IND)
{
print("got U_SYSTEM_STAT_IND: 0x");
while (true)
{
int tmp = _platform.readUart();
if (tmp < 0)
continue;
print(tmp, HEX); // Check for layer-2 packets
_RxByteCnt = 0;
_xorSum = 0;
if ((rxByte & L_DATA_MASK) == L_DATA_STANDARD_IND)
{
buffer[_RxByteCnt++] = rxByte;
_xorSum ^= rxByte;
_RxByteCnt++; //convert to L_DATA_EXTENDED
_convert = true;
_rxState = RX_L_DATA;
#ifdef DBG_TRACE
println("RLS");
#endif
break; break;
} }
println(); else if ((rxByte & L_DATA_MASK) == L_DATA_EXTENDED_IND)
{
buffer[_RxByteCnt++] = rxByte;
_xorSum ^= rxByte;
_convert = false;
_rxState = RX_L_DATA;
#ifdef DBG_TRACE
println("RLX");
#endif
break;
}
// Handle all single byte packets here
else if ((rxByte & L_DATA_CON_MASK) == L_DATA_CON)
{
dataConnMsg = rxByte;
}
else if (rxByte == L_POLL_DATA_IND)
{
// not sure if this can happen
println("got L_POLL_DATA_IND");
}
else if ((rxByte & L_ACKN_MASK) == L_ACKN_IND)
{
// this can only happen in bus monitor mode
println("got L_ACKN_IND");
}
else if (rxByte == U_RESET_IND)
{
println("got U_RESET_IND");
}
else if ((rxByte & U_STATE_IND) == U_STATE_IND)
{
print("got U_STATE_IND:");
if (rxByte & 0x80) print (" SC");
if (rxByte & 0x40) print (" RE");
if (rxByte & 0x20) print (" TE");
if (rxByte & 0x10) print (" PE");
if (rxByte & 0x08) print (" TW");
println();
}
else if ((rxByte & U_FRAME_STATE_MASK) == U_FRAME_STATE_IND)
{
print("got U_FRAME_STATE_IND: 0x");
print(rxByte, HEX);
println();
}
else if ((rxByte & U_CONFIGURE_MASK) == U_CONFIGURE_IND)
{
print("got U_CONFIGURE_IND: 0x");
print(rxByte, HEX);
println();
}
else if (rxByte == U_FRAME_END_IND)
{
println("got U_FRAME_END_IND");
}
else if (rxByte == U_STOP_MODE_IND)
{
println("got U_STOP_MODE_IND");
}
else if (rxByte == U_SYSTEM_STAT_IND)
{
print("got U_SYSTEM_STAT_IND: 0x");
while (true)
{
int tmp = _platform.readUart();
if (tmp < 0)
continue;
print(tmp, HEX);
break;
}
println();
}
else
{
print("got UNEXPECTED: 0x");
print(rxByte, HEX);
println();
}
} }
else
{
print("got UNEXPECTED: 0x");
print(rxByte, HEX);
println();
}
_loopState = IDLE;
break; break;
case RX_L_DATA: case RX_L_DATA:
if (millis() - _lastByteRxTime > BYTE_TIMEOUT) if (isEOP)
{ {
_RxByteCnt = 0; _rxState = RX_WAIT_START;
_loopState = IDLE; print("EOP inside RX_L_DATA");
println("Timeout during RX_L_DATA"); printHex(" => ", buffer, _RxByteCnt);
break; break;
} }
if (!_platform.uartAvailable()) if (!_platform.uartAvailable())
break; break;
_lastByteRxTime = millis(); _lastByteRxTime = millis();
rxByte = _platform.readUart(); rxByte = _platform.readUart();
#ifdef DBG_TRACE
print(rxByte, HEX);
#endif
if (_RxByteCnt == MAX_KNX_TELEGRAM_SIZE) if (_RxByteCnt == MAX_KNX_TELEGRAM_SIZE)
{ {
_loopState = IDLE; _rxState = RX_WAIT_EOP;
println("invalid telegram size"); println("invalid telegram size");
} }
else else
@ -280,6 +293,7 @@ void TpUartDataLinkLayer::loop()
c |= 0x01; c |= 0x01;
} }
// Hint: We can send directly here, this doesn't disturb other transmissions
_platform.writeUart(c); _platform.writeUart(c);
} }
} }
@ -299,18 +313,21 @@ void TpUartDataLinkLayer::loop()
} }
if (_isEcho) if (_isEcho)
{ {
_loopState = RX_WAIT_DATA_CON; isEchoComplete = true;
} }
else else
{ {
frameBytesReceived(_receiveBuffer, _RxByteCnt + 2); frameBytesReceived(_receiveBuffer, _RxByteCnt + 2);
_loopState = IDLE;
} }
_rxState = RX_WAIT_START;
#ifdef DBG_TRACE
println("RX_WAIT_START");
#endif
} }
else else
{ {
println("frame with invalid crc ignored"); println("frame with invalid crc ignored");
_loopState = IDLE; _rxState = RX_WAIT_EOP;
} }
} }
else else
@ -318,54 +335,93 @@ void TpUartDataLinkLayer::loop()
_xorSum ^= rxByte; _xorSum ^= rxByte;
} }
break; break;
case RX_WAIT_DATA_CON: case RX_WAIT_EOP:
if (isEOP)
{
_RxByteCnt = 0;
_rxState = RX_WAIT_START;
#ifdef DBG_TRACE
println("RX_WAIT_START");
#endif
break;
}
if (!_platform.uartAvailable()) if (!_platform.uartAvailable())
break; break;
rxByte = _platform.readUart();
_lastByteRxTime = millis(); _lastByteRxTime = millis();
if ((rxByte & L_DATA_CON_MASK) == L_DATA_CON) rxByte = _platform.readUart();
{ #ifdef DBG_TRACE
//println("L_DATA_CON received"); print(rxByte, HEX);
dataConBytesReceived(_receiveBuffer, _RxByteCnt + 2, ((rxByte & SUCCESS) > 0)); #endif
_waitConfirm = false;
delete[] _sendBuffer;
_sendBuffer = 0;
_sendBufferLength = 0;
_loopState = IDLE;
}
else
{
//should not happen
println("expected L_DATA_CON not received");
dataConBytesReceived(_receiveBuffer, _RxByteCnt + 2, false);
_waitConfirm = false;
delete[] _sendBuffer;
_sendBuffer = 0;
_sendBufferLength = 0;
_loopState = IDLE;
}
break; break;
default: default:
break; break;
} }
if (_waitConfirm) // Check for spurios DATA_CONN message
if (dataConnMsg && _txState != TX_WAIT_CONN && _txState != TX_WAIT_ECHO) {
println("got unexpected L_DATA_CON");
}
switch (_txState)
{ {
if (millis() - _waitConfirmStartTime > CONFIRM_TIMEOUT) case TX_IDLE:
{ if (!isTxQueueEmpty())
println("L_DATA_CON not received within expected time"); {
uint8_t cemiBuffer[MAX_KNX_TELEGRAM_SIZE]; loadNextTxFrame();
cemiBuffer[0] = 0x29; _txState = TX_FRAME;
cemiBuffer[1] = 0; #ifdef DBG_TRACE
memcpy((cemiBuffer + 2), _sendBuffer, _sendBufferLength); println("TX_FRAME");
dataConBytesReceived(cemiBuffer, _sendBufferLength + 2, false); #endif
_waitConfirm = false; }
delete[] _sendBuffer; break;
_sendBuffer = 0; case TX_FRAME:
_sendBufferLength = 0; if (sendSingleFrameByte() == false)
if (_loopState == RX_WAIT_DATA_CON) {
_loopState = IDLE; _waitConfirmStartTime = millis();
} _txState = TX_WAIT_ECHO;
#ifdef DBG_TRACE
println("TX_WAIT_ECHO");
#endif
}
break;
case TX_WAIT_ECHO:
case TX_WAIT_CONN:
if (isEchoComplete)
{
_txState = TX_WAIT_CONN;
#ifdef DBG_TRACE
println("TX_WAIT_CONN");
#endif
}
else if (dataConnMsg)
{
bool waitEcho = (_txState == TX_WAIT_ECHO);
if (waitEcho) {
println("L_DATA_CON without echo");
}
dataConBytesReceived(_receiveBuffer, _RxByteCnt + 2, !waitEcho && ((dataConnMsg & SUCCESS) > 0));
delete[] _sendBuffer;
_sendBuffer = 0;
_sendBufferLength = 0;
_txState = TX_IDLE;
}
else if (millis() - _waitConfirmStartTime > CONFIRM_TIMEOUT)
{
println("L_DATA_CON not received within expected time");
uint8_t cemiBuffer[MAX_KNX_TELEGRAM_SIZE];
cemiBuffer[0] = 0x29;
cemiBuffer[1] = 0;
memcpy((cemiBuffer + 2), _sendBuffer, _sendBufferLength);
dataConBytesReceived(cemiBuffer, _sendBufferLength + 2, false);
delete[] _sendBuffer;
_sendBuffer = 0;
_sendBufferLength = 0;
_txState = TX_IDLE;
#ifdef DBG_TRACE
println("TX_IDLE");
#endif
}
break;
} }
} }
@ -496,6 +552,9 @@ bool TpUartDataLinkLayer::sendSingleFrameByte()
cmd[0] = U_L_DATA_END_REQ | _TxByteCnt; cmd[0] = U_L_DATA_END_REQ | _TxByteCnt;
cmd[1] = _sendBuffer[_TxByteCnt]; cmd[1] = _sendBuffer[_TxByteCnt];
#ifdef DBG_TRACE
print(cmd[1], HEX);
#endif
_platform.writeUart(cmd, 2); _platform.writeUart(cmd, 2);
_TxByteCnt++; _TxByteCnt++;

View File

@ -31,16 +31,15 @@ class TpUartDataLinkLayer : public DataLinkLayer
private: private:
bool _enabled = false; bool _enabled = false;
bool _waitConfirm = false;
uint8_t* _sendBuffer = 0; uint8_t* _sendBuffer = 0;
uint16_t _sendBufferLength = 0; uint16_t _sendBufferLength = 0;
uint8_t _receiveBuffer[MAX_KNX_TELEGRAM_SIZE]; uint8_t _receiveBuffer[MAX_KNX_TELEGRAM_SIZE];
uint8_t _loopState = 0; uint8_t _txState = 0;
uint8_t _rxState = 0;
uint16_t _RxByteCnt = 0; uint16_t _RxByteCnt = 0;
uint16_t _TxByteCnt = 0; uint16_t _TxByteCnt = 0;
uint8_t _oldIdx = 0; uint8_t _oldIdx = 0;
bool _isEcho = false; bool _isEcho = false;
bool _isAddressed = false;
bool _convert = false; bool _convert = false;
uint8_t _xorSum = 0; uint8_t _xorSum = 0;
uint32_t _lastByteRxTime; uint32_t _lastByteRxTime;