Доброго всем дня! Продолжаем работать с отладочной платой STM32F3Discovery, и сегодня мы разберемся как настроить и использовать внешние прерывания в микроконтроллерах серии STM32F3.
Что такое вообще внешнее прерывание? Ну тут особо нечего рассказывать - это просто такое прерывание, которое возникает при изменении состояния определенного входа микроконтроллера.
То есть хотим мы, например, оперативно реагировать на изменение входного сигнала на выводе PA0. Тут то нам и придут на помощь внешние прерывания. Настраиваем их соответствующим образом, и при изменении сигнала с 0 на 1 (или наоборот, в зависимости от настроек) на интересующей нас ножке контроллера программа ускачет на обработку прерывания. Как видите, все действительно довольно-таки просто, так что перейдем к практической реализации всего этого безобразия )
У STM32F3 имеется в наличии целых 36(!) внешних прерываний. Прерывания EXTI0...EXTI15 висят на обработке изменения уровня сигнала на обычных ножках нашего контроллера. То есть прерывание EXTI0 мы можем настроить на работу с ножками PA0, PB0, PC0, PD0, PE0 или PF0, аналогично для EXTI3 - выводы PA3, PB3, PC3, PD3, PE3 или PF3. Вот как это все выглядит:
Возникает вопрос - а остальные 20 прерываний? А вот они:
Вот, например, в статье про будильник (вот она) мы использовали EXTI line 17, для пробуждения по будильнику.
Итак, с теорией мы разобрались немного, давайте напишем тестовую программку. А заодно, пожалуй, поэкспериментируем с пользовательской кнопкой (синего цвета), установленной на плате STM32F3Discovery ) Пусть будет такая задача:
- опрашиваем состояние кнопки (ножка PA0)
- если кнопка нажата, то выставляем вывод PE2 в 1 (высокий уровень сигнала)
- замыкаем на плате ногу PE2 на вывод PE1
- настраиваем внешнее прерывание так, чтобы оно возникало при изменении сигнала на PE1 с низкого уровня на высокий
- и в обработчике прерывания меняем состояние какого-нибудь светодиода из установленных на плате - пусть будет синий (PE8)
Что же мы ожидаем увидеть в результате всего этого? Сейчас разберемся...
При нажатии на кнопку сигнал на выводе PE2 меняется с 0 на 1, а, соответственно, аналогичным образом меняется сигнал и на выводе PE1, что приводит к возникновению внешнего прерывания. А в обработчике, как вы помните, мы должны менять состояние светодиода. Таким образом, при нажатиях на кнопку синий светодиод должен то зажигаться, то гаснуть. Для примера подойдет )
Время традиционной вставки: поскольку компания STMicroelectronics прекратила поддержку библиотеки SPL, которая использовалась в этом курсе, я создал новый, посвященный работе уже с новыми инструментами, так что буду рад видеть вас там - STM32CubeMx. Кроме того, вот глобальная рубрика по STM32.
Переходим к делу. Создаем новый проект, подключаем все нужные файлы и объявляем переменные:
/***************************************************************************************/ #include "stm32f30x_gpio.h" #include "stm32f30x_rcc.h" #include "stm32f30x_exti.h" #include "stm32f30x_syscfg.h" #include "stm32f30x_misc.h" #include "stm32f30x.h" /***************************************************************************************/ GPIO_InitTypeDef gpio; EXTI_InitTypeDef exti; NVIC_InitTypeDef nvic; /***************************************************************************************/
Пора переходить к инициализации. И для того, чтобы настроить правильную работу внешних прерываний нужно сделать следующее:
- настраиваем нужный вывод на работу в режиме входа
- используя функцию SYSCFG_EXTILineConfig() выбираем, какой вывод и какого порта будем использовать в качестве источника внешнего прерывания
- настраиваем поля структуры EXTI_InitTypeDef и, как и при работе с любой другой периферией, вызываем функцию инициализации - в данном случае это EXTI_Init()
- аналогично производим настройки прерывания при помощи структуры NVIC_InitTypeDef
Теперь осталось реализовать все эти шаги в коде:
/***************************************************************************************/ void initAll() { RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOE, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); gpio.GPIO_Mode = GPIO_Mode_OUT; gpio.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_2; gpio.GPIO_OType = GPIO_OType_PP; gpio.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOE, &gpio); gpio.GPIO_Mode = GPIO_Mode_IN; gpio.GPIO_Pin = GPIO_Pin_0; gpio.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &gpio); gpio.GPIO_Mode = GPIO_Mode_IN; gpio.GPIO_Pin = GPIO_Pin_1; gpio.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOE, &gpio); SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource1); exti.EXTI_Line = EXTI_Line1; exti.EXTI_Mode = EXTI_Mode_Interrupt; exti.EXTI_Trigger = EXTI_Trigger_Rising; exti.EXTI_LineCmd = ENABLE; EXTI_Init(&exti); nvic.NVIC_IRQChannel = EXTI1_IRQn; nvic.NVIC_IRQChannelPreemptionPriority = 0; nvic.NVIC_IRQChannelSubPriority = 0; nvic.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&nvic); } /***************************************************************************************/
Почти все готово, осталось написать функцию main() и, собственно, обработчик прерывания:
/***************************************************************************************/ int main() { __enable_irq(); initAll(); while(1) { if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 1) { GPIO_SetBits(GPIOE, GPIO_Pin_2); } else { GPIO_ResetBits(GPIOE, GPIO_Pin_2); } } } /***************************************************************************************/ void EXTI1_IRQHandler() { EXTI_ClearFlag(EXTI_Line1); if (GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_8) == 1) { GPIO_ResetBits(GPIOE, GPIO_Pin_8); } else { GPIO_SetBits(GPIOE, GPIO_Pin_8); } } /***************************************************************************************/
В функции main() опрашиваем нашу кнопку и в зависимости от ее состояния меняем уровень сигнала на выводе PE2, который, как вы помните, у нас замкнут на ножку PE1. При нажатии кнопки должно возникать прерывание EXTI1_IRQ, в обработчике которого меняем состояние вывода PE8, на котором у нас висит светодиод.
Компилируем, прошиваем микроконтроллер, жмем на кнопку и видим результат - на каждое нажатие кнопки синий светодиод меняет свое состояние. Соответственно, программа работает именно так, как и было задумано 👍