Как сбросить состояние драйвера STM32 HAL UART (HAL)?

Я знаю, что можно включить прерывание приема UART, используя

HAL_UART_Receive_IT(&huart2, (uint8_t *)rx_buffer, expectedNumberOfBytes)
  • Но однажды начавшись, как это остановить «вручную»?

Мы можем отключить прерывание UART, используя HAL_NVIC_DisableIRQ() (например: HAL_NVIC_DisableIRQ(USART1_IRQn)). Это предотвратит возникновение прерывания, но состояние, установленное функцией HAL_UART_Receive_IT, которое равно HAL_UART_STATE_BUSY_RX, должно быть установлено обратно в HAL_UART_STATE_READY, чтобы дескриптор uart вернулся в состояние, которое может принять новый вызов HAL_UART_Receive_IT().

Вопрос
Как мне сбросить состояние прерывания UART, если я хочу отключить прерывание Rx через некоторое время?

Вопросы о переполнении стека не касаются того, как сбросить состояние; Я обратился к этим вопросам:

  1. Отключение прерывания в обработчике прерываний STM32F407
  2. https://electronics.stackexchange.com/questions/100073/stm32-usart-rx-interrupts < / а>

Я мог бы использовать USART_ClearITPendingBit() или USART_ITConfig(), но они определены как частные функции библиотекой STM HAL. Так я должен их использовать?


person clmno    schedule 28.03.2019    source источник
comment
Вероятно, вам следует использовать более четкую терминологию. HAL_UART_Receive_IT - это функция драйвера, а не обработчик прерывания. Он устанавливает состояние для принимающего драйвера. Периферийное устройство UART имеет собственное состояние прерывания, но оно отличается от состояния в программном драйвере, на котором, похоже, вы застряли. Итак, я думаю, вы спрашиваете, как мне сбросить состояние драйвера STM32 HAL UART? Если вы имеете в виду не это, и вы хотите сбросить состояние периферийного устройства (оборудование UART) или хотите сбросить состояние обработки прерываний (оборудование NVIC), отредактируйте свой вопрос, чтобы прояснить это.   -  person Ben Voigt    schedule 29.03.2019


Ответы (4)


Как [мне] сбросить состояние прерывания UART, если [я] хочу отключить прерывание Rx через некоторое время [?]

(См. Его использование, например, в "stm32f4xx_hal_uart.c".)

Член huart->RxState структуры дескриптора uart на самом деле используется только внутренне библиотекой HAL при выполнении таких вещей, как HAL_UART_Receive(), HAL_UART_Receive_IT(), HAL_UART_Receive_DMA() (и многих других подобных внутренних функций) и т. Д. вызовы UART Tx и Rx на основе кольцевого буфера, однако, который является предпочтительным способом сделать это, этот член совершенно бессмысленен, и не имеет значения, что вы с ним делаете, поскольку он используется только внутри вызовов функций библиотеки HAL и обработчиков HAL ISR (ни один из которых вам не нужно использовать), и на самом деле не имеет ничего общего с прерываниями на уровне регистров и прочим напрямую.

Однако, покопавшись в исходном коде в stm32f4xx_hal_uart.c (например), вы можете использовать несколько допустимых вариантов:

1. Как сбросить huart->RxState на HAL_UART_STATE_READY:

  1. Звоните HAL_UART_Init(). Изучив его исходный код, вы увидите, что перед возвратом он вызывает huart->RxState= HAL_UART_STATE_READY;.
  2. Просто установите huart->RxState = HAL_UART_STATE_READY; вручную. Пока вы знаете, что правильно остановили прием на основе прерывания в середине его обработки, это совершенно верно.

Однако давайте продолжим.

Представьте, что вы используете UART7 на STM32F4. Следовательно, в файле обработчика прерывания stm32f4xx_it.c вы увидите следующий код, автоматически сгенерированный STM32CubeMX:

/**
* @brief This function handles UART7 global interrupt.
*/
void UART7_IRQHandler(void)
{
  /* USER CODE BEGIN UART7_IRQn 0 */

  /* USER CODE END UART7_IRQn 0 */
  HAL_UART_IRQHandler(&huart7);
  /* USER CODE BEGIN UART7_IRQn 1 */

  /* USER CODE END UART7_IRQn 1 */
}

Давайте рассмотрим несколько уровней отключения / включения прерываний.

2. От самого широкого до самого узкого, вот несколько способов отключить / включить прерывание USART Rx:

  1. Вы можете отключить / включить ВСЕ прерывания, включая это UART7_IRQHandler(), с помощью этих вызовов CMSIS ядра ARM:

    __disable_irq();
    __enable_irq();
    

    Источник: https://stm32f4-discovery.net/2015/06/how-to-properly-enabledisable-interrupts-in-arm-cortex-m/

    Итак, вы можете сделать следующее, чтобы отключить прерывание, сбросить RxState, а затем снова запустить прием на основе прерываний, когда будете готовы:

    __disable_irq();
    huart7->RxState= HAL_UART_STATE_READY;
    
    __enable_irq();
    HAL_UART_Receive_IT(&huart7, (uint8_t *)rx_buffer, expectedNumberOfBytes);
    
  2. Вы можете отключить / включить ТОЛЬКО UART7_IRQHandler() прерывания (все 10 типов прерываний uart7, связанных с этим вектором прерываний, включая связанные с Tx, Rx, связанные с ошибками и т. Д.), Используя эти вызовы STM32 HAL:

    HAL_NVIC_DisableIRQ(UART7_IRQn);
    HAL_NVIC_EnableIRQ(UART7_IRQn);
    

    Затем сделайте то же самое, что и выше, за исключением того, что вместо этого используйте эти вызовы для отключения / включения прерываний.

  3. Однако, если вы углубитесь в реализацию HAL_UART_IRQHandler(), которая вызывается UART7_IRQHandler(), вы увидите, что она вызывает обработчик приема на основе прерываний, UART_Receive_IT(), только если оба бита USART_SR_RXNE ("Receive Not Empty", внутри USART Регистр состояния) и бит USART_CR1_RXNEIE («Разрешение прерывания непустого приема» внутри регистра управления USART 1) оба установлены. Бит RXNE устанавливается всякий раз, когда поступает байт, и сбрасывается, когда вы читаете регистр данных или записываете в него ноль. Бит разрешения прерывания - это то, что вы можете полностью контролировать, чтобы отключить это прерывание приема UART, и если вы сбросите этот бит вручную, вы отключите прерывание приема с отключением OUT любого другого типа прерывания, связанного с этим USART. Это лучший способ сделать это, поскольку с этим UART связано 10 источников прерываний. Другими словами, сброс этого бита не только приводит к сбою проверки внутри HAL_UART_IRQHandler(), но также предотвращает прерывание приема. в первую очередь! См. Справочное руководство RM0090 Ред. 16, например:

    p969:  введите здесь описание изображения

    p1009:  введите здесь описание изображения

    p1011:  введите здесь описание изображения

    p1015:  введите здесь описание изображения

    p1013:  введите здесь описание изображения

    Итак, чтобы отключить / включить только прерывание USART «Прием не пусто», выполните следующие действия. См. регистр управления (USART_CR1) на p1013, показанном чуть выше.

    // Disable the USART Receive Not Empty interrupt
    CLEAR_BIT(huart7.Instance.CR1, USART_CR1_RXNEIE);
    
    // Enable the USART Receive Not Empty interrupt
    SET_BIT(huart7.Instance.CR1, USART_CR1_RXNEIE);
    

    Теперь вы можете сделать следующее, чтобы отключить прерывание приема USART, сбросить HAL RxState, а затем снова запустить прием на основе прерываний, когда он будет готов:

    CLEAR_BIT(huart7.Instance.CR1, USART_CR1_RXNEIE);
    huart7->RxState= HAL_UART_STATE_READY;
    
    SET_BIT(huart7.Instance.CR1, USART_CR1_RXNEIE); // This call isn't actually necessary, as this bit is set inside `HAL_UART_Receive_IT()` as well
    HAL_UART_Receive_IT(&huart7, (uint8_t *)rx_buffer, expectedNumberOfBytes);
    

3. Как (неудобно) использовать HAL_UART_Receive_IT() для непрерывного приема на основе прерываний.

ДЕЛАТЬ

4. Почему HAL_UART_Receive_IT() в конце концов не очень полезная функция.

ДЕЛАТЬ

5. Как вручную настроить собственные ISR и функции UART Tx и Rx на основе прерываний.

ДЕЛАТЬ

person Gabriel Staples    schedule 29.03.2019
comment
Мне нравится, как вы думаете. ИМО, драйверы HAL - это неуклюжий недокументированный слой, обертывающий хорошо документированное оборудование. Какие бы преимущества переносимости и повторного использования ни существовали при использовании библиотеки поставщика, они полностью сводятся на нет из-за отсутствия документации и скрытых взаимодействий с периферийными устройствами, отличными от того, для которого предназначен драйвер. Вы не можете надеяться понять и использовать уровень HAL, не внимательно прочитав руководство по интерфейсу уровня регистров, и как только вы это сделаете, вам больше не понадобится HAL. - person Ben Voigt; 29.03.2019
comment
Вау! Спасибо, что нашли время ответить. Итак, как вы сказали, Status - это просто HAL flag и мало что делает с оборудованием. Я проверю ваше предложение 2 и вернусь. И, пожалуйста, напишите 3,4 и 5, когда у вас будет время. Спасибо еще раз. - person clmno; 29.03.2019
comment
@BenVoigt, да, в том, что ты говоришь, определенно много правды. Слой HAL может быть полезен, но я думаю, он мог бы быть намного лучше, и вы абсолютно правы в том, что вам нужно копать, копать и копать, чтобы понять, что он на самом деле делает. Даже зная, как правильно его использовать, нужно посмотреть пример кода, а затем также прочитать Справочные руководства STM32 и просмотреть исходный код HAL, поскольку его использование и структура не соответствуют К сожалению, это очень хорошо задокументировано. - person Gabriel Staples; 02.05.2019
comment
Жду тех TODOS;) - person Rainb; 24.04.2021

Большинство UART сбрасывают любые ожидающие прерывания приема, когда программа читает из регистра временного хранения. Итак, мой ответ был бы: просто прочтите регистр данных после отключения прерываний и проигнорируйте результат.

У меня еще не было возможности попробовать это на моем STM32, но ...

person John Burger    schedule 28.03.2019
comment
Как мне читать из регистра данных? В fn HAL_UART_Receive данные читаются из huart->Instance->DR. Я попробовал читать по нему. Но huart->State не сбрасывается никем / ничем, пришлось сбросить вручную. Это правильный подход? (Он работает, как и следующий Rx_interrupt работает должным образом.) - person clmno; 28.03.2019
comment
Обновление переменной huart->State вручную кажется неприятным, но альтернатива была бы хуже. Если вы прочитаете регистр данных перед отключением прерываний, тогда переменная State (вероятно) будет обновлена, но новый символ может появиться до того, как произойдет отключение, что приведет к вы вернетесь в исходное состояние. - person John Burger; 28.03.2019

Вы можете использовать HAL_UART_Abort_IT.

person storm    schedule 19.12.2019
comment
Привет. Не могли бы вы расширить свой ответ? - person mjuarez; 19.12.2019

В библиотеке HAL есть функция static void UART_EndRxTransfer(UART_HandleTypeDef *huart), которая выполняет следующие действия:

  • Отключить прерывания RXNE, PE и ERR
  • восстановить huart->RxState в состояние "Готов"

Я нашел эту функцию в файле stm32f7xx_hal_uart.c. Однако он определен как static, поэтому я просто скопировал определение в файл, где я его использовал. Это может быть немного взломано, но у меня это сработало.

person I.Jaeger    schedule 02.02.2021