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.

UART_Write hangs on a semaphore when adding uartecho code into existing SensorTag app

Other Parts Discussed in Thread: BLE-STACK, CC2650

I observed this latest SensorTag app/RTOS/BLE Stack (TI-RTOS 2.13.0.06  and BLE-Stack 2.1).

Also observed with prior versions. Using latest CCS 6.1.0.00104

Using CC2650 SensorTag with Debug DevPack

My first step was to import the existing uartecho app, and I verified that it worked perfectly

( ... after changed outlined in https://e2e.ti.com/support/wireless_connectivity/f/538/p/441616/1591077#1591077 )

Echo worked perfectly. I took the exact same code, and integrated a new task (task_UART) into the unmodified, working SensorTag application.

Compiles, and runs properly. But when my task_UART starts to execute, it has the following code:

static void uartTaskFxn(UArg a0, UArg a1)
{

    char input;
    UART_Handle uart;
    UART_Params uartParams;
    const char echoPrompt[] = "\fEchoing characters:\r\n";

    /* 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 = 9600;
    uart = UART_open(CC2650_UART0, &uartParams);

    if (uart == NULL) {
        //System_abort("Error opening the UART");
    }

    UART_write(uart, echoPrompt, sizeof(echoPrompt));

    /* Task Loop forever echoing */
    while (1) {
        UART_read(uart, &input, 1);

        UART_write(uart, &input, 1);
        // Put out a second time, so easy to verify on scope
        UART_write(uart, &input, 1);
    }
}

On my PC UART terminal, I see the text string "Echoing characters" come out as expected.

But in UARTCC26XX_write, it waits upon a semaphore after the TX FIFO has been emptied.

 see 1st statement: if (!Semaphore_pend(Semaphore_handle(&(object->writeSem)), object->writeTimeout)) 

The semaphore never gets posted, and my UART_task is suspended at this point.  See UARTCC26XX_write below,

int UARTCC26XX_write(UART_Handle handle, const void *buffer, size_t size)
{
    unsigned int                     key;
    UARTCC26XX_Object                *object;
    UARTCC26XX_HWAttrs const         *hwAttrs;

    /* Get the pointer to the object */
    object = handle->object;
    hwAttrs = handle->hwAttrs;

    /* Check that there is data to write */
    Assert_isTrue(size != 0, NULL);

    /* Disable preemption while checking if the UART is in use. */
    key = Hwi_disable();
    if (object->writeSize) {
        Hwi_restore(key);
        Log_warning1("UART:(%p) Could not write data, uart in use.",
                    ((UARTCC26XX_HWAttrs const *)(handle->hwAttrs))->baseAddr);

        return (UART_ERROR);
    }

    /* Stop the txFifoEmpty clock in case it was running due to a previous write operation */
    Clock_stop((Clock_Handle) &(object->txFifoEmptyClk));

    /* Update the status of the UART module */
    object->status = UART_OK;

    /* Save the data to be written and restore interrupts. */
    object->writeBuf = buffer;
    object->writeCount = 0;

    Hwi_restore(key);

    /* Set constraints to guarantee transaction */
    threadSafeStdbyDisSet();

    /* Enable TX */
    HWREG(UART0_BASE + UART_O_CTL) |= UART_CTL_TXE;

    uint32_t writtenLast = size;
    /* Fill up TX FIFO */
    if (!(object->writeSize = writeData(handle, size))) {
        /* No more data to transmit - Write is finished but all bytes
        *  may not have been shifted out. */
        startTxFifoEmptyClk((UART_Handle)handle, writtenLast);

        /* If writeMode is blocking, block and get the status. */
        if (object->writeMode == UART_MODE_BLOCKING) {
            /* Pend on semaphore and wait for Hwi to finish. */
            if (!Semaphore_pend(Semaphore_handle(&(object->writeSem)), object->writeTimeout)) {
                /* Reset writeSize */
                object->writeSize = 0;

                /* Set status to TIMED_OUT */
                object->status = UART_TIMED_OUT;

                /* Workaround for flushing the TX FIFO */
                writeTxFifoFlush(object, hwAttrs);

                /* Release constraint */
                threadSafeStdbyDisRelease();

                Log_print2(Diags_USER1, "UART:(%p) Write timed out, %d bytes written",
                           ((UARTCC26XX_HWAttrs const *)(handle->hwAttrs))->baseAddr,
                           object->writeCount);

                /* Return UART_ERROR to indicate something went wrong, object->status set to UART_TIMED_OUT*/
                return UART_ERROR;
            }
            return (object->writeCount);
        }
    } else {

        /* Enable TX interrupts */
        UARTIntEnable(hwAttrs->baseAddr, UART_INT_TX);

        /* If writeMode is blocking, block and get the status. */
        if (object->writeMode == UART_MODE_BLOCKING) {
            /* Pend on semaphore and wait for Hwi to finish. */
            if (!Semaphore_pend(Semaphore_handle(&(object->writeSem)), object->writeTimeout)) {
                /* Semaphore timed out, make the write empty and log the write. */

                /* Starting a timer to enable the posting of semaphore used in writeTxFifoFlush.
                 * writtenLast in this case is equal to full TX FIFO. This is a conservative number as
                 * some of the data might have been sent.
                 */
                startTxFifoEmptyClk((UART_Handle)handle, writtenLast);

                /* Reset writeSize */
                object->writeSize = 0;

                /* Set status to TIMED_OUT */
                object->status = UART_TIMED_OUT;

                /* Workaround for flushing the TX FIFO */
                writeTxFifoFlush(object, hwAttrs);

                /* Release constraint */
                threadSafeStdbyDisRelease();

                Log_print2(Diags_USER1, "UART:(%p) Write timed out, %d bytes written",
                           ((UARTCC26XX_HWAttrs const *)(handle->hwAttrs))->baseAddr,
                           object->writeCount);

                /* Return UART_ERROR to indicate something went wrong (object->status set to UART_TIMED_OUT)*/
                return UART_ERROR;
            }

            /* Return the numbers of samples written */
            return (object->writeCount);
        }
    }
    /* This return will only be active in UART_MODE_CALLBACK mode. */
    return (0);
}

On an oscilloscope, I can see characters being received by the CC2650, and as mentioned, the CC2650 has successfully transmitted the  "Echoing characters" string.

Other notes.

  • In project properties Build->ARM Compiler->Advanced Options->Predefined Symbols, I added TI_DRIVERS_UART_INCLUDED   ... but not sure if it is needed
  • In project, I had to manually add the UART drivers alongside the existing I2C/PIN/SPI/UDMA drivers.
  • Note that the original uartecho app does not run with a BLE Stack. Possibly this is a difference that may be of interest
  • I have some suspicion it has something to do with ROM libraries. On my app, I don't think ROM libraries are involved, because I can single-step through the UARTCC26XX.code.
  • On uartecho, I can' t step through the UART code because I believe it is using some ROM libraries.

  • Martin,

    What happens is that the TX FIFO interrupt is enabled ( UARTIntEnable) which causes the UART interrupt to put data into the TX FIFO. Once the interrupt function is done transmitting all bytes it will disable interrupts and call writeFinishedDoCallback which in turns calls writeSemCallback, posting the semaphore.

    The TI RTOS device-specific drivers are only in flash. If you need to step through the source of them, simply add UARTCC26XX.c to your workspace and set breakpoints in it. This will override the one in the pre-compiled driver library linked in. Low-level driverlib calls (such as UARTIntEnable) are in ROM and cannot be debugged without adding in the source version.

    Regards,
    Svend
  • Hi Svend,

    Thank you for your response. I already had UARTCC26XX.c added into my project ( if I didn't, the project wouidn't compile)

    In stepping through the code, I can see it filling the TX FIFO (and of course, see it received on my PC). But I set breakpoints in writeFinishedDoCallback and writeSemCallback, but the code never stops there.

    Again, it is at the first semaphore_pend statement where it hangs.

    if (!Semaphore_pend(Semaphore_handle(&(object->writeSem)), object->writeTimeout)) 

    By default object->writeTimeout is set in the UART library to UART_WAIT_FOREVER

    I even tried setting writeTimeout to 1, but the semaphore never timedout ... definitely weird.

    I started on this UART quest in https://e2e.ti.com/support/wireless_connectivity/f/538/t/441616

    Others have also seen this same issue when trying to use the UART libraries in conjunction with a BLE stack.

    Any help is appreciated !

    Thanks, Martin

  • Hi Svend,

    FYI ...

    I noticed the uartecho_cc26xx.cfg file has the following lines ...

    /* ================ Driver configuration ================ */
    var TIRTOS = xdc.useModule('ti.tirtos.TIRTOS');
    TIRTOS.useUART = true;
    TIRTOS.libType = TIRTOS.LibType_Instrumented;

    whereas the appBLE.cfg does not.,

    I couldn't find any TI documentation explaining this, so not sure what it's impact is.

    Thx, Martin
  • Found little snippet in TI RTOS Users Guide ...

    5.12.1 Static Configuration
    See Section 2.4.2 and Section 5.2.1 for information about configuring your application to use the
    instrumented driver libraries, which are helpful in debugging.
    To enable this driver, add the following statement to your application’s *.cfg file.
    TIRTOS.useUART = true;
  • Martin Sunstrum said:
    Hi Svend,

    FYI ...

    I noticed the uartecho_cc26xx.cfg file has the following lines ...

    /* ================ Driver configuration ================ */
    var TIRTOS = xdc.useModule('ti.tirtos.TIRTOS');
    TIRTOS.useUART = true;
    TIRTOS.libType = TIRTOS.LibType_Instrumented;

    whereas the appBLE.cfg does not.,

    I couldn't find any TI documentation explaining this, so not sure what it's impact is.

    Thx, Martin

    Martin,

    The documentation of the API is found at <tirtos_install_path>/docs/docs_overview.html -> TI-RTOS Drivers Configuration ->package ti.tirtos;

    Basically the uart example will, when built, pull in the needed UART drivers based on this into the kernel library being built.
    The BLE stack examples does this differently and links in a prebuilt driver library in the linker settings (optimized for size) instead of using the config file.

    Edit: The SensorTag example seems to not use the pre-built library found at $TI_RTOS_DRIVERS_BASE$\ti\drivers\lib\drivers_cc26xxware.arm3

    .:svend

  • Martin,

    I just test your UART code above on the SensorTag project. Below is a description of what I did to make it work:

    Versions:

    • Debugger DevPack Rev 1.2.0
    • SensorTag PCB Rev 1.2 (sticker says 1.1.3)
    • BLE stack v2.1 (tested with IAR v7.40.2 and CCS v6.1)

    Steps:

    • Find XDS110 UART port in Device Manager (COM44 in my case)
    • Open BLE stack v2.1 SensorTag project
    Add a new group (IAR) / virtual folder (CCS)  "UART" in the application project
    • Add in UART.c and uart/UARTCC26XX.c to folder
    • Modify main.c with below code
    • Compile and upload stack and application
    • Open RealTerm, verify "Echoing characters" being output
    • Write data through RealTerm, verify corect data is returned.
    #include <ti/sysbios/knl/Task.h>
    #include <ti/drivers/UART.h>
    static char uartTaskStack[512];
    static Task_Struct uartTask;
    
    static void uartTaskFxn(UArg a0, UArg a1)
    {
    
        char input;
        UART_Handle uart;
        UART_Params uartParams;
        const char echoPrompt[] = "\fEchoing characters:\r\n";
    
        /* 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 = 9600;
        uart = UART_open(CC2650_UART0, &uartParams);
    
        if (uart == NULL) {
            //System_abort("Error opening the UART");
        }
    
        UART_write(uart, echoPrompt, sizeof(echoPrompt));
    
        /* Task Loop forever echoing */
        while (1) {
            UART_read(uart, &input, 1);
    
            UART_write(uart, &input, 1);
            // Put out a second time, so easy to verify on scope
            UART_write(uart, &input, 1);
        }
    }
    
    int main()
    {
      PIN_init(BoardGpioInitTable);
    
    #ifndef POWER_SAVING
      /* Set constraints for Standby and Idle mode */
      Power_setConstraint(Power_SB_DISALLOW);
      Power_setConstraint(Power_IDLE_PD_DISALLOW);
    #endif // POWER_SAVING
    
      /* Initialize ICall module */
      ICall_init();
    
      /* Start tasks of external images - Priority 5 */
      ICall_createRemoteTasks();
    
      /* Kick off profile - Priority 3 */
      GAPRole_createTask();
    
      /* Kick off application - Priority 1 */
      SensorTag_createTask();
      SensorTagTmp_createTask();
      SensorTagHum_createTask();
      SensorTagBar_createTask();
    
      Task_Params tp;
      Task_Params_init(&tp);
      tp.stack      = uartTaskStack;
      tp.stackSize  = sizeof(uartTaskStack);
      tp.priority   = 1;
      Task_construct(&uartTask, uartTaskFxn, &tp, NULL);
    
      BIOS_start();     /* enable interrupts and start SYS/BIOS */
    
      return 0;
    }

    -

  • My apologies.
    At some point, I reduced the Task's stack size to 80 bytes instead of your 512.
    Increased to 512, and works like a charm.

    Thanks again for your help Svend !
  • Hi Martin!

    I am using CC2650 LaunchPad and working with the same UART_echo code as you.

    I came across the same problem as you - UART_write(...) hangs on the semaphore line if (!Semaphore_pend(Semaphore_handle(&(object->writeSem)), object->writeTimeout)) in the UARTCC26xx file.

    I tried making the same changes as given above in the reply by Svend, but it does not help me. I even tried increasing stack size to 1024 but it didnt work.

    Can you think of what might be wrong?