mirror of
https://github.com/thelsing/knx.git
synced 2024-12-18 19:08:18 +01:00
Use full duplex in tpuart_data_link_layer. (#161)
* TPUART full duplex handling * Stay in loop when RX_L_DATA.
This commit is contained in:
parent
fb74931bec
commit
12d0ea1ad8
@ -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,25 +78,28 @@
|
|||||||
#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
|
||||||
|
|
||||||
void TpUartDataLinkLayer::loop()
|
void TpUartDataLinkLayer::loop()
|
||||||
{
|
{
|
||||||
|
|
||||||
_receiveBuffer[0] = 0x29;
|
|
||||||
_receiveBuffer[1] = 0;
|
|
||||||
uint8_t* buffer = _receiveBuffer + 2;
|
|
||||||
uint8_t rxByte;
|
|
||||||
|
|
||||||
if (!_enabled)
|
if (!_enabled)
|
||||||
{
|
{
|
||||||
if (millis() - _lastResetChipTime > 1000)
|
if (millis() - _lastResetChipTime > 1000)
|
||||||
@ -107,33 +113,26 @@ void TpUartDataLinkLayer::loop()
|
|||||||
if (!_enabled)
|
if (!_enabled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
switch (_loopState)
|
do {
|
||||||
|
_receiveBuffer[0] = 0x29;
|
||||||
|
_receiveBuffer[1] = 0;
|
||||||
|
uint8_t* buffer = _receiveBuffer + 2;
|
||||||
|
uint8_t rxByte;
|
||||||
|
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;
|
|
||||||
}
|
|
||||||
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();
|
rxByte = _platform.readUart();
|
||||||
|
#ifdef DBG_TRACE
|
||||||
|
print(rxByte, HEX);
|
||||||
|
#endif
|
||||||
_lastByteRxTime = millis();
|
_lastByteRxTime = millis();
|
||||||
|
|
||||||
|
// Check for layer-2 packets
|
||||||
_RxByteCnt = 0;
|
_RxByteCnt = 0;
|
||||||
_xorSum = 0;
|
_xorSum = 0;
|
||||||
if ((rxByte & L_DATA_MASK) == L_DATA_STANDARD_IND)
|
if ((rxByte & L_DATA_MASK) == L_DATA_STANDARD_IND)
|
||||||
@ -142,7 +141,10 @@ void TpUartDataLinkLayer::loop()
|
|||||||
_xorSum ^= rxByte;
|
_xorSum ^= rxByte;
|
||||||
_RxByteCnt++; //convert to L_DATA_EXTENDED
|
_RxByteCnt++; //convert to L_DATA_EXTENDED
|
||||||
_convert = true;
|
_convert = true;
|
||||||
_loopState = RX_L_DATA;
|
_rxState = RX_L_DATA;
|
||||||
|
#ifdef DBG_TRACE
|
||||||
|
println("RLS");
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if ((rxByte & L_DATA_MASK) == L_DATA_EXTENDED_IND)
|
else if ((rxByte & L_DATA_MASK) == L_DATA_EXTENDED_IND)
|
||||||
@ -150,12 +152,17 @@ void TpUartDataLinkLayer::loop()
|
|||||||
buffer[_RxByteCnt++] = rxByte;
|
buffer[_RxByteCnt++] = rxByte;
|
||||||
_xorSum ^= rxByte;
|
_xorSum ^= rxByte;
|
||||||
_convert = false;
|
_convert = false;
|
||||||
_loopState = RX_L_DATA;
|
_rxState = RX_L_DATA;
|
||||||
|
#ifdef DBG_TRACE
|
||||||
|
println("RLX");
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle all single byte packets here
|
||||||
else if ((rxByte & L_DATA_CON_MASK) == L_DATA_CON)
|
else if ((rxByte & L_DATA_CON_MASK) == L_DATA_CON)
|
||||||
{
|
{
|
||||||
println("got unexpected L_DATA_CON");
|
dataConnMsg = rxByte;
|
||||||
}
|
}
|
||||||
else if (rxByte == L_POLL_DATA_IND)
|
else if (rxByte == L_POLL_DATA_IND)
|
||||||
{
|
{
|
||||||
@ -173,8 +180,12 @@ void TpUartDataLinkLayer::loop()
|
|||||||
}
|
}
|
||||||
else if ((rxByte & U_STATE_IND) == U_STATE_IND)
|
else if ((rxByte & U_STATE_IND) == U_STATE_IND)
|
||||||
{
|
{
|
||||||
print("got U_STATE_IND: 0x");
|
print("got U_STATE_IND:");
|
||||||
print(rxByte, HEX);
|
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();
|
println();
|
||||||
}
|
}
|
||||||
else if ((rxByte & U_FRAME_STATE_MASK) == U_FRAME_STATE_IND)
|
else if ((rxByte & U_FRAME_STATE_MASK) == U_FRAME_STATE_IND)
|
||||||
@ -217,24 +228,26 @@ void TpUartDataLinkLayer::loop()
|
|||||||
print(rxByte, HEX);
|
print(rxByte, HEX);
|
||||||
println();
|
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,40 +335,77 @@ 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())
|
||||||
|
{
|
||||||
|
loadNextTxFrame();
|
||||||
|
_txState = TX_FRAME;
|
||||||
|
#ifdef DBG_TRACE
|
||||||
|
println("TX_FRAME");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TX_FRAME:
|
||||||
|
if (sendSingleFrameByte() == false)
|
||||||
|
{
|
||||||
|
_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");
|
println("L_DATA_CON not received within expected time");
|
||||||
uint8_t cemiBuffer[MAX_KNX_TELEGRAM_SIZE];
|
uint8_t cemiBuffer[MAX_KNX_TELEGRAM_SIZE];
|
||||||
@ -359,14 +413,17 @@ void TpUartDataLinkLayer::loop()
|
|||||||
cemiBuffer[1] = 0;
|
cemiBuffer[1] = 0;
|
||||||
memcpy((cemiBuffer + 2), _sendBuffer, _sendBufferLength);
|
memcpy((cemiBuffer + 2), _sendBuffer, _sendBufferLength);
|
||||||
dataConBytesReceived(cemiBuffer, _sendBufferLength + 2, false);
|
dataConBytesReceived(cemiBuffer, _sendBufferLength + 2, false);
|
||||||
_waitConfirm = false;
|
|
||||||
delete[] _sendBuffer;
|
delete[] _sendBuffer;
|
||||||
_sendBuffer = 0;
|
_sendBuffer = 0;
|
||||||
_sendBufferLength = 0;
|
_sendBufferLength = 0;
|
||||||
if (_loopState == RX_WAIT_DATA_CON)
|
_txState = TX_IDLE;
|
||||||
_loopState = IDLE;
|
#ifdef DBG_TRACE
|
||||||
|
println("TX_IDLE");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
} while (_rxState == RX_L_DATA);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TpUartDataLinkLayer::sendFrame(CemiFrame& frame)
|
bool TpUartDataLinkLayer::sendFrame(CemiFrame& frame)
|
||||||
@ -496,6 +553,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++;
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user