Arduino — типы данных

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

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

типы данных, используемые в Arduino IDE

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

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

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

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

функции преобразования данных

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

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.

редактор

Добавить комментарий

Ваш электронный адрес не будет опубликован.

*