Bug in Serial_LDD Transmission Complete before InterruptTx

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

Bug in Serial_LDD Transmission Complete before InterruptTx

Jump to solution
875 Views
svenkolberg
Contributor I

In the Kinetis Design Studio v3.0.0 I found the following generated Code:

 

 

PE_ISR(ASerialLdd1_Interrupt)

{

  /* {FreeRTOS RTOS Adapter} ISR parameter is passed through the global variable */

  ASerialLdd1_TDeviceDataPtr DeviceDataPrv = INT_UART0_RX_TX__BAREBOARD_RTOS_ISRPARAM;

  register uint32_t StatReg = UART_PDD_ReadInterruptStatusReg(UART0_BASE_PTR); /* Read status register */

  register uint16_t OnErrorFlags = 0U; /* Temporary variable for flags */

  register uint8_t  OnBreakFlag = 0U;  /* Temporary variable flag for OnBreak event */

  register uint16_t Data;              /* Temporary variable for data */

 

  if (StatReg & (UART_S1_NF_MASK | UART_S1_OR_MASK | UART_S1_FE_MASK | UART_S1_PF_MASK)) { /* Is any error flag set? */

    Data = (uint16_t)UART_PDD_GetChar8(UART0_BASE_PTR); /* Read an 8-bit character from receiver */

    if ((StatReg & UART_S1_FE_MASK) != 0U) { /* Is the framing error detected? */

      if (((StatReg & UART_S1_RDRF_MASK) != 0U) && (Data == 0U)) { /* Is the zero character in the receiver? */

        OnBreakFlag++;

        DeviceDataPrv->SerFlag |= BREAK_DETECTED; /* If yes then set the flag */

      } else {

        OnErrorFlags |= LDD_SERIAL_FRAMING_ERROR; /* If yes then set the flag */

      }

    }

    if ((StatReg & UART_S1_OR_MASK) != 0U) { /* Is the overrun error flag set? */

      OnErrorFlags |= LDD_SERIAL_RX_OVERRUN; /* If yes then set the flag */

    }

    if ((StatReg & UART_S1_PF_MASK) != 0U) { /* Is the parity error flag set? */

      OnErrorFlags |= LDD_SERIAL_PARITY_ERROR; /* If yes then set the flag */

    }

    if ((StatReg & UART_S1_NF_MASK) != 0U) { /* Is the noise error flag set? */

      OnErrorFlags |= LDD_SERIAL_NOISE_ERROR; /* If yes then set the flag */

    }

    DeviceDataPrv->ErrFlag |= OnErrorFlags; /* Copy flags status to ErrFlag status variable */

    StatReg &= (uint32_t)(~(uint32_t)UART_S1_RDRF_MASK); /* Clear the receive data flag to discard the errorneous data */

    if (OnBreakFlag != 0U) {

      ASerialLdd1_OnBreak(DeviceDataPrv->UserDataPtr); /* If yes then invoke user event */

    } else {

      ASerialLdd1_OnError(DeviceDataPrv->UserDataPtr); /* Invoke user event */

    }

  }

  if (StatReg & UART_S1_RDRF_MASK) {   /* Is the receiver's interrupt flag set? */

    InterruptRx(DeviceDataPrv);        /* If yes, then invoke the internal service routine. This routine is inlined. */

  }

  if (DeviceDataPrv->SerFlag & ENABLED_TX_INT) { /* Is the transmitter interrupt enabled? */

    if (StatReg & UART_S1_TDRE_MASK) { /* Is the transmitter empty? */

      InterruptTx(DeviceDataPrv);      /* If yes, then invoke the internal service routine. This routine is inlined. */

    }

  }

  if ((UART_PDD_GetTxCompleteInterruptMask(UART0_BASE_PTR) != 0U)  && (StatReg & UART_S1_TC_MASK)) { /* Is a transmission completed? */

    UART_PDD_DisableInterrupt(UART0_BASE_PTR, UART_PDD_INTERRUPT_TRANSMITTER_COMPLETE); /* If yes then disable TX complete interrupt */

    ASerialLdd1_OnTxComplete(DeviceDataPrv->UserDataPtr); /* If yes then invoke user event */

  }

}

 

 

Suppose the Line;

     if (DeviceDataPrv->SerFlag & ENABLED_TX_INT) { /* Is the transmitter interrupt enabled? */

evaluates to true, the InterruptTx will be called and a new char will be send.

 

If coincidentally the Transmission complete flag is set when the StatReg was Read, the Line:

  if ((UART_PDD_GetTxCompleteInterruptMask(UART0_BASE_PTR) != 0U)  && (StatReg & UART_S1_TC_MASK)) { /* Is a transmission completed? */

evaluates to true and the tx complete interrupt will be disabled for the new transfer and the OnTxComplete Event will be called, too.

 

To my understanding, this  behaviour is not correct. The OnTxComplete Event will be called even though a transmission is ongoing.

 

In my Application I used the OnTxComplete to disable the RS485 DriveEnable. But because of this it shows a rather strange behaviour.

 

Is there a way to fix the code?

Labels (1)
0 Kudos
1 Solution
502 Views
marek_neuzil
NXP Employee
NXP Employee

Hello,

As far as I know the UART device there is always internal buffer for transmitting of data (one byte of data at least). TDRE flag is set when this buffer is empty (but data are being transmitted by UART device). When no data are available in the buffer and the last character is sent the TC flag is set.

Therefore the case you have described cannot occur in a normal situation, i.e. when TDRE and TC flags are set there are not any data in the user buffer for sending. Therefore the OnTxComplete event is called correctly.

If the invocation of the ASerialLdd1_Interrupt is blocked for a long time (all characters from the UART internal buffer are sent)  then the OnTxComplete event is called (part of the user data buffer is sent; there is a transmit delay on the line because of an application issue). This case is very specific and indicates a problem of the application. You can detect this case in the OnTxComplete event by using GetTxCompleteStatus() method of the Serial_LDD.

I will report this issue to the responsible development team.

Best Regards,

Marek Neuzil

View solution in original post

3 Replies
502 Views
svenkolberg
Contributor I

Hello,

after further investigation, the method of adding new data and suppressing TxComplete until the very end of the transmission was not correct.

If anyone tries something similar:

Do not use the OnBlockSent Event to add new Bytes, wait always for the OnTxComplete for the completion of the whole transfer.

By doing so, it is always guaranteed that every block will generate the OnTxComplete interrupt (which I was looking to avoid, which is impossible by HW-Design of the UART). But this allows the code to check if the transmission is complete or if new data is ready to be send.

0 Kudos
503 Views
marek_neuzil
NXP Employee
NXP Employee

Hello,

As far as I know the UART device there is always internal buffer for transmitting of data (one byte of data at least). TDRE flag is set when this buffer is empty (but data are being transmitted by UART device). When no data are available in the buffer and the last character is sent the TC flag is set.

Therefore the case you have described cannot occur in a normal situation, i.e. when TDRE and TC flags are set there are not any data in the user buffer for sending. Therefore the OnTxComplete event is called correctly.

If the invocation of the ASerialLdd1_Interrupt is blocked for a long time (all characters from the UART internal buffer are sent)  then the OnTxComplete event is called (part of the user data buffer is sent; there is a transmit delay on the line because of an application issue). This case is very specific and indicates a problem of the application. You can detect this case in the OnTxComplete event by using GetTxCompleteStatus() method of the Serial_LDD.

I will report this issue to the responsible development team.

Best Regards,

Marek Neuzil

502 Views
svenkolberg
Contributor I

Thank you for your quick response.

The ASerielLdd1_Interrupt is indeed blocked for a long time.

The Application consists out of two UARTs, 1 Receiving Data at 115200baud, the other sending Data at 115200 baud.

The Received chars are forwarded within the Interrupt to the Sending interface.

This constellation makes the situation I described very likely (20% of the time).

I would assume running on a slow clock with just a few higher priority interrupts could cause the same problems, even though it might be less likely.

Regards

Sven Kolberg.

0 Kudos