2

I'm implementing a simple UART receive-transmit protocol on STM32F103, the library/boilerplate code I'm using here is LL, not HAL (as HAL includes insane amounts of overhead)

My problem is that after successfully entering the interrupt handler "USART1_IRQHandler" it keeps cycling on forever. My code is here:

    void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
    int ii = 0;
    for(ii=0; ii<4;  ii++){
        LL_mDelay(40);
        LL_GPIO_TogglePin(GPIOA, LL_GPIO_PIN_5);
        LL_mDelay(40);
        LL_GPIO_TogglePin(GPIOA, LL_GPIO_PIN_5);

    }
    uint8_t cc = LL_USART_ReceiveData8(USART1);
    LL_USART_TransmitData8(USART1, cc);
    LL_mDelay(130);

    //LL_USART_ClearFlag_RXNE(USART1);
    //NVIC_ClearPendingIRQ( USART1_IRQn );

  /* USER CODE END USART1_IRQn 0 */
  /* USER CODE BEGIN USART1_IRQn 1 */
  /* USER CODE END USART1_IRQn 1 */
}

and in main.c I have:

LL_USART_EnableIT_RXNE(USART1);
  while (1)
  {

        LL_mDelay(300);
        LL_GPIO_TogglePin(GPIOA, LL_GPIO_PIN_5);
        LL_mDelay(300);
        LL_GPIO_TogglePin(GPIOA, LL_GPIO_PIN_5);

        //LL_USART_EnableIT_TC(USART1);

  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */

  }

The GPIO_Toggle commands are just there to blink an led so I know what's going on. Here's waht happens: When I power the MC up, it enters the main loop and blinks slowly. When I send something (~10 bytes) through UART, the led starts blinking fast, indicating that it has entered the interrupt handler. PProblem is that it never stops and keeps spinning around in the interrupt handler.

I've tried using the now commented functions

LL_USART_ClearFlag_RXNE(USART1);
NVIC_ClearPendingIRQ( USART1_IRQn );

either alone or in combination but they have absolutely no effect on anything. What am I doing wrong? How can I exit the handler?

markalex
  • 8,623
  • 2
  • 7
  • 32
Elmore
  • 256
  • 3
  • 7
  • 6
    There is too much going on inside your interrupt handler. These should be as short as possible. And surely no delays. – Eugene Sh. Sep 13 '18 at 18:28
  • 2
    Are you sure it's stuck in your interrupt handler and not repeatedly called again because you're not clearing a bit in the UART. – Fiddling Bits Sep 13 '18 at 18:32
  • It is indeed being called repeatedly again and again. This can be seen in the led blink. – Elmore Sep 13 '18 at 18:34
  • A delay `LL_mDelay(130);` in an interrupt handler? Hmm, questionable approach. Delays add up to 450. If that is 450 ms, that would only keep up with a very slow baud. – chux - Reinstate Monica Sep 13 '18 at 18:36
  • user2666497, If you remove all `LL_mDelay()`, does the code still "never stops and keeps spinning around in the interrupt handler"? – chux - Reinstate Monica Sep 13 '18 at 18:41
  • Well obviously this is just to get the thing working. No sensible data transfer can have delays inside interruopt handler. – Elmore Sep 13 '18 at 18:42
  • Okay. I took the 3 minutes to do the sensible thing and now the interrupt turns a global variable to 1 and then we blink faster in main loop and turn it back to 0. Now it seems to work as intended with no manual flag clearing needed. It seems that the delays indeed somehow caused this. Why might this be? – Elmore Sep 13 '18 at 18:43
  • How do you initialize USART1 ? – KamilCuk Sep 13 '18 at 18:44
  • USART is initialized by the boilerplate function MX_USART1_UART_Init(); which seems to work fine, save for the need to tweak the baud rate for my use case... I am able to receive and mirror stuff back now. – Elmore Sep 13 '18 at 18:47
  • Note: Better to call `LL_USART_IsActiveFlag_RXNE()` and test if data is truly available before calling `LL_USART_ReceiveData8(USART1); LL_USART_TransmitData8`. I suspect code was stuck because the sluggish handler with delays caused an error to set (Overrun) and the handler never clears errors. – chux - Reinstate Monica Sep 13 '18 at 18:51
  • chux: Interesting. Even though it now does work, does this imply that even if I don't enable other interrupts but the RXNE, the errors still do trigger the handler and thus ANY handler that does not clear error flags will cause be stuck in an endless loop? This seems like an extremely dangerous prospect. – Elmore Sep 13 '18 at 19:01
  • Well you definitely need to clear the interrupt flag, otherwise the interrupt will just fire again. Perhaps these lines: `//LL_USART_ClearFlag_RXNE(USART1)` `//NVIC_ClearPendingIRQ( USART1_IRQn );` Are incorrect or at least no behaving as expected for some reason. – bigwillydos Sep 13 '18 at 19:02
  • Well, what does 'LL_mDelay()' do? If it makes a system call to suspend the calling thread for some interval, then you must not use it in an interrupt handler. – Martin James Sep 13 '18 at 19:26
  • Martin James: The platform is an STM32 microcontroller, the is no system below, or at least to my understanding interrupts are just ad-hoc function calls on top of the stack. – Elmore Sep 13 '18 at 19:35
  • @MartinJames [this](https://os.mbed.com/users/EricLew/code/STM32L4xx_HAL_Driver/docs/tip/stm32l4xx__ll__utils_8h_source.html) line 199 seems to indicate that it doesn't make a system call but reads a register – Ajay Brahmakshatriya Sep 13 '18 at 19:36

2 Answers2

3

Actually everything in your USART interrupt handler is wrong.

  1. You do not check what has caused the interrupt. If it is RXNE flag you should just load the value from the DR register. You do not need to clear any flags. If it is TXE flag you can store the data to the DR register. You cant clear this flag any other way. If you do not have any data to send you need to disable the TXE interrupt. Otherwise it will trigger continuously.

You can't just read and write the data register when you want to. You need to know if you are allowed to

You should also control the error statuses.

  1. You must not use any delays in the interrupt routines. Keep it as fast as possible.
  2. Do not touch NVIC exept for enabling and disabling the interrupts, as for now you do not know what it is for.
0___________
  • 60,014
  • 4
  • 34
  • 74
1

The system time utilised for controlling delays is updated by a periodic sysTick interrupt. If the RXNE interrupt has a higher priority than the sysTick interrupt it won't be handled while you're inside your RXNE IRQ handler, so the time will never increment and your delay end time will never be reached. Depending on how your delay is implemented, it may just put the CPU in a spinlock that can never exit.

ajxs
  • 3,347
  • 2
  • 18
  • 33