cancel
Showing results for 
Search instead for 
Did you mean: 

Self-generated breaks are not detected sometimes

TGnäg.1
Associate II

We configured our controller (STM32F427IG) as master and USART8 to LIN-mode. We observed, that the own breaks are detected with the break detection interrupt. Furthermore, we also get the framing error interrupt as the stop bit arrives later for the break. However, the brake is also missed sometimes. I evaluated this and more than one out of 5 breaks is missed. I showed one example below with the LIN-Data, an interpretation of it, the transmit interrupt, break detection interrupt, receive interrupt and error interrupt. Further the transmit-channel and the receive channel of the master are shown. On the left the error interrupt and the break interrupt occur and on the right they are missed. They are either both missed or they both occur.

0693W00000BZxSIQA1.png 

The signals on the data-channel, rx-channel and tx-channel look fine and one would expect that the break is detected in both cases. The registers are all set according to USART interrupts chapter 30.4 of RM0090 such that the interrupts are enabled.

Do you have an Idea why the break is not detected so often?

7 REPLIES 7

Show us that part of ISR where you handle the status flags.

JW

TGnäg.1
Associate II
void UART8_IRQHandler(void)
{
#ifdef HWLIB_SUPPORTING_EMBOS
   //advantage of OS_Enter...() before variable declaration : higher prioritized interrupts will be handled earlier
   OS_EnterNestableInterrupt();
#endif
 
   E_MFS          mfs   = MFS_8;
   USART_TypeDef* uart  = uartBase[mfs];
   U32            SR    = uart->SR;    //snapshot of status register. better maintainable. set breakpoint on next line.
   boolean        TXE   = READ_BIT(SR, USART_SR_TXE)  == (USART_SR_TXE);
   boolean        LBD   = READ_BIT(SR, USART_SR_LBD)  == (USART_SR_LBD);
   boolean        RXNE  = READ_BIT(SR, USART_SR_RXNE) == (USART_SR_RXNE);
 
   if( ( LL_USART_IsEnabledIT_TXE (uart) == TRUE )
   &&  ( TXE == TRUE )
   &&  ( callbackTable[mfs].MFS_TransmitEmptyCallback != NULL ) )
   {
      callbackTable[mfs].MFS_TransmitEmptyCallback(); //event : Transmit data register empty
   }
 
   if( ( LL_USART_IsEnabledIT_LBD (uart) == TRUE )
   &&  ( LBD == TRUE )
   &&  ( callbackTable[mfs].MFS_TransmitEmptyCallback != NULL ) )
   {
      callbackTable[mfs].MFS_TransmitEmptyCallback(); //event : LIN break detection
   }
 
   if( ( LL_USART_IsEnabledIT_RXNE (uart) == TRUE )
   &&  ( RXNE == TRUE )
   &&  ( callbackTable[mfs].MFS_ReceiveFullCallback != NULL ) )
   {
      callbackTable[mfs].MFS_ReceiveFullCallback();   //event : Read data register not empty
   }
 
#ifdef HWLIB_SUPPORTING_EMBOS
   OS_LeaveNestableInterrupt();
#endif
}

Did you mean this part of the code? I observed, that already this part of the code is not called if the break callback is not called.

Here, the implementation of the callbacks:

static void MfsIrqHandlerTx(E_LINP LINx)
{
   stc_mfs_lin_config_t* pstcMfsInternData = &m_astcMfsInstanceDataLut[LINx];
 
   // LIN break detected?
   if( LL_USART_IsActiveFlag_LBD( linBase[LINx] ) == TRUE )
   {
      LL_USART_ReceiveData8( linBase[LINx] );
      LL_USART_ClearFlag_LBD( linBase[LINx] );  // Clear LIN break detection
      LL_USART_ClearFlag_FE ( linBase[LINx] );  // Clear possible reception errors
      LL_USART_ClearFlag_ORE( linBase[LINx] );  // Clear possible reception errors
 
      // Break Callback if specified
      if (pstcMfsInternData->pfnLinBreakCallback != NULL)
      {
         pstcMfsInternData->pfnLinBreakCallback(pstcMfsInternData->callbackParam);
      }
 
      return;
   }
 
   if (pstcMfsInternData->pfnLinTxCallback != NULL)
   {
      pstcMfsInternData->pfnLinTxCallback(pstcMfsInternData->callbackParam);
   }
}
static void MfsIrqHandlerRx(E_LINP LINx)
{
   volatile uint8_t receivedData;
   stc_mfs_lin_config_t* pstcMfsInternData = &m_astcMfsInstanceDataLut[LINx];
 
   //check if one of the error flags is set
   if( ( LL_USART_IsActiveFlag_FE ( linBase[LINx] ) == TRUE )
   ||  ( LL_USART_IsActiveFlag_ORE( linBase[LINx] ) == TRUE ) )
   {
      //RM0090 : "It is cleared by a software sequence (an read to the USART_SR register followed by a read to the USART_DR register)."
      LL_USART_ClearFlag_FE ( linBase[LINx] );  // Clear possible reception errors
      LL_USART_ClearFlag_ORE( linBase[LINx] );  // Clear possible reception errors
      LL_USART_ReceiveData8( linBase[LINx] );   // Clear possible reception errors
 
      if (pstcMfsInternData->pfnLinErrorCallback != NULL)
      {
         pstcMfsInternData->pfnLinErrorCallback(pstcMfsInternData->callbackParam);
      }
   }
   else
   {
      receivedData = LL_USART_ReceiveData8( linBase[LINx] );
 
      // Check if receive callback function is valid.
      if (pstcMfsInternData->pfnLinRxCallback != NULL)
      {
         // Call receive callback function.
         pstcMfsInternData->pfnLinRxCallback(pstcMfsInternData->callbackParam, receivedData);
      }
   }
}

> I observed, that already this part of the code is not called if the break callback is not called.

Which part, exactly? And how exactly did you generate that LA trace? Did you add/remove some code compared to what you've shown above (if yes, that's a bad idea; it's always harder to guess what's going on if you present a changing situation).

I don't quite understand, in UART8_IRQHandler() why do you call the same function for both LBD and TXE.

You also inconsistently check and clear the flags at multiple places; this is not the best practice. For example, in UART8_IRQHandler(), you read LBD and store it in a local variable, then because of TXE being set you call MfsIrqHandlerTx() where you clear LBD if it has been set, but then upon return to UART8_IRQHandler() you check that stored LBD, which may not reflect its current state.

I'd recommend you to read, store locally and clear the whole SR only once (of course you can clear only some of the bits, but the rest gets cleared by subsequent processing), and then act upon that locally stored copy.

[stylistics, arguable] Maybe removing some/all of the callback-fabric-induced complexity, and perhaps also avoiding the rather superfluous Cube/LL "commands" (functions and macros, which mostly only rename and unnecessarily obfuscate accesses to registers) might help in reducing confusion, it also may help with reducing execution time of the ISR, which is always a good thing. [/]

JW

Sorry, that's my bad. I posted the code in its original form. To debug, I added Debug Signals (pins that can be set high or low).

In the original plot the Debug Signal for the UART8_IRQHandler is not shown that indicates that it is not called:

0693W00000BZyRuQAL.pngHere is the corresponding code:

void UART8_IRQHandler(void)
{
#ifdef HWLIB_SUPPORTING_EMBOS
   //advantage of OS_Enter...() before variable declaration : higher prioritized interrupts will be handled earlier
   OS_EnterNestableInterrupt();
#endif
 
   DEBUG_SignalSet(DEBUG_MODE_TEST, DEBUG_SIGNAL_5, 1);
 
   E_MFS          mfs   = MFS_8;
   USART_TypeDef* uart  = uartBase[mfs];
   U32            SR    = uart->SR;    //snapshot of status register. better maintainable. set breakpoint on next line.
   boolean        TXE   = READ_BIT(SR, USART_SR_TXE)  == (USART_SR_TXE);
   boolean        LBD   = READ_BIT(SR, USART_SR_LBD)  == (USART_SR_LBD);
   boolean        RXNE  = READ_BIT(SR, USART_SR_RXNE) == (USART_SR_RXNE);
 
   if( ( LL_USART_IsEnabledIT_TXE (uart) == TRUE )
   &&  ( TXE == TRUE )
   &&  ( callbackTable[mfs].MFS_TransmitEmptyCallback != NULL ) )
   {
      callbackTable[mfs].MFS_TransmitEmptyCallback(); //event : Transmit data register empty
   }
 
   if( ( LL_USART_IsEnabledIT_LBD (uart) == TRUE )
   &&  ( LBD == TRUE )
   &&  ( callbackTable[mfs].MFS_TransmitEmptyCallback != NULL ) )
   {
      callbackTable[mfs].MFS_TransmitEmptyCallback(); //event : LIN break detection
   }
 
   if( ( LL_USART_IsEnabledIT_RXNE (uart) == TRUE )
   &&  ( RXNE == TRUE )
   &&  ( callbackTable[mfs].MFS_ReceiveFullCallback != NULL ) )
   {
      callbackTable[mfs].MFS_ReceiveFullCallback();   //event : Read data register not empty
   }
 
   DEBUG_SignalSet(DEBUG_MODE_TEST, DEBUG_SIGNAL_5, 0);
 
#ifdef HWLIB_SUPPORTING_EMBOS
   OS_LeaveNestableInterrupt();
#endif
}
static void MfsIrqHandlerTx(E_LINP LINx)
{
   stc_mfs_lin_config_t* pstcMfsInternData = &m_astcMfsInstanceDataLut[LINx];
 
   if(LINx == LINP_8)
   {
      DEBUG_SignalSet(DEBUG_MODE_TEST, DEBUG_SIGNAL_1, 1);
   }
 
   // LIN break detected?
   if( LL_USART_IsActiveFlag_LBD( linBase[LINx] ) == TRUE )
   {
      if(LINx == LINP_8)
      {
         DEBUG_SignalSet(DEBUG_MODE_TEST, DEBUG_SIGNAL_2, 1);
      }
 
      LL_USART_ReceiveData8( linBase[LINx] );
      LL_USART_ClearFlag_LBD( linBase[LINx] );  // Clear LIN break detection
      LL_USART_ClearFlag_FE ( linBase[LINx] );  // Clear possible reception errors
      LL_USART_ClearFlag_ORE( linBase[LINx] );  // Clear possible reception errors
 
      // Break Callback if specified
      if (pstcMfsInternData->pfnLinBreakCallback != NULL)
      {
         pstcMfsInternData->pfnLinBreakCallback(pstcMfsInternData->callbackParam);
      }
 
      if(LINx == LINP_8)
      {
         DEBUG_SignalSet(DEBUG_MODE_TEST, DEBUG_SIGNAL_2, 0);
      }
 
      return;
   }
 
   if (pstcMfsInternData->pfnLinTxCallback != NULL)
   {
      pstcMfsInternData->pfnLinTxCallback(pstcMfsInternData->callbackParam);
   }
 
   if(LINx == LINP_8)
   {
      DEBUG_SignalSet(DEBUG_MODE_TEST, DEBUG_SIGNAL_1, 0);
   }
}
static void MfsIrqHandlerRx(E_LINP LINx)
{
   volatile uint8_t receivedData;
   stc_mfs_lin_config_t* pstcMfsInternData = &m_astcMfsInstanceDataLut[LINx];
 
   if(LINx == LINP_8)
   {
      DEBUG_SignalSet(DEBUG_MODE_TEST, DEBUG_SIGNAL_3, 1);
   }
 
   //check if one of the error flags is set
   if( ( LL_USART_IsActiveFlag_FE ( linBase[LINx] ) == TRUE )
   ||  ( LL_USART_IsActiveFlag_ORE( linBase[LINx] ) == TRUE ) )
   {
      if(LINx == LINP_8)
      {
         DEBUG_SignalSet(DEBUG_MODE_TEST, DEBUG_SIGNAL_4, 1);
      }
 
      //RM0090 : "It is cleared by a software sequence (an read to the USART_SR register followed by a read to the USART_DR register)."
      LL_USART_ClearFlag_FE ( linBase[LINx] );  // Clear possible reception errors
      LL_USART_ClearFlag_ORE( linBase[LINx] );  // Clear possible reception errors
      LL_USART_ReceiveData8( linBase[LINx] );   // Clear possible reception errors
 
      if (pstcMfsInternData->pfnLinErrorCallback != NULL)
      {
         pstcMfsInternData->pfnLinErrorCallback(pstcMfsInternData->callbackParam);
      }
 
      if(LINx == LINP_8)
      {
         DEBUG_SignalSet(DEBUG_MODE_TEST, DEBUG_SIGNAL_4, 0);
      }
   }
   else
   {
      receivedData = LL_USART_ReceiveData8( linBase[LINx] );
 
      // Check if receive callback function is valid.
      if (pstcMfsInternData->pfnLinRxCallback != NULL)
      {
         // Call receive callback function.
         pstcMfsInternData->pfnLinRxCallback(pstcMfsInternData->callbackParam, receivedData);
      }
   }
 
   if(LINx == LINP_8)
   {
      DEBUG_SignalSet(DEBUG_MODE_TEST, DEBUG_SIGNAL_3, 0);
   }
}

DEBUG_SIGNAL_1: TX interrupt

DEBUG_SIGNAL_2: Break detection

DEBUG_SIGNAL_3: RX interrupt

DEBUG_SIGNAL_4: Error Flag

DEBUG_SIGNAL_5: UART8_IRQHandler

I still don't like your code 🙂 (see my previous post for comments), but the fact that there's no Rx/FE interrupt prior to Tx/Break makes me think that the problem starts sooner, at the beginning of packet/break transmission...

JW

Many thanks for your help until now. I'm quite an inexperienced programmer and so it could actually be my code. It is however existing code and I agree. I just can't change tested code so quickly as long as there is no bug in it.

I thought of a problem with the transmission too and that's the reason for debugging the Tx and Rx channels and showing the plots. However, I cannot see any irregularities when the break detection fails. Or can you think of a scenario where these signals look fine but the detection of the break fails?

We all learn as we go. I don't have time to spend on this problem, sorry.

What I had on mind was the code where you *start* the transmission process, you haven't shown that yet.

JW