TRX Host Driver
TRX Host Driver API

Breaks down a received packet and copies the HEADER, PAYLOAD, RSSI,

The TRX Host Driver is a simple API set for controlling the TRX over a SPI interface. The API supports initializing the TRX, scheduling RF commands, and several utility functions, including time synchronization.

Each command sent to the TRX must pass over a SPI interface and adhere to a specific TRX wire-interface protocol. This interface can have varying speeds and cause delays based on other events occurring in the system. For this reason, all TRX Host Driver API calls are non-blocking and require a callback, with the exception of initialization and closing of the driver.

See TRX Host API Interface for a detailed description of available functions and structures.

Fundamentals of the TRX

The TRX Host Driver APIs provide a simple interface for interacting with the TRX; effectively abstracting away many of the concepts described in this section. However, understanding the basics of the communication between a Host and the TRX can help users better understand the APIs provided and make use of them in the most efficient manner for their application.

SPI Communication

The Host communicates with the TRX over a 4-wire SPI interface with an additional fifth input signal designated as "INT" or interrupt. The INT signal alerts the host when the TRX has data available to send on the POCI line, effectively requesting that the host start a SPI transaction to retrieve the data.

overall-arch.png
SPI Interface

SPI Protocol

Data transmitted over the SPI interface conforms to a proprietary formatting protocol specific to the TRX.

spi_transaction_with_requests.png
SPI Transaction

Key characteristics of the protocol include:

  • The interface is full-duplex
    • Data can be received and transmitted at the same time on both the Host and TRX
  • A SPI transaction is called a TRX_SpiBlock and limited to TRX_SPIBLOCK_SIZE bytes
    • Including a 4 byte header and a 2 byte CRC
  • Each TRX_SpiBlock consists of:
  • All information is exchanged via a TRX_Request
  • Each TRX_Request from the Host is ACK'd by the TRX with a TRX_Request_LastStatus
    • Guaranteed response in the order they are sent
    • Can contain the data requested e.g. RSSI, Version, etc

Basic TRX Data Structures

The following data structures form the basis of communication between a Host and the TRX. Together they enable most operations including storing commands to the TRX, submitting them to the radio, and managing "streams" of payload data being transmitted and received.

Basic Flow of Submitting a Radio Command

The following is the basic flow for submitting a TX operation to the TRX. While this describes a high level overview of the process, much of this can be abstracted from users by using the TRX Host Driver APIs.

basic_tx_operation.png
Basic TX Operation
  1. Store front end radio settings using a TRX_Request_ConfigStore with id = 0
    • TRX_Request_LastStatus response signals status of the operation
    • NOTE: this is typically a one-time operation at initialization and is not required each time a radio command is submitted
  2. Store phy settings using a TRX_Request_ConfigStore each using the same id within range [1, TRX_MAX_CONFIGURATIONS]
    • TRX_Request_LastStatus response signals status of the operation
    • NOTE: this is typically a one-time operation at initialization and is not required each time a radio command is submitted
  3. If TX:
  4. Store command to the TRX using a TRX_Request_CommandStore to any slot within range [0, TRX_MAX_NUM_COMMANDS]
  5. Submit the command to the radio using a TRX_Request_CommandSubmit
  6. Eventually a TRX_Request_CommandStatus will be sent from the TRX to the host indicating command termination and reason for completion

Internal Packet Format

When processing radio packets, both in RX and TX direction, the TRX uses an internal packet format regardless of PHY used. This is done in order to keep information essential for processing the packet consistent across all PHYs.

Compared to frames received/transmitted on-air, internal packets:

  • do not include preamble,
  • do not include sync word,
  • do not include CRC,
  • replace PHY header with 4-byte internal packet header.

the PHY payload (PSDU) is not changed between the internal packets and radio packets.

GenFSK Header

For FSK-modulated PHYs, where complete control over the header generation is required, it is possible to select the GenFSK header type.

In this mode, the header is completely pass-through and is not modified by the TRX coming or going. Instead the PHY configuration contains information that allows the TRX to parse the TX and RX headers as-is.

Parameter Description
Length index Bit-position of the length field
Length size Size in bits of the length field

All other configurations than length are static, and it's not possible to dynamically change CRC or whitening depending on the incoming or outgoing header.

Protocol Header

The PHY header formatting for an on-air packet depends on which PHY is used and the user should provide information to the modem on what these fields should be populated with, along with the lenght of the packet. The first 4 bytes of the payload sent over by the user to the TRX is hence, ear marked for this purpose and is refered to as the PHY internal header.

For example, for an OFDM PHY, the user should provide input on the length of the packet, modulation, rate etc. Similarly, for a SUNFSK PHY, the user should provide input on the length of the packet, modulation, whitening, fcs mode etc. Thus, the interpretation of these 4 bytes depends on the PHY used.

Also, When Wi-SUN MDR feature is being used, TRX may receive a packet on an alternate PHY different to what the TRX is initially configured to. The info on which PHY the packet is received on is populated in the internal PHY header by the TRX and sent over to the Host/ User.

The different fields of the internal PHY header for different PHYs and when Wi-SUN MDR feature is used are detailed below.

internal-packet-header.png
Internal Packet Header

Common

See TRX_PayloadHeader_Common

/* Transmit payload */
#define PAYLOAD_LENGTH (10U + sizeof(TRX_PayloadHeader))
uint8_t payload[PAYLOAD_LENGTH] = {
0, 0, 0, 0, // Reserved for common header
0, 1, 2, 3, 4, 5, 6, 7, 8, 9 // Payload data
};
((TRX_PayloadHeader_Common *)payload)->length = PAYLOAD_LENGTH - sizeof(TRX_PayloadHeader); // This length excludes the common header
((TRX_PayloadHeader_Common *)payload)->modulation = TRX_PayloadHeader_Modulation_FSK; // 0U = FSK

OFDM

See TRX_PayloadHeader_SunOFDM

/* Transmit payload */
#define PAYLOAD_LENGTH (10U + sizeof(TRX_PayloadHeader))
uint8_t payload[PAYLOAD_LENGTH] = {
0, 0, 0, 0, // Reserved for common header
0, 1, 2, 3, 4, 5, 6, 7, 8, 9 // Payload data
};
((TRX_PayloadHeader_SunOFDM *)payload)->length = PAYLOAD_LENGTH - sizeof(TRX_PayloadHeader); // This length excludes the common header
((TRX_PayloadHeader_SunOFDM *)payload)->modulation = TRX_PayloadHeader_Modulation_OFDM; // Const(1) for OFDM
((TRX_PayloadHeader_SunOFDM *)payload)->rate = TRX_PayloadHeader_SunOFDM_Rate_MCS0; // See rate in TRX.h
((TRX_PayloadHeader_SunOFDM *)payload)->scrambler = 0U; // Scrambler
((TRX_PayloadHeader_SunOFDM *)payload)->newPhyId = 0U; //Phy ID of the PHY the payload is received on. Valid if RX operation was started on FSK phy.

For OFDM PHYs, rate is set through parameter in the packet header. A rate enum helper is provided in TRX.h.

SUNFSK

See TRX_PayloadHeader_SunFSK

/* Transmit payload */
#define PAYLOAD_LENGTH (10U + sizeof(TRX_PayloadHeader))
uint8_t payload[PAYLOAD_LENGTH] = {
0, 0, 0, 0, // Reserved for common header
0, 1, 2, 3, 4, 5, 6, 7, 8, 9 // Payload data
};
((TRX_PayloadHeader_SunFSK *)payload)->length = PAYLOAD_LENGTH - sizeof(TRX_PayloadHeader); // This length excludes the common header
((TRX_PayloadHeader_SunFSK *)payload)->modulation = TRX_PayloadHeader_Modulation_FSK; // Const(0) for FSK
((TRX_PayloadHeader_SunFSK *)payload)->whitening = 1U; // Whitening enabled (1) or not (0)
((TRX_PayloadHeader_SunFSK *)payload)->fcs_mode = 0U; // CRC32 (0) or CRC16 (1)
((TRX_PayloadHeader_SunFSK *)payload)->mode_switch = 0U; // Const(0) for transmit, but is 1 if payload was received on alternate phy
((TRX_PayloadHeader_SunFSK *)payload)->newPhyId = 0U; // Phy ID of the PHY the payload is received on. Valid if mode_switch==1

Fundamentals of the TRX Host Driver

The TRX Host Driver APIs provide a simple interface for interacting with the TRX. The main goals of the driver include:

  • Abstracting away the most challenging operations from users
  • Enabling a high degree of flexibility to take advantage of all TRX features
  • Providing an event based system using callbacks to inform the application when operations have completed on the TRX

Events & Callbacks

Many APIs provided by the TRX Host Driver enable users to subscribe to TRX_EVENTS and receive a callback when they occur. This model allows the developers to efficiently make use of all features on the TRX for their respective applications.

There exists only one prototype for callbacks from the host driver, enabling users to funnel multiple events to a single callback or split them into separate functions, each with a specific purpose.

Initializing the Driver

The TRX Host Driver can be initialized with a blocking call to TRX_Host_open(), with a pointer to a TRX_Host_Params structure. During initialization of the TRX Host Driver, the TRX is reset and the PHY configurations stored in NV will be discovered. This involves sending a TRX_Request_ConfigList to the TRX and waiting until all the configs are received by the host via TRX_Request_ConfigListResponse.

## Example

/* TRX RF Header files */
#include <ti/trx/TRX.h>
volatile bool resetTrx = false;
volatile bool eraseNv = false;
SemaphoreP_Handle radioSemaphore;
static void generalCallback(TRX_Host_Handle handle, uintptr_t pConfigData,
TRX_Request *request, uint64_t events, uintptr_t arg)
{
if(events & TRX_EventNvCorrupt)
{
eraseNv = true;
}
else
{
// TRX_EventTransportError
resetTrx = true;
}
SemaphoreP_post(radioSemaphore);
}
int main(void)
{
SemaphoreP_Params semParamsRadio;
SemaphoreP_Params_init(&semParamsRadio);
radioSemaphore = SemaphoreP_create(0, &semParamsRadio);
TRX_Host_Params params = {
.generalCb = generalCallback,
.arg = (uintptr_t)NULL
};
/* Init the RF driver */
trxHostHandle = TRX_Host_open(&params);
// Continue with loading PHY configurations, sending commands, etc...
}

Loading Front End and PHY Configurations

RF settings for PHYs and front ends to use on the TRX must first be loaded from the Host using the TRX_Host_storeConfig API. These settings are often referred to as RCL (Radio Control Layer) settings or Configurations and can be generated using Smart RF Studio. The following parameters are required to load a configuration to the TRX:

  • Configuration Identifier (id)
  • Pointer to the settings array
  • Length of the settings array
  • 32-bit reference

The configuration id and reference tuple uniquely identify each configuration on the TRX.

The configuration id is a unique identifier for each configuration in the range [0, TRX_MAX_CONFIGURATIONS]. The application can arbitrarily select the value as long as it's within this range. Only one configuration can exist on the TRX for a given configuration id.

The 32-bit reference can be used by the application to tag any metadata with the configuration. During Configuration Discovery, both the configuration id and the reference tagged to a configuration are sent over to the Host.

For example, if application wishes to store different LORA PHYs on the TRX, the application can set the reference for each configuration to a value indicative of what the preamble length is. Thus, the reference acts as a way to quickly identify what this configuration is for.

Another example, a 32 bit hash of the configuration blob can be set as reference and may be used for integrity check at the application level during Configuration Discovery.

The application is free to set the reference to 0, if it does not want to associate any meta data with the configuration.

See also Configuration Discovery for more details.

Front end configurations are handled the same as PHY configurations, with one exception; they must always be stored using the configuration id TRX_CONFIG_ID_FRONTEND. Everything else regarding the generation, storing and erasing of front end configurations is identical to PHY configurations.

Persisting Configurations in Non-Volatile Memory

Each configuration is stored in RAM on the TRX but can optionally be persisted to non-volatile storage using the TRX_Host_persistConfig API. Persisting a configuration provides two key benefits:

  1. Reduce the amount of RAM used by configurations
  2. Reduce initialization/restart times because the required configuration have already been stored to the TRX

It's recommended to persist configurations that your application is likely to load at every initialization.

Erasing Configurations from Non-Volatile Memory

The TRX_Host_eraseNv() can be used to erase all configs stored in non-volatile memory on the TRX. Executing this API will result in the host sending a TRX_Request_NvErase to the TRX and the provided callback will be executed with the TRX_EventNvEraseComplete event. On success, all of NV on the TRX will be erased, making it necessary to re-load the PHY configurations afterwards.

Configuration Discovery

When TRX_Host_open() is called, the host driver will automatically start discovery of configurations stored in non-volatile memory on the TRX. The driver uses a combination of the following to uniquely map configurations:

  • Configuration identifier (id)
  • 32-bit reference

When a configuration is discovered during initialization, it's details are added to an internal database and tracked. Subsequent calls to TRX_Host_storeConfig, with a matching configuration id and 32-bit reference will return a status of TRX_Host_Config_Exists unless the force argument is set to true. Also, subsequent calls to TRX_Host_storeConfig, with a matching configuration id but a different 32-bit reference, will cause the original configuration to be overwritten.

Settings Arrays

The code generated by Smart RF Studio includes an array containing a bundle of register settings. It is generated with user input and will usually contain all the settings necessary to send or receive RF packets on a characterized PHY.

It contains semi-compressed register values, and may also contain delta configurations that may alter the default PHY behavior. These delta configurations are also sometimes called subPhys.

Delta Configurations

A delta configuration is nothing more than tiny snippets of register values that are applied if the user-selected option mask matches the snippet header's identifier.

In this way, it is possible to store PHY variants very efficiently, as typically only a few registers vary between small variations of a PHY.

Example

#include <stdint.h>
#include <ti/drivers/dpl/SemaphoreP.h>
/* Board Header files */
#include "ti_drivers_config.h"
/* TRX RF Header files */
#include <ti/trx/TRX.h>
// RCL configurations
#include <ti/trx/rfconfig/LP_EM_CC1307R_CC1190/rcl_settings_wisun.h>
extern const uint32_t LRF_CC1190_frontendRegConfig_wisun[];
extern const uint32_t LRF_CC1190_frontendRegConfig_wisun_byteCount;
extern const uint32_t LRF_CC1190_mainRegConfig_wisun[];
extern const uint32_t LRF_CC1190_mainRegConfig_wisun_byteCount;
#define FE_CONFIG_SIZE (LRF_CC1190_frontendRegConfig_wisun_byteCount)
#define FE_CONFIG_PTR ((uint8_t *)LRF_CC1190_frontendRegConfig_wisun)
#define RF_CONFIG_SIZE (LRF_CC1190_mainRegConfig_wisun_byteCount)
#define RF_CONFIG_PTR ((uint8_t *)LRF_CC1190_mainRegConfig_wisun)
// ID of the PHY configuration on the TRX. Can be anything in the range
// (TRX_CONFIG_ID_FRONTEND, TRX_MAX_CONFIGURATIONS) exclusive
#define RF_CONFIG_ID (1U)
// Reference for the PHY configuration
#define RF_CONFIG_REFERENCE (0xDEADBEEF)
// Reference for the front end configuration
#define FE_CONFIG_REFERENCE (0xFEFEFEFE)
SemaphoreP_Handle configSemaphore;
static void configCallback(TRX_Host_Handle handle, uintptr_t pConfigData,
TRX_Request *request, uint64_t events, uintptr_t arg)
{
// If this loops, something went wrong when a TRX_Request_ConfigStore was sent to the TRX
while(TRX_EventLastStatusError & events);
{
// Would typically post a semaphore being pended on elsewhere
SemaphoreP_post(configSemaphore);
}
{
// Would typically post a semaphore being pended on elsewhere
SemaphoreP_post(configSemaphore);
}
{
// Would typically post a semaphore being pended on elsewhere
SemaphoreP_post(configSemaphore);
}
// If status needs to be checked:
// TRX_Request_LastStatus *pReqLastStatus = (TRX_Request_LastStatus *)request;
// RequestStatus requestStatus = pReqLastStatus->status;
}
int main(void)
{
SemaphoreP_Params semParamsConfig;
SemaphoreP_Params_init(&semParamsConfig);
configSemaphore = SemaphoreP_create(0, &semParamsConfig);
// Initialize the TRX Host Driver...
// Erase all existing configurations from non-volatile storage
status = TRX_Host_eraseNv(rf_handle, configCallback);
while(TRX_Host_Success != status);
SemaphoreP_pend(configSemaphore, SemaphoreP_WAIT_FOREVER);
// Load a PHY configuration
status = TRX_Host_storeConfig(rf_handle, RF_CONFIG_ID, RF_CONFIG_PTR, RF_CONFIG_SIZE,
RF_CONFIG_REFERENCE, false, configCallback,
if(TRX_Host_Config_Exists != status)
{
// Loop if an error occurred
while(TRX_Host_Success != status);
}
SemaphoreP_pend(configSemaphore, SemaphoreP_WAIT_FOREVER);
// Persist the configuration to non-volatile storage
status = TRX_Host_persistConfig(rf_handle, RF_CONFIG_ID);
while(TRX_Host_Success != status);
SemaphoreP_pend(configSemaphore, SemaphoreP_WAIT_FOREVER);
// Load a front end configuration
status = TRX_Host_storeConfig(rf_handle, TRX_CONFIG_ID_FRONTEND, FE_CONFIG_PTR, FE_CONFIG_SIZE,
FE_CONFIG_REFERENCE, false, configCallback,
if(TRX_Host_Config_Exists != status)
{
// Loop if an error occurred
while(TRX_Host_Success != status);
}
SemaphoreP_pend(configSemaphore, SemaphoreP_WAIT_FOREVER);
// Continue with storing, sending commands, etc...
}

Submitting RF Commands

All RF commands can be submitted to the TRX via the TRX_Host_storeCmds() API but require the following to be loaded prior to command submission:

  1. Front end configuration
  2. PHY configuration
  3. Stream (if applicable)

There is no requirement on the order in which these are stored on the TRX, only that they are stored prior to command submission. Finally, the command can be submitted to the radio using either the pCmdToSubmit argument to TRX_Host_storeCmds() or the TRX_Host_submitCmd() API.

trx_rf_schedule_tx.png
Example TX Command Submission

Each command uses the TRX_Request_CommandStore structure for common parameters and is differentiated based on the TRX_Request_CommandStore.cmd_id and TRX_Request_CommandStore.params, which details command specific parameters. There is a high degree of flexibility when creating commands, including the ability to set frequency, power, chaining multiple commands, and interrupting ongoing radio operations.

Available commands as described in TRX_RadioCommands

TRX_RadioCommands (Command ID) TRX_RadioCommand_Params Description
TRX_RadioCommand_Transmit TRX_Request_CommandStore_Transmit_Params Transmit command
TRX_RadioCommand_Receive TRX_Request_CommandStore_Receive_Params Receive command
TRX_RadioCommand_CarrierSense TRX_Request_CommandStore_CarrierSense_Params Carrier Sense command
TRX_RadioCommand_TransmitTest TRX_Request_CommandStore_TransmitTest_Params Transmit test command used to create a continuous wave
TRX_RadioCommand_ReceiveTest TRX_Request_CommandStore_ReceiveTest_Params Receive test command used to get immediate RSSI and test other RX functionality

Setting Frequency

The frequency of a given command can be set in the command specific TRX_Request_CommandStore.params structure. When a command is provided to the TRX, a frequency calibration operation occurs prior to submission to the radio. The time required to calibrate is non-trivial and calibration is required only if the frequency changes more than +/- 2MHz compared to the last calibrated frequency.

For this reason, the TRX provides the ability to specify a command's frequency as a "delta" of the last calibrated frequency. This concept is central to supporting Wi-SUN MDR on the TRX and is used to reduce delay between time critical operations. An example of this can be seen below:

cmdTx1.params.tx.frequency = 922900; // Transmit frequency in kHz
// Store and submit cmdTx1...
cmdTx2.params.tx.frequency = -900; // Delta to the *last calibrated* frequency in kHz = 922000
// Store and submit cmdTx2...
cmdTx3.params.tx.frequency = 0U; // Delta to the *last calibrated* frequency in kHz = 922900

Setting Transmit Power

The transmit power can be set in the TRX_RadioCommand_Params.tx or TRX_RadioCommand_Params.txTest structure of the TRX_Request_CommandStore.params union.

The TRX_RadioCommand_Transmit provides the ability to set power in one of two ways:

cmdTx1.params.tx.power.rawValue = TRX_MAX_POWER; // Set Max TX Power
cmdTx2.params.tx.power.rawValue = TRX_MIN_POWER; // Set Min TX Power
cmdTx3.params.tx.power.dBm = -1; // Set TX power to -1.5 dBm
cmdTx3.params.tx.power.fraction = 1;

Setting PHY Configurations

The PHY configuration for a given command can be set in the command specific TRX_Request_CommandStore.params structure. Each PHY configuration has an accompanying 'option mask' used as a bit mask to enable various subPHYs. This be used to have one base configuration with an option mask that enables different preamble lengths, sync words, etc without consuming a new configuration slot on the TRX.

There may be up to TRX_MAX_CONFIGURATIONS different configuration arrays stored on the device at any time, but only up to 3 may be referenced by a command. Starting with phy0, each referenced PHY is then applied to the modem in a sequential order. This means that if phy2 contains a register value that differs from a value in phy0 or phy1, the setting from the array referenced in phy2 will take effect.

In this way, it is possible to have all configurations stored on the TRX in Flash, and make TRX apply small delta changes to the first configuration at runtime. The following is an example of setting these parameters for a TX command.

#include <stdint.h>
#include <ti/drivers/dpl/SemaphoreP.h>
/* Board Header files */
#include "ti_drivers_config.h"
/* TRX RF Header files */
#include <ti/trx/TRX.h>
// LRF register configurations
#include <ti/trx/rfconfig/LP_EM_CC1307R_CC1190/rcl_settings_wisun.h>
extern const uint32_t LRF_CC1190_frontendRegConfig_wisun[];
extern const uint32_t LRF_CC1190_frontendRegConfig_wisun_byteCount;
extern const uint32_t LRF_CC1190_mainRegConfig_wisun[];
extern const uint32_t LRF_CC1190_mainRegConfig_wisun_byteCount;
#define FE_CONFIG_SIZE (LRF_CC1190_frontendRegConfig_wisun_byteCount)
#define FE_CONFIG_PTR ((uint8_t *)LRF_CC1190_frontendRegConfig_wisun)
#define RF_CONFIG_SIZE (LRF_CC1190_mainRegConfig_wisun_byteCount)
#define RF_CONFIG_PTR ((uint8_t *)LRF_CC1190_mainRegConfig_wisun)
#define RF_CONFIG_ID (1U)
#define TX_CMD_SLOT (0U)
void *mainThread(void *arg0)
{
// Initialize TRX...
// Load front end and phy configurations with RF_CONFIG_ID_0, RF_CONFIG_ID_1
/* Create a Tx command */
cmdTx.slot = TX_CMD_SLOT;
cmdTx.cmd_id = TRX_RadioCommand_Transmit;
cmdTx.params.tx.phy0.config_id = RF_CONFIG_ID_0;
cmdTx.params.tx.phy0.option_mask = TRX_PHY_FEATURE_FSK_RATE_50KBPS_SIDEWALK;
cmdTx.params.tx.modem = TRX_RadioCommand_Modem_FSK;
cmdTx.params.tx.power.rawValue = TRX_MAX_POWER;
//submitting this command will make TRX send a packet with preamble length defined in RF_CONFIG_ID_0
cmdTx.params.tx.phy1.config_id = RF_CONFIG_ID_1; // Variable Preamble Length
cmdTx.params.tx.phy1.option_mask = 0;
//submitting this command will make TRX send a packet with preamble length defined in RF_CONFIG_ID_1
// Continue with building cmd, storing streams, submitting cmds, etc...
}

Command Events

When storing commands to the TRX using TRX_Host_storeCmds(), the user can subscribe to events and provide a callback using the subscribedEvents and callback arguments respectively. The following events can be subscribed to for a command:

In addition, commands are automatically subscribed to the following events:

Command Triggers

Commands can be configured to trigger based on the choice of TRX_Command_TriggerType and optional TRX_Command_ScheduleParam in the TRX_Request_CommandStore structure. The following table provides a description of each available trigger.

TRX_Command_TriggerType Description Setting Time
Command_Trigger_Immediate Start immediately N/A
Command_Trigger_IoEdge 1 Start on edge N/A
Command_Trigger_Time_Absolute Start on absolute time match cmdTx.trigger_param = <ABSOLUTE_TIME>
Command_Trigger_Time_Relative_Previous_Start Start relative to previous command start cmdTx.trigger_param = <RELATIVE_TIME>
Command_Trigger_Time_Relative_Previous_End Start relative to previous command end cmdTx.trigger_param = <RELATIVE_TIME>

1Application must enable GPIO triggering with TRX_Host_utilDioSetup() and assert the IO after the TRX_EventCmdSubmitComplete event is received from the TRX Host Driver

In order to use Command_Trigger_IoEdge the following must occur:

  1. Application must initialize and connect an IO for edge triggering to the TRX
  2. Enable edge triggering using TRX_Host_utilDioSetup()
  3. After submitting the command using TRX_Host_storeCmds(), the application must pend on the TRX_EventCmdSubmitComplete event to ensure the command has made it over the SPI interface
  4. Execute TRX_Host_submitCmd() to trigger the command

Note that when chaining commands, the TRX_Request_CommandStore.chain_trigger and TRX_Request_CommandStore.chain_trig_param elements must be set for all commands in the chain except the first command.

Command Chaining

The TRX Host Driver allows for commands to be chained together, meaning that multiple commands can be submitted at the same time with a branching mechanism determining if the next command in the chain will be run by the TRX or not. There are three branching options for chaining:

  • Slot on True
    • The command residing in the provided slot will be executed if the the current running command returns with a value of true.
  • Slot on False
    • The command residing in the provided slot will be executed if the the current running command returns with a value of false.
  • Slot on Compare
    • The command residing in the provided slot will be executed if the the current running command completed with a command status matching the user's input status.

Each command can have a different definition of True and False as defined in the following table.

Command True Condition False Condition
TX All cases where the radio completed a transmission All other cases
RX All cases where the radio completed a reception All other cases
CS If the channel is busy If the channel is idle
TX Test All cases where the command ends after a packet transmission is completed All other cases
RX Test All cases where the radio completed a reception All other cases

Below is an example of chaining a Carrier Sense, Transmit, and Receive command together (CS->TX->RX). This is a common use case when performing CCA prior to transmission then issuing an RX command to receive an acknowledgment. For more details on Carrier Sense command is setup refer to the section on Carrier Sense Command (Carrier Sense Command).

TRX_Request_CommandStore cmds[3U] = {0U};
TRX_Request_CommandStore *pCmdCs = cmds[0U];
pCmdCs->slot = 0U;
// Chain to the TX command when channel is detected as idle
pCmdCs->enable_on_false = true;
pCmdCs->slot_on_false = 1U;
// Continue setting up additional CS command parameters...
TRX_Request_CommandStore *pCmdTx = cmds[1U];
pCmdTx->slot = 1U;
// Chain to the Rx command when transmission completes successfully
pCmdTx->enable_on_true = true;
pCmdTx->slot_on_true = 2U;
// Trigger the command 1000us after the previous CS command completes
pCmdTx->chain_trig_param = 1000;
// Continue setting up additional TX command parameters...
TRX_Request_CommandStore *pCmdRx = cmds[2U];
pCmdRx->slot = 2U;
// Trigger the command immediately after the previous TX command completes
// Continue setting up additional RX command parameters...
// STORE cmds on TRX and submit the top of the chain (the CS command)
uint64_t subscribedCmdEvents = TRX_EventCmdStatus | TRX_EventFinalCmdStatus |
status = TRX_Host_storeCmds(rf_handle, cmds, 3U, pCmdCs, csTxRxCallback, subscribedCmdEvents);
while(TRX_Host_Success != status);
// Continue pending on semaphore for subscribed events...

Handling Streams of Data

The TRX handles commands and their respective payloads (streams) as separate entities and the TRX Host Driver exposes this concept as well. Similar to commands, the application can subscribe to events on streams and receive callbacks when the TRX Host Driver observes such events. Streams intended for transmission over-the-air can be stored to the TRX using the TRX_Host_storeStream() API and streams intended for reception of data can be registered using TRX_Host_registerRxStream().

Transmit Streams

Prior to submitting a TX command to the radio, a stream containing the data must be stored to the TRX with a user specified identifier (ID of the stream) using TRX_Host_storeStream(). Then, when the TX command is submitted, the ID of the stream needs to be referenced within the command structure.

Streams stored on the TRX have an associated retention policy that defines its lifespan.

TRX_Stream_Retention Lifespan Description
Stream_Retention_Flush_Never Indefinite or until a stream is stored with the same ID The stream will not be flushed from the TRX after being transmitted. This will consume RAM indefinitely but can be useful for short packets that don't change and will be sent multiple times.
Stream_Retention_Flush_On_Success Flushed when a TX command sending the stream succeeds The stream is removed on success of a corresponding TX command.
Stream_Retention_Flush_Streaming Flushed when a TX command sending the stream ends The most recommended approach to TX streams. A small portion of the stream is loaded to the TRX, then when a TX command referencing the stream is submitted to the radio, the rest of the data is streamed and immediately sent over-the-air.

The recommended retention policy in most use cases is Stream_Retention_Flush_Streaming, especially when sending large payloads. This approach provides several benefits including:

  • Reducing delay caused by storing the full payload prior to command submission
  • Reducing the amount of RAM consumed on the TRX

Below is an example showing how to store a stream on the TRX and send it using a TX command.

#include <unistd.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
/* Board Header files */
#include "ti_drivers_config.h"
/* TRX RF Header files */
#include <ti/trx/TRX.h>
/* Length of the payload to transmit */
#define TX_PAYLOAD_LENGTH (2043U)
/* Total length of the HEADER + PAYLOAD to transmit */
#define TX_PACKET_LENGTH (sizeof(TRX_PayloadHeader) + TX_PAYLOAD_LENGTH)
/* Slot for the TX cmd. Can be anything in the range 0, TRX_MAX_NUM_COMMANDS
* excluding TRX_MAX_NUM_COMMANDS */
#define TX_CMD_SLOT (0U)
/* ID of the packet data payload on the TRX. Can be anything in the range 0,
* TRX_MAX_STREAMS inclusive */
#define TX_STREAM_ID (0U)
/* ID of the PHY configuration on the TRX. Can be anything in the range 1,
* TRX_MAX_CONFIGURATIONS inclusive */
#define RF_CONFIG_ID (1U)
/* Set packet interval to 500000 us or 500 ms */
#define PACKET_INTERVAL 500000
/* Transmit packet buffer */
uint8_t txPacket[TX_PACKET_LENGTH] = {0U};
static void transmitCallback(TRX_Host_Handle handle, uintptr_t pCmdStore,
TRX_Request *request, uint64_t events, uintptr_t arg)
{
/* If this loops, something went wrong when a TRX_Request_StreamStore was
* sent to the TRX */
while(TRX_EventLastStatusError & events);
if(TRX_EventCmdStatus & events)
{
TRX_Request_CommandStatus *pCmdStatusRequest = (TRX_Request_CommandStatus *)request;
if(TRX_CommandStatus_Finished == pCmdStatusRequest->status)
{
GPIO_toggle(CONFIG_GPIO_GLED);
}
else
{
// Something went wrong
while(1);
}
}
}
void *mainThread(void *arg0)
{
// Initialize TRX...
// Load front end and phy configurations...
/* Create a Tx command */
cmdTx.slot = TX_CMD_SLOT;
cmdTx.params.tx.phy0.config_id = RF_CONFIG_ID;
cmdTx.params.tx.phy0.option_mask = TRX_PHY_FEATURE_FSK_RATE_50KBPS_SIDEWALK;
/* Associate the Stream with this TX command */
cmdTx.params.tx.stream_id = TX_STREAM_ID;
// Continue with building cmd, storing streams, submitting cmds, etc...
/* Store the TX command to the TRX */
status = TRX_Host_storeCmds(trxHostHandle, &cmdTx, 1U, NULL, transmitCallback,
while(TRX_Host_Success != status);
/* Update the PHY header */
((TRX_PayloadHeader_SunFSK *)txPacket)->length = TX_PAYLOAD_LENGTH; // This length excludes the header
((TRX_PayloadHeader_SunFSK *)txPacket)->mode_switch = 0U; // 0U = No mode switch
((TRX_PayloadHeader_SunFSK *)txPacket)->fcs_mode = 0U; // 0U = CRC32; 1U = CRC16
((TRX_PayloadHeader_SunFSK *)txPacket)->whitening = 1U; // 1U = Whitening enabled
/* Build a random payload */
uint16_t i = sizeof(TRX_PayloadHeader);
for (; i < sizeof(txPacket); i++)
{
txPacket[i] = rand();
}
while (1)
{
usleep(PACKET_INTERVAL);
status = TRX_Host_storeStream(trxHostHandle, TX_STREAM_ID, txPacket, sizeof(txPacket),
while(TRX_Host_Success != status);
status = TRX_Host_submitCmd(trxHostHandle, TX_CMD_SLOT);
while(TRX_Host_Success != status);
}
}

Receive Streams

Prior to submitting an RX command to the radio, a stream to store the received data must be registered with the TRX Host Driver with a user specified identifier (ID) using TRX_Host_registerRxStream(). Then, when the RX command is submitted, the ID of the stream needs to be referenced within the command structure.

When an RX command is created the application can decide whether to stream the data from the TRX to the Host as it's received or buffer it on the TRX until reception has completed. It's recommended to stream the data as it's received by setting cmdRx.params.rx.stream_early = true for several reasons:

  • Enable large packet reception through reduction in RAM space required on the TRX
  • Reduce delay from end of packet reception over-the-air to callback to the application

On reception and depending on events subscribed to when registering the RX stream, a callback is issued to the application where data can be parsed. Each received packet consists of the following structure:

rx_packet_format.png
RX Packet Format

Below is an example showing how to register an rx stream and parse the data once received.

/* Standard C Libraries */
#include <unistd.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <stdbool.h>
/* TI Drivers */
#include <ti/drivers/GPIO.h>
#include <ti/drivers/dpl/SemaphoreP.h>
/* Board Header files */
#include "ti_drivers_config.h"
/* TRX RF Header files */
#include <ti/trx/TRX.h>
/* The data received will contain both timestamp (4B) and RSSI (1B)
* information. */
#define RSSI_SIZE_BYTES (1U)
#define TIMESTAMP_SIZE_BYTES (4U)
/* Maximum payload in a receive operation */
#define RX_PAYLOAD_LENGTH (2047U)
/* Total size of data that can be received including
* HEADER + PAYLOAD + RSSI + TIMESTAMP */
#define RX_PACKET_LENGTH (sizeof(TRX_PayloadHeader) + RX_PAYLOAD_LENGTH + RSSI_SIZE_BYTES + TIMESTAMP_SIZE_BYTES)
/* Slot for the RX cmd. Can be anything in the range 0, TRX_MAX_NUM_COMMANDS
* excluding TRX_MAX_NUM_COMMANDS */
#define RX_CMD_SLOT (0U)
/* ID of the packet data payload on the TRX. Can be anything in the range 0,
* TRX_MAX_STREAMS inclusive */
#define RX_STREAM_ID (0U)
/* ID of the PHY configuration on the TRX. Can be anything in the range 1,
* TRX_MAX_CONFIGURATIONS inclusive */
#define RF_CONFIG_ID (1U)
/* Buffer where the received packet header will be copied */
uint8_t rxHeader[sizeof(TRX_PayloadHeader)];
/* Buffer where the received packet payload will be copied */
uint8_t rxPayload[RX_PAYLOAD_LENGTH];
/* Buffer where the RSSI of a received packet will be copied */
uint8_t rxRssi[RSSI_SIZE_BYTES];
/* Buffer where the timestamp of a received packet will be copied */
uint8_t rxTimestamp[TIMESTAMP_SIZE_BYTES];
/* Receive packet buffer */
uint8_t rxBuffer[RX_PACKET_LENGTH] = {0U};
static void readPacket(uint8_t *data)
{
uint8_t *startOfPayload = (uint8_t *)header + sizeof(TRX_PayloadHeader);
uint8_t *endOfPayload = (uint8_t *)header + header->length + sizeof(TRX_PayloadHeader);
int8_t *rssi =(int8_t *)endOfPayload;
uint32_t *timestamp = (uint32_t *)(endOfPayload + sizeof(int8_t));
memcpy(rxHeader, header, sizeof(TRX_PayloadHeader));
memcpy(rxPayload, startOfPayload, header->length);
memcpy(rxRssi, rssi, RSSI_SIZE_BYTES);
memcpy(rxTimestamp, timestamp, TIMESTAMP_SIZE_BYTES);
}
static void receiveCallback(TRX_Host_Handle handle, uintptr_t pCmdStoreOrData,
TRX_Request *request, uint64_t events, uintptr_t arg)
{
if(TRX_EventCmdStatus & events)
{
/* cmd finished */
// Use cmdStatus to check how the command completed
TRX_CommandStatus cmdStatus = pReqCmdStatus->status;
// Use the pRxParams to check status of the data (e.g. crc ok, header ok, etc)
}
{
/* If this loops, the RX buffer wasn't big enough */
/*
* +----------+---------+----------+----------------+
* | hdr (4B) | payload | rssi(1B) | timestamp (4B) |
* +----------+---------+----------+----------------+
*/
if((uintptr_t)NULL != pCmdStoreOrData)
{
/*
* Read the packet, obtaining the header information, the payload,
* the RSSI and the timestamp and copying them into buffers for
* easy access and reading.
*/
readPacket((uint8_t *)pCmdStoreOrData);
GPIO_toggle(CONFIG_GPIO_RLED);
}
}
}
void *mainThread(void *arg0)
{
// Initialize TRX...
// Load front end and phy configurations...
/* Create a Tx command */
/* Create an Rx command */
cmdRx.slot = RX_CMD_SLOT;
cmdRx.enable_on_true = false;
cmdRx.enable_on_false = false;
cmdRx.enable_on_compare = false;
cmdRx.allow_delay = false;
cmdRx.params.rx.phy0.config_id = RF_CONFIG_ID;
cmdRx.params.rx.phy0.option_mask = TRX_PHY_FEATURE_FSK_MODE_2B_WISUN;
cmdRx.params.rx.repeat = true; // Repeat forever
/* Associate the Stream with this RX command */
cmdRx.params.rx.stream_id = RX_STREAM_ID;
// Continue with building cmd, storing streams, submitting cmds, etc...
/* Register the RX buffer for received data */
status = TRX_Host_registerRxStream(trxHostHandle, RX_STREAM_ID, rxBuffer,
sizeof(rxBuffer), receiveCallback,
while(TRX_Host_Success != status);
/* Store the RX command on the TRX and issue it to the radio */
status = TRX_Host_storeCmds(trxHostHandle, &cmdRx, 1U, &cmdRx, receiveCallback,
while(TRX_Host_Success != status);
/* RX will repeat forever */
while (1){};
}

Flushing Streams

A stored stream can be explicitly discarded from the TRX using TRX_Host_flushStream(). This sends a TRX_Request_StreamFlush to the TRX, removing all fragments of the stream identified by id. A success status returned from the function only indicates the request began sending; the provided callback is executed with one of the following events:

On TRX_EventStreamFlushComplete the local stream slot for id is released, so the same identifier can be reused in a subsequent TRX_Host_storeStream() call. This is useful for reclaiming a stream slot without transmitting its contents, or for abandoning a stream that is no longer needed.

Carrier Sense Command

The Carrier Sense (CS) command on the TRX can be used to determine if a frequency/channel is busy or idle with a high degree of flexibility using RSSI threshold, mode, exit condition, sample count, sample window, and timeout. The process of constructing a CS command is application specific and often dictated by a specification requiring a channel to be free for a certain duration. Below is an example of configuration of CS command for a Wi-SUN FSK #2b PHY. Details related to each parameter of the CS command are provided after the below example.

#define CMD_CS_MDR_HEADER_SLOT (0U)
#define RSSI_THRESHOLD (-80) // -80 dBm
/* Create a CS command */
cmdCs.slot = CMD_CS_MDR_HEADER_SLOT;
// Report result on channel busy or timeout
// Channel busy if RSSI above threshold
cmdCs.params.cs.rssi_override = RSSI_THRESHOLD;
// Requires that 5 out of 5 most recent samples to be above the RSSI threshold to report channel busy
// 5 samples / 62.5 kHz (sample rate) = 80 us
cmdCs.params.cs.rssi_window = 5U;
cmdCs.params.cs.rssi_count = 5U;
// Set the maximum time to sample the channel before reporting a result
// Timeout = Settling + RSSI Discarding + RSSI Window Settling
cmdCs.params.cs.timeout = 276U; // 51.3 + 96 + 128 = 275.3

Exit Condition

The exit condition, combined with a timeout, determines when the carrier sense operation should finish and report a result:

TRX_CarrierSenseExitCondition Description
TRX_CarrierSense_ExitCondition_WaitForIdle Carrier sense is finished once idle channel is detected or timeout
TRX_CarrierSense_ExitCondition_WaitForBusy Carrier sense is finished once busy channel is detected or timeout
TRX_CarrierSense_ExitCondition_Fast Carrier sense is finished as soon as any result can be obtained (timeout is N/A)

In the case of TRX_CarrierSense_ExitCondition_WaitForIdle or TRX_CarrierSense_ExitCondition_WaitForBusy, if timeout is non-zero, the CS command will finish and return the state of the channel once that timeout is reached.

For TRX_CarrierSense_ExitCondition_Fast, the CS command finishes immediately upon receiving first valid result and the timeout is not used.

This effectively results in 3 different cases:

  • Wait for idle/busy without timeout - carrier sense will only finish once specific state is reached

    wait_for_idle_no_timeout.png
    Wait for idle with no timeout
  • Wait for idle/busy with timeout - carrier sense will finish once specific state is reached. If it's not reached before the timeout occurs, the opposite of that state will be reported

    wait_for_idle_with_timeout.png
    Wait for idle with timeout
  • Fast evaluation - carrier sense will finish as soon as all required (rssi/correlation) results can be obtained. This happens once rssi_window and/or correlation_window number of samples are gathered, depending on the mode.

    cs_fast_evaluation.png
    Fast evaluation

Mode

The TRX_CarrierSenseMode determines what type of measurements should be performed to determine whether the channel is busy or idle:

TRX_CarrierSenseMode Description
TRX_CarrierSense_Mode_Energy Channel busy if energy above threshold. RSSI based CS.
TRX_CarrierSense_Mode_Preamble 1 Channel busy if preamble detected. Preamble based CS.
TRX_CarrierSense_Mode_And 1 Channel busy if energy above threshold and preamble detected
TRX_CarrierSense_Mode_Or 1 Channel busy if energy above threshold or preamble detected

1Preamble based CS is not supported for OFDM PHYs

In case of TRX_CarrierSense_Mode_And or TRX_CarrierSense_Mode_Or, both sub-results have to be obtained for the carrier sense operation to finish, so timing of the command will be dependent on the longer of the two.

The behavior for TRX_CarrierSense_Mode_Energy based CS is described in the diagram below.

cs_rssi_sampling.png
RSSI/Energy Based CS

The behavior for TRX_CarrierSense_Mode_Preamble based CS is described in the diagram below.

cs_corr_sampling.png
Preamble Based CS

Window and Count

The decision to report a channel busy or idle is dependent on the window and count:

  • window: Sliding window in number of samples
  • count: Number of samples within a given window that must be above the threshold for the channel to be considered busy

This concept is illustrated in the diagram below along with the phy specific settling delay and default 6 samples discarded at the beginning of each CS operation.

cs_sliding_window.png
CS Sliding Window and Count

The above example shows a CS command using the Wi-SUN FSK #2B PHY with the following settings:

As illustrated, the command senses the channel as idle for 10 sliding windows then detects 5 samples above the RSSI threshold within window #10 and reports the channel as busy.

The total duration of a window is dependent on the PHY specific sampling rate. This is the amount of time it takes for the TRX to receive one sample from the modem.

For all OFDM rates, the sample time is 6 us. For FSK PHYs, the modem sends 5 samples over every 8 symbols. Thus, in the above example, for Wi-SUN FSK #2B PHY, the sample rate is 62.5 KHz.

Timeout Calculations

The minimum timeout value of the CS command is: MIN_CS_TIMEOUT = T_SETTLING_US + T_DISCARD + T_WINDOW_SETTLING

Where:

  • T_SETTLING_US = PHY specific settling time in microsecond
  • T_DISCARD = Time taken to discard 6 initial samples before samples are considered valid.
  • T_WINDOW_SETTLING = Amount of time for the first N samples to fill the sliding window

Depending on the PHY you are using, these values may vary. Defines are provided in the RCL settings alongside a PHY configuration for the:

  • T_SETTLING_US
  • T_SAMPLE_US

With these values the minimum timeout can be calculated as: MIN_CS_TIMEOUT = T_SETTLING_US + (6 * T_SAMPLE_US) + (N * T_SAMPLE_US)

Where N is the user selected window value of the CS command.

Stopping and Interrupting RF Commands

Ongoing RF commands can either be explicitly stopped using TRX_Host_stopCmd() or interrupted with a new command using the TRX_ConflictPolicy in a command's TRX_Request_CommandStore.conflict_policy field. It's recommended to use the conflict policy instead of explicit stops as it will significantly reduce the delay from issuing a new command to submission to the radio.

Interrupting

If another command is running when the wanted command should start executing, the choice of TRX_ConflictPolicy in TRX_Request_CommandStore.conflict_policy field determines which of the following takes place:

TRX_ConflictPolicy Action
TRX_ConflictPolicy_AlwaysInterrupt The newer command will always cause a HardStop to be invoked on the current running command, and execution is guaranteed
TRX_ConflictPolicy_Polite The newer command causes GracefulStop to be issued. If the running command is in communication, either having received sync-word or is transmitting, then it will not terminate until complete. If allow_delay is not enabled, the newer command will be rejected and a command status will be issued with TRX_CommandStatus_RejectedStart
TRX_ConflictPolicy_NeverInterrupt The newer command will only take precedence if the currently scheduled command has not yet been triggered; else, the newer command will be rejected and a command status will be issued with TRX_CommandStatus_RejectedStart

Stopping

A command may be stopped manually using TRX_Host_stopCmd() with a choice of TRX_Command_StopType that mirrors the conflict resolution options:

TRX_Command_StopType Action
TRX_Command_StopType_None No stop requested
TRX_Command_StopType_DescheduleOnly Stop the command if it has not started executing
TRX_Command_StopType_Graceful Stop the command gracefully causing a GracefulStop, that is finish a packet or transaction in progress before ending
TRX_Command_StopType_Hard Stop the command as soon as possible causing a HardStop

Allowing Delay

If another command is currently active at the time the wanted command should start executing, the allow_delay parameter in TRX_Request_CommandStore determines whether the command will be rejected, or will pend completion of the ongoing command.

Time Synchronization

The TRX Host RF Driver provides a mechanism for synchronizing the timebase of the TRX with that of the Host via the TRX_Host_startTimeSync() API. The synchronization is driven by a common IO edge event on the SPI interface where both the TRX and the Host can capture a timestamp. These timestamps then correspond to the same event and can be used to establish a synchronized timebase between the two devices. When TRX_Host_startTimeSync() is executed, two events will be generated:

Event Description
TRX_EventCaptureHostTime Signals that the common IO edge event between the TRX and Host just occurred and the host may take a timestamp
TRX_EventReceivedTrxTime Signals the Host has received the timestamp from the TRX for the common IO edge event

Sync Frame Detection (SFD)

The TRX Host Driver can optionally be configured to assert an IO when a Sync Frame is Detected (SFD) using the TRX_Host_utilDioSetup() API. The application can then setup an interrupt on this IO to get notified when an SFD event occurs on the TRX. An example of this can be seen in the rfMdrRx example.

# Device Configuration {#trx-host-driver-device-config}
Beyond the per-setting utility APIs (::TRX_Host_utilDioSetup(),
::TRX_Host_utilSetRfMode(), ::TRX_Host_utilSetPowerMode()), the TRX Host Driver
can manage the device-wide settings as a single bundle using the device
configuration APIs. A device configuration groups the following into one
::TRX_DeviceConfigData structure:
* DIO setup (::TRX_DioConfig)
* Clock source (::TRX_ClockConfig)
* Automatic power mode (::TRX_PowerMode)
* RF mode (::TRX_RfMode)
Storing these together ensures the TRX applies a consistent device setup, and
allows the whole bundle to be persisted to non-volatile storage so it is
restored automatically on reset.
| API | Request | Completion Event |
| --- | ------- | ---------------- |
| ::TRX_Host_storeDeviceConfig() | ::TRX_Request_DeviceConfigStore | ::TRX_EventDeviceConfigStoreComplete |
| ::TRX_Host_persistDeviceConfig() | ::TRX_Request_DeviceConfigPersist | ::TRX_EventDeviceConfigPersistComplete |
| ::TRX_Host_getDeviceConfig() | ::TRX_Request_DeviceConfigGet | ::TRX_EventDeviceConfigGetComplete |
As with other TRX Host APIs, a success status returned from these functions only
indicates the request began sending; the outcome on the TRX is reported through
the subscribed callback.
## Storing and Persisting the Device Configuration {#device-config-store-persist}
::TRX_Host_storeDeviceConfig() stores the bundle in RAM on the TRX.
::TRX_Host_persistDeviceConfig() then writes the currently stored bundle to
non-volatile storage. The `allowReplace` argument controls what happens if a
persisted device configuration already exists: if `false`, the request fails; if
`true`, the existing persisted configuration is replaced.
## Reading Back the Device Configuration {#device-config-get}
::TRX_Host_getDeviceConfig() retrieves the current device configuration from the
TRX. When the ::TRX_EventDeviceConfigGetComplete event fires, the ::TRX_Request
pointer passed to the callback can be cast to
::TRX_Request_DeviceConfigGetResponse to read:
* `persisted` - whether the configuration is persisted to NV
* `modified` - whether the stored configuration has changed since it was last
persisted or erased
* `data` - the returned ::TRX_DeviceConfigData
```c
static void deviceConfigCallback(TRX_Host_Handle handle, uintptr_t pData,
TRX_Request *request, uint64_t events, uintptr_t arg)
{
if(TRX_EventLastStatusError & events)
{
// Handle error
}
if(TRX_EventDeviceConfigGetComplete & events)
{
TRX_Request_DeviceConfigGetResponse *pResponse =
(TRX_Request_DeviceConfigGetResponse *)request;
bool isPersisted = pResponse->persisted;
bool isModified = pResponse->modified;
TRX_DeviceConfigData config = pResponse->data;
// Use the returned configuration...
}
}
void configureDevice(TRX_Host_Handle handle)
{
TRX_DeviceConfigData deviceConfig = {0};
deviceConfig.clockConfig = TRX_ClockConfig_XOSC;
deviceConfig.rfMode = TRX_RfMode_SUN;
deviceConfig.powerMode.powerPolicy = TRX_PowerPolicy_StandbyAllow;
deviceConfig.powerMode.dwellTimeUs = 1000;
deviceConfig.dioConfig.dio0 = DIO0_SFD;
// Remaining DIOs default to their GPIO_SET_0 values...
/* Store the bundle on the TRX */
TRX_Host_Status status =
TRX_Host_storeDeviceConfig(handle, deviceConfig, deviceConfigCallback);
while(TRX_Host_Success != status);
// Pend until the TRX_EventDeviceConfigStoreComplete event...
/* Persist it so it survives a reset (replace any existing persisted config) */
status = TRX_Host_persistDeviceConfig(handle, true, deviceConfigCallback);
while(TRX_Host_Success != status);
// Pend until the TRX_EventDeviceConfigPersistComplete event...
}

Power Management

The TRX Host Driver provides APIs to manage the power state of the TRX.

Automatic Standby

TRX_Host_utilSetPowerMode() configures whether the TRX may automatically enter standby when idle, and the dwell time to wait after the SPI chip select is deasserted before doing so. The configuration is provided as a TRX_PowerMode structure:

The provided callback is executed with the TRX_EventPowerModeComplete event.

Note
Automatic power mode handling is not implemented on all TRX firmware versions. Verify support for your device before relying on automatic standby.

Waking the TRX

When automatic standby is enabled, the TRX wakes on the next SPI chip select assertion. TRX_Host_utilWakeTrx() proactively wakes the TRX and blocks until it is ready. Unlike most APIs it is blocking and subscribes to no events.

Shutdown

TRX_Host_utilShutdown() transitions the TRX into shutdown via a TRX_Request_UtilShutdown request. After a successful shutdown the TRX automatically returns to the active state when the host next asserts the SPI chip select. The provided callback is executed with the TRX_EventShutdownComplete event.

Note
The TRX rejects a shutdown request (returning an error via LastStatus) if a radio command is running or scheduled. Issue TRX_Host_stopCmd() on the running/scheduled slot before calling TRX_Host_utilShutdown().

Wi-SUN MDR

The TRX Host Driver can be configured and used to send and receive Wi-SUN MDR packets on FSK and OFDM. For more information and examples of usage of these features, take a look at the TRX Host RF Driver Wi-SUN MDR documentation .

Closing the Driver

The TRX Host Driver can be closed using the TRX_Host_close(), This API is blocking, but there may be higher priority SPI events that trigger callbacks to the application while TRX_Host_close() is being executed. The application must be prepared to handle these events appropriately before issuing the close.