Руководство по интегрированной среде разработки Arduino *
Необходимое оборудование:
- Galileo плата Intel®
- Источник питания (включен в комплект)
- USB-кабель Micro (тип B)
- Установленный и сконфигурированный программный обеспечение Arduino * версии v 1.5.3
Образец эскиза
Когда вы создаете файл в программном обеспечении Arduino *, он открывает набросок с базовой разметкой программы Arduino. Ниже представлен пользовательский интерфейс:
На панели слева направо отображаются значки в верхней части Arduino пользовательского интерфейса, представляющие следующее:
Убедитесь в том, что вы компилируете код. Используйте для проверки кода на наличие ошибок перед отправкой эскиза.
Отправьте набросок.
В новом окне редактора откроется новое окно редактирования кода вместо текущего.
Открывает файл.
Сохранение эскиза.
Последовательный монитор открывает последовательный монитор, который удобен для отладки
Стрелка вниз предоставляет вам такие возможности, как добавление наброска в текущий проект. Она откроется в виде новой вкладки в текущем редакторе кода, которая полезна для организации кода в логические файлы.
В нижней левой части пользовательского интерфейса Arduino отображается номер строки, где находится курсор.
Примечание | Изображение — это программный интерфейс Arduino, озаглавленный Бареминимум, который находится в разделе примеры файлов > > 0,1 основы. Ознакомьтесь с другими примерами и поэкспериментируем. |
Комментарии
Две косые черты (между знаком «{» и «}») представляют начало комментария встроенного кода. После загрузки вашего кода на доску компилятор игнорирует текст после двух косых черт. Комментарий с помощью встроенного кода позволяет оставлять заметок для себя, а также для людей, считывающих ваш программный код. Кроме того, можно писать многострочные комментарии, запустив комментарий с/* и заканчивая знаками */.
/* You are reading an
example of a comment
that has many lines. */
Переменные
Передача данных по всей программе может быстро стать запутанной. Переменные похожи на контейнеры хранилища, содержащие различные типы значений. Использование переменных для передачи значений — это прекрасный способ обеспечить систематизацию и удобочитаемость вашего кода.
При объявлении переменной (ее внедрении в программу) важно выбрать правильный тип данных. Если вы пытаетесь измерить интенсивность света с помощью фотометра, возможно, вам потребуется выполнить точное считывание. Объявление типа переменной для удвоенного размера резервирует место в памяти для числа с десятичной запятой.
Примере: double light_sensitivity;
Где объявляется double
объявляемая переменная, light_sensitivity
которая является именем переменной. Чтобы сослаться на переменную в вашем коде, просто используйте ее имя.
Примечание | Выберите имя переменной, которое относится к тому, на которое вы ссылаетесь. Если название состоит из нескольких слов, используйте знак подчеркивания (_) между словами, чтобы увеличить читаемость. |
Для получения дополнительной информации о типах данных и переменных перейдите на страницу справочника по Arduino.
Функции
Два компоновочных блока наброска — функция установки и функция Loop . Для работы всех программ требуется использование этих двух функций, поскольку они являются необходимыми структурами для компилируемой программы.
Функция установки — это где вы можете добавить такие вещи, как объявления переменных и инициализации режимов закрепления.
Функция Loop — это сердце вашей программы. В этом поле представлено название, которое будет циклически непрерывно работать с основной логикой программы.
Точно так же, как у переменных, функции бывают разных типов. Функции setup и Loop имеют тип void. Это означает, что они выполняют только те задачи, на которые они указывают, и не возвращают значения (таким образом, void). Функции, возвращающие значения, обсуждаются на следующих занятиях.
Введение
Приступая к работе
Интегрированная среда разработки Arduino
Всем привет
Переключить вверх
Прерывания и многозадачность в Arduino
#include <Servo.h>
class Flasher
{
// Переменные-участники класса устанавливаются при запуске
int ledPin; // Номер контакта со светодиодом
long OnTime; // длительность ВКЛ в мс
long OffTime; // длительность ВЫКЛ в мс
// Контроль текущего состояния
int ledState; // устанавливает текущее состояние светодиода
unsigned long previousMillis; // время последнего обновления состояния светодиода
// Конструктор — создает объект Flasher, инициализирует переменные-участники и состояние
public:
Flasher(int pin, long on, long off)
{
ledPin = pin;
pinMode(ledPin, OUTPUT);
OnTime = on;
OffTime = off;
ledState = LOW;
previousMillis = 0;
}
void Update(unsigned long currentMillis)
{
if((ledState == HIGH) && (currentMillis — previousMillis >= OnTime))
{
ledState = LOW; // ВЫКЛ
previousMillis = currentMillis; // Запомнить время
digitalWrite(ledPin, ledState); // Обновить состояние светодиода
}
else if ((ledState == LOW) && (currentMillis — previousMillis >= OffTime))
{
ledState = HIGH; // ВКЛ
previousMillis = currentMillis; // Запомнить время
digitalWrite(ledPin, ledState); // Обновить состояние светодиода
}
}
};
class Sweeper
{
Servo servo; // объект servo
int pos; // текущее положение сервопривода
int increment; // определяем увеличение перемещения на каждом интервале
int updateInterval; // определяем время между обновлениями
unsigned long lastUpdate; // определяем последнее обновление положения
public:
Sweeper(int interval)
{
updateInterval = interval;
increment = 1;
}
void Attach(int pin)
{
servo.attach(pin);
}
void Detach()
{
servo.detach();
}
void reset()
{
pos = 0;
servo.write(pos);
increment = abs(increment);
}
void Update(unsigned long currentMillis)
{
if((currentMillis — lastUpdate) > updateInterval) //время обновиться
{
lastUpdate = millis();
pos += increment;
servo.write(pos);
if ((pos >= 180) || (pos <= 0)) // инициализируем конец вращения
{
// инициализируем обратное направление
increment = -increment;
}
}
}
};
Flasher led1(11, 123, 400);
Flasher led2(12, 350, 350);
Flasher led3(13, 200, 222);
Sweeper sweeper1(25);
Sweeper sweeper2(35);
void setup()
{
sweeper1.Attach(9);
sweeper2.Attach(10);
// Timer0 уже используется millis() — прерываемся примерно посередине и вызываем ниже функцию «Compare A»
OCR0A = 0xAF;
TIMSK0 |= _BV(OCIE0A);
pinMode(2, INPUT_PULLUP);
attachInterrupt(0, Reset, FALLING);
}
void Reset()
{
sweeper1.reset();
sweeper2.reset();
}
// Прерывание вызывается один раз в миллисекунду, ищет любые новые данные, и если нашло, сохраняет их
SIGNAL(TIMER0_COMPA_vect)
{
unsigned long currentMillis = millis();
sweeper1.Update(currentMillis);
// if(digitalRead(2) == HIGH)
{
sweeper2.Update(currentMillis);
led1.Update(currentMillis);
}
led2.Update(currentMillis);
led3.Update(currentMillis);
}
void loop()
{
}
Структура и синтаксис языка ArduinoIDE | Учи Урок информатики
setup()
Описание
Пример
1 2 3 4 5 6 7 8 9 10 11 12 |
int buttonPin = 3; void setup() { Serial.begin(9600); pinMode(buttonPin, INPUT); } void loop() { // ... } |
к содержанию ->>
loop()
Описание
При этом вызов loop() в точности соответствует ее названию — после завершения выполнения ее тела, она будет вызываться снова и снова — и так до бесконечности. Таким образом обычно обеспечивается «активная» часть скетча, анализирующая состояние платы и выполняющая соответствующие действия.
Пример
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
int buttonPin = 3; // setup инициализирует последовательный порт и кнопку void setup() { beginSerial(9600); pinMode(buttonPin, INPUT); } // в цикле проверяется состояние кнопки, // в последовательный порт будет отправлено сообщение, если она нажата void loop() { if (digitalRead(buttonPin) == HIGH) serialWrite('H'); else serialWrite('L'); delay(1000); } |
к содержанию ->>
; точка с запятой
Описание
Используется для завершения строки.
Пример
В случае, если вы забыли поставить точку с запятой в конце строки, то это скорее всего приведет к ошибке компиляции.
Компиляция — трансляция программы, составленной на исходном языке высокого уровня, в эквивалентную программу на низкоуровневом языке, близком машинному коду. Входной информацией для компилятора (исходный код) является описание алгоритма или программа на объектно-ориентированном языке (например ArduinoIDE), а на выходе компилятора — эквивалентное описание алгоритма на машинно-ориентированном языке (0101000111100100010011111100010101). к содержанию ->>
{} фигурные скобки
Описание
Фигурные скобки (часто их называют просто «скобки») — важная конструкция языка программирования C. Иногда они вызывают затруднения у начинающих, поэтому их использование будет проиллюстрировано ниже.
Открывающая фигурная скобка «{» должна всегда иметь соответствующую закрывающую — «}«. ArduinoIDE имеет специальную возможность проверки парности скобок. Для этого выделите скобку или установите курсор сразу за ней — и тогда будет подсвечена её пара.
Поскольку фигурные скобки используются во многих случаях, хорошем стилем считается печатать закрывающую сразу после открывающей (что автоматически выполняется в CodeBlocks например). Затем можно вернуться на символ назад, вставить новую строку между между ними, где уже и писать операторы. Таким образом, вы никогда не создадите ситуацию с «непарной» скобкой.
Скобки весьма важны с синтаксической точки зрения, и перемещение скобки на одну или две строки может привести к значительному воздействию на выполнение программы.
{} — используются для обхвата так сказать кода внутри этих скобок к чему либо (функции/классу/условию/циклу и т.д. и т.п). Фигурные скобки «открывают» и «закрывают» ту часть кода, которая относится к некоторой функции/классу/условию/циклу и т.д. и т.п.
Пример
1 2 3 4 5 6 |
void print(){ Serial.print("Hello WORLD!"); } void setup(){ F(); } |
к содержанию ->>
Комментарии
Описание
Комментарии — это фрагменты программы, в которых обычно записывают пояснения о том, что она делает. Они полностью игнорируются компилятором, и поэтому ничего не занимают в памяти ATmega после загрузки скетча.
Компилятор — Компьютерная программа, выполняющая компиляцию (ArduinoIDE например).
Основная функция комментариев — помочь разобраться (или вспомнить), как работает программа или объяснить другим, что она делает. Существует два разных стиля написания комментариев:
Пример
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
int x; int gwb=10; void setup(){ x = 5; // Это - однострочный комментарий. Всё, что после двух слэшей - комментарий, // до конца строки /* это - многострочный комментарий - можно использовать для "комментирования" фрагментов программы if (gwb == 10){ // однострочный комментарий - это нормально внутри многострочного x = 3; } /* но многострочный комментарий не допускает вложенности - это не будет компилироваться */ Serial.print(x) //будет отображено 5 а не 3 } // не забудьте "зкарывающий" знак комментария - они должны быть сбалансированы |
к содержанию ->>
#define
Описание
#define часто используется для определения значения имен констант до начала компиляции программы. Константы, которые определены таким образом, ничего не занимают в памяти микроконтроллера — компилятор подставит значения вместо имен во время компиляции.
Работа команды #define string1 string2 сравнима с операцией НАЙТИ и ЗАМЕНИТЬ в любом текстовом редакторе. До компиляции программы среда ArduinoIDE находит в тексте программы строку string1 и заменяет ее на string2
Однако, при использовании возможны побочные нежелательные эффекты — например, если имя определенной через #define константы вдруг входит хотя бы частично в имя другой переменной, то в этом случае произойдет ошибочная замена правильного части имени числом или текстом.
В общем случае, для объявления констант следует использовать ключевое слово const
Синтаксис
1 |
#define constantName value |
1 |
#define ledPin 10; // это ошибка |
1 |
#define ledPin = 3 // это тоже ошибка |
к содержанию ->>
#include
Описание
#include используется для подключения к скетчу внешних библиотек. Это дает возможность программисту не только использовать обширный инструментарий стандартных библиотек C, но и подключать библиотеки Arduino.
Помните, что в #include, также как и в #define, не требуется указывать завершающую точку с запятой, во избежании генерации трудночитаемых ошибок компилятора.
Пример
Этот пример подключает библиотеку, которая позволяет управлять Сервомоторами.
1 2 3 4 5 6 |
#include <Servo.h> //"подключаем" библиотеку для управления сервомоторами Servo myservo; // создать объект Servo // 12 объектов Servo может быть создано на одной плате int pos = 0; // переменная pos приравнивается к нулю |
к содержанию ->>
Пожалуйста, оцените статью
4.2 из 5. (Всего голосов:260)
Tweaking4All.com — Программирование Arduino для начинающих
Обзор этой главы
Полный обзор этого курса можно найти здесь: Обзор курса .
Что такое функции и зачем они нужны?
Теоретически нам не нужны функции… однако без функций наш код стал бы очень длинным и совершенно нечитаемым. Не считая; нам нужно было бы написать намного больше кода.
Мы уже работали с 2 функциями, которые необходимы для вашего Arduino: «setup ()» и «loop ()».
Но что такое функции и зачем они нужны? Или лучше сказать: почему они нам нравятся?
Функцию (также называемую подпрограммой ) можно рассматривать как набор инструкций, сгруппированных вместе, с учетом конкретной задачи.
Мы можем создать функцию, чтобы сделать наш код более читабельным, или потому, что мы хотим использовать эту конкретную задачу в нескольких местах нашей программы (программ).
На некоторых языках это называется «подпрограмма», потому что так оно и есть; рассматривайте это как отдельную небольшую программу.И, как и в случае с обычной программой, функция может иметь другие функции, определенные внутри нее, и это подводит нас к другой проблеме, на которую следует обратить внимание: функция имеет область видимости, как и в случае с переменными, — «область», в которой она может быть увиденным и использованным!
Я понимаю, что это немного сложно понять сразу, но об этом нужно помнить.
Мы можем определить функции, которые действительно возвращают результат (значение) или не возвращает результат .
Последний, не возвращающий результат, в других языках программирования называется «процедурой».Однако в языке C оба они называются просто «функцией».
Функция — это группа инструкций для определенной задачи.
Когда мы определяем нашу функцию?
- Когда код повторяется более одного раза в моей программе
- Если мой код станет на более читабельным с функциями
- Если мой код станет на более управляемым с функциями
- Если мы хотим повторно использовать фрагмент кода , например, в других программах
Давайте посмотрим на примере того, чем может быть функция — просто для понимания концепции.
Допустим, у нас есть собака, которую нужно выгуливать 4 раза в день: в , 8 утра, , , 12 вечера, , , 5 вечера, , и в , 9 вечера, .
Задача выгула собаки включает:
— Надеть пальто
— Надеть собаку на поводок
— Выйти на улицу
— Прогуляться по парку 15 минут
— Вернуться внутрь
— Снять поводок dog
— Снимите пальто.
Теперь предположим, что наша программа справляется с повседневными задачами, когда дело доходит до выгула собаки:
, если 8 утра, то
— Наденьте пальто
— Наденьте собаку на поводок
— Выйдите на улицу
— Пройдите по парку 15 минут
— Вернитесь внутрь
— Снимите поводок с собаки
— Возьмите с пальто.
, если 12:00, то
— Наденьте пальто
— Наденьте собаку на поводок
— Выйдите на улицу
— Пройдите по парку 15 минут
— Вернитесь внутрь
— Снимите поводок с собаки
— Возьмите с пальто.
, если 17:00, то
— Наденьте пальто
— Наденьте собаку на поводок
— Выйдите на улицу
— Пройдите по парку 15 минут
— Вернитесь внутрь
— Снимите поводок с собаки
— Возьмите с пальто.
, если 21:00, то
— Наденьте пальто
— Наденьте собаку на поводок
— Выйдите на улицу
— Пройдите по парку 15 минут
— Вернитесь внутрь
— Снимите поводок с собаки
— Возьмите с пальто.
Наша программа довольно длинная, правда? И еще много повторяющегося кода…
Мы могли бы создать функцию, назовем ее «WalkTheDog ()» и определить ее следующим образом.
WalkTheDog ():
— Наденьте пальто
— Наденьте собаку на поводок
— Выйдите на улицу
— Прогуляйтесь по парку 15 минут
— Вернитесь внутрь
— Снимите поводок с собаки
— Возьмите с пальто.
Теперь наша программа будет выглядеть намного проще:
если 8:00, то
WalkTheDog ()
, если 12:00, то
WalkTheDog ()
если 17:00, то
WalkTheDog ()
если 21:00, то
WalkTheDog ()
Вы видите, что не только наш код стал намного короче, но и стал более читабельным. — «функция» вызывается так, как если бы это был обычный оператор.Фактически мы расширили наш «язык».
В большинстве сценариев использование ваших собственных функций приведет к также , что приведет к уменьшению на скомпилированной программы для вашего Arduino или компьютера, что позволит более эффективно использовать память, необходимую на вашем Arduino (или компьютере).
Но функции имеют также то преимущество, что если вы допустили ошибку на шагах выгула собаки, вам нужно изменить свой код только в одном месте: функции, которая делает ваш код более управляемым .Например, если мы забыли добавить «Разблокировать дверь» в качестве шага. Мы просто редактируем функцию WalkTheDog () вместо того, чтобы редактировать код в 4 разных местах в предыдущем коде.
Теперь, если у нас есть функция более общего назначения, мы можем даже поместить ее в так называемую библиотеку и повторно использовать функцию в других наших программах. Но подробнее о «библиотеках» в следующей части.
Обнаружена блокировка рекламыИмейте в виду, что:
- Функция подобна отдельной маленькой программе … внутри программы
- Функция может иметь внутри функции …
- Функция имеет « область » — как мы видели с переменными и константами.
Пожалуйста, отключите блокировку рекламы на нашем веб-сайте.
Мы полагаемся на эти объявления, чтобы иметь возможность запускать наш веб-сайт.
Конечно, вы можете поддержать нас и другими способами (см. Раздел «Поддержка нас» слева).
Создание собственных функций
Довольно легко определить наши собственные функции на языке C, который мы используем для программирования Arduino. Базовая структура выглядит так:
тип данных FunctionName (FunctionParameters) {
// код функции
}
Надеюсь, вы помните некоторые из DataTypes , которые мы упоминали в Части 3 — DataTypes.Если нет, не волнуйтесь, вы можете оглянуться назад, если хотите, но я также упомяну здесь некоторые детали.
Тип данных определяет, какие данные возвращаются функцией, и, к сожалению, не все типы данных подходят для использования в качестве возвращаемого значения для функций. Определенно избегайте массивов!
Наиболее распространенные типы данных для возвращаемого значения: boolean , int , char , float и void (другие типы также будут работать, например, double, long и unsigned).
Очевидно, что нашей функции требуется FunctionName , и нам нужно следовать тем же правилам именования, что и для переменных и констант:
Название функции :
- Должен начинаться с a буква (a, b,…, z, A, B,…, Z) или подчеркивание (_)
- Может содержать букв
- Может содержать знаков подчеркивания (с)
- Может содержать чисел
- НЕ МОЖЕТ содержать специальные символы, символы или пробелы
- — с учетом регистра !
Параметры функции — это ноль или более значений, которые мы хотим передать функции.Однако количество значений и тип данных этих значений фиксировано и определено как в нашем определении функции!
В некоторых диалектах C вам нужно будет «объявить» (объявить) или «определить» функцию, прежде чем она будет использована в коде. Компилятору необходимо «знать» о его существовании, прежде чем он сможет его использовать. Это похоже на , а не на , как в случае с C на Arduino, поэтому здесь он не рассматривается.
Мы использовали параметры функций и раньше, хотя вы могли не знать об этом полностью.Эти значения мы передаем в скобки, например, в этой строке: Serial.print ("Hello");
Здесь мы вызываем функцию «print ()» из объекта «Serial» и передаем параметр «Hello» (строка). Подробнее об объектах позже.
Следующий блок кода, между похвалами, — это то, что мы видели раньше, например, в функции «if» и различных типах циклов («for», «while» и «do… while…»).
Этот блок кода группирует инструкции для нашей функции.
Начнем с простой функции:
1 | void SayHello () { |
Эта функция, называемая SayHello (), принимает без параметров , поскольку между скобками () нет ничего.
Он также не возвращает значение, поскольку мы использовали специальный тип данных «void», что означает «ничего, разреженный воздух, нада».
«Тело» функции (блок кода) содержит инструкции для этой функции и просто выводит «Hello».
Пример того, как мы будем использовать это:
1 | пустая установка () { // устанавливаем скорость для последовательного монитора: Serial.begin (9600); для (int A = 1; A <= 5; A ++) { Скажи привет(); } } void loop () { void SayHello () { |
Большая часть этого должна показаться знакомой, я все равно надеюсь…
В конце кода вы видите, как мы определили нашу функцию «SayHello ()».
В цикле «for» вы видите, как мы вызываем нашу функцию 5 раз, что дает следующий результат:
Привет
Привет
Привет
Привет
Привет
Легко, правда?
Передача значения функции
Это был довольно простой пример, давайте рассмотрим пример, в котором мы фактически передаем параметр, где мы используем предыдущий пример включения 5 источников света с помощью цикла «for».
Для этого мы создаем новую функцию под названием «DoLights», которой мы хотим передать номер источника света, который необходимо включить.
Мы уже знаем, что это число является целым числом типа «int» (см. Также определение «A» в цикле «for»).
Мы также знаем, что он не вернет никаких значений, поэтому мы получаем эту функцию:
void DoLights (int LightNumber) {
Serial.print («Включить свет для света»);
Серийный.println (LightNumber);
}
Параметр «LightNumber» определяется как «int». Вы можете видеть, что есть определение переменной «LightNumber» внутри функции «DoLights». Когда мы передаем значение для этого параметра, значение будет скопировано в эту «новую» переменную.
Имея в виду «область действия», «LightNumber», очевидно, существует только в функции «DoLights».
Вызов нашей функции (строка 6) точно такой же, как и в предыдущем примере, однако на этот раз мы передаем значение переменной «A».Значение «A» копируется в переменную «LightNumber» — помните, что оно КОПИРОВАННЫЙ.
Всего этого вместе:
1 | пустая установка () { // устанавливаем скорость для последовательного монитора: Serial.begin (9600); для (int A = 1; A <= 5; A ++) { DoLights (A); } } void loop () { void DoLights (int LightNumber) { |
Вы видите, как переменная «LightNumber» используется в функции?
Но это был всего лишь пример того, как передать только одно значение. Как это работает, если нам нужно передать несколько значений?
В качестве иллюстрации мы добавим логическое значение, которое мы хотим передать нашей функции. Если это значение , истинно , тогда свет должен загореться, если ложь , свет должен погаснуть.
Для этого нам нужно разделить два значения, и для этого мы используем запятую (,).
Параметры в функции разделяются запятой,
как при определении функции, так и при ее вызове.
Как и для любого значения, которое мы хотели бы передать, нам снова нужно определить его тип данных (логическое) и имя (LightOn).
Я добавил оператор «if», чтобы функция могла включать и выключать свет — еще одна интересная особенность функций; мы можем написать их таким образом, чтобы они могли работать более чем для одного сценария.
Всего этого вместе:
1 | пустая настройка () { // устанавливаем скорость для последовательного монитора: Serial.begin (9600); для (int A = 1; A <= 5; A ++) { DoLights (A, истина); } для (int A = 1; A <= 5; A ++) { DoLights (A, ложь); } } void loop () { void DoLights (int LightNumber, boolean LightOn) { |
Как видите, сначала мы запускаем цикл for из 5 итераций, включая 5 индикаторов.
Значения, которые мы передаем, также разделяются запятой!
В следующем цикле for, также в 5 итерациях, мы выключаем свет.
Ваш результат должен выглядеть так:
Включите фары для света 1
Включите фары для света 2
Включите фары для света 3
Включите фары для света 4
Включите фары для света 5
Выключите фары для света 1
Выключите фары для света 2
Выключить фары для света 3
Выключить фары для света 4
Выключить фары для света 5
Очевидно, что приведенные здесь примеры не очень хороши, когда дело доходит до более эффективного начала.Но когда вы начнете создавать свои первые, более крупные программы, вы увидите, насколько функции полезны для программиста.
Обнаружена блокировка рекламы Пожалуйста, отключите блокировку рекламы на нашем веб-сайте.
Мы полагаемся на эти объявления, чтобы иметь возможность запускать наш веб-сайт.
Конечно, вы можете поддержать нас и другими способами (см. Раздел «Поддержка нас» слева).
Возврат значения из функции
Итак, мы знаем, что функция может «получать» значения с помощью параметров. А что, если мы хотим, чтобы функция возвращала ответ или результат — например, из сложного вычисления?
Помните, мы использовали «void» в наших предыдущих определениях функций? Здесь мы определяем, каким будет возвращаемый результат.
Однако в самой функции нам действительно нужно «вернуть» этот тип значения, и для этого мы используем функцию « return ».
Если вы определяете функцию, которая будет возвращать определенный тип данных, вам нужно будет использовать оператор « return » для возврата значения, которое должно иметь тот же тип данных, что и определенный тип возвращаемого значения вашего функция…
В качестве примера можно привести один из предыдущих примеров, в котором мы вычислили «AllMyMoney», добавив то, что находится в нашем кошельке, и что находится в банке.Довольно тривиальная ситуация, поскольку мы можем вычислить это уже более эффективно, не создавая функции, но это хорошая иллюстрация. Очевидно, что как только вы начнете писать свои собственные программы, эти функции будут содержать гораздо больше и более сложный код, чем этот.
Итак, мы создали функцию «CalculateMyMoney», которая принимает два параметра. Я намеренно назвал их по-другому, чтобы проиллюстрировать, что значения двух переменных (PocketMoney и Savings) КОПИРУЮТСЯ в имена переменных, которые мы определили в функции.Было бы хорошо дать именам переменных параметров одинаковые имена, если вы вспомните «область действия» переменных: переменные в функции не являются теми же переменными, что и переменные вне функции.
В функции мы складываем эти 2 значения и возвращаем результат «Итого». Это «возвращаемое» значение затем присваивается переменной «AllMyMoney».
Попробуйте этот код.
1 | пустая настройка () { // устанавливаем скорость для последовательного монитора: Серийный.begin (9600); // определяем наши переменные int PocketMoney; int Экономия; int AllMyMoney; // присваиваем значения // выводим значения на серийный монитор Serial.print ("Экономия ="); Serial.print ("AllMyMoney ="); void loop () { int CalculateMyMoney (int Wallet, int Bank) { Итого = кошелек + банк; возврат Итого; |
Функция с возвращаемым значением может рассматриваться как переменная. Так что везде, где мы можем использовать значение или переменную, мы также можем использовать функцию, которая возвращает значение, что я проиллюстрирую в этой слегка измененной версии.Посмотрите на строку 21… мы только что поместили вызов функции прямо туда как «параметр» для « Serial.println ()
»… и это работает! Таким образом, нам не нужно сначала назначать возвращаемое значение переменной.
Если вам нужен результат функции чаще, то сохранение его в переменной имеет смысл. В конце концов: вызов функции приведет к повторному выполнению ее кода, что будет лишним, если нам понадобится один и тот же ответ более одного раза.
Функция с возвращаемым значением может заменять обычное значение или переменную…
Каждый раз, когда мы вызываем функцию, выполняется ее код.Поэтому, если вам нужен результат более одного раза, подумайте о том, чтобы сохранить значение в переменной вместо повторного вызова функции.
1 | пустая настройка () { // устанавливаем скорость для последовательного монитора: Серийный.begin (9600); // определяем наши переменные int PocketMoney; int Экономия; // присваиваем значения // выводим значения на серийный монитор Serial.print ("Экономия ="); Serial.print ("AllMyMoney ="); void loop () { int CalculateMyMoney (int Wallet, int Bank) { Итого = кошелек + банк; возврат Итого; |
Здесь я настоятельно рекомендую поиграть с функциями.Создайте функцию самостоятельно, с возвращаемым значением или без него, и поэкспериментируйте.
Функции, вызывающие себя (рекурсия)
Этот абзац может показаться слишком сложным для начинающих — не стесняйтесь пропустить его, если вам кажется, что он для вас слишком сложен.
Функция может вызывать сама себя, это называется «рекурсией».
Вы, должно быть, думаете, что мы оказались в сумасшедшем городе — ну, не волнуйтесь, если рекурсия для вас слишком сложна. Это многовато для любого новичка и даже для опытных программистов.
Итак, в этом курсе мы оставим все как есть: то, что вы не будете использовать, пока не наберетесь опыта в программировании.
Recursion может быть очень мощным инструментом, позволяющим писать ограниченный объем кода, но при этом давать потрясающие результаты. Например, определенная графика может быть сгенерирована с помощью рекурсии, как дерево ниже или так называемый треугольник Серпинского. (Источник изображения: Википедия — Дерево Brentsmith201 , Серпинский Wereon ).
Они нарисованы с помощью рекурсии.
Рекурсивное дерево
Треугольник Серпинского — Треугольник в треугольнике
Если вам все равно интересно… вот быстрый и простой пример, который менее сложен, чем два изображения выше.
Допустим, мы хотим сложить все целые числа для всех чисел от 1 до 5. Итак, мы хотим знать, сколько равно 1 + 2 + 3 + 4 + 5 (15).
Очевидно, мы можем сделать это очень просто, введя это вручную или используя цикл «for», но сегодня мы хотим усложнить задачу, поэтому используем этот сценарий в качестве примера рекурсии.
Мы собираемся создать функцию, которая начинается с «5» и складывает число под ним (4), затем прибавляет число под ним (3) и т. Д., Пока мы не добавим 1.
1 | int AddOneLess (int Number) { |
Итак, что делает эта функция, берет значение «Число».Если это число равно нулю, верните ноль. Если число не равно нулю, возьмите это число и вызовите функцию «AddOneLess» для числа минус 1.
Странно, правда?
Другими словами: эта функция будет начинаться с заданного числа и каждый раз добавлять предыдущее число, пока оно не достигнет нуля (0).
Позвольте мне отобразить это по-другому, используя этот пример кода:
1 | пустая установка () { // устанавливаем скорость для последовательного монитора: Серийный.begin (9600); Serial.println (AddOneLess (5)); } void loop () { int AddOneLess (int Number) { |
Сначала мы вызываем AddOneLess (5).
Значение «Number» не равно нулю, поэтому мы делаем 5 + «AddOneLess (5-1)», и еще НЕТ возвращаемого значения, так как мы перешли к вызову функции AddOneLess (5-1).
В этом втором вызове значение Number равно 4, но все еще не равно нулю, поэтому мы добавляем 4 + AddOneLess (4-1) и снова вызываем AddOneLess, так что возвращаемого значения пока нет.
Это приводит к третьему вызову, в котором значение «Number» равно 3, но все еще не равно нулю, поэтому мы добавляем 3 + «AddOneLess (3-1)» — возвращаемого значения пока нет.
Это приводит к четвертому вызову, в котором значение «Number» равно 2, но все еще не равно нулю, поэтому мы добавляем 2 + «AddOneLess (2-1)» — возвращаемого значения еще нет.
Это приводит к пятому вызову, в котором значение «Number» равно 1, но все еще не равно нулю, поэтому мы добавляем 1 + «AddOneLess (1-1)» — возвращаемого значения еще нет.
Шестой вызов, однако, значение «Number» равно 0 (ноль), поэтому мы возвращаем «ноль».
Однако… мы возвращаем ноль в «области действия» пятого вызова, мы вернулись на один шаг назад, где значение «Number» равно 1, и где мы говорим: вернуть значение «Number» и возвращаемое значение вызов «AddOneLess (1-1)».
Таким образом, пятый вызов возвращает 1 (значение «Number») + 0 (возвращаемое значение AddOneLess (1-1)) = 1.
Теперь мы возвращаемся в область действия четвертого вызова, где «Число» имело значение 2 и которое возвращает значение «Число» + возвращаемое значение «AddOneLess (2-1)», так что: 2 + 1 = 3.
Возвращаясь к области действия третьего вызова, «Число» имеет значение 3, а возвращаемое значение = 3 + 3 (возврат из «AddOneLess (3-1)») = 6.
При возвращении ко второму вызову «Число» принимает значение 4, поэтому возвращаемое значение = 4 + 6 = 10;
И, возвращаясь к исходному вызову, «Number» равно 5, а результат «AddOneLess (5-1)» стал 10. Таким образом, этот вызов возвращает 5 + 10 = 15.
Красиво и запутанно, правда?
Может, имеет смысл показать это так:
1 | 1-й вызов: AddOneLess (5) // в функции setup () 2-й вызов: AddOneLess (4) // в вызове функции AddOneLess (5) 3-й вызов: AddOneLess (3) // в вызове функции AddOneLess (4) 4-й вызов: AddOneLess (2) // в вызове функции AddOneLess (3) 5-й вызов: AddOneLess (1) // в вызове функции AddOneLess (2) 6-й вызов: AddOneLess (0) // в вызове функции AddOneLess (1) Number теперь будет равно нулю! 5-й звонок: возврат = номер + результат 6-й звонок = 1 + 0 = 1 4-й звонок: возврат = номер + результат 5-й звонок = 2 + 1 = 3 3-й звонок: возврат = номер + результат 4-й звонок = 3 + 3 = 6 2-й звонок: возврат = номер + результат 3-й звонок = 4 + 6 = 10 1-й звонок: возврат = номер + результат 5-й звонок = 5 + 10 = 15 Окончательный возврат: = 15 |
Я знаю, что это сложно понять в первый раз, так что не беспокойтесь, если это вам не по зубам.Мне действительно пришлось серьезно подумать, чтобы даже поместить это объяснение здесь, но для всей полноты я все равно добавил его. Просто помните, что функция может вызывать сама себя и не мешает предыдущим вызовам. Каждый вызов функции получит свою «область видимости».
Просто практическое правило, которое следует иметь в виду при использовании рекурсии: всегда убедитесь, что в функции есть условие «выхода», чтобы функция не продолжалась вечно и не приводила к сбою вашего Arduino или компьютера … в конце концов, для каждого вызова область видимости хранится в памяти, и ваш Arduino имеет ограниченное количество этого.
При создании Рекурсивных функций :
ВСЕГДА убедитесь, что функция имеет условие « exit » для выхода из функции.
Если у вас есть вопросы, просто задавайте их ниже, в разделе комментариев, и имейте в виду: глупых вопросов не бывает! В какой-то момент нам всем пришлось начать!
Следующая глава: Программирование Arduino для начинающих — Часть 7: Строки
Setup & Loop — — Проект модульной электроники Arduino
! Введение
Начнем с основ.В этом первом руководстве показаны первые концепции программирования Arduino.
Существуют тысячи различных программ, написанных специально для Arduino. Однако все они имеют одинаковую структуру с двумя основными разделами, называемыми «Настройка» и «Цикл».
! Анатомия программы для Arduino
Когда вы открываете программу Arduino, появляется следующий экран с. В нем мы можем заметить разделы Setup и Loop:
Программа для Arduino
! Установка
Setup — обязательный раздел программы Arduino.Объявление функции Setup выполняется следующим образом:
установка void () { // Строки установочного кода }
Любой код, который находится внутри setup ()
, то есть между фигурными скобками ({}), выполняется только один раз в начале вашей программы.
Эта функция полезна для настройки Arduino. Именно здесь, например, вы устанавливаете начальные конфигурации, например, если светодиод начинает светиться или загораться, что, помимо прочего, является входным и выходным контактами.Вы поймете эти концепции в следующих уроках.
Вы можете подробнее изучить функцию setup ()
на странице Arduino.
! Петля
Как и раздел «Настройка», Loop также является обязательным в программе Arduino. Его декларация оформляется следующим образом:
пустой цикл () { // Строки кода цикла }
Большая часть вашего кода будет выполняться внутри этого раздела. После выполнения setup ()
запускается цикл ()
.
Программа запускается сразу после открывающей фигурной скобки ( {), и процессор выполняет строки кода, пока не дойдет до закрывающей фигурной скобки (} ). Оказавшись в конце, он возвращается к первой строке цикла и начинается заново.
Функция loop ()
будет выполняться вечно или до тех пор, пока вы не загрузите новый код, перезапустив процесс. Его также можно перезапустить, сбросив Arduino (например, с помощью кнопки сброса).
Вы также можете узнать больше о функции loop ()
на странице Arduino.
delay () Функция Arduino: жесткие циклы и код блокировки
Вы когда-нибудь создавали проект Arudino и хотели бы, чтобы что-то происходило в определенный промежуток времени? Может быть, каждые 3 секунды вы хотите, чтобы сервопривод перемещался, или, может быть, каждые 1 минуту вы хотите отправлять обновление статуса на веб-сервер.
Как вы это делаете? Есть ли простая и понятная функция, которая поможет нам в этом? Да, есть! Мы обсудим функцию задержки, а также функцию millis (), в видео ниже:
Это вторая часть нашей мини-серии о функциях millis ().Часть 1 помогает нам понять, что делает функция millis (), часть 2 обсуждает жесткие циклы и код блокировки, а часть 3 обсуждает, когда функция millis () затмевает функцию delay ().
Темы этого урока
- Петли тугие
- Код блокировки
- Древний рецепт вдохновляющей музыки
Миллис () по сравнению с задержкой ()
Итак, вы хотите, чтобы что-то происходило с заданным интервалом, и вы ищете решение. Если в вашем ответе используется функция задержки, то вы правы.Но есть другой способ.
Более крутым и красивым вариантом является функция Arduino millis (). И чем больше вы знакомы с использованием функции millis, которая помогает вам определять время событий в коде Arduino, тем легче будет позже включить другие части в вашу программу.
Как только вы начнете использовать функцию millis (), вы станете счастливее, веселее, и, черт возьми, вы понравитесь людям.
Узкие петли
Сначала давайте обсудим концепцию замкнутой петли.И когда мы говорим о замкнутой петле, что это означает?
Давайте взглянем на скетч Arduino для демонстрации замкнутого цикла. Начиная с самого простого скетча, у нас есть только две функции: настройка void и цикл void. И, как вы, возможно, знаете, установка void запускается только один раз, а затем передает шоу в цикл void.
Цикл Void затем просматривает каждую строку кода, которая может находиться внутри цикла (внутри фигурных скобок).
Он выполняет первую строку, затем выполняет вторую, а затем третью, и так далее и так далее, пока не дойдет до конца.И затем он возвращается наверх.
Как быстро он выполняет цикл? Это зависит от того, какую плату Arduino вы используете, но тактовая частота Arduino Uno составляет 16 мегагерц. Это означает, что каждую секунду на Arduino выполняется 16 миллионов инструкций!
Каждая строка кода не обязательно является одной инструкцией. На самом деле, скорее всего, это несколько инструкций. Но все же это относительно быстро (процессор вашего компьютера, вероятно, работает на гигагерцовых скоростях… это миллиарды).
Так вы считаете этот пустой набросок тесной петлей? Определенно, это так быстро и плотно, как вы можете сделать петлю. Поскольку внутри цикла нет ничего для выполнения, время, необходимое для прохождения эскиза, практически ничтожно. Другими словами, интервал от начала петли до финиша короткий (следовательно, быстрый или «плотный»).
Давайте добавим несколько строк кода. Мы начнем последовательную связь, а затем что-нибудь напечатаем в окне последовательного монитора.
Это тугая петля? То есть, от начала до конца цикла, это занимает много времени? Это занимает очень мало времени, так что это быстрый и жесткий цикл.
Однако стоит отметить, что этот цикл не такой плотный, как в предыдущем примере. В предыдущем примере у нас не было кода. Так что это просто мчалось по кругу. Теперь, когда у нас есть функция, последовательная печать, потребуется (крошечное) время, чтобы напечатать «Ice Ice Baby» на последовательном мониторе.
Но это все еще довольно быстрый цикл. Итак, давайте добавим еще немного кода. Программа будет проверять, нажата ли кнопка, и если это так, мы отправим что-то новое на монитор последовательного порта
.
Итак, мы объявили кнопку и использовали оператор if, чтобы проверить, была ли нажата кнопка.Высокое напряжение на пятом контакте? Если да, то мы выводим что-нибудь еще в окно монитора последовательного порта.
Это тугая петля? Итак, от начала цикла до конца, это довольно быстро?
Да, все еще довольно быстро. Это довольно сложная петля. У нас есть четыре строчки кода. Мы выполняем печать на монитор последовательного порта, а затем быстро проверяем, не нажата ли кнопка. А если есть, то что-то распечатываем. По-прежнему плотно, по-прежнему быстро.
Теперь давайте добавим задержку в эту программу, используя функцию Arduino delay ().Как видно ниже, мы добавили в цикл задержку в тысячу миллисекунд (1 секунду).
Это все еще замкнутая петля? Много ли времени от начала до конца цикла? Нет, это определенно не замкнутая петля. Код запускается быстро, мы выполняем серийную печать, но затем останавливаемся прямо на функции задержки.
Вся программа останавливается, пока мы ждем завершения этого кода задержки.
Когда Arduino доходит до этой строки кода, это похоже на поход в продуктовый магазин.Вы попадаете в строку с 12 или менее пунктами, но затем парень перед вами вытаскивает свою чековую книжку и начинает выписывать чек. Ты знаешь, что будешь там на минуту. Здесь то же самое.
Так что это не замкнутый цикл. Время от начала цикла до конца цикла довольно существенно. Особенно по сравнению с парой последних программ. Порядок времени огромен.
Теперь мы попытались продемонстрировать здесь, что вся идея о тесных петлях относительна.Все зависит от вашего приложения. Если вам нужно проверять состояние датчика каждые 10 миллионных долей секунды, то программа с тремя строками кода может быть недостаточно жесткой, это просто зависит от обстоятельств.
Еще один момент, который следует отметить, заключается в том, что не все строки кода занимают одинаковое время для выполнения. Если вы вызываете функцию, которая выполняет кучу вещей, например, последовательную печать, то эта одна строка кода может занять намного больше времени, чем 10 других строк кода.
Итак, плотность петли — понятие относительное.
Код блокировки
Когда программа останавливается в какой-то момент и требуется некоторое время для выполнения некоторого кода, этот код может называться блокирующим кодом . Это общий термин.
В нашей программе есть функция задержки, действующая как блокирующий код. Ни один код после задержки не может работать, пока задержка не закончится, поэтому он блокируется.
Блокирующий код действует не только тогда, когда мы используем функцию delay ().
Давайте возьмем нашу программу и избавимся от задержки, но мы добавим цикл for.Наш цикл for будет выводить числа и текст на последовательный порт.
Итак, как долго длится этот цикл? Он будет работать какое-то время, потому что ему нужно пройти 100 итераций, прежде чем он остановится.
А как насчет кода после этого цикла for? Может ли он работать? Нет, он должен ждать, потому что он блокируется циклом for.
Этот для цикла вложен в основной цикл . Цикл for — это «плотный» цикл? Прежде чем вы ответите, давайте еще раз подчеркнем, как вы должны думать об этом: много ли времени нужно, чтобы пройти этот цикл для цикла сверху вниз?
Ну, не совсем.У него всего две строчки кода. Так что это довольно сложный цикл.
Но это сложный цикл, который должен пройти через множество итераций, прежде чем программа сможет перейти к коду под ним. Таким образом, даже жесткий цикл, если он должен выполняться через несколько итераций, может заблокировать нашего кода.
Подробнее о функции delay ()
Давайте поговорим еще об этой функции задержки. Что мы уже установили?
Во-первых, мы сказали, что функция задержки снижает плотность петли.Если у вас плотный цикл и вы добавляете функцию задержки, это займет больше времени и сделает его менее жестким. То есть время, необходимое для перехода от начала цикла до конца, увеличивается с функцией задержки.
Мы также знаем, что код функции задержки. Это идет рука об руку с тем, что мы только что сказали. Когда функция задержки запущена, она блокирует выполнение другого кода во время задержки.
Вы можете подумать, что эта функция задержки просто бездельник! В наших проектах это никогда ни к чему не приведет.Но для многих простых программ, которые мы пишем, функция задержки работает фантастически. Он прост в использовании, его действительно легко писать по буквам, и он делает именно то, что говорит.
Таким образом, нам не обязательно исключать функцию delay () из нашего набора инструментов программирования. Мы должны просто признать, что это простая функция программирования, которая может работать во многих случаях.Однако бывает время, когда вы начинаете сталкиваться с проблемами. Это связано с блокирующим эффектом, который функция задержки имеет в нашей программе.
В следующем уроке этой серии, часть 3, мы собираемся определить, где это действительно становится проблемой. Вы узнаете, когда имеет смысл использовать функцию delay () в программе, а когда пора переходить к использованию функции millis ().
Обзор
Сначала мы поговорили о герметичности петли. Мы сказали, что плотность петли относительна. Это зависит от вашего приложения.
Во-вторых, мы говорили о коде блокировки или коде, который блокирует. По сути, это общий термин, который мы можем дать коду, выполнение которого займет некоторое время, и он остановит выполнение других частей нашей программы во время ее выполнения.
Надеемся, вам понравился этот урок! В следующей части этой серии мы продолжим наше путешествие, изучая, как использовать функцию millis для создания синхронизированных повторяющихся событий в нашем коде Arduino. Увидимся в следующий раз!
Обучение Arduino — Structure Void
Вы художник, дизайнер аудиовизуальных инсталляций, студент, преподаватель, исследователь или программист, и вы хотите создать системы взаимодействия, небольшие интеллектуальные схемы, чтобы ваш компьютер ощущал мир
Вы можете изучить Arduino, следуя своему собственному ритму с полными неделями или отдельными временными интервалами, онлайн или лично во Франции, с Жюльеном Бейлем , который также написал книгу об Arduino: программирование на C для Arduino
Работаете во французской компании? Вы можете получить финансирование для этого обучения (свяжитесь с нами для получения дополнительной информации)
ОПИСАНИЕ
Arduino — одна из самых интересных электронных сред с открытым исходным кодом для художников и дизайнеров.
Используя простую схему Arduino , некоторые провода и электрические реле, некоторые датчики расстояния или сгибания, мы можем легко контролировать, например, всю звуковую и визуальную установку.
Arduino можно запрограммировать на языке программирования C ++ с помощью хорошо документированной и бесплатной интегрированной среды разработки с открытым исходным кодом с помощью простого USB-кабеля.
Arduino является ядром очень впечатляющего числа генеративных и интерактивных установок и систем управления / обратной связи , позволяя создавать материальных интерфейсов с программным обеспечением, таким как Ableton Live , Max 8 и другими.
Обученный Жюльеном Бейлем, художником-программистом и автором книги C ++ Arduino Programming (2014), вы узнаете, как подключить Arduino к компьютеру для управления более или менее сложными генеративными процессами, и будете поражены легкостью путь для создания ваших собственных физических интерфейсов.
Чаще наши студенты запускают наш режим ПО ТРЕБОВАНИЮ и одновременно изучают Arduino и Max 8 на определенных специализированных занятиях.
АУДИТОРИИ И НАВЫКИ
Художники, Интерактивные художественные инсталляции, Дизайнер взаимодействия, Исследователь, Студенты, Любители физических вычислений
Наши тренинги по Arduino являются гибкими и предназначены для начинающих , средних и продвинутых пользователей .
Во время нашего первого контакта мы оцениваем ваши навыки с помощью вопросов и предлагаем вам специальную программу обучения .
УСЛОВИЯ И КАК ПОЛУЧИТЬ
Наши тренировки Arduino можно проводить в один-к-одному и в групповом режимах.
Вы можете выбрать Дистанционное обучение или очное обучение .
Мы можем предложить вам максимальную гибкость, предоставив всех возможных комбинаций : вы можете начать удаленно и прийти сюда, чтобы закончить свое обучение, или мы можем разработать для вашей группы целую неделю, лицом к лицу здесь, во Франции и получить для каждого из вас индивидуальные индивидуальные встречи 3 часа удаленно после этого…
ПРОДОЛЖИТЕЛЬНОСТЬ
Продолжительность всех курсов может быть изменена, но раньше мы предлагали вам эти 3 пакета:
- ИТОГО (5 дней)
- УСКОРЕННЫЙ (3 дня)
- УДАЛЕННЫЕ КАПСУЛЫ (интервалы 3 часа)
Во время нашего первого обсуждения мы вместе решаем, какой режим соответствует вашим потребностям.
TOTAL mode дает вам твердую / полную / полную автономию с Arduino.
УСКОРЕННЫЙ РЕЖИМ предоставляет вам все необходимое для использования Arduino без особых временных ограничений.
УДАЛЕННЫЕ КАПСУЛЫ — это наиболее гибкий режим, который обеспечивает лучший способ обучения в течение всех недель и месяцев, причем медленнее.
В зависимости от ваших потребностей мы можем предоставить более длительные тренировки по Arduino.
ФИНАНСИРОВАНИЕ
Если вы работаете в компании во Франции, во французском учреждении, вы, вероятно, сможете получить финансирование для обучения Arduino .
Свяжитесь с нами для получения более конкретной информации.
Arduino пустота
Цикл: он выполняет задачу, если некоторые задачи записаны в пустоту. Скобки-петли выполняют эту задачу один раз, а затем, в некотором смысле, выходят из пустого цикла, чтобы он не повторял эту задачу бесконечно снова и снова, и любой, кто смотрит это видео, вероятно, испытал что-то подобное, говорят. Я хочу, чтобы предложение напечатали один раз так. Я поместил команду строки последовательной печати в пустой цикл.Учитывая задержку, я ввел только одну строку кода. Я хочу, чтобы он сделал это один раз, поэтому я правильно его ввел. Что происходит, когда я загружаю этот код? Ну, давай откроем серийный маркер, я напечатаю 100 упов, о нет! Я хотел, чтобы он один раз напечатал то, что он делает, он печатает это снова и снова, и это именно то, чего я не хотел. Так как же нам это остановить? Как мы можем контролировать, хочу ли я что-то напечатать один раз или я хочу, чтобы обратный отсчет шел от десяти до девяти восьми, от семи до нуля, и я хочу, чтобы это произошло один раз в программе.Ну, первый вариант — мы можем просто выполнять любые команды. Мы не будем выполнять его один раз, а просто поместим их в void, настроим и оставим void, цикл будет полностью пустым, нет проблем, оставив его пустым, просто пустым, а затем мы можем загрузить этот код и проверить последовательный монитор, посмотреть, что бывает. Мне нужен этот зловещий зонд один раз, и он наберет обороты, поэтому все, что мы сделали, это в основном поместили код, который мы хотим выполнить один раз, внутри самой функции, которая предназначена для выполнения кода только один раз при запуске, а именно в установке void.
Но что происходит после выполнения этой задачи и аннулирования настройки. Что ж, Arduino продолжает работать, она идет, пустота, цикл и поскольку цикл пустоты пуст. Это просто бесконечно, это бесконечный цикл бездействия. Вот что происходит прямо здесь, так что вы в основном заставляли Arduino выполнять некоторые задачи, а затем запускали их в бесконечный цикл, что совершенно нормально, нет ничего плохого. При этом микроконтроллеры не возражают против бесконечного цикла. Операционной системы нет, поэтому возвращаться не к чему, поэтому ей нужно что-то делать, чтобы вращаться в бесконечном цикле.Но что, если бы мы хотели дать ему фактическое управление, то сам зацикливался бы, например, если бы я хотел, чтобы это предложение было напечатано один раз, давайте разрежем его, а затем снова вставим в цикл, скажем, я хочу, чтобы некоторые задачи были выполнены. Один раз это предложение является просто заменой какой-либо групповой отдельной задачи для нескольких задач, кто-то хотел бы выполнить две за один раз. Что это такое, затяните это и что, если я это сделаю, нужно будет выполнить внутри void, loop, хорошо подумайте о том, что произошло, когда мы поместили его в настройку void, мы помещаем предложение в настройку void.Если напечатать один раз, он переходит в пустой цикл и вращается в бесконечном цикле. Что ж, не могли бы мы просто построить бесконечный цикл внизу void, loop, а затем один раз напечатать Сенат, а затем переместить его в этот бесконечный цикл.
Безусловно, вы можете сделать это, используя такую идиому языка C, известную как пустой цикл for, что у него нет точки запуска, теста, увеличения или уменьшения, а затем скобки пусты. Просто обратите внимание, что вы можете сказать пустой, используя многострочность, комментарий, структуру, а затем хорошо справится с этой работой.Мы загрузили такую же проверку серийного монитора, да? Я хотел, чтобы это стало преобладающим, и так оно и было, но разве это единственный способ удержать Arduino в бесконечном цикле? Неужели нет хотя бы двух других? Вы можете сходить с ума, а затем сказать один, а затем оставить его пустым. Вы можете использовать так называемый пустой цикл while и то, что с ним происходит, пока структура управления остается, пока ее условие истинно. Он будет бесконечно делать все, что находится внутри его скобок, и в этом проблема, в то время как большую часть времени зацикливается, как и у вас.Чтобы не забыть обновить некоторую переменную счетчика в бэкэнде и нижнем конце цикла while, иначе вы получите бесконечный вид, особенно когда вы занимаетесь программированием на языке C, C или Python и т. Д. и т. д. Вы должны проявлять осторожность при использовании циклов while, потому что они могут быть довольно опасными там, где вам нужно жестко остановить программу, если вы превратите ее в бесконечный цикл. Что ж, в этом случае мы можем использовать его в сценарии микроконтроллера в наших интересах и фактически заставить Arduino выполнять некоторые задачи.
Я имею в виду, что это может быть несколько задач, я имею в виду, что у нас может быть серийный счет. Я не знаю. Кошки великолепны вне зависимости от того, согласен ли кто-то с этим или нет, и т. Д. И т. Д. В принципе, мы могли бы иметь бла, но сколько бы бла мы ни хотели, прежде чем этот дикий случай произойдет только один раз, просто чтобы протестировать это и загрузить, и Джо Мауро, да. Я хочу спринт по центру, раз уж кошки сейчас в совершенстве, а что происходит с Arduino, он крутится в бесконечном цикле. Так что именно то, что он делает, и это совершенно нормально, потому что, если говорить о внешнем поведении, то, что нас действительно волнует.Он сделал то, что мы от него хотели, а потом остановился и напугал цитатами. Не прекращал делать все, что все еще вращается прямо здесь, но останавливал каждую остановку в той точке, в которой мы хотели, чтобы мы в конечном итоге полностью контролировали пустоту ветра. Цикл останавливает этот пустой цикл for. Мы только что обсуждали это, хотя одно из них, хотя и верно, также будет делать то же самое. Мы можем это изменить. Щенки-верификаторы отлично загружают код, а затем проверяют его через серийный монитор.Хорошо. Друг слушателей хочет, чтобы щенки были отличными, поэтому этот вид преобразования пустого цикла в бесконечный цикл в самом низу, как в закрытой скобке. Цикл void даст нам возможность выполнять любую задачу. Мы хотим, чтобы он выполнялся один раз, а затем он превращает его в бесконечный цикл, то есть три разных стиля бесконечных циклов, просто покажите им еще раз, что у них есть пустые четыре.
Можем съесть одну из пустых скобок или можете съесть дикую настоящую еду. Любой из этих трех вариантов будет работать, и их, вероятно, намного больше.Язык C известен своей способностью делать действительно изящные вещи, такие идиомы одного и того же языка, вот и все. Все, что нужно сделать, чтобы иметь возможность остановить цикл void внизу и кастом, превращает их в бесконечный цикл. Но если мы хотим иметь возможность иметь в некотором смысле немного более технический контроль над этим, что, если мы хотим варьироваться, переходит ли он в пустой или бесконечный цикл или нет, в зависимости от того, что происходит до этого? Что ж, определенно могу это сделать, и это вариант, который я предпочитаю.Я предпочитаю писать игры с тем, где находится Arduino, где есть одиночный игрок, и их запрос Arduino запрашивает ходы в игре. Входит игрок. Эти ходы. Человек играет в игру, он либо выигрывает, либо проигрывает, а затем спрашивает, хотите ли вы воспроизвести игру, и на основе его ответа на этот вопрос. Он либо перезапускается с помощью цикла, либо останавливает его, либо, если хорошо, помещает его в другую форму бесконечного цикла. По крайней мере, как мы можем сделать что-то подобное? Что ж, мы можем сделать глобальную переменную логического времени.Давайте использовать его, чтобы думать больше об игре, мы скажем переменную воспроизведения, и давайте сначала установим для этой переменной значение true и отметим, какую глобальную переменную она будет формировать поверх всего, что часто возникает, поэтому внутрь бы посмотреть. Что мы можем сделать, так это протестировать.
Мы можем использовать управляющую структуру if. Можно сказать, играем ли мы в двойную эквивалентность. Это правда, тогда сделайте что-то вроде здесь, поиграйте в игру, а затем, когда человек закончит играть, мы сможем сделать запрос.Ты хочешь снова сыграть? У этого есть предложение на этот счет, отправленное в сериал, отслеживание, а затем другие виды доступных функций серийного номера, улавливание ответов пользователей и игроков, а затем, в зависимости от того, ответят ли они, отвечает ли он или она? Да или нет, мы можем переключать или сохранять значение воспроизведения таким же, допустим, мы переключаем его на ошибки, поэтому воспроизведите его, используя как false, а затем закройте пустоту, цикл или эту скобку, и поэтому мы можем увидеть это, скажем, последовательную руку Я полностью согласен с этим, это нужно просто распечатать, как только я посмотрю, действительно ли он загружается, наслаждайтесь игрой, если распечатать только один раз.Почему? Ну, потому что то, что произошло изначально, есть у нас. Блум говорит, что для значения воспроизведения установлено значение «истина», он проходит через установку хитов, недействительно, это проверка, каково значение воспроизведения? Что ж, если это так, то распечатайте это предложение, а затем, после того, как оно выйдет из повторного воспроизведения, которое является глобальной переменной, имейте в виду, что значение было полностью задано по умолчанию. Итак, как только мы говорим здесь о ложном, оно меняется на ложное вверх. Вот на этом, на этом нижнем проходе, и поэтому, когда он возвращается, когда возвращается цикл void, replay больше не равно true, поэтому он останавливается, но это не так, на самом деле ваш Arduino ничего не перестает делать.
Нет, он продолжает проверять, в основном опрос — это воспроизведение. Верно, правда ли, правда ли, правда ли, и учитывая, что это было переключено на значения по умолчанию, нет, это больше не правда, поэтому он никогда не вернется в цикл, он никогда не повторится, но вы можете вернуть контроль. Я имею в виду, что у меня будет больше видео, видеоуроков о задействованном коде и фактическом переключении этого значения в зависимости от ввода пользователя или игрока, но это простая структура. Это моя предпочтительная структура. По крайней мере, для меня это предпочтительный способ Miette, предпочитаю этот метод или технику, просто настраивая глобальную переменную, для типа смешивания установлено значение true или false, в зависимости от того, как вы хотите, чтобы он воспроизводился в руке, иметь цикл void begin и немедленно Перейдите в тест if, а затем что-то произойдет внутри, что if проверяет все, что я хочу, один раз, а затем внизу переключает значения по умолчанию, и это в основном заставляет цикл void действовать как setup, за исключением красота этой структуры. В том, что вы могли бы добавить сюда больше управляющего кода и фактически иметь возможность контролировать, переключается ли это значение, ложное или истинное? И если вы знаете, что на случай, если вы захотите, чтобы он снова запустился, вы можете переключить его на истинное значение, а затем, когда он появится, он воспроизведет его снова, просто чтобы увидеть, что все это должно произойти.Давайте просто установим для него истинное значение этого предложения: напечатайте, давайте добавим здесь задержку, чтобы мы не уничтожили его в последовательном порядке, поэтому каждые полсекунды наслаждайтесь игрой, которую следует играть на серийном номере, контролируйте церий, мы наслаждаемся игра.
Каждые полсекунды видите, поэтому все, что вам нужно, это код для управления значением воспроизведения, что довольно просто, то есть просто больше «если» и больше тестирования. Теперь есть еще один вариант: на самом деле вариантов больше, чем каких-либо еще, но, скажем, кто-то хочет создать что-то вроде фантазии.Остановите функцию цикла void, это вполне возможно для вас, просто чтобы соответствовать чему-то в этом роде. Давайте переименуем нашу глобальную переменную, скажем, мы хотим, чтобы в переменной stop it был бык, и установим для нее значение false. Итак, это говорит: мы хотим остановить это? Нет, и все, что нам нужно, это создать просто функцию, которая возвращает обновления для переменной stop it, чтобы мы могли указать, что мы хотим, чтобы она возвращала значение логического типа, поэтому остановитесь. Это будет, мы можем назвать это причудливой функцией остановки основного цикла.На самом деле это не тот метод, который я бы выбрал, но он действительно работает. Итак, что только один преуспевает, вы, по сути, собираетесь делать ту же самую структуру if внутри этого, давайте возьмем, давайте очистим, цикл void, просто чтобы это не слишком запутанно. Итак, у нас есть пустая пустота, цикл и мы. Просто постройка особого назначения. Остановить функцию основного цикла. Мы бы хотели, чтобы он делал каждый раз, когда он вызывается, мы хотим тестировать эту остановку переменной. Если stop равен false, а именно пользователям, которых мы не хотим, чтобы он останавливал основной цикл, чтобы остановить его, затем сделайте что-нибудь, скажем, последовательный.
Итак, мы знаем, что находимся внутри функции остановки, а затем мы можем в нижней части этого условия сделать что-то очень похожее. Как и раньше. Можно сказать, стоп, это правда. Итак, после того, как мы сделаем все, что происходит внутри, если мы переключим значение stop it, а затем просто охватим все базы, потому что иногда внутри функции вы будете создавать переменные, которые, по вашему мнению, являются глобальными, но на самом деле это не так, и чтобы избежать всего, что вы можете просто вернуть, независимо от обновленного значения stop, это так, return stop it, и это эффективно вытолкнет обновленное значение за пределы нижней скобки в функцию остановки и основного цикла.И тогда все, что нам нужно сделать, — это внутри цикла. Имеют те же. Остановитесь на переменной остановке. Оно будет равно хорошо, независимо от того, какое значение обновленное значение было выброшено функцией основного цикла остановки, поэтому, каким бы ни было это возвращаемое значение, в этом случае остановите его, оно будет равным, истина, поэтому оно будет вернуть это значение, а именно true, так что это будет в некотором смысле заменено на true, поэтому остановите его, чтобы оно было равно true, а затем оно попадет в нижнюю часть цикла void. Но это заставит это значение переключиться на глобальном уровне, а затем, когда все это откатится назад, стоп, оно больше не будет равно ошибкам, и это предложение не будет запускаться снова, поэтому мы можем протестировать его просто чтобы убедиться, что это более сложный способ сделать это, но для человека, использующего другие типы языков программирования, который любит создавать небольшие функции, загляните внутрь функции остановки основного цикла, и она выполняется только один раз, и в этот момент делает Arduino? Что ж, он постоянно проверяет это условие, и так уж получилось, что это условие истинно, поэтому он никогда больше не выполняет эту строку кода, и это так.
Просто в качестве причудливой функции остановки основного цикла: это один из способов, которым человек может остановить основной цикл, используя для этого специально созданную функцию, как я уже сказал ранее, это не мой предпочтительный метод, но он действительно работает. просто он имеет тенденцию использовать немного больше байтов, потому что каждый вызов функции требует немного больше предвзятости, чем просто использование всеобъемлющей структуры if внутри void, самого цикла и устранения этого. Но отсутствие памяти всегда вызывает беспокойство, и Arduino связывается с Ram, по крайней мере, так.Но это вариант, который сейчас можно было бы также использовать в качестве завершающего юмористического варианта. Давайте возьмем это, выньте это из основного цикла, уберите это и скажите, что вы хотите, чтобы они могли сделать что-то только один раз, а затем скажите: остановите все, как отключите его питание. Что ж, все, что вам нужно сделать, это иметь код, поэтому вставьте любой код. Вы хотите выполнить один раз здесь, а затем здесь, скажите, что питание губки сервопривода для нашего переключателя и есть переключатель, встроенный в кабель питания для Arduino между Arduino и батареей, а затем по вашей самой последней команде и недействительным.Цикл состоит в том, чтобы сервер отключил этот переключатель, вот почему вы могли бы это сделать, но тогда вы как бы запутались, вы собираетесь вручную щелкнуть переключателем, но это так. Один из способов сделать это. Другой способ — установить какое-то реле с фиксацией.
Я сделал это сам, когда вы сказали, что система безопасности, которую вы построили для своего дома или мастерской, или что-то еще, просто играете с Arduino, и вы постоянно проверяете целую кучу датчиков, целую кучу приближение, датчики и т. д. и т. д.И затем, как только человек нажимает кнопку на внешней стороне или его отпечатки пальцев, распознает или что у вас есть, самая последняя команда на void. Цикл, как и он, непрерывно проверяет свои датчики, проверяя их датчики, и игнорирует это значение до тех пор, пока не произойдет какое-либо другое внешнее событие, в это время он щелкает защелкивающееся реле, которое отключает питание Arduino, а затем включает все системы безопасности. Отключено, это тоже вариант. Просто есть над чем подумать, есть интересное, действительно юмористическое обсуждение всех этих различных способов, даже если оно действительно юмористическое.Так что у вас может быть сервопривод и уронить молоток на Arduino или что-то еще, в чем прелесть Arduino. Вы можете управлять внешним миром, и все, что вам нужно сделать, это управлять питанием Arduino, поскольку это самая последняя команда внутри точечного цикла, это всегда вариант. Что ж, я как раз, как только вы снова включите питание, он запустит этот цикл по всему телу, пока какое-либо значение не будет переключено, чтобы запустить эти другие Вэнсы. Но обсуждение этих различных способов остановить надоедливый, бесконечно повторяющийся основной цикл.
[mam_video id = AuiWwJZQEec]
[adrotate banner = ”2 ″]
[mam_tag id = 163]
[adrotate banner = ”3 ″]
[adrotate banner = ”4 ″]
[adrotate banner = ”5 ″]
Новое растяжимое оптическое кружево
Корнельского университета может позволить роботам ощущать, как они взаимодействуют с окружающей средой, и соответствующим образом корректировать свои действия.
Интересный способ для студентов узнать о кривой брахистохрона!
Arduino っ ち ゃ っ た !!な ん か 役 立 つ も の 作 れ ん か な !!! #Arduino # 電子 工作 # プ ロ グ ラ ミ ン グ https://t.co/JHN5ztO978
@ AykutBu85953656 @ lois_s4d Зорги, что s4d_breadboard.h geïnclude, он лучше всего и s4d_breadboard.h не имеет статуса карты и инициализируетBreadboard () в статусе void setup (), все, что есть в оснастке OLEDino.распечатать анмоет.
Elementi Base Del Linguaggio Di Programmazione Di Arduino
Некоторые проекты Arduino (UNO) uP. Новое для меня программирование на C ++.
[adrotate banner = ”6 ″]
Arduino IDE: условные выражения (if-else-if) — STEMpedia
Введение
Условные операторы проверяют, является ли заданное программистом логическое условие истинным или ложным.Они позволяют проверять переменную на соответствие значению / сравнивать переменную с другой переменной и заставляют программу действовать одним способом, если условие выполняется, и другим — если нет. Они делают программу очень мощной и ее можно использовать для самых разных целей.
В этом руководстве обсуждаются следующие условные операторы:
- если выписка
- if-else оператор
- if-else-if statement.
Заявление
ifНиже представлена структура оператора if :
If (условное выражение) {
Тело оператора if
}
Условное выражение может быть любым, результатом которого может быть истина или ложь.Если утверждение истинно, код в теле оператора выполняется. Однако, если выражение оказывается ложным, код в теле просто пропускается.
Ниже приведен пример использования оператора if :
Результат на последовательном мониторе:
Оценка ученика1 больше 33. Экзамен сдал.
Учащийся2 баллов ниже или равен 33. Он провалил экзамен.
В приведенном выше коде Student1 имеет оценки больше 33; следовательно, первый оператор верен и выполняется.Для Student2 верно второе утверждение; следовательно, выполняется второй оператор.
Попробуйте поменять местами номера, а потом посмотрите, что произойдет.
Убедитесь, что ваш ребенок достаточно знаком с такими футуристическими технологиями, как ИИ, с помощью практического опыта с использованием наборов ИИ для детей, таких как Quarky. Зарегистрируйтесь сегодня, чтобы воспользоваться специальными предложениями по этому набору AI, которые скоро появятся.
Оператор if-else
При использовании оператора if код в его теле выполняет только , когда оператор if принимает значение true.Если он принимает значение false, выполнение программы пропускает код в теле оператора if и переходит к оператору в теле оператора if .
При добавлении оператора else код в теле оператора else будет выполняться, но только , когда его соответствующий оператор if оценивается как ложный.
If (условное выражение) {
Тело оператора if, когда условное выражение истинно
}
else {
Тело оператора else, когда условное выражение ложно
}
Когда условное выражение оценивается как истинно :
- Код в теле , если выполняется инструкция . Код
- в теле оператора else не выполняется.
Когда условное выражение оценивается как false :
- Код в теле , если оператор не выполняется.
- Код в теле оператора else выполняется.
Ниже приведен пример использования оператора if-else :
Результат на последовательном мониторе:
Оценка ученика 1 больше 33.Он сдал экзамен.
Учащийся2 баллов ниже или равен 33. Он провалил экзамен.
Оператор if-else-if
Оператор if-else-if позволяет оценивать более одного условного выражения, чем оператор if-else .
Ниже представлена базовая структура:
if (условное выражение 1) {
Тело оператора if, когда условное выражение 1 истинно
}
else if (условное выражение 2) {
Тело оператора else-if, когда условное выражение 1 ложно, а условное выражение 2 равно true
}
else {
Тело оператора else, когда условные выражения 1 и 2 оба ложны
}
Когда условное выражение 1 оценивается как истина:
- Код в теле , первый , если выполняется инструкция .
- Коды в теле оператора else-if и else не выполняются.
Когда условное выражение 1 оценивается как false и условное выражение 2 оценивается как true :
- Код в теле оператора else-if .
- Коды в теле оператора if и else не выполняются.
Когда оба условного выражения 1 и 2 оцениваются как ложь:
- Код в теле оператора else выполняется.
- Коды в теле оператора if и оператора if-else не выполняются.
Ниже приведен пример использования оператора if-else-if :
Результат на последовательном мониторе:
Ваша оценка: A
Попробуйте поменять местами метки и посмотрите, что произойдет.
Учебное пособие по директивам препроцессора Arduino
— ifdef & endif
Первоначально опубликовано 6 июня 2017 г.
Содержание
- Введение
- Что мне нужно для начала?
- Как это работает
- После нажатия «Загрузить»
- Основы препроцессора
- Добавление дополнительного кода: директива #include
- Определение вещей: директива #define
- Условная компиляция: директивы #if
- Предоставление обратной связи: директивы #warning и #error
- Заключение
- Статьи по теме
Введение
В облегченной библиотеке Arduino для оценочного комплекта датчиков ROHM я представил RohmMultiSensor — библиотеку Arduino, которая позволяет легко взаимодействовать с несколькими датчиками в оценочном комплекте датчиков ROHM.Одна из основных особенностей этой библиотеки заключается в том, что размер программы заметно минимизируется за счет компиляции только тех частей библиотеки, которые содержат код, специфичный для датчика, который вы хотите использовать. Это означает, что при использовании меньшего количества датчиков общий размер программы и использование памяти будут меньше. Но как именно это происходит? И что на самом деле происходит за кулисами, когда вы #include библиотеки, а затем нажимаете кнопку «Загрузить»?
Что мне нужно для начала?
Практически каждый, кто когда-либо использовал Arduino, использовал библиотеку.Это одна из причин, почему программирование Arduino так легко для новичков — вам не нужно иметь глубокое понимание того, как работает датчик; библиотеки сделают за вас большую часть работы. Разделение кода на отдельные файлы также является хорошей практикой программирования. Гораздо проще организовать, отладить и поддерживать несколько разделенных файлов, чем огромный кусок кода.
Новички в Arduino уже должны быть знакомы с #include , который «добавляет» библиотеку к основному скетчу.Чтобы понять, как именно это происходит, мы сначала должны быстро взглянуть на то, как исходный код C / C ++ компилируется в программу. Не волнуйтесь, это звучит намного сложнее, чем есть на самом деле. Давайте посмотрим, как работает компиляция.
Как это работает
1. После нажатия «Загрузить»
Давайте сначала проведем быстрый эксперимент: запустите Arduino IDE, откройте один из примеров кодов (например, «Blink») и нажмите кнопку «Проверить».Предполагая, что в программе нет синтаксических ошибок, консоль внизу должна распечатать некоторую информацию о размере программы и памяти. Что ж, вы только что успешно скомпилировали исходный код C ++ в двоичный файл. Во время компиляции произошло несколько вещей:
- Arduino IDE выполнила так называемую «синтаксическую проверку», чтобы убедиться, что написанное вами действительно является исходным кодом C / C ++. Это момент, когда компиляция остановится, если вы неправильно написали функцию или забыли точку с запятой.
- После проверки синтаксиса Arduino IDE запускает другую программу под названием препроцессор . Это очень простая программа, которой все равно, является ли файл исходным кодом C / C ++. Поскольку мы поговорим об этом шаге позже, мы просто предположим, что результатом является файл с именем «расширенный исходный код», который по-прежнему является просто текстовым файлом.
- Затем расширенный исходный код был передан другой программе под названием , компилятор . Компилятор (в случае с Arduino IDE это avr-gcc ) принимает текстовый источник и создает файл сборки.Это более низкий язык программирования, который по-прежнему удобочитаем, но гораздо ближе к машинному коду — в основном это просто инструкции для процессора. Эта часть является причиной того, почему вы должны выбрать правильную плату Arduino перед началом компиляции скетча — разные платы имеют разные процессоры, которые, в свою очередь, имеют разные наборы инструкций.
- Следующая программа, обрабатывающая ваш скетч, называется ассемблер . Он генерирует «объектный файл». В основном это машинный код, но он также может содержать «ссылки» на объекты в других объектных файлах.Это позволяет Arduino IDE «предварительно компилировать» некоторые библиотеки, которые всегда будут использоваться при написании скетча Arduino, что значительно ускоряет весь процесс.
- Заключительный этап называется компоновкой и выполняется еще одной программой — что неудивительно — linker . Компоновщик берет объектный файл и добавляет все, чего не хватает, чтобы превратить его в исполняемый файл — в основном символы из других объектных файлов. После этого программа полностью преобразуется в машинный код и может быть запрограммирована на плату.
Теперь у нас есть базовое представление о том, что на самом деле нужно для компиляции скетча Arduino, но из всех этапов компиляции, описанных выше, мы собираемся сосредоточиться только на втором: препроцессоре.
2. Основы препроцессора
В приведенном выше тексте я упомянул, что препроцессор по сути очень прост: он просто принимает ввод текста, выполняет поиск по некоторым ключевым словам, выполняет некоторые операции в соответствии с тем, что он находит, а затем выводит другой текст.Несмотря на то, что он очень простой, он также чрезвычайно эффективен, поскольку позволяет делать вещи, которые в противном случае были бы очень сложными — если не невозможными — на простом языке C / C ++.
Препроцессор работает, ища строки, которые начинаются со знака решетки (#) и содержат текст после него. Это называется директивой препроцессора и является своего рода «командой» для препроцессора. Полный список всех поддерживаемых директив с подробной документацией можно найти здесь:
https: // gcc.gnu.org/onlinedocs/cpp/Index-of-Directives.html#Index-of-Directives.
В следующем тексте я сосредоточусь в основном на #include , #define и условных директивах, поскольку они наиболее полезны для Arduino, но если вы хотите узнать больше о некоторых более «экзотических» директивах, например #assert или #pragma , здесь можно получить официальную информацию.
3. Добавление дополнительного кода: #include Директива
Это, вероятно, самая известная директива препроцессора не только среди энтузиастов Arduino, но и в программировании на C / C ++ в целом.Причина проста: он используется для включения библиотек. Но как именно это происходит? Точный синтаксис выглядит следующим образом:
#include <файл>
или
#include "файл"
Разница между ними невелика и в основном сводится к тому, где именно препроцессор ищет файл . В первом случае препроцессор ищет только в каталогах, указанных IDE. Во втором случае препроцессор сначала просматривает папку, содержащую исходный код, и, только если файла нет, он перемещается в те же каталоги, что и в первом случае.Поскольку папка, содержащая библиотеки, указана в Arduino IDE, между ними нет большой разницы при включении библиотеки.
Когда препроцессор находит файл, он просто копирует и вставляет его содержимое в исходный код вместо директивы #include. Однако, если такой файл не может быть найден ни в одном из каталогов, это вызовет фатальную ошибку и остановит компиляцию.
Важно помнить, что препроцессор работает только с текстом — он на самом деле не понимает, что означают все эти причудливые буквы и цифры.И, что наиболее важно, он не выполняет никаких высокоуровневых проверок того, что было включено и сколько раз. Давайте посмотрим, что может произойти, если вы используете неправильно написанную библиотеку.
#includevoid setup () { } #include void loop () { }
В скетче Arduino действительно мало чего происходит. Обратите внимание, что мы включаем файл с именем « ExampleLibrary.h », и что мы включаем его дважды.
// Это пример библиотеки int a = 0; // Конец примера библиотеки
И это то, что находится внутри ExampleLibrary.h. Опять же, ничего особенного, за исключением одной целочисленной переменной. Так что же происходит, когда мы пытаемся скомпилировать этот скетч Arduino?
Ошибка показывает, что переменная a объявляется дважды, что приводит к сбою компиляции. Так выглядит исходный код после завершения препроцессора.
// Это пример библиотеки int a = 0; // Конец примера библиотеки void setup () { } // Это пример библиотеки int a = 0; // Конец примера библиотеки void loop () { }
Теперь очевидно, что ни одна библиотека не должна включаться более одного раза, но как этого добиться, не полагаясь на пользователя? Стандартное решение — обернуть всю библиотеку следующей конструкцией:
#ifndef _EXAMPLE_LIBRARY_H #define _EXAMPLE_LIBRARY_H // Это пример библиотеки int a = 0; // Конец примера библиотеки #endif
Теперь, когда библиотека включается впервые, препроцессор проверяет, есть ли что-то определенное с именем « _EXAMPLE_LIBRARY_H ».Поскольку ничего подобного еще не существует, он переходит к следующей строке и определяет константу с именем « _EXAMPLE_LIBRARY_H ». Затем код библиотеки копируется в эскиз.
При включении библиотеки во второй раз препроцессор снова проверяет наличие константы с именем « _EXAMPLE_LIBRARY_H ». Однако на этот раз константа уже определена из предыдущего #include , поэтому в эскиз ничего не добавляется. Теперь компиляция успешно завершена.Директивы #ifdef и #endif являются условными директивами, которые будут рассмотрены позже.
4. Определение вещей: #define directive
В предыдущем примере мы использовали директиву #define для создания константы, которая определяет, включать ли библиотеку или нет. В официальной документации все, что определяется директивой #define , называется макросом , поэтому в этой статье я буду придерживаться этой терминологии.Синтаксис этой директивы следующий:
#define macro_name macro_body
Большинство новичков в Arduino несколько сбиты с толку макросами. Если я определю макрос вроде следующего:
#define X 10
в чем точное отличие от объявления некоторой переменной, как показано ниже?
int Y = 10;
Опять же, все сводится к тому, что препроцессор работает только с текстом. Когда он найдет директиву #define , он выполнит поиск в остальной части исходного кода и заменит все вхождения «X» на «10».Это означает, что, в отличие от переменных, значение макроса никогда не изменится. Кроме того, вы должны иметь в виду, что препроцессор ищет только остаток исходного кода после строку с #define на ней . Давайте посмотрим, что произойдет, если мы попытаемся использовать макрос до того, как он будет определен.
int Y = X; #define X 10 int Z = X; void setup () { } void loop () { }
Компиляция приведенного выше кода даст нам следующую ошибку:
Код после предварительной обработки будет выглядеть так:
int Y = X; int Z = 10; void setup () { } void loop () { }
Первая строка содержит X , что интерпретируется как переменная, однако эта переменная никогда не объявлялась, поэтому компиляция останавливается.
Несмотря на то, что директива #define чаще всего используется для создания именованных констант, она может делать гораздо больше. Например, предположим, вы хотите знать, какое из двух заданных чисел меньше. Вы можете написать функцию, которая будет делать именно это.
int min (int a, int b) { if (aИли проще с тернарным оператором:
int min (int a, int b) { return ((aОднако обе эти функции будут скомпилированы и займут драгоценное место для хранения программ.Мы можем добиться того же эффекта с помощью следующего макроса, похожего на функцию, который займет гораздо меньше места в программе.
#ifndef MIN #define MIN (A, B) (((A) <(B))? (A): (B)) #endifТеперь каждое вхождение «MIN (A, B)» будет заменено на «(((A) <(B))? (A): (B))», где «A» и «B» могут быть либо число, либо переменная. Обратите внимание, что #define заключен в ту же защитную конструкцию, которая не позволяет пользователю определять макрос дважды.
При создании макросов следует помнить, что они снова обрабатываются как текст.Вот почему в приведенном выше определении почти все заключено в скобки. Попробуйте угадать результат следующей операции.
#ifndef НЕСКОЛЬКО # определить НЕСКОЛЬКО (A, B) A * B #endif // какой-то код ... int результат = НЕСКОЛЬКО (2-0, 3);Значение результат должно быть 6, так как 2-0 равно 2, а 2 * 3 равно 6, верно? Что, если я скажу вам, что результат будет 2? Фактически компилируется следующее:
int result = 2-0 * 3;Поскольку умножение имеет приоритет над вычитанием, теперь очевидно, что результат должен быть 2, потому что 3 * 0 равно 0, а 2-0 все еще равно 2.Правильная версия будет выглядеть так:
#ifndef НЕСКОЛЬКО #define НЕСКОЛЬКО (A, B) ((A) * (B)) #endif5. Условная компиляция: #if директивы
В предыдущих примерах я использовал директиву #ifndef a, которая позволяла мне проверять, была ли уже включена библиотека. Эту директиву можно использовать только для достижения того, что было бы невозможно с точки зрения языка C / C ++: условного синтаксиса. Эти директивы имеют следующий синтаксис:
#if выражение // компилируем этот код #elif different_expression // компилируем этот другой код #еще // компилируем этот совершенно другой код #endifСамый распространенный способ использования условного синтаксиса - проверить, определен ли макрос.Для этого вы можете использовать несколько специализированных директив:
#ifndef имя_макроса // компилируем этот код, если macro_name не существует #endifМы уже знакомы с вышеизложенным, поскольку мы использовали эту директиву, чтобы проверить, была ли уже включена библиотека. Вы также можете использовать это условие:
#ifdef имя_макроса // компилируем этот код, если macro_name существует #endifВышеупомянутое является сокращением для #if defined , которое можно использовать для проверки нескольких макросов в одном условии.Обратите внимание, что каждое условие должно заканчиваться директивой #endif , чтобы указать, какие части кода затронуты условием, а какие нет.
Давайте посмотрим на практический пример. Предположим, вы написали библиотеку и хотите, чтобы она корректно работала как на Arduino UNO, так и на Arduino Mega. Это кажется хорошей идеей, правда? Переносимый код всегда проще использовать, чем адаптировать уже работающую библиотеку для другой платы. Но что, если, например, ваша библиотека использует шину SPI? Эта шина расположена на контактах 11-13 на Arduino UNO, но на Mega она перемещена на контакты 50-52.
Как вы можете сказать компилятору, что он должен использовать правильные контакты, независимо от того, на какую плату вы загружаете в данный момент? Как вы уже догадались - условный синтаксис! В зависимости от того, какую плату вы выбрали в Arduino IDE в меню «Инструменты»> «Плата», среда IDE будет определять различные макросы, позволяя вам выбирать части кода, которые будут компилироваться только для определенной платы! Это невероятно мощно, потому что позволяет делать что-то вроде этого:
#if defined (__ AVR_ATmega168__) || определено (__ AVR_ATmega328P__) // это будет скомпилировано для Arduino UNO, Pro и более старых плат int _sck = 13; int _miso = 12; int _mosi = 11; #elif defined (__ AVR_ATmega1280__) || определено (__ AVR_ATmega2560__) // это будет компилироваться для Arduino Mega int _sck = 52; int _miso = 50; int _mosi = 51; #endifВидите его красоту? Имея всего три строчки кода, мы создали многоплатформенную портативную библиотеку! Кстати, именно так библиотека RohmMultiSensor (из Lightweight Arduino Library for ROHM Sensor Evaluation Kit ) знает, какие части кода должны быть скомпилированы для каждого выбранного датчика.Если вы заглянете внутрь заголовочного файла RohmMultiSensor.h , вы увидите только несколько директив #ifdef и #include . Поскольку весь код конкретного датчика хранится в отдельных файлах .cpp , легко добавлять новые датчики в библиотеку - просто создайте другой файл, а затем создайте ту же структуру #ifdef - #include - #endif для других датчиков использовать. Выполнено!
6. Отправка отзыва: #warning и #error директивы
Последние директивы, которые мы рассмотрим: #warning и #error .Оба они говорят сами за себя, поэтому вот синтаксис:
#warning "сообщение"и
# сообщение об ошибкеКогда препроцессор находит эти директивы, он выводит сообщение в консоль Arduino IDE. Разница между ними в том, что после #warning компиляция продолжается как обычно, а #error полностью останавливает компиляцию.
Мы можем использовать это в нашем предыдущем примере:
#if defined (__ AVR_ATmega168__) || определено (__ AVR_ATmega328P__) // это будет скомпилировано для Arduino UNO, Pro и более старых плат int _sck = 13; int _miso = 12; int _mosi = 11; #elif defined (__ AVR_ATmega1280__) || определено (__ AVR_ATmega2560__) // это будет компилироваться для Arduino Mega int _sck = 52; int _miso = 50; int _mosi = 51; #еще #error «Выбрана неподдерживаемая плата!» #endifТаким образом, когда пользователь пытается скомпилировать библиотеку для какой-либо другой платы Arduino (например,грамм. Yún, LilyPad и т. Д.) Компиляция завершится ошибкой, вместо того, чтобы вообще не определять контакты SPI.
Заключение
На этом наш краткий обзор глубин препроцессора C / C ++ завершается. Я надеюсь, что такие термины, как компиляция , препроцессор, или директива , кажутся, по крайней мере, немного менее пугающими, чем они были до того, как вы прочитали статью. Позвольте мне подытожить наиболее важные моменты, которые я пытался объяснить в этой статье:
- При написании библиотеки обязательно оберните ее внутрь конструкции #ifndef - #define - #endif , которую мы видели несколько раз.Это может избавить вас от неприятностей. То же самое следует делать при определении макросов, подобных функциям.
- Напишите код таким образом, чтобы его можно было легко перенести на другие платы Arduino. Поверьте мне, гораздо легче думать наперед, чем пытаться выяснить, что вызывает проблемы несовместимости.
- Разделяй и властвуй! Несколько файлов меньшего размера лучше, чем один файл длиной более 1000 строк.
Статьи по теме
Хотите узнать больше о DIY-проектах Arduino? Почему бы не ознакомиться с другими нашими замечательными статьями, например:
.
- Как управлять Arduino с компьютера под управлением Windows
- Держите кошек в страхе с помощью автоматического спуска струи воды и мощности Arduino Uno
- Как использовать модуль NRF24l01 + с Arduino