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

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

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

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

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

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

Тестер транзисторов / ESR-метр / генератор
Многофункциональный прибор для проверки транзисторов, диодов, тиристоров...
Подробнее

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

Часто случается, что необходимо поменять один тип данных на другой. Например, заменить символ на число или число на символ. В 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.

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

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


*