Electrical – STM32F7: activate USART Receiver Timeout Interrupt (USART_RTOR.RTOF)

interruptsstm32f7uart

I want the STM32F769NI to generate an interrupt when a pause after a received byte on USART6 was detected.

I cofigured USART6 with 9600 baud in asynchronous mode and enabled the receiver interrupt RXNE and the global USART6 Interrupt. So far this is working, on USART6 interrupt I can read the correct byte from USART_RDR of USART6.

I found the RTO interrupt in the reference manual. It says, the RTOF flag is set if no start bit is set for the number of bit times given in USART.RTOR.RTO. And the USART interrupt is generated if the RTOIE flag is set. I want to get an interrupt 100 ms after the last byte was received, so I configured:

// Set RTOR.RTO = 960 (0,1s * 9600baud = 960):
MODIFY_REG(USART6->RTOR, USART_RTOR_RTO, 960);

// Reset RTOF flag:
WRITE_REG(USART6->ICR, USART_ICR_RTOCF);

// Set RTOIE (enable RTO interrupt):
SET_BIT(USARTx->CR1, USART_CR1_RTOIE);

But the USART6 Interrupt never gets executed with the RTOF flag set.

The USART6 interrupt vector is executed if a byte was received (with the RXNE flag set). I expect the same interrupt to be executed if the timeout happens after the reception of one byte (with the RTOF flag set), but this does not happen. I tried to set a LED on, set breakpoints in the debugger, but after sending one byte to USART6, the interrupt gets executed only once and only with the RXNE flag set. I also tried to set the RTO value in the interrupt handler after the reception of one byte but this changes nothing. I also tried to set RTO = 1, also without result.

Is there something I am missing to configure? Do I need to set RTOR.BLEN (Block Length) to some value? But this is only for SmartCard mode.

Best Answer

I simply missed the USART_CR2_RTOEN flag. Only the USART_CR1_RTOIE flag is mentioned in the manual as necessary for the USART_ISR_RTOF to trigger an interrupt but USART_CR2_RTOEN is necessary to first generate the USART_ISR_RTOF flag.

My initialization now looks like this:

// Set RTOR.RTO = 960 (0,1s * 9600baud = 960):
MODIFY_REG(USART6->RTOR, USART_RTOR_RTO, 960);

// Set CR2.RTOEN (enable Receiver Timeout):
SET_BIT(USARTx->CR2, USART_CR2_RTOEN); // <- this was missing

// Reset RTOF flag:
WRITE_REG(USART6->ICR, USART_ICR_RTOCF);

// Set RTOIE (enable RTO interrupt):
SET_BIT(USARTx->CR1, USART_CR1_RTOIE);

Note: For some strange reasons, the USART_ISR_RTOF receiver timeout flag is set directly after the reception of one byte without a timeout together with the USART_ISR_RXNE interrupt flag. So you need to reset the USART_ISR_RTOF flag at the reception of every byte to not get an interrupt pending directly after the RXNE interrupt. My USART6 interrupt routine now looks like this (with the usage of the ST low level drivers):

void USART6_IRQHandler(void)
{
  if (LL_USART_IsActiveFlag_RXNE(USART6)) // USART6.ISR.RXNE set => Byte received
  {
    // Byte in USART6 RX register

    // Reading USART_RDR register clears RXNE flag.
    // This reads the USART6.RDR[7:0] register and handles the data
    handleRXByte(LL_USART_ReceiveData8(USART6));
    LL_USART_ClearFlag_RTO(USART6); // for some reason this is already set
  }
  else if (LL_USART_IsActiveFlag_RTO(USART6)) // USART6.ISR.RTOF set
  {
    // RX inter-byte timeout

    // reset RTOF:
    LL_USART_ClearFlag_RTO(USART6);

    // handle timeout (reset receiver state):
    serialHandleRXTimeout();
  }
}