mirror of
https://github.com/thelsing/knx.git
synced 2024-12-18 19:08:18 +01:00
Reimplementation of the tpuart data link layer
This commit is contained in:
parent
6b2ac7e50b
commit
1ee78a45d1
301
src/knx/tp_frame.h
Normal file
301
src/knx/tp_frame.h
Normal file
@ -0,0 +1,301 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "cemi_frame.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
// Means that the frame is invalid
|
||||||
|
#define TP_FRAME_FLAG_INVALID 0b10000000
|
||||||
|
|
||||||
|
// Means that the frame is an extended frame
|
||||||
|
#define TP_FRAME_FLAG_EXTENDED 0b01000000
|
||||||
|
|
||||||
|
// Means that the frame has been repeated
|
||||||
|
#define TP_FRAME_FLAG_REPEATED 0b00100000
|
||||||
|
|
||||||
|
// Means that the frame comes from the device itself
|
||||||
|
#define TP_FRAME_FLAG_ECHO 0b00010000
|
||||||
|
|
||||||
|
// Means that the frame is processed by this device
|
||||||
|
#define TP_FRAME_FLAG_ADDRESSED 0b00000100
|
||||||
|
|
||||||
|
// Means that the frame has been acked by this device.
|
||||||
|
#define TP_FRAME_FLAG_ACKING 0b00000010
|
||||||
|
|
||||||
|
// Means that the frame has been acked by other (Busmontior)
|
||||||
|
#define TP_FRAME_FLAG_ACKED 0b00000001
|
||||||
|
|
||||||
|
class TpFrame
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
uint8_t *_data;
|
||||||
|
uint16_t _size;
|
||||||
|
uint16_t _maxSize;
|
||||||
|
uint8_t _flags = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sets a few flags based on the control byte
|
||||||
|
*/
|
||||||
|
inline void presetFlags()
|
||||||
|
{
|
||||||
|
if (isExtended())
|
||||||
|
addFlags(TP_FRAME_FLAG_EXTENDED);
|
||||||
|
|
||||||
|
if (isRepeated())
|
||||||
|
addFlags(TP_FRAME_FLAG_REPEATED);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
/*
|
||||||
|
* Convert a CemiFrame into a TpFrame
|
||||||
|
*/
|
||||||
|
TpFrame(CemiFrame &cemiFrame)
|
||||||
|
{
|
||||||
|
_size = cemiFrame.telegramLengthtTP();
|
||||||
|
_maxSize = cemiFrame.telegramLengthtTP();
|
||||||
|
_data = (uint8_t *)malloc(cemiFrame.telegramLengthtTP());
|
||||||
|
cemiFrame.fillTelegramTP(_data);
|
||||||
|
presetFlags();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a TpFrame with a reserved space.
|
||||||
|
* Used for incoming parsing.
|
||||||
|
*/
|
||||||
|
TpFrame(uint16_t maxSize = 263)
|
||||||
|
: _maxSize(maxSize)
|
||||||
|
{
|
||||||
|
_data = new uint8_t[_maxSize];
|
||||||
|
_size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free the data area
|
||||||
|
*/
|
||||||
|
~TpFrame()
|
||||||
|
{
|
||||||
|
free(_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add a byte at end.
|
||||||
|
* Used for incoming parsing.
|
||||||
|
*/
|
||||||
|
inline void addByte(uint8_t byte)
|
||||||
|
{
|
||||||
|
if (!isFull())
|
||||||
|
{
|
||||||
|
_data[_size] = byte;
|
||||||
|
_size++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read meta data for flags
|
||||||
|
if (_size == 1)
|
||||||
|
presetFlags();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Current frame size. This may differ from the actual size as long as the frame is not complete.
|
||||||
|
*/
|
||||||
|
inline uint16_t size()
|
||||||
|
{
|
||||||
|
return _size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the assigned flags
|
||||||
|
*/
|
||||||
|
inline uint16_t flags()
|
||||||
|
{
|
||||||
|
return _flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Adds one or more flags
|
||||||
|
*/
|
||||||
|
inline void addFlags(uint8_t flags)
|
||||||
|
{
|
||||||
|
_flags |= flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns a pointer to the data
|
||||||
|
*/
|
||||||
|
inline uint8_t *data()
|
||||||
|
{
|
||||||
|
return _data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the byte corresponding to the specified position
|
||||||
|
*/
|
||||||
|
inline uint8_t data(uint16_t pos)
|
||||||
|
{
|
||||||
|
return _data[pos];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Resets the internal values to refill the frame.
|
||||||
|
*/
|
||||||
|
inline void reset()
|
||||||
|
{
|
||||||
|
_size = 0;
|
||||||
|
_flags = 0;
|
||||||
|
// It is important to fill the _data with zeros so that the length is 0 as long as the value has not yet been read in.
|
||||||
|
memset(_data, 0x0, _maxSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Checks whether the frame has been imported completely
|
||||||
|
*/
|
||||||
|
inline bool isFull()
|
||||||
|
{
|
||||||
|
return _size >= (_size >= 7 ? fullSize() : _maxSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns is the frame exteneded or not
|
||||||
|
*/
|
||||||
|
inline bool isExtended()
|
||||||
|
{
|
||||||
|
return (_data[0] & 0xD3) == 0x10;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the source
|
||||||
|
* Assumes that enough data has been imported.
|
||||||
|
*/
|
||||||
|
inline uint16_t source()
|
||||||
|
{
|
||||||
|
return isExtended() ? (_data[2] << 8) + _data[3] : (_data[1] << 8) + _data[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string humanSource()
|
||||||
|
{
|
||||||
|
uint16_t value = source();
|
||||||
|
char buffer[10];
|
||||||
|
sprintf(buffer, "%02i.%02i.%03i", (value >> 12 & 0b1111), (value >> 8 & 0b1111), (value & 0b11111111));
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string humanDestination()
|
||||||
|
{
|
||||||
|
uint16_t value = destination();
|
||||||
|
char buffer[10];
|
||||||
|
if (isGroupAddress())
|
||||||
|
sprintf(buffer, "%02i/%02i/%03i", (value >> 11 & 0b1111), (value >> 8 & 0b111), (value & 0b11111111));
|
||||||
|
else
|
||||||
|
sprintf(buffer, "%02i.%02i.%03i", (value >> 12 & 0b1111), (value >> 8 & 0b1111), (value & 0b11111111));
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the destination
|
||||||
|
* Assumes that enough data has been imported.
|
||||||
|
*/
|
||||||
|
inline uint16_t destination()
|
||||||
|
{
|
||||||
|
return isExtended() ? (_data[4] << 8) + _data[5] : (_data[3] << 8) + _data[4];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the payload size (with checksum)
|
||||||
|
* Assumes that enough data has been imported.
|
||||||
|
*/
|
||||||
|
inline uint8_t payloadSize()
|
||||||
|
{
|
||||||
|
return isExtended() ? _data[6] : _data[5] & 0b1111;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the header size
|
||||||
|
*/
|
||||||
|
inline uint8_t headerSize()
|
||||||
|
{
|
||||||
|
return isExtended() ? 9 : 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the frame size based on header and payload size.
|
||||||
|
* Assumes that enough data has been imported.
|
||||||
|
*/
|
||||||
|
inline uint16_t fullSize()
|
||||||
|
{
|
||||||
|
return headerSize() + payloadSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns if the destination is a group address
|
||||||
|
* Assumes that enough data has been imported.
|
||||||
|
*/
|
||||||
|
inline bool isGroupAddress()
|
||||||
|
{
|
||||||
|
return isExtended() ? (_data[1] >> 7) & 0b1 : (_data[5] >> 7) & 0b1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculates the size of a CemiFrame. A CemiFrame has 2 additional bytes at the beginning.
|
||||||
|
* An additional byte is added to a standard frame, as this still has to be converted into an extendend.
|
||||||
|
*/
|
||||||
|
uint16_t cemiSize()
|
||||||
|
{
|
||||||
|
return fullSize() + (isExtended() ? 2 : 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a buffer and converts the TpFrame into a CemiFrame.
|
||||||
|
* Important: After processing (i.e. also after using the CemiFrame), the reference must be released manually.
|
||||||
|
*/
|
||||||
|
uint8_t *cemiData()
|
||||||
|
{
|
||||||
|
uint8_t *cemiBuffer = (uint8_t *)malloc(cemiSize());
|
||||||
|
|
||||||
|
// Das CEMI erwartet die Daten im Extended format inkl. zwei zusätzlicher Bytes am Anfang.
|
||||||
|
cemiBuffer[0] = 0x29;
|
||||||
|
cemiBuffer[1] = 0x0;
|
||||||
|
cemiBuffer[2] = _data[0];
|
||||||
|
if (isExtended())
|
||||||
|
{
|
||||||
|
memcpy(cemiBuffer + 2, _data, fullSize());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cemiBuffer[3] = _data[5] & 0xF0;
|
||||||
|
memcpy(cemiBuffer + 4, _data + 1, 4);
|
||||||
|
cemiBuffer[8] = _data[5] & 0x0F;
|
||||||
|
memcpy(cemiBuffer + 9, _data + 6, cemiBuffer[8] + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return cemiBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Checks whether the frame is complete and valid.
|
||||||
|
*/
|
||||||
|
inline bool isValid()
|
||||||
|
{
|
||||||
|
if (!isComplete())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uint8_t sum = 0;
|
||||||
|
const uint16_t s = fullSize() - 1;
|
||||||
|
for (uint16_t i = 0; i < s; i++)
|
||||||
|
sum ^= _data[i];
|
||||||
|
return _data[s] == (uint8_t)~sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Checks whether the frame is long enough to match the length specified in the frame
|
||||||
|
*/
|
||||||
|
inline bool isComplete()
|
||||||
|
{
|
||||||
|
return _size == fullSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool isRepeated()
|
||||||
|
{
|
||||||
|
return !(_data[0] & 0b100000);
|
||||||
|
}
|
||||||
|
};
|
File diff suppressed because it is too large
Load Diff
@ -3,14 +3,26 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#ifdef USE_TP
|
#ifdef USE_TP
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include "data_link_layer.h"
|
#include "data_link_layer.h"
|
||||||
|
#include "tp_frame.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
#define MAX_KNX_TELEGRAM_SIZE 263
|
#define MAX_KNX_TELEGRAM_SIZE 263
|
||||||
|
#ifndef MAX_RX_QUEUE_BYTES
|
||||||
|
#define MAX_RX_QUEUE_BYTES MAX_KNX_TELEGRAM_SIZE + 50
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// __time_critical_func fallback
|
||||||
|
#ifndef ARDUINO_ARCH_RP2040
|
||||||
|
#define __time_critical_func(X) X
|
||||||
|
#define __isr
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void printFrame(TpFrame* tpframe);
|
||||||
|
|
||||||
class ITpUartCallBacks
|
class ITpUartCallBacks
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~ITpUartCallBacks() = default;
|
virtual ~ITpUartCallBacks() = default;
|
||||||
virtual bool isAckRequired(uint16_t address, bool isGrpAddr) = 0;
|
virtual bool isAckRequired(uint16_t address, bool isGrpAddr) = 0;
|
||||||
};
|
};
|
||||||
@ -24,56 +36,129 @@ class TpUartDataLinkLayer : public DataLinkLayer
|
|||||||
TpUartDataLinkLayer(DeviceObject& devObj, NetworkLayerEntity& netLayerEntity,
|
TpUartDataLinkLayer(DeviceObject& devObj, NetworkLayerEntity& netLayerEntity,
|
||||||
Platform& platform, ITpUartCallBacks& cb, DataLinkLayerCallbacks* dllcb = nullptr);
|
Platform& platform, ITpUartCallBacks& cb, DataLinkLayerCallbacks* dllcb = nullptr);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void loop();
|
void loop();
|
||||||
void enabled(bool value);
|
void enabled(bool value);
|
||||||
bool enabled() const;
|
bool enabled() const;
|
||||||
DptMedium mediumType() const override;
|
DptMedium mediumType() const override;
|
||||||
|
bool reset();
|
||||||
|
void monitor();
|
||||||
|
void stop(bool state);
|
||||||
|
void requestBusy(bool state);
|
||||||
|
void forceAck(bool state);
|
||||||
|
void setRepetitions(uint8_t nack, uint8_t busy);
|
||||||
|
// Alias
|
||||||
|
void setFrameRepetition(uint8_t nack, uint8_t busy);
|
||||||
|
bool isConnected();
|
||||||
|
bool isMonitoring();
|
||||||
|
bool isStopped();
|
||||||
|
bool isBusy();
|
||||||
|
|
||||||
|
#ifdef USE_TP_RX_QUEUE
|
||||||
|
void processRxISR();
|
||||||
|
#endif
|
||||||
|
#ifdef NCN5120
|
||||||
|
void powerControl(bool state);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uint32_t getRxInvalidFrameCounter();
|
||||||
|
uint32_t getRxProcessdFrameCounter();
|
||||||
|
uint32_t getRxIgnoredFrameCounter();
|
||||||
|
uint32_t getRxUnknownControlCounter();
|
||||||
|
uint8_t getMode();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _enabled = false;
|
// Frame
|
||||||
uint8_t* _sendBuffer = 0;
|
struct knx_tx_queue_entry_t
|
||||||
uint16_t _sendBufferLength = 0;
|
|
||||||
uint8_t _receiveBuffer[MAX_KNX_TELEGRAM_SIZE];
|
|
||||||
uint8_t _txState = 0;
|
|
||||||
uint8_t _rxState = 0;
|
|
||||||
uint16_t _RxByteCnt = 0;
|
|
||||||
uint16_t _TxByteCnt = 0;
|
|
||||||
uint8_t _oldIdx = 0;
|
|
||||||
bool _isEcho = false;
|
|
||||||
bool _convert = false;
|
|
||||||
uint8_t _xorSum = 0;
|
|
||||||
uint32_t _lastByteRxTime;
|
|
||||||
uint32_t _lastByteTxTime;
|
|
||||||
uint32_t _lastLoopTime;
|
|
||||||
uint32_t _waitConfirmStartTime = 0;
|
|
||||||
uint32_t _lastResetChipTime = 0;
|
|
||||||
|
|
||||||
struct _tx_queue_frame_t
|
|
||||||
{
|
{
|
||||||
uint8_t* data;
|
TpFrame* frame;
|
||||||
uint16_t length;
|
knx_tx_queue_entry_t* next = nullptr;
|
||||||
_tx_queue_frame_t* next;
|
|
||||||
|
knx_tx_queue_entry_t(TpFrame* tpFrame)
|
||||||
|
: frame(tpFrame)
|
||||||
|
{
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _tx_queue_t
|
// TX Queue
|
||||||
|
struct knx_tx_queue_t
|
||||||
{
|
{
|
||||||
_tx_queue_frame_t* front = NULL;
|
knx_tx_queue_entry_t* front = nullptr;
|
||||||
_tx_queue_frame_t* back = NULL;
|
knx_tx_queue_entry_t* back = nullptr;
|
||||||
} _tx_queue;
|
} _txFrameQueue;
|
||||||
|
|
||||||
void addFrameTxQueue(CemiFrame& frame);
|
TpFrame* _txFrame = nullptr;
|
||||||
bool isTxQueueEmpty();
|
TpFrame* _rxFrame = nullptr;
|
||||||
void loadNextTxFrame();
|
|
||||||
bool sendSingleFrameByte();
|
volatile bool _stopped = false;
|
||||||
|
volatile bool _connected = false;
|
||||||
|
volatile bool _monitoring = false;
|
||||||
|
volatile bool _busy = false;
|
||||||
|
volatile bool _initialized = false;
|
||||||
|
|
||||||
|
volatile uint32_t _stateTime = 0;
|
||||||
|
volatile uint8_t _rxState = 0;
|
||||||
|
volatile uint8_t _txState = 0;
|
||||||
|
volatile uint32_t _rxProcessdFrameCounter = 0;
|
||||||
|
volatile uint32_t _rxInvalidFrameCounter = 0;
|
||||||
|
volatile uint32_t _rxIgnoredFrameCounter = 0;
|
||||||
|
volatile uint32_t _rxUnkownControlCounter = 0;
|
||||||
|
volatile bool _rxMarker = false;
|
||||||
|
volatile bool _rxOverflow = false;
|
||||||
|
volatile uint8_t _tpState = 0x0;
|
||||||
|
volatile uint32_t _txLastTime = 0;
|
||||||
|
volatile uint32_t _rxLastTime = 0;
|
||||||
|
volatile bool _forceAck = false;
|
||||||
|
|
||||||
|
inline bool markerMode();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* bits
|
||||||
|
*
|
||||||
|
* 5-7 Busy (Default 11 = 3)
|
||||||
|
* 0-3 Nack (Default 11 = 3)
|
||||||
|
*/
|
||||||
|
volatile uint8_t _repetitions = 0b00110011;
|
||||||
|
|
||||||
|
// to prevent parallel rx processing by isr (when using)
|
||||||
|
volatile bool _rxProcessing = false;
|
||||||
|
|
||||||
|
volatile uint32_t _lastStateRequest = 0;
|
||||||
|
|
||||||
|
// void loadNextTxFrame();
|
||||||
|
inline bool processTxFrameBytes();
|
||||||
bool sendFrame(CemiFrame& frame);
|
bool sendFrame(CemiFrame& frame);
|
||||||
void frameBytesReceived(uint8_t* buffer, uint16_t length);
|
void rxFrameReceived(TpFrame* frame);
|
||||||
void dataConBytesReceived(uint8_t* buffer, uint16_t length, bool success);
|
void dataConBytesReceived(uint8_t* buffer, uint16_t length, bool success);
|
||||||
void enterRxWaitEOP();
|
|
||||||
bool resetChip();
|
void processRx(bool isr = false);
|
||||||
bool resetChipTick();
|
void checkConnected();
|
||||||
void stopChip();
|
void processRxByte();
|
||||||
|
void processTxQueue();
|
||||||
|
void processRxFrameComplete();
|
||||||
|
inline void processRxFrame(TpFrame* tpFrame);
|
||||||
|
void pushTxFrameQueue(TpFrame* tpFrame);
|
||||||
|
void requestState();
|
||||||
|
void requestConfig();
|
||||||
|
inline void processRxFrameByte(uint8_t byte);
|
||||||
|
|
||||||
|
#ifdef USE_TP_RX_QUEUE
|
||||||
|
// Es muss ein Extended Frame rein passen + 1Byte je erlaubter ms Verzögerung
|
||||||
|
volatile uint8_t _rxBuffer[MAX_RX_QUEUE_BYTES] = {};
|
||||||
|
volatile uint16_t _rxBufferFront = 0;
|
||||||
|
volatile uint16_t _rxBufferRear = 0;
|
||||||
|
volatile uint8_t _rxBufferCount = 0;
|
||||||
|
|
||||||
|
void pushByteToRxQueue(uint8_t byte);
|
||||||
|
uint8_t pullByteFromRxQueue();
|
||||||
|
uint16_t availableInRxQueue();
|
||||||
|
void pushRxFrameQueue();
|
||||||
|
void processRxQueue();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
inline bool isrLock(bool blocking = false);
|
||||||
|
inline void isrUnlock();
|
||||||
|
inline void clearUartBuffer();
|
||||||
|
inline void connected(bool state = true);
|
||||||
|
|
||||||
ITpUartCallBacks& _cb;
|
ITpUartCallBacks& _cb;
|
||||||
DataLinkLayerCallbacks* _dllcb;
|
DataLinkLayerCallbacks* _dllcb;
|
||||||
|
Loading…
Reference in New Issue
Block a user