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.

CC2642R: UART2 receive interrupt triger has a excessive delay

Part Number: CC2642R
Other Parts Discussed in Thread: LAUNCHXL-CC26X2R1, SYSCONFIG

Hi Zhang,

Currently, we need to use the CC2642 UART to simulate a LIN bus for data transmission. During testing, we found that there is an excessive delay in UART data reception. The details are as follows:

Test demo:
SDK example: examples\rtos\CC26X2R1_LAUNCHXL\drivers\uart2callback

UART configuration:
Baud rate: 19200 bps

Test procedure:
The PC sends one byte at a time. After the CC2642 receives the byte, it immediately transmits one byte back (echo: receive what is sent).

Test result:
For data sent from the PC, the CC2642 takes more than 2 ms to receive and retransmit the byte. When the baud rate is increased to 115200 bps, the delay is reduced to approximately 400 µs. As shown in the figure below (yellow waveform: data sent by PC; pink waveform: data returned by CC2642).

In addition, switching to blocking mode reception based on this demo produces the same result.

Requirement:
A 2 ms reception delay at 19200 bps is unacceptable for LIN simulation.This issue may be related to the UART idle interrupt. Is there any way to shorten the UART receive interrupt trigger latency?

  • Hi runfa,

    This is an expected behavior of the UART2 TI Driver which relies solely on the RT (read timeout) interrupt to process read events.  The best description of interrupts is provide in Section 21.4.6 of the TRM, but here are the two you are most interested in:

    • RX: The receive interrupt changes state when one of the following events occurs: – If the FIFOs are enabled and the receive FIFO reaches the programmed trigger level. When this happens, the receive interrupt is asserted high. The receive interrupt is cleared by reading data from the receive FIFO until it becomes less than the trigger level, or by clearing the interrupt. – If the FIFOs are disabled (have a depth of one location) and data is received, thereby filling the location, the receive interrupt is asserted high. The receive interrupt is cleared by performing a single read of the receive FIFO, or by clearing the interrupt.
    • RX time-out: The receive time-out interrupt is asserted when the receive FIFO is not empty, and no more data is received during a 32-bit period. The receive time-out interrupt is cleared either when the FIFO becomes empty through reading all the data (or by reading the holding register), or when 1 is written to the corresponding bit of the Interrupt Clear Register (UART:ICR).

    Essentially, the UART2 driver will currently use the RX time-out to wait 32-bit periods after data is received to time-out with FIFO storage.  This is accounting for the additional 1.67 ms after the initial byte is received, 1/19200 = 52.08 us * 32 = 1.67 ms.

    By copying the UART2CC26X2.c file directly into my project workspace, disabling the FIFO from enabling (i.e. removed HWREG(hwAttrs->baseAddr + UART_O_LCRH) |= UART_LCRH_FEN; from UART2CC26X2_initHw), and swapping UART_INT_RT usage for UART_INT_RS, I am able to observe minimal latency

    I've provided my sample modifications below.  Of course this is a preliminary demonstration and more development efforts are required to fully test and validate this solution for your needs.  For example, I am concerned that if bytes are coming in consecutively then there is no FIFO existing to store bytes that may be missed while reading/processing the previous byte.  You really should consider the possibility of FIFO thresholds for your design, if not using read time-out interrupts.

    /*
     * Copyright (c) 2019-2024, 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.
     */
    
    /*
     *  ======== UART2CC26X2.c ========
     */
    
    #include <stdint.h>
    #include <stdbool.h>
    #include <string.h>
    
    #include <ti/drivers/dpl/ClockP.h>
    #include <ti/drivers/dpl/HwiP.h>
    #include <ti/drivers/dpl/SemaphoreP.h>
    
    #include <ti/drivers/GPIO.h>
    #include <ti/drivers/Power.h>
    #include <ti/drivers/power/PowerCC26X2.h>
    #include <ti/drivers/dma/UDMACC26XX.h>
    
    #include <ti/drivers/uart2/UART2CC26X2.h>
    #include <ti/drivers/uart2/UART2Support.h>
    
    #include <ti/log/Log.h>
    
    /* driverlib header files */
    #include <ti/devices/DeviceFamily.h>
    #include DeviceFamily_constructPath(inc/hw_memmap.h)
    #include DeviceFamily_constructPath(inc/hw_ints.h)
    #include DeviceFamily_constructPath(inc/hw_types.h)
    #include DeviceFamily_constructPath(driverlib/uart.h)
    #include DeviceFamily_constructPath(driverlib/sys_ctrl.h)
    #include DeviceFamily_constructPath(driverlib/flash.h)
    
    /* Headers required for intrinsics */
    #if defined(__TI_COMPILER_VERSION__)
        #include <arm_acle.h>
    #elif defined(__GNUC__)
        #include <arm_acle.h>
    #elif defined(__IAR_SYSTEMS_ICC__)
        #include <intrinsics.h>
    #else
        #error "Unsupported compiler"
    #endif
    
    /* Macro for using the definition of the UART2 Log module */
    Log_MODULE_USE(LogModule_UART2);
    
    /* Options for DMA write and read */
    #define TX_CONTROL_OPTS (UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE | UDMA_ARB_4 | UDMA_MODE_BASIC)
    #define RX_CONTROL_OPTS (UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 | UDMA_ARB_4 | UDMA_MODE_BASIC)
    
    /* Initial invalid address of internal write buffer. Set intentionally outside
     * of flash region to ensure power constraints are set correctly
     */
    #define INVALID_ADDR_NOT_IN_FLASH ((const unsigned char *)0xFFFFFFFF)
    
    /* Maximum number of bytes that DMA can transfer */
    #define MAX_DMA_SIZE (UDMA_XFER_SIZE_MAX)
    
    /* Static functions */
    static void UART2CC26X2_eventCallback(UART2_Handle handle, uint32_t event, uint32_t data, void *userArg);
    static void UART2CC26X2_hwiIntFxn(uintptr_t arg);
    static void UART2CC26X2_initHw(UART2_Handle handle);
    static void UART2CC26X2_initIO(UART2_Handle handle);
    static void UART2CC26X2_finalizeIO(UART2_Handle handle);
    static int UART2CC26X2_postNotifyFxn(unsigned int eventType, uintptr_t eventArg, uintptr_t clientArg);
    static void UART2CC26X2_hwiIntWrite(uintptr_t arg);
    static void UART2CC26X2_hwiIntRead(uintptr_t arg, uint32_t status);
    
    /* Map UART2 data length to driverlib data length */
    static const uint8_t dataLength[] = {
        UART_CONFIG_WLEN_5, /* UART2_DataLen_5 */
        UART_CONFIG_WLEN_6, /* UART2_DataLen_6 */
        UART_CONFIG_WLEN_7, /* UART2_DataLen_7 */
        UART_CONFIG_WLEN_8  /* UART2_DataLen_8 */
    };
    
    /* Map UART2 stop bits to driverlib stop bits */
    static const uint8_t stopBits[] = {
        UART_CONFIG_STOP_ONE, /* UART2_StopBits_1 */
        UART_CONFIG_STOP_TWO  /* UART2_StopBits_2 */
    };
    
    /* Map UART2 parity type to driverlib parity type */
    static const uint8_t parityType[] = {
        UART_CONFIG_PAR_NONE, /* UART2_Parity_NONE */
        UART_CONFIG_PAR_EVEN, /* UART2_Parity_EVEN */
        UART_CONFIG_PAR_ODD,  /* UART2_Parity_ODD */
        UART_CONFIG_PAR_ZERO, /* UART2_Parity_ZERO */
        UART_CONFIG_PAR_ONE   /* UART2_Parity_ONE */
    };
    
    /*
     *  Map UART2CC26X2 FIFO threshold types to driverlib TX FIFO threshold
     *  defines.
     */
    static const uint8_t txFifoThreshold[5] = {
        UART_FIFO_TX1_8, /* UART2CC26X2_FIFO_THRESHOLD_1_8     */
        UART_FIFO_TX2_8, /* UART2CC26X2_FIFO_THRESHOLD_2_8     */
        UART_FIFO_TX4_8, /* UART2CC26X2_FIFO_THRESHOLD_4_8     */
        UART_FIFO_TX6_8, /* UART2CC26X2_FIFO_THRESHOLD_6_8     */
        UART_FIFO_TX7_8  /* UART2CC26X2_FIFO_THRESHOLD_7_8     */
    };
    
    /*
     *  Map UART2CC26X2 FIFO threshold types to driverlib RX FIFO threshold
     *  defines.
     */
    static const uint8_t rxFifoThreshold[5] = {
        UART_FIFO_RX1_8, /* UART2CC26X2_FIFO_THRESHOLD_1_8     */
        UART_FIFO_RX2_8, /* UART2CC26X2_FIFO_THRESHOLD_2_8     */
        UART_FIFO_RX4_8, /* UART2CC26X2_FIFO_THRESHOLD_4_8     */
        UART_FIFO_RX6_8, /* UART2CC26X2_FIFO_THRESHOLD_6_8     */
        UART_FIFO_RX7_8  /* UART2CC26X2_FIFO_THRESHOLD_7_8     */
    };
    
    /*
     *  ======== uartDmaEnable ========
     *  Atomic version of DriverLib UARTDMAEnable()
     */
    static inline void uartDmaEnable(uint32_t ui32Base, uint32_t ui32DMAFlags)
    {
        uintptr_t key;
    
        key = HwiP_disable();
        /* Set the requested bits in the UART DMA control register. */
        HWREG(ui32Base + UART_O_DMACTL) |= ui32DMAFlags;
    
        HwiP_restore(key);
    }
    
    /*
     *  ======== uartDmaDisable ========
     *  Atomic version of DriverLib UARTDMADisable()
     */
    static inline void uartDmaDisable(uint32_t ui32Base, uint32_t ui32DMAFlags)
    {
        uintptr_t key;
    
        key = HwiP_disable();
        /* Clear the requested bits in the UART DMA control register. */
        HWREG(ui32Base + UART_O_DMACTL) &= ~ui32DMAFlags;
    
        HwiP_restore(key);
    }
    
    /*
     *  ======== uartEnableCTS ========
     *  CTS only version of DriverLib UARTHwFlowControlEnable()
     */
    static inline void uartEnableCTS(uint32_t ui32Base)
    {
        HWREG(ui32Base + UART_O_CTL) |= (UART_CTL_CTSEN);
    }
    
    /*
     *  ======== uartEnableRTS ========
     *  RTS only version of DriverLib UARTHwFlowControlEnable()
     */
    static inline void uartEnableRTS(uint32_t ui32Base)
    {
        HWREG(ui32Base + UART_O_CTL) |= (UART_CTL_RTSEN);
    }
    
    /*
     *  ======== uartDisableCTS ========
     *  CTS only version of DriverLib UARTHwFlowControlDisable()
     */
    static inline void uartDisableCTS(uint32_t ui32Base)
    {
        HWREG(ui32Base + UART_O_CTL) &= ~(UART_CTL_CTSEN);
    }
    
    /*
     *  ======== uartDisableRTS ========
     *  RTS only version of DriverLib UARTHwFlowControlDisable()
     */
    static inline void uartDisableRTS(uint32_t ui32Base)
    {
        HWREG(ui32Base + UART_O_CTL) &= ~(UART_CTL_RTSEN);
    }
    
    /*
     *  ======== UART2CC26X2_getRxData ========
     *  Transfer data from FIFO to ring buffer. Must be called with HWI disabled.
     */
    static inline size_t UART2CC26X2_getRxData(UART2_Handle handle, size_t size)
    {
        UART2CC26X2_Object *object         = handle->object;
        UART2CC26X2_HWAttrs const *hwAttrs = handle->hwAttrs;
        size_t consumed                    = 0;
        uint8_t data;
    
        while (!(HWREG(hwAttrs->baseAddr + UART_O_FR) & UART_FR_RXFE) && size)
        {
            data = HWREG(hwAttrs->baseAddr + UART_O_DR);
            Log_printf(LogModule_UART2, Log_VERBOSE,
                       "UART2CC26X2_getRxData: Write one byte to the ring buffer from the FIFO.");
            RingBuf_put(&object->rxBuffer, data);
            ++consumed;
            --size;
        }
    
        return consumed;
    }
    
    /*
     *  ======== dmaChannelNum ========
     *  Get the channel number from the channel bit mask
     */
    static inline uint32_t dmaChannelNum(uint32_t x)
    {
    #if defined(__TI_COMPILER_VERSION__)
        return ((uint32_t)__clz(__rbit(x)));
    #elif defined(__GNUC__)
        return ((uint32_t)__builtin_ctz(x));
    #elif defined(__IAR_SYSTEMS_ICC__)
        return ((uint32_t)__CLZ(__RBIT(x)));
    #else
        #error "Unsupported compiler"
    #endif
    }
    
    /*
     *  ======== UART2CC26X2_getRxStatus ========
     *  Get the left-most bit set in the RX error status (OE, BE, PE, FE)
     *  read from the RSR register:
     *      bit#   3   2   1   0
     *             OE  BE  PE  FE
     *  e.g., if OE and FE are both set, OE wins.  This will make it easier
     *  to convert an RX error status to a UART2 error code.
     */
    static inline uint32_t UART2CC26X2_getRxStatus(uint32_t bitMask)
    {
    #if defined(__TI_COMPILER_VERSION__)
        return ((uint32_t)(bitMask & (0x80000000 >> __clz(bitMask))));
    #elif defined(__GNUC__)
        return ((uint32_t)(bitMask & (0x80000000 >> __builtin_clz(bitMask))));
    #elif defined(__IAR_SYSTEMS_ICC__)
        return ((uint32_t)(bitMask & (0x80000000 >> __CLZ(bitMask))));
    #else
        #error "Unsupported compiler"
    #endif
    }
    
    /*
     * Function for checking whether flow control is enabled.
     */
    static inline bool UART2CC26X2_isFlowControlEnabled(UART2CC26X2_HWAttrs const *hwAttrs)
    {
        return hwAttrs->flowControl == UART2_FLOWCTRL_HARDWARE;
    }
    
    /*
     *  ======== UART2_close ========
     */
    void UART2_close(UART2_Handle handle)
    {
        UART2CC26X2_Object *object         = handle->object;
        UART2CC26X2_HWAttrs const *hwAttrs = handle->hwAttrs;
    
        /* Disable UART and interrupts. */
        UARTIntDisable(hwAttrs->baseAddr,
                       UART_INT_TX | UART_INT_RX | UART_INT_RT | UART_INT_OE | UART_INT_BE | UART_INT_PE | UART_INT_FE |
                           UART_INT_CTS);
    
        /* Both these functions will only run conditionally on writeInUse and readInUse, respectively */
        UART2_writeCancel(handle);
        UART2_readCancel(handle);
    
        /* Releases Power constraint if rx is enabled. */
        UART2_rxDisable(handle);
    
        /* Disable UART. This function will wait until TX FIFO is empty before
         * shutting down peripheral, otherwise the BUSY-bit will remain set and
         * can cause the peripheral to get stuck.
         */
        UARTDisable(hwAttrs->baseAddr);
        object->state.txEnabled = false;
    
        /* Deallocate pins */
        UART2CC26X2_finalizeIO(handle);
    
        HwiP_destruct(&(object->hwi));
        SemaphoreP_destruct(&(object->writeSem));
        SemaphoreP_destruct(&(object->readSem));
    
        if (object->udmaHandle)
        {
            UDMACC26XX_close(object->udmaHandle);
        }
    
        /* Unregister power notification objects */
        Power_unregisterNotify(&object->postNotify);
    
        /* Release power dependency - i.e. potentially power down serial domain. */
        Power_releaseDependency(hwAttrs->powerId);
    
        object->state.opened = false;
    }
    
    /*
     *  ======== UART2_flushRx ========
     */
    void UART2_flushRx(UART2_Handle handle)
    {
        UART2CC26X2_Object *object         = handle->object;
        UART2CC26X2_HWAttrs const *hwAttrs = handle->hwAttrs;
        uintptr_t key;
    
        key = HwiP_disable();
        UART2Support_dmaStopRx(handle);
        HwiP_restore(key);
    
        RingBuf_flush(&object->rxBuffer);
    
        /* Read RX FIFO until empty */
        while (UARTCharsAvail(hwAttrs->baseAddr))
        {
            UARTCharGetNonBlocking(hwAttrs->baseAddr);
        }
    
        /* Clear any read errors */
        UARTRxErrorClear(hwAttrs->baseAddr);
    
        key = HwiP_disable();
        /* Start new RX transaction */
        UART2Support_dmaStartRx(handle);
        HwiP_restore(key);
    }
    
    /*
     *  ======== UART2_open ========
     */
    UART2_Handle UART2_open(uint_least8_t index, UART2_Params *params)
    {
        UART2_Handle handle = NULL;
        uintptr_t key;
        UART2CC26X2_Object *object;
        UART2CC26X2_HWAttrs const *hwAttrs;
        HwiP_Params hwiParams;
    
        if (index < UART2_count)
        {
            handle  = (UART2_Handle) & (UART2_config[index]);
            hwAttrs = handle->hwAttrs;
            object  = handle->object;
        }
        else
        {
            return NULL;
        }
    
        /* Check for callback when in UART2_Mode_CALLBACK */
        if (((params->readMode == UART2_Mode_CALLBACK) && (params->readCallback == NULL)) ||
            ((params->writeMode == UART2_Mode_CALLBACK) && (params->writeCallback == NULL)))
        {
            return NULL;
        }
    
        key = HwiP_disable();
    
        if (object->state.opened)
        {
            HwiP_restore(key);
            return NULL;
        }
        object->state.opened = true;
    
        HwiP_restore(key);
    
        object->state.rxEnabled       = false;
        object->state.txEnabled       = false;
        object->state.rxCancelled     = false;
        object->state.txCancelled     = false;
        object->state.overrunActive   = false;
        object->state.inReadCallback  = false;
        object->state.inWriteCallback = false;
        object->state.overrunCount    = 0;
        object->state.readMode        = params->readMode;
        object->state.writeMode       = params->writeMode;
        object->state.readReturnMode  = params->readReturnMode;
        object->readCallback          = params->readCallback;
        object->writeCallback         = params->writeCallback;
        object->eventCallback         = params->eventCallback;
        object->eventMask             = params->eventMask;
        object->baudRate              = params->baudRate;
        object->stopBits              = params->stopBits;
        object->dataLength            = params->dataLength;
        object->parityType            = params->parityType;
        object->userArg               = params->userArg;
    
        /* Set UART transaction variables to defaults. */
        object->writeBuf   = INVALID_ADDR_NOT_IN_FLASH;
        object->readBuf    = NULL;
        object->writeCount = 0;
        object->readCount  = 0;
        object->writeSize  = 0;
        object->readSize   = 0;
        object->rxStatus   = 0;
        object->txStatus   = 0;
        object->rxSize     = 0;
        object->txSize     = 0;
        object->readInUse  = false;
        object->writeInUse = false;
        object->udmaHandle = NULL;
    
        /* Set the event mask to 0 if the callback is NULL to simplify checks */
        if (object->eventCallback == NULL)
        {
            object->eventCallback = UART2CC26X2_eventCallback;
            object->eventMask     = 0;
        }
    
        /* Register power dependency - i.e. power up and enable clock for UART. */
        Power_setDependency(hwAttrs->powerId);
    
        RingBuf_construct(&object->rxBuffer, hwAttrs->rxBufPtr, hwAttrs->rxBufSize);
        RingBuf_construct(&object->txBuffer, hwAttrs->txBufPtr, hwAttrs->txBufSize);
    
        UARTDisable(hwAttrs->baseAddr);
    
        /* Configure IOs */
        UART2CC26X2_initIO(handle);
    
        /* Always succeeds */
        object->udmaHandle = UDMACC26XX_open();
    
        /* Initialize the UART hardware module. Enable the UART and FIFO, but do not enable RX or TX yet. */
        UART2CC26X2_initHw(handle);
    
        /* Register notification function */
        Power_registerNotify(&object->postNotify, PowerCC26XX_AWAKE_STANDBY, UART2CC26X2_postNotifyFxn, (uintptr_t)handle);
    
        /* Create Hwi object for this UART peripheral. */
        HwiP_Params_init(&hwiParams);
        hwiParams.arg      = (uintptr_t)handle;
        hwiParams.priority = hwAttrs->intPriority;
        HwiP_construct(&(object->hwi), hwAttrs->intNum, UART2CC26X2_hwiIntFxn, &hwiParams);
    
        SemaphoreP_constructBinary(&(object->readSem), 0);
        SemaphoreP_constructBinary(&(object->writeSem), 0);
    
        /* Set rx src and tx dst addresses in open, since these won't change */
        hwAttrs->dmaRxTableEntryPri->pvSrcEndAddr = (void *)(hwAttrs->baseAddr + UART_O_DR);
        hwAttrs->dmaTxTableEntryPri->pvDstEndAddr = (void *)(hwAttrs->baseAddr + UART_O_DR);
    
        /* UART opened successfully */
        return handle;
    }
    
    /*
     *  ======== UART2Support_disableRx ========
     */
    void UART2Support_disableRx(UART2_HWAttrs const *hwAttrs)
    {
        UARTIntDisable(hwAttrs->baseAddr,
                       UART_INT_OE | UART_INT_BE | UART_INT_PE | UART_INT_FE | UART_INT_RT | UART_INT_RX);
        UARTIntClear(hwAttrs->baseAddr, UART_INT_OE | UART_INT_BE | UART_INT_PE | UART_INT_FE | UART_INT_RT | UART_INT_RX);
        HWREG(hwAttrs->baseAddr + UART_O_CTL) &= ~UART_CTL_RXE;
    }
    
    /*
     *  ======== UART2Support_disableTx ========
     */
    void UART2Support_disableTx(UART2_HWAttrs const *hwAttrs)
    {
        HWREG(hwAttrs->baseAddr + UART_O_CTL) &= ~(UART_CTL_TXE);
    }
    
    /*
     *  ======== UART2Support_dmaStartRx ========
     *  For mutual exclusion, must be called with HWI disabled.
     */
    void UART2Support_dmaStartRx(UART2_Handle handle)
    {
        UART2CC26X2_Object *object            = handle->object;
        UART2CC26X2_HWAttrs const *hwAttrs    = handle->hwAttrs;
        UDMACC26XX_HWAttrs const *hwAttrsUdma = object->udmaHandle->hwAttrs;
        volatile tDMAControlTable *rxDmaEntry = hwAttrs->dmaRxTableEntryPri;
        unsigned char *dstAddr;
    
        if (object->state.rxEnabled == false)
        {
            /* DMA RX not enabled. No need to return a status code, this function shold never be called if RX is disabled */
            return;
        }
    
        /* Stop DMA RX which updates the ring buffer count */
        UART2Support_dmaStopRx(handle);
    
        /* Decide whether to either read from the FIFO into the ring buffer, into the user-buffer, or do nothing */
        if ((object->rxSize == 0 && (object->readBuf == NULL || object->readCount == 0)) || /* a) */
            (RingBuf_getCount(&object->rxBuffer) >= object->readCount) ||                   /* b) */
            (object->state.readMode == UART2_Mode_NONBLOCKING))                             /* c) */
        {
            /* Setup a DMA transaction from FIFO to ring buffer if either
             * a) Currently no active DMA RX transaction, and (no user-buffer available or no more bytes left to read)
             * b) There are enough bytes in the ring buffer for this current read operation
             * c) The driver is setup for a nonblocking read
             */
            object->rxSize              = RingBuf_putPointer(&object->rxBuffer, &dstAddr);
            object->state.readToRingbuf = true;
        }
        else if (object->rxSize > 0 && (object->readBuf == NULL || object->readCount == 0))
        {
            /* If there is an active DMA transaction into the ring buffer, and there is no user-buffer available,
             * then keep doing that transaction. Do nothing further here
             */
            return;
        }
        else if (object->readBuf != NULL)
        {
            /* If there is a user-buffer available, and neither of the cases above are true, then the driver should setup
             * a DMA transaction into the the user-buffer. Offset the transaction with the number of bytes we already have
             * in the ring buffer. The bytes in the ring buffer will be copied out at a later stage, in UART2_read
             */
            UART2Support_dmaStopRx(handle);
    
            object->rxSize              = object->readCount - RingBuf_getCount(&object->rxBuffer);
            dstAddr                     = object->readBuf + object->bytesRead + RingBuf_getCount(&object->rxBuffer);
            object->state.readToRingbuf = false;
        }
        else
        {
            /* It should not be possible to reach here */
            return;
        }
    
        /* Setup and start a DMA RX transaction, if the rxSize was set above */
        if (object->rxSize > 0)
        {
            object->state.overrunActive = false;
    
            /* Limit the transaction to the maximum DMA transfer size */
            if (object->rxSize > MAX_DMA_SIZE)
            {
                object->rxSize = MAX_DMA_SIZE;
            }
    
            Log_printf(LogModule_UART2,
                       Log_VERBOSE,
                       "UART2Support_dmaStartRx: Starting DMA transfer of %d byte(s) to address 0x%x",
                       object->rxSize,
                       dstAddr);
    
            /* Setup DMA control-structure and destination */
            rxDmaEntry->pvDstEndAddr = dstAddr + object->rxSize - 1;
            rxDmaEntry->ui32Control  = RX_CONTROL_OPTS;
    
            /* Set the size in the control options */
            rxDmaEntry->ui32Control |= UDMACC26XX_SET_TRANSFER_SIZE(object->rxSize);
    
            /* enable burst mode */
            HWREG(hwAttrsUdma->baseAddr + UDMA_O_SETBURST) = 1 << dmaChannelNum(hwAttrs->rxChannelMask);
            UDMACC26XX_channelEnable(object->udmaHandle, hwAttrs->rxChannelMask);
    
            uartDmaEnable(hwAttrs->baseAddr, UART_DMA_RX);
        }
    }
    
    /*
     *  ======== UART2Support_dmaStartTx ========
     *  For mutual exclusion, must be called with HWI disabled.
     */
    void UART2Support_dmaStartTx(UART2_Handle handle)
    {
        UART2CC26X2_Object *object         = handle->object;
        UART2CC26X2_HWAttrs const *hwAttrs = handle->hwAttrs;
        volatile tDMAControlTable *txDmaEntry;
        unsigned char *srcAddr;
    
        if (object->txSize > 0)
        {
            /* DMA TX already in progress */
            return;
        }
    
        /* If nonblocking, set txSize to available bytes in ring buffer. Set source address to the ring buffer. */
        if (object->state.writeMode == UART2_Mode_NONBLOCKING)
        {
            object->txSize = RingBuf_getPointer(&object->txBuffer, &srcAddr);
        }
        else
        {
            /* Blocking or callback mode */
            object->txSize = object->writeCount;
            srcAddr        = (unsigned char *)object->writeBuf + object->bytesWritten;
        }
    
        if (object->txSize > 0)
        {
            UARTIntDisable(hwAttrs->baseAddr, UART_INT_EOT);
    
            /* Invoke UART2_EVENT_TX_BEGIN callback when first enabling TX */
            if ((object->eventMask & UART2_EVENT_TX_BEGIN) && object->eventCallback && (object->state.txEnabled == false))
            {
    
                Log_printf(LogModule_UART2,
                           Log_INFO,
                           "UART2Support_dmaStartTx: Entering event callback with event = 0x%x and user argument = 0x%x",
                           UART2_EVENT_TX_BEGIN,
                           object->userArg);
    
                object->eventCallback(handle, UART2_EVENT_TX_BEGIN, 0, object->userArg);
            }
    
            if (object->txSize > MAX_DMA_SIZE)
            {
                object->txSize = MAX_DMA_SIZE;
            }
    
            Log_printf(LogModule_UART2,
                       Log_VERBOSE,
                       "UART2Support_dmaStartTx: Starting DMA transfer of %d byte(s) from address 0x%x",
                       object->txSize,
                       srcAddr);
    
            txDmaEntry               = hwAttrs->dmaTxTableEntryPri;
            txDmaEntry->pvSrcEndAddr = srcAddr + object->txSize - 1;
    
            txDmaEntry->ui32Control = TX_CONTROL_OPTS;
    
            /* Set the size in the control options */
            txDmaEntry->ui32Control |= UDMACC26XX_SET_TRANSFER_SIZE(object->txSize);
    
            UDMACC26XX_channelEnable(object->udmaHandle, hwAttrs->txChannelMask);
    
            uartDmaEnable(hwAttrs->baseAddr, UART_DMA_TX);
    
            if (object->state.txEnabled == false)
            {
                /* Set standby constraint to guarantee transaction. Also set flash constraint if necessary */
                UART2Support_powerSetConstraint(handle, true);
                object->state.txEnabled = true;
            }
        }
    }
    
    /*
     *  ======== UART2Support_dmaStopRx ========
     *  For mutual exclusion, must be called with HWI disabled.
     */
    void UART2Support_dmaStopRx(UART2_Handle handle)
    {
        UART2CC26X2_Object *object            = handle->object;
        UART2CC26X2_HWAttrs const *hwAttrs    = handle->hwAttrs;
        UDMACC26XX_HWAttrs const *hwAttrsUdma = object->udmaHandle->hwAttrs;
        uint32_t bytesRemaining;
        uint32_t rxCount;
    
        /* If there is a currently active DMA RX transaction */
        if (object->rxSize > 0)
        {
            /* Disable the RX DMA channel and stop the transfer. Disable and clear interrupts */
            UDMACC26XX_channelDisable(object->udmaHandle, hwAttrs->rxChannelMask);
            uartDmaDisable(hwAttrs->baseAddr, UART_DMA_RX);
            UDMACC26XX_clearInterrupt(object->udmaHandle, hwAttrs->rxChannelMask);
    
            /* Get the number of bytes that remain to be transferred */
            bytesRemaining = uDMAChannelSizeGet(hwAttrsUdma->baseAddr, dmaChannelNum(hwAttrs->rxChannelMask));
            rxCount        = object->rxSize - bytesRemaining;
    
            Log_printf(LogModule_UART2,
                       Log_VERBOSE,
                       "UART2Support_dmaStopRx: Stop DMA transfer. Remaining number of bytes: %d",
                       bytesRemaining);
    
            /* If the driver is currently reading data into the ring buffer, update the ring buffer count with the
             * number of bytes transferred into it through DMA
             */
            if (object->state.readToRingbuf)
            {
                RingBuf_putAdvance(&object->rxBuffer, rxCount);
                Log_printf(LogModule_UART2, Log_VERBOSE,
                           "UART2Support_dmaStopRx: Data read into ring buffer by DMA: %d byte(s)",
                           rxCount);
            }
            else
            {
                /* If the driver was reading data into the user-buffer, we update the number of bytes read,
                 * and number of bytes still to read
                 */
                object->readCount -= rxCount;
                object->bytesRead += rxCount;
            }
    
            /* Set the DMA rxsize to 0 to indicate that there is no active DMA transaction ongoing */
            object->rxSize = 0;
        }
    }
    
    /*
     *  ======== UART2Support_dmaStopTx ========
     *  For mutual exclusion, must be called with HWI disabled.
     */
    uint32_t UART2Support_dmaStopTx(UART2_Handle handle)
    {
        UART2CC26X2_Object *object            = handle->object;
        UART2CC26X2_HWAttrs const *hwAttrs    = handle->hwAttrs;
        UDMACC26XX_HWAttrs const *hwAttrsUdma = object->udmaHandle->hwAttrs;
        uint32_t bytesRemaining               = 0;
        uint32_t txCount;
    
        /* If there is currently an active DMA TX transaction */
        if (object->txSize > 0)
        {
            /* Disable the TX DMA channel and stop the transfer. Disable and clear interrupts */
            UDMACC26XX_channelDisable(object->udmaHandle, hwAttrs->txChannelMask);
            uartDmaDisable(hwAttrs->baseAddr, UART_DMA_TX);
            UDMACC26XX_clearInterrupt(object->udmaHandle, hwAttrs->txChannelMask);
    
            /* Get the number of bytes that remain to be transferred */
            bytesRemaining = uDMAChannelSizeGet(hwAttrsUdma->baseAddr, dmaChannelNum(hwAttrs->txChannelMask));
            txCount        = object->txSize - bytesRemaining;
    
            Log_printf(LogModule_UART2,
                       Log_VERBOSE,
                       "UART2Support_dmaStopTx: Stop DMA transfer. Remaining number of bytes: %d",
                       bytesRemaining);
    
            /* If the driver is currently doing a nonblocking write, update the ring buffer */
            if (object->state.writeMode == UART2_Mode_NONBLOCKING)
            {
                Log_printf(LogModule_UART2, Log_VERBOSE,
                           "UART2Support_dmaStopTx: Attempt to consume %d byte(s) in the ring buffer",
                           txCount);
                RingBuf_getConsume(&object->txBuffer, txCount);
            }
            else
            {
                /* If the driver was writing data from the user-buffer, we update the number of bytes written,
                 * and number of bytes still to write
                 */
                object->bytesWritten += txCount;
                object->writeCount -= txCount;
            }
    
            /* Set the DMA txSize to 0 to indicate that there is no active DMA transaction ongoing */
            object->txSize = 0;
        }
    
        return bytesRemaining;
    }
    
    /*
     *  ======== UART2Support_enableInts ========
     *  Function to enable receive, receive timeout, and error interrupts
     */
    void UART2Support_enableInts(UART2_Handle handle)
    {
        UART2CC26X2_Object *object         = handle->object;
        UART2CC26X2_HWAttrs const *hwAttrs = handle->hwAttrs;
    
        if (object->eventCallback)
        {
            if (object->eventMask & UART2_EVENT_OVERRUN)
            {
                UARTIntClear(hwAttrs->baseAddr, UART_INT_OE);
                UARTIntEnable(hwAttrs->baseAddr, UART_INT_OE);
            }
        }
        UARTIntEnable(hwAttrs->baseAddr, UART_INT_RX);
    }
    
    /*
     *  ======== UART2Support_enableRx ========
     *  Call with interrupts disabled
     */
    void UART2Support_enableRx(UART2_HWAttrs const *hwAttrs)
    {
        /* Enable RX but not interrupts, since we may be using DMA */
        HWREG(hwAttrs->baseAddr + UART_O_CTL) |= UART_CTL_RXE;
    }
    
    /*
     *  ======== UART2Support_enableTx ========
     *  Enable TX - interrupts must be disabled
     */
    void UART2Support_enableTx(UART2_HWAttrs const *hwAttrs)
    {
        HWREG(hwAttrs->baseAddr + UART_O_CTL) |= UART_CTL_TXE;
    }
    
    /*
     *  ======== UART2Support_powerRelConstraint ========
     */
    void UART2Support_powerRelConstraint(UART2_Handle handle, bool relFlashConstraint)
    {
        UART2_Object *object = handle->object;
    
        Power_releaseConstraint(PowerCC26XX_DISALLOW_STANDBY);
    
        /* If most significant byte of writeBuf is 0, it must be in flash */
        if (relFlashConstraint && ((((uint32_t)(object->writeBuf) & 0xF0000000) == 0x0)))
        {
            Power_releaseConstraint(PowerCC26XX_NEED_FLASH_IN_IDLE);
    
            /* Reset writeBuf pointer so it wont be accidentally released again */
            object->writeBuf = INVALID_ADDR_NOT_IN_FLASH;
        }
    }
    
    /*
     *  ======== UART2Support_powerSetConstraint ========
     */
    void UART2Support_powerSetConstraint(UART2_Handle handle, bool setFlashConstraint)
    {
        UART2_Object *object = handle->object;
    
        Power_setConstraint(PowerCC26XX_DISALLOW_STANDBY);
    
        /* If most significant byte of writeBuf is 0, it must be in flash */
        if (setFlashConstraint && ((((uint32_t)(object->writeBuf) & 0xF0000000) == 0x0)))
        {
            Power_setConstraint(PowerCC26XX_NEED_FLASH_IN_IDLE);
        }
    }
    
    /*
     *  ======== UART2Support_rxStatus2ErrorCode ========
     *  Convert RX status (OE, BE, PE, FE) to a UART2 error code.
     */
    int_fast16_t UART2Support_rxStatus2ErrorCode(uint32_t errorData)
    {
        uint32_t status;
    
        status = UART2CC26X2_getRxStatus(errorData);
        return -((int_fast16_t)status);
    }
    
    /*
     *  ======== UART2Support_sendData ========
     *  Function to send data
     */
    uint32_t UART2Support_sendData(UART2_HWAttrs const *hwAttrs, size_t size, uint8_t *buf)
    {
        uint32_t writeCount = 0;
    
        while (size)
        {
            if (!UARTCharPutNonBlocking(hwAttrs->baseAddr, *buf))
            {
                break;
            }
    
            buf++;
            writeCount++;
            size--;
        }
    
        return writeCount;
    }
    
    /*
     *  ======== UART2Support_txDone ========
     *  Used when interrupts are not enabled.
     */
    bool UART2Support_txDone(UART2_HWAttrs const *hwAttrs)
    {
        if (UARTBusy(hwAttrs->baseAddr))
        {
            return false;
        }
    
        return true;
    }
    
    /*
     *  ======== UART2Support_uartRxError ========
     *  Function to clear RX errors
     */
    int UART2Support_uartRxError(UART2_HWAttrs const *hwAttrs)
    {
        int status = UART2_STATUS_SUCCESS;
        uint32_t errStatus;
    
        /* Check for RX error since the last read */
        errStatus = UARTRxErrorGet(hwAttrs->baseAddr);
        status    = UART2Support_rxStatus2ErrorCode(errStatus);
        UARTRxErrorClear(hwAttrs->baseAddr); /* Clear receive errors */
    
        return status;
    }
    
    /*
     *  ======== UART2CC26X2_eventCallback ========
     *  A dummy event callback function in case the user didn't provide one
     */
    static void UART2CC26X2_eventCallback(UART2_Handle handle, uint32_t event, uint32_t data, void *userArg)
    {}
    
    /*
     *  ======== UART2CC26X2_hwiIntRead ========
     *  Function called by Hwi to handle read-related interrupt
     */
    static void UART2CC26X2_hwiIntRead(uintptr_t arg, uint32_t status)
    {
        UART2_Handle handle                = (UART2_Handle)arg;
        UART2CC26X2_HWAttrs const *hwAttrs = handle->hwAttrs;
        UART2CC26X2_Object *object         = handle->object;
        void *readBufCopy;
    
        /* Temporarily stop RX transaction */
        UART2Support_dmaStopRx(handle);
    
        /* Read timeout interrupt */
        if (status & UART_INT_RX)
        {
            /* If the read transaction has a user buffer as destination,
             * copy as much as we can from the FIFO to the buffer
             */
            if (!(object->state.readToRingbuf))
            {
                while (UARTCharsAvail(hwAttrs->baseAddr) && object->readCount)
                {
                    uint8_t data                           = HWREG(hwAttrs->baseAddr + UART_O_DR);
                    *(object->readBuf + object->bytesRead) = data;
                    object->bytesRead++;
                    object->readCount--;
                }
            }
    
            /* If the read transaction is setup with the ring buffer as destination then copy as much
             * as we can can from the FIFO into the ring buffer
             */
            if (object->state.readToRingbuf)
            {
                UART2CC26X2_getRxData(handle, RingBuf_space(&object->rxBuffer));
            }
        }
    
        /* Do not invoke callback or post semaphore if there is no active read, or readMode is nonblocking */
        if (object->readInUse && object->state.readMode != UART2_Mode_NONBLOCKING)
        {
            /* Read-transaction is complete if either
             * a) ReadReturnMode is partial and we received a read-timeout
             * b) There are no more bytes to read
             */
            if (((object->state.readReturnMode == UART2_ReadReturnMode_PARTIAL) && (status & UART_INT_RX)) ||
                (object->readCount == 0))
            {
                object->readInUse = false;
                object->readCount = 0;
                /* Set readBuf to NULL, but first make a copy to pass to the callback. We cannot set it to NULL after
                 * the callback in case another read was issued from the callback.
                 */
                readBufCopy       = object->readBuf;
                object->readBuf   = NULL;
    
                if (object->state.readMode == UART2_Mode_CALLBACK)
                {
                    Log_printf(LogModule_UART2,
                               Log_INFO,
                               "UART2CC26X2_hwiIntRead: Entering read callback. Function pointer = 0x%x",
                               object->readCallback);
    
                    object->readCallback(handle, readBufCopy, object->bytesRead, object->userArg, UART2_STATUS_SUCCESS);
                }
                else
                {
                    /* Blocking mode. Post semaphore to unblock reading task */
                    SemaphoreP_post(&object->readSem);
                }
            }
        }
    
        /* Start another RX transaction */
        UART2Support_dmaStartRx(handle);
    }
    
    /*
     *  ======== UART2CC26X2_hwiIntWrite ========
     *  Function called by Hwi to handle write-related interrupt
     */
    static void UART2CC26X2_hwiIntWrite(uintptr_t arg)
    {
        UART2_Handle handle                = (UART2_Handle)arg;
        UART2CC26X2_HWAttrs const *hwAttrs = handle->hwAttrs;
        UART2CC26X2_Object *object         = handle->object;
    
        /* DMA transaction finished. Restart in case there are more bytes to write */
        UART2Support_dmaStopTx(handle);
        UART2Support_dmaStartTx(handle);
    
        if ((object->state.writeMode == UART2_Mode_CALLBACK) && (object->writeCount == 0) && object->writeInUse)
        {
            Log_printf(LogModule_UART2,
                       Log_INFO,
                       "UART2CC26X2_hwiIntWrite: Entering write callback. Function pointer = 0x%x",
                       object->writeCallback);
    
            object->writeInUse = false;
            object->writeCallback(handle,
                                  (void *)object->writeBuf,
                                  object->bytesWritten,
                                  object->userArg,
                                  UART2_STATUS_SUCCESS);
        }
    
        if (object->txSize == 0)
        {
            /* No more data pending in the TX buffer, wait for it to finish shifting out of the transmit shift register. */
            UARTIntEnable(hwAttrs->baseAddr, UART_INT_EOT);
        }
    }
    
    /*
     *  ======== UART2CC26X2_hwiIntFxn ========
     *  Hwi function that processes UART interrupts.
     */
    static void UART2CC26X2_hwiIntFxn(uintptr_t arg)
    {
        uint32_t status;
        uint32_t errStatus = 0;
        uint32_t event;
        UART2_Handle handle                = (UART2_Handle)arg;
        UART2CC26X2_HWAttrs const *hwAttrs = handle->hwAttrs;
        UART2CC26X2_Object *object         = handle->object;
    
        /* Clear interrupts */
        status = UARTIntStatus(hwAttrs->baseAddr, true);
        UARTIntClear(hwAttrs->baseAddr, status);
    
        Log_printf(LogModule_UART2,
                   Log_INFO,
                   "UART2CC26X2_hwiIntFxn: Entering ISR. MIS register = 0x%x",
                   status);
    
        if (status & (UART_INT_OE | UART_INT_BE | UART_INT_PE | UART_INT_FE))
        {
            if (status & UART_INT_OE)
            {
                /* Overrun error occurred, get what we can.  No need to stop
                 * the DMA, should already have stopped by now.
                 */
                UART2CC26X2_getRxData(handle, RingBuf_space(&object->rxBuffer));
                /* Throw away the rest in order to clear the overrun */
                while (!(HWREG(hwAttrs->baseAddr + UART_O_FR) & UART_FR_RXFE))
                {
                    volatile uint8_t data = HWREG(hwAttrs->baseAddr + UART_O_DR);
                    (void)data;
                }
                object->state.overrunCount++;
                if (object->state.overrunActive == false)
                {
                    object->state.overrunActive = true;
                }
            }
    
            errStatus = UARTRxErrorGet(hwAttrs->baseAddr);
            event     = UART2CC26X2_getRxStatus(errStatus & object->eventMask);
    
            if (event && object->eventCallback)
            {
                Log_printf(LogModule_UART2,
                           Log_INFO,
                           "UART2CC26X2_hwiIntFxn: Entering event callback with event = 0x%x and user argument = 0x%x",
                           event,
                           object->userArg);
    
                object->eventCallback(handle, event, object->state.overrunCount, object->userArg);
            }
            object->rxStatus = UART2Support_rxStatus2ErrorCode(errStatus);
        }
    
        /* UDMACC26XX_channelDone() checks for rxChannel bit set in REQDONE */
        if (UDMACC26XX_channelDone(object->udmaHandle, hwAttrs->rxChannelMask) || (status & UART_INT_RX))
        {
            UART2CC26X2_hwiIntRead(arg, status);
        }
    
        if (UDMACC26XX_channelDone(object->udmaHandle, hwAttrs->txChannelMask) && (object->txSize > 0))
        {
            UART2CC26X2_hwiIntWrite(arg);
        }
    
        if (status & (UART_INT_EOT))
        {
            /* End of Transmission occurred. Make sure TX FIFO is truly empty before disabling TX */
            while (HWREG(hwAttrs->baseAddr + UART_O_FR) & UART_FR_BUSY) {}
            /* Disable TX */
            HWREG(hwAttrs->baseAddr + UART_O_CTL) &= ~UART_CTL_TXE;
    
            if (object->state.txEnabled)
            {
                object->state.txEnabled = false;
    
                if ((object->eventMask & UART2_EVENT_TX_FINISHED) && object->eventCallback)
                {
                    Log_printf(LogModule_UART2,
                               Log_INFO,
                               "UART2CC26X2_hwiIntFxn: Entering event callback with event = 0x%x and user argument = 0x%x",
                               UART2_EVENT_TX_FINISHED,
                               object->userArg);
    
                    object->eventCallback(handle, UART2_EVENT_TX_FINISHED, 0, object->userArg);
                }
    
                /* Release standby constraint because there are no active transactions. Also release flash constraint */
                UART2Support_powerRelConstraint(handle, true);
            }
    
            UARTIntDisable(hwAttrs->baseAddr, UART_INT_EOT);
    
            if (object->state.writeMode == UART2_Mode_BLOCKING)
            {
                /* Unblock write-function, which is waiting for EOT */
                SemaphoreP_post(&object->writeSem);
            }
        }
    }
    
    /*
     *  ======== UART2CC26X2_initHw ========
     */
    static void UART2CC26X2_initHw(UART2_Handle handle)
    {
        ClockP_FreqHz freq;
        UART2CC26X2_Object *object         = handle->object;
        UART2CC26X2_HWAttrs const *hwAttrs = handle->hwAttrs;
    
        /* Configure frame format and baudrate. UARTConfigSetExpClk() disables
         * the UART and does not re-enable it, so call this function first.
         */
        ClockP_getCpuFreq(&freq);
        UARTConfigSetExpClk(hwAttrs->baseAddr,
                            freq.lo,
                            object->baudRate,
                            dataLength[object->dataLength] | stopBits[object->stopBits] | parityType[object->parityType]);
    
        /* Clear all UART interrupts */
        UARTIntClear(hwAttrs->baseAddr,
                     UART_INT_OE | UART_INT_BE | UART_INT_PE | UART_INT_FE | UART_INT_RT | UART_INT_TX | UART_INT_RX |
                         UART_INT_CTS);
    
        /* Set TX interrupt FIFO level and RX interrupt FIFO level */
        UARTFIFOLevelSet(hwAttrs->baseAddr, txFifoThreshold[hwAttrs->txIntFifoThr], rxFifoThreshold[hwAttrs->rxIntFifoThr]);
    
        /* If Flow Control is enabled, configure hardware flow control for CTS and/or RTS. */
        if (UART2CC26X2_isFlowControlEnabled(hwAttrs) && (hwAttrs->ctsPin != GPIO_INVALID_INDEX))
        {
            uartEnableCTS(hwAttrs->baseAddr);
        }
        else
        {
            uartDisableCTS(hwAttrs->baseAddr);
        }
    
        if (UART2CC26X2_isFlowControlEnabled(hwAttrs) && (hwAttrs->rtsPin != GPIO_INVALID_INDEX))
        {
            uartEnableRTS(hwAttrs->baseAddr);
        }
        else
        {
            uartDisableRTS(hwAttrs->baseAddr);
        }
    
        /* Enable UART FIFOs */
        //HWREG(hwAttrs->baseAddr + UART_O_LCRH) |= UART_LCRH_FEN;
    
        /* Enable the UART module, but not RX or TX */
        HWREG(hwAttrs->baseAddr + UART_O_CTL) |= UART_CTL_UARTEN;
    
        if ((hwAttrs->rxChannelMask > 0) && (hwAttrs->txChannelMask > 0))
        {
            /* Prevent DMA interrupt on UART2CC26X2_open() */
            uartDmaDisable(hwAttrs->baseAddr, UART_DMA_RX);
            UDMACC26XX_channelDisable(object->udmaHandle, hwAttrs->rxChannelMask);
            UDMACC26XX_clearInterrupt(object->udmaHandle, hwAttrs->rxChannelMask);
    
            /* Disable the alternate DMA structure */
            UDMACC26XX_disableAttribute(object->udmaHandle, dmaChannelNum(hwAttrs->rxChannelMask), UDMA_ATTR_ALTSELECT);
            UDMACC26XX_disableAttribute(object->udmaHandle, dmaChannelNum(hwAttrs->txChannelMask), UDMA_ATTR_ALTSELECT);
        }
    }
    
    /*
     *  ======== UART2CC26X2_initIO ========
     */
    static void UART2CC26X2_initIO(UART2_Handle handle)
    {
        UART2CC26X2_HWAttrs const *hwAttrs = handle->hwAttrs;
    
        /* Make sure RX and CTS pins have their input buffers enabled, then apply the correct mux */
        GPIO_setConfigAndMux(hwAttrs->txPin, GPIO_CFG_NO_DIR, hwAttrs->txPinMux);
        GPIO_setConfigAndMux(hwAttrs->rxPin, GPIO_CFG_INPUT, hwAttrs->rxPinMux);
    
        if (UART2CC26X2_isFlowControlEnabled(hwAttrs))
        {
            GPIO_setConfigAndMux(hwAttrs->ctsPin, GPIO_CFG_INPUT, hwAttrs->ctsPinMux);
            GPIO_setConfigAndMux(hwAttrs->rtsPin, GPIO_CFG_NO_DIR, hwAttrs->rtsPinMux);
        }
    }
    
    /*
     *  ======== UART2CC26X2_finalizeIO ========
     */
    static void UART2CC26X2_finalizeIO(UART2_Handle handle)
    {
        UART2CC26X2_HWAttrs const *hwAttrs = handle->hwAttrs;
    
        GPIO_resetConfig(hwAttrs->txPin);
        GPIO_resetConfig(hwAttrs->rxPin);
    
        if (UART2CC26X2_isFlowControlEnabled(hwAttrs))
        {
            GPIO_resetConfig(hwAttrs->ctsPin);
            GPIO_resetConfig(hwAttrs->rtsPin);
        }
    }
    
    /*
     *  ======== UART2CC26X2_postNotifyFxn ========
     *  Called by Power module when waking up from LPDS.
     */
    static int UART2CC26X2_postNotifyFxn(unsigned int eventType, uintptr_t eventArg, uintptr_t clientArg)
    {
        /* Reconfigure the hardware if returning from sleep */
        if (eventType == PowerCC26XX_AWAKE_STANDBY)
        {
            UART2CC26X2_initHw((UART2_Handle)clientArg);
        }
    
        return Power_NOTIFYDONE;
    }
    

    Regards,
    Ryan

  • Hi Ryan,

    Thank you for your response.

    Following your suggestion, I replaced the original UART2CC26X2.c file in the SDK with the modified version you provided, and then ran the demo again (examples\rtos\CC26X2R1_LAUNCHXL\drivers\uart2callback). However, the results show that the latency has not improved.

    Besides UART2CC26X2.c, are there any other files or configurations that need to be modified?

    Looking forward to your reply.
    Best regards.

  • Hi Ryan,

    I would like to provide an additional update.

    I noticed that after modifying the file
    /source/ti/drivers/uart2/UART2CC26X2.c in the SDK, the changes do not take effect during compilation. Even if I delete this file, the project still builds successfully without any impact.

    Furthermore, in the generated map file from CCS, I found that the UART2CC26X2_hwiIntFxn function (defined in UART2CC26X2.c) is actually linked from the following library:

    (/source/ti/drivers/lib/iar/m4f/drivers_cc26x2.a)

    This suggests that the project is using the precompiled driver library instead of the source file.

    In this case, how should I properly integrate your modified UART2CC26X2.c file so that it takes effect in the build?

    Best regards.

  • Hi runfa,

    copying the UART2CC26X2.c file directly into my project workspace

    Appears as such in practice:

    This is the only change I've made to the default project.  I've confirmed that I am using the release SDK and that the .c file I've provided you has the appropriate changes.  I tested on a LAUNCHXL-CC26X2R1 which should be equivalent to the CC2642R in this sense.

    Regards,
    Ryan

  • Hi Ryan,

    Thank you for your reply. After adding UART2CC26X2.c to the project, the modifications to this file take effect as expected.

    In the demo (examples\rtos\CC26X2R1_LAUNCHXL\drivers\uart2callback), the UART reception latency has been significantly reduced. Next, I will try to apply this modification to my design where UART2 is used to simulate a LIN bus.

    By the way, does your company provide any example projects or documentation about using CC2642 UART2 to simulate LIN?

    In addition, I would like to confirm one point with you:

    Taking UART2CC26X2_hwiIntFxn() as an example —

    • If UART2CC26X2.c is not added to the CCS project, the map file shows that the implementation of UART2CC26X2_hwiIntFxn() comes from the precompiled library source/ti/drivers/lib/iar/m4f/drivers_cc26x2.a.

    • After adding UART2CC26X2.c to the CCS project, the map file shows that the implementation of UART2CC26X2_hwiIntFxn() comes directly from UART2CC26X2.c.

    As this is my first time developing on this platform, this configuration mechanism feels somewhat error-prone. Is there any detailed documentation explaining how this driver library and source override mechanism works?

    Best regards.

  • SimpleLink Connectivity does not have any out-of-box examples for LIN simulation on the CC2642R UART2.  There are some TI App Notes and User's Guides from other MCU product lines.  I am also aware that the CC27X5X10 has LIN capability included in its UART hardware, and TI Driver support should be released later this year.

    By default the TI Drivers use a pre-compiled library (drivers_cc26x2.a) for the default projects.  You can see this in the ti_utils_build_linker.cmd.genlibs file generated by SysConfig.  This is why the source files (/source/ti/drivers/uart2/UART2CC26X2.c) are not referenced by the project.  We can change this by adding a UART2CC26X2.c file to the project's Arm Build Include Option paths.  As I don't want to change the source UART2CC26X2.c or add an additional build path, copying the file locally into my project workspace and editing accordingly makes the most sense.  This local file will overwrite the usage from the pre-compiled library.

    Regards,
    Ryan