Реклама ⓘ
Главная » Микроконтроллеры
Призовой фонд
на апрель 2024 г.
1. 100 руб.
От пользователей

Реклама ⓘ

Обработка нажатия пользовательской кнопки, используя внешние прерывания

В статье пользователя Rough был описан способ обработки нажатия пользовательской кнопки с использованием чтения состояния портов ввода/вывода, а так же таймера.  В случае, когда разрабатываемое  устройство должно незамедлительно реагировать на какое - либо событие (например, нажатие кнопки, или срабатывание датчика), более уместно использовать внешние прерывания.

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

Микроконтроллеры семейства STM32F4xx имеют 23 линии внешних прерываний. Первые 16 линий используются для формирования запросов на прерывание от любых из пинов микроконтролерра для портов от A до H, к оставшимся же линиям «привязаны» события от периферии, например USB, Ethernet, часов реального времени, а так же PVD (Programmable voltage detector), модуля, позволяющего контролировать изменение напряжения питания микроконтроллера[1].

Использование альтернативных функций, которые совмещены с портами ввода/вывода, позволяют обрабатывать до 83 различных событий, полный перечень которых можно найти в Reference manual [2]. Но здесь есть одно ограничение, если процедура прерывания сконфигурирована таким образом, что к линии EXTI_Line0 «привязан» пин PA0, то мы не имеем права добавлять к этой линии любой другой источник прерывания от выводов PB0 – PH0. Зато у нас есть возможность выставить до 16 уровней приоритета на обработку прерываний, а так же настроить вывод на регистрацию фронта, спада, или же любого изменения уровня сигнала.

При работе с внешними прерываниями мы будем использовать дополнительные библиотеки stm32f4xx_exti.h, stm32f4xx_syscfg.h и misc.h. Первые две необходимы для конфигурации процедуры прерывания, а последняя, для настройки обработчика запроса на прерывание. Среда разработки CooCox v1.7.6.

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

Для реализации этого задания нам необходимо выполнить следующие основные пункты:

  1. Сконфигурировать порты ввода/вывода для светодиодов;
  2. Сконфигурировать процедуру прерывания и обработчик запроса на прерывание;
  3. Написать код обработчика прерывания.

Итак, подключаем библиотеки, объявляем переменные, создаем функцию init_led_port, которая будет отвечать за инициализацию выводов, к которым подключены светодиоды. Каждая строка кода имеет подробное описание, поэтому будем двигаться дальше.

#include "stm32f4xx.h"
#include "stm32f4xx_rcc.h"
#include "misc.h"
#include "stm32f4xx_exti.h
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_syscfg.h"


EXTI_InitTypeDef exti;										//Присваеваем структуре EXTI_InitTypeDef имя exti
NVIC_InitTypeDef nvic;										//Присваеваем структуре NVIC_InitTypeDef имя nvic
GPIO_InitTypeDef GPIO_InitStructure;						//Присваеваем структуре GPIO_InitTypeDef имя GPIO_InitStructure
uint8_t a = 0, b = 0;										//Объявляем переменные

void init_led_port(void)									//Функция конфигурации порта со светодиодами
	{
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);	//Включаем тактирование порта со светодиодами

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15;	//Выбираем необходимые пины
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;			//Пины работают на выход
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;		//Скорость работы пинов
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;			//Тип значение порта - двухуровневый
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;			//Начальное значение пинов - 0
	GPIO_Init(GPIOD, &GPIO_InitStructure);		        	//Инициализация настроек
	}

Теперь перейдем к функции, в которой будем задавать конфигурацию нашей процедуры прерывания.

void init_EXTI()											//Конфигурация процедуры прерывания и обрботчика запроса на прерывание
{
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);	//Включаем тактирование порта с пользовательской кнопкой
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);	//Включаем тактирование контроллера конфигурации системы
    														//(отвечает за ремапинг выводов)

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;				//Выбираем необходимые пины
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;			//Пины работают на вход
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;		//Скорость работы пинов
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;			//Тип значение порта - двухуровневый
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;			//Начальное значение пинов - 1
	GPIO_Init(GPIOA, &GPIO_InitStructure);					//Инициализация настроек

Для начала проинициализируем порт, которому подключена пользовательская кнопка и контроллер конфигурации системы, который управляет ремапингом выводов микроконтроллера. Настройка пина, к которому подключена пользовательская кнопка полностью аналогична настройке порта со светодиодами.

Строка является очень важной, так как именно здесь мы конкретизируем, что к нулевой лини прерывания мы хотим подключить пин PA0, а не какой либо другой.

SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);

Далее следует настройка остальных параметров задающих приоритет, вид изменения сигнала, для генерации запроса на прерывание, а так же конфигурация обработчика запроса на прерывания (структура nvic)

    exti.EXTI_Line = EXTI_Line0;							//Выбираем нулевую линию
    exti.EXTI_Mode = EXTI_Mode_Interrupt;					//Устанавливаем, что событие будет источником прерывания
    														//Запрос на прерывание генерируется по фронту импульса, т.е. при
    exti.EXTI_Trigger = EXTI_Trigger_Rising;			    //переходе из 0 в 1
    exti.EXTI_LineCmd = ENABLE;								//Указываем новое состояние для линии прерывания
    EXTI_Init(&exti);										//Инициализируем настройки

    nvic.NVIC_IRQChannel = EXTI0_IRQn;						//Устанавливаем источник запроса на прерывание
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);   //присваиваем данному прерыванию первую группу
    nvic.NVIC_IRQChannelPreemptionPriority = 0;				//Устанавливаем приоритет для обработчика прерывания
    nvic.NVIC_IRQChannelSubPriority = 0;					//Устанавливаем субприоритет
    nvic.NVIC_IRQChannelCmd = ENABLE;						//Указываем, что выбранный канал будет источником вектора прерывания
    NVIC_Init(&nvic);										//Инициализируем настройки
}

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

void Delay(uint32_t s)										//Функция задержки
{
    volatile uint32_t i;									//Объявляем переменную
    RCC_ClocksTypeDef rcc;									//Присваиваем структуре RCC_ClocksTypeDef имя rcc

    RCC_GetClocksFreq (&rcc);								//Заполняем структуру rcc текущиим значением параметров SYSCLK, HCLK, PCLK1, PCLK2
    i = (rcc.HCLK_Frequency/16)*s;							//Получаем значение HCLK в герцах

    for (i; i != 0; i--);									//Выполняем некоторое количество циклов с уменьшением значения переменной i
}

Осталось написать последний элемент - обработчик прерывания. Имя функции-обработчика должно обязательно соответствовать номеру линии, на которой мы ожидаем прерывание, то есть для нулевой линии это будет EXTI0_IRQHandler, для первой EXTI1_IRQHandler и так далее.

void EXTI0_IRQHandler()
{
    EXTI_ClearITPendingBit(EXTI_Line0);						//Очищаем флаг прерывания

    a = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0);			//Читаем состояние кнопки

    														//Цикл for будет выполняться 140 раз (b = 140) при условии, что все это время
    for (a; a == 1; b++)    								//кнопка нажата, этого времени достаточно, чтобы дребезг контакта закончился
    {
    	a = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0);		//Читаем состояние кнопки
    	if ((b == 140) && (GPIO_ReadOutputDataBit(GPIOD, GPIO_Pin_12) == 0))//Если в течении 140 циклов кнопка была нажата, и первый из
    																		//светодиодов не горит, то включаем светодиоды
    	{
    		GPIO_SetBits(GPIOD, GPIO_Pin_12);
    		Delay(1);
    		GPIO_SetBits(GPIOD, GPIO_Pin_13);
    		Delay(1);
    		GPIO_SetBits(GPIOD, GPIO_Pin_14);
    		Delay(1);
    		GPIO_SetBits(GPIOD, GPIO_Pin_15);
    		Delay(1);
    		b = 0;
    		return;											//Выходим из цикла
    	}
    	if ((b == 140) && (GPIO_ReadOutputDataBit(GPIOD, GPIO_Pin_12) == 1))//Если в течении 140 циклов кнопка была нажата, но первый из
																			//светодиодов уже горит, то выключаем светодиоды
        {
    		GPIO_ResetBits(GPIOD, GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);
    		b = 0;
    		return;											//Выходим из цикла
        }
    }
    b = 0;													//Обнуляем счетчик в случае отпускания кнопки

}

Здесь мы сталкиваемся с небольшой проблемой - дребезг контактов. Существует два основных метода программной борьбы с этим явлением - временная задержка и подсчет числа совпадающих значений сигнала. Мы воспользуемся вторым методом. Наш цикл в течении 140 итераций проверяет остается ли нажатой кнопка, и в случае появления низкого потенциала (кнопка оказалось отпущенной или дребезг еще не закончился) счетчик сбрасывается. В нашем случае необходимо достаточно большое количество итераций цикла. Это связано с видом кнопок, которые мы используем, применение мембранных кнопок позволяет сократить время дребезга примерно в 10 раз. Наличие оператора return может показаться избыточным, но по субъективным ощущениям это повышает надежность функционирования обработчика прерывания.

Формируем основное тело программы:

int main(void)
{
	__enable_irq();											//Разрешаем прерывания
	init_led_port();										//Инициализируем порт со светодиодами
	init_EXTI();											//Инициализируем линию прерывания

    while(1)
    {

    }
}

Список используемой литературы и источников:

  1. Reference manual STM32F4 стр. 383-384.
  2. Reference manual STM32F4 стр. 373.
  3. microtechnics.ru

Прикрепленные файлы:

Теги:

Опубликована: 0 0
Я собрал 0 0
x

Оценить статью

  • Техническая грамотность
  • Актуальность материала
  • Изложение материала
  • Полезность устройства
  • Повторяемость устройства
  • Орфография
0

Средний балл статьи: 0 Проголосовало: 0 чел.

Комментарии (0) | Я собрал (0) | Подписаться

Статью еще никто не комментировал. Вы можете стать первым.
Добавить комментарий
Имя:
E-mail:
не публикуется
Текст:
Защита от спама:
В чем измеряется напряжение?
Файлы:
 
Для выбора нескольких файлов использйте CTRL

Программатор Pickit3
Программатор Pickit3
Регулятор мощности 2 кВт Discovery V8
вверх