Глобальные переменные Arduino

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

Область видимости переменных в Arduino

Как мы хорошо знаем, в любом языке программирования переменные служат для хранения каких-либо данных. У каждой переменной есть название и тип, а для создания переменной надо просто указать вместе и то и другое. Например, написав в коде “int sum;” мы укажем компилятору, что нужно создать ячейку памяти с типом данных int и названием sum.

Создав переменную, мы можем использовать ее для сохранения и считывания значений. Но остается вопрос – где именно мы можем это сделать? Согласно правилам Arduino (на самом деле, эти правила заложены в основу C++), у каждой переменной есть свой ареал обитания – область, где с ней можно работать, т.е. сохранять значения и считывать их. И программист должен обязательно понимать, где заканчивается эта область.

Локальные переменные

Локальные переменные – это переменные, область видимости которых ограничена текущим, «локальным», блоком. Например, мы можем определить переменную в блоке, ограниченном операторами if (условия) и переменная будет видна только внутри. Примеры локальных блоков:

  • Внутри цикла (for, while)
  • Внутри функции
  • Внутри блока условия (if, else).

Ограниченность означает следующее: компилятор позволит использовать название переменной внутри блока, но «не узнает» ее, если мы обратимся извне. Давайте посмотрим пример:

int sum(int a, int b){
int internalSum = a+ b;
return internalSum;
}

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

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


int sum(int a, int b){

int internalSum = a+ b;

Serial.print(“Sum = “);

Serial.println(internalSum);

return internalSum;

}

Мы обратились к переменной, взяли ее значение и отправили в монитор порта в функции Serial.println.

А давайте попробуем написать другую функцию и попробуем вывести это значение оттуда. Получится ли у нас это?


void calculator(){

Serial.print(“Sum = “);

Serial.println(internalSum);

}

Конечно же, нет. В функции calculator компилятор попытается найти  определение переменной internalSum внутри функции, но не найдет его (мы же определили internalSum совсем в другой функции, sum()). Расстроившись от этого, компилятор выдаст ошибку (на самом деле, возможность ее избежать и есть использование глобальных переменных – об этом как раз и расскажем позже). Таким образом, мы можем что угодно делать с переменной в своем блоке, но ничего не можем сделать в другом.

Такое поведение компилятора не случайно и легко объяснимо. Зачем нам использовать переменную и данные в области, где их не нужно обрабатывать? Если мы в другом месте случайно изменим значение переменной (глобальной переменной), которую используем в этом месте, то можем создать множество трудноуловимых ошибок. Компилятор защищает нас от этого, просто запрещая использование название переменной из другой области, если это не требуется. К тому же фантазия человека ограничена и нам будет трудно придумать множество названий глобальных переменных так, чтобы они были понятны и не пересекались.

Глобальная переменная

Но как все-таки быть, если нам нужно иметь доступ к переменной из другого блока? Тут есть два варианта.

Если блок находится внутри текущего (например, внутри функции есть  блок с циклом), то в нем будут видны значения из «внешнего» блока. Например, в данном примере внутри блока for мы можем замечательно видеть и использовать переменную result:

int factorial(int val) {
  long result = 0;
  for (int i = 0; i < val; i++) {
    result = result * val ; // Берем текущее значение произведения, умножаем на очередное число и сохраняем обратно в переменную, определенную вне текущего блока, но внутри «верхнего»
  }
}

Второй вариант – использовать глобальную переменную.

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

Давайте рассмотрим следующий пример скетча arduino:

int const pinLed = 13; // Глобальная переменная для хранения номера пина. Может быть заменена на константу
int lastMillis; // Глобальная переменная для хранения последнего времени функции millis()
int duration = 1000; // Глобальная переменная для хранения длительности паузы
int counter = 0; // Глобальная переменная - счетчик
void setup() {
  Serial.begin(9600);
  pinMode(pinLed, OUTPUT);
}
void loop() {
  if (millis() < lastMillis + duration) {
    digitalWrite(pinLed, LOW);    
  } else {
    digitalWrite(pinLed, HIGH);
    delay(1000);
    counter++;
    Serial.println(counter);
    lastMillis = millis();
  }
}

Мы используем глобальные переменные для того, чтобы хранить значения между вызовами функции loop, а также для того, чтобы не писать несколько раз номер пина (глобальная переменная pinLed используется в качестве константы). Переменная lastMills нужна для реализации работы части функционала без использования delay().

Глобальные переменные  arduino– возможные проблемы

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

Другой специфичной проблемой использования глобальных переменных arduino является память. Как известно, в контроллерах Arduino не то что не ограниченное, а как раз очень сильно ограниченное количество памяти. В реальных проектах может вестись борьба практически за каждый байт. Локальные переменные создаются динамически и исчезают из памяти после завершения выполнения функции, освобождая память. А как тут ведут себя глобальные переменные? А ведут они себя очень плохо. Т.к. их значения должны быть доступны повсюду, то они постоянно висят в памяти, не освобождая ее. И если глобальных переменных в коде много, то и памяти под них в итоге требуется тоже немало, что может привести к проблемам.

Альтернативой использованию глобальных переменных  могут стать константы или инструкции #define. Если мы напишем в коде int const pinLed = 13, то заставим компилятор просто вставить в нужные места цифру, убрав эту переменную из глобальной области памяти.

ПОДЕЛИТЬСЯ

ОСТАВЬТЕ ОТВЕТ

Please enter your comment!
Please enter your name here