Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Подавление дребезга контактов
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему
Axxel
Привет всем!

Написал небольшой кусочек кода для
подавления дребезга контактов, IMHO данный
код должен "фильтровать" ложные срабатывания
менее заданного периода.
Но почему-то не покидает ощущение того, что
в коде могут оказаться грабли smile.gif

Какие еще есть правильные методы,
либо можно это сделать проще или красивее?

Пример для одного бита порта ввода:

#define BOUNCE_CANCELLATION_PERIOD 20


unsigned char BITcounter=0; //счетчик состояний бита
bit BITstate=0; //используемое (устойчивое) состояние бита (более 20 мсек)
bit BITmemory=0; // промежуточная переменная, запоминающаяя предыдущее состояние бита
bit toggled=0; //флаг,указывающий на изменение состояния бита

interrupt Timer0() //возникает 1 раз в мсек
{
if(BITmemory^BITn){if(toggled){BITcounter=0;} toggled=1;}
if(toggled ){BITcounter++;}
if(BITcounter==BOUNCE_CANCELLATION_PERIOD){BITstate=BITn;BITcounter=0;toggled=0;
}
BITmemory=BITn; //чтение с порта
}



Вот, примерно так...
Oleg_IT
Давно и хорошо работающий код. Обслуживает несколько клавиш.

unsigned long int debounced_state = 0,debounced_stateOld = 0;
unsigned long int A = 0x0;
unsigned long int B = 0x0;
unsigned long int C = 0x0;

void debounce(unsigned long int new_sample)
{
unsigned long int delta;

delta = new_sample ^ debounced_state; //Find all of the changes

/* clock_A ^= clock_B; //Increment the counters
clock_B = ~clock_B;*/

A = A^(B&C);
B = B^C;
C = ~C;

A &= delta; //Reset the counters if no changes
B &= delta; //were detected.
C &= delta;

//Preserve the state of those bits that are being filtered and simultaneously
//clear the states of those bits that are already filtered.
debounced_state &= (A|B|C);
//Re-write the bits that are already filtered.
debounced_state |= (~(A|B|C) & new_sample);
}
NVade
а я просто опрашиваю клавиатуру с интервалом большим, чем максимальное время дребезга. Скажем в первом примере вместо прерывания каждую 1 мс делать опрос раз в 20-50мс. Человек такую задержку не чувствует
IEC
Контролировать один раз нажатие клавиши (совет NVade) глупо, т.к. любой дребезг вызовет событие нажатия.

По хорошему необходимо контролировать время первого удержания клавиши (состояния порта) и через контрольный тайм-аут, если состояние ни разу не изменилось, принимать решение о нажатии клавиши. После этого необходимо переходить на другой этап и контролировать время отпускания. Через определенный тайм-аут принимать решение об завершении работы с клавишей.
И уже после всего этого можно переходить к повторному ожиданию нажатия клавиши.
Axxel
По хорошему необходимо контролировать время первого удержания клавиши (состояния порта) и через контрольный тайм-аут, если состояние ни разу не изменилось, принимать решение о нажатии клавиши. После этого необходимо переходить на другой этап и контролировать время отпускания. Через определенный тайм-аут принимать решение об завершении работы с клавишей.
И уже после всего этого можно переходить к повторному ожиданию нажатия клавиши.
[/quote]


Да, примерно так и сделано, т.е к примеру если BOUNCE_CANCELLLATION_PERIOD равен 20
то в течение за все 20 прерываний от таймера значение бита должно быть равно одному и тому
же значению, только в этом случае состояние запоминается.

Вопрос: нормально ли для этого алгоритма использование прерывания от таймера?
Может есть способы без прерываний?
SergCom07
Думаю лучший вариант реализуется при помощи прерываний от линий IO кнопок.
В этом случае изменение состояния линии не пропустишь, а по таймеру только проверять время, прошедшее с момента последнего изменения состояния и через скажем 20 мс фиксировать логическое состояние кнопки (нажата/нет).
Вопрос только в количестве кнопок и аппаратных прерываний, но у последних AVR их много.
Visor
На самом деле достаточно ловить первый фронт изменения состояния и через таймаут проверить сохранилось ли состояние. И не надо ждать когда сигнал перестанет дребезжать, и только потом включать выдержку времени, это бессмысленно. Подумайте. smile.gif
DeXteR
Цитата(Visor @ Mar 28 2007, 20:56) *
На самом деле достаточно ловить первый фронт изменения состояния и через таймаут проверить сохранилось ли состояние. И не надо ждать когда сигнал перестанет дребезжать, и только потом включать выдержку времени, это бессмысленно. Подумайте. smile.gif


Если неждать то кто даст гарантию что после принятия решения о нажатой кнопке не происходит дребезга
А с дребезгом при отжатии как быть ??

По моему мнению самый верный способ - запускать програмный таимер по нажатию
Делать +1 если нажата и -1 если отжата
Ограничить нулем и числом А снизу и сверху
И принимать решение о точ что нажата кнопка когда таимер достигнет А

Заодно можно и блительное нажатие обрабатывать
rezident
На всех форумах одни и те же вопросы задаются smile.gif
http://www.telesys.ru/wwwboards/mcontrol/1...es/202772.shtml
Axxel
У меня примерно так:
Visor
Цитата(DeXteR @ Mar 30 2007, 00:19) *
Если неждать то кто даст гарантию что после принятия решения о нажатой кнопке не происходит дребезга
А с дребезгом при отжатии как быть ??

Либо я не ясно выразился, либо одно из двух. smile.gif
Ловим фронт изменения входа (не важно какой), запоминаем новое состояние, ждем время достаточное чтоб дребезг успокоился, проверяем вновь состояние входа, если совпало, его и принимаем за текущее. Обрабытывается таким образом и вкл и выкл.
Axxel
А если произойдет такое?
Visor
Цитата(Axxel @ Apr 2 2007, 10:12) *
А если произойдет такое?

Объясните пожалуйста, как такое возможно?
=AK=
Я проще делаю. В 8-битный сдвиговый регистр каждые 10 мс вдвигаю текущее состояние кнопки. Если в сдвиговом регистре все '1', то кнопка нажата, выходной сигнал устанавливаю в '1'. Если все '0', то кнопка отжата, выходной сигнал устанавливаю в '0'. Все другие комбинации игнорируются и не меняют состояние вых. сигнала.

При опроcе каждые 10 мс и 8-ми битах время получается 80 мс, это отлично работает со всеми обычными кнопками, в т.ч. с мембранными клавами. Для массивных контактов, которые могут дребезжать с малой частотой (контактор какой-нибудь, например), время опроса можно увеличить до 20..30 мс.
Axxel
Цитата(Visor @ Apr 2 2007, 09:09) *
Объясните пожалуйста, как такое возможно?


Помехи например, ложные срабатывания от датчиков, ведь такое IMHO может быть

Цитата(=AK= @ Apr 2 2007, 09:25) *
Я проще делаю. В 8-битный сдвиговый регистр каждые 10 мс вдвигаю текущее состояние кнопки. Если в сдвиговом регистре все '1', то кнопка нажата, выходной сигнал устанавливаю в '1'. Если все '0', то кнопка отжата, выходной сигнал устанавливаю в '0'. Все другие комбинации игнорируются и не меняют состояние вых. сигнала.

При опроcе каждые 10 мс и 8-ми битах время получается 80 мс, это отлично работает со всеми обычными кнопками, в т.ч. с мембранными клавами. Для массивных контактов, которые могут дребезжать с малой частотой (контактор какой-нибудь, например), время опроса можно увеличить до 20..30 мс.


Можно кусочек кода?

И еще вопрос: используя прерывания от аппаратного таймера для подавления
дребезга, с какой частотой целесообразнее всего устраивать эти прерывания?
И каков риск что пользовательская функция-обработчик не успеет обработаться
до возникновения следующего прерывания?
rezident
Цитата(Axxel @ Apr 2 2007, 10:28) *
И еще вопрос: используя прерывания от аппаратного таймера для подавления
дребезга, с какой частотой целесообразнее всего устраивать эти прерывания?

Прерывания для опроса кнопок должны следовать с периодом превышающим дребезг клавиш. Частота следования от 3Гц до 100Гц. Меньше 3Гц неудобно для пользователя, слишком медленная реакция на клавиши. Выше 100Гц не имеет смысла, никакая супер-пупер машинистка-профессионалка не сможет в таком темпе печатать, это раз. А во-вторых, дребезг многих широкораспространенных тактовых и мембранных кнопок составляет как раз порядка 10мс.
Цитата(Axxel @ Apr 2 2007, 10:28) *
И каков риск что пользовательская функция-обработчик не успеет обработаться
до возникновения следующего прерывания?

А в чем состоит риск-то? Во вложенных прерываниях? Дык можно их не допускать. А если вложенные прерывания разрешены, то обходите повторный вызов функции и всех делов-то.
Код
__interrupt void TimerISR(void)
{ static unsigned Cntr=0;
   Cntr+=1;
   __enable_interrupt();
   if (Cntr<2) key_scan_func(); // вызов функции сканирования клавиатуры, только если не было вложенного прерывания
   ...
   Cntr-=1;
}
Visor
Цитата(Axxel @ Apr 2 2007, 12:28) *
Помехи например, ложные срабатывания от датчиков, ведь такое IMHO может быть

Сигналы конечно в природе возможны всякие, но мы вроде обсуждаем сабж!
Axxel
Цитата(Visor @ Apr 2 2007, 10:55) *
Сигналы конечно в природе возможны всякие, но мы вроде обсуждаем сабж!


Но ведь неплохо бы фильтровать и ложные срабатывания от немеханических датчиков. smile.gif
Visor
Цитата(Axxel @ Apr 2 2007, 12:28) *
И еще вопрос: используя прерывания от аппаратного таймера для подавления
дребезга, с какой частотой целесообразнее всего устраивать эти прерывания?
И каков риск что пользовательская функция-обработчик не успеет обработаться
до возникновения следующего прерывания?

А не надо использовать прерывания от таймера, опрашивайте состояние таймера в общем цикле, спешить некуда.
Цитата(Axxel @ Apr 2 2007, 12:28) *
Но ведь неплохо бы фильтровать и ложные срабатывания от немеханических датчиков.

Универсальность - враг оптимальности.
Axxel
Универсальность - враг оптимальности.
[/quote]


Так каким же все-таки будет оптимальный код(алгоритм)? smile.gif
Dog Pawlowa
Цитата(Axxel @ Apr 2 2007, 10:56) *
...Так каким же все-таки будет оптимальный код(алгоритм)? smile.gif

СВОЙ! biggrin.gif
А это мой, заведомо хуже laugh.gif
Сервис вставлен в прерывание 1 мс.
Автоповтор.
Ускорение автоповтора.
Обработка комбинаций клавиш (генерируется альтернативный код)
Предделитель для плохих кнопок.
Успехов smile.gif

Код
#define     StartDelay 25
#define     NextDelay 3

const char * const key_name[LastKbdEvent+1]=
    {    "",name1,name2, name3, name4, name5, name6, name7 };


__no_init char kbd_status0;
__no_init char kbd_status1;
__no_init char kbd_status2;
__no_init char kbd_status3;
__no_init char kbd_status4;

__no_init char interrupt_counter;
__no_init char found_events;
__no_init char old_pressed_keys;
__no_init char pressed_counter;
__no_init char pressed_keys;
          char prescaler;

char DnPressed(void)
{   if (pressed_keys==kDn) return 1;
    else return 0;
}
char UpPressed(void)
{   if(pressed_keys==kUp) return 1;
    else return 0;
}
char NoPressed(void)
{   if (!pressed_keys) return 1;
    else return 0;
}


void InitKbd(void)
{
  kbd_status3=kbd_status2=kbd_status1=kbd_status0=0;
  found_events=0;
}


void KbdService(void)    // into timer interrupt 1 ms
{   if (prescaler<4)  { prescaler++; return;}
    prescaler=0;
            switch (interrupt_counter)     // to reduce time of function
    {
    case    0:    interrupt_counter=1;        //refresh statuses
            kbd_status3=kbd_status2;
            kbd_status2=kbd_status1;
            kbd_status1=kbd_status0;
            kbd_status0=Kbd;
            interrupt_counter=interrupt_counter;
            break;

    case    1:    interrupt_counter=2;        //check pressed key every tic
            pressed_keys=kbd_status0&kbd_status1&kbd_status2;
            if (pressed_keys&(~kbd_status3))
                if (bSound==SoundAlwaysOn) Beep(5,1);
            found_events|=pressed_keys&(~kbd_status3);        // found edge
            if ((pressed_keys==kUp+kDn)&&  // now this two keys
                ((~kbd_status3)&(kUp+kDn)))//and before it wasn't so
                {found_events|=kUpDn;    // set new bit
                found_events&=~(kUp+kDn);    break; } //reset usual bits
            if ((pressed_keys==kUp+kSt)&&  // now this two keys
                ((~kbd_status3)&(kUp+kSt)))//and before it wasn't so
                {found_events|=kUpSt;    // set new bit
                found_events&=~(kUp+kSt);    break; }//reset usual bits                
            if ((pressed_keys==kDn+kSt)&&  // now this two keys
                ((~kbd_status3)&(kDn+kSt)))//and before it wasn't so
                {found_events|=kDnSt;    // set new bit
                found_events&=~(kDn+kSt); break; }    //reset usual bits                    
        break;            
                
    case    2:    interrupt_counter=3;        //check if key the same as before
            if ((pressed_keys&kUp)||(pressed_keys&kDn))    //key for repeat
            {    if (pressed_keys==old_pressed_keys) // same key?
                {    if (pressed_counter)         // yes, time over?
                    {pressed_counter--;}      //No
                    else     {    pressed_counter=NextDelay;        //Yes
                            found_events=pressed_keys; }        //reset
                    }
                else                                            // other key
                    {pressed_counter=StartDelay;    
                    }
            };
            old_pressed_keys=pressed_keys;
            break;
    case    3:    interrupt_counter++;
                    break;
    default: interrupt_counter=0;
    }
}
Axxel
Да,действительно не такая уж и тривиальная это задача... подавление дребезга smile.gif
Visor
Цитата(Axxel @ Apr 2 2007, 15:56) *
Так каким же все-таки будет оптимальный код(алгоритм)? smile.gif

Выбор за вами. Я свой описал достаточно понятно, чтоб реализовать. Причём реализовать его можно очень просто, несколькими логическими инструкциями с байтами, для 8 кнопок сразу.
vaivai
На Kazus.ru было несколько примеров на асемблере (давненько)-спрашывавшый был очень даволен результатом.
birden
Я очень часто использую следующий принцип:
Код
unsigned char keyb;         // глобальная переменная, состояние клавиатуры
unsigned char kcnt;          // счетчик времени события "кнопка нажата"
...
//    Опрос клавиатуры, вызывается из подпрограммы
//    обработки прерывания по таймеру (каждые 4 мс)
//
void rd_keyb(void)  
{
    unsigned char st;
    st = ~PINC;                                              // читаем состояние кнопок
    if (st == 0) kcnt=0; else kcnt++;
    if (kcnt == 20) keyb=st;                            // таймаут антидребезга
    if (kcnt == 120) { keyb=st; kcnt -= 60; }   // автоповтор
}
Axxel
Остановился пока на таком методе:

unsigned char acc=0,state=0;

interrupt Timer0()
{
acc=acc<<1;
acc|=Pinx;
if(acc==0){state=0;}
if(acc==255){state=1;}
}

Всем респект! cheers.gif
slog
Ну вы и нагородили, на пустом месте. Достаточно 20-30 раз в секунду опрашивать матрицу кнопок и ВСЁ. Дребезг не влияет ни на что. Можно считать что его нет.
Axxel
Цитата(slog @ Apr 3 2007, 12:20) *
Достаточно 20-30 раз в секунду опрашивать матрицу кнопок



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

Конечно, в принципе я понимаю что хватит и двух устойчивых состояний для
принятия нажатия, но... мало ли что может произойти.





To =AK=
О таком методе шла речь?
(с битовым сдвигом,выше)
=AK=
Цитата(Axxel @ Apr 3 2007, 17:04) *
To =AK=
О таком методе шла речь?
(с битовым сдвигом,выше)

Ну так вы же его использовали в своем коде. Я то же самое делал на ассемблере.
Axxel
Цитата(=AK= @ Apr 3 2007, 13:40) *
Ну так вы же его использовали в своем коде. Я то же самое делал на ассемблере.



Вы же мне про него и сказали smile.gif
Visor
Цитата(Axxel @ Apr 3 2007, 15:34) *
Дело в том что мне необходимо контролировать не клавиатуру, а несколько датчиков.
Т.е каждый датчик нужно проверять на дребезг по отдельности,датчики могут сработать
одновременно, и.т.д. Т.е необходим одновременный контроль.

Конечно, в принципе я понимаю что хватит и двух устойчивых состояний для
принятия нажатия, но... мало ли что может произойти.

Я свой алгоритм как раз таки и применял для датчиков, для индуктивных датчиков оборота в счетчиках электроэнергии, дребезг с них был дай боже. В результате точность подсчёта соответствовала действительности. Вот как это можно реализовать на асме для 8 входов:
Код
; Time slot
; Pushbuttons #00-07 status
    in        PB00_07_In,PIND
    eor        PB00_07_In_Aux,PB00_07_In
    and        PB00_07_Stat,PB00_07_In_Aux
    com        PB00_07_In_Aux
    and        PB00_07_In_Aux,PB00_07_In
    or        PB00_07_Stat,PB00_07_In_Aux
    mov        PB00_07_In_Aux,PB00_07_In
Axxel
К сожалению АСМом не владею... sad.gif
Как это будет выглядеть на Си?

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

Я сразу как то предположил что обрабатывать
нужно каждый вывод по отдельности.
Visor
Цитата(Axxel @ Apr 3 2007, 18:24) *
Суть следующая: не могу понять
как можно обработать один датчик(клавишу, и.т.д)
на дребезг, обрабатывая порт целиком.

Берётся новое состояние байта входов и сравнивается с предыдущим посредством "Exclusive OR", тем самым выделяются изменившиеся и неизменившиеся биты, затем с помощью простых логических операций, изменившимся битам присваиваются старые значения статуса, а неизменившимся новые значения байта входов. Алгебра логики рулит! wink.gif
А чтоб понять листинг, поглядите используемые команды хоть в любом даташите для AVR, хотя и так понять можно AND - И, OR - ИЛИ, ...
slog
Если опрашивать реже, чем время дребезга, никакие сложные алгоритмы не нужны вообще. Считали порт. Есть сигнал - датчик сработал, нет - значит нет. И всё.
Mario
Цитата(Visor @ Apr 3 2007, 14:21) *
Берётся новое состояние байта входов и сравнивается с предыдущим посредством "Exclusive OR", тем самым выделяются изменившиеся и неизменившиеся биты, затем с помощью простых логических операций, изменившимся битам присваиваются старые значения статуса, а неизменившимся новые значения байта входов. Алгебра логики рулит! wink.gif

Если можно покажите это на примере, (на С), пожалуйста.


Иногда делаю вот таким кустарным методом:
unsigned char SkanKlava (void)
{
if(KN_Ok==0){
delay_ms(10);// защита от дребезга

if(KN_Ok==0){ //проверяем еще раз
return 0x01; //нажата кнопка "Ок"
};
};
//...и т.д. (все остальные кнопки)
}
Может не совсем красиво но иногда выгодно. huh.gif
Axxel
Мне кажется что задержки
типа delayms() в алгоритме подавления дребезга
это не есть гут, хотя все конечно зависит от программы.
Kitsok
Цитата(Oleg_IT @ Mar 28 2007, 12:50) *
Давно и хорошо работающий код. Обслуживает несколько клавиш.


Очень код понравился, пред-оптимизированный wink.gif Но вот 8 циклов чтения на подавление дребезга - это много... А нету ли чего-нибудь похожего, но, скажем, с тремя циклами?
Kitsok
Нашел. Технология называется vertical counters.
Вот ссылки:
http://www.dattalo.com/technical/software/pic/debounce.html
http://www.dattalo.com/technical/software/pic/vertcnt.html
Oleg_IT
Цитата(Kitsok @ May 15 2007, 13:24) *
Нашел. Технология называется vertical counters.
Вот ссылки:
http://www.dattalo.com/technical/software/pic/debounce.html
http://www.dattalo.com/technical/software/pic/vertcnt.html


См. моё предложение на первой странички http://electronix.ru/forum/index.php?showt...=29296&st=0
Mario
Цитата(Axxel @ Apr 9 2007, 08:00) *
Мне кажется что задержки
типа delayms() в алгоритме подавления дребезга
это не есть гут, хотя все конечно зависит от программы.


Полностью с Вами согласен. Поэтому я тут. Хочу чему-нибудь научиться у Форумчан. smile.gif

С уважением, Mario.
Kitsok
Цитата(Oleg_IT @ May 15 2007, 13:58) *
См. моё предложение на первой странички

Так я собственно от этого и искал wink.gif

Я-ж написал, что алгоритм очень хороший, но 8 шагов - многовато.
Кстати, вкурив в эти вертикальные счетчики я обнаружил, как "дешево" с точки зрения кода сделать еще ряд операций в моем проекте, так что спасибо за наводку wink.gif
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.