Asynchronous communications protocol functions

Important Points

The protocol is designed for use on multi-drop master-slave arrangements such as RS485. The protocol supports a master and up to 254 slave nodes (although RS485 physically supports only up to 32 nodes on a given link). The protocol also supports broadcast, which is implemented by transmitting on a node number of 255. The protocol can also be used with a point-to-point arrangement such as RS232.

The protocol uses a half-duplex poll-response system, where the master polls one node at a time, and that node responds before the master polls that node again or moves onto another one. The only exception to this is broadcast, where the master sends out a message without expecting a response. The format of the message in each direction is as follows:

DLESTXNodeSeqNoData0 … DataNDLEETX16-bit Checksum

The protocol is fault-tolerant in that it supports message retries, and the sequence number (SeqNo above) is provided for this purpose. The master controls the sequence number, and it is recommended that when the master starts, its first message uses a sequence number of 0. Subsequent messages then increment the sequence number wrapping round after 255 to 1 (not 0). Slaves respond with the same sequence number as the message to which they are responding. If a sequence number the same as the previous, then it was because the master did not receive the reply correctly (it is retrying), and the last reply should be re-transmitted without processing the incoming data. A sequence number of 0 indicates a start-up and slaves should therefore always process the message and give a fresh response for this sequence number.

In order for the DLE-STX and DLE-ETX sequences to be unique, any data byte in the data portion of the message that is a DLE byte is followed by a further DLE byte. The node and sequence number do not conform to this rule.

The checksum is a CRC-16 of all the bytes between (and not including) the DLE-STX and the DLE-ETX.

If a message arrives while one is being transmitted (and it is not listening to its own outgoing message), then there is a sequencing problem. On the slave, it is advisable not to respond in this situation, and on the master it is advisable to wait for another response to come in before proceeding.

In systems where the same transmission medium is used for transmission and reception (such as two-wire RS485 or single-frequency radio) there may be a problem with turnaround time, in that slaves may respond too quickly, before the master has had a chance to switch hardware modes from transmission to reception. This is particularly likely where the master is a non-embedded computer such as a PC running Windows or Unix. In this situation, the slave must delay its response, or separate media for transmit and received must be used, such as 4-wire RS485, where the master transmits on one pair and receives on another.

When communicating between dissimilar systems, issues of byte ordering and structure packing become relevant. Routines are provided to normalise the data format over the transmission medium, to solve this particular problem. A fully packed (1-byte aligned) big-ending format is used (big-endian = high order byte first). This is the easiest format to interpret on a communications analyser, and is commonly used on many different types of network.

execAsyncBufferOut()

Function prototype:

word execAsyncBufferOut (byte Node, byte *Raw, byte *TxBuf, 
                         byte RawSize, byte SeqNo);

Parameters: Node the target (recipient) node number
Raw the unformatted data to be transmitted
TxBuf the buffer to receive the formatted data
RawSize the size of the unformatted data
SeqNo the sequence number of the message

Returns: the number of formatted bytes placed in TxBuf.

This function puts the data to be transmitted into a protocol packet as per the protocol described in section 2.1.5.1.

The maximum size of the formatted data is 2*RawSize + 8. This assumes that all the data bytes are 0x10 (DLE). Clearly, this is unlikely to be the case in practice, and a sufficiently large buffer should be provided to cover all eventualities of the application in question.

execAsyncByteIn()

Function prototype:

boolean execAsyncByteIn (byte Value, 
                         struct execAsyncStateDef *State, byte *Buffer);

Parameters: Value the byte just received, to be processed
State the state and progress of the incoming data stream
Buffer the buffer receiving the incoming data

Returns: true when a complete or corrupted message has been received, false otherwise.

This routine is designed to be called from within the receive interrupt routine, where bytes are received one at a time.

See section 2.5.6 for a description of State and how the information should be used.

This function places only processed bytes in the buffer provided. All the protocol has been stripped by the time data has finished arriving in this buffer.

execPackData()

Function prototype:

word execPackDataEx (byte *Target, byte *Source,
                     struct execStructDef VCONST *Descriptor);
                     word execPackData (byte *Buffer,
                     struct execStructDef VCONST *Descriptor);

Parameters: Target the buffer where the packed data is to be saved
Source the buffer containing the data to be packed
Buffer the buffer for packing the data in-place
Descriptor an array of structure item description structures

Returns: the size of the packed data in bytes.

This routine reformats the data provided into a format for transmitting over a network or a data link. All structure padding is removed and the bytes are re-ordered (if necessary) to be big-endian. In the second version (above) of this function, the transformation is done in-place, and the old data is therefore effectively overwritten.

The structure and layout of the descriptor array is described in section 2.5.6.

execUnPackData()

Function prototype:

word execUnPackDataEx (byte *Target, byte *Source,
                       struct execStructDef VCONST *Descriptor);
                       word execUnPackData (byte *Buffer, 
                       struct execStructDef VCONST *Descriptor);

Parameters: Target the buffer where the unpacked data is to be saved
Source the buffer containing the data to be unpacked
Buffer the buffer for unpacking the data in-place
Descriptor an array of structure item description structures

Returns: the size of the unpacked data in bytes.

This routine reformats the data provided from a format for transmitting over a network or a data link into a format that corresponds to the structure local to the CPU/compiler. The relevant structure padding is and the bytes are re-ordered (if appropriate to the CPU) to be little-endian. In the second version (above) of this function, the transformation is done in-place, and the old data is therefore effectively overwritten.

The structure and layout of the descriptor array is described in section 2.5.6.