Всем доброго времени суток Недавно написал скетч "автомат световых эффектов" с 3 режимами работы и возможностью переключения режимов кнопкой. Спойлер: автомат световых эффектов Код (C++): #include <Bounce.h> // Основная часть #define LED1 0 #define LED2 1 #define LED3 2 #define LED4 3 #define LED5 4 #define LED6 5 #define LED7 6 #define BUTTON 8 int value = 0; Bounce bouncer = Bounce(BUTTON,5); void setup() { pinMode(LED1, OUTPUT); pinMode(LED2, OUTPUT); pinMode(LED3, OUTPUT); pinMode(LED4, OUTPUT); pinMode(LED5, OUTPUT); pinMode(LED6, OUTPUT); pinMode(LED7, OUTPUT); pinMode(BUTTON, INPUT); } void loop() { char a=0; while(a < 50) { LED_CENTER(); a++; BOUNCE(); if(value==1) break; } value=0; delay(1000); char b=0; while(b < 5) { RUN_LED(); b++; BOUNCE(); if(value==1) break; } value=0; delay(1000); char c=50; while(c < 0) { RANDOM_LED(); c++; } } //// ОПИСАНИЕ ФУНКЦИЙ ////// void RUN_LED() // ПРОГРАММА 1 { for (int i=0; i<=7; i++) { digitalWrite(i,HIGH); delay(50); digitalWrite(i,LOW); delay(50); if(i==7) { for (int i=7; i>=0; i--) { digitalWrite(i,HIGH); delay(50); digitalWrite(i,LOW); delay(50); } } } } void RANDOM_LED() // ПРОГРАММА 2 { for (int i=random(0, 15); i<=random(0, 10); i++) { digitalWrite(i,HIGH); delay(50); digitalWrite(i,LOW); delay(50); } } void LED_CENTER() // ПРОГРАММА 3 { digitalWrite(LED4,HIGH); delay(50); digitalWrite(LED3,HIGH); digitalWrite(LED5,HIGH); delay(50); digitalWrite(LED6,HIGH); digitalWrite(LED2,HIGH); delay(50); digitalWrite(LED7,HIGH); digitalWrite(LED1,HIGH); delay(50); digitalWrite(LED7,LOW); digitalWrite(LED1,LOW); delay(50); digitalWrite(LED6,LOW); digitalWrite(LED2,LOW); delay(50); digitalWrite(LED3,LOW); digitalWrite(LED5,LOW); delay(50); digitalWrite(LED4,LOW); delay(50); } void BOUNCE() { if ( bouncer.update() ) { if ( bouncer.read() == HIGH) value=1; } } Но проблема этого кода в некорректном переключении режимов из-за кучи delay в функциях. Вот и решил я переписать функции с использованием millis(). Начал делать, в одну сторону светодиоды включаются и выключаются как надо. Спойлер: Бегущие огни Код (C++): unsigned long timeSave; unsigned long time; long numberPin=1; void setup() { pinMode(1, OUTPUT); pinMode(2, OUTPUT); pinMode(3, OUTPUT); pinMode(4, OUTPUT); pinMode(5, OUTPUT); pinMode(6, OUTPUT); pinMode(7, OUTPUT); } void loop() { time=millis(); digitalWrite(numberPin,HIGH); if(numberPin <= 7 && (time-timeSave) >= 200) { digitalWrite(numberPin,LOW); timeSave=time; numberPin++; } if (numberPin>=8) numberPin=1; } а вот в обратную... никак не могу организовать этот процесс. Подскажите люди добрые как это можно организовать.
как-то так: Код (C++): const byte ledCols = 7; // количество светодиодов const byte ledPins[ledCols] = {1,2,3,4,5,6,7}; // пины для светодиодов const byte ledTimeout = 200; // длительность свечения светодиода, либо пауза до включения следующего (мсек) byte numberPin = 1; enum { LED_LEFT, LED_RIGHT, LED_SPARK, LED_SNAKE}; // команды для LED void setup() { for(byte pin = 0; pin < ledCols; ++pin) pinMode(pin, OUTPUT); } void loop() { ledFlash(LED_LEFT, LED_SPARK); // ledFlash(LED_RIGHT, LED_SNAKE); // тут параллельно делаем что-то еще } void ledFlash(byte dir, byte effect) // входные данные: направление (dir) и эффект (effect) { static unsigned long timeOld; // префикс static запрещает обнулять переменную при выходе из функции static byte pin; if(millis() - timeOld > ledTimeout) // прошло больше N мсек с момента прошлого действия { if(dir == LED_RIGHT) // смотрим в каком направлении включать { pin++; if(pin >= ledCols) pin = 0; } else if (dir == LED_LEFT) { if(pin == 0) pin = ledCols; pin--; } if(effect == LED_SNAKE) // смотрим как именно включать { digitalWrite(ledPins[pin], !digitalRead(ledPins[pin])); } else if(effect == LED_SPARK) { digitalWrite(ledPins[pin], HIGH); delay(ledTimeout); digitalWrite(ledPins[pin], LOW); } timeOld = millis(); // запоминаем текущее время } }
Для решения задачи обработку нажатия кнопки лучше доверить прерыванию, а в основном цикле -- 'loop()' -- оставить только функционал, отвечающий за мигание светодиодами в соответствии с заданным режимом. Тогда использование 'delay()' в основном цикле не помешает, а наоборот упростит решение задачи.
Думал об этом, я щас пробую данный код под свои задачи подстроить... Спойлер: скетч Код (C++): const byte ledCols = 7; // количество светодиодов const byte ledPins[ledCols] = {1,2,3,4,5,6,7}; // пины для светодиодов const byte ledTimeout = 200; // длительность свечения светодиода, либо пауза до включения следующего (мсек) byte numberPin = 1; enum { LED_LEFT, LED_RIGHT, LED_SPARK, LED_SNAKE}; // команды для LED void setup() { for(byte pin = 0; pin < ledCols; ++pin) pinMode(pin, OUTPUT); } void loop() { ledFlash(LED_LEFT, LED_SPARK); // ledFlash(LED_RIGHT, LED_SNAKE); // тут параллельно делаем что-то еще } void ledFlash(byte dir, byte effect) // входные данные: направление (dir) и эффект (effect) { static unsigned long timeOld; // префикс static запрещает обнулять переменную при выходе из функции static byte pin; if(millis() - timeOld > ledTimeout) // прошло больше N мсек с момента прошлого действия { if(dir == LED_RIGHT) // смотрим в каком направлении включать { pin++; if(pin >= ledCols) pin = 0; } else if (dir == LED_LEFT) { if(pin == 0) pin = ledCols; pin--; } if(effect == LED_SNAKE) // смотрим как именно включать { digitalWrite(ledPins[pin], !digitalRead(ledPins[pin])); } else if(effect == LED_SPARK) { digitalWrite(ledPins[pin], HIGH); delay(ledTimeout); digitalWrite(ledPins[pin], LOW); } timeOld = millis(); // запоминаем текущее время } } если не получится буду под прерывания пробовать переделывать
А как можно эти две функции запустить отдельно с определенным условием? Пробовал и циклами и if-ом, херня получается
а какой смысл их запускать одновременно? Ведь светодиоды уже заняты одной функцией и если запустить вторую, то мигающая каша получится. Или надо просто переключаться между этими функциями? То просто: Код (C++): const byte ledCols = 7; // количество светодиодов const byte ledPins[ledCols] = {1, 2, 3, 4, 5, 6, 7};// пины для светодиодов const byte button1pin = A1; // кнопка для переключения направления движения огоньков, подключается между пином и GND const byte button2pin = A2; // кнопка для переключения эффекта огоньков, подключается между пином и GND const byte ledTimeout = 200; // длительность свечения светодиода, либо пауза до включения следующего (мсек) enum {LED_LEFT, LED_RIGHT}; // возможные направления движения огоньков enum {SPARK, SNAKE, STROB, END}; // возможные эффекты огоньков byte currentDirection; // переменная для хранения направления byte currentEffect; // переменная для хранения текущего эффекта void setup() { for (byte pin = 0; pin < ledCols; ++pin) pinMode(pin, OUTPUT); pinMode(button1pin, INPUT_PULLUP); pinMode(button2pin, INPUT_PULLUP); //pinMode(LED_BUILTIN, OUTPUT); //Serial.begin(9600); } void loop() { if (!digitalRead(button1pin)) currentDirection = 1 - currentDirection; // по нажатию кнопки 1 меняем направление на противоположное if (!digitalRead(button2pin)) currentEffect = ++currentEffect % END; // по нажатию кнопки 2 включаем следующий эффект ledFlash(currentDirection, currentEffect); // тут параллельно делаем что-то еще } void ledFlash(byte dir, byte effect) // входные данные: направление (dir) и эффект (effect) { static unsigned long timeOld; // префикс static запрещает обнулять переменную при выходе из функции static byte pin; if (millis() - timeOld > ledTimeout) // прошло больше N мсек с момента прошлого действия { if (dir == LED_RIGHT) // смотрим в каком направлении включать { pin++; if (pin >= ledCols) pin = 0; } else if (dir == LED_LEFT) { if (pin == 0) pin = ledCols; pin--; } switch (effect) // смотрим как именно включать { case SNAKE: digitalWrite(ledPins[pin], !digitalRead(ledPins[pin])); //digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); //Serial.print(pin); break; case SPARK: digitalWrite(ledPins[pin], HIGH); //digitalWrite(LED_BUILTIN, HIGH); delay(ledTimeout); //Serial.print(pin); digitalWrite(ledPins[pin], LOW); //digitalWrite(LED_BUILTIN, LOW); break; case STROB: for (byte n = 0; n < 4; n++) { digitalWrite(ledPins[pin], HIGH); //digitalWrite(LED_BUILTIN, HIGH); delay(ledTimeout / 8); //Serial.print(pin); digitalWrite(ledPins[pin], LOW); //digitalWrite(LED_BUILTIN, LOW); delay(ledTimeout / 8); } break; } timeOld = millis(); // запоминаем текущее время } }
http://arduino.ru/Tutorial/Upravlenie_portami_cherez_registry. Это подходит для автомата световых эффектов. Создайте массив констант и гоняйте его то в одну сторону, то обратно. А можно даже хаотично.
Не, не, запускать одновременно не надо,я не это имел ввиду. Я хочу добиться принципа работы как у китайской новогодней гирлянды (которая с пультом с одной кнопкой) Чтобы режимы переключались автоматически по достижении заданного порога, а также по нажатию кнопки, как было у меня в самом первом коде. Код (C++): char a=0; while(a < 50) { LED_CENTER(); a++; BOUNCE(); if(value==1) break; } value=0; delay(1000); char b=0; while(b < 5) { RUN_LED(); b++; BOUNCE(); if(value==1) break; } value=0; delay(1000);
Если бы я решал такую задачу, то делал в два вычислительных потока. 1 поток - отслеживания кнопки и переключение режима . 2 поток шаг программы в выбраном режиме. Единственный минус. Код сложен для понимания начинающих.
да ладно, чего там сложного? Код для отслеживания кнопок уже есть, он просто меняет нужные переменные по кругу. Добавить еще один блок, который отслеживает сколько времени прошло и изменяет те же самые переменные. Вводим еще одну константу, отвечающую за интервал автоматической смены эффекта: Код (C++): const byte effectTimeout = 30; // период автоматической смены эффекта (сек) Пишем вышеописанную функцию: Код (C++): void changeEffectByTimeout(unsigned long tt ) // входные данные: интервал "срабатывания" функции(сек) { static unsigned long timeOld; // префикс static запрещает обнулять переменную при выходе из функции if (millis() - timeOld > tt * 1000) // прошло больше N сек с момента прошлого действия { currentDirection = random(2); // выбираем случайное направление currentEffect = ++currentEffect % END; // активируем следующий эффект timeOld = millis(); // запоминаем текущее время } } Добавляем вызов этой функции в блок loop(), заодно можно избавиться от одной кнопки: Код (C++): void loop() { //if (!digitalRead(button1pin)) currentDirection = 1 - currentDirection; // по нажатию кнопки 1 меняем направление на противоположное //if (!digitalRead(button2pin)) currentEffect = ++currentEffect % END; // по нажатию кнопки 2 включаем следующий эффект if (!digitalRead(button1pin)) changeEffectByTimeout(0); // по нажатию кнопки 1 немедленно сменяем эффект else changeEffectByTimeout(effectTimeout); // если не прошло 30 сек, функция тупо ничего не делает и завершает свою работу ledFlash(currentDirection, currentEffect); // тут параллельно делаем что-то еще } Ну и чтобы random хоть как-то был произвольным, добавляем в блок setup() немного белого шума: Код (C++): randomSeed(analogRead(0));
Спасибо Буду пытаться освоить полученную информацию! И еще маленький вопрос: зачем в конце строки пишется % END??? И, что это означает??? Если просто к переменной прибавляется единица.
Общее количество эффектов равно трем (4-й элемент массива - это 3, т.к. в программировании отсчет ведется от нуля). END - это просто метка конца массива, в данном случае синоним числа 3 (для наглядности и удобства - я всегда могу поменять количество эффектов и мне не придется нигде в коде искать и исправлять 3 на новую цифру). И там не просто прибавляется единица, а еще от суммы берется остаток (в языке C++ это знак %), кратный трем. Почему записано именно так? А мне так захотелось Вместо скучного Код (C++): currentEffect = currentEffect + 1; if (currentEffect > 3) currentEffect = 0; то же самое выражение можно записать при помощи классической математики: Код (C++): currentEffect =++currentEffect % 3; или тернарного оператора: Код (C++): currentEffect = currentEffect < 3 ? currentEffect++ : 0 ; или булевой математики: Код (C++): currentEffect = currentEffect+++1-3*(currentEffect>2); Результат везде одинаковый - всегда на выходе получается цепочка 0-1-2-0-1-2-0-1-2... Это поначалу немного взрывает мозг, зато заставляет его шевелиться.