Гирлянда на Arduino. Функция debounce().

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

const int buttonPin = 2; // Определим, что тактовая кнопка подключена к контакту "2"
const int ledPin =  13;  // Определим, что светодиод подключен к контакту "13"
int buttonState = 0;     // Зададим некую переменную и изначально присвоим ей нулевое значение (присвоим низкий уровень)

void setup()
{
  pinMode(ledPin, OUTPUT);   // Конфигурируем режим работы контакта "13" на выход
  pinMode(buttonPin, INPUT); // Конфигурируем режим работы контакта "2" на вход
}

void loop()
{
  buttonState = digitalRead(buttonPin); // Задаем, что переменная buttonState принимает значение с buttonPin (с контакта "2")

  if (buttonState == HIGH)
    {  
    digitalWrite(ledPin, HIGH); // Подаем высокий уровень. Светодиод включается
    }
  else
    {
    digitalWrite(ledPin, LOW);  // Подаем низкий уровень. Светодиод выключается
    }
}

Для того чтобы осуществить "переключение" необходимо добавить новую переменную, которая будет хранить значение уровня сигнала предыдущего нажатия на кнопку. Помимо нее необходимо указать еще одну переменную. Она понадобится нам в теле функции void loop(). Поскольку обе переменные могу принимать только два значения, то разумно присвоить им тип данных boolean.

boolean lastButton = LOW;
boolean enable = LOW;

Основной код устроен достаточно просто. Алгоритм переделанной программы выглядит так: изначально lastButton = LOW, переменная buttonState хранит текущий уровень сигнала на выходе кнопки, а условие if проверяет, если кнопка зажата, и предыдущее значение lastButton было LOW, то переменная enable инвертируется (LOW станет HIGH). После этого вне условия if функция digitalWrite() запишет уровень, который хранит переменная enable на контакт, к которому подключен светодиод. Финальный вид программы выглядит так:

const int buttonPin = 2; // Определим, что тактовая кнопка подключена к контакту "2"
const int ledPin =  13;  // Определим, что светодиод подключен к контакту "13"
int buttonState = 0;     // Зададим некую переменную и изначально присвоим ей нулевое значение (присвоим низкий уровень)
boolean lastButton = LOW;
boolean enable = LOW;

void setup()
{
  pinMode(ledPin, OUTPUT);   // Конфигурируем режим работы контакта "13" на выход
  pinMode(buttonPin, INPUT); // Конфигурируем режим работы контакта "2" на вход
}

void loop()
{
  buttonState = digitalRead(buttonPin); // Задаем, что переменная buttonState принимает значение с buttonPin (с контакта "2")

  if (lastButton == LOW && buttonState == HIGH) enable = !enable;

  lastButton = buttonState;    // Запоминаем значение кнопки. При следующем нажатии на кнопку, значение buttonState будет сравниваться с этой переменной
  digitalWrite(ledPin,enable); // Состояние светодиода управляется переменной enable
}

Тестируя данную программу можно заметить, что светодиод включается и выключается иногда не с первого раза. Дело в том, что при нажатии на кнопку происходит "дребезжание" впоследствии которого на контакт "2" поступает хаотичная последовательность из высоких и низких уровней. Решить эту задачу можно двумя способами: программно и аппаратно. Мы рассмотрим первый вариант, поскольку он легко реализуется и не требует включения в схему дополнительных элементов.
Конфигурация контактов и функция void seup() остаются без изменений. Помимо изменений в основном коде необходимо добавить новую функцию. Назовем её debounce(). Поскольку эта функция будет возвращать нам значение boolean, то необходимо приписать соответствующий тип данных, который возвращает функции. Эта функция принимает boolean значение, которое записывается в переменную под названием last. Далее снова считывается значение тактовой кнопки, идет сравнение с предыдущим значением и если значения различны, то после кратковременной задержки идет повторное считывание уровня поступающего на контакт "2".

const int buttonPin = 2;   // Определим, что тактовая кнопка подключена к контакту "2"
const int ledPin = 13;     // Определим, что светодиод подключен к контакту "13"
boolean buttonState = LOW; // Зададим некую переменную и изначально присвоим ей нулевое значение (присвоим низкий уровень)
boolean lastButton = LOW;  // Хранит состояние предыдущего нажатия
boolean enable = LOW;      // Переменная "переключатель"

void setup()
{
  pinMode(buttonPin, INPUT);
  pinMode(ledPin, OUTPUT);
}

boolean debounce(boolean last)
{
  boolean current = digitalRead(buttonPin);
  if(last != current)
  {
    delay(5);
    current = digitalRead(buttonPin);
  }
  return current;
}

void loop()
{
  buttonState = debounce(lastButton);
  if (lastButton == LOW && buttonState == HIGH)
    {
    enable = !enable;
    }

    lastButton = buttonState;
    digitalWrite(ledPin,enable);
}

Теперь все работает, как и задумывалось! А как же наша гирлянда? Давайте подключим 6 светодиодов через резисторы номиналом в 150 Ом согласно схеме.


Теперь нам необходимо просто идентифицировать наши светодиоды в программе. Мы не будем писать номер контакта и режим работы для каждого светодиода. Мы создадим массив pins[], а режим работы зададим через цикл for(). Окончательный вариант программы выглядит так:


const int buttonPin = 2;      // Определим, что тактовая кнопка подключена к контакту "2"
int pins[] = {3,5,6,9,10,11}; // Определим все подключенные светодиоды через массив
boolean buttonState = LOW;    // Зададим некую переменную и изначально присвоим ей нулевое значение (присвоим низкий уровень)
boolean lastButton = LOW;     // Хранит состояние предыдущего нажатия
boolean enable = LOW;

void setup()
{
  pinMode(buttonPin, INPUT);
  for(int mode = 0; mode <= 5; mode++) pinMode(pins[mode], OUTPUT);
}

boolean debounce(boolean last)
{
  boolean current = digitalRead(buttonPin);
  if(last != current)
  {
    delay(5);
    current = digitalRead(buttonPin);
  }
  return current;
}

void loop()
{
  buttonState = debounce(lastButton);
  if (lastButton == LOW && buttonState == HIGH)
    {
    enable = !enable;
    }

    lastButton = buttonState;
    for (int i=0; i<=5; i++)
      {
      digitalWrite(pins[i], enable);
      delay(40);
      digitalWrite(pins[i], LOW);
      delay(40);
      }
  
}

Цикл for() просто перебирает элементы массива pins[] и подставляет на место первого аргумента функции digitalWrite(). Зашиваем программу на Arduino и наслаждаемся результатом. Скорость гирлянды можно регулировать, варьируя значением аргумента функции delay(). Следует учитывать, что при большом его значении кнопка срабатывает по истечению временной задержки, но для данной задачи это не критично.

Надеюсь, что эта статья была для Вас полезной. В следующем уроке "Arduino и PWM" мы рассмотрим принцип работы ШИМ (широтно-импульсная модуляция) на данном примере.

3 коммент.:

plutov.by комментирует...

JS - http://plutov.by/post/fn_delay

Маргаритка комментирует...

Второй код прекрасно работает без "boolean debounce(boolean last)":
достаточно вставить delay(20) перед digitalWrite/

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

а если поменять void loop (удалив из кода переменную lastButton), то часть кода от дребезга контактов не помогает от слова совсем:

void loop(){

buttonState = digitalRead(buttonPin); // Задаем, что переменная buttonState принимает значение с buttonPin (с контакта "2")

if (buttonState == HIGH) enable = !enable;

digitalWrite(ledPin,enable); // Состояние светодиода управляется переменной enable

}

Unknown комментирует...

Здравствуйте! Прошу помочь по гирлянде с APA106, никак не хотят работать корректно! Какой проект есть для применения? Заранее, спасибо!

Отправить комментарий

 
Copyright © 2013 | Arduinokit
Arduino проекты. Уроки, программирование, управление и подключение ардуино.