USART with CMSIS: no receive events

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

USART with CMSIS: no receive events

3,643 Views
danielholala
Senior Contributor II

Hi there,

I'm developing for a LPC55xx based board. I'm trying to be a good NXP customer and use MCUXpresso IDE with all tools (Pins, Clocks, Peripherals). 

I configured the Flexcom/USART component using Peripherals tools.  Peripherals tool also adds code to initialize USART. The code is based on "fsl_usart_cmsis.h". It's based on ARM's CMSIS API and looks like this:

static void FLEXCOMM0_init(void) {
  /* Initialize CMSIS USART */
  FLEXCOMM0_PERIPHERAL.Initialize(USART0_SignalEvent);
  /* Power control of CMSIS USART */
  FLEXCOMM0_PERIPHERAL.PowerControl(ARM_POWER_FULL);
  /* Control of CMSIS USART */
  FLEXCOMM0_PERIPHERAL.Control(ARM_USART_MODE_ASYNCHRONOUS | ARM_USART_DATA_BITS_8 | ARM_USART_PARITY_NONE | ARM_USART_STOP_BITS_1, 230400);
  /* Enable or disable receiver. */
  FLEXCOMM0_PERIPHERAL.Control(ARM_USART_CONTROL_RX , 1);
  /* Enable or disable transmitter. */
  FLEXCOMM0_PERIPHERAL.Control(ARM_USART_CONTROL_TX , 1);
}

As you can see, a user callback function USART0_SignalEvent is registered for serial events (e.g. data received).

However, this functions is only called once when sending data out.

The issue here: The user callback function is not called when data is received by the MCU.

Why is this?

I can see in the debugger that the receive interrupt is called. The call stack is:

FLEXCOMM0_IRQHandler -> FLEXCOMM0_DriverIRQHandler -> USART_TransferHandleIRQ

in void USART_TransferHandleIRQ(USART_Type *base, usart_handle_t *handle) the handle struct looks like:

danielholala_0-1642176466281.png

Received bytes have been stored in a RingBuffer handle->rxRingBuffer.
There are 3 bytes (handle->rxRingBufferHead ==3).

The user callback USART0_SignalEvent is stored in handle->userData and is called from KSDK_USART_NonBlockingCallback() (aka handle->callback) which in turn is called from USART_TransferHandleIRQ .

However, I do not see a call to handle->callback in the code for the "ringbuffer case". There's only a call to signal ringbuffer overflow:

handle->callback(base, handle, kStatus_USART_RxRingBufferOverrun, handle->userData);

 

Any idea what I might be missing here?

0 Kudos
Reply
8 Replies

3,472 Views
DineshRibadiya
Contributor I

@danielholala 

I am using LPC55Sxx controller and facing similar kind of the issue. Did you get any solution?

@xiangjun_rong 

In my case, I am sending large data file from LPC55xx to the other controller over USART. I am receiving few data from other controller to the LPC55xx during sending file.

Many times LPC55xx able to receive the data but sometime not getting expected data and rxState is set  kUSART_RxBusy in _usart_handle structure. After that rxState does not set as kUSART_RxIdle. It is indicates that not getting USART Rx interrupt. (Not calling the  USART_TransferHandleIRQ() where we are expecting to set kUSART_RxIdle after getting all remaining data).

0 Kudos
Reply

3,466 Views
frank_m
Senior Contributor III

I suppose you get a receive error your interrupt handler does not handle.

In most USART implementation I know, one needs to check for Rx errors and reset the respective flag explicitly, or else reception does not resume.

0 Kudos
Reply

3,453 Views
DineshRibadiya
Contributor I

hello Frank,

Thanks for the prompt response!

I am handling the USART_FIFOSTAT_RXERR_MASK. please refer below image and suggest me if i am missing anything else.

DineshRibadiya_0-1654780400620.png

 

 

0 Kudos
Reply

3,450 Views
frank_m
Senior Contributor III

I would use the debugger, and check if any UART or FIFO  error flag is set in peripheral registers when reception stops.

To be honest, it is years since I worked on UART code for NXP MCUs the last time. I did a lot with other devices, many without FIFO (some rely on DMA instead).

UART is not really "CMSIS", i.e. there is hardly anything common amongst vendors, the concepts and register interface is highly specific. Perhaps you need the USART section of the MCU's  user manual for reference.

0 Kudos
Reply

3,445 Views
DineshRibadiya
Contributor I

@frank_m I go through the other post and found the that, it is same issue which is i am facing.

I have made changes as per below image. I see some improvement but getting still same issue. I would appreciate your suggession. 

DineshRibadiya_0-1654790921540.png

 

0 Kudos
Reply

3,440 Views
frank_m
Senior Contributor III

Perhaps it's just a plain data size issue. The rxState member is set in fsl_uart.c in the following manner:

if (bytesToReceive != 0U)
{
    /* No data in ring buffer, save the request to UART handle. */
    ...
    handle->rxState = (uint8_t)kUSART_RxBusy;
}

If your data size is not exactly a multiple of the DMA or FIFO block size, there is always a problem with  the last package. Or, when characters are lost.

This is why I prefer single-character interrupts for asynchronous reception.

0 Kudos
Reply

3,639 Views
danielholala
Senior Contributor II

I figured that the API is not designed as I expected it. From reading other documents on CMSIS I gleaned that I might have to initiate a read transfer first using e.g.

int32_t ARM_USART_Receive ( void * data, uint32_t num )

and then the ARM_USART_EVENT_RECEIVE_COMPLETE  event will be triggered as soon as the requested bytes have been read.

I wonder how to use this API if a limited but unknown numbers of characters are to be read, e.g., when using a line based text procotol? If I read one character after another that's inefficient, if I request more characters than which will be send, the event does not trigger. 

 

 

0 Kudos
Reply

3,630 Views
xiangjun_rong
NXP TechSupport
NXP TechSupport

Hi, Daniel,

I have to admit the software architecture is complex.

But I think this is the a clue to understand the code.

#define DEMO_USART Driver_USART0

1)calling DEMO_USART.PowerControl(ARM_POWER_FULL); in main() is the same as calling

DEMO_USART.USART0_NonBlockingPowerControl(ARM_POWER_FULL);

ARM_DRIVER_USART Driver_USART0 = {
USARTx_GetVersion, USARTx_GetCapabilities,
#if RTE_USART0_DMA_EN
USART0_DmaInitialize, USART0_DmaUninitialize, USART0_DmaPowerControl, USART0_DmaSend, USART0_DmaReceive,
USART0_DmaTransfer, USART0_DmaGetTxCount, USART0_DmaGetRxCount, USART0_DmaControl, USART0_DmaGetStatus,
#else
USART0_NonBlockingInitialize,
USART0_NonBlockingUninitialize,
USART0_NonBlockingPowerControl,
USART0_NonBlockingSend,
USART0_NonBlockingReceive,
USART0_NonBlockingTransfer,
USART0_NonBlockingGetTxCount,
USART0_NonBlockingGetRxCount,
USART0_NonBlockingControl,
USART0_NonBlockingGetStatus,
#endif
USARTx_SetModemControl, USARTx_GetModemStatus};

 

2)In the USART_NonBlockingPowerControl(), the USART_TransferCreateHandle(usart->resource->base, usart->handle, KSDK_USART_NonBlockingCallback,
(void *)usart->cb_event); is called.

 

static int32_t USART_NonBlockingPowerControl(ARM_POWER_STATE state, cmsis_usart_non_blocking_driver_state_t *usart)
{
usart_config_t config;

switch (state)
{
case ARM_POWER_OFF:
if (usart->flags & USART_FLAG_POWER)
{
USART_Deinit(usart->resource->base);
usart->flags = USART_FLAG_INIT;
}
break;
case ARM_POWER_LOW:
return ARM_DRIVER_ERROR_UNSUPPORTED;
case ARM_POWER_FULL:
/* Must be initialized first. */
if (usart->flags == USART_FLAG_UNINIT)
{
return ARM_DRIVER_ERROR;
}

if (usart->flags & USART_FLAG_POWER)
{
/* Driver already powered */
break;
}

USART_GetDefaultConfig(&config);
config.enableTx = true;
config.enableRx = true;

USART_Init(usart->resource->base, &config, usart->resource->GetFreq());
USART_TransferCreateHandle(usart->resource->base, usart->handle, KSDK_USART_NonBlockingCallback,
(void *)usart->cb_event);
usart->flags |= (USART_FLAG_POWER | USART_FLAG_CONFIGURED);

break;
default:
return ARM_DRIVER_ERROR_UNSUPPORTED;
}

return ARM_DRIVER_OK;
}

 

 

3)In the USART_TransferCreateHandle(), the FLEXCOMM_SetIRQHandler() is called

status_t USART_TransferCreateHandle(USART_Type *base,
usart_handle_t *handle,
usart_transfer_callback_t callback,
void *userData)
{
/* Check 'base' */
assert(!((NULL == base) || (NULL == handle)));

uint32_t instance = 0;
usart_to_flexcomm_t handler;
handler.usart_master_handler = USART_TransferHandleIRQ;

if ((NULL == base) || (NULL == handle))
{
return kStatus_InvalidArgument;
}

instance = USART_GetInstance(base);

(void)memset(handle, 0, sizeof(*handle));
/* Set the TX/RX state. */
handle->rxState = (uint8_t)kUSART_RxIdle;
handle->txState = (uint8_t)kUSART_TxIdle;
/* Set the callback and user data. */
handle->callback = callback;
handle->userData = userData;
handle->rxWatermark = (uint8_t)USART_FIFOTRIG_RXLVL_GET(base);
handle->txWatermark = (uint8_t)USART_FIFOTRIG_TXLVL_GET(base);

FLEXCOMM_SetIRQHandler(base, handler.flexcomm_handler, handle);

/* Enable interrupt in NVIC. */
(void)EnableIRQ(s_usartIRQ[instance]);

return kStatus_Success;
}

 

typedef union usart_to_flexcomm
{
flexcomm_usart_irq_handler_t usart_master_handler;
flexcomm_irq_handler_t flexcomm_handler;
} usart_to_flexcomm_t;

 

4)In the FLEXCOMM_SetIRQHandler(), the s_flexcommIrqHandler[instance] is initialized by handler;, the s_flexcommIrqHandler[instance] is called by USART ISR.

 void FLEXCOMM_SetIRQHandler(void *base, flexcomm_irq_handler_t handler, void *handle)
{
uint32_t instance;

/* Look up instance number */
instance = FLEXCOMM_GetInstance(base);

/* Clear handler first to avoid execution of the handler with wrong handle */
s_flexcommIrqHandler[instance] = NULL;
s_flexcommHandle[instance] = handle;
s_flexcommIrqHandler[instance] = handler;
SDK_ISR_EXIT_BARRIER;
}

5) in the end, the USART_TransferHandleIRQ() is called by ISR

void USART_TransferHandleIRQ(USART_Type *base, usart_handle_t *handle)

{

    /* Check arguments */

    assert((NULL != base) && (NULL != handle));

 

    bool receiveEnabled = ((handle->rxDataSize != 0U) || (handle->rxRingBuffer != NULL));

    bool sendEnabled    = (handle->txDataSize != 0U);

    uint8_t rxdata;

    size_t tmpsize;

 

    /* If RX overrun. */

    if ((base->FIFOSTAT & USART_FIFOSTAT_RXERR_MASK) != 0U)

    {

        /* Clear rx error state. */

        base->FIFOSTAT |= USART_FIFOSTAT_RXERR_MASK;

        /* clear rxFIFO */

        base->FIFOCFG |= USART_FIFOCFG_EMPTYRX_MASK;

        /* Trigger callback. */

        if (handle->callback != NULL)

        {

            handle->callback(base, handle, kStatus_USART_RxError, handle->userData);

        }

    }

    while ((receiveEnabled && ((base->FIFOSTAT & USART_FIFOSTAT_RXNOTEMPTY_MASK) != 0U)) ||

           (sendEnabled && ((base->FIFOSTAT & USART_FIFOSTAT_TXNOTFULL_MASK) != 0U)))

    {

        /* Receive data */

        if (receiveEnabled && ((base->FIFOSTAT & USART_FIFOSTAT_RXNOTEMPTY_MASK) != 0U))

        {

            /* Receive to app bufffer if app buffer is present */

            if (handle->rxDataSize != 0U)

            {

                rxdata          = (uint8_t)base->FIFORD;

                *handle->rxData = rxdata;

                handle->rxDataSize--;

                handle->rxData++;

                receiveEnabled = ((handle->rxDataSize != 0U) || (handle->rxRingBuffer != NULL));

                if (0U == handle->rxDataSize)

                {

                    if (NULL == handle->rxRingBuffer)

                    {

                        base->FIFOINTENCLR = USART_FIFOINTENCLR_RXLVL_MASK | USART_FIFOINTENSET_RXERR_MASK;

                    }

                    handle->rxState = (uint8_t)kUSART_RxIdle;

                    if (handle->callback != NULL)

                    {

                        handle->callback(base, handle, kStatus_USART_RxIdle, handle->userData);

                    }

                }

            }

            /* Otherwise receive to ring buffer if ring buffer is present */

            else

            {

                if (handle->rxRingBuffer != NULL)

                {

                    /* If RX ring buffer is full, trigger callback to notify over run. */

                    if (USART_TransferIsRxRingBufferFull(handle))

                    {

                        if (handle->callback != NULL)

                        {

                            handle->callback(base, handle, kStatus_USART_RxRingBufferOverrun, handle->userData);

                        }

                    }

                    /* If ring buffer is still full after callback function, the oldest data is overridden. */

                    if (USART_TransferIsRxRingBufferFull(handle))

                    {

                        /* Increase handle->rxRingBufferTail to make room for new data. */

                        if ((size_t)handle->rxRingBufferTail + 1U == handle->rxRingBufferSize)

                        {

                            handle->rxRingBufferTail = 0U;

                        }

                        else

                        {

                            handle->rxRingBufferTail++;

                        }

                    }

                    /* Read data. */

                    rxdata                                         = (uint8_t)base->FIFORD;

                    handle->rxRingBuffer[handle->rxRingBufferHead] = rxdata;

                    /* Increase handle->rxRingBufferHead. */

                    if ((size_t)handle->rxRingBufferHead + 1U == handle->rxRingBufferSize)

                    {

                        handle->rxRingBufferHead = 0U;

                    }

                    else

                    {

                        handle->rxRingBufferHead++;

                    }

                }

            }

        }

        /* Send data */

        if (sendEnabled && ((base->FIFOSTAT & USART_FIFOSTAT_TXNOTFULL_MASK) != 0U))

        {

            base->FIFOWR = *handle->txData;

            handle->txDataSize--;

            handle->txData++;

            sendEnabled = handle->txDataSize != 0U;

            if (!sendEnabled)

            {

                base->FIFOINTENCLR = USART_FIFOINTENCLR_TXLVL_MASK;

 

                base->INTENSET = USART_INTENSET_TXIDLEEN_MASK;

            }

        }

    }

 

    /* Tx idle and the interrupt is enabled. */

    if ((0U != (base->INTENSET & USART_INTENSET_TXIDLEEN_MASK)) && (0U != (base->INTSTAT & USART_INTSTAT_TXIDLE_MASK)))

    {

        /* Set txState to idle only when all data has been sent out to bus. */

        handle->txState = (uint8_t)kUSART_TxIdle;

        /* Disable tx idle interrupt */

        base->INTENCLR = USART_INTENCLR_TXIDLECLR_MASK;

 

        /* Trigger callback. */

        if (handle->callback != NULL)

        {

            handle->callback(base, handle, kStatus_USART_TxIdle, handle->userData);

        }

    }

 

    /* ring buffer is not used */

    if (NULL == handle->rxRingBuffer)

    {

        tmpsize = handle->rxDataSize;

 

        /* restore if rx transfer ends and rxLevel is different from default value */

        if ((tmpsize == 0U) && (USART_FIFOTRIG_RXLVL_GET(base) != handle->rxWatermark))

        {

            base->FIFOTRIG =

                (base->FIFOTRIG & (~USART_FIFOTRIG_RXLVL_MASK)) | USART_FIFOTRIG_RXLVL(handle->rxWatermark);

        }

        /* decrease level if rx transfer is bellow */

        if ((tmpsize != 0U) && (tmpsize < (USART_FIFOTRIG_RXLVL_GET(base) + 1U)))

        {

            base->FIFOTRIG = (base->FIFOTRIG & (~USART_FIFOTRIG_RXLVL_MASK)) | (USART_FIFOTRIG_RXLVL(tmpsize - 1U));

        }

    }

}

 

 

Hope it can help you

BR

XiangJun Rong

0 Kudos
Reply