На предыдущих страницах рассматривал управление светодиодом и всё это плавно перешло в программу управления светодиодными гирляндами. Вроде на улице март, но такой холодный и снежный, что новогодние гирлянды как раз в тему , хотя, конечно, не обязательно вешать эти гирлянды именно на ёлку, можно просто использовать как праздничное оформление.
В скетче, который рассмотрим на этой странице, будет использование аппаратного прерывания для считывания кнопки, а так же используем ШИМ (PWM), как в предыдущем примере, для управления яркостью некоторых гирлянд.
Так как хочу использовать кнопку для выбора светового эффекта, то потребуется устранить дребезг контактов этой кнопки. В данном случае, раз мы её будем использовать через прерывания, то для уменьшения кода имеет смысл сделать схему аппаратного устранения дребезга. Для этих целей использовал операционный усилитель LM358P. Эта микросхема не дорогая, доступная, имеет корпус DIP8 и обладает пониженным энергопотреблением. Её удобно использовать, и в одном корпусе имеется два операционных усилителя, что удобно, если использовать две кнопки (для возможного расширения функций в будущем).
Для устранения дребезга кнопки подходит схема триггера Шмитта. Это такое построение схемы, при котором имеется гистерезис выходного сигнала. Изменяющееся входное напряжение при достижении определённого уровня вызывает скачкообразное изменение на выходе, то есть позволяет на выходе получить импульсный сигнал из аналогового на входе. Этим выходным сигналом мы будем управлять выбором световых эффектов.
Чтобы устранить множественность импульсов, которые даёт кнопка при её нажатии и отпускании, использую интегрирующий конденсатор, который установим параллельно кнопке. Сигнал с этого конденсатора подадим на инвертирующий вход ОУ (контакт 2).
Принцип работы триггера Шмитта поясняется на картинке Pic 1. Когда нажимается кнопка SB, то напряжение на конденсаторе C быстро разряжается через контакты кнопки. При достижении на нём уровня TL, выходное напряжение на триггере переходит в высокое состояние. При отпускании кнопки, конденсатор C начинает относительно медленно заряжаться через резистор R до уровня напряжения питания. Когда напряжение на нём достигнет уровня TH, то на выходе триггера напряжение скачком перейдёт в низкое значение. Дребезг контактов кнопки в виде пачки импульсов на включении и выключении сглаживается за счёт инерции заряда конденсатора, что не даёт напряжению на входе инвертора достигнуть порога TH и тем самым переключить выход триггера в другое состояние.
Для реализации схемы триггера Шмитта на ОУ потребуется:
Ниже на рисунке два типа включения LM358. На типе a) ближе к монтажной схеме, где операционный усилитель представлен DIP корпусом, а на типе b) — уже электрическая принципиальная схема.
Используя схему триггера Шмитта в качестве подавителя дребезга кнопки, разрабатываем нашу схему, см. рис.3.
В этом примере, в качестве гирлянд, использую одиночные светодиоды, которые можно подключить к выходам 3, 4, 5, 6, 7 через резисторы 330...1000 Ом. Конечно, для подключения реальной светодиодной гирлянды потребуется сделать силовую часть схемы, на которую будут поступать сигналы с Arduino. Думаю, что к следующим новогодним праздникам и новогодней ёлке сделаю такую силовую плату и добавлю в этот проект, а пока чисто программа управления гирляндами и имитация их с помощью светодиодов.
Всё монтируем на макетной панельке. Делаем надлежащие подключения к плате Arduino и переходим к самому скетчу (программе).
Используем наработки предыдущих скетчей и добавляем кое-что новое. Этим новым будет использование прерываний. У Arduino есть два аппаратных прерывания int0 и int1, которые подключены к контактам pin2 и pin3 соответственно. В моём случае буду использовать прерывание int0 (контакт pin2). На этот контакт подключим выход схемы устранения дребезга.
Что такое прерывание? Это значит, что выполнение программы аппаратно прерывается и опрашивается определённый вход, в нашем случае – pin2. Если на заданном входе ничего не происходит, то выполняется очередной шаг программы дальше.
Само прерывание программно подключается с помощью функции attachInterrupt. Аргументы у этой функции следующие:
Для того, чтобы передавать значение выбранного эффекта из функции обработки прерывания fn_ISR() в функцию loop(), используем для переменной effect тип volatile byte.
Ниже скетч v3.1, для просмотра – нажмите на кнопку:
Этот скетч можно дополнить ещё какими-нибудь эффектами. К примеру, добавить эффект случайного перебора гирлянд. Возможно в дальнейшем этот эффект и добавлю.
Код подробно закоментирован, надеюсь трудностей в его понимании не будет.
P.S. Изменил типы переменных с int на byte, что позволило уменьшить код.
2018-03-28Скетч можно скачать тут: sketch__3.zip
2018-07-23Подошло время новогодней ёлки, соответственно потребовалась уже реальная гирлянда из светодиодов. Сделал небольшую гирлянду из 25 светодиодов. Пять секций по пять светодиодов. Можно сделать любую, какую хотите, всё упирается в напряжение, которое подаётся на светодиоды. У меня был использован блок питания Импульсный блок питания на 9 В
, только выходное напряжение не 9 В, а перенастроил его на 12 В.
Дополнительно сделал фильтр. Добавил модуль ключей на транзисторах 2N2222, которые управляют секциями гирлянды. На входы ключей идут управляющие сигналы с Ардуино через резисторы 1.2 кОм. Для их управления задействовал порты только с ШИМ, чтобы можно было плавно менять яркость всех секций. Номера контактов: D3, D5, D6, D9, D10. Кнопку выбора эффекта и кнопку сброса вывел на небольшую плату, на ней же вначале установил линейный стабилизатор на КРЕН5В, дающий 5В для питания Ардуино от импульсного блока питания. Потом для этого стабилизатора сделал отдельный модуль. Размеры его платы 27.5х20 мм.
Окончательный скетч смотрите ниже:
Новый скетч можно скачать тут: my_led_elka.zip
Триггер Шмитта (подавитель дребезга кнопки) сделал в виде отдельного модуля, если вдруг понадобится не в новогоднее время, то можно быстро снять и использовать в каком-нибудь другом проекте . Модуль имеет размеры 25х20 мм.
Ниже на фото реализация устройства, остаётся лишь всё поместить в удобную коробку или какой-нибудь корпусок.
На фото выше слева-направо видны отдельные модули:
В процессе проверки работы устройства.
Осталось всю эту механизацию
прикрутить на ёлку и заряжаться праздничным настроением
Вот и дошло дело до коробка. Решил сделать его из ДВП, по-быстрому. Ниже на фото рабочий процесс изготовления.
А далее всё уже установлено в коробок. На боковой стенке есть отверстие под кнопки управления и ещё отверстие под USB, для загрузки обновлений программы. В принципе, это устройство нужно до 19 января, а там можно его части, по необходимости, снять и использовать в других проектах, особенно саму плату Ардуино.
2018-12-23Было проверено на ёлке – замечательно!
Подходит окончание этого тяжёлого 2020 года, приближается праздник Нового 2021 года. Сразу возникает потребность отыскать всякую иллюминацию и привести её в надлежащий порядок .
Недавно нашёл гирлянду и блок управления. Из него, после того 2018 нового года, изъял плату Arduino UNO R3 для отладки других проектов. А в этот блок управления сейчас поставил уже Arduino Pro Mini на микроконтроллере ATMega168P. Сам скетч использует всего 3722 байт, поэтому использовать ATMega168P более разумно, чем ATMega328P.
Так как такая ёлочная гирлянда используется всего несколько дней в году, то сделал небольшую плату-шилд, с установленной на ней панелькой под Arduino Pro Mini. Теперь можно легко вставить или вытащить плату с Ардуино, если понадобится для каких-нибудь других проектов, а на время Нового года всегда можно запрограммировать этим скетчем для управления гирляндой.
Глянув критически на скетч, решил его немного модернизировать. Что в нём нового?
Немного переписал функции. Поигрался временными задержками, но самое главное, это для режима effect = 0 применил не последовательный перебор эффектов, а случайный их выбор, через функцию random().
Ещё заменил функцию delay() на свою функцию fnDelay(), которая делает задержку через функцию millis().
В остальном скетч 2020 года подобен предыдущим.
/*********************************************************** 2018-03-19 Mr.ALB Тренировка в программировании Ардуино Управление 5-ю светодиодными гирляндами 7-ть эффектов работы Дребезг книпки SB1 устраняется аппаратно с помощью LM358P 2018-12-08 9-ть эффектов работы 2018-12-11 11-ть эффектов работы 2020-12-02 Arduino Pro Mini + случайный выбор эффектов при effect = 0 + добавлена функция fnDelay() вместо delay() ***********************************************************/ /* Задержки в мс */ #define DELAY_1 100 #define DELAY_2 200 #define DELAY_3 300 #define DELAY_5 500 #define DELAY_10 1000 #define MAX_EFFECT 11 byte mode = 0; //Режим работы гирлянды byte ledLevel = 0; byte deltaLevel = 10; // Шаг изменения яркости /* Переменные для кнопки */ #define BUTTON 2 // Подключаем кнопку SB1 на 2-й pin /* Перывание */ volatile byte effect = 0; //Визуальный эффект через прерывание const byte numberInt = 0; //Номер прерывания byte repeat; // Повтор unsigned long time_now, time_check; // Для fnDelay() /*************************************** Функция - Включение гирлянды (секции) Состояние flag: Если 0, то выключить Если 1, то включить Если 2, то включить и выключить **************************************/ void fnBlinkLed(byte pin, word msec, byte flag = 2) { if (flag == 1 || flag == 2) { digitalWrite(pin, HIGH);//Включение fnDelay(msec); } if (flag == 0 || flag == 2) { digitalWrite(pin, LOW);//Выключение fnDelay(msec); } } /*********************** Настройка программы ***********************/ void setup() { /* Цифровые выводы под номерами 3,5,6,9,10 на выход */ for (byte i = 3; i < 11; i++) { if (i != 4 || i != 7 || i != 8) pinMode(i, OUTPUT); } pinMode(BUTTON, INPUT); // SB1 - Выбор эффектов pinMode(LED_BUILTIN, OUTPUT); // Светодиод на pin13 сигнализация digitalWrite(LED_BUILTIN, LOW);// Потушим светодиод на pin13 /* Прикрепим прерывание int0 (pin2) к функции fn_ISR */ attachInterrupt(numberInt, fn_ISR, RISING); /* Установка начала псевдослучайной последовательности */ randomSeed(analogRead(0)); // Проверка работоспособности всех светодиодов fnEffect11(); } /******************************************* Переключение пяти гирлянд. 9 эффектов effect = 0 - все эффекты последовательно 1 - первый эффект 2 - второй эффект ... ********************************************/ void loop() { if (effect == 1) fnEffect1(); if (effect == 2) fnEffect2(); if (effect == 3) fnEffect3(); if (effect == 4) fnEffect4(); if (effect == 5) fnEffect5(); if (effect == 6) fnEffect6(); if (effect == 7) fnEffect7(); if (effect == 8) fnEffect8(); if (effect == 9) fnEffect9(); if (effect == 10) fnEffect10(); if (effect == 11) fnEffect11(); if (effect == 0) { /* Включаем случайным выбором эффект */ switch (random(1, 12)) { case 1: fnEffect1(); break; case 2: fnEffect2(); break; case 3: fnEffect3(); break; case 4: fnEffect4(); break; case 5: fnEffect5(); break; case 6: fnEffect6(); break; case 7: fnEffect7(); break; case 8: fnEffect8(); break; case 9: fnEffect9(); break; case 10: fnEffect10(); break; case 11: fnEffect11(); } } /* Выключаем все гирлянды */ for (byte i = 3; i < 11; i++) { if (i != 4 && i != 7 && i != 8) fnBlinkLed(i, 1, 0); //Выключаем } fnDelay(DELAY_1); //Сигнализируем об окончании цикла fnBlinkLed(LED_BUILTIN, DELAY_1); } /*********************** Функции ***********************/ /****************************************** Функция - задержка на время delaytime delaytime в мс *****************************************/ void fnDelay(int delaytime) { time_check = millis(); do { time_now = millis(); } while (time_now - time_check < delaytime); } /* Функция - Обработка прерывания */ void fn_ISR() { /* При нажатии на кнопку SB1, происходит выбор эффекта */ ++effect; //Переходим на другой эффект if (effect > MAX_EFFECT) effect = 0; // Установим все эффекты сразу } /* Функция - Изменение яркости светодиода */ void fn_stepBrightness(byte ledPin, word step_delay, byte deltaLevel, int sign) { if (sign == 1)ledLevel = 0; else ledLevel = 255; for (byte i = 0; i < 25; i++) { analogWrite(ledPin, ledLevel); ledLevel = ledLevel + deltaLevel * sign; if (ledLevel < deltaLevel)ledLevel = 0; fnDelay(step_delay); } } /******************************* Первый эффект. Поочерёдно включаем и выключаем гирлянды */ void fnEffect1() { for (int i = 3; i < 11; i++) { if (i != 4 && i != 7 && i != 8) fnBlinkLed(i, DELAY_3); } } /*************************************** Второй эффект. Последовательно включаем все гирлянды ****************************************/ void fnEffect2() { for (byte i = 3; i < 11; i++) { if (i != 4 && i != 7 && i != 8) fnBlinkLed(i, DELAY_5, 1); } /* ...потом в обратном порядке их выключаем */ for (byte i = 10; i > 2; i--) { if (i != 4 && i != 7 && i != 8) fnBlinkLed(i, DELAY_3, 0); } } /******************************* Третий эффект. Все включить, задержка 0,5 с Все выключить, задержка 0,5 с ********************************/ void fnEffect3() { repeat = 5; //Повтор 5 раз for (byte k = 0; k < repeat; k++) { for (byte j = 0; j < 2; j++) { if (j == 0) mode = 1; //Режим включения else mode = 0; //Режим выключения for (byte i = 3; i < 11; i++) { if (i != 4 && i != 7 && i != 8) fnBlinkLed(i, 1, mode);//Задержка 1 мс } fnDelay(DELAY_5);//Задержка 0,5 с } } } /************************************************* Четвертый эффект. Быстрая последовательная пробежка по гирляндам **************************************************/ void fnEffect4() { if (effect == 4 || effect == 0) { repeat = 5; //Повтор 5 раз for (byte k = 0; k < repeat; k++) { // В прямом порядке... for (byte i = 3; i < 11; i++) { if (i == 3) fnBlinkLed(i, DELAY_2, 0); if (i == 5 || i == 6 || i == 9) fnBlinkLed(i, DELAY_2); if (i == 10) fnBlinkLed(i, DELAY_2, 1); } // ...в обратном порядке for (byte i = 10; i > 2; i--) { if (i == 10) fnBlinkLed(i, DELAY_2, 0); if (i == 5 || i == 6 || i == 9) fnBlinkLed(i, DELAY_2); if (i == 3) fnBlinkLed(i, DELAY_2, 1); } } } } /************************************ Пятый эффект. Включаем с крайних к центральной ************************************/ void fnEffect5() { // Включаем с крайних до центральной fnBlinkLed(3, 1, 1); fnBlinkLed(10, 1, 1); //Задержка 1 мс fnDelay(DELAY_3); //Задержка 500 мс fnBlinkLed(5, 1, 1); fnBlinkLed(9, 1, 1); fnDelay(DELAY_3); fnBlinkLed(6, 1, 1); fnDelay(DELAY_10); // Выключаем центральную fnBlinkLed(6, 1, 0); //Задержка 1 мс fnDelay(DELAY_2); //Задержка 500 мс fnBlinkLed(5, 1, 0); fnBlinkLed(9, 1, 0); fnDelay(DELAY_2); fnBlinkLed(3, 1, 0); fnBlinkLed(10, 1, 0); fnDelay(DELAY_2); } /******************************* Шестой эффект. Включаем ШИМ на 3,5,6,9,10 *******************************/ void fnEffect6() { for (byte i = 3; i < 11; i++) { if (i != 4 && i != 7 && i != 8) { fn_stepBrightness(i, DELAY_1, deltaLevel, 1); // Зажигается fn_stepBrightness(i, DELAY_1, deltaLevel, -1); // Тухнет } } fnDelay(DELAY_2); } /******************************* Седьмой эффект. Быстрое моргание - мерцание *******************************/ void fnEffect7() { repeat = 108; //Повтор 108 раз for (byte k = 0; k < repeat; k++) { for (byte j = 0; j < 2; j++) { if (j == 0) mode = 1; //Режим включения else mode = 0; //Режим выключения for (byte i = 3; i < 11; i++) { if (i != 4 && i != 7 && i != 8) fnBlinkLed(i, 1, mode);//Задержка 1 мс } fnDelay(21); } } } /***************************************** Восьмой эффект. включение 3-6-10 далее 5-9 → повтор *****************************************/ void fnEffect8() { repeat = 16; //Повтор 16 раз for (byte k = 0; k < repeat; k++) { fnBlinkLed(5, 1, 0); //Режим выключения fnBlinkLed(9, 1, 0); fnBlinkLed(3, 1, 1); //Режим включения fnBlinkLed(6, 1, 1); fnBlinkLed(10, 1, 1); fnDelay(DELAY_5); fnBlinkLed(5, 1, 1); //Режим включения fnBlinkLed(9, 1, 1); fnBlinkLed(3, 1, 0); //Режим выключения fnBlinkLed(6, 1, 0); fnBlinkLed(10, 1, 0); fnDelay(DELAY_2); } } /*********************** Девятый эффект. включение x-5-6-9-10 далее 3-x-6-9-10 далее 3-5-x-9-10 далее 3-5-6-x-10 далее 3-5-6-9-x ***********************/ void fnEffect9() { repeat = 5; //Повтор 5 раз for (byte k = 0; k < repeat; k++) { for (byte i = 5; i < 11; i++) { // Включаем 5-6-9-10 if (i != 7 && i != 8) fnBlinkLed(i, 1, 1); //Задержка 1 мс if (i == 3) fnBlinkLed(3, DELAY_3, 0); // Гасим } fnBlinkLed(3, 1, 1); // Включаем fnBlinkLed(5, DELAY_5, 0); // Гасим fnBlinkLed(5, 1, 1); // Включаем fnBlinkLed(6, DELAY_3, 0); // Гасим fnBlinkLed(6, 1, 1); // Включаем fnBlinkLed(9, DELAY_3, 0); // Гасим fnBlinkLed(9, 1, 1); // Включаем fnBlinkLed(10, DELAY_3, 0); // Гасим } } /*************************************** Десятый эффект. Быстрое разное моргание - мерцание **************************************/ void fnEffect10() { repeat = 108; //Повтор 108 раз for (byte k = 0; k < repeat; k++) { for (byte j = 0; j < 2; j++) { if (j == 0) mode = 1; //Режим включения else mode = 0; //Режим выключения for (byte i = 3; i < 11; i++) { if (i == 3) fnBlinkLed(i, 1, mode);//Задержка 1 мс if (i == 5) fnBlinkLed(i, 2, mode);//Задержка 2 мс if (i == 6) fnBlinkLed(i, 3, mode);//Задержка 3 мс if (i == 9) fnBlinkLed(i, 4, mode);//Задержка 4 мс if (i == 10) fnBlinkLed(i, 5, mode);//Задержка 5 мс } fnDelay(21); } } } /*********************** Одиннадцатый эффект. Просто включены ***********************/ void fnEffect11() { mode = 1; //Режим включения for (byte i = 3; i < 11; i++) { if (i != 4 && i != 7 && i != 8) fnBlinkLed(i, 1, mode);//Задержка 1 мс } fnDelay(10000);// Задержка 10 c mode = 0; //Режим выключения for (byte i = 3; i < 11; i++) { if (i != 4 && i != 7 && i != 8) fnBlinkLed(i, 1, mode);//Задержка 1 мс } }
Используемые библиотеки и программы: