Язык программирования устройств Ардуино основан на C/C++. Он прост в освоении, и на данный момент Arduino — это, пожалуй, самый удобный способ программирования устройств на микроконтроллерах.
Базовые и полезные знания, необходимые для успешного программирования под платформу Arduino:
- Начало работы с Arduino в Windows
- Работа с Arduino Mini
- Цифровые выводы
- Аналоговые входы
- Широтно-импульсная модуляция
- Память в Arduino
- Использование аппаратных прерываний в Arduino
- Перепрошивка контроллера Atmega8U2 для Arduino Uno и Mega2560
- Переменные
- Функции
- Создание библиотек для Arduino
- Использование сдвигового регистра 74HC595 для увеличения количества выходов
- Прямое управления выходами через регистры микроконтроллера Atmega
Справочник языка Ардуино
Язык Arduino можно разделить на три раздела:
Операторы
Управляющие операторы
Синтаксис
- ; (semicolon)
- <> (curly braces)
- // (single line comment)
- /* */ (multi-line comment)
Арифметические операторы
- = (assignment)
- + (addition)
- — (subtraction)
- * (multiplication)
- / (division)
- % (modulo)
Операторы сравнения
- == (equal to)
- != (not equal to)
- <(less than)
- > (greater than)
- <=(less than or equal to)
- >= (greater than or equal to)
Логические операторы
Унарные операторы
- ++ (increment)
- — (decrement)
- += (compound addition)
- -= (compound subtraction)
- *= (compound multiplication)
- /= (compound division)
Пожалуй самый частоиспользуемый тип для хранения целых чисел со знаком — integer (целое число). Занимает 2 байта и может хранить цисла от -32768 до 32767.
На платформе Arduino также присутствует тип , который ничем не отличается от типа int .
int y = 10;
unsigned int
Беззнаковое целое число, занимает так же, как и int , 2 байта. Диапазон значений — от 0 до 65535.
Тип long служит для хранение больших целых знаковых чисел. Диапазон его значений от -2147483648 до 2147483647, а занимает в памяти он 4 байта.
Типы данных
Переменные разных типов имеют разные особенности и позволяют хранить числа в разных диапазонах.
Название | Альт. название | Вес | Диапазон | Особенность |
boolean | bool | 1 байт * | 0 или 1, true или false | Логический тип |
char | – | 1 байт | -128… 127 (AVR), 0.. 255 (esp) | Символ (код символа) из таблицы ASCII |
– | int8_t | 1 байт | -128… 127 | Целые числа |
byte | uint8_t | 1 байт | 0… 255 | Целые числа |
int ** | int16_t , short | 2 байта | -32 768… 32 767 | Целые числа. На ESP8266/ESP32 – 4 байта! См. ниже |
unsigned int ** | uint16_t , word | 2 байта | 0… 65 535 | Целые числа. На ESP8266/ESP32 – 4 байта! См. ниже |
long | int32_t | 4 байта | -2 147 483 648… 2 147 483 647 | Целые числа |
unsigned long | uint32_t | 4 байта | 0… 4 294 967 295 | Целые числа |
float | – | 4 байта | -3.4E+38… 3.4E+38 | Числа с плавающей точкой, точность: 6-7 знаков |
double | – | 4/8 байт | -1.7E+308.. 1.7E+308 | Для AVR то же самое, что float . |
- (*) – да, bool занимает 1 байт (8 бит), так как это минимальная адресуемая ячейка памяти. Есть способы запаковать логические переменные в 1 бит, о них поговорим в другом уроке.
- (**) – на ESP8266/ESP32 int и unsigned int занимает 4 байта, то есть является аналогами типов long и unsigned long !
- (***) – Компилятор также поддерживает 64 битные числа. Стандартные Arduino-библиотеки с переменными этого типа не работают, поэтому можно использовать только в своём коде.
Целочисленные типы
Переменные целочисленных типов нужны для хранения целых чисел. В своей программе рекомендуется использовать альтернативное название типов (второй столбец в таблице выше), потому что:
- Проще ориентироваться в максимальных значениях
- Легче запомнить
- Название более короткое
- Проще изменить один тип на другой
- Размер переменной задан жёстко и не зависит от платформы (например int на AVR это 2 байта, а на esp8266 – 4 байта)
Максимальные значения хранятся в константах, которые можно использовать в коде. Иногда это помогает избавиться от лишних вычислений:
- UINT8_MAX – 255
- INT8_MAX – 127
- UINT16_MAX – 65 535
- INT16_MAX – 32 767
- UINT32_MAX – 4 294 967 295
- INT32_MAX – 2 147 483 647
- UINT64_MAX – 18 446 744 073 709 551 615
- INT64_MAX – 9 223 372 036 854 775 807
Логический тип
bool – логический, он же булевый (придуман Джорджем Булем) тип данных, принимает значения 0 и 1 или false и true – ложь и правда. Используется для хранения состояний, например включено/выключено, а также для работы в условных конструкциях.
Также переменная типа bool принимает значение true , если присвоить ей любое отличное от нуля число.
bool a = 0; // false bool b = 1; // true bool c = 25; // true
Символьный тип
char – тип данных для хранения символов, символ указывается в одинарных кавычках: char var = ‘a’; . По факту это целочисленный тип данных, а переменная хранит номер (код) символа в таблице ASCII:
Отдельный символьный тип данных нужен для удобства работы, чтобы программа могла понять разницу между числом и символом, например для вывода на дисплей (чтобы вывести именно букву A, а не число 65). Из символов можно составлять строки, об этом более подробно поговорим в уроках про символьные строки и String-строки.
Символы и числа
Несмотря на то, что в языке Си символ это по сути целое число, значения например ‘3’ и 3 не равны между собой, потому что символ ‘3’ с точки зрения программы является числом 51 . На практике иногда бывает нужно конвертировать символы чисел в соответствующие им целые числа и наоборот (при работе со строками и буферами вручную), для этого распространены следующие алгоритмы:
- Из символа в число – взять младший ниббл (4 бита): symbol v = 123456.654321; // 123456.656250 v = 0.0123456789; // 0.0123456788 v = 0.0000123456789; // 0.0000123456788 v = 123456789; // 123456792.0
Другие особенности float чисел и работу с ними мы рассмотрим в уроках про математические операции и условия.
Объявление и инициализация
- Объявление переменной – резервирование ячейки памяти указанного типа на имя: тип_данных имя;
- Присваивание – задание переменной значения при помощи оператора = (равно): имя = значение;
- Инициализация переменной – объявление и присваивание начального значения: тип_данных имя = значение;
Можно объявить и инициализировать несколько переменных через запятую:
byte myVal; int sensorRead = 10; byte val1, val2, val3 = 10;
- Переменная должна быть объявлена до использования, буквально выше по коду. Иначе вы получите ошибку Not declared in this scope – переменная не объявлена.
- Нельзя объявить две и более переменных с одинаковым именем в одной области определения.
Локальные и глобальные переменные Ардуино
Глобальные и локальные переменные имеют разную видимость (время жизни). Локальная переменная в Arduino живет только внутри функции в которой ее инициализировали, если вы попытаетесь получить доступ к локальной переменной вне функции, вы получите ошибку. Например, в программе с мигающим светодиодом переменная объявляется внутри цикла for и, поэтому переменная не может быть использована вне цикла for.
Объявление локальной переменной Ардуино
#define LED 10 void setup() < Serial.begin(9600); pinMode(LED, OUTPUT); >void loop() < for(int i = 0; i for(int i = 255; i >= 0; i—) < analogWrite(LED, i); Serial.println(i); delay(10); >>
Глобальная переменная в Arduino доступна в программе из любого места функции или подпрограммы. Время жизни такой переменной — от начала программы до ее конца. Глобальная переменная int i в следующем примере может быть использована из любой функции программы. Использование переменных при программировании означает, что вам не нужно хранить ее значение, а нужно ссылаться на участок памяти по заданному имени.
Дробные числа (float)
float (англ. float – плавающий) – тип данных для чисел с плавающей точкой, т.е. десятичных дробей. В Arduino используется например для считывания значений с аналоговых пинов. Arduino поддерживает три типа ввода чисел с плавающей точкой:
Тип записи Пример Чему равно Десятичная дробь 20.5 20.5 Научный 2.34E5 2.34*10^5 или 234000 Инженерный 67e-12 67*10^-12 или 0.000000000067 Выше в таблице есть пометка “точность: 6-7 знаков” – это означает, что в этом типе можно хранить числа, размер которых не больше 6-7 цифр, остальные цифры будут утеряны! Причём целой части отдаётся приоритет.
Важный момент: если имя локальной переменной совпадает с одной из глобальных, то приоритет обращения отдаётся локальной переменной
Преобразования между типами данных
Часто случается, что необходимо поменять один тип данных на другой. Например, заменить символ на число или число на символ. В Arduino IDE мы имеем несколько функций, которые позволяют производить преобразование между типами данных. В следующей таблице приведены функции преобразования данных.
Диапазон переменных
Arduino IDE базируется на языке C/C++. Из него же позаимствован способ обработки переменных. При написании программы можно использовать как глобальные переменные, так и локальные переменные.
Глобальная переменная — это такая переменная, который инициализируется при запуске программы и доступна из любого места (функции) в течение всего времени действия программы.
В основном это является преимуществом, поскольку из любой функции мы получаем доступ к переменной, без необходимости передачи информации в качестве параметра вызова.
Но в некоторых случаях, к сожалению, это является недостатком. Используя готовые функции, может оказаться так, что это же имя переменной одновременно используется и для других целей и из-за этого программа может работать неправильно.
Второй тип переменной – локальная переменная. Мы определяем ее в теле функции, и она доступна только на уровне этой функции. Локальная переменная инициализируется при вызове функции и уничтожается после завершения ее работы. Использование локальных переменных снижает спрос на оперативную память, но в то же время затрудняет передачу информации между различными функциями программы.
Настоятельно рекомендуется использовать локальные переменные и функции с параметрами вызова. Глобальные переменные следует использовать в тех ситуациях, когда одна и та же переменная используется в нескольких функциях.
Следующий код иллюстрирует место и способ декларации глобальных и локальных переменных:
[slh lang=»php»]char tablica[10]; // глобальный массив доступный из любого места программы
void setup()
int a; // локальная переменная «a» доступна из функции setup()
>
int x; // глобальная переменная доступна из любого места программы
void loop()
int a; // локальная переменная «a» доступна из функции loop()
char b; // локальная переменная «b» доступна из функции loop()
> [/slh]Как показано в приведенном выше примере, переменные, объявленные внутри функции, являются локальными переменными, а переменные, объявленные вне тела функций, являются глобальными переменными.
Переменная «а» в функции setup () — это совершенно другая переменная, чем «а» в функции loop().
Давайте рассмотрим другой код.
Следующий код можно скомпилировать и запустить, а результат работы программы наблюдать с помощью монитора последовательного порта, доступного в Arduino IDE (функции Serial.begin и Serial.print предназначены для отправки данных через последовательный порт)
[slh lang=»php»] 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
>[/slh]В этом примере есть дополнительная часть — глобальная переменная и локальная переменная с тем же именем. Компилятор не возвращает ошибку. Однако, необходимо помнить о проблемах, которые могут возникнуть из приведенного выше примера.
В начале функции loop() локальная переменная еще не объявлена. Обращаясь к переменной «а», мы обращаемся к глобальной переменной, значение которой составляет 10. После объявления локальной переменной «а» внутри функции loop (), обращаемся к ней и получаем значение 1. Функция loop() вызывается в системе циклически, поэтому с помощью монитора последовательного порта, мы можем наблюдать чередование появление чисел 10 и 1.
Изменим немного код:
[slh lang=»php»] 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); //вывод переменной «а»
>
[/slh]Как видно на примере изменение значения переменной «а» относится к локальной переменной. Следует избегать использования локальных и глобальных переменных с одним и тем же именем, потому что это может создать потенциальные проблемы с нормальным функционированием программы.
Инструкция по программированию Arduino
Друзья, на связи снова специалист Giant 4 Алексей! Мы продолжаем наш небольшой курс статей, по использованию платы Arduino Nano и так называемой адресной лентой, на основе светодиодов ws 2812 b . На сегодня это уже четвертая статья. В прошлый раз мы написали первую программу для управления лентой. Но все-таки нужно иметь хотя бы небольшое представление о языке программирования, которым мы пользовались. Иначе говорить о чем-то дальше будет просто бессмысленно. И так, перед Вами C / C ++ подобный язык. Конечно же, мы не будем изучать его полностью, но я постараюсь затронуть основные моменты. Основные функции Конечно же, это функции setup () и loop () и они нам уже знакомы. Функция setup () вызывается автоматически, при старте программы. Она выполняется один раз и ничего не возвращает, поэтому мы обозначаем данную функцию типом void . В дальнейшем мы разберем, что это значит. Функция loop () начинает выполняться после завершения функции setup (), данная функция тоже ничего не возвращает и выполняется по кругу бесконечное количество раз. И loop (), и setup () являются необходимым минимумом программы. Эти функции должны всегда присутствовать, иначе компилятор выдаст ошибку. Как правило, в setup () происходит инициализация объектов. А в loop () выполняется основная программа и вызываются другие функции.
void setup() < >void loop() < >Типы данных Конечно же, когда речь идет о программировании, то приходится использовать данные разного типа. Типов достаточно много, но мы разберем лишь некоторые из них. — Boolean – это логический тип данных, переменные такого типа могут принимать лишь два значения true или false . Где true – это истина, а false – это ложь. Без этого типа было бы невозможно организовать логику программы. Для того, чтобы объявить переменную такого типа, необходимо записать « boolean a ;». И тогда нам будет доступна переменная с именем «а» типа boolean . А для того, чтобы присвоить данной переменной значение, нужно сделать такую запись – « a = true ;». Хотя можно присвоить значение сразу, при объявлении переменной «boolean a = true;». А чтобы в дальнейшем изменить значение переменной, достаточно просто присвоить ей новое значение. Кстати, да, знак равенства – это операция присваивания. Записи с такой операцией принято читать справа налево. « a = true ;» — Значение true присваивается переменно « a ».
void setup() < >void loop() < boolean a = true; a = false; >Таким же образом, как мы объявляли переменные типа boolean , можно объявить переменную любого типа. — int – пожалуй, самый используемый и востребованный тип. В переменных такого типа хранятся целые числа от -32768 до 32767. Данные ограничения связанны с тем, что на переменную выделяется два байта памяти и больше записать просто не получится. Хотя, если понадобится, то можно воспользоваться другими типами, на которые выделено больше памяти, например, тип long (от -2 147 483 648 до 2 147 483 647). int i = 256; — String – строковый тип. В переменную такого типа можно записать строку или массив символов.
void setup() < >void loop() < String s = » Привет «; >Переменная Переменная – это имя, закреплённое за выделенной областью памяти. Переменная позволяет опустить потребность контроля, за расположением принадлежащей ей ячейки памяти. Достаточно записать значение в переменную, и значение попадет в выделенную для этого область памяти. При этом, пока существует переменная, присвоенное ей значение будет оставаться в целостности и не будет случайно стерто. Как мы уже могли понять, переменные могут быть разных типов и содержать в себе разные данные. Мало того, очень важно то, в каком месте объявлена переменная, так как объявление работает лишь в рамках тела, обособленного фигурными скобками. Как только тело заканчивается, переменная перестает существовать. В следующем примере при каждом повторе функции loop (), в начале работы, переменная инициализируется и ей присваивается значение, а в момент завершения, переменная перестает существовать.
void setup() < >void loop() < String s = » Привет «; >С этим нужно быть аккуратным, так как на инициализацию переменной тратится немного вычислительного ресурса микроконтроллера. Теперь хочу еще раз обратить ваше внимание на то, что если мы объявим переменную в теле одной функции, и при этом попытаемся использовать в теле другой функции, то программа не сможет ее увидеть. Хорошо, что ошибка вылезет еще на этапе компиляции.