cancel
Showing results for 
Search instead for 
Did you mean: 

UART RXNE interrupt not asserted in certain situations

L.Heinrichs
Associate

I am currently running into a problem using an STM32F446 microncontrollers UART interface that i cannot explain and require help.

The system we are looking at is meant to do measurements.

Each measurement cycle is triggered by an external Host.

The Host sends a specified 1-byte command to the STM's UART interface, the STM then starts a measurement and returns the results to the host using the same UART interface.

  1. Host sends 1-byte command to UART interface
  2. STM generates RXNE interrupt
  3. STM interrupt routine code validates the byte command, sets a start bit, resets interrupt flag
  4. STM main routine polls start bit and triggers some measurements
  5. On measurement completion, a short report is sent to the Host using the UART interface. Sending is done based on interrupts and a buffer (TC flag set: more data in buffer ? send next byte : turn transmitter off).

Our customer, who is currently testing the prototypes, notified us about missing reports. The Host computer triggers a measurement by sending the command on the USART interface, but does not get a response. Missing responses happen frequently, about 1 in 20 times.

To figure out where the command is lost, I added a hardware loopback on the STM32F446's UART and removed the host computer. Instead of the host sending the UART command, the STM32F446 is now sending the command to itsself. The setup is the following:

LOOPBACK VARIANT I

SysTick Interrupt

HAL_IncTick();
    UART4->DR = 0;
    UART4->CR1 |= USART_CR1_TE;

UART4 Interrupt ( tcCount / rcvdCount are global variables initialized to 0).

uint8_t byte;
if(UART4->SR & USART_SR_TC){ // Transfer complete
    UART4->CR1 &= ~USART_CR1_TE;	//Disable Transmitter
    UART4->SR &= ~(USART_SR_TC);	//Clear Flag
    tcCount++;
}
 
if(UART4->SR & USART_SR_RXNE){	//Receive Register not empty
    byte = UART4->DR;
    rcvdCount++;
    UART4->SR &= ~USART_SR_RXNE;
}

Main():

while (1)
    {
        Toggle_Watchdog();
        HAL_Delay(1);
    }

On letting this code run, about 5% of the commands sent do not trigger an RXNE interrupt:

Delay between sending commands: 2ms

Baud Rate: 38400

TC interrupts: 104840

RXNE Interrupts: 100330

Loss: 4,30 %

Delay between sending commands: 1ms

Baud Rate: 38400

TC interrupts: 106241

RXNE Interrupts: 100776

Loss: 5,14 %

Delay between sending commands: 40ms

Baud Rate: 38400

TC interrupts: 129148

RXNE Interrupts: 122165

Loss: 5,41 %

When increasing the baud rate, the number of missing interrupts increases as well:

9600 baud ~1%

115200 baud ~25%

On measuring the TX/RX lines with an oscilloscope, i cannot see any missing commands.

LOOPBACK VARIANT II

Instead of sending the command from within the SysTick Interrupt, the command is sent from the main function:

while (1) {
    Toggle_Watchdog();
    UART4->DR = 0;
    UART4->CR1 |= USART_CR1_TE;
    HAL_Delay(1);
}

The behaviour is the same as in Variant I.

LOOPBACK VARIANT III

in this variant, the UART interface echos any data that was received after incrementing it by 1. Instead of a timer interrupt sending.

UART Interrupt routine:

uint8_t byte;
if(UART4->SR & USART_SR_TC){          // Transfer complete
    UART4->CR1 &= ~USART_CR1_TE;	//Disable Transmitter
    UART4->SR &= ~(USART_SR_TC);	//Clear Flag
    tcCount++;
}
if(UART4->SR & USART_SR_RXNE){	//RXRegister not empty
    byte = UART4->DR;
    UART4->DR = byte + 1;
    UART4->CR1 |= USART_CR1_TE;
    rcvdCount++;
    UART4->SR &= ~USART_SR_RXNE;
}

One initial transmit is generated in the main function:

UART4->DR = 0;
    UART4->CR1 |= USART_CR1_TE;
    while (1)
    {
        Toggle_Watchdog();
        HAL_Delay(1);
    }

Based on the previous variants, it is expected that this variant will do a limited number of transmits until a missing RXNE interrupt. Communication should stop at that point. However, this version seems to run endlessly, without any missing RXNE interrupts.

Questions

  1. What are possible reasons for a missing RXNE interrupt, even if the communication is visible on an oscilloscope connected to the data lines?
  2. Why does Variant III behave differently?

UART Setup Code

FYI:

void MX_UART4_Init(void)
{
  LL_USART_InitTypeDef USART_InitStruct;
 
  LL_GPIO_InitTypeDef GPIO_InitStruct;
  /* Peripheral clock enable */
  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_UART4);
  
  /**UART4 GPIO Configuration  
  PC10   ------> UART4_TX
  PC11   ------> UART4_RX 
  */
  GPIO_InitStruct.Pin                   = LL_GPIO_PIN_10|LL_GPIO_PIN_11;
  GPIO_InitStruct.Mode                  = LL_GPIO_MODE_ALTERNATE;
  GPIO_InitStruct.Speed                 = LL_GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.OutputType            = LL_GPIO_OUTPUT_PUSHPULL;
  GPIO_InitStruct.Pull                  = LL_GPIO_PULL_UP;
  GPIO_InitStruct.Alternate             = LL_GPIO_AF_8;
  LL_GPIO_Init(GPIOC, &GPIO_InitStruct);
 
  NVIC_SetPriority(UART4_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
  NVIC_EnableIRQ(UART4_IRQn);
 
  USART_InitStruct.BaudRate             = 38400;
  USART_InitStruct.DataWidth            = LL_USART_DATAWIDTH_8B;
  USART_InitStruct.StopBits             = LL_USART_STOPBITS_1;
  USART_InitStruct.Parity               = LL_USART_PARITY_NONE;
  USART_InitStruct.TransferDirection    = LL_USART_DIRECTION_TX_RX;
  USART_InitStruct.HardwareFlowControl  = LL_USART_HWCONTROL_NONE;
  USART_InitStruct.OverSampling         = LL_USART_OVERSAMPLING_16;
 
  LL_USART_Init(UART4, &USART_InitStruct);
 
  LL_USART_ConfigAsyncMode(UART4);
 
  LL_USART_Enable(UART4);
 
}

3 REPLIES 3

Aren't the bits in the SR self-clearing for TXE/RXNE?

Don't think I'd blind write/enable the registers in the SysTick, might want to check the TE (Transmit Empty) before writing DR, the TC is a last bit out indicator, occurs much later than TE

RXNE might not flag if there are error status on the receiver, say parity, framing or noise.

Tips, buy me a coffee, or three.. PayPal Venmo Up vote any posts that you find helpful, it shows what's working..
L.Heinrichs
Associate

Hello User 6.02214076×10²³, thanks for your quick reply.

I checked your suggestions.

TXE and RXNE are described as "Set by hardware, cleared by software" in the reference manual TXE/RXNE/BSY behavior (image ai17343).

Checking TE empty: In Variant III (echo) the intervalls are shorter (~0.54 ms) than in the manually triggered variants I+II (1 ms). So im sure the TXE is already set, especially as the TC Flags are counted that occur after TXE. Adding a while (!( UART4->SR & USART_SR_TXE )); before writing to the DR register, nothing changes.

Parity is set to none, Framing and noise should only be set when using multi buffer communication (reference Manual 25.5 - USART interrupts).

As Avogadro said, the TXE/RXNE bits are - well I wouldn't call them "self-clearing", but a mechanism to be cleared by writing/reading the data register. Read the description of these bits at the USART_SR register description.

> TXE and RXNE are described as "Set by hardware, cleared by software" in the reference manual TXE/RXNE/BSY behavior (image ai17343).

That's unfortunately misleading. That figure ought to say, "hardware receives the byte and that sets RXNE; then software reads DR and that clears RXNE".

But not only you do a redundant operation - that in itself wouldn't be such a problem - but you do it incorrectly. NEVER perform a Read-Modify-Write (RMW) on a register which is manipulated by hardware! Your problem is caused by the

UART4->SR &= ~(USART_SR_TC);

inadvertently clearing the RXNE bit, when the hardware sets it between the read and the write caused by that line.

JW

PS. Please change your username to a normal nick.