Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Обработка прерывания кнопки
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > Программирование
Nikitoc
Может я засиделся сегодня у компа, но что-то меня заклинило: как правильно писать обработчик прерывания по нажатию кнопки? Суть вопроса сводится к расположению задержки (~30мс) для "устранения " дребезга контактов кнопки. Ведь существует неписанное правило о том, что обработчики прерывания должны быть как можно короче по времени исполнения, а введение "антидребезговой" задержки этому как-то не способствует. Или это неизбежное зло и таким обработчикам просто следует назначать наименьший приоритет? Если тема боян - ткните носом. Не нашел.
нечитатель
Кто запрещает что-то другое делать, пока задержка. Почему обязательно всё это время висеть в прерывании.
Кто разрешил делать предположения о принципиальной возможности других подходов к.

Цитата
Не нашел.
Длительность импульсов в "дребезге контактов" например заголовок, простой и логичный.
alexeyv
Примерно лет шесть делаю так:



CODE

// в .h-файле:
// кол-во времени до события "длинное нажатие"
#define TIMER_COUNT 250
// кол-во кнопок
#define KEY_COUNT 4
// время автоинкремента
#define TIMER_SCROLL 20

// состояние кнопки
enum {KN_DOWN =0, KN_UP =1, KN_PRESS=2 };
// тип функции кнопки
typedef byte(KeyFunc)(void);

// структура хранящая набор переменных для каждой кнопки
typedef struct key_struct__
{
byte state; // состояние кнопки
byte mask; // маска клавиши
byte pins; // буфер значений с клавиши
byte count; // счетчик нажатий
KeyFunc * exec_down;
KeyFunc * exec_up;
KeyFunc * exec_pres;
}key_struct;

// в .c-файле:
//*****************************************************************************
// Драйвер кнопок
// назначение кнопок
// 1- MiNUS 2-PLUS 3- Select 4- Enter
//*****************************************************************************

//=============================================================================
// состояния кнопок
static key_struct key_mass [KEY_COUNT] = {
//state mask pins count exec_down exec_up exec_pres
{KN_UP, PIN_KEY_MINUS, 0xFF,0x00, kn_empty, kn_minus_up ,kn_minus_up }
,{KN_UP, PIN_KEY_PLUS, 0xFF,0x00, kn_empty, kn_plus_up ,kn_plus_up }
,{KN_UP, PIN_KEY_SELECT, 0xFF,0x00, kn_empty, kn_select_up ,kn_empty }
,{KN_UP, PIN_KEY_ENTER, 0xFF,0x00, kn_empty, kn_empty ,kn_enter_press }
};
//=============================================================================

ISR(TIMER1_COMPA_vect)
{
#define Sta key->state
#define Pin key->pins
#define Cnt key->count
#define Msk key->mask
#define Down key->exec_down
#define Up key->exec_up
#define Pres key->exec_pres

key_struct * key = key_mass;
for(byte i=0; i< KEY_COUNT ; i++)
{
Pin *=2; // * or <<
if(KEY_PIN & Msk) Pin |= 0x01;

if(Sta == KN_UP)
{
if(!Pin) // начало счета - нажатие
{
Down();
Sta = KN_DOWN;
Cnt = 0x00;
}
}
else // down or press
{
if(Pin==0xFF) // отпустили
{
if(Sta == KN_DOWN) Up();
Sta = KN_UP;
}
else// удерживаем
{
if(Cnt <= TIMER_COUNT) ++Cnt;
if(Cnt == TIMER_COUNT) // длительное удержание - попадаем один раз!!
{
if(Pres()) Cnt = TIMER_COUNT - TIMER_SCROLL;
else Sta = KN_PRESS;
}
}
}
key++;
}
}


Таймер в CTC-режиме на порядка 5....15 мсек
777777
Цитата(Nikitoc @ Aug 22 2011, 01:35) *
Может я засиделся сегодня у компа, но что-то меня заклинило: как правильно писать обработчик прерывания по нажатию кнопки? Суть вопроса сводится к расположению задержки (~30мс) для "устранения " дребезга контактов кнопки. Ведь существует неписанное правило о том, что обработчики прерывания должны быть как можно короче по времени исполнения, а введение "антидребезговой" задержки этому как-то не способствует. Или это неизбежное зло и таким обработчикам просто следует назначать наименьший приоритет? Если тема боян - ткните носом. Не нашел.

А задержка в вашем представлении это обязательно delay_ms()? Мне например за свою жизнь никогда подобные задержки не требовались. Запусти таймер на 30 мс и сравни значение кнопки до и после.
SSerge
Сама идея прерываний от кнопки не очень хороша. Из-за дребезга придётся или отрабатывать десятки прерываний на каждое нажатие или мутить с их разрешением/запрещением. Обычно проще оказывается просто регулярно опрашивать кнопки в низкоприоритетном процессе, благо мгновенной реакции не требуется, задержку до 100 .. 150 мс человек не замечает.
Nikitoc
Спасибо всем за ответы. Прокомментируйте, пожалуйста, следующий вариант обработки нажатия кнопки: по нажатию на кнопку устанавливаем в обработчике флаг соотв. кнопки и запускаем таймер (и выходим из прерывания), по прерыванию от которого проверяем состояние кнопки (чей флаг предварительно был установлен) и устанавливаем флаг нажатия кнопок, который (в паре с флагом соотв. кнопки) обрабатываем в основной программе.

P.S. Учитывая пост Sserge добавляем запрещение соотв. прерывания в первом обработчике и разрешение во втором.
whiteTigr
Цитата(Nikitoc @ Aug 22 2011, 13:19) *
Спасибо всем за ответы. Прокомментируйте, пожалуйста, следующий вариант обработки нажатия кнопки: по нажатию на кнопку устанавливаем в обработчике флаг соотв. кнопки и запускаем таймер (и выходим из прерывания), по прерыванию от которого проверяем состояние кнопки (чей флаг предварительно был установлен) и устанавливаем флаг нажатия кнопок, который (в паре с флагом соотв. кнопки) обрабатываем в основной программе.

P.S. Учитывая пост Sserge добавляем запрещение соотв. прерывания в первом обработчике и разрешение во втором.


Возможно, заработает. Возможно, будет даже немного лучше чем вообще без антидребезга. Возможно...

По хорошему алгоритм в следующем:
Опрос кнопки в прерывании от таймера, раз в 0.5-5мс
Счетчик - от 0 до N, где N задает время антидребезга (порядка 10-200мс)
Буфер - состояние кнопки после антидребезга.

1. Если кнопка нажата и счетчик меньше максимума - прибавляем счетчик
2. Если кнопка отжата и счетчик больше нуля - убавляем счетчик
3. Если счетчик в максимуме, устанавливаем буфер в '1'
4. Если счетчик в нуле, устанавливаем буфер в '0'
5. По переходу буфера '0' -> '1' или '1' -> '0' делаем вывод о нажатии/отжатии кнопки.
MrYuran
Я вообще не вижу смысла вешать кнопку на вход прерывания, кроме одного случая - когда это используется для вывода процессора из спячки.
Равно как и использование отдельного таймера под обработку.
У меня сделано так:
В прерывании от системного таймера (Т=1мс) сканируется порт клавиатуры, и если обнаружено изменение состояния (NextState != PrevState), выставляется соответствующий флаг.
По этому флагу в основном цикле запускается обработчик, который отмеряет задержки, анализирует длительность нажатия (single/fixed) и запускает необходимые функции.
ILYAUL
Запускаешь АЦП и никаких тебе дребезгов и задержек очена подходит к случаю описанному alexeyv
whiteTigr
Цитата(ILYAUL @ Aug 22 2011, 16:36) *
Запускаешь АЦП и никаких тебе дребезгов и задержек очена подходит к случаю описанному alexeyv

Что-то я не совсем понял каким боком тут АЦП оказалось и как оно поможет с кнопкой.
ILYAUL
Цитата(whiteTigr @ Aug 23 2011, 08:57) *
Что-то я не совсем понял каким боком тут АЦП оказалось и как оно поможет с кнопкой.

А я не понял , что не понятного?
whiteTigr
Цитата(ILYAUL @ Aug 23 2011, 11:43) *
А я не понял , что не понятного?


Зачем целый АЦП выделять под кнопку?
ILYAUL
Цитата(whiteTigr @ Aug 23 2011, 11:47) *
Зачем целый АЦП выделять под кнопку?

А почему под кнопку? Я написал для кого проекта это удобно использовать.
Цитата
Запускаешь АЦП и никаких тебе дребезгов и задержек очена подходит к случаю описанному alexeyv

И к тому же один канал на 8 кнопок. Если уж очень постараться то и 12 кнопок влезут. Учитывая , что каналы (8 шт.) можно переключать во время работы и никто не запрещал менять настройки АЦП , во время работы, то проще выделить просто один канал под кнопки , а с отальными I/O делать , что душе угодно. В том числе и просто как входы -выходы
skripach
Ни в одном проекте не использовал прерывания для кнопок, опрос в прерывании таймера самое оно как по мне, там же и дребезг и фильтрация от помех.
ARV
вообще не понимаю, зачем для столь низкоприоритетного процесса, как работа с кнопками, задействовать прерывания или таймеры. в большинстве обычных ситуаций обработка кнопок премило делается в основном цикле путем поллинга с традиционными программными задержками - а вот фоновые процессы пусть крутятся в прерываниях.

прерывание требуется для обеспечения немедленной реакции на внешнее событие, причем "немедленно" - по меркам микроконтроллера, а не человека. назовите хотя бы какую-то ситуацию, где действительно необходимо реагировать на кнопку за 5-10 микросекунд. кстати - даже если придумаете, это будет бесполезно, т.к. подавление дребезга все равно заставит затянуть процесс до 10-15 миллисекунд, т.е. примерно в 1000 раз.
mempfis_
Для устранения дребезга контактов давно делаю так:
1. Завожу буффер например на 3 - 5 снятых состояния пинов клавиатуры и 2 переменных текущего и прошлого состояния кнопок.
2. По таймеру раз в 5-10 мс считываю текущее состояние пинов и заношу его в буффер на позицию самого старого показания
3. Если нажатым считается состояние 0 то делаю or всех элементов буффера, если 1 - то соотв. &
4. Сохраняю старое значение состояния в переменную прошлого состояния, в переменную текущего состояния заношу результат логической операции над буффером
5. Получаю результат сканирования кнопок с антидребезгом.

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

_Pasha
Если кнопки на АЦП, то лучше пройдет поллинг с использованием системного таймера, тикающего 1мс
И еще, приведу случай, где я не использую прерывания вообще, ... ну максимум если там уарт на больше 19200 ходит
mega8/16, 8МГц, 1 канал ацп, светодиодный индикатор 3+ позиций, кнопок сколько угодно (в смысле - до 16) и вычисления не касаются плавучки.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.