что значит в arduino
Сравнения, условия и выбор
Сравнение
Результаты операторов сравнения можно использовать для работы с условиями, а также для цикла while (речь о нём пойдёт в следующем уроке)
Сравнение float
Со сравнением float чисел всё не так просто из за особенности самой модели “чисел с плавающей точкой” – вычисления иногда производятся с небольшой погрешностью, из за этого сравнение может работать неверно! Пример из урока про вычисления:
Будьте внимательны при сравнении float чисел, особенно со строгими операциями : результат может быть некорректным и нелогичным!
Условный оператор if
Условный оператор if (англ. “если”) позволяет разветвлять выполнение программы в зависимости от логических величин, т.е. результатов работы операторов сравнения, которые мы рассмотрели выше, а также напрямую от логических переменных.
Оператор else (англ. “иначе”) работает в паре с оператором if и позволяет предусмотреть действие на случай невыполнения if :
Также есть третья конструкция, позволяющая ещё больше разветвить код, называется она else if :
Посмотрим на все эти операторы в действии в большом примере:
Особенность boolean
Порядок условий
Порядок условий играет очень большую роль при оптимизации кода и попытке сделать его более быстрым в некоторых случаях. Суть очень проста: логические выражения/величины проверяются слева направо, и если хоть одно значение делает всё выражение неверным (ложью), дальнейшая проверка условий прекращается. Например если в выражении
Если флаг опущен, микроконтроллер не будет тратить лишние 100 мкс на работу с АЦП, и сразу проигнорирует остальные логические выражения. Это конечно очень мало, но иногда и 100 мкс решают, просто помните о том, что порядок условий имеет значение.
Это работает так: вычисляется условие, если оно истинно, то всё действие возвращает значение выражения 1, а если оно ложно, то всё действие возвращает значение выражения 2. Пример:
Вот для сравнения аналогичная конструкция на if-else
Ещё вариант с вычислением:
Также можно получать значения из функций, если они имеют одинаковый возвращаемый тип данных:
Оператор выбора
Оператор выбора switch позволяет создать удобную конструкцию, разветвляющую действия в зависимости от значения одной переменной. Синтаксис такой:
Наличие оператора default необязательно. Наличие оператора break обязательно, иначе сравнение пойдёт дальше, как показано для case 2, 3 и 4.
При помощи условных операторов и операторов выбора строится логика работы программы. Условный оператор поможет сравнить значение с датчика и принять решение, что делать дальше. Оператор выбора отлично справится с изменяющимися режимами работы программы или опросом кнопок, нажатых на ИК пульте.
ВАЖНОЕ ЗАМЕЧАНИЕ
Более того, крайне не рекомендуется вообще создавать локальные переменные внутри кейсов, так как это может сломать код!
Что делать, если очень хочется или “очень надо”? Обернуть содержимое кейса в блок при помощи фигурных скобок:
Условные директивы #if #else
Как ими пользоваться давайте посмотрим на примере:
Таким образом при помощи настройки DEBUG можно включить или исключить любой кусок кода.
Именно на условной компиляции строится вся универсальность библиотек для Arduino, ведь при выборе платы автоматически “создаётся” дефайн на название микроконтроллера, выглядят они так:
Это позволяет создавать универсальный код, используя конструкцию с #ifdef или #if defined() :
Таким образом микроконтроллерам (платам Arduino) разных моделей будет доступен персональный кусок кода, который будет передан компилятору при выборе этой платы из списка плат.
Читай продвинутый урок по директивам препроцессора, если хочешь узнать больше!
Видео
Логика
В прошлом уроке мы научились считывать аналоговый и цифровой сигнал и столкнулись с логическими переменными, условным оператором и другими логическими штуковинами.
Сегодня мы рассмотрим логику языка C++ более подробно.
Нам понадобятся
Boolean
Начнем с того, что вспомним, что такое логическая переменная.
Boolean – это логическая переменная. У нее может быть только два значения:
True – истина. Значение в Serial monitor – 1.
False – ложь. Значение в Serial monitor – 0.
С помощью логических переменных мы можем выполнять различные поставленные задачи.
Вкыл и выкл
Давайте попробуем решить несложную задачу:
«По кратковременному нажатию кнопки включаем светодиод, по кратковременному нажатию выключаем светодиод»
Кто уже почувствовал свои силы и читал все предлагаемые для ознакомления статьи, может попробовать выполнить поставленную задачу самостоятельно.
Соберем схему, как и в прошлом уроке.
Пояснения
Логические операторы Всего их три. Логическое «И», логическое «ИЛИ» и логическое «НЕ»
Логическое «И». Обозначается «&&». Пример с кодом: a > 1 && b 6 || b > 0 Это кусочек кода означает, что условие будет истинно, если хотя бы одно из малых условий истинно. Условие будет ложно только в том случае, когда все малые условия ложны.
Меньше или равно. Обозначение « = ». X >= Y означает, что X больше или равен Y.
Дребезг При замыкании и размыкании между пластинами кнопки возникают искры, провоцирующие до десятка переключений за несколько миллисекунд.
Явление называется дребезгом (англ. bounce).В нашем эксперименте мы делали небольшую задержку, чтобы дребезг не смог помешать получить реальное значение.
Вывод
Теперь мы умеем пользоваться логикой С++ для создания ветвления алгоритмов. В будущем это умение пригодится во многих проектах.
Список команд Arduino
На этой странице представлен список всех (или почти всех) доступных “из коробки” команд для Arduino с кратким описанием и примерами. Часть информации взята из Гугла, в основном некоторые особенности языка, часть получена методом проб и ошибок. Полную информацию о том, как этим пользоваться, можно получить из уроков или официального reference. Также изо всех сил рекомендую вот этот онлайн справочник по C++, из него можно узнать гораздо больше о некоторых особенностях использования операторов и типов данных.
А ещё у меня есть краткий сборник всех основных Ардуино-функций для печати! Смотри в этом уроке.
Структура скетча
Синтаксис, структура кода
Ставится в конце каждого действия
Функция, содержимое которой выполняется один раз при запуске микроконтроллера. Подробнее – в этом уроке.
Функция, содержимое которой выполняется (или пытается выполняться) “по кругу” на протяжении всего времени работы МК. Подробнее – в этом уроке.
Директива, позволяющая подключать в проект дополнительные файлы с кодом.
В чём отличие <> и “”? Когда указываем название “в кавычках”, компилятор сначала ищет файл в папке со скетчем, а затем в папке с библиотеками. При использовании компилятор ищет файл только в папке с библиотеками
Директива, дающая команду препроцессору заменить указанное название на указанное значение. Чаще всего таким образом объявляют константы:
После компиляции все встречающиеся в тексте программы слова MOTOR_PIN будут заменены на цифру 10, а LED_PIN – на цифру 3. Такой способ хранения констант не использует оперативную память микроконтроллера. Также define позволяет делать т.н. макро функции. Например Ардуиновская функция sq (квадрат) является макро, который при компиляции превращается в умножение:
Директивы препроцессору, позволяющие включать или исключать участки кода по условию
При помощи условной компиляции очень удобно собирать и настраивать сложные проекты с кучей настроек и библиотек, подключаемых “по условию”. Например:
Если параметру DEBUG установить 1, то будет подключена библиотека Serial, если 0 – то нет. Таким образом получаем универсальный оптимизированный проект с отладкой. Подробнее – в этом уроке.
Условные директивы препроцессору, позволяют включать или исключать участки кода по условию: ifdef – определено ли? ifndef – не определено ли? Подробнее – в этом уроке.
Оператор перехода в другую часть кода по метке. Не рекомендуется к использованию, всегда можно обойтись без него. Как пример использования – выход из кучи условий
Оператор прерывания функции, он же оператор возврата значения из функции. Подробнее – в этом уроке
Условия (if, switch)
Оператор сравнения и его друзья. Подробнее – в этом уроке.
Оператор выбора, заменяет конструкцию с else if. Подробнее – в этом уроке.
Оператор break очень важен, позволяет выйти из switch. Но можно использовать так:
Циклы (for, while)
Цикл – “счётчик”. for (инициализация; условие; инкремент). Подробнее – в этом уроке.
Также используется для создания замкнутых циклов, т.к. настройки for необязательны. Выход только через break или goto
Цикл с предусловием. Подробнее – в этом уроке.
Может быть использован для создания замкнутого цикла, выход только через break или goto
Цикл с постусловием. Подробнее – в этом уроке.
Отличается от while тем, что гарантированно выполнится хотя бы один раз
Пропускает все оставшиеся в теле цикла действия и переходит к следующей итерации
Операторы
Запятая тоже является оператором, используется в следующих случаях:
Рассмотрим третий случай здесь:
Арифметические
Арифметические операторы – самые простые и понятные из всех
Большой урок по математическим действиям.
Сравнение и логика
Подробный урок по сравнениям и условиям.
Составные операторы
По битовым операциям читай отдельный урок. По математическим операциям у меня тоже есть подробный урок.
Битовые операции
По битовым операциям читай урок у меня на сайте.
Указатели и ссылки
Подробнее об указателях и ссылках читайте в отдельном уроке.
Работа с данными
Типы данных, переменные
Переменная – элементарная ячейка для хранения данных (цифр). Переменные разных типов имеют разный “размер ячейки” и имеют разный лимит на размер числа.
Название | Альт. название | Вес | Диапазон | Особенность |
boolean | bool | 1 байт | 0 или 1, true или false | Логическая переменная. bool на Arduino тоже занимает 1 байт, а не бит! |
char | int8_t | 1 байт | -128… 127 | Хранит номер символа из таблицы символов ASCII |
byte | uint8_t | 1 байт | 0… 255 | |
int | int16_t, short | 2 байта | -32 768… 32 767 | |
unsigned int | uint16_t, word | 2 байта | 0… 65 535 | |
long | int32_t | 4 байта | -2 147 483 648… 2 147 483 647 | – 2 миллиарда… 2 миллиарда |
unsigned long | uint32_t | 4 байта | 0… 4 294 967 295 | 0… 4 миллиарда… |
float | — | 4 байта | -3.4028235E+38… 3.4028235E+38 | Хранит числа с плавающей точкой (десятичные дроби). Точность: 6-7 знаков |
double | — | 4 байта | Для AVR то же самое, что float. А так он 8 байт | |
— | int64_t | 8 байт | -(2^64)/2… (2^64)/2-1 | Очень большие числа. Serial не умеет такие выводить |
— | uint64_t | 8 байт | 2^64-1 | Очень большие числа. Serial не умеет такие выводить |
Существует еще несколько специальных типов данных для символов. Подробнее можно почитать здесь.
Создаёт тип данных под названием color, который будет абсолютно идентичен типу byte (то есть принимать 0-255). Теперь с этим типом можно создавать переменные:
Создали три переменные типа color, который тот же byte, только в профиль. Это всё!
Более подробно о переменных и данных можно почитать вот здесь.
Структуры
Структура (struct) – очень составной тип данных: совокупность разнотипных переменных, объединённых одним именем.
Ярлык будет являться новым типом данных, и, используя этот ярлык, можно объявлять уже непосредственно саму структуру:
Также есть вариант объявления структуры без создания ярлыка, т.е. создаём структуру, не объявляя её как тип данных со своим именем.
Более подробно про структуры читай тут.
Перечисления
Перечисления (enum – enumeration) – тип данных, представляющий собой набор именованных констант, нужен в первую очередь для удобства программиста.
Объявление перечисления чем-то похоже на объявление структуры:
Таким образом мы объявили ярлык. Теперь, используя этот ярлык, можно объявить само перечисление:
Также как и у структур, можно объявить перечисление без создания ярлыка (зачем нам лишняя строчка?):
Более подробно про перечисления читай тут.
Классы
Классы в С++ – это основной и очень мощный инструмент языка, большинство “библиотек” являются классами. Иерархия такая:
Класс объявляется следующим образом:
Более подробно про работу с классами читай вот в этом уроке про классы.
Массивы
Для объявления массива достаточно указать квадратные скобки после имени переменной, тип данных – любой. Более подробно про массивы читай в уроке.
Строки (объект String)
String – очень мощный инструмент для работы со строками, т.е. текстовыми данными. Объявить строку можно несколькими способами:
Подробнее про строки String и массивы символов читай в этом уроке.
Строки можно сравнивать, складывать и вычитать, также для работы с ними есть куча функций:
myString.charAt(index);
Возвращает элемент строки myString под номером index. Аналог – myString[index];
myString.setCharAt(index, val);
Записывает в строку myString символ val на позицию index. Аналог – myString[index] = val;
myString.compareTo(myString2);
myString.concat(value);
Присоединяет value к строке (value может иметь любой численный тип данных). Возвращает true при успешном выполнении, false при ошибке. Аналог – сложение, myString + value;
myString.endsWith(myString2);
Проверяет, заканчивается ли myString символами из myString2. В случае совпадения возвращает true
myString.startsWith(myString2);
Проверяет, начинается ли myString символами из myString2. В случае совпадения возвращает true
myString.equals(myString2);
Возвращает true, если myString совпадает с myString2. Регистр букв важен
myString.equalsIgnoreCase (myString2);
Возвращает true, если myString совпадает с myString2. Регистр букв неважен
myString.indexOf(val);
myString.indexOf(val, from);
myString.lastIndexOf(val);
myString.lastIndexOf(val, from);
myString.length();
Возвращает длину строки в количестве символов
myString.remove(index);
myString.remove(index, count);
Удаляет из строки символы, начиная с index и до конца, либо до указанного count
myString.replace(substring1, substring2);
В строке myString заменяет последовательность символов substring1 на substring2.
myString.reserve(size);
Зарезервировать в памяти количество байт size для работы со строкой
myString.c_str();
Преобразовывает строку в “СИ” формат (null-terminated string) и возвращает указатель на полученную строку
myString.trim();
Удаляет пробелы из начала и конца строки. Действует со строкой, к которой применяется
myString.substring(from);
myString.substring(from, to);
Возвращает кусок строки, содержащейся в myString начиная с позиции from и до конца, либо до позиции to
myString.toCharArray(buf, len);
Раскидывает строку в массив – буфер buf (типа char []) с начала и до длины len
myString.getBytes(buf, len);
Копирует указанное количество символов len (вплоть до unsigned int) в буфер buf (byte [])
myString.toFloat();
Возвращает содержимое строки в тип данных float
myString.toDouble();
Возвращает содержимое строки в тип данных double
myString.toInt();
Возвращает содержимое строки в тип данных int
myString.toLowerCase();
Переводит все символы в нижний регистр. Было ААААА – станет ааааа
myString.toUpperCase();
Переводит все символы в верхний регистр. Было ааааа – станет ААААА
Спецификаторы переменных
Преобразование типов данных
Таким образом можно преобразовывать обычные переменные, указатели и другие типы данных.
И для строк мы уже рассматривали выше
Как пользоваться: на примере предыдущего примера
Подробный урок по типам данных смотри тут.
“Символьные” функции
Все следующие функции принимают как аргумент символ (тип char), анализируют его и возвращают true или false в зависимости от предназначения.
Работа с цифрами
Целые и дробные числа
Arduino поддерживает работу с целыми числами в разных системах исчисления:
Базис | Префикс | Пример | Особенности |
2 (двоичная) | B или 0b (ноль бэ) | B1101001 | цифры 0 и 1 |
8 (восьмеричная) | 0 (ноль) | 0175 | цифры 0 – 7 |
10 (десятичная) | нет | 100500 | цифры 0 – 9 |
16 (шестнадцатеричная) | 0x (ноль икс) | 0xFF21A | цифры 0-9, буквы A-F |
ВАЖНО! Для арифметических вычислений по умолчанию используется ячейка long (4 байта), но при умножении и делении используется int (2 байта), что может привести к непредсказуемым результатам! Если при умножении чисел результат превышает 32’768, он будет посчитан некорректно. Для исправления ситуации нужно писать (тип данных) перед умножением, что заставит МК выделить дополнительную память для вычисления (например (long)35 * 1000). Также существую модификаторы, делающие примерно то же самое.
Посмотрим, как это работает на практике:
Arduino поддерживает работу с числами с плавающей точкой (десятичные дроби). Этот тип данных не является для неё “родным”, поэтому вычисления с ним производятся в несколько раз дольше, чем с целочисленным типом (около 7 микросекунд на действие). Arduino поддерживает три типа ввода чисел с плавающей точкой:
Тип записи | Пример | Чему равно |
Десятичная дробь | 20.5 | 20.5 |
Научный | 2.34E5 | 2.34*10^5 или 234000 |
Инженерный | 67e-12 | 67*10^-12 или 0.000000000067 |
С вычислениями есть такая особенность: если в выражении нет float чисел, то вычисления будут иметь целый результат (дробная часть отсекается). Для получения правильного результата нужно писать (float) перед действием, или использовать float числа при записи. Смотрим:
Ну и напоследок, при присваивании float числа целочисленному типу данных дробная часть отсекается. Если хотите математическое округление – его нужно использовать отдельно:
Математические функции и константы
Математических функций Arduino поддерживает очень много, малая часть из них являются макро функциями, идущими в комплекте с Arduino.h, все остальные же наследуются из мощной C++ библиотеки math.h
Константа | Значение |
UINT8_MAX | 255 |
INT8_MAX | 127 |
UINT16_MAX | 65535 |
INT16_MAX | 32767 |
UINT32_MAX | 4294967295 |
INT32_MAX | 2147483647 |
Функция | Значение |
min(a, b) | Возвращает меньшее из чисел a и b |
max(a, b) | Возвращает большее из чисел |
abs(x) | Модуль числа |
constrain(val, low, high) | Ограничить диапазон числа val между low и high |
map(val, min, max, outMin, outMax) | Перевести диапазон числа val (от min до max) в новый диапазон (от outMin до outMax). val = map(analogRead(0), 0, 1023, 0, 100); – получить с аналогового входа значения 0-100 вместо 0-1023. Работает только с целыми числами! |
round(x) | Математическое округление |
radians(deg) | Перевод градусов в радианы |
degrees(rad) | Перевод радиан в градусы |
sq(x) | Квадрат числа |
Константа | Значение | Описание |
INT8_MAX | 127 | Максимальное значение для char, int8_t |
UINT8_MAX | 255 | Максимальное значение для byte, uint8_t |
INT16_MAX | 32767 | Максимальное значение для int, int16_t |
UINT16_MAX | 65535 | Максимальное значение для unsigned int, uint16_t |
INT32_MAX | 2147483647 | Максимальное значение для long, int32_t |
UINT32_MAX | 4294967295 | Максимальное значение для unsigned long, uint32_t |
M_E | 2.718281828 | Число e |
M_LOG2E | 1.442695041 | log_2 e |
M_LOG10E | 0.434294482 | log_10 e |
M_LN2 | 0.693147181 | log_e 2 |
M_LN10 | 2.302585093 | log_e 10 |
M_PI | 3.141592654 | pi |
M_PI_2 | 1.570796327 | pi/2 |
M_PI_4 | 0.785398163 | pi/4 |
M_1_PI | 0.318309886 | 1/pi |
M_2_PI | 0.636619772 | 2/pi |
M_2_SQRTPI | 1.128379167 | 2/корень(pi) |
M_SQRT2 | 1.414213562 | корень(2) |
M_SQRT1_2 | 0.707106781 | 1/корень(2) |
NAN | __builtin_nan(“”) | nan |
INFINITY | __builtin_inf() | infinity |
PI | 3.141592654 | Пи |
HALF_PI | 1.570796326 | пол Пи |
TWO_PI | 6.283185307 | два Пи |
EULER | 2.718281828 | Число Эйлера е |
DEG_TO_RAD | 0.01745329 | Константа перевода град в рад |
RAD_TO_DEG | 57.2957786 | Константа перевода рад в град |
Псевдослучайные числа
Биты и байты
Битовые операции – подробнее читай в отдельном уроке.
Ввод-вывод
Цифровые пины
Устанавливает режим работы пина pin (ATmega 328: D0-D13, A0-A5) на режим mode:
Читает состояние пина pin и возвращает :
Подаёт на пин pin сигнал value:
Запускает генерацию ШИМ сигнала (отдельный урок про ШИМ) на пине pin со значением value. Для стандартного 8-ми битного режима это значение 0-255, соответствует скважности 0-100%. Подробнее о смене частоты и разрядности ШИМ смотрите в этом уроке. ШИМ пины:
Аналоговые пины
Читает и возвращает оцифрованное напряжение с пина pin. АЦП на большинстве плат Arduino имеет разрядность 10 бит, так что возвращаемое значение 0 – 1023 при напряжении 0 – опорное на пине. Урок про аналоговые пины.
Устанавливает режим работы АЦП согласно mode:
Как это влияет на работу? Значение 1023 функции analogRead() будет соответствовать опорному напряжению или выше его, соответственно поставив INTERNAL можно измерять напряжение от 0 до 1.1V с точностью (1.1 / 1023
1.2 мВ), напряжение выше 1.1V будет всегда 1023. После изменения источника опорного напряжения (вызова analogReference) первые несколько измерений могут быть нестабильными.
Нельзя использовать напряжение меньше 0V или выше 5V в качестве внешнего опорного в пин AREF. Также при использовании режима EXTERNAL нужно вызвать analogReference(EXTERNAL) до вызова функции analogRead(), иначе можно повредить микроконтроллер. Также можно подключить опорное в пин AREF через резистор на
5 кОм, но так как вход AREF имеет собственное сопротивление в 32 кОм, реальное опорное будет например 2.5 * 32 / (32 + 5) =
Аппаратные прерывания
Подключить прерывание (читай урок про прерывания) на номер прерывания pin, назначить функцию ISR как обработчик и установить режим прерывания mode: