Radio Control Layer (RCL)
Generic Rx Command Handler

Introduction

The generic Rx command allows to receive packets at a specific RF frequency and with a specific syncword. The following sections describe how the command can be configured and used, its life cycle, and how it fits into the RCL architecture.

In order to submit a Generic Rx 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_GENERIC_RX_t command has been initialized and configured.
  3. A Multibuffer has been initialized and set up.
  4. An entry has been added to the buffer, that is, an element has been placed at the end of the Rx Buffer list.

Once these steps have been completed, RCL_Command_submit and RCL_Command_pend are called to effectively send and then wait for the command to conclude. 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.

Usage

The Generic Rx command is normally employed when the user wants to receive a packet where the RCL does not provide a dedicated handler for the communication protocol. In this case, it is up to the application to define how packets are built (for the Tx side) and how they are meant to be received (for the Rx side). All of this while also considering the internal packet format in which packets are stored in the LRF FIFOs.

Just as with any command, it's necessary to declare it and configure it before submitting it. However, since this is a Rx operation, in addition to performing the command configuration, it is also necessary to set up the Rx buffers to store the packets. Furthermore, the timing information stored in the common command configuration can be used to set graceful stop and hard stop times.

One particularity about the Generic Rx command handler is that if supported on the PHY, it is possible to configure the LRF so that it listens to two different syncwords (known as SyncwordA and SyncwordB).

#define NUM_RX_BUF 1
#define MULTI_BUF_SZ 2048
void runGenericRx(void)
{
uint32_t rxMultiBuffer[NUM_RX_BUF][MULTI_BUF_SZ / 4];
List_List multiBuffers = { 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);
}
RCL_Handle h = RCL_open(&rclClient, &LRF_configGfsk500Kbps);
/* Declare command */
RCL_CmdGenericRx cmd;
RCL_StatsGeneric 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.syncWordB = SYNCWORD_B;
cmd.config.discardRxPackets = 0;
cmd.common.status = RCL_CommandStatus_Idle;
/* Listen to syncword B and disable syncword A*/
cmd.config.disableSyncA = 1;
cmd.config.disableSyncB = 0;
/* Stats configuration */
rxStats.config.accumulate = 1;
rxStats.nRxNok = 0;
rxStats.nRxOk = 0;
/* 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.maxPktLen + packetOffset;
cmd.common.timing.relGracefulStopTime = startDelay + NUM_PKT * pktInterval;
cmd.stats = &rxStats;
/* Clear multi buffers 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;
/* Schedule first 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 Generic Rx command handler has a life cycle that depends on several things. For instance, the operation can be configured so that it concludes once a correct or incorrect packet is received. It can also conclude after having received several packets due to a graceful (or hard) stop. The operation can also conclude when the LRF hasn't found a sync after a specific amount of time set by the protocol, or by a graceful (or hard) stop.

Generic Rx 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_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_CmdGenericRx_DefaultRuntime
#define RCL_CmdGenericRx_DefaultRuntime()
Definition: generic.h:264
RCL_CommandStatus_Idle
@ RCL_CommandStatus_Idle
Definition: RCL_Command.h:84
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_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_StatsGeneric_DefaultRuntime
#define RCL_StatsGeneric_DefaultRuntime()
Definition: generic.h:286