STM32: невозможно выйти из обработчика прерывания для прерывания UART

Я реализую простой протокол приема-передачи UART на STM32F103, библиотечный / шаблонный код, который я использую здесь, - это LL, а не HAL (поскольку HAL включает в себя безумные накладные расходы)

Моя проблема в том, что после успешного ввода обработчика прерывания «USART1_IRQHandler» он продолжает работать вечно. Мой код здесь:

    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 */
}

и в main.c у меня есть:

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 */

  }

Команды GPIO_Toggle нужны только для того, чтобы мигать светодиодом, чтобы я знал, что происходит. Вот что происходит: когда я включаю MC, он входит в основной цикл и медленно мигает. Когда я отправляю что-то (~ 10 байт) через UART, светодиод начинает быстро мигать, указывая на то, что он вошел в обработчик прерывания. Проблема в том, что он никогда не останавливается и продолжает вращаться в обработчике прерывания.

Я пробовал использовать прокомментированные функции

LL_USART_ClearFlag_RXNE(USART1);
NVIC_ClearPendingIRQ( USART1_IRQn );

либо по отдельности, либо в комбинации, но они абсолютно ни на что не влияют. Что я делаю неправильно? Как мне выйти из обработчика?


person Elmore    schedule 13.09.2018    source источник
comment
В вашем обработчике прерываний слишком много всего происходит. Они должны быть как можно короче. И уж точно никаких задержек.   -  person Eugene Sh.    schedule 13.09.2018
comment
Вы уверены, что он застрял в обработчике прерывания и не вызывается повторно, потому что вы немного не очищаете UART?   -  person Fiddling Bits    schedule 13.09.2018
comment
Его действительно призывают снова и снова. Это видно по миганию светодиода.   -  person Elmore    schedule 13.09.2018
comment
Задержка LL_mDelay(130); в обработчике прерывания? Хм, сомнительный подход. Задержки в сумме составляют 450. Если это 450 мс, этого хватит только на очень медленную скорость передачи.   -  person chux - Reinstate Monica    schedule 13.09.2018
comment
user2666497, Если убрать все LL_mDelay(), код все равно никогда не останавливается и продолжает крутиться в обработчике прерываний?   -  person chux - Reinstate Monica    schedule 13.09.2018
comment
Очевидно, это просто для того, чтобы все заработало. Никакая разумная передача данных не может иметь задержек внутри обработчика прерывания.   -  person Elmore    schedule 13.09.2018
comment
Хорошо. Я потратил 3 минуты на то, чтобы сделать разумную вещь, и теперь прерывание превращает глобальную переменную в 1, а затем мы быстрее моргаем в основном цикле и возвращаем ее обратно в 0. Теперь, похоже, все работает, как задумано, без необходимости ручного сброса флагов. Кажется, что задержки действительно так или иначе были причиной этого. Почему это могло быть?   -  person Elmore    schedule 13.09.2018
comment
Как инициализировать USART1?   -  person KamilCuk    schedule 13.09.2018
comment
USART инициализируется стандартной функцией MX_USART1_UART_Init (); который, кажется, работает нормально, за исключением необходимости настраивать скорость передачи данных для моего варианта использования ... Теперь я могу получать и зеркально отображать данные.   -  person Elmore    schedule 13.09.2018
comment
Примечание. Лучше позвонить LL_USART_IsActiveFlag_RXNE() и проверить, действительно ли данные доступны, прежде чем вызывать LL_USART_ReceiveData8(USART1); LL_USART_TransmitData8. Я подозреваю, что код застрял, потому что вялый обработчик с задержками вызывал ошибку для установки (Overrun), и обработчик никогда не очищает ошибки.   -  person chux - Reinstate Monica    schedule 13.09.2018
comment
chux: Интересно. Несмотря на то, что теперь он работает, означает ли это, что даже если я не включаю другие прерывания, кроме RXNE, ошибки все равно запускают обработчик, и, следовательно, ЛЮБОЙ обработчик, который не сбрасывает флаги ошибок, приведет к застреванию в бесконечном цикле? Это кажется чрезвычайно опасной перспективой.   -  person Elmore    schedule 13.09.2018
comment
Что ж, вам определенно нужно сбросить флаг прерывания, иначе прерывание просто сработает снова. Возможно, эти строки: //LL_USART_ClearFlag_RXNE(USART1) //NVIC_ClearPendingIRQ( USART1_IRQn ); Неправильны или, по крайней мере, по какой-то причине ведут себя не так, как ожидалось.   -  person bigwillydos    schedule 13.09.2018
comment
Что же делает LL_mDelay ()? Если он выполняет системный вызов для приостановки вызывающего потока на некоторый интервал, вы не должны использовать его в обработчике прерывания.   -  person Martin James    schedule 13.09.2018
comment
Мартин Джеймс: Платформа представляет собой микроконтроллер STM32, ниже нет системы, или, по крайней мере, насколько я понимаю, прерывания - это просто специальные вызовы функций поверх стека.   -  person Elmore    schedule 13.09.2018
comment
@MartinJames эта строка 199, похоже, указывает что он не выполняет системный вызов, а читает регистр   -  person Ajay Brahmakshatriya    schedule 13.09.2018


Ответы (2)


На самом деле все в вашем обработчике прерывания USART неверно.

  1. Вы не проверяете, что вызвало прерывание. Если это флаг RXNE, вы должны просто загрузить значение из регистра DR. Вам не нужно снимать флажки. Если это флаг TXE, вы можете сохранить данные в регистре DR. Другим способом сбросить этот флаг нельзя. Если у вас нет данных для отправки, вам необходимо отключить прерывание TXE. В противном случае он будет срабатывать постоянно.

Вы не можете просто читать и записывать регистр данных, когда хотите. Вам нужно знать, разрешено ли вам

Вы также должны контролировать статусы ошибок.

  1. Вы не должны использовать какие-либо задержки в процедурах прерывания. Делайте это как можно быстрее.
  2. Не трогайте NVIC, кроме включения и отключения прерываний, так как пока вы не знаете, для чего он нужен.
person 0___________    schedule 13.09.2018

Системное время, используемое для управления задержками, обновляется периодическим прерыванием sysTick. Если прерывание RXNE имеет более высокий приоритет, чем прерывание sysTick, оно не будет обрабатываться, пока вы находитесь внутри обработчика RXNE IRQ, поэтому время никогда не будет увеличиваться и время окончания задержки никогда не будет достигнуто. В зависимости от того, как реализована ваша задержка, она может просто поставить ЦП в спин-блокировку, которая никогда не сможет выйти.

person ajxs    schedule 23.06.2019