Классы Ардуино по qwone для чайников.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Так оно очень редко будет сбиваться. Совсем редко. Чтобы оно сбилось нужно, чтобы прерывание по таймеру пришлось на момент, когда часть переменной timer0_millis уже считана, а остальное ещё нет. Но и этого мало. Надо ещё, чтобы при обработке прерывания произошёл перенос разряде из считанной части в несчитанную. Вот тогда прочитает криво.

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Парень, ты не ерепенься, а просто прикинь. Вот, допустим, у тебя в скетче с пяток кнопок, пара датчиков, ну, там, скажем, с десяток объектов. Это ведь нормально? Десяток объектов на скетч, правда?

А теперь

qwone пишет:

Вопрос : Сколько раз вызывается функция millis() за проход loop().

Ответ: в лучшем случае 10 раз, а в худшем - 20 раз.

Вот скажи честно, НАФИГА? В каком месте у Мейера написано, что millis нужно десять-двадцать раз вызывать?

Если уж ты решил, что

qwone пишет:

я требую от классов:
1 - что бы в них обязательно были public методы setup() и loop()
2-  что бы представители этих классов,что из одного класса,что из разных, могли взаимно не тормозить друг друга. 

то, начни с того, что создай абстрактный класс ArduinoBase с двумя абстрактыми ( = 0) методами setup и loop, а все классы объектов наследуй от него. И это будет, кстати, гораздо более "по Мейеру"! А функции loop сделай константный параметр currentMillis. Тогда ты будешь один раз спрашивать millis в начале глобального loop и просто всем остальным loop'ам его передавать. Тогда millis будет спрашиваться один раз за проход loop, а не 10-20. А больше и не надо.

 

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

Нет криво может прочитать только timer0_millis потому что при вашем чтении может вклинится прерывание и испортить. Обвяжите запретом прерывания 9 и 10 строчку и всё будет нормально. Кстати, испортить может на 1001 раз.

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

Ворота пишет:
А функции loop сделай константный параметр currentMillis. Тогда ты будешь один раз спрашивать millis в начале глобального loop и просто всем остальным loop'ам его передавать. Тогда millis будет спрашиваться один раз за проход loop, а не 10-20. А больше и не надо.

Вот только код будет выглядить так .

/*
*/
unsigned long mill;
//------------------------------------------------
class Cl_Led {
    const byte _pin;
    bool led;
    unsigned long past = 0;
  public:
    Cl_Led(byte pin): _pin(pin) {}
    void setup() {
      pinMode(_pin, OUTPUT);
      digitalWrite(_pin, led = 0);
    }
    void loop() {
      if (mill - past >= 200) {
        past = mill;
        digitalWrite(_pin, led = !led);
      }
    }
};
//----------Компоновка--------------------------------------
Cl_Led Led(/*пин*/13);
//--------main()----------------------------------------
void setup() {
  Led.setup();
}

void loop() {
  mill = millis();
  Led.loop();
}
/*Скетч использует 926 байт (2%) памяти устройства. Всего доступно 32256 байт.
Глобальные переменные используют 19 байт (0%) динамической памяти, оставляя 2029 байт для локальных переменных. Максимум: 2048 байт.
*/

ПС: В Си сначало надо объявить, а потом использовать. Но никак наоборот.:(

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

nik182 пишет:

Нет криво может прочитать только timer0_millis потому что при вашем чтении может вклинится прерывание и испортить. Обвяжите запретом прерывания 9 и 10 строчку и всё будет нормально. Кстати, испортить может на 1001 раз.

А если прерывание пройдет когда был запрет. Вот один тик и улетит.

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

Нет не улетит. Тики улетают если время обработки прерывани больше двойного времени тика. Флаг прерывания никуда не девается. После разрешения всё обработается. 

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

nik182 пишет:

Нет не улетит. Тики улетают если время обработки прерывани больше двойного времени тика. Флаг прерывания никуда не девается. После разрешения всё обработается. 

Тогда код будет таким. Или есть лучше?

/*
*/
unsigned long mill;
extern unsigned long timer0_millis;
//------------------------------------------------
class Cl_Led {
    const byte _pin;
    bool led;
    unsigned long past = 0;
  public:
    Cl_Led(byte pin): _pin(pin) {}
    void setup() {
      pinMode(_pin, OUTPUT);
      digitalWrite(_pin, led = 0);
    }
    void loop() {
      if (mill - past >= 1000) {
        past = mill;
        digitalWrite(_pin, led = !led);
      }
    }
};
//----------Компоновка--------------------------------------
Cl_Led Led(/*пин*/13);
//--------main()----------------------------------------
void setup() {
  Led.setup();
}

void loop() {
  uint8_t oldSREG = SREG;
  cli();
  mill = timer0_millis;
  SREG = oldSREG;
  Led.loop();
}
/*Скетч использует 944 байт (2%) памяти устройства. Всего доступно 32256 байт.
  Глобальные переменные используют 19 байт (0%) динамической памяти, оставляя 2029 байт для локальных переменных. Максимум: 2048 байт.
*/

 

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

Есть. Сохранять регистр нужно в прерываниях. А так быстрее 

noInterrupts();
long myCounter = isrCounter; // получаем значение, выданное ISR
interrupts()
qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

/*
*/
unsigned long mill;
extern unsigned long timer0_millis;
//------------------------------------------------
class Cl_Led {
    const byte _pin;
    bool led;
    unsigned long past = 0;
  public:
    Cl_Led(byte pin): _pin(pin) {}
    void setup() {
      pinMode(_pin, OUTPUT);
      digitalWrite(_pin, led = 0);
    }
    void loop() {
      if (mill - past >= 1000) {
        past = mill;
        digitalWrite(_pin, led = !led);
      }
    }
};
//----------Компоновка--------------------------------------
Cl_Led Led(/*пин*/13);
//--------main()----------------------------------------
void setup() {
  Led.setup();
}

void loop() {
  noInterrupts();
  mill = timer0_millis;
  interrupts();
  Led.loop();
}
/*Скетч использует 942 байт (2%) памяти устройства. Всего доступно 32256 байт.
  Глобальные переменные используют 19 байт (0%) динамической памяти, оставляя 2029 байт для локальных переменных. Максимум: 2048 байт.
*/
Клапауций 112
Клапауций 112 аватар
Offline
Зарегистрирован: 01.03.2017

ЕвгенийП пишет:

Я бы не стал делать currentMillis  static - зачем? По мне, вот так лучше:

void loop() {
  static uint32_t periodStart = 0;
  const  uint32_t  currentMillis = millis();
  if (currentMillis - periodStart >= 1000) {
    periodStart = currentMillis;
  }
}

а, const зачем?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Клапауций 112 пишет:

а, const зачем?

Есть такое правило хорошего тона: "если не собираешься изменять переменную (а я как раз не собираюсь), скажи об этом компилятору". Часто это не влияет ни на что, но иногда облегчает ему работу по оптимизации. Поэтому привычка объявлять неизменяемые переменные константами хорошая. Хуже не будет, а лучше может быть.

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

ЕвгенийП пишет:

Клапауций 112 пишет:

а, const зачем?

Есть такое правило хорошего тона: "если не собираешься изменять переменную (а я как раз не собираюсь), скажи об этом компилятору". Часто это не влияет ни на что, но иногда облегчает ему работу по оптимизации. Поэтому привычка объявлять неизменяемые переменные константами хорошая. Хуже не будет, а лучше может быть.

ок. 

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

qwone пишет:

Вот только код будет выглядить так .

ПС: В Си сначало надо объявить, а потом использовать. Но никак наоборот.:(

Не будет. Я же написал "параметр" и даже выделил жирным. А ты зачем-то глобальную переменную завёл.

Объявлени loop будет выглядеть примерно так:

void loop(const uint32_t currentMillis) { ... }

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

ну, не знаю - как-то дико и бессмысленно это смотрится в классе.

10   public:
 
12     void setup() {
16     void loop() {

а, если что-то третье нужно сделать, кроме сетап и луп, то что? - вся концепция теряется, возвращаемся к классическому именованию  подпрограмм?

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

Ворота пишет:

Объявлени loop будет выглядеть примерно так:

void loop(const uint32_t currentMillis) { ... }

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

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

Клапауций 112 пишет:

ну, не знаю - как-то дико и бессмысленно это смотрится в классе

а, если что-то третье нужно сделать, кроме сетап и луп, то что? - вся концепция теряется, возвращаемся к классическому именованию  подпрограмм?

Расматривайте что это независимые программы. Вас же не раздражает, что каждый новый скетч это снова и снова setup()  loop()

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

qwone пишет:

Расматривайте что это независимые программы. Вас же не раздражает, что каждый новый скетч это снова и снова setup()  loop()

ок. а, как у тебя будет именоваться третья и более "независимая" программа в классе?

*с двумя понятно - настройки и регулярное исполнение, а внезапно - третья?

ну, и далее - если у тебя есть метод сетап в классе, то почему ты не делаешь так?

12     void setup(byte pin) {
13       pinMode(pin, OUTPUT);
14       digitalWrite(pin, led = 0);
15     }

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

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

Клапауций 112  Ну это очень просто. Скетчи приведеные здесь это просто переходный вариант к этому.#1173

// Скетч расчитан на кнопки с подтяжкой резисторов 10к на землю

class Cl_Sys;// предварительно объявить о создании класса Sys для послед подключения к нему
//----------------Cl_LED------------------------------------------------------------
class Cl_LED {
    byte pin; // нога для подключения светодиода
    bool inv; // 0 светодиод горит при 1/ 1 светодиод горит при 0
    bool stat_ON = 0, led = 0;
    uint32_t time = 500, past = 0;
  public:
    //указатель на следующий компонент
    Cl_LED *pnt_LED;
    //конструктор
    Cl_LED(Cl_Sys *Sys, byte _pin, bool _inv);
    // setup()
    void setup() {
      pinMode(pin, OUTPUT);// подключить светодиод
      led = 0;
      digitalWrite(pin, led ^ inv) ; // погасить светодиод
    }
    // loop()
    void loop() {
      if (stat_ON && millis() - past >= time)OFF();
    }
    // включить светодиод
    void ON() {
      stat_ON = 0;
      led = 1;
      digitalWrite(pin, led ^ inv) ; // зажечь светодиод
    }
    // включить светодиод на время
    void ON( uint32_t _time) {
      time = _time;
      stat_ON = 1;
      past = millis();
      led = 1;
      digitalWrite(pin, led ^ inv) ; // зажечь светодиод
    }
    // выключить светодиод
    void OFF() {
      stat_ON = 0;
      led = 0;
      digitalWrite(pin, led ^ inv) ; // погасить светодиод
    }
};
//----------------Cl_Mech_BTN------------------------------------------------------------
// класс на механич кнопку подкл к кнопке Ардуины.
class Cl_Mech_BTN {
    byte pin; // номер ноги на кнопке
    void (* Do)();// указатель на обработчик
    bool btn, btn_old;
    bool bounce = 0; // антидребезговый флаг
    uint32_t past = 0 ;
  public:
    //указатель на следующий компонент
    Cl_Mech_BTN *pnt_Mech_BTN;
    //конструктор
    Cl_Mech_BTN(Cl_Sys *Sys, byte _pin, void (* _Do)());
    void setup() {
      pinMode(pin, INPUT);// подключить кнопку 
      btn = digitalRead(pin); // прочитать реальное значение на выводе};
    }
    void loop () {
      if (! bounce && btn != digitalRead(pin)) { // если прошел фронт изм на выводн
        bounce = 1;                              // выставить флаг
        past = millis();                         // сделать временую засветку
      }
      else if ( bounce && millis() - past >= 5 ) { // если прошло антидребезговое время
        bounce = 0;                                // то снять флаг
        btn_old = btn ;
        btn = digitalRead(pin) ;                   // прочитать реальное значение на выводе
        if (!btn_old && btn) Do();
      }
    }
};
//----------------Cl_Sys------------------------------------------------------------
class Cl_Sys {
  public:
    // указатели на блоки однотипных классов в системе
    Cl_LED * Start_LED = NULL;
    Cl_Mech_BTN * Start_Mech_BTN = NULL;
    void setup() {
      for (Cl_LED *i = Start_LED; i != NULL; i = i->pnt_LED) i->setup();
      for (Cl_Mech_BTN *i = Start_Mech_BTN; i != NULL; i = i->pnt_Mech_BTN) i->setup();
    }
    void loop () {
      for (Cl_LED *i = Start_LED; i != NULL; i = i->pnt_LED) i->loop ();
      for (Cl_Mech_BTN *i = Start_Mech_BTN; i != NULL; i = i->pnt_Mech_BTN)  i->loop ();
    }
};
//-----------------конструкторы классов вход в систему-------------------------------
Cl_LED::Cl_LED(Cl_Sys *Sys, byte _pin, bool _inv): pin(_pin), inv(_inv) {
  pnt_LED = Sys->Start_LED;
  Sys->Start_LED = this;
}
Cl_Mech_BTN::Cl_Mech_BTN(Cl_Sys *Sys, byte _pin, void (* _Do)()): pin(_pin), Do(_Do) {
  pnt_Mech_BTN = Sys->Start_Mech_BTN;
  Sys->Start_Mech_BTN = this;
}
//------------------компоновка-------------------------------------------------------
Cl_Sys *Sys = new Cl_Sys();
Cl_LED *LED1 = new Cl_LED(Sys,/*пин*/A0,/*инверсия*/0);// создать светодиод LED1
Cl_LED *LED2 = new Cl_LED(Sys,/*пин*/A1,/*инверсия*/0);// создать светодиод LED2
Cl_LED *LED3 = new Cl_LED(Sys,/*пин*/A2,/*инверсия*/0);// создать светодиод LED3
Cl_LED *LED4 = new Cl_LED(Sys,/*пин*/A3,/*инверсия*/0);// создать светодиод LED4
Cl_LED *LED5 = new Cl_LED(Sys,/*пин*/A4,/*инверсия*/0);// создать светодиод LED5
Cl_LED *LED6 = new Cl_LED(Sys,/*пин*/A5,/*инверсия*/0);// создать светодиод LED6
Cl_LED *LED7 = new Cl_LED(Sys,/*пин*/11,/*инверсия*/0);// создать светодиод LED7
Cl_LED *LED8 = new Cl_LED(Sys,/*пин*/12,/*инверсия*/0);// создать светодиод LED8
void Do_BTN1() {
  LED1->ON(2000);
}
void Do_BTN2() {
  LED2->ON(2000);
}
void Do_BTN3() {
  LED3->ON(2000);
}
void Do_BTN4() {
  LED4->ON(2000);
}
void Do_BTN5() {
  LED5->ON(2000);
}
void Do_BTN6() {
  LED6->ON(2000);
}
void Do_BTN7() {
  LED7->ON(2000);
}
void Do_BTN8() {
  LED8->ON(2000);
}
Cl_Mech_BTN *BTN1 = new Cl_Mech_BTN(Sys,/*пин*/2,/*обработчик*/Do_BTN1);// создать кнопку BTN1
Cl_Mech_BTN *BTN2 = new Cl_Mech_BTN(Sys,/*пин*/3,/*обработчик*/Do_BTN2);// создать кнопку BTN2
Cl_Mech_BTN *BTN3 = new Cl_Mech_BTN(Sys,/*пин*/4,/*обработчик*/Do_BTN3);// создать кнопку BTN3
Cl_Mech_BTN *BTN4 = new Cl_Mech_BTN(Sys,/*пин*/5,/*обработчик*/Do_BTN4);// создать кнопку BTN4
Cl_Mech_BTN *BTN5 = new Cl_Mech_BTN(Sys,/*пин*/6,/*обработчик*/Do_BTN5);// создать кнопку BTN5
Cl_Mech_BTN *BTN6 = new Cl_Mech_BTN(Sys,/*пин*/7,/*обработчик*/Do_BTN6);// создать кнопку BTN6
Cl_Mech_BTN *BTN7 = new Cl_Mech_BTN(Sys,/*пин*/8,/*обработчик*/Do_BTN7);// создать кнопку BTN7
Cl_Mech_BTN *BTN8 = new Cl_Mech_BTN(Sys,/*пин*/9,/*обработчик*/Do_BTN8);// создать кнопку BTN8
//------------------ main()----------------------------------------------------------
void setup() {
  Sys->setup();
}

void loop() {
  Sys->loop();
}

То есть к пакетной обработке. Разумеется я не нашел пока лучше этой конструкции. Но я придерживаюсь правила , что методы в классе должны быть void setup(void) , void loop(void) , хотя и их можно перегружать, а вот установочные данные надо вводить конструктором И да, я не навязываю стандарты, просто я думаю, что так проще перейти к ООП.

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

qwone пишет:

Клапауций 112  Ну это очень просто. Скетчи приведеные здесь это просто переходный вариант к этому.#1173

а, зачем? - я же уже написал велосипед для кнопок.

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

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

Все та же проблема, различные ключевые моменты в программировании приводят некоторой несовместимости скетчей. Так для вас событие это ввзведеный бит, а у меня повод вызвать обработчик этого события. Или же запихнуть его в очередь. И тот же обработчик это void Do(void){/*код обработки*/}.

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

qwone пишет:

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

это ты сейчас о каких несовместимостях скетчей?

если о велосипеде, то все версии велосипедов 100% совместимы с версиями скетчей, написанными для этих версий. :D

qwone пишет:

Так для вас событие это ввзведеный бит, а у меня повод вызвать обработчик этого события. Или же запихнуть его в очередь. И тот же обработчик это void Do(void){/*код обработки*/}.

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

про очередь - непонятное и сколько тебе понадобится оперативки, что бы хранить очередь длинной полчаса?

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016
/*Скетч Аналоговая клавиатура
*/
unsigned long mill;
//----------Cl_Key---------
// класс аналоговая клавиатура
class Cl_Key {
    byte pin;// нога
    void (*Do1)(), (*Do2)(), (*Do3)(), (*Do4)(), (*Do5)(); //обработчики
    int val_old, val; // состояние на клавиатуре
    unsigned long past;
    int read() { // считать состояние клавиатуры
      int value = analogRead(pin);
      if (value < 130) return 1;
      else if (value < 310) return 2;
      else if (value < 500) return 3;
      else if (value < 730) return 4;
      else if (value < 900) return 5;
      return 6;
    }
  public:
    Cl_Key(byte _pin, void (*_Do1)(), void (*_Do2)(), void (*_Do3)(), void (*_Do4)(), void (*_Do5)())
      : pin(_pin), Do1(_Do1), Do2(_Do2) , Do3(_Do3) , Do4(_Do4) , Do5(_Do5)  {}
    void setup() {
      val_old = read();
    }
    void loop() {
      if (mill - past >= 100) {
        past = mill;
        val_old = val;
        val = read();
        if (val_old == 6) {
          if (val == 1) Do1();
          else if (val == 2) Do2();
          else if (val == 3) Do3();
          else if (val == 4) Do4();
          else if (val == 5) Do5();
        }
      }
    }
};
//--------Компоновка-----------
void Do1() {
  Serial.println("Do1");
}
void Do2() {
  Serial.println("Do2");
}
void Do3() {
  Serial.println("Do3");
}
void Do4() {
  Serial.println("Do4");
}
void Do5() {
  Serial.println("Do5");
}
Cl_Key Key(/*нога*/A0,/*обработчик1*/Do1,/*обработчик1*/Do2,/*обработчик1*/Do3,/*обработчик1*/Do4,/*обработчик1*/Do5);
//----------main()---------
void setup() {
  Serial.begin(9600);
  Key.setup();
}
void loop() {
  mill = millis();
  Key.loop();
}
/*Скетч использует 1920 байт (5%) памяти устройства. Всего доступно 32256 байт.
  Глобальные переменные используют 229 байт (11%) динамической памяти, оставляя 1819 байт для локальных переменных. Максимум: 2048 байт.

*/

Внешняя аналаговая клавиатура из 5 кнопок.

infyniti
Offline
Зарегистрирован: 15.07.2017

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

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

infyniti пишет:

Битва титанов......Титаны не засоряйте эфир.Создавайте свои темы и продвигайте там свои методы.

А это и есть тема одного из них. Так что они создали и продвигают, в точном соответствии с Вашими ценными указаниями.

А что нужно 

infyniti пишет:

Человеку не понимающему в программировании 

в этой теме всем 

infyniti пишет:

побарабану

 

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

infyniti пишет:

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

блин. я так и думал - придёт какой-нибудь карлик и закроет тему.
qwone, закрывай контору - уходим прадавать каму нибуть буквари великага рускага языка.
ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

infyniti пишет:

Вот так же и вы обсуждаете между собой а новичку не понятно

Мля, люди открыли тему и обсуждают что-то между собой. Тут в их тему вваливается новичёк, которому что-то там не понятно и начинает учить людей о чём им можно говорить, а о чём нельзя в их собственных темах! Вы ... это ... вообще здоровы? Вас так по жизни часто нах посылают или не очень? :))))

infyniti
Offline
Зарегистрирован: 15.07.2017

ЕвгенийП пишет:

Мля, люди открыли тему и обсуждают что-то между собой. Тут в их тему вваливается новичёк, которому что-то там не понятно и начинает учить людей о чём им можно говорить, а о чём нельзя в их собственных темах! Вы ... это ... вообще здоровы? Вас так по жизни часто нах посылают или не очень? :))))

Извиняюсь но тема называется. 

Классы Ардуино по qwone для чайников.

И ни кого я не учил.Хотите обсуждайте свои три строчки.Только кому это интерестно.

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

infyniti пишет:

Только кому это интерестно.

мне.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

infyniti пишет:

Только кому это интерестно.

Всем, кроме Вас.

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

Вот есть программа.Обычная программа светодиод с кнопкой.

/**/
uint32_t mill;// переменная под millis()
//------Cl_Led------------------------------------------
// класс светодиод
class Cl_Led {
    const byte pin;
    const bool inv;
    bool led;
  public:
    Cl_Led(byte _pin, bool _inv)
      : pin(_pin), inv(_inv) {}
    void setup() {
      pinMode(pin, OUTPUT);
      OFF();
    }
    void ON() {
      led = 1;
      digitalWrite(pin, led ^ inv);
    }
    void OFF() {
      led = 0;
      digitalWrite(pin, led ^ inv);
    }
};
//------Cl_Btn------------------------------------------
// класс кнопка
class Cl_Btn {
    const byte pin;
    void (*Do1)(), (*Do2)();
    bool btn, btn_old;
    bool bounce = 0; // антидребезговый флаг
    uint32_t past = 0 ;
  public:
    Cl_Btn(byte _pin, void (*_Do1)(), void (*_Do2)())
      : pin(_pin), Do1(_Do1), Do2(_Do2) {}
    void setup() {
      pinMode(pin, INPUT_PULLUP);
      btn_old = digitalRead(pin);
    }
    void loop() {
      if (! bounce && btn != digitalRead(pin)) { // если прошел фронт изм на выводн
        bounce = 1;                              // выставить флаг
        past = mill;                         // сделать временую засветку
      }
      else if ( bounce && mill - past >= 5 ) { // если прошло антидребезговое время
        bounce = 0;                                // то снять флаг
        btn_old = btn ;
        btn = digitalRead(pin) ;                   // прочитать реальное значение на выводе
        if (btn_old && ! btn) Do1();
        if (!btn_old && btn) Do2();
      }
    }
};
//-------------компоновка-----------------------------------
Cl_Led Led(/*пин*/13,/*инв*/0);
void Do1_Btn() {
  Led.ON();
}
void Do2_Btn() {
  Led.OFF();
}
Cl_Btn Btn(/*пин*/2,/*обраб наж*/Do1_Btn,/*обраб отж*/Do2_Btn);
//-------------main()-----------------------------------
void setup() {
  Led.setup();
  Btn.setup();
}

void loop() {
  mill = millis();
  Btn.loop();
}
/*Скетч использует 1330 байт (4%) памяти устройства. Всего доступно 32256 байт.
  Глобальные переменные используют 28 байт (1%) динамической памяти, оставляя 2020 байт для локальных переменных. Максимум: 2048 байт.
*/

А теперь надо написать класс светодиод+кнопка, который объединит класс светодиод и класс кнопку

/**/
uint32_t mill;// переменная под millis()
//------Cl_Led------------------------------------------
// класс светодиод
class Cl_Led {
    const byte pin;
    const bool inv;
    bool led;
  public:
    Cl_Led(byte _pin, bool _inv)
      : pin(_pin), inv(_inv) {}
    void setup() {
      pinMode(pin, OUTPUT);
      OFF();
    }
    void ON() {
      led = 1;
      digitalWrite(pin, led ^ inv);
    }
    void OFF() {
      led = 0;
      digitalWrite(pin, led ^ inv);
    }
};
//------Cl_Btn------------------------------------------
// класс кнопка
class Cl_Btn {
    const byte pin;
    void (*Do1)(), (*Do2)();
    bool btn, btn_old;
    bool bounce = 0; // антидребезговый флаг
    uint32_t past = 0 ;
  public:
    Cl_Btn(byte _pin, void (*_Do1)(), void (*_Do2)())
      : pin(_pin), Do1(_Do1), Do2(_Do2) {}
    void setup() {
      pinMode(pin, INPUT_PULLUP);
      btn_old = digitalRead(pin);
    }
    void loop() {
      if (! bounce && btn != digitalRead(pin)) { // если прошел фронт изм на выводн
        bounce = 1;                              // выставить флаг
        past = mill;                         // сделать временую засветку
      }
      else if ( bounce && mill - past >= 5 ) { // если прошло антидребезговое время
        bounce = 0;                                // то снять флаг
        btn_old = btn ;
        btn = digitalRead(pin) ;                   // прочитать реальное значение на выводе
        if (btn_old && ! btn) Do1();
        if (!btn_old && btn) Do2();
      }
    }
};
//---------Cl_Led_Btn--------------------------------------------------
// класс кнопка со светодиодом
class Cl_Led_Btn {
    Cl_Led Led;
    Cl_Btn Btn;
  public:
    Cl_Led_Btn (byte _pin1, bool _inv, byte _pin2, void (*_Do1)(), void (*_Do2)())
      : Led(_pin1, _inv), Btn(_pin2, _Do1, _Do2) {}
    void setup() {
      Led.setup();
      Btn.setup();
    }
    void loop() {
      Btn.loop();
    }
    void ON() {
      Led.ON();
    }
    void OFF() {
      Led.OFF();
    }
};
//-------------компоновка-----------------------------------
void Do1_Led_Btn();
void Do2_Led_Btn();
Cl_Led_Btn Led_Btn(/*пин светодиода*/13,/*инв*/0,/*пин кнопки*/2,/*обраб наж*/Do1_Led_Btn,/*обраб отж*/Do2_Led_Btn);
void Do1_Led_Btn() {
  Led_Btn.ON();
}
void Do2_Led_Btn() {
  Led_Btn.OFF();
}
//-------------main()-----------------------------------
void setup() {
  Led_Btn.setup();
}

void loop() {
  mill = millis();
  Led_Btn.loop();
}
/*Скетч использует 1324 байт (4%) памяти устройства. Всего доступно 32256 байт.
  Глобальные переменные используют 28 байт (1%) динамической памяти, оставляя 2020 байт для локальных переменных. Максимум: 2048 байт.
*/

 Диковатая конструкция, хотя и работает. Я пытался объединить их внутри, но пока ничего не выходит. Разберусь выложу.

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

Теперь подошло время указателей на методы класса. Возьмем простой скетч светодиод с кнопкой.

/**/
uint32_t mill;// переменная под millis()
//------Cl_Led------------------------------------------
// класс светодиод
class Cl_Led {
    const byte pin;
    const bool inv;
    bool led;
  public:
    Cl_Led(byte _pin, bool _inv)
      : pin(_pin), inv(_inv) {}
    void setup() {
      pinMode(pin, OUTPUT);
      OFF();
    }
    void ON() {
      led = 1;
      digitalWrite(pin, led ^ inv);
    }
    void OFF() {
      led = 0;
      digitalWrite(pin, led ^ inv);
    }
};
//------Cl_Btn------------------------------------------
// класс кнопка
class Cl_Btn {
    const byte pin;
    void (*Do1)(), (*Do2)();
    bool btn, btn_old;
    bool bounce = 0; // антидребезговый флаг
    uint32_t past = 0 ;
  public:
    Cl_Btn(byte _pin, void (*_Do1)(), void (*_Do2)())
      : pin(_pin), Do1(_Do1), Do2(_Do2) {}
    void setup() {
      pinMode(pin, INPUT_PULLUP);
      btn_old = digitalRead(pin);
    }
    void loop() {
      if (! bounce && btn != digitalRead(pin)) { // если прошел фронт изм на выводн
        bounce = 1;                              // выставить флаг
        past = mill;                         // сделать временую засветку
      }
      else if ( bounce && mill - past >= 5 ) { // если прошло антидребезговое время
        bounce = 0;                                // то снять флаг
        btn_old = btn ;
        btn = digitalRead(pin) ;                   // прочитать реальное значение на выводе
        if (btn_old && ! btn)(*Do1)();
        if (!btn_old && btn) (*Do2)();
      }
    }
};
//-------------компоновка-----------------------------------
Cl_Led Led(/*пин*/13,/*инв*/0);
void Do1_Btn() {  // <-- попробуем убрать это
  Led.ON();
}
void Do2_Btn() {  // <-- попробуем убрать и это
  Led.OFF();
}
Cl_Btn Btn(/*пин*/2,/*обраб наж*/Do1_Btn,/*обраб отж*/Do2_Btn);
//-------------main()-----------------------------------
void setup() {
  Led.setup();
  Btn.setup();
}

void loop() {
  mill = millis();
  Btn.loop();
}
/*Скетч использует 1328 байт (4%) памяти устройства. Всего доступно 32256 байт.
  Глобальные переменные используют 28 байт (1%) динамической памяти, оставляя 2020 байт для локальных переменных. Максимум: 2048 байт.
*/

И вот оно то самое "счастье"

/**/
uint32_t mill;// переменная под millis()
//------Cl_Led------------------------------------------
// класс светодиод
class Cl_Led {
    const byte pin;
    const bool inv;
    bool led;
  public:
    Cl_Led(byte _pin, bool _inv)
      : pin(_pin), inv(_inv) {}
    void setup() {
      pinMode(pin, OUTPUT);
      OFF();
    }
    void ON() {
      led = 1;
      digitalWrite(pin, led ^ inv);
    }
    void OFF() {
      led = 0;
      digitalWrite(pin, led ^ inv);
    }
};
//------Cl_Btn------------------------------------------
// класс кнопка
class Cl_Btn {
    const byte pin;
    Cl_Led *LED;  //<--- появляется это
    void (Cl_Led::*Do1)(), (Cl_Led::*Do2)();  //<--- изменяется это
    bool btn, btn_old;
    bool bounce = 0; // антидребезговый флаг
    uint32_t past = 0 ;
  public:
    Cl_Btn(byte _pin, Cl_Led *_LED, void (Cl_Led::*_Do1)(), void (Cl_Led::*_Do2)()) //<--- изменяется это
      : pin(_pin), LED(_LED), Do1(_Do1), Do2(_Do2) {}
    void setup() {
      pinMode(pin, INPUT_PULLUP);
      btn_old = digitalRead(pin);
    }
    void loop() {
      if (! bounce && btn != digitalRead(pin)) { // если прошел фронт изм на выводн
        bounce = 1;                              // выставить флаг
        past = mill;                         // сделать временую засветку
      }
      else if ( bounce && mill - past >= 5 ) { // если прошло антидребезговое время
        bounce = 0;                                // то снять флаг
        btn_old = btn ;
        btn = digitalRead(pin) ;                   // прочитать реальное значение на выводе
        if (btn_old && ! btn)(LED->*Do1)(); //<--- изменяется это
        if (!btn_old && btn) (LED->*Do2)(); //<--- изменяется это
      }
    }
};
//-------------компоновка-----------------------------------
Cl_Led Led(/*пин*/13,/*инв*/0);
Cl_Btn Btn(/*пин*/2,/*представитель класса*/&Led,/*обраб наж*/&Led.ON,/*обраб отж*/&Led.OFF);//<--- изменяется это
//-------------main()-----------------------------------
void setup() {
  Led.setup();
  Btn.setup();
}

void loop() {
  mill = millis();
  Btn.loop();
}
/*Скетч использует 1430 байт (4%) памяти устройства. Всего доступно 32256 байт.
  Глобальные переменные используют 34 байт (1%) динамической памяти, оставляя 2014 байт для локальных переменных. Максимум: 2048 байт.
*/

ПС: И осталось ответить на "извечный еврейский вопрос": И оно нам надо?. 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

А почему у Вас исполнители - всегда внешние функции (типа Do1_Btn) а не виртуальные методы?

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

ЕвгенийП пишет:

А почему у Вас исполнители - всегда внешние функции (типа Do1_Btn) а не виртуальные методы?

Зачем мне виртуальные методы. Я пока наследование не сильно применяю.

По "моей терминологии", но я не навязываю ни кому, классы с внешними обработчиками void Do(void) это классы Do (классы Ду). Есть еще классы Is (классы Из) но я редко их применяю. Это класс потенциометр. Там указатель на внешнюю переменную.

ПС: Да если кому-то хочется почитать про указатели вот хорошая статья.http://rsdn.org/article/cpp/fastdelegate.xml

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

qwone пишет:

Зачем мне виртуальные методы. Я пока наследование не сильно применяю.

Так вопрос-то был как раз "почему Вы выбрали такое решение"? Обычно (в том же MFC и ещё в миллионе библиотек классов) обработчики - это виртуальные методы: там, типа onClick, onButtonUp, onMouseOver и т.д. и т.п.

Вы жу почему-то применяете внешние функции. Из каких соображений?

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

ЕвгенийП пишет:
Вы жу почему-то применяете внешние функции. Из каких соображений?
какие умею, такие и применяю. Если научите другие, и они окажутся лучше поменяю.

ЕвгенийП пишет:
Обычно (в том же MFC и ещё в миллионе библиотек классов) обработчики - это виртуальные методы: там, типа onClick, onButtonUp, onMouseOver и т.д. и т.п.
А вот то что вы перечислили это отдельный класс обработчиков и то под GUI. А у меня нет GUI, вот и с этими обработчиками не задалось.

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

qwone пишет:

ЕвгенийП пишет:
Вы жу почему-то применяете внешние функции. Из каких соображений?
какие умею, такие и применяю. Если научите другие, и они окажутся лучше поменяю.

ну, так впиши в тело класса свои функции и вызывай их в контексте экземпляра класса.

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

Клапауций 112 пишет:
ну, так впиши в тело класса свои функции и вызывай их в контексте экземпляра класса
Вот здесь все не очень. Вот класс для меня это как "черный ящик", а точнее "серый ящик". В черный ящик лезть не надо. А вот в серый можно, но когда очень хочется . Для отладки программы лучше менять в разделе Компановка и main . А другие разделы это как подключаемые файлы. Но на форум лучше выкладывать одним файлом.  Вот и внешний обработчик он создается в разделе Компоновка. Хотя если буду собирать большой класс из мелких улетит на верх.

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

qwone пишет:

Вот здесь все не очень. Вот класс для меня это как "черный ящик", а точнее "серый ящик". В черный ящик лезть не надо. 

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

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

ЕвгенийП пишет:
Так вопрос-то был как раз "почему Вы выбрали такое решение"? Обычно (в том же MFC и ещё в миллионе библиотек классов) обработчики - это виртуальные методы: там, типа onClick, onButtonUp, onMouseOver и т.д. и т.п.
Вы имели ввиду это?

/**/
//-------Cl_Btn----------------------
// класс кнопка
class Cl_Btn {
    byte pin; // номер ноги на кнопке

    bool btn, btn_old;
    bool bounce = 0; // антидребезговый флаг
    uint32_t past = 0 ;
  public:
    void (*onClick)(); // указатель на обработчик
    Cl_Btn( byte _pin, void (* _onClick)() = NULL): pin(_pin), onClick(_onClick) {}
    void setup() {
      pinMode(pin, INPUT_PULLUP);// подключить кнопку 1 с подтяжкой
      btn = digitalRead(pin); // прочитать реальное значение на выводе};
    }
    void loop() {
      if (! bounce && btn != digitalRead(pin)) { // если прошел фронт изм на выводн
        bounce = 1;                              // выставить флаг
        past = millis();                         // сделать временую засветку
      }
      else if ( bounce && millis() - past >= 5 ) { // если прошло антидребезговое время
        bounce = 0;                                // то снять флаг
        btn_old = btn ;
        btn = digitalRead(pin) ;                   // прочитать реальное значение на выводе
        if (btn_old && ! btn) (*onClick)();
      }
    }
};
//---------компоновка--------------------
Cl_Btn Btn(/*пин*/2);
void func() {
  Serial.println("func()");
}
//-----------main()------------------
void setup() {
  Serial.begin(9600);
  Btn.setup();
  Btn.onClick = &func;
}
void loop() {
  Btn.loop();
}
/*Скетч использует 1986 байт (6%) памяти устройства. Всего доступно 32256 байт.
  Глобальные переменные используют 202 байт (9%) динамической памяти, оставляя 1846 байт для локальных переменных. Максимум: 2048 байт.
*/

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

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

Скетч с использованием шаблонов на классы и структуры.

/**/
//-------Cl_AAA----------------------------
template<typename T> struct str_AAA {
  int data;
  struct str_AAA *next;
};
template<typename T> class Cl_AAA {
    struct str_AAA<T> *start = NULL;
  public:
    void push(T data) {
      struct str_AAA<T> *New = new struct str_AAA<T>;
      New->next = start;
      New->data = data;
      start = New;
    }
    void viev() {
      for (struct str_AAA<T> *ii = start; ii != NULL; ii = ii->next)
        Serial.println(ii->data);
    }
};
//-----------------------------------
Cl_AAA<byte> AAA;
//-----------------------------------
void setup() {
  Serial.begin(9600);
  AAA.push(12);
  AAA.push(10);
  AAA.push(8);
  AAA.viev();
}

void loop() {
}
/*Скетч использует 2410 байт (7%) памяти устройства. Всего доступно 32256 байт.
  Глобальные переменные используют 198 байт (9%) динамической памяти, оставляя 1850 байт для локальных переменных. Максимум: 2048 байт.
*/
/**/
//-------Cl_AAA----------------------------
template<typename T> struct str_AAA {
  int data;
  struct str_AAA *next;
};
template<typename T> class Cl_AAA {
    struct str_AAA<T> *start = NULL;
    struct str_AAA<T> *end = NULL;
  public:
    void pushToEnd(T data) {
      struct str_AAA<T> *New = new struct str_AAA<T>;
      if (start == NULL) start = New;
      end->next = New ;
      New->next = NULL;
      New->data = data;
      end = New;
    }
    void pushToStart(T data) {
      struct str_AAA<T> *New = new struct str_AAA<T>;
      if (end == NULL) end = New;
      New->next = start;
      New->data = data;
      start = New;
    }
    void viev() {
      for (struct str_AAA<T> *ii = start; ii != NULL; ii = ii->next)
        Serial.println(ii->data);
    }
};
//-----------------------------------
Cl_AAA<byte> AAA;
//-----------------------------------
void setup() {
  Serial.begin(9600);
  AAA.pushToEnd(12);
  AAA.pushToEnd(10);
  AAA.pushToEnd(8);
  AAA.viev();
}

void loop() {
}
/*Скетч использует 2426 байт (7%) памяти устройства. Всего доступно 32256 байт.
  Глобальные переменные используют 200 байт (9%) динамической памяти, оставляя 1848 байт для локальных переменных. Максимум: 2048 байт.
*/

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

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

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

АААА! Началось! New показался!!!! Память у МК маленькая. Кто контролировать расход будет?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

nik182 пишет:

АААА! Началось! New показался!!!! Память у МК маленькая. Кто контролировать расход будет?

new-фобия напоминает мне типичную жертву американской рекламы, которую можно видеть в любом фастфуде в Америке - человек берёт диетическую колу (чтобы не полнеть) и к ней здоровенный бургер. А чо, про колу в рекламе говорят, а про бургеры - нет.

Так и здесь. Народ предаёт страшной анафеме new, но при этом спокойно пользуется классом String :)))))

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

ЕвгенийП пишет:

.......

Так и здесь. Народ предаёт страшной анафеме new, но при этом спокойно пользуется классом String :)))))

До первого глюка с переполнением памяти.

Я не против NEW. Я против классов в микроконтроллере. Для стабильной работы вся память должна быть распределена до рантайма. 

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

nik182 пишет:

до рантайма. 

это как?

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

nik182 пишет:

Я против классов в микроконтроллере.

код для 200 кнопок будешь копипастом писать?

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

200 кнопок для контроллера? Больше шести не разу не пришлось окучивать. А писать блиньк через классы - только для тренировки навыка. В реальном проекте как то всё пишется прямо. 

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

nik182 пишет:
Я не против NEW. Я против классов в микроконтроллере. Для стабильной работы вся память должна быть распределена до рантайма.
Как там : " И маленькую собачонку. Однако За время пути. Собака Могла подрасти!" . Так что микроконтроллеры уже немного подрасли. А вот программисты на микроконтроллерах этого не заметили. Да что говорить , они даже провтыкали как Си перерос в С++ , да С++ не стоит на месте . Сейчас С++11 и дальше развивается. Опять же практически все библиотеки пишутся с помощью классов. Но при вроде распростаненности классов, народ не умеет писать программы при помощи классов.

ПС: Для стабильной работы надо уметь писать программы. Так что эта тема скорее всего для меня, ну и тем кому интересно это направление.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

qwone пишет:

Сейчас С++11 и дальше развивается.

Вы хотели сказать "14"? А то уж скоро стандарт 17 выйдет.

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

nik182 пишет:

200 кнопок для контроллера?

нет - для суперкомпьютера