1

Тема: Опрацювання натиснення кнопок (усунення брязкоту контактів)

Усім доброго дня! Одразу повідомлю про своє практично нульове знання c++
Попри це взявся опановувати, створюючи реальну річ.
Розробляю мікрокотролерний пристрій на базі плати Arduino, компілятор і IDE хоч теж Arduino, але побудований на основі C++.

Суть проблеми наступна.
У мене до одного аналогового входу підключено 5 кнопок, які через резисторі підключені до 5 В живлення з однієї сторони і до аналогового входу мікроконтролера - з іншої сторони.
Натискання відповідної кнопки подає на аналоговий вхід певну напругу, яку АЦП контролера опрацьовує і перетворює на int значення (від 0 до 1023).

Електроніка має таку специфіку, що, скажімо, при натисненні кнопок певний час відбуваються перехідні процеси (зміна напруги), що має назву "брязкання контактів" (bouncing). Мається на увазі, що у момент натиснення  і відпускання кнопки контролер на вході отримує якийсь час купу різних значення напруги, і кожне значення напруги він опрацьовує і відповідно повертає кожен раз різне значення натиснення кнопок.

Діаграма брязкання контактів

Прихований текст

http://i.stack.imgur.com/EGMeq.jpg

У мене стоїть задача розробити опрацювання на подію натиснення кнопки таким чином, щоб не давати змогу контролеру реагувати на оці брязкання (Bouncing).

У моєму розумінні воно має відбуватись так:
Я натискаю кнопку, у коді є прапор натиснення кнопки, скажімо BTevent, який за дефолтом FALSE, а при натисненні стає TRUE.
Потім має відбуватись затримка зчитування значення кнопки, щоб уникнути зчитувань помилкових значень при брязканні. Скажімо, брязкання при натисненні відбувається 20 мілісекунд. Відповідно, оцих 20 мілісекунд контролер не повинен зчитувати значення кнопки. Через 20 мілісекунд відбувається зчитування, і як тільки воно відбулось, зчитування припиняється до відпускання кнопки.

Тепер те саме, але у коді.

Перед цим - голосарій :)
analogRead(0) - функція, яка повертає значення від 0 до 1023 на вході контролера 0.
1023 - жодна кнопка не натиснута
millis() - функція яка повертає час у мілісекундах від початку запуску контролера
BTtime - змінна, у яку ми записуємо час коли відбулось натиснення кнопки
BTevent - прапор. 0 - кнопка не була натиснута, 1 - кнопка була натиснута
BTread  - значення кнопки зчитано? 1 - так, 0 - ні
BTvalue - сюди записуємо значення кнопки

unsigned long  BTtime = 0; 
int BTevent = 0;
int BTread = 0;
int BTvalue = 0;

void loop {

   if (analogRead(0)>1000) //якщо не натиснута
  {BTevent = 0;                  // події не відбувається
BTvalue=analogRead(0);}  // просто зчитуємо значення з порта при ненатиснутих кнопках


if (analogRead(0)<1000)  // якщо натиснуто
{BTevent = 1;                 // подія відбулась
  BTtime = millis();}       // точка початку відліку від початку події
  
if ( (BTtime + 30) < millis() && BTevent == 1) // чекаємо 30 мс від події і перевіряємо чи відбулась подія
  {BTread = 1;                // позначаємо, що ми зчитали значення кнопки
BTvalue = analogRead(0);} // записуємо значення кнопки у змінну  
  
  if (BTread == 1) // якщо ми зчитали значення, то скидуємо всі прапори
  {BTevent = 0;
  BTread = 0;}

}

Далі ми маємо опрацювати BTvalue, але то інша справа. Наразі оцей код адекватно не працює можливо, хтось допоможе розмусолити цю тему?
У мережі купа інформації по усуненню брязкоту контаків на цифрових входах, але зовсім немає інформації по усуненню цієї проблеми на аналогових входах при навішаних кількох кнопках....

2 Востаннє редагувалося raxp (28.01.2016 19:13:35)

Re: Опрацювання натиснення кнопок (усунення брязкоту контактів)

int d;
int delta = 10;
long prevms = 0; 
int keyval[8] = {200, 300, 400, 500, 600, 700, 800, 900};  
byte key[8] = {0, 1, 2, 3, 4, 5, 6, 7}; 

void setup() {
  Serial.begin(19200);
}

void loop {
   int d = analogRead(0);

   if (d<1000) { 
   
     // if > 100 мc 
     if (millis() - prevms > 100) { 
       prevms = millis(); 
     
       for (int i=0; i<8-1; i++) {
         if ( d >= (keyval[i]+delta) && d <= (keyval[i+1]-delta) ) {
          serial.writeln(key[i])
         }
       }


      }
     }
   };

http://raxp2.blogspot.com/2014/09/blog-post.html

Подякували: reverse2500, Rusik, leofun014

3

Re: Опрацювання натиснення кнопок (усунення брязкоту контактів)

raxp написав:

...

Дякую. Але все одно мікроконтролер опрацьовує то як подвійне натиснення і відпусканні кнопки отримує якісь проміжні значення...

4

Re: Опрацювання натиснення кнопок (усунення брязкоту контактів)

Потрібно обрацьовувати скважність сигналу
https://ru.wikipedia.org/wiki/Скважность
сигнали з великим значенням скважності не опрацьовувати

Подякували: leofun011

5

Re: Опрацювання натиснення кнопок (усунення брязкоту контактів)

https://www.youtube.com/watch?v=m73Xh0zILnw
одне з вирішень є тут і озвучене на прикладі

6

Re: Опрацювання натиснення кнопок (усунення брязкоту контактів)

-=ЮрА=- написав:

Потрібно обрацьовувати скважність сигналу

ну власне її я й мав на увазі, коли описував брязкіт контактів. і з цієї точки зору я й хочу здійснити затримку зчитування сигналу з метою стабілізації сигналу на контактах

5reverse2500 - у відео уроці використовується функція delay(), яка призупиняє виконання будь-чого у контролері. Плюс там використовується дискретний вхід із кнопки. У випадку аналогового входу і використання часових зміщень читання сигналу трохи важче.

7

Re: Опрацювання натиснення кнопок (усунення брязкоту контактів)

з аналоговим сигналом взагалі незручно працювати, вічно їм не догодиш

8 Востаннє редагувалося -=ЮрА=- (28.01.2016 23:07:15)

Re: Опрацювання натиснення кнопок (усунення брязкоту контактів)

Rusik - у тебе є

millis() - функція яка повертає час у мілісекундах від початку запуску контролера

, далі у тебе є analogRead(0) - при зміні коду якої ти бачиш чи напруга з'явилась чи зникла. Поки напруга є - це твій імпульс, тільки зникла імпульс закінчився. Далі фіксуй
tb - початок напруги te кінець
тоді te[ i - 1 ] - tb[ i - 1 ] = tau tb[ i ] - tb[ i - 1 ] = T
Узяв знайшов S = T / tau якщо S велике - не опрацьовуй, якщо відповідає часовій діаграмі натиску - опрацьовуй.

9

Re: Опрацювання натиснення кнопок (усунення брязкоту контактів)

Щоб щось намалювати конкретніше потрібні усі коди

функція, яка повертає значення від 0 до 1023 на вході контролера 0.
1023 - жодна кнопка не натиснута

приведи діаграму значеннь кодів analogRead

10 Востаннє редагувалося raxp (29.01.2016 08:00:50)

Re: Опрацювання натиснення кнопок (усунення брязкоту контактів)

delay() затримує виконання основного машинного циклу, під час якого можуть виконуватися інші корисні завдання. Використовувати його не рекомендується. Саме тому використовується метод зарубки часу через - mills(), еквівалентний gettickcount в вiндах.

У моєму коді два фільтра:
1- антибрязкiт за рахунок часу нечутливості за рахунок millis() на 100 мс, еквівалентний введенню ємності для придушення брязкоту.
2- введений гістерезис, бо значення з АЦП завжди плаваючі.

Оскільки код писав прямо в браузері по пам'яті без перевірки на реальному пристрої, то допустив логічну помилку - антибрязкiт затримки потрібно вводити після появи події спрацьовування. Хоча за логікою, якщо жодна кнопка не було натиснута і ваші значення з дільників при цьому більше 1000, то факт спрацьовування по d <1000 є, і у мене все вірно. По ідеї повинно ще можна так:

    int d;
    int delta = 10;
    long prevms = 0; 
    int keyval[8] = {200, 300, 400, 500, 600, 700, 800, 900};  
    byte key[8] = {0, 1, 2, 3, 4, 5, 6, 7}; 
     
    void setup() {
      Serial.begin(19200);
    }
     
    void loop {
       int d = analogRead(0);
     
       if (d<1000) { 
       

         
           for (int i=0; i<8-1; i++) {
             if ( d >= (keyval[i]+delta) && d <= (keyval[i+1]-delta) ) {

             // if > 100 мc 
             if (millis() - prevms > 100) { 
              prevms = millis(); 
              serial.writeln(key[i])
             }

             }
           }
         }
       };

І пару важливих зауважень:

1. Коли ви описуєте роботу системи, будьте люб'язні привести схему включення ланцюгiв. При роботi з залiзом це правило. Підключення декількох кнопок за схемою дільника до одного входу АЦП логічно і не потребує пояснень, але хто вас знає, що у вас насправді. Схему сюди.
2. Проміжних значень в принципі бути не може, на верхній рівень видається вже нормоване значення кнопки, прив'язане до умовних рівней з АЦП, з масиву. Оскільки значення дільників для ваших кнопок нам невідомі (немає схеми), то і значення можуть відрізнятися. Числа 200, 300, 400 - умовні. Реальні значення нам невідомі, ви повинні їх самі підставити в масив.

Подякували: reverse2500, leofun01, Rusik3

11 Востаннє редагувалося Yola (29.01.2016 14:11:33)

Re: Опрацювання натиснення кнопок (усунення брязкоту контактів)

-=ЮрА=- написав:

Потрібно обрацьовувати скважність сигналу
https://ru.wikipedia.org/wiki/Скважность
сигнали з великим значенням скважності не опрацьовувати

Так є ж україномовна - Прогальність

Прихований текст

Отак 80% посилань з україномовних ресурсів ведуть на російськомовні, перепрошую за офтоп

12 Востаннє редагувалося raxp (29.01.2016 15:03:23)

Re: Опрацювання натиснення кнопок (усунення брязкоту контактів)

Прихований текст

Отак 80% посилань з україномовних ресурсів ведуть на російськомовні, перепрошую за офтоп

нiкто не заважаэ так дати посилання https://en.wikipedia.org/wiki/Duty_cycle, загалом iнглiш пiдтянемо )

Мова немаэ значення, важливо, щоб бiльше посилань було на наших девелоперiв.

13

Re: Опрацювання натиснення кнопок (усунення брязкоту контактів)

мовний офтоп
Yola написав:

Отак 80% посилань з україномовних ресурсів ведуть на російськомовні, перепрошую за офтоп

Тут проблема ширша: припустимо, український термін справді присутній в якомусь словнику, але для більшості фахівців він не на слуху. Особливо якщо це неологізм — тоді взагалі проблематично отримати відповідь на питання, яке без словника не зрозумієш. Теоретично, якби український термін був адаптацією російського (напр., скважність) — що, зрештою, відповідає усній мовній практиці наших днів — його було б простіше почати використовувати на практиці. Проблема в тому, що русизмів в українській мові прийнято боятися більше, ніж самої російської мови замість української.

Подякували: -=ЮрА=-1

14

Re: Опрацювання натиснення кнопок (усунення брязкоту контактів)

Вітаю. Трохи в ступор увійшов після невдалих спроб організувати нормальне опрацювання кнопок. Наразі проблему, можна сказати, вирішив. Почну з нуля:
У мене на аналоговий вхід 0 через дільники напруги підключено 5 кнопок
Схема

Прихований текст

http://www.hobbytronics.co.uk/image/cache/data/dealextreme/arduino-lcd-keyboard-shield-5-500x500.jpg

Натиснення відповідної кнопки подає на АЦП 0, 100, 256, 410, 640

Ідею raxp я відпрацьовував, але ефекту не було. Щодо умовноті значень АЦП 100 200 300... я це прекрасно розумію.
Очевидно, я щось не враховував... для мого розуму допилювати існуюче надто складно, тому вирішив покопатись трохи в мережі і ще раз все переосмислити.

Головна ідея та сама - використовуємо millis, при цьому у тілі loop через кожні, скажімо, 25 мс робимо опрацювання натиснення.
Запам'ятовуємо не час, коли було натиснуто, а час перед подією, по суті те саме, але у мене воно пішло. Потім чекаємо трохи (100 мс) і опрацьовуємо натиснення. Так само використовуємо прапор опрацювання події.
плюс підглянув як реалізувати реакцію на більше тривале затиснення кнопки, щоб ітерувати одне тривале натиснення на певну кількість одинарних.
Далі вже окремою функцією матиму змогу реалізувати реакцію на натиснення відповідних кнопок. От тільки не знаю, чи передавати їй змінну чи використовувати глобальні змінні для цього.

Код:

Прихований текст
#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

void setup() {
   // set up the LCD's number of columns and rows:
  lcd.begin(16, 2);  
 pinMode (0, INPUT);
  }
int BTNpin = 0;           // номер виводу
long LCDdelay = 0;        // для обліку часу оновлення LCD
int BTNval=0;             // для зберігання значення кнопок
long BTNtimePress = 0;    // часу, коли відбудеться, якась подія  
long BTNdelay = 100;      // затримка для стабілізації
boolean BTNread = false;  // прапор опрацювання натиснення
long BTNdelayLong = 1500; // затримка при тривалому натисненні
int BTNtimeLong = 0;      // для опрацювання при тривалому натисканні
long BTNtimeFromPress = 0; //часу продейно від першого натиснення
long BTNreadIteration = 25; // у loop через кожні 25 мс проганяємо функцію BTNprocessor ()
long BTNtime1 = 0;          // облік часу для прогонки у циклі
long BTNtime2 = 0;          // --------

int x = 0; // просто зміння для наочного відображення


void BTNprocessor () //Функція для опрацювання натискань
{
 BTNval = analogRead (BTNpin);
 if (BTNval >= 1000)         // при значенні бльіше 1000 на вході жодна кнопка не натиснута 
 {
   BTNtimePress = millis ();  //останній час перед подією натиснення, постійно оновлюється, поки кнопка не натиснута
   BTNread = false;          // опрацюванян не відбулось
   BTNtimeLong = 0;           // час довгого натиснення онулено
 }
  
  BTNtimeFromPress = millis() - BTNtimePress;  // у випадку натиснення кнопки змінна BTNtimePress 
                                               //отримує останнє значення після попередньої умови, 
                                               //відповідно, BTNtimeFromPress росте від 0 до....
  if (BTNval < 1000)                          // кнопка натисннена (у мене від 0 до 640)
  {
    // одинарне натиснення
     if ( (BTNtimeFromPress > BTNdelay) && (BTNread == false) ) // якщо час фактичного натиснення більше заданого часу очікування, то опрацьовуємо натискання 
     {
       BTNval = analogRead (BTNpin);          //зчитуємо значення
       BTNaction();                            // виконуємо якусь функцію
       BTNread = true;                         // прапор опрацювання:  опрацьовано
       BTNtimeLong = 0;                       // онулюємо час довго натисакання
     }
     // натиснення, яке триває > 1,5 секунди
     if ( (BTNtimeFromPress > (BTNdelayLong + BTNtimeLong)) &&  (BTNread == true) ) //якщо час натсинення більше суми ітераційного часу  + часу очікуваення 1,5 C і якщо першу подіб опрацьовано
     {
       BTNval = analogRead (BTNpin);  // зчитуємо значення
       BTNaction();                   // виконуємо функцію
       BTNtimeLong = BTNtimeLong + 200;  //ітерація, потрібна для того, щоб кожні 200 мс дублювалась подія 
     }
  }
   
}
 
   void  BTNaction ()          // інкрементуємо число х при кодному натисненін якоїсь із кнопок і відображуємо значення екрані. 
   {if ((LCDdelay + 50) <= millis())
   {
     LCDdelay = millis();
     lcd.clear();     
     lcd.print(x);
     x = x+1;
   
   }
}
   
   

void loop() {
 
  BTNtime2 = millis();                               // кожні 25мс запускаємо функцію опрацювання натиснення кнопок
  if ( (BTNtime2 - BTNtime1) > BTNreadIteration)
 {
  BTNtime1 = BTNtime2;
  BTNprocessor ();
 }    

}

Якось так
Усім дякую, що відгукнулись!