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.
Шаг 2. Выберите нужный процессор двойным кликом на его имени в списке:
Шаг 3. Откроется окно с активной закладкой Pinout & Configuration. Кликните на ножку в картинке корпуса, которую хотите использовать под выход для светодиода (LED). В этом примере для светодиода будет использоваться ножка A8. Выпадающем списке выберите настройку для этой ножки как GPIO_Output.
Шаг 4. Кликните на ножке порта, которую хотите сконфигурировать как вход, реагирующий на изменение уровня с генерацией прерывания (External Interrupt Input). В нашем примере это будет вывод порта PA9, соответствующий линии прерывания EXTI9 (к нему мы подключим кнопку с замыканием на GND).
Шаг 5. В левой панели разверните список настроек System Core, кликните на раздел GPIO, выберите ножку порта PA9 и настройте у неё и режим GPIO на генерацию прерывания при нарастании уровня (GPIO mode: External Interrupt Mode with Rising edge trigger detection) и внутренний верхний подтягивающий резистор (GPIO Pull-up/Pull fown: Pull-up).
Шаг 7. Выберите в левой панели раздел настроек NVIC и при необходимости измените приоритет прерывания для EXTI line9 (не обязательный шаг).
Шаг 8. Выберите в левой панели раздел настроек RCC и настройте источник тактирования на использование внешнего кварцевого резонатора. Для этого в выпадающем списке High Speed Clock (HSE) выберите Crystal/Ceramic Resonator.
Шаг 9. Перейдите на закладку настроек тактирования (Clock Configuration) и настройте частоту тактов 72 МГц (или другую частоту, если это необходимо).
Шаг 10. Перейдите на закладку Project Manager и выберите каталог, куда будет сохраняться генерируемый проект, версию тулчейна (IDE) и другие настройки. Это тоже не обязательный шаг, по умолчанию исходный код будет сгенерирован в папке, куда Вы сохранили файл проекта STM32CubeMX (файл с расширением *.ioc).
Шаг 11. Кликните на кнопку GENERATE CODE (находится справа вверху). Если Вы генерируете проект в первый раз, то появится сообщение о необходимости загрузки пакета библиотек STM32Cube:
Дождитесь окончания загрузки, распаковки и установки пакета библиотек.
После этого исходный код будет сгенерирован в папке, куда был сохранен файл проекта STM32CubeMX (файл с расширением *.ioc).
При загрузке и установке пакетов, необходимых для генерации проекта, может появиться ошибка в виде окна "Problem during Download and/or Unzip of selected Files" с указанием имени распаковываемого архива библиотек:
[Способ1]
Для устранения ошибки выполните следующее:
1. Найдите в профиле пользователя файл загруженного архива библиотек. Обычно архив находится в папке c:\Users\имяпользователя\STM32Cube\Repository\, и архив называется наподобие stm32cube_fw_f1_v1xx.zip (имя зависит от выбранного микроконтроллера и от версии библиотеки). Перенесите этот файл архива в какую-нибудь другую папку (например c:\TEMP).
2. В STM32CubeMX перейдите на стартовый экран (Home) и в разделе менеджера библиотек (Manage software installations) и кликните на кнопку INSTALL/REMOVE.
3. Откроется экран менеджера библиотек. Кликните на кнопку From Local ..., выберите перенесенный на шаге 1 архив библиотек.
[Способ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\:
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). Вы увидите, что необходимый пакет библиотек успешно установлен.
[Проверка сгенерированного кода]
Откройте проект в IDE (в моем случае это был IAR 8.30). Модуль main.c содержит следующий основной код:
#include "main.h"
voidSystemClock_Config(void);
staticvoidMX_GPIO_Init(void);
/** * @brief Точка входа в основную программу.
* @retval int */
intmain(void)
{
/* Сброс всех периферийных устройств, инициализация интерфейса Flash и Systick. */
HAL_Init();
/* Конфигурирование тактов системы */
SystemClock_Config();
/* Инициализация всех сконфигурированных периферийных устройств */
MX_GPIO_Init();
/* Вход в бесконечный цикл */while (1)
{
/* Сюда можно добавить любой код пользователя */
}
}
Функция MX_GPIO_Init содержит настройки для портов ввода/вывода.
Последние строки подпрограммы 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].
*/
voidEXTI9_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 */
voidHAL_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 voidHAL_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
voidHAL_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:
Подключите плату к отладчику ST-Link через выводы SWD. Питание будет поступать от ST-Link через порт USB:
Скомпилируйте код через выбор в меню Project -> Make (F7) и загрузите его в память микроконтроллера путем запуска сессии отладки Project -> Download and Debug (Ctrl+D).
Подключите к ножке PA9 кнопку, которая выполняет замыкание на GND, к ножке PA8 через резистор 470 Ом подключите светодиод - анод светодиода к резистору, катод к GND. Передерните питание у платы, чтобы перезапустить записанную программу. При нажатии на кнопку светодиод должен переключаться в противоположное состояние - зажигаться и гаснуть.
[Измерение отклика EXTI ISR]
Ниже показан скриншот осциллографа, где отображены сигналы на выводе кнопки (синяя осциллограмма) и на светодиоде LED (желтая осциллограмма). При выбранном интервале развертки 10 мс в клетке перепады обоих сигналов визуально совпадают, потому что задержка между входным и выходным сигналами очень мала.
Если переключить режим развертки на интервал 200 нс в клетке, то можно измерить задержку между изменением уровня на входе и реакцией ISR на это изменение. Оно составляет приблизительно 1.6 мкс:
Эта задержка включает латентность реакции на прерывание, время на переключение контекста, выполнение кода ISR и цепочки вызовов до callback-функции HAL_GPIO_EXTI_Callback, а также время обработки функции HAL_TogglePin. Очевидно, время реакции достаточно мало, но его еще можно дополнительно уменьшить, если устранить цепочку вызовов HAL-функций.
Комментарии
- Здесь правильно использовать термин EXTI9.
microsin: увеличением тактовой частоты, переносом кода в RAM, переходом от программной логики к аппаратной (ПЛИС, триггеры).
RSS лента комментариев этой записи