Опрос кнопки

Обсуждаем контроллеры компании Atmel.
Закрыто
Открыл глаза
Аватара пользователя
Сообщения: 56
Зарегистрирован: Пт июн 17, 2011 12:49:59

Сообщение 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
Processor not found! Software emulation ...
Реклама
Вымогатель припоя
Сообщения: 513
Зарегистрирован: Сб фев 19, 2011 18:04:08
Откуда: Татарстан, пос. Актюбинский

Сообщение 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   

      }
Контактная информация:
Реклама
Открыл глаза
Аватара пользователя
Сообщения: 56
Зарегистрирован: Пт июн 17, 2011 12:49:59

Сообщение skeef »

К сожалению, Ваше предложение особо не меняет логику. А обнуление butR также ничего не даст (но все равно проверю, спасибо :) )
Дело еще в том, что если я делаю следующий код:

Код: Выделить всё


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

По поводу работы без внешнего прерывания - не получается, т.к. в схеме сегментный LCD индикатор управляемый этим же камнем. Кнопки отрабатывались с очень большими задержками ...
В любом случае уже стало просто интересно как решить данную проблему, т.е. определение длинного и короткого нажатия кнопки с использованием внешнего прерывания. Интернет рыт несколько дней и ничего кроме теоретических размышлений я найти не смог :(
Последний раз редактировалось skeef Вс мар 09, 2014 13:14:03, всего редактировалось 1 раз.
Processor not found! Software emulation ...
Вымогатель припоя
Сообщения: 513
Зарегистрирован: Сб фев 19, 2011 18:04:08
Откуда: Татарстан, пос. Актюбинский

Сообщение phanis »

PORTE или PINE?
Контактная информация:
Реклама
Эиком - электронные компоненты и радиодетали
Открыл глаза
Аватара пользователя
Сообщения: 56
Зарегистрирован: Пт июн 17, 2011 12:49:59

Сообщение skeef »

Жму руку, брат! Безсонная ночь ....
Processor not found! Software emulation ...
Реклама
Вымогатель припоя
Сообщения: 513
Зарегистрирован: Сб фев 19, 2011 18:04:08
Откуда: Татарстан, пос. Актюбинский

Сообщение phanis »

Бывает, постарайтесь без внешнего прерывания, таймер же все равно молотит. В протеусе все гладко, в реале дребезг, наводки всплески, и тд.
Контактная информация:
Реклама
Опытный кот
Аватара пользователя
Сообщения: 882
Зарегистрирован: Ср фев 22, 2012 01:25:21

Сообщение 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)     {.....}//обработка длинного нажатия ВЛЕВО
} 
Открыл глаза
Аватара пользователя
Сообщения: 56
Зарегистрирован: Пт июн 17, 2011 12:49:59

Сообщение skeef »

Я начинающий, это первый самостоятельный (с нуля) большой проект. Спасибо за советы, парни, уже меняю алгоритм.
Разобрался с тормозами.
Основная проблема - это использование больших или "тяжелых" процедур непосредственно в прерываниях. Код, конечно же бы вынесен в отдельные функции, вот только вызывались они у меня прямо в прерывании. Правильнее же, как я теперь понял, регистрировать факт прерывания, а обработку его выполнять в основном цикле. К сожалению об этом нигде не пишут ...
Processor not found! Software emulation ...
Опытный кот
Аватара пользователя
Сообщения: 882
Зарегистрирован: Ср фев 22, 2012 01:25:21

Сообщение shads »

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

Вообще то многие для решения таких задач используют всякие RTOS... но я например еще не пробовал работать с этими монстрами... вроде бы все и так решается, с помощью флагов.....
Вымогатель припоя
Сообщения: 513
Зарегистрирован: Сб фев 19, 2011 18:04:08
Откуда: Татарстан, пос. Актюбинский

Сообщение 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;
}
Контактная информация:
Опытный кот
Аватара пользователя
Сообщения: 882
Зарегистрирован: Ср фев 22, 2012 01:25:21

Сообщение shads »

Действительно... результат аналогичный...

Я сначала не понял зачем там такой кульбит с if... но потом понял что таким образом, без применения cli() и sei() можно исключить случайную потерю результатов....
Родился
Сообщения: 12
Зарегистрирован: Вс дек 14, 2014 10:51:16

Сообщение ZmiterIv »

Добрый день!
Помогите, плз, начинающему. Пытаюсь разобраться с библиотекой опроса кнопок, но уже на этапе подключения библиотеки к проекту в CVAVR получаю ошибки: первая сразу при объявлении переменной

Код: Выделить всё

volatile uint8_t BtnFlags;                        //байт флагов нажатия кнопки
ошибка - Error: button_lib.h(32): '(' expected. Что это значит?
Спасибо!
Модератор
Аватара пользователя
Сообщения: 4614
Зарегистрирован: Чт мар 18, 2010 23:09:57
Откуда: Планета Земля

Сообщение Аlex »

Что за библиотека опроса кнопок ? Зачем для кнопок нужна какая-то библиотека ?
Скоро люди будут искать библиотеки для зажигания светодиода :facepalm:

По кнопкам есть уже отдельная тема, постите туда.
Закрыто

Вернуться в «AVR»