Независимый 3х-канальный плавный розжиг/затухание светодиодов. Помогите, пожалуйста!

Sharmanshik
Offline
Зарегистрирован: 15.10.2017

Доброго времени суток! 

Помогите, пожалуйста, разобраться со скетчем. 

Есть 3 кнопки, есть 3 светодиода. Каждая кнопка отвечает за свой светодиод. Алгоритм работы такой:

Нажали кнопку 1 — плавно загорелся светодиод 1. Отпустили кнопку 1 — плавно потух светодиод 1.

Нажали кнопку 2 — плавно загорелся светодиод 2. Отпустили кнопку 2 — плавно потух светодиод 2.

Нажали кнопку 3 — плавно загорелся светодиод 3. Отпустили кнопку 3 — плавно потух светодиод 3.

Реализовал скетч следующим образом:

#define led1 9 
#define button1 2
int light1;

void setup()
{ 
  pinMode(button1, INPUT);      //настариваем пин кнопки на вход
  pinMode(led1, OUTPUT);        //настариваем пин светодиода на выход
  light1 = 0;                   //задаем переменную для хранения последнего состояния света
}

void loop(){
   if(digitalRead(button1) == LOW)   //если кнопка нажата
   {
     if(light1 == 0)                 //и если свет не был включен
     {
       for(int i=0; i<=255; i++)     //то плавно включаем свет
       {
       analogWrite(led1, i); 
       delay(10);                    //каждые 10мс увеличение яркости на 1
       } 
       light1 = 1;                   //и передаем значение переменной, что свет включен
     }
   } 
   else                              //если кнопку отпускаем
   {
     if(light1 == 1)                 //если свет включен
     {
       for(int i=255; i>=0; i--)     //плавно гасим его
       {
       analogWrite(led1, i); 
       delay(10);
       } 
       light1 = 0;                    //и передаем значение переменной, что свет выключен
     }
   }
}

И точно так же для Button2 Led 2 и Button 3 Led 3

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

Пока разгорается/гаснет один из светодиодов, микроконтроллер не воспринимает другие команды. Например:

Быстро нажимаем подряд 1, 2, 3 кнопки. Сначала разгорается первый, потом второй, и только потом третий.

Как я понял, это происходит из-за delay, но уйти от этой функции у меня не получилось. Пытался внедрить millis(), максимум, чего добился - плавная пульсация при нажатой кнопке.

Вопрос: как реализовать независимую работу этих 3х каналов розжига? Нужно, чтобы во время розжига одного можно было разжигать/тушить остальные.

Всем откликнувшимся буду благодарен!

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Вы напоминаете мне Мартышку лезящую за бананами https://youtu.be/JazydPPq-2I?t=29   Во вот так. И это не издевка. Просто вы заведомо неправильно решаете эту задачу. А вот что бы правильно написать эту задачу у вас не ни знаний ни "мускул программирования" И выходит Вжик и вниз . Просто я не знаю как быть. Если я напишу скетч вы же его не поймете.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Sharmanshik, найдите скетчи "blink" и "blink without delay".

Внимательно изучите их и постарайтесь понять, чем они друг от друга отличаются.

Делают они одно и то же, но отличаются подходом.

Так вот, тот подход, что реализован в blink подходит для уроавления единственным устройством и принципиально не подходит для управления несколькими устройствами одновременно. А подход blink without delay может обеспечить работу с любым количеством устройств одновременно.

Приведенный Вами скетч написан в стиле blink, поэтому, для Вашей задачи требуется сначала переписать его в стиле blink without delay.

vosara
vosara аватар
Offline
Зарегистрирован: 08.02.2014
#define led1 9 
#define button1 2
#define led2 10 
#define button2 3
#define led3 11 
#define button3 4
uint8_t light1;
uint8_t light2;
uint8_t light3;
uint32_t time;

void setup()
{ 
  pinMode(button1, INPUT_PULLUP);      //настариваем пин кнопки на вход
  pinMode(led1, OUTPUT);        //настариваем пин светодиода на выход
  pinMode(button2, INPUT_PULLUP);      //настариваем пин кнопки на вход
  pinMode(led2, OUTPUT);
  pinMode(button3, INPUT_PULLUP);      //настариваем пин кнопки на вход
  pinMode(led3, OUTPUT);
}

void loop(){
  if(millis() - time >= 10){
    time = millis();
    if(!digitalRead(button1) && light1 < 255) light1++;  //кнопка нажата
    else if(digitalRead(button1) && light1 > 0) light1--;
    if(!digitalRead(button2) && light2 < 255) light2++;  //кнопка нажата
    else if(digitalRead(button2) && light2 > 0) light2--;
    if(!digitalRead(button3) && light3 < 255) light3++;  //кнопка нажата
    else if(digitalRead(button3) && light3 > 0) light3--;
  }
  analogWrite(led1, light1); 
  analogWrite(led2, light2); 
  analogWrite(led3, light3); 
}

 

Sharmanshik
Offline
Зарегистрирован: 15.10.2017

Большое спасибо, железяка заработала так, как хотелось. Завтра буду ставить в машину. Очень Вам благодарен! 

Sharmanshik
Offline
Зарегистрирован: 15.10.2017

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

Sharmanshik
Offline
Зарегистрирован: 15.10.2017

Если бы у меня были знания или "мускул программирования", я бы не обратился за помощью к опытным специалситам. Все мы с чего-то начинали, верно? 

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

sharmansik, я просто сказал, что "мускулы" надо тренировать регулярно занимаясь программированием. А вот готовое решение мускулы не подкачает.

пс: vosara написал достаточно простой скетч. У меня бы вышел сложнее. Но в моем свои резоны.

Sharmanshik
Offline
Зарегистрирован: 15.10.2017

На счет мускулов согласен. Пробовал писать простые программы, поморгать там, релешками пощелкать. Тема интересная, затягивает. Хочу поглубже вникнуть.

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

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Sharmanshik пишет:
Если подскажете, за счет каких элементов можно улучшить скетч, постараюсь разобраться.

Ну здесь надо знать философию программирования. Понятно для на деле у каждого программиста своя, как и методика как написать программу. Вот это как литературный русский. Вроде он един, но детские писатели пишут в одном стиле, взрослые в другом, официальные в третем, научные в четвертом, ну и так далее. Вот так и с программированием , а конкретно в ардуине. вашу задачу можно написать по разному. Так что улучшить скетч это написать более соответствующий скетч одной из философий. Свою философию, ну так как я ее тогда считал привел  здесь http://arduino.ru/forum/programmirovanie/klassy-arduino-po-qwone-dlya-chainikov  . Но для большинства на форуме это ересь и они никогда так писать не будут. А как улучшать свою программу решать вам.

Sharmanshik
Offline
Зарегистрирован: 15.10.2017

Спасибо, будем разбираться. 

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

sharmansik, вот перебивка програмы vosara под мой стиль. Програму не проверял в работе. Написал для себя в рамке тренировки мускул

/**/
unsigned long mill;
typedef void (*handler)() ;
//---------------------------------------------
class Cl_Led {
  protected:
    const byte pin;
    byte led = 0;
    unsigned long past;
  public:
    Cl_Led(byte _pin): pin(_pin) {}
    void init() {
      pinMode(led, OUTPUT);        //настариваем пин светодиода на выход
    }
    void run() {
      if (mill - past > 20) {
        past = mill;
        if (led > 0)analogWrite(pin, --led);
      }
    }
    void ON() {
      if (led < 255)analogWrite(pin, ++led);
    }
};
//-------------------------------
class Cl_Btn {
  protected:
    const byte pin;
    handler Do;
    unsigned long past;
  public:
    Cl_Btn(byte _pin, handler _Do): pin(_pin), Do(_Do) {}
    void init() {
      pinMode(pin, INPUT_PULLUP);      //настариваем пин кнопки на вход
    }
    void run() {
      if (mill - past > 10) {
        past = mill;
        if (!digitalRead(pin)) Do();
      }
    }
};
//-------Компоновка------------------------------------
Cl_Led Led1(/*пин*/9);
Cl_Led Led2(/*пин*/10);
Cl_Led Led3(/*пин*/11);
void Do_Btn1() {
  Led1.ON();
}
void Do_Btn2() {
  Led2.ON();
}
void Do_Btn3() {
  Led3.ON();
}
Cl_Btn Btn1(/*пин*/2,/*обработчик*/&Do_Btn1);
Cl_Btn Btn2(/*пин*/3,/*обработчик*/&Do_Btn2);
Cl_Btn Btn3(/*пин*/4,/*обработчик*/&Do_Btn3);
//-----------main()----------------------------------------
void setup() {
  Led1.init();
  Led2.init();
  Led3.init();
  Btn1.init();
  Btn2.init();
  Btn3.init();
}
void loop() {
  mill = millis();
  Led1.init();
  Led2.init();
  Led3.init();
  Btn1.init();
  Btn2.init();
  Btn3.init();
}
/*Скетч использует 1300 байт (4%) памяти устройства. Всего доступно 30720 байт.
  Глобальные переменные используют 52 байт (2%) динамической памяти, оставляя 1996 байт для локальных переменных. Максимум: 2048 байт.
*/

 

Sharmanshik
Offline
Зарегистрирован: 15.10.2017

Благодарю за помощь. Вопрос не по теме: изучение С++ поможет в написании скетчей для Ардуино? 

nik182
Offline
Зарегистрирован: 04.05.2015

Квон. Слепое копирование приводит к ошибкам.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

nik182 пишет:
Квон. Слепое копирование приводит к ошибкам.

Вижу уже описался.

/**/
unsigned long mill;
typedef void (*handler)() ;
//---------------------------------------------
class Cl_Led {
  protected:
    const byte pin;
    byte led = 0;
    unsigned long past;
  public:
    Cl_Led(byte _pin): pin(_pin) {}
    void init() {
      pinMode(led, OUTPUT);        //настариваем пин светодиода на выход
    }
    void run() {
      if (mill - past > 20) {
        past = mill;
        if (led > 0)analogWrite(pin, --led);
      }
    }
    void ON() {
      if (led < 255)analogWrite(pin, ++led);
    }
};
//-------------------------------
class Cl_Btn {
  protected:
    const byte pin;
    handler Do;
    unsigned long past;
  public:
    Cl_Btn(byte _pin, handler _Do): pin(_pin), Do(_Do) {}
    void init() {
      pinMode(pin, INPUT_PULLUP);      //настариваем пин кнопки на вход
    }
    void run() {
      if (mill - past > 10) {
        past = mill;
        if (!digitalRead(pin)) Do();
      }
    }
};
//-------Компоновка------------------------------------
Cl_Led Led1(/*пин*/9);
Cl_Led Led2(/*пин*/10);
Cl_Led Led3(/*пин*/11);
void Do_Btn1() {
  Led1.ON();
}
void Do_Btn2() {
  Led2.ON();
}
void Do_Btn3() {
  Led3.ON();
}
Cl_Btn Btn1(/*пин*/2,/*обработчик*/&Do_Btn1);
Cl_Btn Btn2(/*пин*/3,/*обработчик*/&Do_Btn2);
Cl_Btn Btn3(/*пин*/4,/*обработчик*/&Do_Btn3);
//-----------main()----------------------------------------
void setup() {
  Led1.init();
  Led2.init();
  Led3.init();
  Btn1.init();
  Btn2.init();
  Btn3.init();
}
void loop() {
  mill = millis();
  Led1.run();  // < здесь должны быть методы run()
  Led2.run();
  Led3.run();
  Btn1.run();
  Btn2.run();
  Btn3.run();
}
/*Скетч использует 1596 байт (5%) памяти устройства. Всего доступно 30720 байт.
  Глобальные переменные используют 52 байт (2%) динамической памяти, оставляя 1996 байт для локальных переменных. Максимум: 2048 байт.
*/

После написания программы идет ее отладка. В том числе отлавливания ошибок копирования. 

Sharmanshik пишет:

Благодарю за помощь. Вопрос не по теме: изучение С++ поможет в написании скетчей для Ардуино? 

Так я на С++ пишу. Просто остальные только на Си.  Вот еще нюанс который все тупо не замечают. Си это не два языка, а три. Третий язык это пользовательский. Пользователь (разбирающий программист) создает новый язык используя команды Си и Си++  и на нем пишет. Все подключают библиотеки . А это и есть элементы пользовательского языка. Но не все могут создать новый пользовательский язык и к нему новые быблиотеки. Так что изучайте Си++ и разумеется философию OOП.

Клапауций 112
Клапауций 112 аватар
Offline
Зарегистрирован: 01.03.2017

qwone, я смотрю на пример, который ты постоянно показываешь навичкам и в недоумении - ладно бы это был пример для одной кнопки, то вопрос администрирования кнопок, стоял бы где-то там.

но, ты можешь себе представить, как вся эта твоя "компоновка" будет выглядеть для, допустим 100 кнопок?

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

qwone пишет:

пс: vosara написал достаточно простой скетч. У меня бы вышел сложнее. Но в моем свои резоны.

Ну так Вы б на С++ писали, а тут на С.

 

Да, посмотрел дальше (уже написанное на С++): на мой вкус, все, что больше двух - в массив.

 

qwone пишет:

Пользователь (разбирающий программист) создает новый язык используя команды Си и Си++  и на нем пишет.

Подтверждаю. Лично я никогда не забочусь о "чистоте языка", поэтому получается именно смесь С и С++. Правда, новым языком я бы это не назвал. Тем более, что соотношение С и С++ явно непостоянно - чем больше проект, тем больше доля С++.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Клапауций 112 пишет:
но, ты можешь себе представить, как вся эта твоя "компоновка" будет выглядеть для, допустим 100 кнопок?

А вы знаете о законе перехода количественных изменений в качественные в диалектике Гегеля. Очень полезный закон для уважающего себя программиста. Хотя если Страуструп или Деннис Ритчи о нем не говорил, значит и нечего дурное в голову тащить. Будем тупо херачить в 100 один и тот же код 100 раз.

Ну ладно приведу пример вот есть солдат. 9 солдат это отделение, 3 отделения это взвод 3 взвода это рота итого 81 человек. Вот возьмем 100 случайных солдат и 1 роту. По количественному соотношению 100 человек могут запинать одну роту. Ну ладно возьмем 200-500 человек , эту толпу и кинем на одну огранизованую роту. То странно 1 рота может разогнать эту толпу. Разумеется если командиры командиры отделений , взвода, роты постоянно дрючат своих солдат бится организовано, прикрывая друг друга, а баранами бежать на врага.

ПС: Если в программе меньще 5 кнопок, то можно использовать класс одиночной кнопки, со своми короткими и длинными нажатиями, одинарными и двойными тройными нажатиями. Но если у вас в скетче 5 кнопок, то не занимайтесь херней. Это у вас уже клавиатура. И пишите класс для клавиатуры из 5 кнопок. Но если кнопок 100. То это уже консоль ввода и класс должен быть с интерфейсом по типу Serial.

Клапауций 112
Клапауций 112 аватар
Offline
Зарегистрирован: 01.03.2017

qwone пишет:

ПС: Если в программе меньще 5 кнопок, то можно использовать класс одиночной кнопки, со своми короткими и длинными нажатиями, одинарными и двойными тройными нажатиями. Но если у вас в скетче 5 кнопок, то не занимайтесь херней. Это у вас уже клавиатура. И пишите класс для клавиатуры из 5 кнопок. Но если кнопок 100. То это уже консоль ввода и класс должен быть с интерфейсом по типу Serial.

ясно - в любой непонятной ситуации забалтывать тему и не отвечать на поставленный вопрос.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

andriano пишет:

 

Да, посмотрел дальше (уже написанное на С++): на мой вкус, все, что больше двух - в массив.

Я сказал об этом ранее . Ради 2-х городить массив ни к чему. А больше 5 это уже новый класс.

andriano пишет:

Подтверждаю. Лично я никогда не забочусь о "чистоте языка", поэтому получается именно смесь С и С++. Правда, новым языком я бы это не назвал. Тем более, что соотношение С и С++ явно непостоянно - чем больше проект, тем больше доля С++.

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

  Чем больше проект тем больше элементов пользовательского языка, которые проще реализуются через Си++. Поэтому доля Си++ увеличинается. И да обычно программисты кидаются на программирование программы, чем на проектирование. Хотя бы потому что проектировать программы умеют кое-как. Это сложнее чем освоить синтаксис языка и даташиты для электронных компонентов.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Клапауций 112 пишет:
ясно - в любой непонятной ситуации забалтывать тему и не отвечать на поставленный вопрос.
Может я не буду отвечать на ваши вопросы, вы все равно не понимаете, а забалтывать тему мне не хочется.

Клапауций 112
Клапауций 112 аватар
Offline
Зарегистрирован: 01.03.2017

qwone пишет:

Может я не буду отвечать на ваши вопросы...

может, ты мне ответишь, почему твой пример масштабируется в километровую простыню?