Андрей Смирнов
Время чтения: ~14 мин.
Просмотров: 21

Локальные и глобальные переменные Ардуино

Каждая компьютерная система тесно связана с таким понятием как «тип данных». Это связано со спецификой информации, хранящейся в памяти контроллера.

Arduino, построенная на базе микроконтроллеров семейства ATmega, использует основные типы данных. Знание типов данных крайне важно для правильного программирования. Ниже перечислены типы данных, используемые в Arduino IDE:

arduino-tipy-dannyx-1.jpg

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

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

Преобразования между типами данных

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

arduino-tipy-dannyx-2.jpg

Диапазон переменных

Arduino IDE базируется на языке C/C++. Из него же позаимствован способ обработки переменных. При написании программы можно использовать как глобальные переменные, так и локальные переменные.

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

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

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

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

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

Следующий код иллюстрирует место и способ декларации глобальных и локальных переменных:

char tablica[10]; // глобальный массив доступный из любого места программы  void setup()  {  int a; // локальная переменная «a» доступна из функции setup()  }  int x; // глобальная переменная доступна из любого места программы  void loop()  {  int a; // локальная переменная «a» доступна из функции loop()  char b; // локальная переменная «b» доступна из функции loop()  } 

Как показано в приведенном выше примере, переменные, объявленные внутри функции, являются локальными переменными, а переменные, объявленные вне тела функций, являются глобальными переменными.

Переменная «а» в функции setup () — это совершенно другая переменная, чем «а» в функции loop().

Давайте рассмотрим другой код.

 Следующий код можно скомпилировать и запустить, а результат работы программы наблюдать с помощью монитора последовательного порта, доступного в Arduino IDE (функции Serial.begin и Serial.print предназначены для отправки данных через последовательный порт)

  void setup()  {  Serial.begin(9600);    //инициализация последовательного порта  }  int a=10;    //глобальная переменная «а» со значением 10  void loop()  {  Serial.print(a);    //вывод переменной «а», 10  Serial.print(» «);  int a=1;    //локальная переменная «a» со значением 1  Serial.println(a);    //вывод переменной «а», 1  }

В этом примере есть дополнительная часть — глобальная переменная и локальная переменная с тем же именем. Компилятор не возвращает ошибку. Однако, необходимо помнить о проблемах, которые могут возникнуть из приведенного выше примера.

В начале функции loop() локальная переменная еще не объявлена. Обращаясь к переменной «а», мы обращаемся к глобальной переменной, значение которой составляет 10. После объявления локальной переменной «а» внутри функции loop (), обращаемся к ней и получаем значение 1. Функция loop() вызывается в системе циклически, поэтому с помощью монитора последовательного порта, мы можем наблюдать чередование появление чисел 10 и 1.

Изменим немного код:

  void setup()  {  Serial.begin(9600);    //инициализация последовательного порта  }  int a=10;    //глобальная переменная «а» со значением 10  void loop()  {  Serial.print(a);    //вывод переменной «а», 10  Serial.print(» «);  int a=1;    //локальная переменная «a» со значением 1  a++;    //увеличить значение переменной «а» на единицу  Serial.println(a);    //вывод переменной «а»  }  

Как видно на примере изменение значения переменной «а» относится к локальной переменной. Следует избегать использования локальных и глобальных переменных с одним и тем же именем, потому что это может создать потенциальные проблемы с нормальным функционированием программы.

Директивы const, static, volatile

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

Директива const позволяет создать переменную «только для чтения», то есть постоянную. Примеры предопределенных констант можно найти в другой статье.

Альтернативой const является define. Создатели Arduino IDE рекомендуют использовать именно const. Ниже приведены примеры для пользовательских констант:

const float pi=3.14;    // определение константы pi  const byte max=100;    // максимальное значение=100  #define ledPin 13    // определение pinu13 как ledPin  

Как видно const указывает на тип данных, а define присваивает значение без указания типа. Отсутствие точки с запятой в конце строки с define не упущение, а правильный синтаксис, заимствованный из C/C++.

Хорошей практикой является использование define для присвоения словесных обозначений для входов/выходов, а для остальных констант рекомендуется использовать директиву const.

Директива static позволяет определить способ инициализации локальных переменных. Обычно локальная переменная инициализируется во время вызова функции и уничтожается при ее завершении. Добавление директивы static приведет к тому, что с точки зрения охвата переменная останется по-прежнему локальной, но не будет уничтожена после завершения выполнения функции.

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

void setup()  {  Serial.begin(9600);  }  void loop()  {  int a=1;  static int b=1;  Serial.print(a); // всегда 1  Serial.print(» «);  Serial.println(b); // 1, 2, 3…  a++;  b++;  }

При запуске программы вы увидите, что переменная «а» каждый раз принимает начальное значение 1, а переменная «b» значение 1, 2, 3, ….

Последней важной директивой является volatile. Ее следует использовать в случае, когда значение переменной может быть изменено с помощью функции обработчика прерывания.

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

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

volatile int x;  …  void обработка_прерывания()  {  x++;  }

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

3.jpg_640x640.jpgСтенд для пайки со светодиодной подсветкойМатериал: АБС + металл + акриловые линзы. Светодиодная подсветка…Подробнее

СОДЕРЖАНИЕ ►

Глобальные и локальные переменные в скетче Ардуино имеют разную область видимости (время жизни). Разберем, что такое переменные в функции Ардуино, какие переменные следует использовать в процедуре (подпрограмме) void loop или void setup. Также рассмотрим ситуацию, что делать, если при компиляции кода появляется ошибка — глобальные переменные используют много памяти Arduino.

Объявление переменных в скетче Ардуино

Важно помнить, что переменная в программе должна бывать объявлена до обращения к себе, то есть находиться выше по коду. Иначе вы получите ошибку при компиляции – Not declared in this scope. Еще следует учесть, что при инициализации переменной внутри цикла void loop — при каждом новом запуске цикла переменная будет каждый раз обновляться. Переменная в скетче объявляется просто:

Пример скетча:

>int var;  int var2 = 10;    voidsetup() {  }    voidloop() {  }

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

Глобальная переменная Arduino IDE доступна («видна») в программе из любого места в подпрограмме или функции. Время жизни такой переменной с начала запуска программы до ее окончания. Локальные переменные Arduino IDE доступны внутри тех функций или подпрограмм, в которых они были объявлены. Рассмотрим несколько примеров объявления глобальных и локальных переменных в программе Ардуино.

Ардуино глобальные переменные в скетче

Глобальная переменная объявляется в программе вне функций и доступна в любом месте к чтению или записи. Загрузите по очереди в плату следующие два примера.

Правильный пример скетча:

>int var = 10;  // создаем глобальную переменную в кодеvoidsetup() {      Serial.begin(9600);      // выводим значение глобальной переменной на монитор портаSerial.println(var);      delay(1000);  }    voidloop() {      // меняем значение переменной var и выводим на монитор      var = var + 10;      Serial.println(var);      delay(500);  }

Пример скетча с ошибкой:

>int var = 10;  // создаем глобальную переменную в кодеvoidsetup() {      int var2 = 20;  // создаем локальную переменнуюSerial.begin(9600);      // выводим значение глобальной и локальной переменнойSerial.println(var);      Serial.println(var2);      delay(1000);  }    voidloop() {      // при компиляции скетча появится ошибкаSerial.println(var);      Serial.println(var2);      delay(500);  }

Пояснения к коду:

  1. глобальную переменную var можно использовать: перезаписывать и читать из любой функции (процедуры) программы;
  2. при попытке использовать локальную переменную var2 в процедуре void loop появится ошибка компиляции Ардуино, так как переменную var2 можно использовать только в той функции, где она была объявлена.

Ардуино локальные переменные в скетче

Локальная переменная живет только внутри функции или внутри блока кода в фигурных скобках. При попытке обратиться к переменной за пределами функции (как на примере выше) вы получите ошибку. Во многих уроках Ардуино мы объявляли переменную внутри цикла for. Как и в следующем примере программы, где локальную переменную нельзя использовать за пределами фигурных скобок цикла for.

Правильный пример скетча:

voidsetup() {      Serial.begin(9600);  }    voidloop() {         for(int var = 0; i <= 100; var = var + 10) {         Serial.println(var);         delay(500);     }  }

Пример скетча с ошибкой:

voidsetup() {      Serial.begin(9600);  }    voidloop() {         for(int var = 0; i <= 100; var = var + 10) {         Serial.println(var);         delay(500);     }         Serial.println(var);  }

Пояснения к коду:

  1. локальная переменная var инициализируется внутри цикла for, поэтому при попытке обратиться к переменной за пределами цикла — появится ошибка.

Ардуино формальные переменные в скетче

Формальная переменная, она же параметр, передаваемый в функцию, ведет себя как локальная переменная, но появляется в программе при других условиях: при вызове функции. Эту переменную можно читать и записывать внутри ее функции. Загрузите следующий пример кода в микроконтроллер и откройте монитор порта.

voidsetup() {      Serial.begin(9600);  }    voidloop() {      // передаем значение 10, как аргумент функции      Func(10);      delay(500);  }    void Func(int var) {      var = var + 10;      Serial.println(var);  }

Пояснения к коду:

  1. формальную переменную var можно использовать только внутри функции Func;
  2. после окончания работы функции, переменная var будет удалена из памяти.

Привет друзья от geekmatic.in.ua! Пора заняться полезным делом и разобрать типы данных, представленные в Arduino IDE.

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

Тип Синоним Байт Диапазон
1
1
1
1
2
2
4
4
4
void
String()

Так что погружаемся в дебри ардуиновских правил урезанного языка Си и да прибудет с нами мотивация! Обещаю в конце урока выделить самые необходимые типы создаваемых переменных. А пока начнем разбор по порядку с типа bool.

Тип Синоним Байт Диапазон
1

Тип данных bool используется для логических переменных, хранящих два возможных значения: true или false (правда или ложь). Такие переменные удобны для создания программных флажков или защелок, для хранения состояния дискретного входа. Для переменных, которые должны отвечать на вопрос типа да или нет, включен или выключен и подобных.

Булевскую переменную можно вставлять в функцию digitalWrite() для задания состояния дискретному выходу контроллера.

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

Тип Синоним Байт Диапазон
1

Byte незаслуженно недооцененный начинающими программистами тип данных, который почему-то заменен типом int даже в большинстве примеров Arduino IDE. Он хорош тем, что занимает столько же места, сколько и один регистр памяти 8-битных контроллеров Arduino. 8 бит так же занимает и минимальный коммуникационный пакет данных в сети UART, I2C и других. LCD-индикаторы тоже принимают побайтные (8-битные) данные и команды. Поэтому тип byte незаменим при коммуникациях контроллера с различной умной периферией, при прямой работе с регистрами, а так же для хранения целых десятичных чисел в промежутке 0…255.

1
1

Тип char создан для работы с символьными данными. Одна переменная этого типа может хранить только один текстовый символ. Значение переменной можно присваивать как через символ в одинарных кавычках, так и цифровым эквивалентом. Цифровой код, соответствующий каждому поддерживаемому символу для этого типа переменных, заложен согласно таблице ASCII. Применяется такой тип для работы со строками: отображения текстовой информации на LCD-индикаторах; обмена текстовыми сообщениями через монитор порта и так далее.

Unsigned char – тоже символьный тип данных, но он может хранить цифровые коды символов в диапазоне 0…255. Считается бесполезным. Неоднократно получал награду Золотой валенок.

Теперь посмотрим на саму таблицу ASCII.

ASCII.png

Это таблица соответствия заложенных в компилятор символов и их десятичных цифровых кодов. До 32-го символа идут не читаемые символы. В примере мы использовали букву А. Вы можете попробовать найти здесь её цифровой код. А кириллицу кстати тут не найдёшь. Её буквы начинаются с кода 192 в расширенной таблице ASCII. О кириллице продолжим в конце урока.

2
2

Int и unsigned int – самые популярные целочисленные типы. Они охватывают большой диапазон целых чисел, но занимают в два раза больше памяти чем тип byte. Переменная типа int занимает 16 бит. Unsigned int – тот же тип, только без знака. Он охватывает только положительные значения 0…65535. Эти два типа хорошо подходят например для хранения считанного значения аналогового входа.

4
4

Типы long и unsigned long используются в тех случаях, когда не хватает размера int. Они тоже содержат целые числа, но, в отличии от int, занимают 32 бита памяти каждый. В переменных такого типа можно хранить например скорость UART-порта контроллера, номер телефона, количество миллисекунд. Такие большие числа в проектах встречаются не часто, но бывают.

4

Float – тип переменной с плавающей точкой или вещественного числа. Float позволяет получать и хранить значения данных с точностью до 6-7 знаков после запятой. Занимает 32 бита памяти. Применяется для хранения и отображения значения физических величин, результатов математических расчетов, аргументов математических формул, математических констант и так далее.

Обратите внимание на примеры, взятые из документации Arduino, по преобразованию целого числа int в вещественное float – тут есть свои нюансы.

Тип double на других платформах, позволяет получать значения чисел с точностью до 15 знаков после запятой, но у Arduino этот тип полностью аналогичен float.

Тип void применим к функциям. Это тип-пустышка, который означает, что функция не возвращает никакого значения.

Здесь на примере для сравнения показана функция myfunc1, возвращающая значение типа int и три функции не возвращающие никакого значения, объявленные с типом void.

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

Текст присваивается переменной в двойных кавычках. А так же в Arduino IDE предусмотрена функция String(), которая преобразовывает значения различных типов переменных и констант в строку String. Вы видите примеры того, какие аргументы можно передавать функции String(). Интересно и то, что текст тут можно писать даже на кириллице. 

Подробнее о строковых переменных будем говорить позднее.

А пока, напомним себе таблицу типов данных Arduino. Запоминать их все пока не обязательно, а ознакомиться не помешает. И, Чтобы новичков не сильно пугать, выполняя обещание, свожу самые используемые типы переменных в следующую упрощенную таблицу.

2
4

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

</span>rma_218-228x228.png

RMA-218 паста флюс для пайки

RMA-218 хорошо активируется со свинцовыми и без свинцовыми припоями. Не требует отмывки после п..

68.14грн.

pushka_gausa-228x228.jpg

Пушка Гауса

Подключаем к контроллеру Arduino, stm32, Raspberry PI пушку Гауса через электропускателиА если ещё с..

remen_dlia_3d_printera_cnc-228x228.png

Ремень зубчатый 6 мм для 3D-принтера

Отрезок зубчатого ремня шириной 6 мм.Применяется для перемещения каретки 3D-принтера или CNC по разн..

37.21грн.

HC_06_Bluetooth_1-228x228.png

Модуль Bluetooth HC 06

Связь с контроллером через UART портУправляется АТ-командамиПо умолчанию скорость обмена 9600 бодПар..

125.09грн.

korpus_dlia_orange_pi_pc_s_ventiliatorom-228x228.png

Корпус для Orange PI PC с вентилятором

Корпус для мини компьютера Orange PI PCМатериал прозрачный акрилВ комплект входит вентилятор ох..

166.71грн.

Используемые источники:

  • http://www.joyta.ru/10724-arduino-tipy-dannyx/
  • https://xn--18-6kcdusowgbt1a4b.xn--p1ai/%d0%bf%d0%b5%d1%80%d0%b5%d0%bc%d0%b5%d0%bd%d0%bd%d1%8b%d0%b5-%d0%b0%d1%80%d0%b4%d1%83%d0%b8%d0%bd%d0%be/
  • http://geekmatic.in.ua/arduino_uroki_tipi_dannih

Рейтинг автора
5
Подборку подготовил
Максим Уваров
Наш эксперт
Написано статей
171
Ссылка на основную публикацию
Похожие публикации