Обновлено 04 мая 2017
Кравченко Виктор

Arduino: Дребезг — программное и аппаратное устранение

Цифровые устройства Основы электричества Arduino Arduino Lang
01

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

02
Не забываем о стягивающем резисторе 10 КОм от пина 8 и ноги кнопки к земле. О том для чего нужны стягивающие и подтягивающие резисторы можно почитать в статье Подтягивающий (стягивающий) резистор - что это, и зачем он нужен.
03

Скетч:

04 Arduino (C++)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int pinButton = 8; // Пин кнопки int pinLED = 7; // Пин светодиода void setup() { pinMode(pinButton, INPUT); pinMode(pinLED, OUTPUT); } bool flag = LOW; bool lastButton = LOW; void loop() { int currentButton = digitalRead(pinButton); // Считываем значение пина кнопки if (lastButton == LOW && currentButton == HIGH) { // Если состояние кнопки "нажата" - HIGH, а предыдущее LOW - фиксируем событие нажатия кнопки flag = !flag; // Меняем флаг статуса светодиода digitalWrite(pinLED, flag); // В зависимости от флага, зажигаем или гасим светодиод } lastButton = currentButton; // Запоминаем последнее состояние кнопки }
05

После сборки схемы и заливки скетча, можно убедиться в том, что схема работает не так, как задумывалось — светодиод загорается и гаснет не всегда предсказуемым образом. Почему так происходит? Ответ на этот вопрос даст осциллограф, подключенный к 8 пину Arduino и земле GND.

06 На заметку:
Вот и пришло время взрослеть начинающим электронщикам — тем, у кого нет осциллографа. Далее практический пример того, для чего нужен осциллограф и как его использовать.
Автором в демонстрации использован самый бюджетный вариант, доступный любому начинающему ардуинщику — осциллограф DSO138 (набор для самостоятельной сборки).
Самое время обзаводиться осциллографом!
07

На самом деле смыкание-размыкание контактов кнопки в реальном мире немного отличается от того, как этого бы хотелось:

08
Дребезг — это явление между стабильными состояниями контактов
Дребезг — это явление между стабильными состояниями контактов
09

Это подтверждают данные осциллографа DSO 138:

10
Даже с низкой частотой захвата виден дребезг на размыкании контакта
Даже с низкой частотой захвата виден дребезг на размыкании контакта
С обзором и инструкцией по эксплуатации осциллографа DSO138 (набора для самостоятельной сборки) можно ознакомиться в статье Осциллограф DSO 138 - инструкция по эксплуатации (основы, ликбез, что умеет и как работать?).
11

Логический анализатор Saleae Logic 16 нагляднее отображает дребезг из-за большей частоты сэмплирования:

12
13

Итак, дребезг контактов (англ. bounce — дребезг, debounce — устранение дребезга) — явление, происходящее в электромеханических коммутационных устройствах, длящееся некоторое время после замыкания электрических контактов и состоящее из многократных неконтролируемых замыканий и размыканий контактов, обусловленных упругостью материалов и деталей контактной системы — некоторое время контакты «подпрыгивают» при соударениях, размыкая и замыкая электрическую цепь.

14
Дребезг при нажатии кнопки, зафиксированный осциллографом a id=253 class=codius href=/articles/253Hantek DSO4202C/a
Дребезг при нажатии кнопки, зафиксированный осциллографом Hantek DSO4202C
15

Но с этим вредным явлением можно справиться. При чем, это можно сделать 2 путями — программно и аппаратно (на уровне железа).

16

Программное устранение дребезга.

Для программного устранения дребезга, достаточно после первого изменения состояния кнопки, установить паузу в несколько милисекунд, для того чтобы контакт стабилизировался и дребезг был проигнорирован. Для этого дополним код функцией debounce():

17 Arduino (C++)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
int pinButton = 8; // Пин кнопки int pinLED = 7; // Пин светодиода void setup() { pinMode(pinButton, INPUT); pinMode(pinLED, OUTPUT); } bool flag = LOW; bool lastButton = LOW; bool debounce() { bool current = digitalRead(pinButton); if (current != lastButton) { // Старое значение отличается от полученного delay(10); // Ждем пока состояние стабилизируется - игнорируем дребезг current = digitalRead(pinButton); // Считываем стабилизированное значение } return current; } void loop() { int currentButton = debounce(); // Получаем стабилизированное значение if (lastButton == LOW && currentButton == HIGH) { // Если состояние кнопки "нажата" - HIGH, а предыдущее LOW - фиксируем событие нажатия кнопки flag = !flag; // Меняем флаг статуса светодиода digitalWrite(pinLED, flag); // В зависимости от флага, зажигаем или гасим светодиод } lastButton = currentButton; // Запоминаем последнее состояние кнопки }
18

После добавления функции, стабилизирующей состояние кнопки, дребезг игнорируется.

19

Аппаратное устранение дребезга.

Аппаратное устранение дребезга в подавляющем большинстве случаев более предпочтительно программному, по ряду причин:

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

20 Важно:
Одна из главных причин применения аппаратного устранения дребезга заключается в частом использовании подобных сигналов в прерываниях. Программно решить проблему дребезга для «скармливания» сигналов прерываниям невозможно!
21

Начнем с рассмотрения RC-цепочки — последовательного включения в цепь резистора (10 КОм) и конденсатора (10 мкФ):

22
23

Время, численно равное произведению RC, называется постоянной времени цепи RC и обозначается τ (не путать с временем заряда конденсатора $t$).

24
$$τ = RC$$
25

При замкнутой цепи и заряде конденсатора, напряжение $U_с$ на его выводах будет увеличиваться от нуля до значения $U=5 В$ по экспоненте:

26
$$\begin{equation}U_c = U(1 - e^{-t\over{RC}})\end{equation}$$
27
28

Из формулы $(1)$ можно выявить следующие закономерности (выделены на графике):

  • за время τ ($t=RC$) происходит заряд конденсатора на 63,2% ($U_c = U(1 - e^{-1})= U(1 - {1\over{e}})$ )
  • за время 3τ происходит заряд конденсатора на 95% ($U_c = U(1 - e^{-3})= U(1 - {1\over{e^3}})$ )
  • за время 5τ происходит заряд конденсатора на 99% ($U_c = U(1 - e^{-5})= U(1 - {1\over{e^5}})$ )

29

В нашем примере время зарядки конденсатора составит $10КОм \times 10мкФ = 0.1 сек$.

30

Исходя из этих данных необходимо подбирать номиналы резистора и конденсатора.

31

Для аппаратного устранения дребезга придется изменить принципиальную электрическую схему. В первую очередь необходимо резистор сделать не стягивающим, а подтягивающим, а во-вторых, параллельно с выводами кнопки, установить конденсатор. Он уберет дребезг:

32
Слева — стандартное подключение кнопки со стягивающим резистором, справа — модифицированная схема для устранения дребезга
Слева — стандартное подключение кнопки со стягивающим резистором, справа — модифицированная схема для устранения дребезга
33

Теперь в состоянии покоя на 8 пине будет 5 В, в случае нажатия на кнопку, произойдет разряд конденсатора:

34
35

После того, как кнопка будет отпущена, конденсатор начнет заряжаться за время 5τ.:

36
37

Теперь необходимо дополнить схему дополнительным резистором R2 (он сделает работу схемы стабильнее — разрядка конденсатора будет происходить медленнее, скорость разряда зависит от номинала резистора) и инвертирующим триггером Шмитта — 74HC14N.


Проверено — автор рекомендует:
http://ali.pub/1k19zx 74HC14 (Инвертирующий триггер Шмитта)
Как покупать на Aliexpress со скидкой от 5,5%

Видео-инструкция о покупке со скидками на Aliexpress
38 На заметку:
Даташит на инвертирующий триггер Шмитта — sn74hc14.pdf.
39
40

Основными особенностями инвертирующего триггера Шмитта являются:

  • инвертирование входящего сигнала,
  • преобразование аналогового сигнала в «цифровой».

41
42

Результат:

43
44

Устранение дребезга на аналоговом входе

Данная ситуация отличается от вышеописанных и встречается в схемах подключения нескольких кнопок к одному аналоговому входу. Разница заключается в том, что интерпретация нажатия кнопки происходит путем вызова функции analogRead(), которая запускает механизм преобразования АЦП. И его погрешность не даст воспользоваться обычным ожиданием того момента, когда дребезг пройдет. Для устранения дребезга АЦП можно фиксировать приходящие значения и когда придет, например, 5-10 одинаковых значений можно говорить о нажатии кнопки. Но поскольку АЦП тоже имеет погрешность этот путь не очень эффективен. Эффективность метода можно повысить путем преобразования полученных данных в идентификатор кнопки, путем сравнения полученного значения с эталонным, скорректированным на допуск. И уже фиксировать полученные значения идентификатора кнопки. Таким образом погрешность АЦП будет нивелирована.

45

Этот метод использован в коде примера №2 статьи Arduino: Подключение нескольких кнопок к одному аналоговому входу. Схема подключения оттуда же:

Принципиальная схема
Принципиальная схема
46
47

Код:

48 Arduino (C++)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
int pinIn = A0; int keyValue = 0; // Состояние покоя void setup() { pinMode(pinIn, INPUT); Serial.begin(9600); } void loop() { int newKeyValue = GetKeyValue(); // Получаем актуальное состояние кнопок с коррекцией дребезга if (keyValue != newKeyValue) { // Если новое значение не совпадает со старым - реагируем на него keyValue = newKeyValue; // Актуализируем переменную хранения состояния if (keyValue > 0) { // Если значение больше 0, значит кнопка нажата Serial.println("Key pressed: " + String(keyValue)); } else { // Если 0, то состояние покоя Serial.println("all keys are not pressed"); } } } int GetKeyValue() { // Функция устраняющая дребезг static int count; static int oldKeyValue; // Переменная для хранения предыдущего значения состояния кнопок static int innerKeyValue; // Здесь уже не можем использовать значение АЦП, так как оно постоянно меняется в силу погрешности int actualKeyValue = GetButtonNumberByValue(analogRead(pinIn)); // Преобразовываем его в номер кнопки, тем самым убирая погрешность if (innerKeyValue != actualKeyValue) { // Пришло значение отличное от предыдущего count = 0; // Все обнуляем и начинаем считать заново innerKeyValue = actualKeyValue; // Запоминаем новое значение } else { count += 1; // Увеличиваем счетчик } if ((count >= 10) && (actualKeyValue != oldKeyValue)) { oldKeyValue = actualKeyValue; // Запоминаем новое значение } return oldKeyValue; } int GetButtonNumberByValue(int value) { // Новая функция по преобразованию кода нажатой кнопки в её номер int values[5] = {0, 513, 615, 769, 1023}; int error = 15; // Величина отклонения от значений - погрешность for (int i = 0; i <= 4; i++) { // Если значение в заданном диапазоне values[i]+/-error - считаем, что кнопка определена if (value <= values[i] + error && value >= values[i] - error) return i; } return -1; // Значение не принадлежит заданному диапазону }
50

Похожие запросы:

  • Дребезг кнопки
  • аппаратное устранение дребезга контактов
  • Дребезг контактов и способы подавления дребезга
  • Как избавиться от дребезга контактов
  • Фильтрация дребезга контактов переключателя
  • Как бороться с дребезгом контактов?
  • Подавляем дребезг кнопки
  • Правильное подавление дребезга контактов
comments powered by HyperComments