Алгоритм преодоления дребезга контактов

Вопросы настройки, программирования, прошивки микроконтроллеров и микросхем программируемой логики
Закрыто
karimm
Родился
Сообщения: 5
Зарегистрирован: Сб июн 18, 2011 21:19:47

Алгоритм преодоления дребезга контактов

Сообщение karimm »

Добрый День!
Это мое первое сообщение на этом форуме, прошу не пинать :?

Подскажите пожалуйста алгоритм преодаления дребезга контактов
1) с исключением повтора нажатия кнопки
2) без использования задрежки

в кратце о схеме
на одном входе atmega8 речезе АЦП подключено 10 кнопок, через резистры разных номиналов происходит деление напряжения.

алгоритм работы такой
1) настроено прерывание по изменению АЦП
2) если оно входит в заданный диапазон значений, то переменной присваивается номер нажатой кнопки (Key)
3) если этот номер повторяется в течении 100 преобразований то значит дребезга нет
4) вызываю функцию соответсвующей нажатой кнопке (функция тригерная - первый заход один результат, второй другой)
5) ...


вот тут и начинается проблема функия вызывается много раз

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

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


  //защита от дребезга и не только. если нажата какая-нибудь кнопка
  //сравниваем совпадают ли текущее состояние и предыдущее
  //если совпадают - проверяем счетчик comp, если нет обнуляем его
  //кнопка считается нажатой если она удерживается в течении 100
  
  if (Key)
  { 
    if (Key == LastState)
    {
      if (comp > 100)
       {
         if (Key!=LastBut) 
         {  
            Key_p=Key;        
            out_trig();  //  выполняю действия
            LastBut = Key; // переменную lastBut  сбрасываю каждую секунду
         }
         else
         {
         
         }
      }
      else 
        comp++;
    }
    else   // сбрасываем счетчик
    {
      LastState = Key;
      comp = 0;
    }
  }
  else            // кнопка не нажата 
  {
    comp = 0;
    KeyBuf = KEY_NULL;      
    LastState = KEY_NULL;
  }
  StartConvAdc();  //запускаем преобразование и выходим



ps
камень Atmega8
компилятор CodeVisionAVR
Последний раз редактировалось karimm Сб июн 18, 2011 22:05:44, всего редактировалось 1 раз.
Реклама
Аватара пользователя
ARV
Ум, честь и совесть. И скромность.
Сообщения: 18656
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск
Контактная информация:

Re: Алгоритм преодоления дребезга контактов

Сообщение ARV »

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

Мой уютный бложик... заходите!
Реклама
karimm
Родился
Сообщения: 5
Зарегистрирован: Сб июн 18, 2011 21:19:47

Re: Алгоритм преодоления дребезга контактов

Сообщение karimm »

с дребезгом более менее все ок, проблема с повтором

как обойтись без таймера?

если я нажимаю одну и туже кнопку два раза подряд то должна срабатывать соответсвенная функция
как отличить мое повторное нажатие от "задвоения" результата от АЦП?
Аватара пользователя
ARV
Ум, честь и совесть. И скромность.
Сообщения: 18656
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск
Контактная информация:

Re: Алгоритм преодоления дребезга контактов

Сообщение ARV »

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

Мой уютный бложик... заходите!
Реклама
Эиком - электронные компоненты и радиодетали
Аватара пользователя
ibiza11
Поставщик валерьянки для Кота
Сообщения: 1900
Зарегистрирован: Сб фев 21, 2009 13:11:40
Откуда: Москва

Re: Алгоритм преодоления дребезга контактов

Сообщение ibiza11 »

karimm писал(а):с дребезгом более менее все ок, проблема с повтором
как обойтись без таймера?
не смотрел исходники ARV, извиняюсь, если повторяю алгоритм. У меня было реализовано почти как у Вас, karimm, но я ввел флаг не секундного таймера, а флаг того, что кнопка уже нажата.
в прерывании, где Вы определяете нажатую кнопку, после проверки значения счетчика длительности нажатия (в Вашем коде это переменная comp), добавьте проверку этого флага.
Если флаг уже возведен, то значит функция, назначенная кнопке уже была выполнена и еще раз ее выполнять не нужно.
Если флаг не возведен, выполняете действие и возводите этот флаг.
В самом начале прерывания, если не нажата ни одна кнопка (у Вас же есть такое значение АЦП?), сбрасываете этот флаг.

Еще, т.к. у Вас много кнопок, а на каждую заводить счетчик comp нет смысла, и значения АЦП могут иногда скакать, я бы посоветовал Вам завести счетчик прерываний, а не счетчик для каждой кнопки, просто суммировать 128 значений АЦП подряд, а на 128ое прерывание делить значение суммы на 128 (= сдвиг на 7 разрядов вправо) и проверять в какой интервал попадает усредненное значение. Я так делал. Стабильных нажатий стало больше.
Ставим плюсы: )
Реклама
karimm
Родился
Сообщения: 5
Зарегистрирован: Сб июн 18, 2011 21:19:47

Re: Алгоритм преодоления дребезга контактов

Сообщение karimm »

Спасибо за идеюibiza11

вот что получилось, в шпроте работает хорошо :))

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

  if (Key)
  { 
    if (Key == LastState)
    {
      if (comp > 100)
       {
         if (Key!=LastBut) 
         {          
            out_trig(); 
            LastBut = Key;
            printf("LastBut %u ",LastBut)  ;
            printf("\n\r");            
         }
         else
         {
            printf("Povtor %u ",LastBut)  ;
            printf("\n\r");
         }
      }
      else 
        comp++;
    }
    else
    {
      LastState = Key;
      comp = 0;
    }
  }
  else
  {
    LastBut =0;
    comp = 0;
    KeyBuf = KEY_NULL;
    LastState = KEY_NULL;
  }

не понял зачем заводить переменную для каждой кнопки?

вот как сделал определение нажатой кнопки

есть массив btn_arr в котором записаны расчетные значения ацп для каждой кнопки,
для того чотбы точно отловить нужную кнопку проверяю входит ли значение полученное с АЦП в диапазон х+rk
дальше все понятно

у меня в схеме параллельно кнопкам размещен конденсатор, и пока он разряжается/заряжается показания АЦП могут плавать,
по этому решил вылавливать поворные попадания в заданный диапазон

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

    AdcBuf = ADCH;
    
  if (AdcBuf < btn_arr[0]+rk) 
    {
    Key = KEY_S0;
    #ifdef DEBUG
    printf("%u ",Key);
    printf("%u ",AdcBuf);
    printf("\n\r");
    #endif
    }
  else if  (AdcBuf < btn_arr[1]+rk) 
      {
        Key = KEY_S1;
        #ifdef DEBUG
        printf("%u ",Key);
        printf("%u ",AdcBuf);
        printf("\n\r");
        #endif
      }
 /// отрезал лишние кнопки

  else if  (AdcBuf > btn_arr[16]+rk) 
      {
        Key = KEY_NULL;
        #ifdef DEBUG
        //printf("%u ",Key);
        //printf("%u ",AdcBuf);
        //printf("\n\r");
        #endif
      } 


как можно оптимизировать код, с многократно вложенным if ?
Реклама
Аватара пользователя
ibiza11
Поставщик валерьянки для Кота
Сообщения: 1900
Зарегистрирован: Сб фев 21, 2009 13:11:40
Откуда: Москва

Re: Алгоритм преодоления дребезга контактов

Сообщение ibiza11 »

Ну вот смотрите: у вас программа работает так:
1) прерывание АЦП - выявление кода нажатой кнопки. (ну пусть 100 тактов)
2) устранение дребезга и выполнение действия (ну пусть 50 тактов)
3) возврат к пункту 1) (и так 100 раз).
итого за 100 прерываний АЦП - забито 15000 тактов процессорного времени

Я предложил (для простоты возьмем не 128, как я сказал, а 100 прерываний):
1) прерывание АЦП, суммирование результата (пусть 20 тактов)
2) 100 прерываний прошло? (пусть 20 тактов)
да) деление на 100 и выявление нажатой кнопки (пусть 300 тактов, в случае деления на 128 это около 10+100 тактов)
нет) выход
3) устранение дребезга если было выявление нажатой кнопки(если (Key!=KEY_NULL) ) (50 тактов)
итого за 100 прерываний АЦП - забито (20+20)*100+300+50=4350 тактов. :) выигрыш в 3 раза. и еще не нужны кондеры на входе АЦП)

насчет счетчиков на каждую кнопку, я погорячился, сначала не разглядел полностью Ваш алгоритм.
Ставим плюсы: )
eufs
Опытный кот
Сообщения: 772
Зарегистрирован: Вс апр 10, 2011 02:24:06
Откуда: г.Северодонецк

Re: Алгоритм преодоления дребезга контактов

Сообщение eufs »

Если в Асме разбираешься, то посмотри, как сделано подавление, задержки и автоповтор.
Из основного цикла подпрограмма вызывается, проверяется код нажатой кнопки и выполняется действие. Для твоего случая нужно только изменить ПП предварительного ввода.
Вложения
ПП кнопок.txt
(1.47 КБ) 337 скачиваний
C0FFEE=‭12648430‬
karimm
Родился
Сообщения: 5
Зарегистрирован: Сб июн 18, 2011 21:19:47

Re: Алгоритм преодоления дребезга контактов

Сообщение karimm »

Спасибо ibiza11, eufs
2eufs Был небольшой опыт работы с АСМ но под пик - больше не тянет =)


2 ibiza11 почему именно 128? не могли бы вы показать Свой исходинк.

2 ARV очень трудно найти нужный пост, когда их под 6к =)

вот что нашел среди Ваших ссылок

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

uint8_t buf[9];							// массив для приема данных из датчика

/** Опрос кнопки.
 * Функция возвращает результат опроса кнопки: 0 - не нажата, 1 - было однократное нажатие,
 * 2 - кнопка нажата и удерживается более 2,5 секунд (примерно).
 * @return - возвращаемое значение
 */
uint8_t get_key(void){
	static uint8_t repeat = 0;						// флаг автоповтора кода кнопки
	// нажатой кнопке соответствует 0 на выводе
	if(!(PINB & _BV(3))){
		// обнаружен 0 - отфильтруем дребезг
		_delay_ms(15);
		if(!(PINB & _BV(3))){
			// через 15 мс (время дребезга) все еще 0
			if(repeat) {
				// идет удержание - делаем автоповтор через 100 мс
				wdt_reset();
				_delay_ms(100);
				return 2;
			}
			// проверим, будет ли кнопка удержана более 2,5 секунд
			for(uint8_t i=0; i<250; i++){
				if(PINB & _BV(3))
					return 1;	// если менее - считаем нажатие однократным
				wdt_reset();
				_delay_ms(10);
			}
			// если держали 2,5 секунды - установим признак автоповтора и вернем соотв. результат
			repeat = 1;
			return 2;
		}
	}
	// если не обнаружено нажатие кнопки - вернем ноль, сбросив флаг автоповтора
	repeat = 0;
	return 0;
}
но мне кажется это немного не то... здесь используются задержки, мне хотелось бы без задержек...
Аватара пользователя
IfoR
Поставщик валерьянки для Кота
Сообщения: 2029
Зарегистрирован: Сб ноя 15, 2008 10:09:56
Откуда: г. Тула
Контактная информация:

Re: Алгоритм преодоления дребезга контактов

Сообщение IfoR »

А почему бы не...:

1) ... удалить бы всё лишнее:

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


  //защита от дребезга и не только. если нажата какая-нибудь кнопка
  //сравниваем совпадают ли текущее состояние и предыдущее
  //если совпадают - проверяем счетчик comp, если нет обнуляем его
  //кнопка считается нажатой если она удерживается в течении 100
  
  if (Key)
  { 
      if (comp++ > 100)
       {
            Key_p=Key;        
            out_trig();  //  выполняю действия
       }
  }
  else            // кнопка не нажата 
  {
    comp = 0;
   
  }
  StartConvAdc();  //запускаем преобразование и выходим
2) ... вставить нужное:

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

  // Где-нибуть выше
  int KeyPressed = 0;  
  
  ...

  //защита от дребезга и не только. 
  //кнопка считается нажатой если она удерживается в течении 100

  if (Key)
  { 
      if (!KeyPressed && comp++ > 100)
       {
	    KeyPressed = 1;
            //Key_p=Key;        
            //out_trig();  //  выполняю действия
	    out_trig(Key); // Лучше всё же так. По усмотрению.
       }
  }
  else            // кнопка не нажата 
    comp = KeyPressed = 0;

  StartConvAdc();  //запускаем преобразование и выходим
Так сойдёт?
Изображение
/dev/urandom - гигабайты информации.

OS: openSUSE 13.2 (x86_64)
Аватара пользователя
ibiza11
Поставщик валерьянки для Кота
Сообщения: 1900
Зарегистрирован: Сб фев 21, 2009 13:11:40
Откуда: Москва

Re: Алгоритм преодоления дребезга контактов

Сообщение ibiza11 »

128 потому что делить на степень двойки проще всего.
Исходника нет, вот накидал на скорую руку:

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

/*это обработчик прерывания окончания преобразования АЦП */
static unsigned short SumADC=0;
static unsigned char Cnt=0;
#define MAX_CNT 128
SumADC+=ADCH;
Key=KEY_NULL;//это из Вашего исходника
if (++Cnt==MAX_CNT)
{
	Cnt=0;
	SumADC=(SumADC>>7); //деление на 128
	/* здесь алгоритм определения нажатой кнопки из Вашего исходника */
}


/*Это главный модуль, здесь делаете то же самое, что и делали кроме счетчика comp*/
 if (Key)
  { 
    if (Key == LastState)
    {
      if (Key!=LastBut) 
      {          
         out_trig(); 
         LastBut = Key;
         printf("LastBut %u ",LastBut)  ;
         printf("\n\r");            
      }
      else
      {
         printf("Povtor %u ",LastBut)  ;
         printf("\n\r");
      }     
    }
    else
    {
      LastState = Key;
    }
  }
  else
  {
    LastBut =0;
    KeyBuf = KEY_NULL;
    LastState = KEY_NULL;
  }
Ставим плюсы: )
Закрыто

Вернуться в «Микроконтроллеры и ПЛИС»