This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CC1310: wireless MCU smart Preamble

Part Number: CC1310

Hello Team,

i want to know if its possible to implement the RxSniff Mode with smart Preamble onto the wireless MCU for example at CC1310 MCU.

When yes, do you have some code examples!

Thx

  • SmartWakeOnRadio.h

    SmartWakeOnRadio_genfsk.c
    /*
     * Copyright (c) 2017, Texas Instruments Incorporated
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     *
     * *  Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     *
     * *  Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the distribution.
     *
     * *  Neither the name of Texas Instruments Incorporated nor the names of
     *    its contributors may be used to endorse or promote products derived
     *    from this software without specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
     * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     */
    
    /***** Includes *****/
    #include <assert.h>
    #include <stdlib.h>
    #include <stdbool.h>
    #include <unistd.h>
    
    /* Drivers */
    #include <ti/drivers/rf/RF.h>
    
    /* RF settings, defines and helper files */
    #include "SmartWakeOnRadio.h"
    #include <smartrf_settings/smartrf_settings.h>
    
    #include <ti/devices/cc13x0/driverlib/rf_prop_cmd.h>
    #include <ti/devices/cc13x0/driverlib/rf_prop_mailbox.h>
    #include <ti/devices/cc13x0/driverlib/rf_data_entry.h>
    
    /***** Defines *****/
    #define PREAMBLE_PACKET_COUNTER_LENGTH      (2)         /* 2 counter bytes */
    #define PREAMBLE_PACKET_HEADER_LENGTH       (1)         /* 1 length byte, has to be increased when address information is used */
    #define PREAMBLE_PACKET_PAYLOAD_LENGTH      (PREAMBLE_PACKET_COUNTER_LENGTH)
    #define PREAMBLE_PACKET_CRC_BYTES           (2)         /* If enabled, 2 Bytes CRC are used. */
    
    #define REQUIRED_RSSI_SAMPLES               (1)         /* How many RSSI samples have to be obtained before the channel is assumed as busy */
    #define MAX_PAYLOAD_LENGTH                  (250)
    #define QUEUE_ENTRY_DATA_LENGTH             ((MAX_PAYLOAD_LENGTH) + 1)   /* Queue entry needs to store length byte and identifier as well */
    
    /* Helper macros */
    
    #define RF_convertSymbolsToRatTicks(symbols, symbolRate) \
        ((uint32_t)(4000000.0f/(symbolRate) * (symbols)))
    
    /* Convenience macro to convert milliseconds into RAT clock ticks */
    #define RF_convertUsToRatTicks(microseconds) \
        (((uint32_t)(microseconds)) * 4)
    
    /* Convenience macro to convert milliseconds into RAT clock ticks */
    #define RF_convertMsToRatTicks(milliseconds) \
        (((uint32_t)(milliseconds)) * 1000 * 4)
    
    /* Swap bytes in 32 bit value.  */
    #define SWAP_BYTES_32(x) \
         ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >>  8) | \
          (((x) & 0x0000ff00) <<  8) | (((x) & 0x000000ff) << 24))
    
    /* Swap bytes in 16 bit value.  */
    #define SWAP_BYTES_16(x) \
          ((((x) & 0xff00) >>  8) | (((x) & 0x00ff) << 8))
    
    
    /* Convenience macro to divide integers and always round the result up */
    #define DIV_INT_ROUND_UP(divident, divisor) \
        (((divident) + ((divisor) - 1)) / (divisor))
    
    
    /***** Type declarations *****/
    typedef struct PartialQueueEntryHeader PartialQueueEntryHeader;
    
    /* The same as rfc_dataEntryPartial_s but without data. This makes it more flexible */
    struct PartialQueueEntryHeader
    {
       PartialQueueEntryHeader* pNextEntry; //!<        Pointer to next entry in the queue, NULL if this is the last entry
       uint8_t status;                      //!<        Indicates status of entry, including whether it is free for the system CPU to write to
       struct {
          uint8_t type:2;                   //!< \brief Type of data entry structure<br>
                                            //!<        0: General data entry <br>
                                            //!<        1: Multi-element Rx entry<br>
                                            //!<        2: Pointer entry<br>
                                            //!<        3: Partial read Rx entry
          uint8_t lenSz:2;                  //!< \brief Size of length word in start of each Rx entry element<br>
                                            //!<        0: No length indicator<br>
                                            //!<        1: One byte length indicator<br>
                                            //!<        2: Two bytes length indicator<br>
                                            //!<        3: <i>Reserved</i>
          uint8_t irqIntv:4;                //!< \brief For partial read Rx entry only: The number of bytes between interrupt generated
                                            //!<        by the radio CPU (0: 16 bytes)
       } config;
       uint16_t length;                     //!< \brief For pointer entries: Number of bytes in the data buffer pointed to<br>
                                            //!<        For other entries: Number of bytes following this length field
       struct {
          uint16_t numElements:13;          //!<        Number of entry elements committed in the entry
          uint16_t bEntryOpen:1;            //!<        1 if the entry contains an element that is still open for appending data
          uint16_t bFirstCont:1;            //!<        1 if the first element is a continuation of the last packet from the previous entry
          uint16_t bLastCont:1;             //!<        1 if the packet in the last element continues in the next entry
       } pktStatus;
       uint16_t nextIndex;                  //!<        Index to the byte after the last byte of the last entry element committed by the radio CPU
    };
    
    
    /* Represents a preamble packet in RX operations */
    typedef __packed struct
    {
        uint8_t length;
        uint16_t counter;
        uint8_t _dummyBytes[2]; // Needed because there are issues on the RF core when receiving packets with less than 4 bytes.
    } RxPreamblePacket;
    
    typedef __packed struct
    {
        uint8_t length;
        uint8_t data[MAX_PAYLOAD_LENGTH];
    } RxDataPacket;
    
    /* Queue entry used for RX operations */
    typedef __packed struct
    {
        rfc_dataEntry_t header;
        union {
            RxPreamblePacket preamblePacket;
            RxDataPacket dataPacket;
        };
    } RxQueueEntry;
    
    /* Represents a preamble packet in TX operations */
    typedef __packed struct
    {
        uint32_t syncWord;
        uint8_t length;
        uint16_t counter;
        uint16_t crc;
    } TxPreamblePacket;
    
    /* Represents a data packet on TX */
    typedef __packed struct
    {
        uint32_t syncWord;
        uint8_t length;
        uint8_t payload[MAX_PAYLOAD_LENGTH + 2];
    } TxDataPacket;
    
    typedef struct
    {
        PartialQueueEntryHeader header;
        TxPreamblePacket packet;
    } TxPreambleQueueEntry;
    
    typedef struct
    {
        PartialQueueEntryHeader header;
        TxDataPacket packet;
    } TxDataQueueEntry;
    
    
    typedef enum {
        TxOperationMode,
        RxOperationMode
    } OperationMode;
    
    /***** Variable declarations *****/
    /* RF driver object and handle */
    static RF_Object rfObject;
    static RF_Handle rfHandle;
    
    static volatile rfc_propRxOutput_t rxStatistics;
    static uint32_t preamblePacketCsTimeoutTicks;
    static uint32_t preamblePacketRxTimeoutTicks;
    static uint32_t dataPacketRxTimeoutTicks;
    static int32_t dataPacketRxStartOffsetTicks;
    static uint32_t preamblePacketDurationTicks;
    static uint32_t numberOfPreamblePackets;
    static uint32_t clockDriftPerWorIntervalTicks;
    static uint32_t rxStartMarginTicks;
    static OperationMode operationMode = RxOperationMode;
    static uint32_t symbolRate;
    
    int32_t preamblePacketCounter;
    static int32_t packetLength;
    
    static uint32_t syncWord;
    
    /* Create a data queue with a single entry */
    volatile RxQueueEntry rxEntry =
    {
         .header.config =
         {
            .type = DATA_ENTRY_TYPE_GEN,
            .lenSz = 0         /* Let the RX command write the length byte instead. */
         },
         .header.pNextEntry = (uint8_t*)&rxEntry,
         .header.status = DATA_ENTRY_PENDING,
         .header.length = QUEUE_ENTRY_DATA_LENGTH
    };
    
    dataQueue_t rxQueue =
    {
         .pCurrEntry = (uint8_t*)&rxEntry,
         .pLastEntry = NULL
    };
    
    RxPreamblePacket rxPreamblePacket;
    
    /* TX side needs a queue for partial entry items.
     * 2 partial entry items are needed as double-buffer
     * for the preamble sequence.
     * This is followed by a 1 partial entry item containing
     * the data packet.
     */
    TxPreambleQueueEntry txPreambleEntries[2] =
    {
        {
            .header.config =
            {
                .type = DATA_ENTRY_TYPE_PARTIAL,
                .lenSz = 0,
                .irqIntv = 0
            },
            .header.pNextEntry = &txPreambleEntries[1].header,
            .header.status = DATA_ENTRY_PENDING,
            .header.length = sizeof(TxPreamblePacket)
        },
        {
            .header.config =
            {
                .type = DATA_ENTRY_TYPE_PARTIAL,
                .lenSz = 0,
                .irqIntv = 0
            },
            .header.pNextEntry = &txPreambleEntries[0].header,
            .header.status = DATA_ENTRY_PENDING,
            .header.length = sizeof(TxPreamblePacket)
        }
    };
    
    TxDataQueueEntry txDataEntry =
    {
        .header.config =
        {
            .type = DATA_ENTRY_TYPE_PARTIAL,
            .lenSz = 0,
            .irqIntv = 0
        },
        .header.pNextEntry = NULL,
        .header.status = DATA_ENTRY_PENDING,
        .header.length = 0 // Is set dynamically in the application
    };
    
    dataQueue_t txQueue =
    {
         .pCurrEntry = (uint8_t*)&txPreambleEntries[0],
         .pLastEntry = NULL
    };
    
    
    /* Used in the RX command queue. */
    rfc_CMD_NOP_t RF_cmdNop =
    {
         .commandNo = CMD_NOP,
         .status = IDLE,
         .startTrigger.triggerType = TRIG_NOW,
         .condition.rule = COND_ALWAYS,
         .pNextOp = (RF_Op*)&RF_cmdPropTx
    };
    
    rfc_CMD_PROP_RX_SNIFF_t RF_cmdPropSniff =
    {
         .commandNo = CMD_PROP_RX_SNIFF,
         .status = IDLE,
         .pNextOp = NULL,
         .startTime = 0,
         .startTrigger.triggerType = TRIG_NOW,
         .startTrigger.bEnaCmd = false,
         .startTrigger.triggerNo = 0,
         .startTrigger.pastTrig = false,
         .condition.rule = COND_NEVER,
         .condition.nSkip = 0,
         .pktConf.bFsOff = false,
         .pktConf.bRepeatOk = false,            /* Stop after receiving a single valid packet */
         .pktConf.bRepeatNok = false,           /* Stop after receiving an erroneous packet */
         .pktConf.bUseCrc = true,              /* Smart preamble packets uses CRC */
         .pktConf.bVarLen = true,               /* Smart preamble packets have a fixed length */
         .pktConf.bChkAddress = false,
         .pktConf.endType = 0,                  /* Packet is received to the end if the end trigger occurred after the sync word */
         .pktConf.filterOp = false,
         .rxConf.bAutoFlushIgnored = true,      /* Drop ignored packets from the queue */
         .rxConf.bAutoFlushCrcErr = false,
         .rxConf.bIncludeHdr = true,
         .rxConf.bIncludeCrc = false,
         .rxConf.bAppendRssi = false,
         .rxConf.bAppendTimestamp = false,
         .rxConf.bAppendStatus = false,
         .syncWord = 0,                                 /* Is copied from RF_cmdPropRx in the application */
         .maxPktLen = PREAMBLE_PACKET_PAYLOAD_LENGTH,
         .address0 = 0,
         .address1 = 0,
         .endTrigger.triggerType = 0x1,
         .endTrigger.bEnaCmd = 0x0,
         .endTrigger.triggerNo = 0x0,
         .endTrigger.pastTrig = 0x0,
         .endTime = 0x00000000,
         .pQueue = &rxQueue,
         .pOutput = (uint8_t*)&rxStatistics    /* Needed to get the RX time stamp */
    };
    
    rfc_CMD_PROP_TX_ADV_t RF_cmdPropTxAdv =
    {
        .commandNo = CMD_PROP_TX_ADV,
        .status = IDLE,
        .pNextOp = NULL,
        .startTime = 0,
        .startTrigger.triggerType = TRIG_NOW,
        .startTrigger.bEnaCmd = 0,
        .startTrigger.triggerNo = 0,
        .startTrigger.pastTrig = 0,
        .condition.rule = COND_NEVER,
        .condition.nSkip = 0,
        .pktConf.bFsOff = 0,
        .pktConf.bUseCrc = 0,
        .pktConf.bCrcIncSw = 0,
        .pktConf.bCrcIncHdr = 0,
        .numHdrBits = 0,
        .startConf.bExtTxTrig = 0,
        .startConf.inputMode = 0,
        .startConf.source = 0,
        .preTrigger.triggerType = TRIG_NOW,
        .preTrigger.bEnaCmd = 0,
        .preTrigger.triggerNo = 0,
        .preTrigger.pastTrig = 0,
        .preTime = 0,
        .pktLen = 0,
        .syncWord = 0, // Not used. Sync word is included into the packet.
        .pPkt = NULL,
    };
    
    /***** Prototypes *****/
    static void setOperationMode(OperationMode newmode);
    void txCallback(RF_Handle handle, RF_CmdHandle ch, RF_EventMask events);
    static uint32_t calculateSymbolRate(uint8_t prescaler, uint32_t rateWord);
    static uint32_t calculateCsTimeout(uint8_t rxBandwidth, uint8_t rssiSamples);
    static uint32_t createPreamblePacket(TxPreamblePacket* destinationBuffer, uint32_t counter);
    static uint32_t createDataPacket(TxDataPacket* destinationBuffer, const void* sourceBuffer, uint32_t length);
    uint16_t crc16(const void* buffer, uint32_t length);
    
    void SmartWakeOnRadio_init()
    {
    #ifdef SmartWakeOnRadio_SKYWORKS_PA
        RF_cmdPropRadioDivSetup.config.frontEndMode = 0x2;
    #endif
    
        /* Calculate timing values first. */
        symbolRate = calculateSymbolRate(RF_cmdPropRadioDivSetup.symbolRate.preScale,
                      RF_cmdPropRadioDivSetup.symbolRate.rateWord);
    
        /* Only 32 bits sync words are supported by this implementation.
         * Use the sync word from smartrf_settings. */
        assert(RF_cmdPropRadioDivSetup.formatConf.nSwBits == 32);
        uint32_t syncWordSymbols = 32;
        syncWord = SWAP_BYTES_32(RF_cmdPropTx.syncWord);
    
        /* When calculating timeouts, we need to take into account that the CC13x0 needs to receive at least 32 symbols
         * random data before the sync word so that the frequency offset tracking works correctly.
         * Even though there is no plain preamble. This can be configured in the setup command so we take the value
         * from there. */
        uint32_t minPreambleSymbolsNeededInRx = RF_cmdPropRadioDivSetup.preamConf.nPreamBytes * 8;
    
        uint32_t dataSymbols = (PREAMBLE_PACKET_HEADER_LENGTH + PREAMBLE_PACKET_PAYLOAD_LENGTH) * 8;
    
        uint32_t crcSymbols = (PREAMBLE_PACKET_CRC_BYTES) * 8;
    
        clockDriftPerWorIntervalTicks = RF_convertUsToRatTicks(20) * (SmartWakeOnRadio_WAKEUP_INTERVAL_MS) / 1000;   /* 20 ppm crystal => 20 us per second */
    
        rxStartMarginTicks = RF_convertUsToRatTicks(250);                /* Extra margin for RX startup delay between start trigger and when
                                                                            the first bit can be received. */
        uint32_t syncWordEvaluationMargin = RF_convertUsToRatTicks(100); /* It takes a bit until the RF core has processed and double-chcecked the sync word. */
    
        preamblePacketDurationTicks =
                RF_convertSymbolsToRatTicks(syncWordSymbols + dataSymbols + crcSymbols, symbolRate);
    
        /* Worst-case carrier-sense (CS) timeout. */
        preamblePacketCsTimeoutTicks =
                calculateCsTimeout(RF_cmdPropRadioDivSetup.rxBw, REQUIRED_RSSI_SAMPLES)
                + RF_convertUsToRatTicks(50);                           /* Add some safety margin */
    
    
        /* Worst-case RX timeout to receive a preamble packet. Assume that has RX started
         * just after the first bit of a preamble packet so that the first packet is lost
         * and we have to wait for a second preamble packet. */
        preamblePacketRxTimeoutTicks =
                calculateCsTimeout(RF_cmdPropRadioDivSetup.rxBw, REQUIRED_RSSI_SAMPLES)         /* RF front-end settle time */
                + RF_convertSymbolsToRatTicks(minPreambleSymbolsNeededInRx, symbolRate)         /* Needed for frequency offset correction */
                + preamblePacketDurationTicks                                                   /* Time for the (potentially) lost first packet */
                + RF_convertSymbolsToRatTicks(syncWordSymbols, symbolRate)                      /* Time until sync of the second packet */
                + syncWordEvaluationMargin;                                                     /* Additional margin that is needed for sync
                                                                                                   word evaluation. If the end trigger would
                                                                                                   fire earlier, the RF core would abort
                                                                                                   RX even though the sync word
                                                                                                   has already been received. */
    
        /* The data packet start time is later calculated from the preamble packet RX time,
         * but it needs a certain compensation. This is a negative value. */
       dataPacketRxStartOffsetTicks =
               - calculateCsTimeout(RF_cmdPropRadioDivSetup.rxBw, REQUIRED_RSSI_SAMPLES)          /* RF front-end settle time */
               - clockDriftPerWorIntervalTicks
               - rxStartMarginTicks;                                                              /* Compensation time between RX start trigger
                                                                                                     and when the first bit can be received */
    
        /* Worst-case RX timeout to receive a data packet. Assume that we woke up
         * on-time. */
        dataPacketRxTimeoutTicks =
                RF_convertMsToRatTicks(1)
                + RF_convertSymbolsToRatTicks(minPreambleSymbolsNeededInRx, symbolRate)         /* Needed for frequency offset correction */
                + RF_convertSymbolsToRatTicks(syncWordSymbols, symbolRate)                    /* Time until sync of the data packet */
                + clockDriftPerWorIntervalTicks                                               /* Compensate clock drift while sleeping
                                                                                                 and waiting for the data packet. When catching
                                                                                                 the first preamble packet, then the sleep duration
                                                                                                 is almost a whole wake-on-radio interval. */
    
                + syncWordEvaluationMargin                                                     /* Additional margin that is needed for sync
                                                                                                  word evaluation. If the end trigger would
                                                                                                  fire earlier, the RF core would abort
                                                                                                  RX even though the sync word
                                                                                                  has already been received. */
                - dataPacketRxStartOffsetTicks;                                                /* RX Start time compensation needs to be taken
                                                                                                  into account. */
    
    
        /* Send 1 more packet than required to guarantee that the receiver will catch a preamble packet
         * within its sniff interval. */
        uint32_t wakeupintervalTicks = RF_convertMsToRatTicks(SmartWakeOnRadio_WAKEUP_INTERVAL_MS);
        numberOfPreamblePackets = DIV_INT_ROUND_UP(wakeupintervalTicks, preamblePacketDurationTicks) + 1;
    
        /* Requiring more than 1 preamble packets allows a simpler implementation. */
        assert(numberOfPreamblePackets >= 2);
    
        /* Copy over non-constant values from CMD_RX_SNIFF generated by SmartRF Studio */
        RF_cmdPropSniff.syncWord = RF_cmdPropRx.syncWord;
        RF_cmdPropSniff.rssiThr = SmartWakeOnRadio_RSSI_THRESHOLD;
        RF_cmdPropSniff.numRssiBusy = REQUIRED_RSSI_SAMPLES;    /* Require this amount of RSSI samples before the channel is assumed to be busy */
        RF_cmdPropSniff.numRssiIdle = 1;
        RF_cmdPropSniff.csConf.bEnaRssi = true;
        RF_cmdPropSniff.csConf.bEnaCorr = false;
        RF_cmdPropSniff.csConf.idleOp = 0;                   /* Do not end on IDLE before csEndTime */
        RF_cmdPropSniff.csConf.busyOp = 1;                   /* Only end earlier when the channel has been found busy */
        RF_cmdPropSniff.csConf.operation = 0;                /* No not require RSSI AND correlation check */
        RF_cmdPropSniff.csConf.timeoutRes = 0;               /* When timeout occurs, treat invalid channel state as BUSY */
    
        RF_cmdPropSniff.csEndTrigger.triggerType = TRIG_REL_START;
        RF_cmdPropSniff.csEndTime = preamblePacketCsTimeoutTicks;
    
        RF_cmdPropSniff.endTrigger.triggerType = TRIG_REL_START;
        RF_cmdPropSniff.endTime = preamblePacketCsTimeoutTicks + preamblePacketRxTimeoutTicks;
    
        /* Request access to the radio. This does not power-up the RF core, but only initialise
         * the driver and cache the setup command. */
        RF_Params rfParams;
        RF_Params_init(&rfParams);
        rfParams.nInactivityTimeout = 1000;
        rfHandle = RF_open(&rfObject, &RF_prop, (RF_RadioSetup*)&RF_cmdPropRadioDivSetup, &rfParams);
        assert(rfHandle != NULL);
    
        setOperationMode(RxOperationMode);
    
        /* Set the frequency. Now the RF driver powers the RF core up and runs the setup command from above.
         * The FS command is executed and also cached for later use when the RF driver does an automatic
         * power up. */
        RF_EventMask events = RF_runCmd(rfHandle, (RF_Op*)&RF_cmdFs, RF_PriorityNormal, NULL, 0);
        assert((events == RF_EventLastCmdDone) && ((volatile RF_Op*)&RF_cmdFs)->status == DONE_OK);
    }
    
    
    int32_t SmartWakeOnRadio_receivePacketFromMaster(void* buffer, uint8_t maxLength)
    {
        assert(buffer != NULL);
        assert(maxLength <= MAX_PAYLOAD_LENGTH);
    
        setOperationMode(RxOperationMode);
    
        /* A small ad-hoc state machine for the RX process */
        typedef enum {
            RxInit,
            WaitingForPreamblePacket,
            WaitingForDataPacket,
            RxFinished
        } RxState;
    
        /* Stay in this function until a valid data packet has been received */
        RxState state;
        RxState nextState;
        for (state = RxInit; state != RxFinished; state = nextState)
        {
            if (state == RxInit)
            {
                /* Try to receive preamble packets periodically. */
                RF_cmdPropSniff.startTrigger.triggerType = TRIG_ABSTIME;
                RF_cmdPropSniff.startTime = RF_getCurrentTime();
                RF_cmdPropSniff.maxPktLen = PREAMBLE_PACKET_PAYLOAD_LENGTH;
    
                nextState = WaitingForPreamblePacket;
            }
            else if (state == WaitingForPreamblePacket)
            {
                rxEntry.header.status = DATA_ENTRY_PENDING;     /* Prepare queue entry for RX */
    
                /* Set a start time in the future and allow the RF driver to power the RF core down. */
                RF_cmdPropSniff.startTime += RF_convertMsToRatTicks(SmartWakeOnRadio_WAKEUP_INTERVAL_MS);
    
                /* Start the RX operation and try to receive a single packet.
                 * First iteration: Execute immediately (startTrigger.pastTrig = 1).
                 * Other iterations: Power the RF core down and wait for the start trigger. */
                RF_EventMask events = RF_runCmd(rfHandle, (RF_Op*)&RF_cmdPropSniff, RF_PriorityNormal, NULL, 0);
                if (events & (RF_EventCmdAborted | RF_EventCmdStopped | RF_EventCmdCancelled))
                {
                    /* Operation was cancelled by the user. */
                    packetLength = 0;
                    nextState = RxFinished;
                }
                else if (events & RF_EventLastCmdDone)
                {
                    /* Evaluate the RX command status */
                    switch (((volatile RF_Op*)&RF_cmdPropSniff)->status)
                    {
                    case PROP_DONE_OK:
                    case PROP_DONE_ENDED:
                        preamblePacketCounter = 0;
                        memcpy((uint32_t*)&preamblePacketCounter, (const uint8_t*)&rxEntry.preamblePacket.counter, PREAMBLE_PACKET_COUNTER_LENGTH);
                        nextState = WaitingForDataPacket;
                        break;
                    case PROP_DONE_RXTIMEOUT:
                    case PROP_DONE_IDLE:
                    case PROP_DONE_IDLETIMEOUT:
                        /* No packet received. Proceed with next sniff iteration. */
                        nextState = WaitingForPreamblePacket;
                        break;
                    case PROP_ERROR_NO_FS:
                        /* Synth calibration failed. See errata "synth calibration might fail".
                         * It is safe to ignore it and proceed with the next sniff iteration. */
                        nextState = WaitingForPreamblePacket;
                        break;
                    default:
                        /* Some unexpected error occurred. */
                        assert(false);
                        break;
                    }
                }
                else
                {
                    /* Unknown event happened */
                    assert(false);
                }
            }
            else if (state == WaitingForDataPacket)
            {
                /* Now we know when the real data packet will arrive and can
                 * set a very tight start and end time. */
    
                /* The expected start time is calculated from the RX timestamp of the preamble
                 * packet and needs to be compensated because the RX timestamp assumes that a
                 * proper preamble has been sent as configured in the setup command. */
                RF_cmdPropRx.startTrigger.triggerType = TRIG_ABSTIME;
                RF_cmdPropRx.startTime = rxStatistics.timeStamp
                        + preamblePacketCounter * preamblePacketDurationTicks
                        + dataPacketRxStartOffsetTicks;
    
                RF_cmdPropRx.endTrigger.triggerType = TRIG_REL_START;
                RF_cmdPropRx.endTime = dataPacketRxTimeoutTicks;
    
                RF_cmdPropRx.maxPktLen = maxLength;             /* Implement packet length filtering to avoid PROP_ERROR_RXBUF */
                RF_cmdPropRx.pQueue = &rxQueue;
    
                rxEntry.header.status = DATA_ENTRY_PENDING;     /* Prepare queue entry for RX */
    
                RF_EventMask events = RF_runCmd(rfHandle, (RF_Op*)&RF_cmdPropRx, RF_PriorityNormal, NULL, 0);
                if (events & (RF_EventCmdAborted | RF_EventCmdStopped | RF_EventCmdCancelled))
                {
                    /* Operation was cancelled by the user. */
                    packetLength = 0;
                    nextState = RxFinished;
                }
                else if (events & RF_EventLastCmdDone)
                {
                    /* Evaluate the RX command status */
                    switch (((volatile RF_Op*)&RF_cmdPropRx)->status)
                    {
                    case PROP_DONE_OK:
                    case PROP_DONE_ENDED:
                        /* Packet received. */
                        packetLength = rxEntry.dataPacket.length;
                        /* Copy the data to the caller's buffer. */
                        memcpy(buffer, (const uint8_t*)&rxEntry.dataPacket.data, rxEntry.dataPacket.length);
                        nextState = RxFinished;
                        break;
                    case PROP_DONE_RXTIMEOUT:
                        /* No packet received. Error. */
                        nextState = RxInit;
                        break;
                    case PROP_ERROR_NO_FS:
                        /* Synth calibration failed. See errata "synth calibration might fail".
                         * It will only rarely fail, so we can ignore it. */
                        nextState = RxInit;
                        break;
                    default:
                        /* Some unexpected error occurred */
                        assert(false);
                        break;
                    }
                }
            }
        }
    
        /* Start manual power down since we do not know when the next operation starts */
        //RF_yield(rfHandle);
    
        return packetLength;
    }
    
    
    int32_t SmartWakeOnRadio_receivePacketFromClient(void* buffer, uint8_t maxLength)
    {
        assert(buffer != NULL);
        assert(maxLength <= MAX_PAYLOAD_LENGTH);
    
        setOperationMode(RxOperationMode);
    
        /* A small ad-hoc state machine for the RX process */
        typedef enum {
            RxInit,
            WaitingForDataPacket,
            RxFinished
        } RxState;
    
        /* Stay in this function until a valid data packet has been received */
        RxState state;
        RxState nextState;
        for (state = RxInit; state != RxFinished; state = nextState)
        {
            if (state == RxInit)
            {
                /* Now we know when the real data packet will arrive and can
                 * set a very tight start and end time. */
                RF_cmdPropRx.startTrigger.triggerType = TRIG_NOW;
                RF_cmdPropRx.endTrigger.triggerType = TRIG_NEVER;
                RF_cmdPropRx.maxPktLen = maxLength;             /* Implement packet length filtering to avoid PROP_ERROR_RXBUF */
                RF_cmdPropRx.pQueue = &rxQueue;
    
                nextState = WaitingForDataPacket;
            }
            else if (state == WaitingForDataPacket)
            {
                rxEntry.header.status = DATA_ENTRY_PENDING;     /* Prepare queue entry for RX */
    
                RF_EventMask events = RF_runCmd(rfHandle, (RF_Op*)&RF_cmdPropRx, RF_PriorityNormal, NULL, 0);
                if (events & (RF_EventCmdStopped | RF_EventCmdCancelled))
                {
                    packetLength = 0;
                    nextState = RxFinished;
                }
                else if (events & RF_EventLastCmdDone)
                {
                    /* Evaluate the RX command status */
                    switch (((volatile RF_Op*)&RF_cmdPropRx)->status)
                    {
                    case PROP_DONE_OK:
                        /* Packet received. */
                        packetLength = rxEntry.dataPacket.length;
                        /* Copy the data to the caller's buffer. */
                        memcpy(buffer, (void*)&rxEntry.dataPacket.data, packetLength);
                        nextState = RxFinished;
                        break;
                    case PROP_DONE_RXTIMEOUT:
                        /* No packet received. Error. */
                        nextState = RxInit;
                        break;
                    case PROP_ERROR_NO_FS:
                        /* Synth calibration failed. See errata "synth calibration might fail".
                         * It will only rarely fail, so we can ignore it. */
                        nextState = RxInit;
                        break;
                    default:
                        /* Some unexpected error occurred */
                        assert(false);
                        break;
                    }
                }
                else
                {
                    /* Unexpected event. */
                    assert(false);
                }
            }
        }
    
        return packetLength;
    }
    
    
    int32_t SmartWakeOnRadio_transmitPacketToMaster(void* buffer, uint8_t length)
    {
        assert(buffer != NULL);
        assert(length <= MAX_PAYLOAD_LENGTH);
    
        setOperationMode(TxOperationMode);
    
        packetLength = length;
    
        RF_cmdPropTx.startTrigger.triggerType = TRIG_NOW;
        RF_cmdPropTx.condition.rule = COND_NEVER;
        RF_cmdPropTx.pktConf.bVarLen = true;
        RF_cmdPropTx.pktConf.bUseCrc = true;
        RF_cmdPropTx.pPkt = (uint8_t*)buffer;
        RF_cmdPropTx.pktLen = PREAMBLE_PACKET_PAYLOAD_LENGTH;
    
        RF_EventMask events = RF_runCmd(rfHandle, (RF_Op*)&RF_cmdNop, RF_PriorityNormal, NULL, 0);
        if (events & (RF_EventCmdStopped | RF_EventCmdCancelled))
        {
            /* Operation was cancelled by the user. Ignore. */
            length = 0;
        }
        else if (events & RF_EventLastCmdDone)
        {
            if (((volatile RF_Op*)&RF_cmdPropTx)->status == PROP_DONE_OK)
            {
                /* Packet was sent successfully */
            }
            else if (((volatile RF_Op*)&RF_cmdPropTx)->status == PROP_ERROR_NO_FS)
            {
                /* Ignore this error as it occurs only very rarely, but return 0
                 * to indicate that the packet was not sent. */
                length = 0;
            }
            else
            {
                /* An unknown error occured */
                assert(false);
            }
        }
        else
        {
            /* Unexpected event */
            assert(false);
        }
    
        return length;
    }
    
    
    int32_t SmartWakeOnRadio_transmitPacketToClient(void* buffer, uint8_t length)
    {
        assert(buffer != NULL);
        assert(length <= MAX_PAYLOAD_LENGTH);
    
        setOperationMode(TxOperationMode);
    
        // Reset the TX queue
        txPreambleEntries[0].header.status = DATA_ENTRY_PENDING;
        txPreambleEntries[0].header.pNextEntry = &txPreambleEntries[1].header;
        txPreambleEntries[1].header.status = DATA_ENTRY_PENDING;
        txPreambleEntries[1].header.pNextEntry = &txPreambleEntries[0].header;
        txDataEntry.header.status = DATA_ENTRY_PENDING;
        txQueue.pCurrEntry = (uint8_t*)&txPreambleEntries[0];
    
        RF_cmdPropTxAdv.syncWord = 0x55555555; /* Sync word is not used. Make it look like a preamble */
        RF_cmdPropTxAdv.startTrigger.triggerType = TRIG_NOW;
        RF_cmdPropTxAdv.pPkt = (uint8_t*)&txQueue;
        RF_cmdPropTxAdv.pktLen = 0; // Indicates that we are using a queue for TX
    
        // Prepare the data packet entry and 2 initial preamble queue entries
        uint32_t dataPacketLength = createDataPacket(&txDataEntry.packet, buffer, length);
        txDataEntry.header.length = dataPacketLength;
    
        createPreamblePacket(&txPreambleEntries[0].packet, numberOfPreamblePackets);
        createPreamblePacket(&txPreambleEntries[1].packet, numberOfPreamblePackets - 1);
        preamblePacketCounter = numberOfPreamblePackets - 2;
    
        RF_EventMask events = RF_runCmd(rfHandle, (RF_Op*)&RF_cmdPropTxAdv, RF_PriorityNormal, txCallback, RF_EventTxEntryDone);
        if (events & RF_EventCmdStopped)
        {
            /* Operation was cancelled by the user. Ignore. */
            length = 0;
        }
        else if (events & RF_EventLastCmdDone)
        {
            uint32_t status = ((volatile RF_Op*)&RF_cmdPropTxAdv)->status;
            if (status == PROP_DONE_OK)
            {
                /* Packet was sent successfully */
            }
            else if (status == PROP_ERROR_NO_FS)
            {
                /* Ignore this error as it occurs only very rarely, but return 0
                 * to indicate that the packet was not sent. */
                length = 0;
            }
            else
            {
                /* An unknown error occured */
                assert(false);
            }
        }
    
        return length;
    }
    
    
    void SmartWakeOnRadio_cancelOperation()
    {
        RF_cancelCmd(rfHandle, RF_CMDHANDLE_FLUSH_ALL, 1);
    }
    
    
    void txCallback(RF_Handle handle, RF_CmdHandle ch, RF_EventMask events)
    {
        /* This callback event is time-critical. It must be processed at least within 1 millisecond
         * so that the next TX queue item can be updated on-time.
         */
        if (events & RF_EventTxEntryDone)
        {
            /* Transmission started at txPreambleEntries[0],
             * but we are counting backwards. Get the index of the
             * next free entry.
             */
            uint32_t nextEntryToBeProcessedIndex = (numberOfPreamblePackets - preamblePacketCounter) % 2;
    
            if (preamblePacketCounter > 1)
            {
                /* Prepare the counter for the next preamble packet */
                createPreamblePacket(&txPreambleEntries[nextEntryToBeProcessedIndex].packet, preamblePacketCounter);
                preamblePacketCounter--;
    
                /* Reset entry status */
                txPreambleEntries[nextEntryToBeProcessedIndex].header.status = DATA_ENTRY_PENDING;
            }
            else if (preamblePacketCounter == 1)
            {
                /* Prepare a normal preamble pattern with the same length
                 * as a preamble packet. */
                memset(&txPreambleEntries[nextEntryToBeProcessedIndex].packet, 0x55, sizeof(TxPreamblePacket));
                preamblePacketCounter--;
            }
            else if (preamblePacketCounter == 0)
            {
                /* All preamble packets have been processed, and
                 * one preamble packet is still pending in the TX queue.
                 * Get the index of the pending buffer. */
                uint32_t currentTxEntryIndex = (numberOfPreamblePackets + 1) % 2;
    
                /* Now insert the data packet to terminate the whole sequence. */
                txPreambleEntries[currentTxEntryIndex].header.pNextEntry = &txDataEntry.header;
                preamblePacketCounter--;
            }
            else
            {
                /* The buffer queue is being flushed now. */
            }
        }
        else if (events & RF_EventLastCmdDone)
        {
            /* The last TX command has been finished. Nothing to do here. */
        }
    }
    
    /* Calculates datarate from prescaler and rate word */
    static uint32_t calculateSymbolRate(uint8_t prescaler, uint32_t rateWord)
    {
        /* Calculate datarate according to TRM Section 23.7.5.2:
         * f_baudrate = (R * f_ref)/(p * 2^20)
         *   - R = rateWord
         *   - f_ref = 24Mhz
         *   - p = prescaler */
        uint64_t numerator = rateWord*24000000ULL;
        uint64_t denominator = prescaler*1048576ULL;
        uint32_t result = (uint32_t)(numerator/denominator);
        return result;
    }
    
    /* Calculates the time to get an amount of 'samples' RSSI samples.
     *
     * The timing has been taken from the Proprietary RF user's guide
     * at http://dev.ti.com/tirex/content/simplelink_cc13x0_sdk_1_30_00_06/docs/proprietary-rf/html/rf-commands-reference/cmd_prop_cs.html
     */
    static uint32_t calculateCsTimeout(uint8_t rxBandwidth, uint8_t samples)
    {
        uint32_t firstRssiSampleTimeUs = 0;
        uint32_t rssiSamplePeriodUs = 0;
        switch (rxBandwidth)
        {
        case 32:
        case 33:
        case 34:
            firstRssiSampleTimeUs = 348;
            rssiSamplePeriodUs = 136;
            break;
        case 35:
        case 36:
        case 37:
            firstRssiSampleTimeUs = 214;
            rssiSamplePeriodUs = 68;
            break;
        case 38:
        case 39:
        case 40:
            firstRssiSampleTimeUs = 149;
            rssiSamplePeriodUs = 34;
            break;
        case 41:
        case 42:
        case 43:
            firstRssiSampleTimeUs = 114;
            rssiSamplePeriodUs = 17;
            break;
        case 44:
        case 45:
        case 46:
            firstRssiSampleTimeUs = 98;
            rssiSamplePeriodUs = 10;
            break;
        case 47:
        case 48:
        case 49:
            firstRssiSampleTimeUs = 89;
            rssiSamplePeriodUs = 10;
        default:
            assert(false); /* Illegal RX bandwidth */
        }
    
        uint32_t csTimeoutUs = firstRssiSampleTimeUs + rssiSamplePeriodUs * samples + 5;
        return RF_convertUsToRatTicks(csTimeoutUs);
    }
    
    /* Assembles a full smart preamble packet including sync word, data, (CRC)
     * in 'destinationBuffer' and returns the final packet length.
     */
    static uint32_t createPreamblePacket(TxPreamblePacket* destinationBuffer, uint32_t counter)
    {
      destinationBuffer->syncWord = syncWord;
      destinationBuffer->length   = sizeof(destinationBuffer->counter);
      destinationBuffer->counter  = counter;
      uint16_t crc = crc16(&destinationBuffer->length, sizeof(destinationBuffer->length) + sizeof(destinationBuffer->counter));
      destinationBuffer->crc = SWAP_BYTES_16(crc);
      return 4 + 2;
    }
    
    /* Assembles a full data packet including sync word, data and CRC in 'destinationBuffer'
     * and returns the final packet length.
     */
    static uint32_t createDataPacket(TxDataPacket* destinationBuffer, const void* sourceBuffer, uint32_t length)
    {
        destinationBuffer->syncWord = syncWord;
        destinationBuffer->length = length;
        memcpy(&destinationBuffer->payload, sourceBuffer, length);
        uint16_t crc = crc16(&destinationBuffer->length, sizeof(destinationBuffer->length) + length);
        crc = SWAP_BYTES_16(crc);
        memcpy(destinationBuffer->payload + length, &crc, sizeof(crc));
        return 4 + length + 2;
    }
    
    
    uint16_t crc16(const void* buffer, uint32_t length)
    {
        const uint32_t CrcInitValue = 0xFFFF;
        const uint16_t CrcPolynomial = 0x8005;
    
        uint16_t checksum = CrcInitValue;
    
        uint32_t byteIndex;
        for (byteIndex = 0; byteIndex < length; byteIndex++)
        {
            uint8_t byte = ((const uint8_t*)buffer)[byteIndex];
            uint32_t bitIndex;
            for (bitIndex = 0; bitIndex < 8; bitIndex++)
            {
                if (((checksum & 0x8000) >> 8) ^ (byte & 0x80)) {
                    checksum = (checksum << 1) ^ CrcPolynomial;
                } else {
                    checksum = (checksum << 1);
                }
                byte <<= 1;
            }
        }
        return checksum;
    }
    
    
    /*
    Configures the radio front-end into the correct mode.
    This function may take up to 1.5 ms to run.
    */
    void setOperationMode(OperationMode newmode)
    {
        if (operationMode == newmode)
        {
            return;
        }
    
    #ifdef SmartWakeOnRadio_SKYWORKS_PA
        /* Update the setup command with a new front-end config.
         * The change takes effect at the next power cycle */
        if(newmode == TxOperationMode)
        {
            RF_cmdPropRadioDivSetup.config.frontEndMode = 0x1;
            RF_cmdPropRadioDivSetup.txPower = 0x1cc7;
        }
        else if (newmode == RxOperationMode)
        {
            RF_cmdPropRadioDivSetup.config.frontEndMode = 0x2;
        }
        /* Tell the RF driver that the setup command has changed. */
        RF_control(rfHandle, RF_CTRL_UPDATE_SETUP_CMD, NULL);
        /* Power the RF core down manually if necessary */
        RF_yield(rfHandle);
        /* Sleep for 1.5 ms to make sure that sleep takes effect. */
        usleep(1500);
    #endif
    
        operationMode = newmode;
    }
    
    Hi,

    see the attached files on how to implement smartpreamble