Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Кармин Новиелло - Освоение STM32.pdf
Скачиваний:
2740
Добавлен:
23.09.2021
Размер:
47.68 Mб
Скачать

Обработка прерываний

179

Рисунок 2: Минимальная таблица векторов в микроконтроллере STM32 на базе ядра Cortex-M3/4/7

7.2. Разрешение прерываний

Когда микроконтроллер STM32 загружается, по умолчанию разрешены только исключения Reset, NMI и Hard Fault. Остальные исключения и периферийные прерывания запрещены, и они должны быть разрешены при возникновении запроса IRQ. Чтобы разрешить IRQ, CubeHAL предоставляет следующую функцию:

void HAL_NVIC_EnableIRQ(IRQn_Type IRQn);

где IRQn_Type – это перечисление всех исключений и прерываний, определенных для конкретного микроконтроллера. Перечисление IRQn_Type является частью HAL от ST и определяется в заголовочном файле, различном для конкретного микроконтроллера

STM32, в папке Eclipse system/include/cmsis/. Эти файлы названы stm32fxxxx.h. Напри-

мер, для микроконтроллера STM32F030R8 соответствующее имя файла – stm32f030x8.h (имя шаблона этих файлов совпадает с именем startup-файла).

Соответствующая функция для запрета IRQ:

void HAL_NVIC_DisableIRQ(IRQn_Type IRQn);

Обработка прерываний

180

Важно отметить, что две предыдущие функции разрешают/запрещают прерывание на уровне контроллера NVIC. Посмотрев на рисунок 1, вы можете увидеть, что линия прерывания утверждается периферийным устройством, подключенным к соответствующему этой линии выводу. Например, периферийное устройство USART2 устанавливает линию прерывания, которая соответствует линии прерывания USART2_IRQn внутри контроллера NVIC. Это означает, что каждое периферийное устройство должно быть правильно сконфигурировано для работы в режиме прерываний. Как мы увидим в оставшейся части данной книги, большинство периферийных устройств STM32 предназначены для работы, в том числе, в режиме прерываний. Используя специальные процедуры HAL, мы можем разрешить прерывание на периферийном уровне. Например, используя HAL_USART_Transmit_IT(), мы неявно конфигурируем периферийное устройство USART в режиме прерываний. Очевидно, что необходимо также разрешить соответствующее прерывание на уровне NVIC, вызывая HAL_NVIC_EnableIRQ().

Теперь самое время начать играть с прерываниями.

7.2.1. Линии запроса внешних прерываний и контроллер NVIC

Как мы видели на рисунке 1, микроконтроллеры STM32 предоставляют различное количество источников внешних прерываний, подключенных к контроллеру NVIC через контроллер EXTI, который, в свою очередь, способен управлять несколькими линиями запроса внешних прерываний контроллера EXTI (EXTI lines). Количество источников пре-

рываний и линий запроса внешних прерываний зависит от конкретного семейства

STM32.

GPIO подключены к линиям запроса прерываний контроллера EXTI, и прерывания можно разрешить для каждого GPIO микроконтроллера индивидуально, несмотря на то что большинство из них имеют одну и ту же линию запроса внешних прерываний. Например, для микроконтроллера STM32F4 до 114 GPIO подключены к 16 линиям запроса прерываний EXTI. Однако только 7 из этих линий имеют независимое прерывание, связанное с ними.

На рисунке 3 показаны линии EXTI 0, 10 и 15 микроконтроллера STM32F4. Все выводы Px0 подключены к EXTI0, все выводы Px10 подключены к EXTI10, а все выводы Px15 подключены к EXTI15. Однако линии EXTI 10 и 15 имеют один и тот же IRQ в контроллере NVIC (и, следовательно, обслуживаются одной и той же ISR)3.

Это означает, что:

Только один вывод PxY может быть источником прерывания. Например, мы не можем определить и PA0, и PB0 как входные выводы для прерывания.

Для линий прерываний EXTI, совместно использующих один и тот же IRQ в контроллере NVIC, нам необходимо кодировать соответствующую ISR, чтобы мы могли различать, какие линии генерировали прерывание.

3 Иногда также бывает, что разные периферийные устройства совместно используют одну и ту же линию запроса, даже в микроконтроллере на базе Cortex-M3/4/7, где доступно до 240 конфигурируемых линий запроса. Например, в микроконтроллере STM32F446RE таймер TIM6 совместно использует свой глобальный IRQ с прерываниями от DAC1 и DAC2 при ошибке из-за неполного завершения преобразования

(under-run error interrupts).

Обработка прерываний

181

Рисунок 3: Соотношение между GPIO, линиями запроса прерываний EXTI и соответствующей ISR

вмикроконтроллере STM32F4

Вследующем примере4 показано, как использовать прерывания для переключения светодиода LD2 при каждом нажатии программируемой пользовательской кнопки, которая подключена к выводу PC13. Сначала мы конфигурируем на выводе GPIO PC13 срабатывание прерывания каждый раз, когда он переходит с низкого уровня на высокий (строки 49:52). Это достигается установкой .Mode GPIO равным GPIO_MODE_IT_RISING (полный список доступных режимов, связанных с прерываниями, см. в таблице 2 в Главе 6). Затем мы разрешаем прерывание от линии EXTI, связанной с выводами Px13, то есть

EXTI15_10_IRQn.

Имя файла: src/main-ex1.c

39int main(void) {

40GPIO_InitTypeDef GPIO_InitStruct;

42 HAL_Init();

43

44/* Разрешение тактирования портов GPIO */

45__HAL_RCC_GPIOC_CLK_ENABLE();

46__HAL_RCC_GPIOA_CLK_ENABLE();

47

48/* Конфигурирование вывода GPIO : PC13 - USER BUTTON */

49GPIO_InitStruct.Pin = GPIO_PIN_13;

4 Пример предназначен для работы с платой Nucleo-F401RE. Пожалуйста, обратитесь к другим примерам книги, если у вас другая плата Nucleo.

Обработка прерываний

182

50GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;

51GPIO_InitStruct.Pull = GPIO_PULLDOWN;

52HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

53

54/* Конфигурирование вывода GPIO : PA5 – светодиод LD2 */

55GPIO_InitStruct.Pin = GPIO_PIN_5;

56GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;

57GPIO_InitStruct.Pull = GPIO_NOPULL;

58GPIO_InitStruct.Speed = GPIO_SPEED_LOW;

59HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

60

61 HAL_NVIC_EnableIRQ(EXTI15_10_IRQn); 62

63while(1);

64}

65

66void EXTI15_10_IRQHandler(void) {

67__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_13);

68HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);

69}

Наконец, нам нужно определить функцию void EXTI15_10_IRQHandler()5, которая является процедурой ISR, связанной с IRQ для линии EXTI15_10 в таблице векторов (строки 66:69). Содержание ISR достаточно простое. Мы переключаем вывод PA5 каждый раз, когда запускается ISR. Нам также необходимо сбросить бит отложенного состояния, связанный с линией EXTI (подробнее об этом далее).

К счастью, HAL от ST предоставляет механизм абстрагирования, который не позволяет нам иметь дело со всеми этими подробностями, если мы действительно не должны думать о них. Предыдущий пример можно переписать следующим образом:

Имя файла: src/main-ex2.c

48/* Конфигурирование выводов GPIO : PC12 и PC13 */

49GPIO_InitStruct.Pin = GPIO_PIN_13 | GPIO_PIN_12;

50GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;

51GPIO_InitStruct.Pull = GPIO_PULLDOWN;

52HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

53

54/* Конфигурирование вывода GPIO : PA5 – светодиод LD2 */

55GPIO_InitStruct.Pin = GPIO_PIN_5;

56GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;

57GPIO_InitStruct.Pull = GPIO_NOPULL;

58GPIO_InitStruct.Speed = GPIO_SPEED_LOW;

59HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

5 Еще одна особенность архитектур ARM – возможность использовать обычные функции Си в качестве ISR. Когда срабатывает прерывание, ЦПУ переключается из Режима потока, англ. Threaded mode (то есть основного потока выполнения) в Режим обработчика (Handler mode). Во время данного процесса переключения текущий контекст выполнения сохраняется благодаря процедуре, которая называется загрузка в стек (stacking). ЦПУ сам отвечает за хранение предыдущего сохраненного контекста, когда ISR прекращает свое выполнение (извлечение из стека, unstacking). Объяснение этой процедуры выходит за рамки данной книги. Для получения дополнительной информации об этих аспектах обратитесь к книге Джозефа Ю.

GPIO_Pin

Обработка прерываний

183

60

61 HAL_NVIC_EnableIRQ(EXTI15_10_IRQn); 62

63while(1);

64}

65

66void EXTI15_10_IRQHandler(void) {

67HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_12);

68HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13);

69}

70

71void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {

72if(GPIO_Pin == GPIO_PIN_13)

73HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);

74else if(GPIO_Pin == GPIO_PIN_12)

75HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, RESET);

76}

На этот раз мы сконфигурировали в качестве источника прерывания два вывода PC13 и PC12. Когда вызывается ISR EXTI15_10_IRQHandler(), мы передаем управление функции HAL_GPIO_EXTI_IRQHandler() внутри HAL. Она выполнит для нас все действия, связанные с прерываниями, и вызовет процедуру обратного вызова HAL_GPIO_EXTI_Callback(), передающую GPIO, сгенерировавший IRQ. На рисунке 4 четко показана последовательность вызовов, которая генерируется из IRQ6.

Рисунок 4: Как IRQ обрабатывается HAL

Этот механизм используется почти всеми процедурами IRQ внутри HAL.

Обратите внимание, что, поскольку линии прерываний EXTI12 и EXTI13 подключены к одному и тому же IRQ, в нашем коде необходимо различать, какой из двух выводов сгенерировал прерывание. Эту работу выполняет за нас HAL, передавая параметр

при вызове функции обратного вызова.

6 Не учитывайте данные временные интервалы, связанные с тактовыми циклами ЦПУ, они просто используются для обозначения «последующих» событий.