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.

CCS/CC1310: Get IQ samples in Continuous RX mode

Part Number: CC1310

Tool/software: Code Composer Studio

Hello,

I'm working on CC1310 Launchpad and follows the rfPacketRx example. I modify the code to get I/Q samples following official tutorial and it works well. Now, I would like to do continuous Rx and get I/Q samples continuously. However, I found that the I/Q sample buffering is handled by RF_EventRxEntryDone Event mask and its corresponding callback function. If I would like to do continuous Rx, I would have to use RF_cmdRxTest Command instead of RF_cmdPropRx Command in RF_runCmd.

Original RF thread code when doing I/Q sample raw data record:

    /* Request access to the radio */
    rfHandle = RF_open(&rfObject, &RF_prop, 
                       (RF_RadioSetup*)&RF_cmdPropRadioDivSetup, &rfParams);

    /* Set the frequency */
    RF_postCmd(rfHandle, (RF_Op*)&RF_cmdFs, RF_PriorityNormal, NULL, 0);

    /* Enter RX mode and stay forever in RX */
    RF_EventMask terminationReason = RF_runCmd(rfHandle, (RF_Op*)&RF_cmdPropRx,
                                               RF_PriorityNormal, &callback,
                                               RF_EventRxEntryDone);

I/Q sample buffer callback:

void callback(RF_Handle h, RF_CmdHandle ch, RF_EventMask e)
{
    if (e & RF_EventRxEntryDone)
    {
        /* Toggle pin to indicate RX */
        PIN_setOutputValue(ledPinHandle, Board_PIN_LED2,
                           !PIN_getOutputValue(Board_PIN_LED2));

        packetDataPointer = &currentReadEntry->rxData;
        //---------------------------------------------------------------------------
        // Implement code for handling the IQ data

        uint16_t i;
        // IQ Sample Handling
        for (i = index; i < (NUMBER_OF_SAMPLE_PAIRS + index); i++)
        {
            iSamples[i] = (((*(packetDataPointer + 1)) << 8) | (*packetDataPointer)) & 0x0FFF;
            qSamples[i] = (((*(packetDataPointer + 2)) << 8) | (*(packetDataPointer + 1))) >> 4;
            packetDataPointer += 3;
        }

                if (index == (NUMBER_OF_SAMPLE_PAIRS*NUMBER_OF_BUFFERS))
        {
            index = 0;
        }
        //---------------------------------------------------------------------------
        currentReadEntry->status = DATA_ENTRY_PENDING;
        currentReadEntry = (rfc_dataEntryPartial_t*)currentReadEntry->pNextEntry;

        index += NUMBER_OF_SAMPLE_PAIRS;

Is it doable to buffer I/Q samples on continuous Rx mode? If so, which event mask should I use? 

Thank you so much! 

  • If you look at the example explained in the "CC13x0 IQ Samples" application report, that example is already doing continuous RX. When using the IQ patch the radio is not looking for sync word and have no knowledge of length fields etc. so the radio will just stay in RX until you tell it to exit (abort the RX command) or until it exit due to for example an overflow (if you are not emptying the data entries fast enough the radio won’t have anywhere to write the samples and RX would exit).

    Siri
  • Hello Siri,

    Thanks for your reply. I would like to send I/Q data through UART to computer and now I want CC1310 to send some characters for benchmark. However, receiver will not work continuously even when rxDataEntryBuffer is not full. I add a counter"testNum" in the call back function and it would stop when the counter reaches 21. 

    The following is my RF thread and callback function which are all modified based on your toutorial.

    void *mainThread(void *arg0)
    {
        RF_Params rfParams;
        RF_Params_init(&rfParams);
    
        // UART Init
        UART_init();
    
        /* Create a UART with data processing off. */
        UART_Params_init(&uartParams);
    //    uartParams.writeDataMode = UART_DATA_BINARY;
        uartParams.baudRate = 115200;
        uart = UART_open(Board_UART0, &uartParams);
    
    
        partialReadEntry1->length = (NUMBER_OF_SAMPLE_PAIRS * 3) + 4;
        partialReadEntry1->config.type = DATA_ENTRY_TYPE_PARTIAL;
        partialReadEntry1->status = DATA_ENTRY_PENDING;
        partialReadEntry2->length = (NUMBER_OF_SAMPLE_PAIRS * 3) + 4;
        partialReadEntry2->config.type = DATA_ENTRY_TYPE_PARTIAL;
        partialReadEntry2->status = DATA_ENTRY_PENDING;
        partialReadEntry1->pNextEntry = (uint8_t*)partialReadEntry2;
        partialReadEntry2->pNextEntry = (uint8_t*)partialReadEntry1;
        dataQueue.pCurrEntry = (uint8_t*)partialReadEntry1;
        dataQueue.pLastEntry = NULL;
    
        /* Open LED pins */
        ledPinHandle = PIN_open(&ledPinState, pinTable);
        if (ledPinHandle == NULL)
        {
            while(1);
        }
    
        /* Set the Data Entity queue for received data */
        RF_cmdPropRx.pQueue = &dataQueue;           
    
        /* Request access to the radio */
        rfHandle = RF_open(&rfObject, &RF_prop, 
                           (RF_RadioSetup*)&RF_cmdPropRadioDivSetup, &rfParams);
    
        /* Set the frequency */
        RF_postCmd(rfHandle, (RF_Op*)&RF_cmdFs, RF_PriorityNormal, NULL, 0);
    
        /* Enter RX mode and stay forever in RX */
        RF_EventMask terminationReason = RF_runCmd(rfHandle, (RF_Op*)&RF_cmdPropRx,
                                                   RF_PriorityNormal, &callback,
                                                   RF_EventRxEntryDone);

    Callback:

    void callback(RF_Handle h, RF_CmdHandle ch, RF_EventMask e)
    {
        if (e & RF_EventRxEntryDone)
        {
            /* Toggle pin to indicate RX */
            PIN_setOutputValue(ledPinHandle, Board_PIN_LED2,
                               !PIN_getOutputValue(Board_PIN_LED2));
    
            UART_write(uart, output, sizeof(output));
            testNum++;
    
            packetDataPointer = &currentReadEntry->rxData;
            //---------------------------------------------------------------------------
            // Implement code for handling the IQ data
            {
                uint16_t i;
                // IQ Sample Handling
                for (i = index; i < (NUMBER_OF_SAMPLE_PAIRS + index); i++)
                {
                    iSamples[i] = (((*(packetDataPointer + 1)) << 8) | (*packetDataPointer)) & 0x0FFF;
                    qSamples[i] = (((*(packetDataPointer + 2)) << 8) | (*(packetDataPointer + 1))) >> 4;
                    packetDataPointer += 3;
                }
            }
            index += NUMBER_OF_SAMPLE_PAIRS;
    
            if (index == (NUMBER_OF_SAMPLE_PAIRS*NUMBER_OF_BUFFERS))
            {
                index = 0;
            }
            //---------------------------------------------------------------------------
            currentReadEntry->status = DATA_ENTRY_PENDING;
            currentReadEntry = (rfc_dataEntryPartial_t*)currentReadEntry->pNextEntry;
    
        }
    }

    Other variables:

    /* Packet RX Configuration */

    #define NUMBER_OF_SAMPLE_PAIRS 600

    #define NUMBER_OF_SAMPLE_PAIRS 8
    #define NUMBER_OF_BUFFERS 5
    static uint16_t iSamples[NUMBER_OF_SAMPLE_PAIRS*NUMBER_OF_BUFFERS];
    static uint16_t qSamples[NUMBER_OF_SAMPLE_PAIRS*NUMBER_OF_BUFFERS];

    char    output[] = "Good!";

    Could you please tell me:

    1. why it would stop when buffer is not full? Acording to you said, it will do contiuous receiving.

    2. I used CoolTerm to receive characters on my desktop. I would like to receive one character array when callback function is called.  However, I could not receive all the data from UART. In most tests I could only receive 4 character arrays("Good!") which I defined above. But when I redebug the code before I excute TROS, it would send the remaining 17 character arrays. Is there something wrong with my UART write function?

    Thank you so much!

  • I am pretty sure that what you are seeing in the data entry overflowing due to the time it takes to process the UART in your callback.
    I just tested the code from the app note, and did not do anything other that blinking a LED in the callback. It toggled "forever". What you can do is to run your code /with the debugger attached) until your LED stops toggling. You should then "pause" your code and check the status of the RF_cmdPropRx command. It will tell you why RX has stopped.
    Please note that if you "pause" the code from the debugger when the radio is still in RX (LED toggling), RX will be terminated due to overflow. This is because you are only halting the M3 and not the radio core.

    Siri
  • Hello Siri,

    Thank you for your kind reply. I comented all the UART parts but still couldn't get continuous receiving. I have faced the following issues:

    1. I put a breakpoint inside the callback function.(at "if (index == (NUMBER_OF_SAMPLE_PAIRS*NUMBER_OF_BUFFERS))") and I started debug, and the code stops there. "terminationReason" was not found which means, rx is still working. So I just resumed the code. And at the second time it stopped at the breakpoint, it was still undefined. But after I resume, code didn't excute to the breakpoint and I paused it. I saw it stuck in the last "while(1)" in the main thread. "terminationReason" was 2.  I removed the breakpoint and tried manual pausing the code. It had the exact same problem. Rx only terminate after your second pause!  I also tried different excuting time which shows the same results. It seems like it doesn't relate to your excuting time but your pause times. Could you please tell me what would the issue happen? 

    2. I add my UART write function in the callback and did the same pasu test. But I didn't see that issue happen and "terminationReason" was not found.

    However, UART was suspended and I got the following issues:

    I check the code and buffer defination, but I didn't find out which part could cause overflow. Could you please tell why it shows I could not find a source file?

    Is there a way to solve these two issues?

    Thank you so much!!

  • You cannot set breakpoints in the code while running RX. When stopping at the breakpoints the M3 (MCU) will be halted but the radio core will be active. It will very fast fill up your buffers. Since the MCU is halted, you are not reading the buffers, so the data entries will overflow and you will exit RX. If you run the code from my app note (do not do any UART processing in the callback, and do not set any breakpoints) you will see that the LED are toggling continuously, meaning that you are in RX all the time. If you want to print you result you must do this after exiting RX. BElow are som ecode I have been used for testing. Please note that the radio stays in RX until a button is pushed. After that 1500 IQ samples pair is printed over the UART. The code will not enter RX again

    void *mainThread(void *arg0)
    {
        RF_Params rfParams;
        RF_Params_init(&rfParams);
    
        UART_Handle uart;
        UART_Params uartParams;
    
        UART_init();
    
        /* Create a UART with data processing off. */
        UART_Params_init(&uartParams);
        uartParams.writeDataMode = UART_DATA_BINARY;
        uartParams.readDataMode = UART_DATA_BINARY;
        uartParams.readReturnMode = UART_RETURN_FULL;
        uartParams.readEcho = UART_ECHO_OFF;
        uartParams.baudRate = 115200;
    
        uart = UART_open(Board_UART0, &uartParams);
    
        if (uart == NULL) {
            while (1);
        }
    
        /* Initialize print semaphore */
        Semaphore_construct(&printSemaphore, 0, NULL);
        printSemaphoreHandle = Semaphore_handle(&printSemaphore);
    
        partialReadEntry1->length = (NUMBER_OF_SAMPLE_PAIRS * 3) + 4;
        partialReadEntry1->config.type = DATA_ENTRY_TYPE_PARTIAL;
        partialReadEntry1->status = DATA_ENTRY_PENDING;
        partialReadEntry2->length = (NUMBER_OF_SAMPLE_PAIRS * 3) + 4;
        partialReadEntry2->config.type = DATA_ENTRY_TYPE_PARTIAL;
        partialReadEntry2->status = DATA_ENTRY_PENDING;
        partialReadEntry1->pNextEntry = (uint8_t*)partialReadEntry2;
        partialReadEntry2->pNextEntry = (uint8_t*)partialReadEntry1;
        dataQueue.pCurrEntry = (uint8_t*)partialReadEntry1;
        dataQueue.pLastEntry = NULL;
    
        /* Open LED pins */
        ledPinHandle = PIN_open(&ledPinState, pinTable);
        if (ledPinHandle == NULL)
        {
            while(1);
        }
    
        /* Open Button pins */
        buttonPinHandle = PIN_open(&buttonPinState, buttonPinTable);
        Assert_isTrue(buttonPinHandle != NULL, NULL);
    
        /* Setup callback for button pins */
        PIN_Status status = PIN_registerIntCb(buttonPinHandle, &buttonCallbackFunction);
        Assert_isTrue((status == PIN_SUCCESS), NULL);
    
        /* Set the Data Entity queue for received data */
        RF_cmdPropRx.pQueue = &dataQueue;           
    
        /* Request access to the radio */
        rfHandle = RF_open(&rfObject, &RF_prop, 
                           (RF_RadioSetup*)&RF_cmdPropRadioDivSetup, &rfParams);
    
        /* Set the frequency */
        RF_postCmd(rfHandle, (RF_Op*)&RF_cmdFs, RF_PriorityNormal, NULL, 0);
    
        /* Enter RX mode and stay forever in RX */
        RF_CmdHandle rxHandle = RF_postCmd(rfHandle, (RF_Op*)&RF_cmdPropRx,
                                                   RF_PriorityNormal, &callback,
                                                   RF_EventRxEntryDone);
    
        /* Wait for a button press */
       Semaphore_pend(printSemaphoreHandle, BIOS_WAIT_FOREVER);
    
       RF_cancelCmd(rfHandle, rxHandle, 0); // abort abruptly
    
       for (j = index; j < (NUMBER_OF_SAMPLE_PAIRS * NUMBER_OF_BUFFERS); j++)
       {
            intToAscii(iSamples[j]);
            UART_write(uart, asciiString, 5);
            UART_write(uart, " ", 1);
    
            intToAscii(qSamples[j]);
            UART_write(uart, asciiString, 5);
            UART_write(uart, "\n\r", 2);
        }
       for (j = 0; j < index; j++)
       {
           intToAscii(iSamples[j]);
           UART_write(uart, asciiString, 5);
           UART_write(uart, " ", 1);
    
           intToAscii(qSamples[j]);
           UART_write(uart, asciiString, 5);
           UART_write(uart, "\n\r", 2);
       }
        while(1);
    }
    
    
    void callback(RF_Handle h, RF_CmdHandle ch, RF_EventMask e)
    {
        if (e & RF_EventRxEntryDone)
        {
            // Toggle pin to indicate RX
            PIN_setOutputValue(ledPinHandle, Board_PIN_LED2,!PIN_getOutputValue(Board_PIN_LED2));
    
            // Get a pointer to the first IQ sample byte
            packetDataPointer = &currentReadEntry->rxData;
    
            //---------------------------------------------------------------------------
            // Implement code for handling the IQ data
            // In this example, I and Q data are simply copied into two separate array
            {
                uint16_t i;
    
                // IQ Sample Handling
                for (i = index; i < (NUMBER_OF_SAMPLE_PAIRS + index); i++)
                {
                    iSamples[i] = (((*(packetDataPointer + 1)) << 8) | (*packetDataPointer)) & 0x0FFF;
                    qSamples[i] = (((*(packetDataPointer + 2)) << 8) | (*(packetDataPointer + 1))) >> 4;
                    packetDataPointer += 3;
                }
            }
            index += NUMBER_OF_SAMPLE_PAIRS;
            if (index == (NUMBER_OF_SAMPLE_PAIRS*NUMBER_OF_BUFFERS))
            {
                index = 0;
    
            }
            currentReadEntry->status = DATA_ENTRY_PENDING;
            currentReadEntry = (rfc_dataEntryPartial_t*)currentReadEntry->pNextEntry;
        }
    }
    

    Siri

  • Hello Siri,

    Thanks for sending me your code. I have tried that before and it worked well. But that is not what I need. I would like to build a contious RX and send IQ data to other master device for further analysis. I think I have somehow achieved that goal by decreasing receiver symbol rate, decreasing UART packet length and increasing buffers's sizes. I did several tests and my LED could blink for couple minute without any issue. 

    I have two more questions and after answering these, I think this ticket could end. 

    1. Is the symbol rate corresponding to the receiver ADC sampling rate? As there is no modulation, symbol rate should be data rate. If so, how could I back calculate ADC sample rate by using symbol rate? I/Q samples are all 12bits. Should I calculate by doing  Symbol rate / 12 or Symbol rate / 24?

    2. I guess, my solution is not the optimal or correct way to use the I/Q patch. Is there any other way to send I/Q data to other device when RX is doing continuous receiving. I don't want add any hardware interrupt or terminate RX just like what you did in your code.

    Thank you so much!