Программирование ARM STM32: обработка прерывания по изменению уровня ножки GPIO Wed, April 24 2024  

Поделиться

Нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.

STM32: обработка прерывания по изменению уровня ножки GPIO Печать
Добавил(а) microsin   

В этой статье (перевод [1]) мы рассмотрим, как настроить ножку GPIO на генерацию прерывания по спаду, нарастанию уровня, а также по обоим этим перепадам. Для обработки этих прерываний мы напишем соответствующий код ISR (Interrupt Service Routine), и для индикации запуска ISR будем использовать управление выходом на светодиод (LED). И наконец, мы проверим время реакции обработки прерывания и латентность прерывания.

Для тестирования понадобится плата на микроконтроллере ARM Cortex-M4 80 МГц (MCU). Может использоваться плата Nucleo32-L432KC [3] или любая другая с MCU STM32L432KCU6U. С небольшими изменениями в настройках и коде может использоваться любая другая плата с MCU семейства STM32 (например Blue Pill STM32-F103 [4] на ARM Cortex-M3 @ 72MHz, которую можно купить на AliExpress или Ebay). Для программирования понадобится отладчик ST-Link v2 [6] или SEGGER J-Link [7].

Общий план выполняемых действий:

• Конфигурирование одной ножки GPIO на выход (для управления светодиодом) и одной ножки GPIO на вход (для реакции на изменение уровня). Это будет сделано в процессе генерации шаблона проекта с помощью утилиты STM32CubeMX [5].
• Конфигурирование ножки входа на обработку прерывания EXTI по нарастанию уровня.
• В обработчике прерывания (ISR EXTI) будет вставлен код, который будет переключать уровень на выходе для управления LED.
• Измерение времени ответа ISR EXTI с помощью осциллографа - какой получится интервал между нарастанием уровня на входе EXTI и изменением уровня на управляемом выходе LED.

Процесс по шагам:

Шаг 1. Откройте генератор кода CubeMX [5] и создайте в нем новый проект для используемого MCU или отладочной платы. При запуске откроется окно с кнопками для создания нового проекта с помощью выбора процессора (ACCESS TO MCU SELECTOR) или с помощью выбора одной из поддерживаемых плат разработчика (ACCESS TO BOARD SELECTOR). В этом примере код генерировался для популярного MCU типа STM32F103C8.

Кликните на кнопку ACCESS TO MCU SELECTOR, откроется диалог для выбора используемого MCU.

EXTI example fig01

Шаг 2. Выберите нужный процессор двойным кликом на его имени в списке:

EXTI example fig02

Шаг 3. Откроется окно с активной закладкой Pinout & Configuration. Кликните на ножку в картинке корпуса, которую хотите использовать под выход для светодиода (LED). В этом примере для светодиода будет использоваться ножка A8. Выпадающем списке выберите настройку для этой ножки как GPIO_Output.

EXTI example fig03

Шаг 4. Кликните на ножке порта, которую хотите сконфигурировать как вход, реагирующий на изменение уровня с генерацией прерывания (External Interrupt Input). В нашем примере это будет вывод порта PA9, соответствующий линии прерывания EXTI9 (к нему мы подключим кнопку с замыканием на GND).

EXTI example fig04

Шаг 5. В левой панели разверните список настроек System Core, кликните на раздел GPIO, выберите ножку порта PA9 и настройте у неё и режим GPIO на генерацию прерывания при нарастании уровня (GPIO mode: External Interrupt Mode with Rising edge trigger detection) и внутренний верхний подтягивающий резистор (GPIO Pull-up/Pull fown: Pull-up).

EXTI example fig05

Шаг 6. Выберите закладку NVIC и разрешите прерывание EXTI9 (EXTI line9 Interrupt).

EXTI example fig06

Шаг 7. Выберите в левой панели раздел настроек NVIC и при необходимости измените приоритет прерывания для EXTI line9 (не обязательный шаг).

EXTI example fig07

Шаг 8. Выберите в левой панели раздел настроек RCC и настройте источник тактирования на использование внешнего кварцевого резонатора. Для этого в выпадающем списке High Speed Clock (HSE) выберите Crystal/Ceramic Resonator.

EXTI example fig08

Шаг 9. Перейдите на закладку настроек тактирования (Clock Configuration) и настройте частоту тактов 72 МГц (или другую частоту, если это необходимо).

EXTI example fig09

Шаг 10. Перейдите на закладку Project Manager и выберите каталог, куда будет сохраняться генерируемый проект, версию тулчейна (IDE) и другие настройки. Это тоже не обязательный шаг, по умолчанию исходный код будет сгенерирован в папке, куда Вы сохранили файл проекта STM32CubeMX (файл с расширением *.ioc).

EXTI example fig10

Шаг 11. Кликните на кнопку GENERATE CODE (находится справа вверху). Если Вы генерируете проект в первый раз, то появится сообщение о необходимости загрузки пакета библиотек STM32Cube:

EXTI example fig11

Дождитесь окончания загрузки, распаковки и установки пакета библиотек.

EXTI example fig12

После этого исходный код будет сгенерирован в папке, куда был сохранен файл проекта STM32CubeMX (файл с расширением *.ioc).

При загрузке и установке пакетов, необходимых для генерации проекта, может появиться ошибка в виде окна "Problem during Download and/or Unzip of selected Files" с указанием имени распаковываемого архива библиотек:

STM32cubeMX Firmware package download error fig01

[Способ1]

Для устранения ошибки выполните следующее:

1. Найдите в профиле пользователя файл загруженного архива библиотек. Обычно архив находится в папке c:\Users\имяпользователя\STM32Cube\Repository\, и архив называется наподобие stm32cube_fw_f1_v1xx.zip (имя зависит от выбранного микроконтроллера и от версии библиотеки). Перенесите этот файл архива в какую-нибудь другую папку (например c:\TEMP).

2. В STM32CubeMX перейдите на стартовый экран (Home) и в разделе менеджера библиотек (Manage software installations) и кликните на кнопку INSTALL/REMOVE.

STM32cubeMX Firmware package download error fig02

3. Откроется экран менеджера библиотек. Кликните на кнопку From Local ..., выберите перенесенный на шаге 1 архив библиотек.

STM32cubeMX Firmware package download error fig03

[Способ2]

В случае, когда необходимо обновление уже установленного пакета библиотек, вышеописанный Способ1 может не сработать. Тогда нужно будет распаковать архив библиотек вручную. У меня такое произошло, когда STM32CubeMX загрузил 2 архива библиотек stm32cube_fw_f1_v180.zip и stm32cube_fw_f1_v184.zip. В этом случае распаковка второго архива в директорию, где уже находились распакованные файлы библиотек, приводила к ошибке.

Процесс по шагам:

1. Зайдите в каталог репозиториев STM32Cube\Repository (он находится в каталоге профиля пользователя c:\Users\имяпользователя), и перенесите оттуда архивы библиотек в другое место. В моем случае это были архивы stm32cube_fw_f1_v180.zip и stm32cube_fw_f1_v184.zip, я их перенес в папку c:\TEMP.

2. Судя по именам, файл stm32cube_fw_f1_v180.zip это архив предыдущей версии библиотек, а файл stm32cube_fw_f1_v184.zip это архив с обновлениями до версии 1.8.4. Откройте в Проводнике (или в Total Commander) первый архив, и распакуйте каталог внутри него (в моем случае STM32Cube_FW_F1_V1.8.0) в папку STM32Cube\Repository. После распаковки библиотеки должны находиться в каталоге STM32Cube\Repository\STM32Cube_FW_F1_V1.8.0\:

STM32cubeMX Firmware package download error fig04

3. Откройте второй архив stm32cube_fw_f1_v184.zip, в нем находится папка с таким же именем STM32Cube_FW_F1_V1.8.0. Распакуйте эту папку в тот же каталог STM32Cube\Repository\STM32Cube_FW_F1_V1.8.0\, с перезаписью существующих файлов (потому что это обновление библиотек).

4. Откройте менеджер библиотек STM32CubeMX выбором Home -> кнопка INSTALL/REMOVE, или через меню Help -> Manage embedded software packages (Alt-U). Вы увидите, что необходимый пакет библиотек успешно установлен.

STM32cubeMX Firmware package download error fig05

[Проверка сгенерированного кода]

Откройте проект в IDE (в моем случае это был IAR 8.30). Модуль main.c содержит следующий основной код:

#include "main.h"
 
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
 
/**  * @brief  Точка входа в основную программу.
  * @retval int  */
int main(void)
{
  /* Сброс всех периферийных устройств, инициализация интерфейса Flash     и Systick. */
  HAL_Init();
 
  /* Конфигурирование тактов системы */
  SystemClock_Config();
 
  /* Инициализация всех сконфигурированных периферийных устройств */
  MX_GPIO_Init();
 
  /* Вход в бесконечный цикл */
  while (1)
  {
    /* Сюда можно добавить любой код пользователя */
  }
}

Функция MX_GPIO_Init содержит настройки для портов ввода/вывода.

/**
  * @brief Функция инициализации GPIO.
  * @param None
  * @retval None  */
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
 
  /* Разрешение тактирования используемых портов GPIO */
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
 
  /* Конфигурирование уровня ножки выхода GPIO */
  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_8, GPIO_PIN_RESET);
 
  /* Настройка ножки порта выхода GPIO: PA8 */
  GPIO_InitStruct.Pin = GPIO_PIN_8;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 
  /* Настройка ножки порта входа GPIO: PA9 */
  GPIO_InitStruct.Pin = GPIO_PIN_9;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
 
  /* Инициализация прерывания EXTI */
  HAL_NVIC_SetPriority(EXTI9_5_IRQn, 0, 0);
  HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);
}

Последние строки подпрограммы MX_GPIO_Init конфигурируют уровень приоритета прерывания (EXTI line9 interrupt priority level) и разрешают его сигнал прерывания. Режим срабатывания прерывания был выбран в конфигурации настройки ножки порта входа PA9 по нарастанию уровня (GPIO_MODE_IT_RISING). При необходимости Вы можете поменять эти настройки в соответствии с требованиями разрабатываемого приложения.

Обработчик прерывания. ISR будет вызван, когда произойдет прерывание по нарастанию уровня на выводе PA9. Его код находится в функции EXTI9_5_IRQHandler модуля stm32f1xx_it.c:

/**
  * @brief Эта функция обрабатывает прерывания EXTI line[9:5].
  */
void EXTI9_5_IRQHandler(void)
{
  /* Сюда можно добавить свой код */
  HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_9);
  /* Сюда также можно добавить свой код */
}

В этом обработчике вызывается функция HAL_GPIO_EXTI_IRQHandler, которая находится в модуле stm32f1xx_hal_gpio.c:

/**
  * @brief  Эта функция обрабатывает запрос прерывания EXTI.
  * @param  GPIO_Pin: указывает ножки, которые привязаны к сигналу EXTI line
  * @retval None  */
void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
  /* Было детектировано прерывание EXTI line */
  if (__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != 0x00u)
  {
    // Очистка флага прерывания:
    __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
    // Вызов кода пользователя:
    HAL_GPIO_EXTI_Callback(GPIO_Pin);
  }
}

Таким образом, обработка прерывания EXTI организована по цепочке вызовов EXTI9_5_IRQHandler -> HAL_GPIO_EXTI_IRQHandler -> HAL_GPIO_EXTI_Callback. Последняя функция в этой цепочке HAL_GPIO_EXTI_Callback определена в модуле stm32f1xx_hal_gpio.c с атрибутом __weak, что дает возможность переопределить эту функцию в коде пользователя:

/**
  * @brief  Функция обратного вызова (callback) для обработки детектирования
  *         изменения уровня на EXTI.
  * @param  GPIO_Pin: указывает ножки, соединенные с сигналом EXTI line
  * @retval None  */
__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  /* Этот макрос предотвращает генерацию предупреждения на неиспользуемый
     EXTI line аргумент функции */
  UNUSED(GPIO_Pin);
  /* Внимание: эту функцию изменять не нужно. Когда необходим callback
               для отслеживания изменения уровня на ножке EXTI, его код
               в виде функции с таким же именем HAL_GPIO_EXTI_Callback
               должен быть реализован в одном из модулей пользователя. */
}

В этом примере мы реализуем код callback-функции HAL_GPIO_EXTI_Callback в модуле, чтобы она для индикации переключала уровень на ножке светодиода:

// Функция обратного вызова обработки прерывания EXTI Line9 External Interrupt
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
   if(GPIO_Pin == GPIO_PIN_9) // если прерывание поступило от ножки PA9
   {
      // Переключение в противоположное состояние ножки
      // выхода для управления светодиодом (LED):
      HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_8);
   }
}

[Проверка работы кода]

Цоколевка выводов STM32 Blue Pill:

EXTI example fig13

Подключите плату к отладчику ST-Link через выводы SWD. Питание будет поступать от ST-Link через порт USB:

EXTI example fig14

Скомпилируйте код через выбор в меню Project -> Make (F7) и загрузите его в память микроконтроллера путем запуска сессии отладки Project -> Download and Debug (Ctrl+D).

Подключите к ножке PA9 кнопку, которая выполняет замыкание на GND, к ножке PA8 через резистор 470 Ом подключите светодиод - анод светодиода к резистору, катод к GND. Передерните питание у платы, чтобы перезапустить записанную программу. При нажатии на кнопку светодиод должен переключаться в противоположное состояние - зажигаться и гаснуть.

[Измерение отклика EXTI ISR]

Ниже показан скриншот осциллографа, где отображены сигналы на выводе кнопки (синяя осциллограмма) и на светодиоде LED (желтая осциллограмма). При выбранном интервале развертки 10 мс в клетке перепады обоих сигналов визуально совпадают, потому что задержка между входным и выходным сигналами очень мала.

EXTI example fig15

Если переключить режим развертки на интервал 200 нс в клетке, то можно измерить задержку между изменением уровня на входе и реакцией ISR на это изменение. Оно составляет приблизительно 1.6 мкс:

EXTI example fig16

Эта задержка включает латентность реакции на прерывание, время на переключение контекста, выполнение кода ISR и цепочки вызовов до callback-функции HAL_GPIO_EXTI_Callback, а также время обработки функции HAL_TogglePin. Очевидно, время реакции достаточно мало, но его еще можно дополнительно уменьшить, если устранить цепочку вызовов HAL-функций.

[Ссылки]

1. STM32 External Interrupt Example site:deepbluembedded.com.
2. Stm32 Event and interrupts site:electronics.stackexchange.com.
3. NUCLEO-L432KC site:st.com.
4. Программирование STM32 Blue Pill через USB.
5. STM32CubeMX site:st.com.
6. Ошибка установки пакета в STM32 CubeMX.
7. ST-LINK: отладчик/программатор для STM8 и STM32.
8. Клоны Segger J-Link.
9220111EXTIO.ZIP - исходный код для STM32F103 и STM32F429 (проекты STM32CubeMX и IAR 8.30).

 

Комментарии  

 
0 #3 Сергей 26.09.2023 10:21
if(GPIO_Pin == GPIO_PIN_9) // если прерывание поступило от ножки PA9

- Здесь правильно использовать термин EXTI9.
Цитировать
 
 
0 #2 12val12 02.02.2023 11:26
1.6 микросекунд это очень много... Как ускорить-то?

microsin: увеличением тактовой частоты, переносом кода в RAM, переходом от программной логики к аппаратной (ПЛИС, триггеры).
Цитировать
 
 
0 #1 Boris 12.01.2022 04:03
Очень подробно. Спасибо!
Цитировать
 

Добавить комментарий


Защитный код
Обновить

Top of Page