Страница 1 из 1
Опрос кнопки
Добавлено: Вс мар 09, 2014 12:32:35
skeef
Всем привет!
На Mega128 использую 3 кнопки по прерыванию INT5-INT7.
Ноги подтянуты килоомными резисторами на плюс, кнопки коротят на землю. Ноги настроены на вход с подтягивающими резисторами.
Стоит задача отследить длинное и короткое нажатие кнопки.
Прерывание отрабатывает без вопросов, но не могу зарегистрировать отпускание кнопки.
Код такой (сокращенно):
Код: Выделить всё
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#define LED1 PORTE0 // светодиоды управляются 0
#define LED2 PORTE1
#define LED3 PORTE2
#define LED4 PORTE3
#define LED5 PORTE4
#define BUTNR PINE5 // кнопка R
...
// Глобальные переменные
volatile unsigned int butR;
volatile unsigned int butScnt;
volatile unsigned int butSflag;
volatile unsigned int butShort;
volatile unsigned int butLong;
// External Interrupt 5 R
ISR(INT5_vect)
{
butR=1; // сработала кнопка R
}
// Timer0 64Hz
ISR(TIMER0_COMP_vect)
{
if(butSflag==1) // флаг старта счетчика
{
butScnt++; // счетчик
if(!(PORTE & (1<<BUTNR))) // нажата кнопка R
{
if(butScnt>128)
butLong=1; // 2 секунды задержка - длинное нажатие
}
else
butShort=1; // короткое нажатие
}
}
int main(void)
{
_delay_ms(1000);
funcInit();
butR = 0; // Кнопка R
butScnt=0; // счетчик длины нажатия
butSflag=0; // флаг кнопки
butShort=0; // короткое нажатие
butLong=0; // длинное нажатие
sei();
while(1)
{
if(butR==1) // сработала кнопка R
{
butSflag=1; // разрешаем счетчик в прерывании 64Hz
}
if(butShort==1) // короткая кнопка
{
butR=0;
butSflag=0;
PORTE &= (~(1<<LED4)); // зажигаем 4 диод
}
else if(butLong==1) // длинная кнопка
{
butR=0;
butSflag=0;
PORTE &= (~(1<<LED5)); // зажигаем 5 диод
}
}
}
void funcInit(void)
{
...
// Port E initialization
// Function: Bit7=In Bit6=In Bit5=In Bit4=Out Bit3=Out Bit2=Out Bit1=Out Bit0=Out
DDRE=(0<<DDE7) | (0<<DDE6) | (0<<DDE5) | (1<<DDE4) | (1<<DDE3) | (1<<DDE2) | (1<<DDE1) | (1<<DDE0);
// State: Bit7=T Bit6=T Bit5=T Bit4=1 Bit3=1 Bit2=1 Bit1=1 Bit0=1
PORTE=(0<<PORTE7) | (0<<PORTE6) | (0<<PORTE5) | (1<<PORTE4) | (1<<PORTE3) | (1<<PORTE2) | (1<<PORTE1) | (1<<PORTE0);
...
/ Interrupts
TIMSK = (0<<OCIE2) | (0<<TOIE2) | (0<<TICIE1) | (1<<OCIE1A) | (0<<OCIE1B) | (0<<TOIE1) | (1<<OCIE0) | (0<<TOIE0);
ETIMSK=(0<<TICIE3) | (1<<OCIE3A) | (0<<OCIE3B) | (0<<TOIE3) | (0<<OCIE3C) | (0<<OCIE1C);
EICRA=(0<<ISC31) | (0<<ISC30) | (0<<ISC21) | (0<<ISC20) | (0<<ISC11) | (0<<ISC10) | (0<<ISC01) | (0<<ISC00);
EICRB=(1<<ISC71) | (0<<ISC70) | (1<<ISC61) | (0<<ISC60) | (1<<ISC51) | (0<<ISC50) | (0<<ISC41) | (0<<ISC40);
EIMSK=(1<<INT7) | (1<<INT6) | (1<<INT5) | (0<<INT4) | (0<<INT3) | (0<<INT2) | (0<<INT1) | (0<<INT0);
EIFR=(1<<INTF7) | (1<<INTF6) | (1<<INTF5) | (0<<INTF4) | (0<<INTF3) | (0<<INTF2) | (0<<INTF1) | (0<<INTF0);
}
Всегда срабатывает 5 диод длинного нажатия. Такое впечатление что кнопка буферизируется

Всем заранее спасибо за помощь
Proteus 7.8
Re: Опрос кнопки
Добавлено: Вс мар 09, 2014 12:52:37
phanis
Вроде как здесь не правильно организовано
Код: Выделить всё
// Timer0 64Hz
ISR(TIMER0_COMP_vect)
{
if(butSflag==1) // флаг старта счетчика
{
butScnt++; // счетчик
if(!(PORTE & (1<<BUTNR))) // нажата кнопка R
{
if(butScnt>128)
butLong=1; // 2 секунды задержка - длинное нажатие
}
else
butShort=1; // короткое нажатие
}
}
нужно примерно так
если кнопка нажата регистр
PINx то увеличиваем счетчик butScnt++;
если счетчик досчитал > 128 тогда butLong=1;butSflag=0
если кнопку отпустили раньше
обнуляем счетчик butScnt = 0, butShort=1; butSflag=0
Если есть таймер в обработчике которого мы следим за состоянием кнопок можно обойтись без
// External Interrupt 5 R
ISR(INT5_vect)
еще добавлю. в основной функции
Код: Выделить всё
if(butR==1) // сработала кнопка R
{
butR =0; // поймали нажатие обнулили состояние
butSflag=1; // разрешаем счетчик в прерывании 64Hz
}
Re: Опрос кнопки
Добавлено: Вс мар 09, 2014 13:07:23
skeef
К сожалению, Ваше предложение особо не меняет логику. А обнуление butR также ничего не даст (но все равно проверю, спасибо

)
Дело еще в том, что если я делаю следующий код:
Код: Выделить всё
if((PORTE & (1<<BUTNR))!=0)
{
butScnt++;
if(butScnt>256)
butLong=1;
}
else
{
butScnt=0;
butShort=1;
}
т.е. условие проверяет, что кнопка отпущена, то срабатывает butShort. Т.е. получается что кнопка всегда нажата.
Я ничего не понимаю

Может протеус глючит?
По поводу работы без внешнего прерывания - не получается, т.к. в схеме сегментный LCD индикатор управляемый этим же камнем. Кнопки отрабатывались с очень большими задержками ...
В любом случае уже стало просто интересно как решить данную проблему, т.е. определение длинного и короткого нажатия кнопки с использованием внешнего прерывания. Интернет рыт несколько дней и ничего кроме теоретических размышлений я найти не смог

Re: Опрос кнопки
Добавлено: Вс мар 09, 2014 13:12:33
phanis
PORTE или PINE?
Re: Опрос кнопки
Добавлено: Вс мар 09, 2014 13:15:57
skeef
Жму руку, брат! Безсонная ночь ....
Re: Опрос кнопки
Добавлено: Вс мар 09, 2014 13:20:09
phanis
Бывает, постарайтесь без внешнего прерывания, таймер же все равно молотит. В протеусе все гладко, в реале дребезг, наводки всплески, и тд.
Re: Опрос кнопки
Добавлено: Вс мар 09, 2014 17:20:51
shads
phanis писал(а):постарайтесь без внешнего прерывания, таймер же все равно молотит. В протеусе все гладко, в реале дребезг, наводки всплески, и тд.
+1...
Через прерывание стОит делать - только если энергосбережение заставляет...
Иначе только на опросе строить надо... Другие задачи мешать не должны, т.к. опрашиваются кнопки в цикличном прерывании, ну а запрещать прерывания на долго - это уже признак непрофессионализма... так что если программа построена правильно - все будет ОК...
Я использую такой вариант (реализована обработка 4-х кнопок, короткие и длинные нажатия):
Код: Выделить всё
//#######################################################################################################################
//#
//# ФУНКЦИИ РАБОТЫ С КЛАВОЙ
//#
//# в начале проинитить библиотеку вызовом BtnInit();
//# организовать вызов из прерывания с частотой 100Гц - функцию BtnExe();
//# чтение значения состояния флагов кнопок производится с помощью функции BtnGet();
//#
//#######################################################################################################################
//настройка параметров работы функций
#define BTN_LOCK_TIME 30 /*время обработки дребезга в милисекундах (10-100)*/
#define BTN_LONG_TIME 1000 /*время фиксации длинного нажатия в милисекундах (1000 - 2500)*/
//настройки портов
#define BTN_PORT PORTC /*порт чтения кнопок*/
#define BTN_DDR DDRC
#define BTN_PIN PINC
#define BTN_LINE_UP (1<<0) /*пины чтения кнопок*/
#define BTN_LINE_DN (1<<1)
#define BTN_LINE_LEFT (1<<2)
#define BTN_LINE_RIGHT (1<<3)
//#######################################################################################################################
//глобальные переменные
volatile uint8_t BtnFlags; //байт флагов нажатия кнопки
#define BTN_SHRT_UP (1<<0) /*бит короткого нажатия кнопки up*/
#define BTN_SHRT_DN (1<<1) /*бит короткого нажатия кнопки dn*/
#define BTN_SHRT_LEFT (1<<2) /*бит короткого нажатия кнопки left*/
#define BTN_SHRT_RIGHT (1<<3) /*бит короткого нажатия кнопки right*/
#define BTN_LONG_UP (1<<4) /*бит длинного нажатия кнопки up*/
#define BTN_LONG_DN (1<<5) /*бит длинного нажатия кнопки dn*/
#define BTN_LONG_LEFT (1<<6) /*бит длинного нажатия кнопки left*/
#define BTN_LONG_RIGHT (1<<7) /*бит длинного нажатия кнопки right*/
//-----------------------------------------------------------------------------------------------------------------------
//функция настройки библиотеки работы с кнопками
void BtnInit (void)
{
BTN_DDR &= ~(BTN_LINE_UP| BTN_LINE_DN| BTN_LINE_LEFT| BTN_LINE_RIGHT); //на ввод
BTN_PORT |= (BTN_LINE_UP| BTN_LINE_DN| BTN_LINE_LEFT| BTN_LINE_RIGHT); //подтяжка вкл
}
//-----------------------------------------------------------------------------------------------------------------------
//функция чтения данных о нажатии кнопок
char BtnGet (void)
{
cli ();
char temp = BtnFlags;
BtnFlags = 0;
sei ();
return temp;
}
//-----------------------------------------------------------------------------------------------------------------------
//ФУНКЦИЯ ОБРАБОТКИ НАЖАТИЙ КЛАВИШ (вызывать в прерывании с частотой 100 Гц)
//короткое нажатие устанавливает бит BTN_SHRT_X глобальной переменной BtnFlags
//длинное нажатие устанавливает бит BTN_LONG_X глобальной переменной BtnFlags
void BtnExe (void)
{
static unsigned char BtnLockBit; //ащелка (защита от дребезга)
static unsigned char BtnLockCoun; //счетчик защелки (защита от дребезга)
static unsigned char BtnLongCoun; //счетчик длинного нажатия
static unsigned char BtnLastState; //последнее состояние кнопок перед отпусканием
char mask = 0;
if (! (BTN_PIN & BTN_LINE_UP)) mask = BTN_SHRT_UP;
if (! (BTN_PIN & BTN_LINE_DN)) mask = BTN_SHRT_DN;
if (! (BTN_PIN & BTN_LINE_LEFT)) mask = BTN_SHRT_LEFT;
if (! (BTN_PIN & BTN_LINE_RIGHT)) mask = BTN_SHRT_RIGHT;
if (mask){ //опрос состояния кнопки
if (BtnLockCoun < (BTN_LOCK_TIME/10)){ //клавиша нажата
BtnLockCoun++;
return; //защелка еще не дощитала - возврат
}
BtnLastState = mask;
BtnLockBit =1; //нажатие зафиксировано
if (BtnLongCoun >= (BTN_LONG_TIME/10))
return; //возврат, т.к. счетчик длинн нажат досчитал до максимума еще раньше
if (++BtnLongCoun >= (BTN_LONG_TIME/10))
BtnFlags |= (BtnLastState<<4); //счетчик досчитал до максимума - устанавливаем биты длинного нажатия
}
else{ //клавиша отжата
if (BtnLockCoun){
BtnLockCoun --;
return; //защелка еще не обнулилась - возврат
}
if (! BtnLockBit) //СТАТИЧЕСКИЙ ВОЗВРАТ
return;
BtnLockBit =0; //отжатие зафиксировано
if (BtnLongCoun < (BTN_LONG_TIME/10))
BtnFlags |= BtnLastState; //установка бита короткого нажатия
BtnLongCoun = 0; //сброс счетчика длительности нажатия
}
}
//#######################################################################################################################
Пример использования:
Код: Выделить всё
while (1){
char BtnMask = BtnGet ();
if (BtnMask == BTN_SHRT_UP) {.....}//обработка короткого нажатия ВВЕРХ
if (BtnMask == BTN_SHRT_DN) {.....}//обработка короткого нажатия ВНИЗ
if (BtnMask == BTN_SHRT_RIGHT) {.....}//обработка короткого нажатия ВПРАВО
if (BtnMask == BTN_SHRT_LEFT) {.....}//обработка короткого нажатия ВЛЕВО
if (BtnMask == BTN_LONG_UP) {.....}//обработка длинного нажатия ВВЕРХ
if (BtnMask == BTN_LONG_DN) {.....}//обработка длинного нажатия ВНИЗ
if (BtnMask == BTN_LONG_RIGHT) {.....}//обработка длинного нажатия ВПРАВО
if (BtnMask == BTN_LONG_LEFT) {.....}//обработка длинного нажатия ВЛЕВО
}
Re: Опрос кнопки
Добавлено: Вс мар 09, 2014 17:47:26
skeef
Я начинающий, это первый самостоятельный (с нуля) большой проект. Спасибо за советы, парни, уже меняю алгоритм.
Разобрался с тормозами.
Основная проблема - это использование больших или "тяжелых" процедур непосредственно в прерываниях. Код, конечно же бы вынесен в отдельные функции, вот только вызывались они у меня прямо в прерывании. Правильнее же, как я теперь понял, регистрировать факт прерывания, а обработку его выполнять в основном цикле. К сожалению об этом нигде не пишут ...
Re: Опрос кнопки
Добавлено: Вс мар 09, 2014 18:18:01
shads
Да, вы все правильно поняли...
В прерывании просто устанавливаем флаги... а уже в главном файле эти флаги опрашиваются ну и после отработки сбрасываются...
Вообще то многие для решения таких задач используют всякие RTOS... но я например еще не пробовал работать с этими монстрами... вроде бы все и так решается, с помощью флагов.....
Re: Опрос кнопки
Добавлено: Вс мар 09, 2014 18:27:48
phanis
shads писал(а):
Код: Выделить всё
//функция чтения данных о нажатии кнопок
char BtnGet (void)
{
cli ();
char temp = BtnFlags;
BtnFlags = 0;
sei ();
return temp;
}
У меня не много по другому
Код: Выделить всё
char BtnGet (void)
{
char temp = BtnFlags;
if (temp) BtnFlags = 0;
return temp;
}
Re: Опрос кнопки
Добавлено: Вс мар 09, 2014 19:03:10
shads
Действительно... результат аналогичный...
Я сначала не понял зачем там такой кульбит с if... но потом понял что таким образом, без применения cli() и sei() можно исключить случайную потерю результатов....
Re: Опрос кнопки
Добавлено: Вс дек 14, 2014 11:28:53
ZmiterIv
Добрый день!
Помогите, плз, начинающему. Пытаюсь разобраться с библиотекой опроса кнопок, но уже на этапе подключения библиотеки к проекту в CVAVR получаю ошибки: первая сразу при объявлении переменной
Код: Выделить всё
volatile uint8_t BtnFlags; //байт флагов нажатия кнопки
ошибка - Error: button_lib.h(32): '(' expected. Что это значит?
Спасибо!
Re: Опрос кнопки
Добавлено: Вс дек 14, 2014 17:07:29
Аlex
Что за библиотека опроса кнопок ? Зачем для кнопок нужна какая-то библиотека ?
Скоро люди будут искать библиотеки для зажигания светодиода
По кнопкам есть уже
отдельная тема, постите туда.