Radio Control Layer (RCL)
NESB PTX Command Handler

Introduction

A PTX device normally acts as the main transmitter in an NESB network. Depending on user configuration, the handler acts similarly to the Generic Tx Command handler, or it can add automatic retransmission of unacknowledged packets.

The following sections describe how the command can be configured and used, its life cycle, and how it fits into the RCL architecture.

Usage

In order to submit a PTX command, the following steps must have taken place:

  1. RCL has been initialized (See RCL_init) and a handle must exist (See RCL_open).
  2. The RCL_CMD_NESB_PTX_t command has been initialized and configured.
  3. The Tx buffer has been set up.
  4. An RX buffer has been set up to receive the ACK (if configuration demands it).

Once these steps have been completed, RCL_Command_submit and RCL_Command_pend are called to effectively send a packet and then wait for the command to conclude.

As with any command handler, the application must built packets so that they are compatible with the internal packet format used in the LRF. Having said this, NESB packets also have a specific packet format that needs to be considered at the application level.

This can be accomplished by using a struct to define the various fields that need to be considered when building or checking the content of the packet.

#define PKT_LEN 200
#define HDR_LEN 3
#define NUM_PKT 4
#define NUM_PAD 3
#define NUM_RX_BUF 1
#define MULTI_BUF_SZ 2048
#define FREQUENCY 2470000000
#define SYNCWORD 0xAB0B51DE
#define NESB_ADDR 0xAC9825CB
#define NESB_hdrDef_Default() \
{ \
.numHdrBits = 11, \
.numLenBits = 8, \
.lenPos = 3, \
.lenOffset = 0, \
.numPad = 3, \
}
#define NESB_hdrDef_DefaultRuntime() (ExampleRCL_NesbHdrDef) NESB_hdrDef_Default()
typedef struct {
uint8_t numHdrBits;
uint8_t numLenBits;
uint8_t lenPos;
int8_t lenOffset;
uint8_t numPad;
} ExampleRCL_NesbHdrDef;

Taking this into consideration, packet generation needs to consider how the 11-bit header is placed in the LRF FIFO. the following function shows one way to accomplish this:

void generatePackets(RCL_Buffer_TxBuffer **txBuffers, uint32_t numPkt, uint32_t pktLen,
ExampleRCL_NesbHdrDef *hdrDef, uint32_t address, uint32_t addressLen)
{
union
{
uint32_t value;
uint8_t bytes[4];
} nesbAddress;
union
{
uint16_t value;
uint8_t bytes[2];
} nesbHeader;
uint8_t hdrLen = (hdrDef->numHdrBits + 7) / 8;
uint8_t seqNum = 0; // Start with a seq number equal to zero
uint8_t noAckBit = 0; // Always set the noAckBit to zero
nesbAddress.value = address;
for (int i = 0; i < numPkt; i++)
{
uint8_t *txData;
/* Initialize Tx Buffer */
txData = RCL_TxBuffer_init(txBuffers[i], hdrDef->numPad, hdrLen, pktLen);
/* Build the 11 bit NESB header */
nesbHeader.value = ((pktLen << 3) | (seqNum << 1) | noAckBit);
/* Write NESB header to buffer */
for (int i = 0; i < hdrLen; i++)
{
txData[i] = nesbHeader.bytes[i];
}
/* Write NESB address to buffer. Address comes immediately after header */
for (int i = 0; i < addressLen; i++)
{
txData[hdrLen + i] = nesbAddress.bytes[i];
}
/* Write the payload */
if(pktLen >= addressLen)
{
uint32_t payloadLength = pktLen - addressLen;
for(uint32_t i = 0; i < payloadLength; i++)
{
txData[hdrLen + addressLen + i] = i & 0xFF;
}
}
/* Update packet ID (seq number) for next packet */
seqNum = (seqNum + 1) % 4;
}
}

Command configuration and submitting is similar to generic command handlers.

void runNesbPtx(void)
{
ExampleRCL_NesbHdrDef hdrDef = NESB_hdrDef_DefaultRuntime();
List_List multiBuffers = { 0 };
uint32_t address = NESB_ADDR;
uint32_t addressLen = 4;
/* Set up Tx and Rx buffers considering 4 packets, packet length of 200 bytes, 2 header bytes and 3 padding bytes */
RCL_Buffer_TxBuffer *txBuffers[NUM_PKT];
uint32_t pktBuffer[NUM_PKT][RCL_TxBuffer_len_u32(NUM_PAD, HDR_LEN, PKT_LEN)];
uint32_t rxMultiBuffer[NUM_RX_BUF][MULTI_BUF_SZ / 4];
RCL_Handle h = RCL_open(&rclClient, &LRF_configNesb);
/* Declare command */
RCL_CmdNesbPtx cmd;
/* Command configuration */
cmd.common.scheduling = RCL_Schedule_AbsTime;
cmd.common.runtime.callback = defaultCallback;
cmd.common.runtime.rclCallbackMask.value = RCL_EventLastCmdDone.value | RCL_EventRxEntryAvail.value;
cmd.rfFrequency = FREQUENCY;
cmd.syncWord = SYNCWORD;
cmd.config.hdrConf = 1; // Insert SEQ and NO_ACK from Tx buffer
cmd.config.autoRetransmitMode = 3; // Always listen for ACK and retransmit if missing.
cmd.maxRetrans = 5; // Attempt 5 retransmissions if ACK is not received.
cmd.retransDelay = 50000; // Wait 50000 Systim ticks (i.e. 12.5 ms) between retransmissions.
cmd.common.status = RCL_CommandStatus_Idle;
for (int i = 0; i < NUM_PKT; i++)
{
txBuffers[i] = (RCL_Buffer_TxBuffer *)pktBuffer[i];
}
/* Set up Rx buffer to receive ACK packets */
for (int i = 0; i < NUM_RX_BUF; i++)
{
RCL_MultiBuffer *multiBuffer = (RCL_MultiBuffer *)rxMultiBuffer[i];
RCL_MultiBuffer_init(multiBuffer, MULTI_BUF_SZ);
RCL_MultiBuffer_put(&multiBuffers, multiBuffer);
}
/* Clear multibuffer before starting the operation */
RCL_MultiBuffer *multiBuffer = RCL_MultiBuffer_head(&multiBuffers);
while (multiBuffer != NULL)
{
RCL_MultiBuffer_clear(multiBuffer);
multiBuffer = RCL_MultiBuffer_next(multiBuffer);
}
cmd.rxBuffers = multiBuffers;
/* Generate the packets that will be sent and fill Tx buffer */
generatePackets(txBuffers, NUM_PKT, PKT_LEN, &hdrDef, address, addressLen);
for(int i = 0; i < NUM_PKT; i++)
{
RCL_TxBuffer_put(&cmd.txBuffers, txBuffers[i]);
}
/* Schedule first command considering a start delay of 600 us */
for (int i = 0; i < NUM_PKT; i++)
{
cmd.common.status = RCL_CommandStatus_Idle;
cmd.common.timing.absStartTime = nextTime;
/* Wait for command to conclude */
/* Schedule next command considering the same start delay */
}
/* Check ACK packets */
for (int i = 0; i < NUM_PKT; i++)
{
RCL_Buffer_DataEntry *rxPkt = RCL_MultiBuffer_RxEntry_get(&cmd.rxBuffers, NULL);
if (rxPkt != NULL)
{
/* Do something with the received ACK */
}
}
}

Architecture

The NESB Ptx command handler has a life cycle that depends on several things. If configuration is such that an acknowledgement is expected, the device will automatically switch from Tx mode to Rx mode to listen for an acknowledge coming from an associated PRX device in the network. Furthermore, if retransmission of unacknowledged packets is enabled, the PTX command handler will automatically switch back and forth between Tx mode and Rx mode in order to retransmit the packet. This will go on for a user-defined number of times and with a user-defined retransmission delay. If a valid acknowledgement is received, or if a packet is transmitted and no acknowledgement is expected, the command will conclude not before incrementing a sequence number that is used by the PRX device to differentiate between new packets and retransmitted packets.

Once this has happened, the callback and the command status can be used for error checking and the application can proceed according to its specification.

NESB PTX handler state machine
RCL Event (In) Description
setup Setup has been performed
timerStart Timer-based start signalled
RCL Event (Out) Description
lastCmdDone The RCL is finished with the command
cmdStarted Command handler has accepted and started executing
rxBufferFinished An RX multi-buffer is finished
rxEntryAvail An RX entry has been made available
LRF Event Description
opDone The PBE operation has finished
opError Something went wrong. Cause located in the PBE ENDCAUSE register
rxOk Packet received with CRC OK and not to be ignored by the MCU
rxNok Packet received with CRC error
RCL_TxBuffer_put
void RCL_TxBuffer_put(List_List *list, RCL_Buffer_TxBuffer *elem)
Function to atomically put an elem onto the end of a Tx Buffer list.
Definition: RCL_Buffer.c:58
RCL_Command_pend
RCL_CommandStatus RCL_Command_pend(RCL_Command_Handle c)
Wait for a submitted command to complete.
Definition: RCL.c:672
RCL_open
RCL_Handle RCL_open(RCL_Client *c, const LRF_Config *lrfConfig)
Initializes an RCL client instance.
Definition: RCL.c:559
RCL_SCHEDULER_SYSTIM_US
#define RCL_SCHEDULER_SYSTIM_US(x)
Definition: RCL_Scheduler.h:45
RCL_Command_submit
RCL_CommandStatus RCL_Command_submit(RCL_Handle h, RCL_Command_Handle c)
Submit RCL command object to be scheduled for execution.
Definition: RCL.c:638
RCL_Scheduler_getCurrentTime
static uint32_t RCL_Scheduler_getCurrentTime(void)
Get current time.
Definition: RCL_Scheduler.h:233
RCL_MultiBuffer_put
void RCL_MultiBuffer_put(List_List *list, RCL_MultiBuffer *elem)
Function to atomically put an elem onto the end of a multi buffer list.
Definition: RCL_Buffer.c:117
RCL_MultiBuffer_RxEntry_get
RCL_Buffer_DataEntry * RCL_MultiBuffer_RxEntry_get(List_List *list, List_List *consumedBuffers)
Function to get the first entry in a MultiBuffer list.
Definition: RCL_Buffer.c:127
RCL_EventLastCmdDone
#define RCL_EventLastCmdDone
Definition: RCL_Event.h:40
RCL_CommandStatus_Idle
@ RCL_CommandStatus_Idle
Definition: RCL_Command.h:84
RCL_TxBuffer_init
uint8_t * RCL_TxBuffer_init(RCL_Buffer_TxBuffer *buffer, uint32_t numPad, uint32_t hdrLen, uint32_t dataLen)
Function to initialize a TX buffer entry for use by RCL.
Definition: RCL_Buffer.c:69
RCL_MultiBuffer_head
static RCL_MultiBuffer * RCL_MultiBuffer_head(List_List *list)
Function to return the head of a MultiBuffer list.
Definition: RCL_Buffer.h:257
RCL_MultiBuffer_clear
void RCL_MultiBuffer_clear(RCL_MultiBuffer *buffer)
Function to clear a multi buffer entry for re-use by RCL.
Definition: RCL_Buffer.c:95
RCL_Schedule_AbsTime
@ RCL_Schedule_AbsTime
Definition: RCL_Command.h:144
RCL_init
int RCL_init(void)
Initializes the RCL driver state.
Definition: RCL.c:542
RCL_MultiBuffer_init
void RCL_MultiBuffer_init(RCL_MultiBuffer *buffer, size_t size)
Function to initialize a multi buffer entry for use by RCL.
Definition: RCL_Buffer.c:105
RCL_CmdNesbPtx_DefaultRuntime
#define RCL_CmdNesbPtx_DefaultRuntime()
Definition: generic.h:354
RCL_EventRxEntryAvail
#define RCL_EventRxEntryAvail
Definition: RCL_Event.h:41
RCL_MultiBuffer_next
static RCL_MultiBuffer * RCL_MultiBuffer_next(RCL_MultiBuffer *elem)
Function to return the next elem in a linked list of MultiBuffers.
Definition: RCL_Buffer.h:272
RCL_TxBuffer_len_u32
#define RCL_TxBuffer_len_u32(numPad, hdrLen, dataLen)
Total length of a TX buffer in 32-bit words, including all fields and padding.
Definition: RCL_Buffer.h:158