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 );

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

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

Eugene Sh. 13.09.2018 20:28

Вы уверены, что он застрял в вашем обработчике прерывания и не вызывается повторно, потому что вы немного не очищаете UART?

Fiddling Bits 13.09.2018 20:32

Его действительно призывают снова и снова. Это видно по миганию светодиода.

Elmore 13.09.2018 20:34

Задержка LL_mDelay(130); в обработчике прерывания? Хм, сомнительный подход. Задержки в сумме составляют 450. Если это 450 мс, этого хватит только на очень медленную скорость передачи.

chux - Reinstate Monica 13.09.2018 20:36

user2666497, Если удалить все LL_mDelay(), код все равно "никогда не останавливается и крутится в обработчике прерываний"?

chux - Reinstate Monica 13.09.2018 20:41

Очевидно, это просто для того, чтобы все заработало. Никакая разумная передача данных не может иметь задержек внутри обработчика прерывания.

Elmore 13.09.2018 20:42

Хорошо. Я потратил 3 минуты на то, чтобы сделать разумную вещь, и теперь прерывание превращает глобальную переменную в 1, а затем мы быстрее моргаем в основном цикле и возвращаем ее обратно в 0. Теперь, похоже, все работает, как задумано, без необходимости ручного сброса флагов. Кажется, что задержки действительно так или иначе были причиной этого. Почему это могло быть?

Elmore 13.09.2018 20:43

Как инициализировать USART1?

KamilCuk 13.09.2018 20:44

USART инициализируется стандартной функцией MX_USART1_UART_Init (); который, кажется, работает нормально, за исключением необходимости настраивать скорость передачи данных для моего варианта использования ... Теперь я могу получать и зеркально отображать данные.

Elmore 13.09.2018 20:47

Примечание. Лучше вызвать в LL_USART_IsActiveFlag_RXNE() и проверить, действительно ли данные доступны, прежде чем звонить в LL_USART_ReceiveData8(USART1); LL_USART_TransmitData8. Я подозреваю, что код застрял, потому что вялый обработчик с задержками вызывал ошибку для установки (Overrun), и обработчик никогда не очищает ошибки.

chux - Reinstate Monica 13.09.2018 20:51

chux: Интересно. Несмотря на то, что теперь он работает, означает ли это, что даже если я не включаю другие прерывания, кроме RXNE, ошибки все равно запускают обработчик, и, следовательно, ЛЮБОЙ обработчик, который не сбрасывает флаги ошибок, приведет к застреванию в бесконечном цикле? Это кажется чрезвычайно опасной перспективой.

Elmore 13.09.2018 21:01

Что ж, вам определенно нужно сбросить флаг прерывания, иначе прерывание просто сработает снова. Возможно, эти строки: //LL_USART_ClearFlag_RXNE(USART1)//NVIC_ClearPendingIRQ( USART1_IRQn ); неверны или, по крайней мере, по какой-то причине ведут себя не так, как ожидалось.

bigwillydos 13.09.2018 21:02

Что же делает LL_mDelay ()? Если он выполняет системный вызов для приостановки вызывающего потока на некоторый интервал, вы не должны использовать его в обработчике прерывания.

Martin James 13.09.2018 21:26

Мартин Джеймс: Платформа представляет собой микроконтроллер STM32, ниже нет системы, или, по крайней мере, насколько я понимаю, прерывания - это просто специальные вызовы функций поверх стека.

Elmore 13.09.2018 21:35

@MartinJames это строка 199, кажется, указывает, что он не выполняет системный вызов, а читает регистр

Ajay Brahmakshatriya 13.09.2018 21:36
1
15
2 084
2

Ответы 2

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

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

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

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

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

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

Другие вопросы по теме