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.

CC1312R: Notification a specific packet is received or dropped after it is transmitted from a sender

Part Number: CC1312R
Other Parts Discussed in Thread: CC1200

Hi:

    I am running the rfPacketTx app transmitting continuously small packets to a dedicated receiver.

I run rf_runCmd on the receiver and use the callback to notify me when a packet arrives.  I need to

have a way to know that a packet was sent but the receiver did not detect it.  I've looked at all the

event masks from the callback, but I don't think I've seen anything useful for me.  Is there a way I

can check down in the RF driver to get a notification immediately when a packet arrives, instead

of relying on the callback?   In other words, I would like to find a way to signal to the receiver a

packet has been sent, and have the receiver check within the transmit timeframe if the packet

actually arrives at around that time.

Thanks,

TW

  • Hi Tak,

    There are a few different ways to approach this. E.g. if you use the TI 15.4-Stack, ACK packets are implemented.

    Sounds like you might want to implement a synchronous network where packets have a predetermined timeframe when they should be sent?

    The callback is implemented to give you an immediate event when a packet arrives. Is there some other configuration you need? E.g. an event when just the sync word was received?

    Cheers,

    Marie H

  • Marie H:

       Yes.  The transmission and reception of my packets have to be synchronized, only the data content, though.  I don't care about preambles and sync words. Suppose I have 16 packets of data.  Each packet is sent from the transmitter at a fixed time.  On the receiving side, I have 16 slots to receive all the packets.  I need to know, only on the receiving end, what packets were sent but not received within the fixed time, so I can put the received packets in the right slots and leave the slots that correspond to missed packets empty.  The ACK packets of TI 15.4-stack are for the transmitting side or receiving side?  I don't care about the transmitter dropping packets, but the receiving side needs to know, as it is a continuing streams of packets being transmitted.

    Can you point me to some example usage or documentation?

    Thank you, as usual.


    TW

  • Curious I: What is the use case here? 

    Curious II: It sounds like what you need to know the time slot the packet is sent in or received in. On the TX side you can add a sequence number (or a time information) and the RX will know if the last received packet was sequence number 12 and it receives number 14 that a packet is lost. Similar can be used on the RX side. If the lat packet was received at time slot 4 and you know it's 50 ms between packets and you receive the next packet after 100 ms, then you know that this is for slot 6. Would that work? 

    Normally acks are used as a notification to resend a packet (no ack received, resend). From the question it sounds like that is not an option?  

  • Ter:

             We have very stringent timing and rate requirements.  So the packets are as short as we can deal with and each slot is a different frequency.  Adding an extra byte for sequence number is not an option.  So, I had a design that a synchronous signal (base on FPGA that is synchronized globally) is provided to both the sender and receiver, so that when a short packet is sent, the receiver is expecting a reception within that timerframe, so we only fill out the right slots with received packets, and the higher level utilities can deal with FEC/retransmission.

           Using rf_runCmd to start the receiver, the rxCallback for a packet is way behind the synchronous signal, so it is not useful.  I've looked at all the rxCallback events and could not find anything I could use.  I was thinking I might need to go down to the SWI level where the radio detects power and begins to receive to coordinate with the transmission timeframe?

           Any recommendations?

    TW

  • Not sure if your current approach will work. Even if you use CC1200 which has a hardware based state machine which gives a very deterministic receive chain (no SW delay), the reception is delayed some bytes due to latency through the modem. Meaning that if you setup the rx slot equal to the tx slot you will miss a rx packet. CC1312R is a software based radio meaning that the timing is not fully deterministic. That won't help if you try to modify the driver. The callback is basically the swi handling . 

    Would you be able to sort the packets based on the timeslot that could be appended in rx?  

    RF_cmdPropRx.rxConf.bAppendTimestamp = 1;

    See dev.ti.com/.../time-synchronization.html

  • Ter:

      If I set RF_cmdPropRx.rxConf.bAppendTimestamp = 1, is there a timestamp associated with the rxCallback for each packet? If the timestamp corresponds to the reception in hundred microseconds, that might work.  I looked at the page, but I am not clear where to look for the timestamp.

    TW

  • Ter:

       I found it in some documentation:  RF_cmdPropRx.rxConf.bAppendTimestamp = true; /* Append RX time stamp to the packet payload */

    If the timestamp granularity is in hundred microseconds, it will work for my purpose.

    Thanks,

    TW

  • I suggest you test the https://dev.ti.com/tirex/explore/node?node=AKAX6KSybstfuDpGhvdR5w__BSEc4rl__LATEST examples and check the resolution/ accuracy. I don't have the numbers on the top of my head. 

  • Ter:

       I believe the timestamps is in ticks based on an absolute start time.  I've also found an utility to convert ticks to microseconds:

    RF_convertRatTicksToUs.  I will give a quick try and let you know.

    Thanks,

    TW

  • The following is from https://software-dl.ti.com/simplelink/esd/simplelink_cc13x2_26x2_sdk/4.20.00.35/exports/docs/proprietary-rf/proprietary-rf-users-guide/proprietary-rf/time-synchronization.html

    When receiving this packet, the receiver must read the timestamp from the packet payload, but must also configure the RF core to append a timestamp to each received packet. This is the time when the RF core raises the signal “Synchronization found” and we choose the name rxTimestamp:

    // Exported from SmartRF Studio
    rfc_CMD_PROP_RX_t rxCommand;
    
    // Append RX timestamp to the payload
    RF_cmdPropRx.rxConf.bAppendTimestamp = 1;
    
    // The code to execute the RX command and to setup
    // the RX data queue is not shown here.
    
    // When reading the packet content from the
    // RX data queue, rxTimestamp is behind the payload:
    rfc_dataEntryGeneral_t* currentDataEntry = RFQueue_getDataEntry();
    // Assuming variable length
    uint8_t packetLength = *(uint8_t*)(&currentDataEntry->data);
    uint8_t* packetDataPointer = (uint8_t*)(&currentDataEntry->data + 1);
    uint32_t rxTimestamp;
    -----> memcpy(&rxTimestamp, &packetDataPointer + packetLength, 4);
    
    // The TX timestamp is found in the payload
    uint32_t txTimestamp;
    -----> memcpy(&rxTimestamp, packetDataPointer + packetLength, 4);


    The two lines above do not look right. I cannot use txTimestamp. Is the RX
    timestamp the time the preamble is received? What is the starting time
    the RX timestamp is referenced to?
  • The following snippet is from my code:

    uint32_t rxTime;

    define MAX_LENGTH 30 /* Max length byte the radio will accept */
    #define NUM_APPENDED_BYTES 5 /* The Data Entries data field will contain:
    * 1 address byte in the header
    * Max 30 payload bytes
    * 4 timestamp bytes */

    packetDataPointer = (uint8_t*)(&currentDataEntry->data);
    memcpy(rxBuf, packetDataPointer, PAYLOAD_LENGTH);         /* Fixed length, so no length byte */
    memcpy(&rxTime, packetDataPointer + PAYLOAD_LENGTH, 4);

    Printing out rxTime, I expect to see the number of ticks since the receive was started.  But I only see

    incoherent data like:

    0x0000000c
    0x00000001
    0x00000099
    0x00000004
    0x00000000
    0x00000000

    Something I did not do or did wrong?

    TW

  • Did you set: RF_cmdPropRx.rxConf.bAppendTimestamp = 1;

  • Yes, I did.  

  • Also, what is the appended timestamp relative to?  The startTime of the RF_cmdPropRx is set to 0.

    Is the timestamp incremental starting at 0?  Does the timestamp value wrap around?

  • I am attaching the source code I use to receive and read the timestamp.

    Please let me know if you see the problem.  Thanks.

     /*
     * Copyright (c) 2019, 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 *****/
    /* Standard C Libraries */
    #include <stdlib.h>
    #include <unistd.h>
    #include <semaphore.h>
    #include <ti/sysbios/knl/Semaphore.h>
    #include <ti/sysbios/knl/Mailbox.h>
    #include <ti/sysbios/BIOS.h>
    
    /* TI Drivers */
    #include <ti/drivers/rf/RF.h>
    #include <ti/drivers/PIN.h>
    #include <ti/drivers/pin/PINCC26XX.h>
    
    /* Driverlib Header files */
    #include DeviceFamily_constructPath(driverlib/rf_prop_mailbox.h)
    #include <ti/drivers/GPIO.h>
    #include <ti/drivers/UART2.h>
    
    
    /* Board Header files */
    #include "ti_drivers_config.h"
    #include "RFQueue.h"
    #include <ti_radio_config.h>
    
    /***** Defines *****/
    
    /* Packet RX Configuration */
    #define DATA_ENTRY_HEADER_SIZE 8  /* Constant header size of a Generic Data Entry */
    #define NUM_DATA_ENTRIES       32  /* NOTE: Only two data entries supported at the moment */
    #define MAX_LENGTH             30 /* Max length byte the radio will accept */
    #define NUM_APPENDED_BYTES     4  /* The Data Entries data field will contain:
    				   * 1 address byte in the header
                                       * Max 30 payload bytes
                                       * 4 timestamp bytes */
    
    
    /* Do power measurement */
    //#define POWER_MEASUREMENT
    
    /* Packet TX Configuration */
    #define WRITE_SIZE	96	
    #define NUM_HOPS	16
    #define PAYLOAD_LENGTH     (WRITE_SIZE/NUM_HOPS) 
    #ifdef POWER_MEASUREMENT
    #define PACKET_INTERVAL     0  /* For power measurement set packet interval to 5s */
    #else
    #define PACKET_INTERVAL     0  /* Set packet interval to 500000us or 500ms */
    #endif
    
    /* semaphore object definitions */
    sem_t hop_sem;
    static hop_state = 0;
    static hop_count = 0;
    
    /***** Prototypes *****/
    static void rxCallback(RF_Handle h, RF_CmdHandle ch, RF_EventMask e);
    static void receive_data(void);
    
    
    /***** Variable declarations *****/
    static RF_Object rfObject;
    static RF_Handle rfHandle;
    
    
    extern const Mailbox_Handle UART_mailbox;
    
    /* Pin driver handle */
    static PIN_Handle ledPinHandle;
    static PIN_State ledPinState;
    
    
    /* Buffer which contains all Data Entries for receiving data.
     * Pragmas are needed to make sure this buffer is 4 byte aligned (requirement from the RF Core) */
    #if defined(__TI_COMPILER_VERSION__)
    #pragma DATA_ALIGN (rxDataEntryBuffer, 4);
    static uint8_t
    rxDataEntryBuffer[RF_QUEUE_DATA_ENTRY_BUFFER_SIZE(NUM_DATA_ENTRIES,
                                                      MAX_LENGTH,
                                                      NUM_APPENDED_BYTES)];
    #elif defined(__IAR_SYSTEMS_ICC__)
    #pragma data_alignment = 4
    static uint8_t
    rxDataEntryBuffer[RF_QUEUE_DATA_ENTRY_BUFFER_SIZE(NUM_DATA_ENTRIES,
                                                      MAX_LENGTH,
                                                      NUM_APPENDED_BYTES)];
    #elif defined(__GNUC__)
    static uint8_t
    rxDataEntryBuffer[RF_QUEUE_DATA_ENTRY_BUFFER_SIZE(NUM_DATA_ENTRIES,
                                                      MAX_LENGTH,
                                                      NUM_APPENDED_BYTES)]
                                                      __attribute__((aligned(4)));
    #else
    #error This compiler is not supported.
    #endif
    
    /* Receive dataQueue for RF Core to fill in data */
    static dataQueue_t dataQueue;
    static rfc_dataEntryGeneral_t* currentDataEntry;
    static uint8_t packetLength;
    static uint8_t* packetDataPointer;
    
    
    static uint8_t packet[MAX_LENGTH + NUM_APPENDED_BYTES - 1]; /* The length byte is stored in a separate variable */
    static uint8_t frame[128];
    static uint16_t seqNumber;
    static int tx_ready;
    
    UART2_Handle uart;
    static int packet_rx;
    static char buffer[128];
    static int numb = 0;
    
    
    /*
     * Application LED pin configuration table:
     *   - All LEDs board LEDs are off.
     */
    PIN_Config pinTable[] =
    {
        CONFIG_PIN_GLED | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,
        CONFIG_PIN_RLED | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,
        PIN_TERMINATE
    };
    
    void *write_uart(void *arg0)
    {
    static int cnt = 0;
    static c = 0;
    
        while(1)
        {
    
           	Semaphore_pend(&hop_sem, BIOS_WAIT_FOREVER);
            UART2_write(uart, frame, WRITE_SIZE, NULL);
    	memset(frame, 'X', WRITE_SIZE);
    	memset(frame, 0, WRITE_SIZE);
    	if (++cnt == 1000)
    	{
    		cnt = 0;
            	PIN_setOutputValue(ledPinHandle, CONFIG_PIN_RLED,
                               !PIN_getOutputValue(CONFIG_PIN_RLED));
    	}
        }
    }
    
    
    
    char pbuf[128];
    void rxCallback(RF_Handle h, RF_CmdHandle ch, RF_EventMask e)
    {
    uint8_t timestamp[4];
    uint32_t tstamp;
    
        if (e & RF_EventRxEntryDone)
        {
            /* Get current unhandled data entry */
            currentDataEntry = RFQueue_getDataEntry();
    
            /* Handle the packet data, located at &currentDataEntry->data:
             * - Length is the first byte with the current configuration
             * - Data starts from the second byte */
    	/* Configuring fixed length data, the length field no longer exists */
            packetDataPointer = (uint8_t*)(&currentDataEntry->data);
    
    	//Mailbox_post(UART_mailbox, packetDataPointer, BIOS_NO_WAIT);
    	/* Do not copy data */
    	//memcpy(&frame[hop_count * PAYLOAD_LENGTH], packetDataPointer, PAYLOAD_LENGTH);
    	/* Copy the timestamp, instead and send it to RPi for analysis */
    	memcpy(&frame[hop_count * PAYLOAD_LENGTH, packetDataPointer + 6, 4);
    	
            RFQueue_nextEntry();
        }
        /* If error or no-receive events, still advance the hop_count */
        if (++hop_count >= NUM_HOPS) /* Full frame, notify to write to uart */
        {
    	hop_count = 0;
        	sem_post(&hop_sem);
        }
    }
    
    
    
    void hop_signal(uint_least8_t index)
    {
    static int cnt = 0;
    
            if (++cnt == 5000)
            {
                    cnt = 0;
                    PIN_setOutputValue(ledPinHandle, CONFIG_PIN_GLED,
                               !PIN_getOutputValue(CONFIG_PIN_GLED));
            }
    
    }
    
    void *mainThread(void *arg0)
    {
        RF_Params rfParams;
        RF_Params_init(&rfParams);
    
        /* Open LED pins */
        ledPinHandle = PIN_open(&ledPinState, pinTable);
        if (ledPinHandle == NULL)
        {
            while(1);
        }
    
    
        sem_init(&hop_sem, 0, 0);
    
        /* UART initialization */
        UART2_Params uartParams;
    
        /* Call driver init functions */
        GPIO_init();
        GPIO_setConfig(CONFIG_GPIO_0, GPIO_CFG_IN_PU | GPIO_CFG_IN_INT_FALLING);
        GPIO_setConfig(CONFIG_GPIO_1, GPIO_CFG_IN_PU | GPIO_CFG_IN_INT_FALLING);
        GPIO_setCallback(CONFIG_GPIO_1, hop_signal);
        GPIO_enableInt(CONFIG_GPIO_1);
    
    
        /* Create a UART with data processing off. */
        UART2_Params_init(&uartParams);
        //uartParams.baudRate = 230400;
        //uartParams.baudRate = 460800;
        uartParams.baudRate = 921600;
        //uartParams.baudRate = 1000000;
    
        uart = UART2_open(CONFIG_UART2_0, &uartParams);
    
    
        if (uart == NULL) {
            /* UART2_open() failed */
            while (1);
        }
    
        //UART_control(uart, UARTCC26XX_CMD_RX_FIFO_FLUSH, NULL);
    
        if( RFQueue_defineQueue(&dataQueue,
                                rxDataEntryBuffer,
                                sizeof(rxDataEntryBuffer),
                                NUM_DATA_ENTRIES,
                                MAX_LENGTH + NUM_APPENDED_BYTES))
        {
            /* Failed to allocate space for all data entries */
            while(1);
        }
    
    
    
    
        /* Modify CMD_PROP_RX command for application needs */
        /* Set the Data Entity queue for received data */
        RF_cmdPropRx.pQueue = &dataQueue;
        /* Discard ignored packets from Rx queue */
        RF_cmdPropRx.rxConf.bAutoFlushIgnored = 1;
        /* Discard packets with CRC error from Rx queue */
        RF_cmdPropRx.rxConf.bAutoFlushCrcErr = 1;
        /* Implement packet length filtering to avoid PROP_ERROR_RXBUF */
        RF_cmdPropRx.maxPktLen = MAX_LENGTH;
        RF_cmdPropRx.maxPktLen = PAYLOAD_LENGTH;
        RF_cmdPropRx.pktConf.bRepeatOk = 1;
        RF_cmdPropRx.pktConf.bRepeatNok = 1;
        RF_cmdPropRx.pktConf.bUseCrc = 0;
        RF_cmdPropRx.pktConf.bVarLen = 0x0;
        RF_cmdPropRx.rxConf.bAppendTimestamp = 1;
    
    
    
        /* Request access to the radio */
    #if defined(DeviceFamily_CC26X0R2)
        rfHandle = RF_open(&rfObject, &RF_prop, (RF_RadioSetup*)&RF_cmdPropRadioSetup, &rfParams);
    #else
        rfHandle = RF_open(&rfObject, &RF_prop, (RF_RadioSetup*)&RF_cmdPropRadioDivSetup, &rfParams);
    #endif// DeviceFamily_CC26X0R2
    
        /* Set the frequency */
        RF_postCmd(rfHandle, (RF_Op*)&RF_cmdFs, RF_PriorityHighest, NULL, 0);
    
    
        memset(frame, 'X', WRITE_SIZE);
        memset(frame, 0, WRITE_SIZE);
        /* Enter RX mode and stay forever in RX */
        RF_EventMask terminationReason = RF_runCmd(rfHandle, (RF_Op*)&RF_cmdPropRx,
                                                   RF_PriorityNormal, &rxCallback,
                                                   RF_EventRxEntryDone | RF_EventRxEmpty | RF_EventRxBufFull | RF_EventRxAborted);
    }

  • Ter:

       I discovered a bug in my interpretation of the timestamp once my packets went over

    to the receiving app on RPi.  After my fix, I am getting much better timestamp data.

    I am going to do a bit more testing and see if this feature will solve my issues.

    Thanks for you help, as always.

    TW

  • Hi Ter:

       I am reading the 4-byte timestamp after the data packet.  However, the timestamp value seems to shift

    8 bits to the right after a while.  Here is some sample output:

    The timestamp corresponds to my transmitted packets' timing.  However, after

    a few hundred values

    0x0851f522
    0x0851fab1
    0x08520044
    0x085205d3
    0x08520b97

    all of a sudden, the value is shifted 8 bits to the right

    0x0008522d
    0x00085216
    0x0008521c
    0x00085221
    0x00085227

    0x000854ff
    0x00085505

    And it happens again and again.


    0x00000855
    0x00000855

    I have configured fixed-length packets of 6 bytes.  Each time after reading the packet data, 

    I read the next 4 bytes for the timestamp.  The first few hundred values are always correct.

    Then the timestamps start shifting at different intervals.

  • I have not been able to see any such shifts. 

    Instead of appending the timestamp to the packet, you can simply read it our from the RF_cmdPropRx.pOutput

    In the rfPacketRX example you can add the following:

    static rfc_propRxOutput_t rxStatistics;

    RF_cmdPropRx.pOutput = (uint8_t*)&rxStatistics;

    I sent 100 packets from SmartRF Studio with a 100 ms interval and stored every timestamp in a buffer:

    Siri

  • Siri:

       Thank you so much.  Using the timestamp in the rfc_propRxOutput_t struct does work better.

    I have the timestamp consistently showing the right time of reception.  As for the rotated 32-bit value

    I experienced before, I suspect that there was an endianness issue with the Raspberry Pi.

    Thanks again,

    TW