Объявление глобальной переменной

Twilkeris
Offline
Зарегистрирован: 05.02.2016

Добрый день,  уважаемые форумчане!

У меня есть класс Engine, который содержит только один конструктор с агументами типа int. Есть класс Movement, который содержит конструктор с аргментуами типа Engine. 

В setup() у меня такая запись: 

  Engine left_en(PIN_A_LEFT, PIN_B_LEFT, PIN_C_LEFT, PIN_D_LEFT);
  Engine right_en(PIN_A_RIGHT, PIN_B_RIGHT, PIN_C_RIGHT, PIN_D_RIGHT);
  
  Movement movement(left_en, right_en);

Необходимо переменные left_en, right_en и movement сделать глобальными. Если в самом начале программы делаю объявление(аналогично Java)

Movement movement;

, то комплиятор говорит, что не знает такой тип данных. 

 

Подскажите пожалуйста, каким образом правильно объявить глобальную переменную собственного типа? 

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

Что то мутноват у меня хрустальный шар или я не вижу в этой теме ваш скетч.

 

ПС: я бы написал так. 

/**/
//------------------------
class Cl_Engine {
  protected:
    byte aPin, bPin, cPin, dPin;
  public:
    Cl_Engine(byte _aPin, byte _bPin, byte _cPin, byte _dPin)
      : aPin(_aPin), bPin(_bPin), cPin(_cPin), dPin(_dPin) {}
};
//------------------------
class Cl_Movement {
  protected:
    Cl_Engine *Left, *Right;
  public:
    Cl_Movement(Cl_Engine *_Left, Cl_Engine *_Right)
      : Left(_Left), Right(_Right) {}
};
//---компановка--------------------
Cl_Engine LeftEn(/*aPin*/2,/*bPin*/3,/*cPin*/4,/*dPin*/5);
Cl_Engine RightEn(/*aPin*/6,/*bPin*/7,/*cPin*/8,/*dPin*/9);
Cl_Movement Movement(&LeftEn, &RightEn);
//---main()-------------------

void setup() {


}

void loop() {


}
/*Скетч использует 554 байт (1%) памяти устройства. Всего доступно 32256 байт.
  Глобальные переменные используют 21 байт (1%) динамической памяти, оставляя 2027 байт для локальных переменных. Максимум: 2048 байт.
*/

 

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

Twilkeris пишет:

В setup() у меня такая запись: 

  Engine left_en(PIN_A_LEFT, PIN_B_LEFT, PIN_C_LEFT, PIN_D_LEFT);
  Engine right_en(PIN_A_RIGHT, PIN_B_RIGHT, PIN_C_RIGHT, PIN_D_RIGHT);
  
  Movement movement(left_en, right_en);

Необходимо переменные left_en, right_en и movement сделать глобальными. 

А что, просто вынести их из setup на глобальный уровень нельзя? Там какие-то нетривиальные конструкторы?

Twilkeris
Offline
Зарегистрирован: 05.02.2016

qwone, ЕвгенийП, спасибо большое за помощь! Шутка про зеркальный шар заставила улыбнуться. )) 

Я хотел зарезервировать переменные на глобальном уровне, а потом присовоить им ссылку на новый созданный объект в другом месте. Так сделать не получилось.

Решил присвоить этим переменным ссылку на инициализированный объект с нужными мне значениями аргументов в конструкторе, но опять не получилось. Оказалось, что проблема была в том, что я инициализировал их ДО описания используемого класса, а не ПОСЛЕ. Привые писать на Java, то такое разрешается. 

Сейчас проблема решена. 

 

Можно задам вопрос в этой теме, чтобы не плодить лишний раз? Тем более, наверной, мой вопрос, как всегда, очень примитивный. Но я действительно не могу понять, почему так происходит. 

Необходимо реализовать прием данные через Bluetooth и их обработку. Если значение входящих данных равно 48, то выполнить определенный код. Проблема в том, что у меня получается так: 

- (ввожу '4')

- Я получил: 52

- (ввожу '2')

- Я получил: 50

- (ввожу '0')

- Я получил: 48

- (ввожу '7')

- Вхожу в условие 48

- Я получил: 48

Почему когда я ввожу '0'(48), происхдит печать результата только при вводе следующего значения?

#include <SoftwareSerial.h>   // Модуль для работы с Serial
 
int incomingByte = 0;         // Переменная для хранения входных данных
 
SoftwareSerial mySerial(51, 50); // Пины для RX, TX 
 
void setup()
{
   mySerial.begin(9600);           // Скорость передачи по Serial
}
void loop()
{
  if(mySerial.available()>0)       // Если имеются в порту данные
  {
     incomingByte = mySerial.read();    // считывание данных
      if(incomingByte!=-1)              // если данные не равны -1
      {
        mySerial.print("\nЯ получил: ");        //вывод сообщения
        mySerial.println(incomingByte, DEC);  // вывод данных
        if(incomingByte  == 48){
          mySerial.print("\nВошли в условие 48");
        }
      }
  }
}

 

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

Вам не когда не казалось странным  когда пишете 1 подразумеваете что это одна палочка, а когда пишете 8 то почему то  видите не восемь палочек а значек 8.  А вот когда пишете 0, то должно быть пусто, а там значек 0.  Вот так и по непонятной логике код числа '0' почему то 48 в десятичной. Ну и так далее.  Так в Serial отправляется все числа и сообщения в коде ASC-2 . 

Twilkeris
Offline
Зарегистрирован: 05.02.2016

qwone, спасибо за разъяснение. Я понимаю, что происходит обмен символами, которые описываются кодами ASCII.

Вопрос был в другом. Когда я ввожу символ '0', в Arduino поступает код "48". В этом случае должен выполниться код из этого блока: 

20         if(incomingByte  == 48){
21           mySerial.print("\nВошли в условие 48");
22         }
 

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

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016
mySerial.print("Вошли в условие 48\n");// или так

Ну наверно потому что вы написали print, а не println . Print помещает строку в буффер, а вот что бы отправить на консоль по каналу надо сделать перевод строки.

Twilkeris
Offline
Зарегистрирован: 05.02.2016

qwone, спасибо большое!!! Всё заработало. 

Извинитие пожалуйста за глупые вопросы. 

Twilkeris
Offline
Зарегистрирован: 05.02.2016

Происходит непонятное.. для меня. Объявляю перменную movemen на глобальном уровне. В методе setup() создаю объект класса Movement, произвожу его настройку и присваиваю переменной movemen ссылку на созданный объект. 

У класса Movement есть метод void to_home(). Если я использую этот метод в setup(), то всё хорошо. Но если я использую void to_home() в методе loop(), то ничего не происходит. Подскажите пожалуйста, в чём может быть проблема? 


Movement* movemen; // Резервируем переменную

void setup() {
  Movement movement(left_en, right_en);
  movement.setSpeed(150); // Больше изменения в объект movement не вносятся
 
  movemen = &movement;
  movemen->to_home(); // Здесь работает

}

void loop() {
  //movemen->to_home(); // А во тут работать отказывается
}

Определил то, что метод в loop() не работает тем, что поочередно комментировал вызов метода в setup() и loop()

b707
Offline
Зарегистрирован: 26.05.2017

Twilkeris. у вас глобальной является только ссылка на обьект, а сам обьект локальной переменной функции setup(), потому снаружи ее и не работает. Строчку 4 переместите "наружу" из функции setup() - и все будет работать.

b707
Offline
Зарегистрирован: 26.05.2017

Почитал ваши посты выше - у вас, так сказать, глобальное непонимание глобальных переменных

Twilkeris пишет:

Я хотел зарезервировать переменные на глобальном уровне, а потом присовоить им ссылку на новый созданный объект в другом месте. Так сделать не получилось.

Так делать нельзя. Создание указателя на обьект не создает самого обьекта. Этим вы ничего не "резервируете".

Twilkeris пишет:
Решил присвоить этим переменным ссылку на инициализированный объект с нужными мне значениями аргументов в конструкторе, но опять не получилось.

тоже самое

Twilkeris пишет:
Оказалось, что проблема была в том, что я инициализировал их ДО описания используемого класса, а не ПОСЛЕ.

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

Обьекты ничем не отличаются от простых типов, например int. Если вы создаете переменную типа int внутри функции setup(), вы же, надеюсь, понимаете, что она в loop() видна не будет? - так почему для обьекта вы ждете другого?

 

Twilkeris
Offline
Зарегистрирован: 05.02.2016

b707, спасибо большое за помощь! Дело в том, что до этого программировал только на Java, поэтому непривычно работать отдельно с ссылками на объект и отдельно самим объектом. То, что Вы написали - понял, свою ошибку осознал. 

Если я создаю глобальный объект класса Movement, то при обращении к методу to_home() ничего не происходит.

Engine left_en(PIN_A_LEFT, PIN_B_LEFT, PIN_C_LEFT, PIN_D_LEFT); // аргументы - константы типа int
Engine right_en(PIN_A_RIGHT, PIN_B_RIGHT, PIN_C_RIGHT, PIN_D_RIGHT);

Movement movement(left_en, right_en); // аргументы - объекты класса Engine

void setup() {
  // Концевые выключители
  pinMode(endStop_right, INPUT); 
  pinMode(endStop_down, INPUT);

  movement.setSpeed(150);
 
  movement.to_home();

}

void loop() {
  // put your main code here, to run repeatedly:

}

Если же инициализирую объект класса Movement внутри метода setup(), то всё на работает. 

void setup() {
  // Концевые выключители
  pinMode(endStop_right, INPUT); 
  pinMode(endStop_down, INPUT);

  Engine left_en(PIN_A_LEFT, PIN_B_LEFT, PIN_C_LEFT, PIN_D_LEFT); // аргументы - константы типа int
  Engine right_en(PIN_A_RIGHT, PIN_B_RIGHT, PIN_C_RIGHT, PIN_D_RIGHT);

  Movement movement(left_en, right_en); // аргументы - объекты класса Engine
  movement.setSpeed(150);
 
  movement.to_home();

}

void loop() {
  // put your main code here, to run repeatedly:

}

Может ли это быть связано как-то с тем, что метод to_home() использует значенения, полученные от концевых выключателей? pinMode() для них назначается в setup(). Просто больше других причин не вижу..

    void to_home(){
      while(debounce(lastState_right, endStop_right)){
        to_right(1);
      }
      while(debounce(lastState_down, endStop_down)){
        to_down(1);
      }
      /*current_left = 0;
      current_up = 0;*/
       Serial.println("current_up");
       Serial.println(current_up);
    } 

    boolean debounce(boolean last, int end_stop){
      boolean current = digitalRead(end_stop);
      if(last!=current){
          delay(5);
          current = digitalRead(end_stop);
      }
      return current;
    }

 

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

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

Пс:   http://arduino.ru/forum/programmirovanie/klassy-arduino-po-qwone-dlya-chainikov

почитайте это

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

Twilkeris пишет:

У меня есть класс Engine, который содержит только один конструктор с агументами типа int. Есть класс Movement, который содержит конструктор с аргментуами типа Engine. 

А у меня, как и у qwone некоторые проблемы с хрустальным шаром. 

А тексты тех классов, что у Вас есть, Вы не приводите.

И как в такой ситуации быть?

Могу лишь высказать предположение, что либо классы написаны неправильно, либо Вы их неправильно используете.

Еще могу высказать некоторые соображения, которые либо будут Вам полезными, либо нет (в зависимости от того, сумел ли я воспользоваться хрустальным шаром).

Работа скетча для Ардуино и программы для ПК имеют некоторые различия. В частности, для обеспечения гибкости создатели Ардуино сделали так, что глобальные переменные инициализируются до того, как произойдет полная инициализация "железа". Соответственно, это накладывает некоторые ограничения на действия доступные конструктору, создающего глобальный объект. Чтобы обойти эти ограничения в Ардуино принято часть действий по инициализации и настройке оборудования, используемого объектом, производить не из конструктора, а из метода begin(), вызываемого из setup(), т.е. уже после того, как будет окончательно настроена вся системная периферия.

Twilkeris
Offline
Зарегистрирован: 05.02.2016

qwone, спасибо! Ещё летом натыкался на Вашу тему. Изучу подробнее.

andriano, очень сыдно приводить свой код целиком, потому что писался он для того, чтобы проверить работоспособность идеи. Заранее логика работы программы не продумывалась, поэтому код такой некрасивый. Совсем не думал, что может возникнуть проблема в работе с глобальными перменными.. И тем более не подумал про особенности работы с Arduino, про которые, конечно же, я не знаю. 

Прикладываю файл со скетчем и код скетча в сообщении. Если нужно будет, то оставлю что-то одно из двух. 

Ссылка на скетч

#include <Stepper.h>

const int PIN_A_LEFT = 11;
const int PIN_B_LEFT = 10;
const int PIN_C_LEFT = 9;
const int PIN_D_LEFT = 8;

const int PIN_A_RIGHT = 7;
const int PIN_B_RIGHT = 6;
const int PIN_C_RIGHT = 5;
const int PIN_D_RIGHT = 4;

const int endStop_down = 52;
const int endStop_right = 53;

const int magnet = 12; 

const int STEPS_ENGINE = 200; // Число шагов за 1 полный оборотa
const int STEP_FOR_MM = 5; // Число шагов на 1 мм

const int MAX_LEFT = 600; // Максимальное число оборот по OX
const int MAX_UP = 600; // Максимальное число оборот по OY
int current_left = 0; 
int current_up = 0;

const int STANDART_SPEED = 180;

class Engine{
  private:
    Stepper engine;
    int steps; // число сделанных шагов 
  public:
    Engine(int pinA, int pinB, int pinC, int pinD):
      engine(STEPS_ENGINE, pinA, pinB, pinC, pinD) {
        engine.setSpeed(STANDART_SPEED);
       };
     void step(int num_step){
      engine.step(num_step);
      steps = steps+num_step;
     }
     void set_speed(int speed){
      engine.setSpeed(speed);
     }
     void to_zero_steps(){
      steps = 0; 
     }
     int get_Steps(){
      return steps;
     }
};

class Movement{
  private:
    Engine* left;
    Engine* right;
    int speed;
    boolean lastState_right = false;
    boolean lastState_down = false;
  public:
    Movement(Engine e_left, Engine e_right){
      left = &e_left;
      right = &e_right;
    };
    void setSpeed(int speed){
      left->set_speed(speed);
      right->set_speed(speed);
      this->speed = speed;
    }
    void to_right(int length){
      for(int k=0; k<(length*STEP_FOR_MM); k++){
        left->step(1);
        right->step(1);
        current_left--;
      }
    }
    void to_left(int length){
      for(int k=0; k<(length*STEP_FOR_MM); k++){
        left->step(-1);
        right->step(-1);
        current_left++;
      }
    }
    void to_up(int length){
      for(int k=0; k<(length*STEP_FOR_MM); k++){
        left->step(-1);
        right->step(1);
        current_up++;
      }
    }
    void to_down(int length){
      for(int k=0; k<(length*STEP_FOR_MM); k++){
        left->step(1);
        right->step(-1);
        current_up--; 
      }
    }
    void to_home(){
      while(debounce(lastState_right, endStop_right)==true){
        to_right(1);
      }
      while(debounce(lastState_down, endStop_down)==true){
        to_down(1);
      }
      /*current_left = 0;
      current_up = 0;*/
       Serial.println("current_up");
       Serial.println(current_up);
    } 

    boolean debounce(boolean last, int end_stop){
      boolean current = digitalRead(end_stop);
      if(last!=current){
          delay(5);
          current = digitalRead(end_stop);
      }
      return current;
    }
};
class Magnet{
  public:
  static void on(){
    digitalWrite(magnet, HIGH);
  }
  static void off(){
    digitalWrite(magnet, LOW);
  }
};

// Вариант 2. Нерабочий
Engine left_en(PIN_A_LEFT, PIN_B_LEFT, PIN_C_LEFT, PIN_D_LEFT);
Engine right_en(PIN_A_RIGHT, PIN_B_RIGHT, PIN_C_RIGHT, PIN_D_RIGHT);
Movement movement(left_en, right_en);

Movement* movemen; 

void setup() {
  pinMode(endStop_right, INPUT);
  pinMode(endStop_down, INPUT);
  pinMode(magnet, OUTPUT);  

  // Вариант 1. Рабочий
  /*Engine left_en(PIN_A_LEFT, PIN_B_LEFT, PIN_C_LEFT, PIN_D_LEFT);
  Engine right_en(PIN_A_RIGHT, PIN_B_RIGHT, PIN_C_RIGHT, PIN_D_RIGHT);
  Movement movement(left_en, right_en);*/
  
  movement.setSpeed(150);

  movemen = &movement; 
}

void loop() {
  movemen->to_home();
}
DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Конечно, жесть - передавать объекты классов по значению, когда можно (и даже нужно) по ссылке. Наверняка, ещё и в определении класса Movement объекты Engine объявлены по значению. Эээх, не экономим память, совсем... Короче, вот так - неправильно:

class Engine
{
...
};

class Movement
{
Engine thisA, thisB;
 public:
 Movement(Engine a, Engine b);
};

а вот так = расово вернее:

class Engine
{
...
};

class Movement
{
Engine *thisA, *thisB;
 public:
 Movement(Engine* a, Engine* b) { thisA = a; thisB = b; };
};

Ну - или так:

class Engine
{
...
};

class Movement
{
Engine *thisA, *thisB;
 public:
 Movement(const Engine& a, const Engine& b) { thisA = &a; thisB = &b; };
};

Ну и до кучи - определить оператор присваивания и конструктор копирования, как минимум. Норм загрузил, не? :) :) :)

Правка: писал сообщение, не видя кода всего скетча, с ТС практически в одно время разродились :) Но там всё равно - по значению в конструктор лезет экземпляр класса, непорядок. Потому оно и не взлетает, собсно.

Twilkeris
Offline
Зарегистрирован: 05.02.2016

DIYMan, спасибо за замечания, учту. Я понимаю, что в моем коде очень много изъянов. Наверное, правильно говорить, что он весь сплошной изъян. :))

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

Если сейчас разберусь с тем, как работать с объектом класса Movement из метода setup() в данных условиях, то большего от этого проекта мне ничего не потребуется. В свободное время можно будет сесть, собраться с мыслями и переписать его. 

 

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Twilkeris пишет:

 

Если сейчас разберусь с тем, как работать с объектом класса Movement из метода setup() в данных условиях, то большего от этого проекта мне ничего не потребуется. В свободное время можно будет сесть, собраться с мыслями и переписать его. 

Дык вы не поняли, не? Я вам написал, почему не работает - вы передаёте экземпляр класса по значению в конструктор другого класса. При этом объект КОПИРУЕТСЯ на стек, и вы сохраняете ссылку на ЛОКАЛЬНЫЙ на стеке объект в области видимости. Как только вы выпадете из области видимости (вышли из конструктора) - всё, приплыли, ваши указатели указывают на Марс, ориентировочно.

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