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

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

201

7.5. Реентерабельность прерываний

Предположим, мы изменили Пример 3 так, чтобы он использовал вывод PC12 вместо PB2. В этом случае, поскольку EXTI12 и EXTI13 используют один и тот же IRQ, наша Nucleo никогда не перестанет мигать. Из-за того, что в процессорах Cortex-M реализован механизм приоритетов (то есть исключение с заданным приоритетом не может быть вытеснено другим с таким же приоритетом), исключения и прерывания не являются реентерабельными (not re-entrant)17. Поэтому они не могут быть вызваны рекурсивно18.

Тем не менее, в большинстве случаев наш код может быть перестроен для устранения этого ограничения. В следующем примере19 мигающий код выполняется внутри функции main(), оставляя ISR ответственность только за конфигурирование глобальной переменной blink.

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

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

51GPIO_InitStruct.Pin = GPIO_PIN_12 | GPIO_PIN_13;

52GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;

53GPIO_InitStruct.Pull = GPIO_PULLDOWN;

54HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

55

56/* Конфигурирование вывода GPIO : PA5 */

57GPIO_InitStruct.Pin = GPIO_PIN_5;

58GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;

59GPIO_InitStruct.Pull = GPIO_NOPULL;

60GPIO_InitStruct.Speed = GPIO_SPEED_LOW;

61HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

62

63HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_1);

64HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);

65HAL_NVIC_SetPriority(EXTI15_10_IRQn, 0x0, 0);

66

67while(1) {

68if(blink) {

69HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);

70for(int i = 0; i < 100000; i++);

71}

72}

73}

74

75void EXTI15_10_IRQHandler(void) {

76HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_12);

77HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13);

78}

17 Реентерабельность – свойство процедуры, позволяющее ее совместное использование несколькими процессами без возникновения ошибки в работе, т.е. позволяющее повторный вход в нее. (прим. переводчика)

18Джозеф Ю показывает способ обойти данное ограничение в своих книгах. Однако я настоятельно не рекомендую использовать эти хитрые приемы, если вам действительно не нужна реентерабельность прерываний в вашем приложении.

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

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

202

79

80void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {

81if(GPIO_Pin == GPIO_PIN_13)

82blink = 1;

83else

84blink = 0;

85}

7.6. Разовое маскирование всех прерываний

или на приоритетной основе

Иногда мы хотим быть уверены, что в нашем коде нет вытеснений, позволяющих выполнять прерывания или более привилегированный код. То есть мы хотим убедиться, что наш код является потокобезопасным (thread-safe). Процессоры на базе Cortex-M позволяют временно маскировать выполнение всех прерываний и исключений, не запрещая их последовательно одно за другим. Два специальных регистра, называемые PRIMASK и FAULTMASK, позволяют разом запрещать все прерывания и исключения соответственно.

Рисунок 22: Регистры PRIMASK, FAULTMASK и BASEPRI

Несмотря на то, что эти регистры размером 32 бита, только первый бит используется для разрешения/запрета прерываний и исключений. Инструкция CPSID i ассемблера ARM запрещает все прерывания, устанавливая бит PRIMASK в 1, в то время как инструкция CPSIE i разрешает их, устанавливая PRIMASK в 0. Напротив, инструкция CPSID f запрещает все исключения (кроме NMI), устанавливая бит FAULTMASK в 1, в то время как инструкция CPSIE f разрешает их.

Пакет CMSIS-Core предоставляет несколько макросов, которые мы можем использовать для выполнения данных операций: __disable_irq() и __enable_irq() автоматически устанавливают и сбрасывают PRIMASK. Любая критическая задача может быть помещена между этими двумя макросами, как показано ниже:

...

__disable_irq();

/* Все исключения с конфигурируемым приоритетом временно запрещены. Вы можете поместить сюда критический код */

...

__enable_irq();

Однако имейте в виду, что, как правило, прерывание должно быть маскировано только на очень короткое время, иначе вы можете потерять важные прерывания. Помните, что прерывания не ставятся в очередь.

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

203

Другим макросом, которым мы можем воспользоваться, является __set_PRIMASK(x), где x

– это содержимое регистра PRIMASK (0 или 1). Макрос __get_PRIMARK() возвращает содер-

жимое регистра PRIMASK. Напротив, макросы __set_FAULTMASK(x) и __get_FAULTMASK() поз-

воляют манипулировать регистром FAULTMASK.

Важно отметить, что, как только регистр PRIMASK снова устанавливается в 0, все отложенные прерывания обслуживаются в соответствии с их приоритетом: PRIMASK приводит к тому, что устанавливается бит отложенного состояния прерывания, но его ISR не обслуживается. Вот почему мы говорим, что прерывания маскируются, а не запрещаются. Прерывания начинают обслуживаться, как только сбрасывается PRIMASK.

Ядра Cortex-M3/4/7 позволяют выборочно маскировать прерывания на приоритетной основе. Регистр BASEPRI маскирует исключения или прерывания на уровне приоритета. Размер регистра BASEPRI такой же, как и у IPR, который занимает 4 старших бита в микроконтроллере STM32 на базе данных ядер. Когда BASEPRI установлен в 0, он запрещен. Когда ему присваивается ненулевое значение, он блокирует исключения (включая прерывания), которые имеют такой же или более низкий уровень приоритета, и в то же время разрешает процессору принимать исключения с более высоким уровнем приоритета. Например, если регистр BASEPRI установлен в 0x60, то все прерывания с приоритетом между 0x60-0xFF запрещаются. Помните, что в ядрах Cortex-M чем выше приоритет, тем ниже уровень приоритета прерывания. Макрос __set_BASEPRI(x) позволяет установить содержимое регистра BASEPRI: помните, опять же, что HAL автоматически смещает уровни приоритета в MSB-биты (старшие значащие биты, англ. Most Significant Bit). Итак, если мы хотим запретить все прерывания с приоритетом выше 2, то мы должны передать макросу __set_BASEPRI() значение 0x20. В качестве альтернативы мы можем использовать следующий код:

__set_BASEPRI(2 << (8 - __NVIC_PRIO_BITS));

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

204

Интермеццо Eclipse

При написании кода для каждого разработчика важна производительность. Современные редакторы исходного кода позволяют определять пользовательские фрагменты кода (snippets), то есть фрагменты исходного кода, которые автоматически вставляются редактором при вводе определенного «ключевого слова (keyword)». Eclipse называет данную функцию «шаблонами кода (code templates)», и их можно вызвать, нажав Ctrl+Space сразу после написания ключевого слова. Например, откройте файл с исходным кодом и напишите ключевое слово «for» и сразу после него нажмите Ctrl+Space. Появится контекстное меню, как показано на следующем рисунке.

Выбрав запись «for - for loop», Eclipse автоматически поместит новый цикл for внутрь кода. Теперь обратите внимание: переменная цикла var выделена, как показано на следующем рисунке.

Если вы напишите новое имя для переменной цикла, Eclipse автоматически изменит его имя во всех трех местах. Eclipse определяет свой набор шаблонов кода, но хорошей новостью является то, что вы можете определять свои собственные! Зайдите в настройки Eclipse, а затем в C/C++ → Editor → Templates. Здесь вы можете найти все заранее определенные фрагменты кода (snippets) и, в конечном итоге, можете добавить свои собственные.

Например, мы можем добавить новый шаблон кода, который вставляет программную точку останова (asm("BKPT #0");), когда мы пишем ключевое слово bkpt, как показано на предыдущем рисунке. Шаблоны кода легко персонализируются благодаря использованию переменных и других шаблонных конструкций. Для получения дополнительной информации обратитесь к документации Eclipsea.

a http://help.eclipse.org/neon/index.jsp?topic=/org.eclipse.jdt.doc.user/gettingStarted/qs-EditorTemplates.htm