Radio Control Layer (RCL)
NESB PRX Command Handler

Introduction

A PRX device normally acts as the main receiver in an NESB network. Depending on user configuration, the handler acts similarly to the Generic Rx Command handler, or it can add automatic acknowledgement and address filtering.

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 PRX 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_PRX_t command has been initialized and configured.
  3. A Multibuffer has been set up to receive the incoming packets.

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, but depending on the desired auto acknowledgment behavior, it might be necessary to also configure the sycnword specific behavior.

#define NUM_RX_BUF 1
#define MULTI_BUF_SZ 2048
void runNesbPrx(void)
{
uint32_t rxMultiBuffer[NUM_RX_BUF][MULTI_BUF_SZ / 4];
List_List multiBuffers = { 0 };
RCL_Handle h = RCL_open(&rclClient, &LRF_configNesb);
/* Declare command */
RCL_CmdNesbPrx cmd;
RCL_StatsNesb rxStats;
/* 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.syncWordA = SYNCWORD;
cmd.syncWord[0].address = NESB_ADDR; // Set NESB address
cmd.addrLen = 4; // Consider 4 bytes for the NESB address
cmd.syncWord[0].seqValid = 1; // Only accept packets with sequence number and CRC different from the previous one
cmd.syncWord[0].autoAckMode = 3; // Enable auto-acknowledgement regardless of received NO_ACK.
/* Stats configuration */
rxStats.config.accumulate = 1;
rxStats.nTx = 0;
rxStats.nRxOk = 0;
rxStats.nRxNok = 0;
rxStats.nRxIgnored = 0;
rxStats.nRxAddrMismatch = 0;
rxStats.nRxBufFull = 0;
/* Set up Rx (multi)buffer */
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);
}
/* Setup a graceful stop time */
uint32_t ticksPerByte = RCL_SCHEDULER_SYSTIM_US(8);
uint32_t startDelay = RCL_SCHEDULER_SYSTIM_US(600);
uint32_t endPktDelay = RCL_SCHEDULER_SYSTIM_US(150);
uint32_t packetOffset = ticksPerByte * 10 + endPktDelay;
uint32_t pktInterval = ticksPerByte * cmd.syncWord[0].maxPktLen + packetOffset;
cmd.common.timing.relGracefulStopTime = startDelay + NUM_PKT * pktInterval;
cmd.stats = &rxStats;
/* Clear multi buffers before starting */
RCL_MultiBuffer *multiBuffer = RCL_MultiBuffer_head(&multiBuffers);
while (multiBuffer != NULL)
{
RCL_MultiBuffer_clear(multiBuffer);
multiBuffer = RCL_MultiBuffer_next(multiBuffer);
}
cmd.rxBuffers = multiBuffers;
/* Schedule command considering a start delay of 600 us */
cmd.common.timing.absStartTime = nextTime;
/* Wait for command to conclude */
/* Check the packet contents */
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 data */
}
}
}

Architecture

The NESB Prx 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 Rx mode to Tx mode to send an acknowledge to the specific PTX device that started the packet transaction. Furthermore, the PRX device performs address filtering so that it can ignore packets coming from PTX devices not associated with the network. If a valid packet from a valid address is received, the command can either continue listening for additional packets until a graceful or hard stop ends the operation. 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 PRX handler state machine
RCL Event (In) Description
setup Setup has been performed
timerStart Timer-based start signalled
rxBufferUpdate RX buffer has been updated
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_Command_pend
RCL_CommandStatus RCL_Command_pend(RCL_Command_Handle c)
Wait for a submitted command to complete.
Definition: RCL.c:667
RCL_open
RCL_Handle RCL_open(RCL_Client *c, const LRF_Config *lrfConfig)
Initializes an RCL client instance.
Definition: RCL.c:554
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:633
RCL_StatsNesb_DefaultRuntime
#define RCL_StatsNesb_DefaultRuntime()
Definition: generic.h:467
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_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:136
RCL_init
int RCL_init(void)
Initializes the RCL driver state.
Definition: RCL.c:537
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_CmdNesbPrx_DefaultRuntime
#define RCL_CmdNesbPrx_DefaultRuntime()
Definition: generic.h:441
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