Шаговый двигатель 28BYJ-48. Прошу помощи в доработке класса

Да нет, когда поймёт пусть private применяет. А по умолчанию всегда protected.

Захочет он лет через 5 потомков от своего сегодняшнего класса породить и «хапнет». Уж лучше сразу такое вызубрить.

и он прав.
Это банально не будет работать на многих контроллерах

я бы его назвал init()

Что касается пинов - вам зачем надо, чтобы они были недоступны? Чтобы юзер их не изменил? - ну так сделайте их константами

Сейчас попробую.

Мельком глянул исходник.
Споткнулся на строках 29-56.

if(arrow){
   uk = (uk - 1) & 3;
} else {
   uk = (uk + 1) & 3;
}

а если пожертвовать наглядностью:

uk = (uk + 1 - 2*arrow) & 3;
1 лайк

protected не использую, пока не хапнул. А если хапну - кто помешает мне свой класс подправить? ))

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

в момент создания экземпляра - сможет.
А то что после их уже не поменять - так на то они и константы.

Спасибо за намек. Но я пока с этим не разобрался, поэтому для начала остановился на этом:

      if (arrow==true) uk=(uk+3) % 4;
      else uk=(uk+1) % 4;

Но вот так тоже работает

      uk = (uk + 2 * arrow + 1) % 4;

Изменить константу? Хм…

не изменить, а инициализировать:

class MOTORS
{
  private:
   const int pin11; 
   const int pin22; 
   const int pin33; 
   const int pin44; 
  public:
    MOTORS(const int pin1, const int pin2, const int pin3, const int pin4): 
     pin11(pin1), pin22(pin2), pin33(pin3), pin44(pin4)
    {
     
    }
  void init() {
    pinMode(pin11, OUTPUT);
    pinMode(pin22, OUTPUT);
    pinMode(pin33, OUTPUT);
    pinMode(pin44, OUTPUT);
  }
};    

MOTORS m(2,3,4,5);

void setup() {
   m.init();
}

void loop() { }
2 лайка

Разобрался с Вашим #24. Красиво! Два младших бита в uk показвают отстаток от деления на 4. Он то и нужен. Но перед этим смещаете uk и глушите биты 2-7.

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

Да я ж не против.
Просто я стараюсь читать, что пишет Евгений Петрович, а ты видимо нет.
Ну или еще какая причина, не знаю. На гитхабе в серьезных проектах private практически не встретить…

Чой-та нет? Нигде Петрович о вреде private не писал :wink:

Ну как это не писал? Писал…
Я вот найти только не могу, может быть он сам сегодня заглянет и прояснит (вдруг я чего-то не понял? Нельзя исключать…).

Писал о пользе protected, а не о вреде private ))

ЗЫ: если ты точно уверен, что никогда не будешь наследовать этот класс, или наследники обойдутся без приватных методов/полей, то какой смысл?

Вот, я никогда в этом не уверен )))

ЗЫ: Ничего о вреде private я тоже не писал.

А я вот уверен, что бывают случаи, когда приватные поля/методы нужно скрывать даже от потомков класса ))

Ну, это я удачно паузу взял. Вам практически всё уже написали.

(все номера строк по самому первому скетчу)

  1. Функция, имя которой совпадает с именем класса является конструктором независимо ни от чего другого, потому, да, это конструктор;
  2. @BOOM написал Вам про private, я тоже считаю его здесь чрезмерным. С задачей недоступности из программы вполне и protected справится. А использование private сужает возможности дальнейшего использования класса, а именно – запрещает классам-наследникам доступ к этим переменным;
  3. @andriano написал про то, что инициализацию железа лучше делать не в конструкторе, а в методе, который вызывается вручную. Согласен. В данном случае никаких проблем нет, но если уж делать так, как делаете Вы, то надо чётко понимать что Вы делаете и где лежат грабли. Надеюсь, Вы понимаете;
  4. @MMM Вам совершенно правильно сказал про константы. Вообще, есть общее правило, “если Вы не собираетесь менять какую-то переменную (или метод не собирается менять свойства класса), не скрывайте этого от компилятора, пусть знает”. Про эти дела я позже отдельно скажу;

Клевета! :slight_smile:

То нет особого смысла делать его классом.

Ну, теперь, что я ещё заметил по мелочи (“крупняк” пока оставим):

  1. в строке №20 moment2 (а в строке №29 – uk, а в строке №80 – arrow2) используются до инициализации. Нет, я понимаю, что Вы используете то, что изначально они нули, но это некрасивая практика – коробит аж. Неужели трудно поставить инициализацию?
  2. За имена переменных типа pin1, pin2, pin3 и т.п. надо бить по рукам. У этих пинов есть вполне осмысленные функции и переменные заслуживают осмысленных названий;
  3. Ваш метод mkstep (как и появившейся позже begin) не изменяет никаких свойств класса. И не собирается изменять! Тогда почему он (метод) не объявлен const? Просто, в силу упомянутого выше правила?
  4. Всё, что там связано с uk и выводом на пины, нуждается в серьёзно сутевом комментарии.

Теперь про “крупняк”. (я согласен со всем, что написали коллеги, но здесь буду использовать Ваш изначальный код без правок, которые они предложили. Эти правки Вы сами внесёте).

Про inline классы и константные свойства

Если в классе все методы inline, как у Вас, то нет никакого смысла иметь константные свойства – они только память отжирают. Все константные свойства можно и нужно вынести в параметры шаблона. Смотрите:

Ваша программа:
class MOTORS
{
  private:
    int pin11, pin22, pin33, pin44;
  public:
    MOTORS(int pin1, int pin2, int pin3, int pin4)
    {
      pinMode(pin1, OUTPUT);
      pinMode(pin2, OUTPUT);
      pinMode(pin3, OUTPUT);
      pinMode(pin4, OUTPUT);
      pin11 = pin1;
      pin22 = pin2;
      pin33 = pin3;
      pin44 = pin4;
    }
    void mkstep(bool arrow)
    {
      static unsigned long moment2;
      while (millis() - moment2 < 2)
      {
      }
      moment2 = millis();
      static byte array1[] = {1, 0, 0, 1};
      static byte array2[] = {1, 1, 0, 0};
      static byte array3[] = {0, 1, 1, 0};
      static byte array4[] = {0, 0, 1, 1};
      static byte uk;
      digitalWrite(pin11, array1[uk]);
      digitalWrite(pin22, array2[uk]);
      digitalWrite(pin33, array3[uk]);
      digitalWrite(pin44, array4[uk]);
      if (arrow == true)
      {
        switch (uk)
        {
          case 0: uk = 3;
            break;
          case 1: uk = 0;
            break;
          case 2: uk = 1;
            break;
          case 3: uk = 2;
            break;
        }
      }
      else
      {
        switch (uk)
        {
          case 0: uk = 1;
            break;
          case 1: uk = 2;
            break;
          case 2: uk = 3;
            break;
          case 3: uk = 0;
            break;
        }
      }
    }
};

// подключение шагового двигателя
#define IN1 2
#define IN2 3
#define IN3 4
#define IN4 5

MOTORS Stmt(IN1, IN2, IN3, IN4);

byte arrow2;

void setup()
{

}
void loop()
{
  Stmt.mkstep(arrow2);
}

У меня показывает память программы/данных: 1092/38

А вот

То же самое, но без свойств, через параметры шаблона
template <const uint8_t pin1, const uint8_t pin2, const uint8_t pin3, const uint8_t pin4>
class MOTORS
{
  public:
    MOTORS(void)
    {
      pinMode(pin1, OUTPUT);
      pinMode(pin2, OUTPUT);
      pinMode(pin3, OUTPUT);
      pinMode(pin4, OUTPUT);
    }
    void mkstep(bool arrow)
    {
      static unsigned long moment2;
      while (millis() - moment2 < 2)
      {
      }
      moment2 = millis();
      static byte array1[] = {1, 0, 0, 1};
      static byte array2[] = {1, 1, 0, 0};
      static byte array3[] = {0, 1, 1, 0};
      static byte array4[] = {0, 0, 1, 1};
      static byte uk;
      digitalWrite(pin1, array1[uk]);
      digitalWrite(pin2, array2[uk]);
      digitalWrite(pin3, array3[uk]);
      digitalWrite(pin4, array4[uk]);
      if (arrow == true)
      {
        switch (uk)
        {
          case 0: uk = 3;
            break;
          case 1: uk = 0;
            break;
          case 2: uk = 1;
            break;
          case 3: uk = 2;
            break;
        }
      }
      else
      {
        switch (uk)
        {
          case 0: uk = 1;
            break;
          case 1: uk = 2;
            break;
          case 2: uk = 3;
            break;
          case 3: uk = 0;
            break;
        }
      }
    }
};

// подключение шагового двигателя
#define IN1 2
#define IN2 3
#define IN3 4
#define IN4 5

MOTORS<IN1, IN2, IN3, IN4> Stmt;

byte arrow2;

void setup()
{

}
void loop()
{
  Stmt.mkstep(arrow2);
 }

У меня показывает память программы/данных: 1038/30

Как видите, мы сэкономили и то, и другое.

Конечно, это правило (как и любое другое) надо применять с умом.

Про Ваши массивы array1-4

(это уже не про класс, правда)

Зачем они память жрут? Давайте посмотрим на строки №№29-32. В них Вы всегда двум пинам выставляете HIGH и двум пинам – LOW. При этом, в зависимости от uk имеем:

uk pin1 pin2 pin3 pin4
0 1 1 0 0
1 0 1 1 0
2 0 0 1 1
3 1 0 0 1

Легко заметить, что:
pin1 получает значение HIGH при uk == 0 или 3
pin2 – при uk < 2
pin3 – при uk == 1 или 2
pin4 – при uk > 1

Ну, и кто нам мешает так и написать в строках №№29-32

digitalWrite(pin11, uk==0 || uk==3);
digitalWrite(pin22, uk < 2);
digitalWrite(pin33, uk==1 || uk==2);
digitalWrite(pin44, uk > 1);

И массивы не нужны вовсе.

Это при Ваших значениях uk. А если бы Вы сделали их такими, чтобы просто за каждый пин отвечал свой бит в uk, было бы ещё проще.

6 лайков

Вот! Я же говорю - недавно было… :slight_smile:

Я не представляю как это сделать. Поэтому реализовал с битовыми операциями так (здесь я решил перейти на 8-ми тактовый режим).
Переменную uk переименовал в takt.

    void mkstep(bool arrow)
    {
      static unsigned long moment2;
      while (millis() - moment2 < 1)
      {
      }
      moment2 = millis();

      static byte takt;
      takt = (takt + 2 * arrow - 1) & 7;

// такт (takt) 
// 0  1  2  3  4  5  6  7
// 0  0  0  0  0  1  1  1
// 0  0  0  1  1  1  0  0
// 0  1  1  1  0  0  0  0
// 1  1  0  0  0  0  0  1

      digitalWrite(pin11, bitRead(0b00000111, takt));
      digitalWrite(pin22, bitRead(0b00011100, takt));
      digitalWrite(pin33, bitRead(0b01110000, takt));
      digitalWrite(pin44, bitRead(0b11000001, takt));
    }