Execution States and Status Codes¶
After delivering to the RF core, a radio operation passes several states. They are illustrated as a UML state chart in Figure 13. and explained in Table 1. Each state performs several activities which are explained during the next sections.
Command Execution States¶
Status | Description |
---|---|
IDLE | The command has not been posted to the RF core, yet. This is the default status. |
PENDING | The command is being parsed and the RF core waits for the start trigger to fire. |
ACTIVE | This state is very command-specific. The command executes until it terminates.
Some commands provide also end triggers for alternative termination.
Command execution produces a result, either of TRUE , FALSE or ABORT .
After the execution phase, both, the result value and the command condition
rule get evaluated.
Several command-specific callback events may be generated during the ACTIVE state. |
DONE* | A command-specific status code is written to status and a callback event is generated
for single commands or for the last command in a chain. |
ERROR* | Either parsing failed or command execution ended with an error. A command-specific error code is
written to status and a callback event is generated. |
SKIPPED | The command was skipped due to a skipping rule in a previous command.
command.status is set to SKIPPED . This applies only to commands in chains.
A callback is not performed. |
States marked with (*) do not exist as one explicit status code, but as
various command-specific variants. The code PROP_DONE_OK
, for instance, is
specific to proprietary commands.
Status codes are defined in
<ti/devices/${DEVICE_FAMILY}/driverlib/rf_mailbox.h>
for common radio
operations. Additional codes for proprietary commands are are defined in
<ti/devices/${DEVICE_FAMILY}/driverlib/rf_prop_mailbox.h>
.
The status is queried via the command’s status field that is available for every radio operation:
struct RF_Op {
// ...
uint16_t status; // An integer telling the status of the command. This value is
// updated by the RF core during operation and may be read by the
// system CPU at any time.
// ...
};
It is recommended to read the status only after command completion:
// Assuming that 'command' points to a valid radio operation and
// execution has completed. Volatile is needed because 'status'
// is written on another processor.
volatile RF_Op* command;
if (((volatile RF_Op*)command)->status == PROP_DONE_RXTIMEOUT) {
// ...
}
It is tempting to use the status member to synchronize to command execution, for instance, to wait until the command has actually started:
// Dangerous, don't do this!
while (((volatile RF_Op*)command)->status != ACTIVE) {}
However, this is strongly discouraged because the calling thread might get
interrupted while the command has already finished in the meantime. Hence, the
ACTIVE
state is never seen and the while
loop runs forever. Instead,
use RF driver events to synchronize to command execution, either via callbacks
or RF_pendCmd().
Error Cases¶
Whenever executing an RF operation, it is good practice to check the status code and test for possible error cases. Depending on whether it is a proprietary or a PHY-independent command, several various status codes apply. It is up to the application to take appropriate measures.
The following code snippet is an example of how to execute a TX operation and how to check the status:
#include <ti/devices/${DEVICE_FAMILY}/driverlib/rf_prop_mailbox.h>
// Execute the command
RF_EventMask terminationReason = RF_runCmd(rfHandle, (RF_Op*)&RF_cmdPropTx,
RF_PriorityNormal, NULL, 0);
if ( terminationReason & RF_EventCmdDone )
{
// A radio operation command in a chain finished
}
else if ( terminationReason & RF_EventLastCmdDone )
{
// A stand-alone radio operation command or the last radio
// operation command in a chain finished.
}
else if (terminationReason & RF_EventCmdCancelled )
{
// Command canceled before it was started; it can be caused
// by RF_cancelCmd() or RF_flushCmd().
}
else if ( terminationReason & RF_EventCmdAborted )
{
// Abrupt command termination caused by RF_cancelCmd() or
// RF_flushCmd().
}
else if ( terminationReason & RF_EventCmdStopped)
{
// Graceful command termination caused by RF_cancelCmd() or
// RF_flushCmd().
}
else
{
// Uncaught error event
}
uint32_t cmdStatus = ((volatile RF_Op*)&RF_cmdPropTx)->status;
switch(cmdStatus)
{
case PROP_DONE_OK:
// Packet transmitted successfully
break;
case PROP_DONE_STOPPED:
// Received CMD_STOP while transmitting packet and finished
// transmitting packet
break;
case PROP_DONE_ABORT:
// Received CMD_ABORT while transmitting packet
break;
case PROP_ERROR_PAR:
// Observed illegal parameter in the posted RF command
break;
case PROP_ERROR_NO_SETUP:
// Command sent without setting up the radio in a supported
// mode using CMD_PROP_RADIO_SETUP or CMD_RADIO_SETUP
break;
case PROP_ERROR_NO_FS:
// Error case. Due to CMD_FS failing during power-up, CMD_PROP_TX
// could not succeed. The RF core is powered up and the synth is not running.
// One possible work-around is: Ignore it and power the RF core down
// manually. CMD_FS will be re-executed on the next RF core power-up.
RF_yield(rfHandle);
break;
case PROP_ERROR_TXUNF:
// TX underflow observed during operation
break;
default:
// Uncaught error event - these could come from the
// pool of states defined in rf_mailbox.h
while(1);
}
Error Callback¶
With the RF_Params struct, you can add an error callback:
void rfErrCb(RF_Handle rfHandle, RF_CmdHandle rfCommandHandle, RF_EventMask rfEventMask);
void *mainThread(void *arg0)
{
RF_Params rfParams;
RF_Params_init(&rfParams);
rfParams.pErrCb = rfErrCb;
In the following example, a RF_ERROR_CMDFS_SYNTH_PROG (synth
error) will be handled by posting a new CMD_FS
. When you do
RF_postCmd() in the error callback, the command will be appended
to the RF driver’s command queue. There is no possibility to inject another
CMD_FS
before any command pending in the queue. When the error callback is
executed, the RF core just came from power-down and there is (usually) an RX or
TX command waiting in the RF driver queue. Thus, if we want the CMD_FS
to be
the next command executed, we should cancel any RF command in the queue.
void rfErrCb(RF_Handle rfHandle, RF_CmdHandle rfCommandHandle, RF_EventMask rfEventMask)
{
switch (rfCommandHandle)
{
case RF_ERROR_INVALID_RFMODE:
// Invalid RF_Mode.
break;
case RF_ERROR_CMDFS_SYNTH_PROG:
{
// Synthesizer error with CMD_FS.
// Cancel any RF commands in the queue
uint8_t stopMode = 1; //1: Stop gracefully, 0: abort abruptly
RF_cancelCmd(rfHandle, RF_CMDHANDLE_FLUSH_ALL, stopMode);
// Post a new CMD_FS
RF_postCmd(rfHandle, (RF_Op*)&RF_cmdFs, RF_PriorityNormal, NULL, 0);
break;
}
default:
// Uncaught error handle
break;
}
}
Attention
You must not use any blocking RF driver calls (such as RF_runCmd() and RF_pendCmd()) in the error callback! Instead, use non-blocking RF driver calls such as RF_postCmd().