Курс arduino – логика

Курс arduino – логика

  • Уроки
  • Программирование
  • Сравнения и условия

Сравнение

В языке C++ (как и пожалуй во всех языках) есть такое понятие, как логическая величина, которая принимает два значения: правда и ложь, true и false , 1 и 0. В качестве типа данных по работе с логическими величинами у нас есть boolean (синоним – bool ), который может принимать значения 0 ( false ) или 1 ( true ). Точно такое же значение возвращает результат сравнения двух чисел или переменных, для сравнения у нас есть несколько операторов сравнения:

  • == равенство (a == b)
  • != неравенство (a != b)
  • >= больше или равно (a >= b)
  • меньше или равно (a > больше (a > b)
  • меньше (a a и b происходит следующее: скобка “возвращает” логическое значение, которое является результатом сравнения чисел. Например если у нас a = 10 и b = 20 , то скобка (a > b) вернёт значение false , потому что a меньше b . А например (a != b) вернёт true , т.к. а действительно не равно b . Для связи нескольких логических величин используются логические операторы:
  • ! логическое НЕ, отрицание. Есть аналог – оператор not
  • && логическое И. Есть аналог – оператор and
  • || логическое ИЛИ. Есть аналог – оператор or

Результаты операторов сравнения можно использовать для работы с условиями, а также для цикла while (речь о нём пойдёт в следующем уроке)

Сравнение float

Со сравнением float чисел всё не так просто из за особенности самой модели “чисел с плавающей точкой” – вычисления иногда производятся с небольшой погрешностью, из за этого сравнение может работать неверно! Пример из урока про вычисления:

Будьте внимательны при сравнении float чисел, особенно со строгими операциями == , >= и : результат может быть некорректным и нелогичным!

Условный оператор if

Условный оператор if (англ. “если”) позволяет разветвлять выполнение программы в зависимости от логических величин, т.е. результатов работы операторов сравнения, которые мы рассмотрели выше, а также напрямую от логических переменных.

Оператор else (англ. “иначе”) работает в паре с оператором if и позволяет предусмотреть действие на случай невыполнения if :

Также есть третья конструкция, позволяющая ещё больше разветвить код, называется она else if :

Посмотрим на все эти операторы в действии в большом примере:

Вот так и работает условный оператор if , позволяя управлять программой и создавать разветвлённые действия в зависимости от разных условий. Обратите внимание на последний блок в примере выше, там где используется else if для выбора действия в зависимости от значения одной и той же переменной. Существует оператор выбора switch , позволяющий сделать код более красивым. О нём поговорим чуть ниже.

Особенность boolean

В уроке о типах данных я упоминал о том, что boolean принимает значение true , если присвоить ему отличное от нуля число, то есть оператору if можно скормить любое число, и он вернёт true в любом случае, кроме нуля . Это бывает удобно в некоторых случаях, но также может и приводить к ошибкам, которые трудно отловить. if (50) <> – да, выполнится код по этому условию.

Порядок условий

Порядок условий играет очень большую роль при оптимизации кода и попытке сделать его более быстрым в некоторых случаях. Суть очень проста: логические выражения/величины проверяются слева направо, и если хоть одно значение делает всё выражение неверным (ложью), дальнейшая проверка условий прекращается. Например если в выражении

хотя бы а имеет значение false , проверка остальных выражений ( b и c ) уже не выполняется. Когда это может быть важно: например, есть какой-то флаг и выражение, которое вычисляется прямо в условии и сразу проверяется. В таком случае если флаг опущен, микроконтроллер не будет тратить время на лишние вычисления. Например:

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

Оператор ?

Оператор знак вопроса ? является более коротким аналогом для записи конструкции if else . Действие с оператором ? имеет следующий вид:

условие ? выражение1 : выражение2

Это работает так: вычисляется условие, если оно истинно, то вычисляется выражение1 и всё действие получает это значение (возвращает), а если оно ложно, то вычисляется выражение2 и всё действие получает это значение. Пример:

Вот для сравнения аналогичная конструкция на if-else

Аналогичным образом можно использовать оператор ? для вывода данных и текста в последовательный порт (подробнее о нём позже):

А можно ли сделать на операторе ? более сложную конструкцию, типа else if ? Можно!

Оператор выбора

Оператор выбора switch позволяет создать удобную конструкцию, разветвляющую действия в зависимости от значения одной переменной. Синтаксис такой:

Наличие оператора default не обязательно. Наличие оператора break обязательно, иначе сравнение пойдёт дальше, как показано для case 2, 3 и 4. Рассмотрим более живой пример:

При помощи условных операторов и операторов выбора строится логика работы программы. Условный оператор поможет сравнить значение с датчика и принять решение, что делать дальше. Оператор выбора отлично справится с изменяющимися режимами работы программы или опросом кнопок, нажатых на ИК пульте.

Условные директивы #if #else

Помимо директивы #define , сообщающей препроцессору о необходимости замены набора символов набором символов, есть ещё условные директивы, позволяющие заниматься так называемой условной компиляцией: обладая такой же логикой, как if-else, данные конструкции позволяют делать некоторый выбор перед компиляцией самого кода. Отличным примером является само “ядро” Ардуино – большинство функций написаны со спецификой каждого процессора, и перед компиляцией кода из множества вариантов реализации функции выбирается тот, который соответствует текущему выбранному микроконтроллеру. Проще говоря, условная компиляция позволяет по условиям включать или исключать тот или иной код из основной компиляции, т.е. сначала препроцессор анализирует код, что-то в него включается, что-то нет, и затем проходит компиляция.

Также например мы не можем объявить какую-либо константу или макро через #define более одного раза, это приведёт к ошибке. Условная компиляция позволяет сделать ветвящуюся конструкцию, где такое возможно. Для условной компиляции нам доступны директивы #if , #elif , #else , #endif , #ifdef , #ifndef

  • #if – аналог if в логической конструкции
  • #elif – аналог else if в логической конструкции
  • #else – аналог else в логической конструкции
  • #endif – директива, завершающая условную конструкцию
  • #ifdef – если “определено”
  • #ifndef – если “не определено”
  • defined – оператор, который не подсвечивается в коде, но работает так: возвращает true если указанное в скобках слово “определено” через #define , и false – если нет

Как ими пользоваться давайте посмотрим на примере:

Таким образом мы получили задефайненную константу VALUE , которая зависит от “настройки” TEST . Конструкция позволяет включать или исключать куски кода перед компиляцией, вот например кусочек про отладку:

Таким образом при помощи настройки DEBUG можно включить или исключить любой кусок кода.

Есть у препроцессора ещё две директивы: #ifdef и #ifndef , они позволяют включать или исключать участки кода по условию: #ifdef – определено ли? #ifndef – не определено ли? Определено или не определено – речь идёт конечно же о #define

Именно на условной компиляции строится вся универсальность библиотек для Arduino, ведь при выборе автоматически “создаётся” дефайн на название микроконтроллера, выглядят они так:

  • __AVR_ATmega32U4__
  • __AVR_ATmega2560__
  • __AVR_ATmega328P__
  • И далее в этом стиле

Это позволяет создавать универсальный код, используя конструкцию с #ifdef или #if defined :

Таким образом микроконтроллерам (платам Arduino) разных моделей будет доступен персональный кусок кода, который будет передан компилятору при выборе этой платы из списка плат.

Видео